mirror of
https://github.com/sissbruecker/linkding.git
synced 2026-03-03 00:13:13 +08:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3086926146 | ||
|
|
b53bd9f112 | ||
|
|
75c0429973 | ||
|
|
0829d00e5f | ||
|
|
88fcb42292 | ||
|
|
aac8bf39b8 | ||
|
|
49f648a908 | ||
|
|
68c3c27b38 | ||
|
|
792a19d15e | ||
|
|
2de6d8151b | ||
|
|
9e9d7ae7d2 | ||
|
|
4e8a183082 |
168
CHANGELOG.md
168
CHANGELOG.md
@@ -1,125 +1,174 @@
|
||||
# Changelog
|
||||
|
||||
## v1.11.0 (26/05/2022)
|
||||
|
||||
### What's Changed
|
||||
* Add background tasks to admin by @sissbruecker in https://github.com/sissbruecker/linkding/pull/264
|
||||
* Improve about section by @sissbruecker in https://github.com/sissbruecker/linkding/pull/265
|
||||
* Allow creating archived bookmark through REST API by @kencx in https://github.com/sissbruecker/linkding/pull/268
|
||||
* Add PATCH support to bookmarks endpoint by @sissbruecker in https://github.com/sissbruecker/linkding/pull/269
|
||||
* Add community reference to linkding-cli by @bachya in https://github.com/sissbruecker/linkding/pull/270
|
||||
|
||||
### New Contributors
|
||||
* @kencx made their first contribution in https://github.com/sissbruecker/linkding/pull/268
|
||||
|
||||
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.10.1...v1.11.0
|
||||
|
||||
---
|
||||
|
||||
## v1.10.1 (21/05/2022)
|
||||
|
||||
### What's Changed
|
||||
* Fake request headers to reduce bot detection by @sissbruecker in https://github.com/sissbruecker/linkding/pull/263
|
||||
|
||||
|
||||
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.10.0...v1.10.1
|
||||
|
||||
---
|
||||
|
||||
## v1.10.0 (21/05/2022)
|
||||
### What's Changed
|
||||
* Add to managed hosting options by @m3nu in https://github.com/sissbruecker/linkding/pull/253
|
||||
* Add community reference to aiolinkding by @bachya in https://github.com/sissbruecker/linkding/pull/259
|
||||
* Improve import performance by @sissbruecker in https://github.com/sissbruecker/linkding/pull/261
|
||||
* Update how-to.md to fix unclear/paraphrased Safari action in IOS Shortcuts by @feoh in https://github.com/sissbruecker/linkding/pull/260
|
||||
* Allow searching for untagged bookmarks by @sissbruecker in https://github.com/sissbruecker/linkding/pull/226
|
||||
|
||||
### New Contributors
|
||||
* @m3nu made their first contribution in https://github.com/sissbruecker/linkding/pull/253
|
||||
* @bachya made their first contribution in https://github.com/sissbruecker/linkding/pull/259
|
||||
* @feoh made their first contribution in https://github.com/sissbruecker/linkding/pull/260
|
||||
|
||||
### What's Changed
|
||||
* Add to managed hosting options by @m3nu in https://github.com/sissbruecker/linkding/pull/253
|
||||
* Add community reference to aiolinkding by @bachya in https://github.com/sissbruecker/linkding/pull/259
|
||||
* Improve import performance by @sissbruecker in https://github.com/sissbruecker/linkding/pull/261
|
||||
* Update how-to.md to fix unclear/paraphrased Safari action in IOS Shortcuts by @feoh in https://github.com/sissbruecker/linkding/pull/260
|
||||
* Allow searching for untagged bookmarks by @sissbruecker in https://github.com/sissbruecker/linkding/pull/226
|
||||
|
||||
### New Contributors
|
||||
* @m3nu made their first contribution in https://github.com/sissbruecker/linkding/pull/253
|
||||
* @bachya made their first contribution in https://github.com/sissbruecker/linkding/pull/259
|
||||
* @feoh made their first contribution in https://github.com/sissbruecker/linkding/pull/260
|
||||
|
||||
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.9.0...v1.10.0
|
||||
|
||||
---
|
||||
|
||||
## v1.9.0 (14/05/2022)
|
||||
### What's Changed
|
||||
* Scroll menu items into view when using keyboard by @sissbruecker in https://github.com/sissbruecker/linkding/pull/248
|
||||
* Add whitespace after auto-completed tag by @sissbruecker in https://github.com/sissbruecker/linkding/pull/249
|
||||
* Bump django from 3.2.12 to 3.2.13 by @dependabot in https://github.com/sissbruecker/linkding/pull/244
|
||||
* Add community helm chart reference to readme by @pascaliske in https://github.com/sissbruecker/linkding/pull/242
|
||||
* Feature: Shortcut key for new bookmark by @rithask in https://github.com/sissbruecker/linkding/pull/241
|
||||
* Clarify archive.org feature by @clach04 in https://github.com/sissbruecker/linkding/pull/229
|
||||
* Make Internet Archive integration opt-in by @sissbruecker in https://github.com/sissbruecker/linkding/pull/250
|
||||
|
||||
### New Contributors
|
||||
* @pascaliske made their first contribution in https://github.com/sissbruecker/linkding/pull/242
|
||||
* @rithask made their first contribution in https://github.com/sissbruecker/linkding/pull/241
|
||||
* @clach04 made their first contribution in https://github.com/sissbruecker/linkding/pull/229
|
||||
|
||||
### What's Changed
|
||||
* Scroll menu items into view when using keyboard by @sissbruecker in https://github.com/sissbruecker/linkding/pull/248
|
||||
* Add whitespace after auto-completed tag by @sissbruecker in https://github.com/sissbruecker/linkding/pull/249
|
||||
* Bump django from 3.2.12 to 3.2.13 by @dependabot in https://github.com/sissbruecker/linkding/pull/244
|
||||
* Add community helm chart reference to readme by @pascaliske in https://github.com/sissbruecker/linkding/pull/242
|
||||
* Feature: Shortcut key for new bookmark by @rithask in https://github.com/sissbruecker/linkding/pull/241
|
||||
* Clarify archive.org feature by @clach04 in https://github.com/sissbruecker/linkding/pull/229
|
||||
* Make Internet Archive integration opt-in by @sissbruecker in https://github.com/sissbruecker/linkding/pull/250
|
||||
|
||||
### New Contributors
|
||||
* @pascaliske made their first contribution in https://github.com/sissbruecker/linkding/pull/242
|
||||
* @rithask made their first contribution in https://github.com/sissbruecker/linkding/pull/241
|
||||
* @clach04 made their first contribution in https://github.com/sissbruecker/linkding/pull/229
|
||||
|
||||
**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.8.8...v1.9.0
|
||||
|
||||
---
|
||||
|
||||
## v1.8.8 (27/03/2022)
|
||||
- [**bug**] Prevent bookmark actions through get requests
|
||||
|
||||
- [**bug**] Prevent bookmark actions through get requests
|
||||
- [**bug**] Prevent external redirects
|
||||
|
||||
---
|
||||
|
||||
## v1.8.7 (26/03/2022)
|
||||
- [**bug**] Increase request buffer size [#28](https://github.com/sissbruecker/linkding/issues/28)
|
||||
- [**enhancement**] Allow specifying port through LINKDING_PORT environment variable [#156](https://github.com/sissbruecker/linkding/pull/156)
|
||||
|
||||
- [**bug**] Increase request buffer size [#28](https://github.com/sissbruecker/linkding/issues/28)
|
||||
- [**enhancement**] Allow specifying port through LINKDING_PORT environment variable [#156](https://github.com/sissbruecker/linkding/pull/156)
|
||||
- [**chore**] Bump NPM packages [#224](https://github.com/sissbruecker/linkding/pull/224)
|
||||
|
||||
---
|
||||
|
||||
## v1.8.6 (25/03/2022)
|
||||
- [bug] fix bookmark access restrictions
|
||||
- [bug] prevent external redirects
|
||||
|
||||
- [bug] fix bookmark access restrictions
|
||||
- [bug] prevent external redirects
|
||||
- [chore] bump dependencies
|
||||
|
||||
---
|
||||
|
||||
## v1.8.5 (13/12/2021)
|
||||
- [**bug**] Ensure tag names do not contain spaces [#182](https://github.com/sissbruecker/linkding/issues/182)
|
||||
- [**bug**] Consider not copying whole GIT repository to Docker image [#174](https://github.com/sissbruecker/linkding/issues/174)
|
||||
## v1.8.5 (12/12/2021)
|
||||
|
||||
- [**bug**] Ensure tag names do not contain spaces [#182](https://github.com/sissbruecker/linkding/issues/182)
|
||||
- [**bug**] Consider not copying whole GIT repository to Docker image [#174](https://github.com/sissbruecker/linkding/issues/174)
|
||||
- [**enhancement**] Make bookmarks count column in admin sortable [#183](https://github.com/sissbruecker/linkding/pull/183)
|
||||
|
||||
---
|
||||
|
||||
## v1.8.4 (16/10/2021)
|
||||
|
||||
- [**enhancement**] Allow non-admin users to change their password [#166](https://github.com/sissbruecker/linkding/issues/166)
|
||||
|
||||
---
|
||||
|
||||
## v1.8.3 (03/10/2021)
|
||||
|
||||
- [**enhancement**] Enhancement: let user configure to open links in same tab instead on a new window/tab [#27](https://github.com/sissbruecker/linkding/issues/27)
|
||||
|
||||
---
|
||||
|
||||
## v1.8.2 (02/10/2021)
|
||||
|
||||
- [**bug**] Fix jumping search box [#163](https://github.com/sissbruecker/linkding/pull/163)
|
||||
|
||||
---
|
||||
|
||||
## v1.8.1 (01/10/2021)
|
||||
- [**enhancement**] Add global shortcut for search [#161](https://github.com/sissbruecker/linkding/pull/161)
|
||||
|
||||
- [**enhancement**] Add global shortcut for search [#161](https://github.com/sissbruecker/linkding/pull/161)
|
||||
- allows to press `s` to focus the search input
|
||||
|
||||
---
|
||||
|
||||
## v1.8.0 (04/09/2021)
|
||||
- [**enhancement**] Wayback Machine Integration [#59](https://github.com/sissbruecker/linkding/issues/59)
|
||||
- Automatically creates snapshots of bookmarked websites on [web archive](https://archive.org/web/)
|
||||
|
||||
- [**enhancement**] Wayback Machine Integration [#59](https://github.com/sissbruecker/linkding/issues/59)
|
||||
- Automatically creates snapshots of bookmarked websites on [web archive](https://archive.org/web/)
|
||||
- This is one of the largest changes yet and adds a task processor that runs as a separate process in the background. If you run into issues with this feature, it can be disabled using the [LD_DISABLE_BACKGROUND_TASKS](https://github.com/sissbruecker/linkding/blob/master/docs/Options.md#ld_disable_background_tasks) option
|
||||
|
||||
---
|
||||
|
||||
## v1.7.2 (26/08/2021)
|
||||
|
||||
- [**enhancement**] Add support for nanosecond resolution timestamps for bookmark import (e.g. Google Bookmarks) [#146](https://github.com/sissbruecker/linkding/issues/146)
|
||||
|
||||
---
|
||||
|
||||
## v1.7.1 (25/08/2021)
|
||||
- [**bug**] umlaut/non-ascii characters broken when using bookmarklet (firefox) [#148](https://github.com/sissbruecker/linkding/issues/148)
|
||||
- [**bug**] Bookmark import accepts empty URL values [#124](https://github.com/sissbruecker/linkding/issues/124)
|
||||
|
||||
- [**bug**] umlaut/non-ascii characters broken when using bookmarklet (firefox) [#148](https://github.com/sissbruecker/linkding/issues/148)
|
||||
- [**bug**] Bookmark import accepts empty URL values [#124](https://github.com/sissbruecker/linkding/issues/124)
|
||||
- [**enhancement**] Show the version in the settings [#104](https://github.com/sissbruecker/linkding/issues/104)
|
||||
|
||||
---
|
||||
|
||||
## v1.7.0 (17/08/2021)
|
||||
- Upgrade to Django 3
|
||||
|
||||
- Upgrade to Django 3
|
||||
- Bump other dependencies
|
||||
|
||||
---
|
||||
|
||||
## v1.6.5 (15/08/2021)
|
||||
|
||||
- [**enhancement**] query with multiple hashtags very slow [#112](https://github.com/sissbruecker/linkding/issues/112)
|
||||
|
||||
---
|
||||
|
||||
## v1.6.4 (13/05/2021)
|
||||
|
||||
- Update dependencies for security fixes
|
||||
|
||||
---
|
||||
|
||||
## v1.6.3 (07/04/2021)
|
||||
## v1.6.3 (06/04/2021)
|
||||
|
||||
- [**bug**] relative names use the wrong "today" after day change [#107](https://github.com/sissbruecker/linkding/issues/107)
|
||||
|
||||
---
|
||||
|
||||
## v1.6.2 (04/04/2021)
|
||||
|
||||
- [**enhancement**] Expose `date_added` in UI [#85](https://github.com/sissbruecker/linkding/issues/85)
|
||||
- [**closed**] Archived bookmarks - no result when searching for a word which is used only as tag [#83](https://github.com/sissbruecker/linkding/issues/83)
|
||||
- [**closed**] Add archive/unarchive button to edit bookmark page [#82](https://github.com/sissbruecker/linkding/issues/82)
|
||||
@@ -128,46 +177,57 @@
|
||||
---
|
||||
|
||||
## v1.6.1 (31/03/2021)
|
||||
|
||||
- Expose date_added in UI [#85](https://github.com/sissbruecker/linkding/issues/85)
|
||||
|
||||
---
|
||||
|
||||
## v1.6.0 (29/03/2021)
|
||||
## v1.6.0 (28/03/2021)
|
||||
|
||||
- Bulk edit mode [#101](https://github.com/sissbruecker/linkding/pull/101)
|
||||
|
||||
---
|
||||
|
||||
## v1.5.0 (28/03/2021)
|
||||
|
||||
- [**closed**] Add a dark mode [#49](https://github.com/sissbruecker/linkding/issues/49)
|
||||
|
||||
---
|
||||
|
||||
## v1.4.1 (20/03/2021)
|
||||
- Security patches
|
||||
|
||||
- Security patches
|
||||
- Documentation improvements
|
||||
|
||||
---
|
||||
|
||||
## v1.4.0 (24/02/2021)
|
||||
|
||||
- [**enhancement**] Improve admin utilization [#76](https://github.com/sissbruecker/linkding/issues/76)
|
||||
|
||||
---
|
||||
|
||||
## v1.3.3 (18/02/2021)
|
||||
|
||||
- [**closed**] Missing "description" request body parameter in API causes 500 [#78](https://github.com/sissbruecker/linkding/issues/78)
|
||||
|
||||
---
|
||||
|
||||
## v1.3.2 (18/02/2021)
|
||||
|
||||
- [**closed**] /archive and /unarchive API routes return 404 [#77](https://github.com/sissbruecker/linkding/issues/77)
|
||||
- [**closed**] API - /api/check_url?url= with token authetification [#55](https://github.com/sissbruecker/linkding/issues/55)
|
||||
|
||||
---
|
||||
|
||||
## v1.3.1 (15/02/2021)
|
||||
|
||||
[enhancement] Enhance delete links with inline confirmation
|
||||
|
||||
---
|
||||
|
||||
## v1.3.0 (14/02/2021)
|
||||
|
||||
- [**closed**] Novice help. [#71](https://github.com/sissbruecker/linkding/issues/71)
|
||||
- [**closed**] Option to create bookmarks public [#70](https://github.com/sissbruecker/linkding/issues/70)
|
||||
- [**enhancement**] Show URL if title is not available [#64](https://github.com/sissbruecker/linkding/issues/64)
|
||||
@@ -179,16 +239,40 @@
|
||||
---
|
||||
|
||||
## v1.2.1 (12/01/2021)
|
||||
|
||||
- [**bug**] Bug: Two equal tags with different capitalisation lead to 500 server errors [#65](https://github.com/sissbruecker/linkding/issues/65)
|
||||
- [**closed**] Enhancement: category and pagination [#11](https://github.com/sissbruecker/linkding/issues/11)
|
||||
|
||||
---
|
||||
|
||||
## v1.2.0 (09/01/2021)
|
||||
|
||||
- [**closed**] Add Favicon [#58](https://github.com/sissbruecker/linkding/issues/58)
|
||||
- [**closed**] Make tags case-insensitive [#45](https://github.com/sissbruecker/linkding/issues/45)
|
||||
|
||||
---
|
||||
|
||||
## v1.1.1 (01/01/2021)
|
||||
- [**enhancement**] Add docker-compose support [#54](https://github.com/sissbruecker/linkding/pull/54)
|
||||
|
||||
- [**enhancement**] Add docker-compose support [#54](https://github.com/sissbruecker/linkding/pull/54)
|
||||
|
||||
---
|
||||
|
||||
## v1.1.0 (31/12/2020)
|
||||
|
||||
- [**enhancement**] Search autocomplete [#52](https://github.com/sissbruecker/linkding/issues/52)
|
||||
- [**enhancement**] Improve Netscape bookmarks file parsing [#50](https://github.com/sissbruecker/linkding/issues/50)
|
||||
|
||||
---
|
||||
|
||||
## v1.0.0 (31/12/2020)
|
||||
|
||||
- [**bug**] Import does not import bookmark descriptions [#47](https://github.com/sissbruecker/linkding/issues/47)
|
||||
- [**enhancement**] Enhancement: return to same page we were on after editing a bookmark [#26](https://github.com/sissbruecker/linkding/issues/26)
|
||||
- [**bug**] Increase limit on bookmark URL length [#25](https://github.com/sissbruecker/linkding/issues/25)
|
||||
- [**enhancement**] API for app development [#24](https://github.com/sissbruecker/linkding/issues/24)
|
||||
- [**enhancement**] Enhancement: detect duplicates at entry time [#23](https://github.com/sissbruecker/linkding/issues/23)
|
||||
- [**bug**] Error importing bookmarks [#18](https://github.com/sissbruecker/linkding/issues/18)
|
||||
- [**enhancement**] Enhancement: better administration page [#4](https://github.com/sissbruecker/linkding/issues/4)
|
||||
- [**enhancement**] Bug: Navigation bar active link stays on add bookmark [#3](https://github.com/sissbruecker/linkding/issues/3)
|
||||
- [**bug**] CSS Stylesheet presented as text/plain [#2](https://github.com/sissbruecker/linkding/issues/2)
|
||||
@@ -129,6 +129,7 @@ This section lists community projects around using linkding, in alphabetical ord
|
||||
- [linkding-extension](https://github.com/jeroenpardon/linkding-extension) Chromium compatible extension that wraps the linkding bookmarklet. Tested with Chrome, Edge, Brave. By [jeroenpardon](https://github.com/jeroenpardon)
|
||||
- [linkding-injector](https://github.com/Fivefold/linkding-injector) Injects search results from linkding into the sidebar of search pages like google and duckduckgo. Tested with Firefox and Chrome. By [Fivefold](https://github.com/Fivefold)
|
||||
- [aiolinkding](https://github.com/bachya/aiolinkding) A Python3, async library to interact with the linkding REST API. By [bachya](https://github.com/bachya)
|
||||
- [linkding-cli](https://github.com/bachya/linkding-cli) A command-line interface (CLI) to interact with the linkding REST API. Powered by [aiolinkding](https://github.com/bachya/aiolinkding). By [bachya](https://github.com/bachya)
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,3 +1,5 @@
|
||||
from background_task.admin import TaskAdmin, CompletedTaskAdmin
|
||||
from background_task.models import Task, CompletedTask
|
||||
from django.contrib import admin, messages
|
||||
from django.contrib.admin import AdminSite
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
@@ -107,3 +109,5 @@ linkding_admin_site.register(Tag, AdminTag)
|
||||
linkding_admin_site.register(User, AdminCustomUser)
|
||||
linkding_admin_site.register(TokenProxy, TokenAdmin)
|
||||
linkding_admin_site.register(Toast, AdminToast)
|
||||
linkding_admin_site.register(Task, TaskAdmin)
|
||||
linkding_admin_site.register(CompletedTask, CompletedTaskAdmin)
|
||||
|
||||
@@ -19,6 +19,7 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||
'description',
|
||||
'website_title',
|
||||
'website_description',
|
||||
'is_archived',
|
||||
'tag_names',
|
||||
'date_added',
|
||||
'date_modified'
|
||||
@@ -33,6 +34,7 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||
# Override optional char fields to provide default value
|
||||
title = serializers.CharField(required=False, allow_blank=True, default='')
|
||||
description = serializers.CharField(required=False, allow_blank=True, default='')
|
||||
is_archived = serializers.BooleanField(required=False, default=False)
|
||||
# Override readonly tag_names property to allow passing a list of tag names to create/update
|
||||
tag_names = TagListField(required=False, default=[])
|
||||
|
||||
@@ -41,14 +43,21 @@ class BookmarkSerializer(serializers.ModelSerializer):
|
||||
bookmark.url = validated_data['url']
|
||||
bookmark.title = validated_data['title']
|
||||
bookmark.description = validated_data['description']
|
||||
bookmark.is_archived = validated_data['is_archived']
|
||||
tag_string = build_tag_string(validated_data['tag_names'])
|
||||
return create_bookmark(bookmark, tag_string, self.context['user'])
|
||||
|
||||
def update(self, instance: Bookmark, validated_data):
|
||||
instance.url = validated_data['url']
|
||||
instance.title = validated_data['title']
|
||||
instance.description = validated_data['description']
|
||||
tag_string = build_tag_string(validated_data['tag_names'])
|
||||
# Update fields if they were provided in the payload
|
||||
for key in ['url', 'title', 'description']:
|
||||
if key in validated_data:
|
||||
setattr(instance, key, validated_data[key])
|
||||
|
||||
# Use tag string from payload, or use bookmark's current tags as fallback
|
||||
tag_string = build_tag_string(instance.tag_names)
|
||||
if 'tag_names' in validated_data:
|
||||
tag_string = build_tag_string(validated_data['tag_names'])
|
||||
|
||||
return update_bookmark(instance, tag_string, self.context['user'])
|
||||
|
||||
|
||||
|
||||
@@ -93,6 +93,7 @@ def _create_missing_tags(netscape_bookmarks: List[NetscapeBookmark], user: User)
|
||||
tag = Tag(name=tag_name, owner=user)
|
||||
tag.date_added = timezone.now()
|
||||
tags_to_create.append(tag)
|
||||
tag_cache.put(tag)
|
||||
|
||||
Tag.objects.bulk_create(tags_to_create)
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ from django.contrib.auth.models import User
|
||||
from waybackpy.exceptions import WaybackError
|
||||
|
||||
from bookmarks.models import Bookmark, UserProfile
|
||||
from bookmarks.services.website_loader import DEFAULT_USER_AGENT
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -38,10 +39,10 @@ def _create_web_archive_snapshot_task(bookmark_id: int, force_update: bool):
|
||||
|
||||
logger.debug(f'Create web archive link for bookmark: {bookmark}...')
|
||||
|
||||
wayback = waybackpy.Url(bookmark.url)
|
||||
archive = waybackpy.WaybackMachineSaveAPI(bookmark.url, DEFAULT_USER_AGENT)
|
||||
|
||||
try:
|
||||
archive = wayback.save()
|
||||
archive.save()
|
||||
except WaybackError as error:
|
||||
logger.exception(f'Error creating web archive link for bookmark: {bookmark}...', exc_info=error)
|
||||
raise
|
||||
|
||||
@@ -45,11 +45,14 @@ def load_page(url: str):
|
||||
return str(results.best())
|
||||
|
||||
|
||||
DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36'
|
||||
|
||||
|
||||
def fake_request_headers():
|
||||
return {
|
||||
"Accept": "text/html,application/xhtml+xml,application/xml",
|
||||
"Accept-Encoding": "gzip, deflate",
|
||||
"Dnt": "1",
|
||||
"Upgrade-Insecure-Requests": "1",
|
||||
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36",
|
||||
"User-Agent": DEFAULT_USER_AGENT,
|
||||
}
|
||||
|
||||
BIN
bookmarks/static/apple-touch-icon.png
Normal file
BIN
bookmarks/static/apple-touch-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -11,4 +11,8 @@
|
||||
.input-group > input[type=submit] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
section.about table {
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="{% static 'favicon.png' %}"/>
|
||||
<link rel="apple-touch-icon" href="{% static 'apple-touch-icon.png' %}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimal-ui">
|
||||
<meta name="description" content="Self-hosted bookmark service">
|
||||
<meta name="robots" content="index,follow">
|
||||
|
||||
@@ -98,13 +98,29 @@
|
||||
</section>
|
||||
|
||||
{# About section #}
|
||||
<section class="content-area">
|
||||
<section class="content-area about">
|
||||
<h2>About</h2>
|
||||
<p>Version: {{ app_version }}</p>
|
||||
<p>
|
||||
Code: <a href="https://github.com/sissbruecker/linkding/"
|
||||
target="_blank">GitHub</a>
|
||||
</p>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{{ version_info }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td rowspan="3" style="vertical-align: top">Links</td>
|
||||
<td><a href="https://github.com/sissbruecker/linkding/"
|
||||
target="_blank">GitHub</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/sissbruecker/linkding#documentation"
|
||||
target="_blank">Documentation</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://github.com/sissbruecker/linkding/blob/master/CHANGELOG.md"
|
||||
target="_blank">Changelog</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -83,6 +83,11 @@ class LinkdingApiTestCase(APITestCase):
|
||||
self.assertEqual(response.status_code, expected_status_code)
|
||||
return response
|
||||
|
||||
def patch(self, url, data=None, expected_status_code=status.HTTP_200_OK):
|
||||
response = self.client.patch(url, data, format='json')
|
||||
self.assertEqual(response.status_code, expected_status_code)
|
||||
return response
|
||||
|
||||
def delete(self, url, expected_status_code=status.HTTP_200_OK):
|
||||
response = self.client.delete(url)
|
||||
self.assertEqual(response.status_code, expected_status_code)
|
||||
|
||||
@@ -34,6 +34,7 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
expectation['description'] = bookmark.description
|
||||
expectation['website_title'] = bookmark.website_title
|
||||
expectation['website_description'] = bookmark.website_description
|
||||
expectation['is_archived'] = bookmark.is_archived
|
||||
expectation['tag_names'] = tag_names
|
||||
expectation['date_added'] = bookmark.date_added.isoformat().replace('+00:00', 'Z')
|
||||
expectation['date_modified'] = bookmark.date_modified.isoformat().replace('+00:00', 'Z')
|
||||
@@ -49,6 +50,7 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
'url': 'https://example.com/',
|
||||
'title': 'Test title',
|
||||
'description': 'Test description',
|
||||
'is_archived': False,
|
||||
'tag_names': ['tag1', 'tag2']
|
||||
}
|
||||
self.post(reverse('bookmarks:bookmark-list'), data, status.HTTP_201_CREATED)
|
||||
@@ -56,6 +58,7 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
self.assertEqual(bookmark.url, data['url'])
|
||||
self.assertEqual(bookmark.title, data['title'])
|
||||
self.assertEqual(bookmark.description, data['description'])
|
||||
self.assertFalse(bookmark.is_archived, data['is_archived'])
|
||||
self.assertEqual(bookmark.tags.count(), 2)
|
||||
self.assertEqual(bookmark.tags.filter(name=data['tag_names'][0]).count(), 1)
|
||||
self.assertEqual(bookmark.tags.filter(name=data['tag_names'][1]).count(), 1)
|
||||
@@ -92,6 +95,30 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
response = self.get(reverse('bookmarks:bookmark-archived') + '?q=#' + self.tag1.name, expected_status_code=status.HTTP_200_OK)
|
||||
self.assertBookmarkListEqual(response.data['results'], [self.archived_bookmark1])
|
||||
|
||||
def test_create_archived_bookmark(self):
|
||||
data = {
|
||||
'url': 'https://example.com/',
|
||||
'title': 'Test title',
|
||||
'description': 'Test description',
|
||||
'is_archived': True,
|
||||
'tag_names': ['tag1', 'tag2']
|
||||
}
|
||||
self.post(reverse('bookmarks:bookmark-list'), data, status.HTTP_201_CREATED)
|
||||
bookmark = Bookmark.objects.get(url=data['url'])
|
||||
self.assertEqual(bookmark.url, data['url'])
|
||||
self.assertEqual(bookmark.title, data['title'])
|
||||
self.assertEqual(bookmark.description, data['description'])
|
||||
self.assertTrue(bookmark.is_archived)
|
||||
self.assertEqual(bookmark.tags.count(), 2)
|
||||
self.assertEqual(bookmark.tags.filter(name=data['tag_names'][0]).count(), 1)
|
||||
self.assertEqual(bookmark.tags.filter(name=data['tag_names'][1]).count(), 1)
|
||||
|
||||
def test_create_bookmark_minimal_payload_does_not_archive(self):
|
||||
data = {'url': 'https://example.com/'}
|
||||
self.post(reverse('bookmarks:bookmark-list'), data, status.HTTP_201_CREATED)
|
||||
bookmark = Bookmark.objects.get(url=data['url'])
|
||||
self.assertFalse(bookmark.is_archived)
|
||||
|
||||
def test_get_bookmark(self):
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
response = self.get(url, expected_status_code=status.HTTP_200_OK)
|
||||
@@ -104,6 +131,56 @@ class BookmarksApiTestCase(LinkdingApiTestCase, BookmarkFactoryMixin):
|
||||
updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id)
|
||||
self.assertEqual(updated_bookmark.url, data['url'])
|
||||
|
||||
def test_update_bookmark_fails_without_required_fields(self):
|
||||
data = {'title': 'https://example.com/'}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.put(url, data, expected_status_code=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_update_bookmark_with_minimal_payload_clears_all_fields(self):
|
||||
data = {'url': 'https://example.com/'}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.put(url, data, expected_status_code=status.HTTP_200_OK)
|
||||
updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id)
|
||||
self.assertEqual(updated_bookmark.url, data['url'])
|
||||
self.assertEqual(updated_bookmark.title, '')
|
||||
self.assertEqual(updated_bookmark.description, '')
|
||||
self.assertEqual(updated_bookmark.tag_names, [])
|
||||
|
||||
def test_patch_bookmark(self):
|
||||
data = {'url': 'https://example.com'}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.patch(url, data, expected_status_code=status.HTTP_200_OK)
|
||||
self.bookmark1.refresh_from_db()
|
||||
self.assertEqual(self.bookmark1.url, data['url'])
|
||||
|
||||
data = {'title': 'Updated title'}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.patch(url, data, expected_status_code=status.HTTP_200_OK)
|
||||
self.bookmark1.refresh_from_db()
|
||||
self.assertEqual(self.bookmark1.title, data['title'])
|
||||
|
||||
data = {'description': 'Updated description'}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.patch(url, data, expected_status_code=status.HTTP_200_OK)
|
||||
self.bookmark1.refresh_from_db()
|
||||
self.assertEqual(self.bookmark1.description, data['description'])
|
||||
|
||||
data = {'tag_names': ['updated-tag-1', 'updated-tag-2']}
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.patch(url, data, expected_status_code=status.HTTP_200_OK)
|
||||
self.bookmark1.refresh_from_db()
|
||||
tag_names = [tag.name for tag in self.bookmark1.tags.all()]
|
||||
self.assertListEqual(tag_names, ['updated-tag-1', 'updated-tag-2'])
|
||||
|
||||
def test_patch_with_empty_payload_does_not_modify_bookmark(self):
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.patch(url, {}, expected_status_code=status.HTTP_200_OK)
|
||||
updated_bookmark = Bookmark.objects.get(id=self.bookmark1.id)
|
||||
self.assertEqual(updated_bookmark.url, self.bookmark1.url)
|
||||
self.assertEqual(updated_bookmark.title, self.bookmark1.title)
|
||||
self.assertEqual(updated_bookmark.description, self.bookmark1.description)
|
||||
self.assertListEqual(updated_bookmark.tag_names, self.bookmark1.tag_names)
|
||||
|
||||
def test_delete_bookmark(self):
|
||||
url = reverse('bookmarks:bookmark-detail', args=[self.bookmark1.id])
|
||||
self.delete(url, expected_status_code=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
@@ -10,15 +10,13 @@ from bookmarks.services import tasks
|
||||
from bookmarks.tests.helpers import BookmarkFactoryMixin, disable_logging
|
||||
|
||||
|
||||
class MockWaybackUrl:
|
||||
|
||||
class MockWaybackMachineSaveAPI:
|
||||
def __init__(self, archive_url: str):
|
||||
self.archive_url = archive_url
|
||||
|
||||
def save(self):
|
||||
return self
|
||||
|
||||
|
||||
class MockWaybackUrlWithSaveError:
|
||||
def save(self):
|
||||
raise NotImplementedError
|
||||
@@ -52,7 +50,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
def test_create_web_archive_snapshot_should_update_snapshot_url(self):
|
||||
bookmark = self.setup_bookmark()
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://example.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://example.com')):
|
||||
tasks.create_web_archive_snapshot(self.get_or_create_test_user(), bookmark, False)
|
||||
self.run_pending_task(tasks._create_web_archive_snapshot_task)
|
||||
bookmark.refresh_from_db()
|
||||
@@ -60,7 +58,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
self.assertEqual(bookmark.web_archive_snapshot_url, 'https://example.com')
|
||||
|
||||
def test_create_web_archive_snapshot_should_handle_missing_bookmark_id(self):
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://example.com')) as mock_wayback_url:
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://example.com')) as mock_wayback_url:
|
||||
tasks._create_web_archive_snapshot_task(123, False)
|
||||
self.run_pending_task(tasks._create_web_archive_snapshot_task)
|
||||
|
||||
@@ -69,7 +67,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
def test_create_web_archive_snapshot_should_handle_wayback_save_error(self):
|
||||
bookmark = self.setup_bookmark()
|
||||
|
||||
with patch.object(waybackpy, 'Url',
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI',
|
||||
return_value=MockWaybackUrlWithSaveError()):
|
||||
with self.assertRaises(NotImplementedError):
|
||||
tasks.create_web_archive_snapshot(self.get_or_create_test_user(), bookmark, False)
|
||||
@@ -78,7 +76,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
def test_create_web_archive_snapshot_should_skip_if_snapshot_exists(self):
|
||||
bookmark = self.setup_bookmark(web_archive_snapshot_url='https://example.com')
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://other.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://other.com')):
|
||||
tasks.create_web_archive_snapshot(self.get_or_create_test_user(), bookmark, False)
|
||||
self.run_pending_task(tasks._create_web_archive_snapshot_task)
|
||||
bookmark.refresh_from_db()
|
||||
@@ -88,7 +86,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
def test_create_web_archive_snapshot_should_force_update_snapshot(self):
|
||||
bookmark = self.setup_bookmark(web_archive_snapshot_url='https://example.com')
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://other.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://other.com')):
|
||||
tasks.create_web_archive_snapshot(self.get_or_create_test_user(), bookmark, True)
|
||||
self.run_pending_task(tasks._create_web_archive_snapshot_task)
|
||||
bookmark.refresh_from_db()
|
||||
@@ -117,7 +115,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
self.setup_bookmark()
|
||||
self.setup_bookmark()
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://example.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://example.com')):
|
||||
tasks.schedule_bookmarks_without_snapshots(user)
|
||||
self.run_pending_task(tasks._schedule_bookmarks_without_snapshots_task)
|
||||
self.run_all_pending_tasks(tasks._create_web_archive_snapshot_task)
|
||||
@@ -131,7 +129,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
self.setup_bookmark(web_archive_snapshot_url='https://example.com')
|
||||
self.setup_bookmark(web_archive_snapshot_url='https://example.com')
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://other.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://other.com')):
|
||||
tasks.schedule_bookmarks_without_snapshots(user)
|
||||
self.run_pending_task(tasks._schedule_bookmarks_without_snapshots_task)
|
||||
self.run_all_pending_tasks(tasks._create_web_archive_snapshot_task)
|
||||
@@ -149,7 +147,7 @@ class BookmarkTasksTestCase(TestCase, BookmarkFactoryMixin):
|
||||
self.setup_bookmark(user=other_user)
|
||||
self.setup_bookmark(user=other_user)
|
||||
|
||||
with patch.object(waybackpy, 'Url', return_value=MockWaybackUrl('https://example.com')):
|
||||
with patch.object(waybackpy, 'WaybackMachineSaveAPI', return_value=MockWaybackMachineSaveAPI('https://example.com')):
|
||||
tasks.schedule_bookmarks_without_snapshots(user)
|
||||
self.run_pending_task(tasks._schedule_bookmarks_without_snapshots_task)
|
||||
self.run_all_pending_tasks(tasks._create_web_archive_snapshot_task)
|
||||
|
||||
@@ -139,6 +139,17 @@ class ImporterTestCase(TestCase, BookmarkFactoryMixin, ImportTestMixin):
|
||||
|
||||
self.assertEqual(Tag.objects.count(), 4)
|
||||
|
||||
def test_create_missing_tags_does_not_duplicate_tags(self):
|
||||
html_tags = [
|
||||
BookmarkHtmlTag(href='https://example.com', tags='tag1'),
|
||||
BookmarkHtmlTag(href='https://foo.com', tags='tag1'),
|
||||
BookmarkHtmlTag(href='https://bar.com', tags='tag1'),
|
||||
]
|
||||
import_html = self.render_html(tags=html_tags)
|
||||
import_netscape_html(import_html, self.get_or_create_test_user())
|
||||
|
||||
self.assertEqual(Tag.objects.count(), 1)
|
||||
|
||||
def test_should_append_tags_to_bookmark_when_reimporting_with_different_tags(self):
|
||||
html_tags = [
|
||||
BookmarkHtmlTag(href='https://example.com', tags='tag1'),
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import random
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from unittest.mock import patch, Mock
|
||||
import requests
|
||||
from requests import RequestException
|
||||
|
||||
from bookmarks.tests.helpers import BookmarkFactoryMixin
|
||||
from bookmarks.models import UserProfile
|
||||
from bookmarks.tests.helpers import BookmarkFactoryMixin
|
||||
from bookmarks.views.settings import app_version, get_version_info
|
||||
|
||||
|
||||
class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
||||
@@ -38,3 +44,31 @@ class SettingsGeneralViewTestCase(TestCase, BookmarkFactoryMixin):
|
||||
self.assertEqual(self.user.profile.bookmark_date_display, form_data['bookmark_date_display'])
|
||||
self.assertEqual(self.user.profile.bookmark_link_target, form_data['bookmark_link_target'])
|
||||
self.assertEqual(self.user.profile.web_archive_integration, form_data['web_archive_integration'])
|
||||
|
||||
def test_about_shows_version_info(self):
|
||||
response = self.client.get(reverse('bookmarks:settings.general'))
|
||||
html = response.content.decode()
|
||||
|
||||
self.assertInHTML(f'''
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td>{get_version_info(random.random())}</td>
|
||||
</tr>
|
||||
''', html)
|
||||
|
||||
def test_get_version_info_just_displays_latest_when_versions_are_equal(self):
|
||||
latest_version_response_mock = Mock(status_code=201, json=lambda: {'name': f'v{app_version}'})
|
||||
with patch.object(requests, 'get', return_value=latest_version_response_mock):
|
||||
version_info = get_version_info(random.random())
|
||||
self.assertEqual(version_info, f'{app_version} (latest)')
|
||||
|
||||
def test_get_version_info_shows_latest_version_when_versions_are_not_equal(self):
|
||||
latest_version_response_mock = Mock(status_code=201, json=lambda: {'name': f'v123.0.1'})
|
||||
with patch.object(requests, 'get', return_value=latest_version_response_mock):
|
||||
version_info = get_version_info(random.random())
|
||||
self.assertEqual(version_info, f'{app_version} (latest: 123.0.1)')
|
||||
|
||||
def test_get_version_info_silently_ignores_request_errors(self):
|
||||
with patch.object(requests, 'get', side_effect=RequestException()):
|
||||
version_info = get_version_info(random.random())
|
||||
self.assertEqual(version_info, f'{app_version}')
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import logging
|
||||
import time
|
||||
from functools import lru_cache
|
||||
|
||||
import requests
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponseRedirect, HttpResponse
|
||||
@@ -17,10 +20,11 @@ logger = logging.getLogger(__name__)
|
||||
try:
|
||||
with open("version.txt", "r") as f:
|
||||
app_version = f.read().strip("\n")
|
||||
except Exception as exc:
|
||||
except Exception as exc:
|
||||
logging.exception(exc)
|
||||
pass
|
||||
|
||||
|
||||
@login_required
|
||||
def general(request):
|
||||
if request.method == 'POST':
|
||||
@@ -32,14 +36,41 @@ def general(request):
|
||||
|
||||
import_success_message = _find_message_with_tag(messages.get_messages(request), 'bookmark_import_success')
|
||||
import_errors_message = _find_message_with_tag(messages.get_messages(request), 'bookmark_import_errors')
|
||||
version_info = get_version_info(get_ttl_hash())
|
||||
return render(request, 'settings/general.html', {
|
||||
'form': form,
|
||||
'import_success_message': import_success_message,
|
||||
'import_errors_message': import_errors_message,
|
||||
'app_version': app_version
|
||||
'version_info': version_info,
|
||||
})
|
||||
|
||||
|
||||
# Cache API call response, for one hour when using get_ttl_hash with default params
|
||||
@lru_cache(maxsize=1)
|
||||
def get_version_info(ttl_hash=None):
|
||||
latest_version = None
|
||||
try:
|
||||
latest_version_url = 'https://api.github.com/repos/sissbruecker/linkding/releases/latest'
|
||||
response = requests.get(latest_version_url, timeout=5)
|
||||
json = response.json()
|
||||
latest_version = json['name'][1:]
|
||||
except requests.exceptions.RequestException:
|
||||
pass
|
||||
|
||||
latest_version_info = ''
|
||||
if latest_version == app_version:
|
||||
latest_version_info = ' (latest)'
|
||||
elif latest_version is not None:
|
||||
latest_version_info = f' (latest: {latest_version})'
|
||||
|
||||
return f'{app_version}{latest_version_info}'
|
||||
|
||||
|
||||
def get_ttl_hash(seconds=3600):
|
||||
"""Return the same value within `seconds` time period"""
|
||||
return round(time.time() / seconds)
|
||||
|
||||
|
||||
@login_required
|
||||
def integrations(request):
|
||||
application_url = request.build_absolute_uri("/bookmarks/new")
|
||||
|
||||
@@ -24,7 +24,7 @@ The following resources are available:
|
||||
GET /api/bookmarks/
|
||||
```
|
||||
|
||||
List bookmarks.
|
||||
List bookmarks.
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -65,7 +65,7 @@ Example response:
|
||||
GET /api/bookmarks/archived/
|
||||
```
|
||||
|
||||
List archived bookmarks.
|
||||
List archived bookmarks.
|
||||
|
||||
Parameters and response are the same as for the regular list endpoint.
|
||||
|
||||
@@ -83,7 +83,8 @@ Retrieves a single bookmark by ID.
|
||||
POST /api/bookmarks/
|
||||
```
|
||||
|
||||
Creates a new bookmark. Tags are simply assigned using their names.
|
||||
Creates a new bookmark. Tags are simply assigned using their names. Including
|
||||
`is_archived: true` saves a bookmark directly to the archive.
|
||||
|
||||
Example payload:
|
||||
|
||||
@@ -92,6 +93,7 @@ Example payload:
|
||||
"url": "https://example.com",
|
||||
"title": "Example title",
|
||||
"description": "Example description",
|
||||
"is_archived": false,
|
||||
"tag_names": [
|
||||
"tag1",
|
||||
"tag2"
|
||||
@@ -201,4 +203,3 @@ Example payload:
|
||||
"name": "example"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "linkding",
|
||||
"version": "1.10.1",
|
||||
"version": "1.11.1",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -22,4 +22,4 @@ supervisor==4.2.2
|
||||
typing-extensions==3.10.0.0
|
||||
urllib3==1.26.6
|
||||
uWSGI==2.0.18
|
||||
waybackpy==2.4.3
|
||||
waybackpy==3.0.6
|
||||
|
||||
@@ -28,4 +28,4 @@ soupsieve==1.9.2
|
||||
sqlparse==0.4.2
|
||||
typing-extensions==3.10.0.0
|
||||
urllib3==1.26.6
|
||||
waybackpy==2.4.3
|
||||
waybackpy==3.0.6
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.10.1
|
||||
1.11.1
|
||||
|
||||
Reference in New Issue
Block a user