Skip to content

Commit

Permalink
Merge pull request #14 from krzysztofjeziorny/master
Browse files Browse the repository at this point in the history
1.35
  • Loading branch information
krzysztofjeziorny authored Sep 24, 2024
2 parents 4beaa76 + efc6128 commit 0f16ae7
Show file tree
Hide file tree
Showing 123 changed files with 11,654 additions and 2,884 deletions.
2 changes: 1 addition & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
"name": "Python 3",
"image": "mcr.microsoft.com/devcontainers/python:0-3.10",
"image": "mcr.microsoft.com/devcontainers/python:3.12",
"features": {
"ghcr.io/devcontainers/features/node:1": {}
},
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
- name: Set up Node
uses: actions/setup-node@v4
with:
Expand All @@ -37,7 +37,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.12"
- name: Set up Node
uses: actions/setup-node@v4
with:
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,30 @@
# Changelog

## (23/09/2024)

### What's Changed
* Add configuration options for pagination by @sissbruecker in https://github.com/sissbruecker/linkding/pull/835
* Show placeholder if there is no preview image by @sissbruecker in https://github.com/sissbruecker/linkding/pull/842
* Allow bookmarks to have empty title and description by @sissbruecker in https://github.com/sissbruecker/linkding/pull/843
* Add clear buttons in bookmark form by @sissbruecker in https://github.com/sissbruecker/linkding/pull/846
* Add basic fail2ban support by @sissbruecker in https://github.com/sissbruecker/linkding/pull/847
* Add documentation website by @sissbruecker in https://github.com/sissbruecker/linkding/pull/833
* Add go-linkding to community projects by @piero-vic in https://github.com/sissbruecker/linkding/pull/836
* Fix a broken link to options documentation by @zbrox in https://github.com/sissbruecker/linkding/pull/844
* Use HTTPS repository link for devcontainer by @voltagex in https://github.com/sissbruecker/linkding/pull/837
* Bump requests version to 3.23.3 by @voltagex in https://github.com/sissbruecker/linkding/pull/839
* Bump path-to-regexp and astro in /docs by @dependabot in https://github.com/sissbruecker/linkding/pull/840
* Bump dependencies by @sissbruecker in https://github.com/sissbruecker/linkding/pull/841

### New Contributors
* @piero-vic made their first contribution in https://github.com/sissbruecker/linkding/pull/836
* @voltagex made their first contribution in https://github.com/sissbruecker/linkding/pull/839
* @zbrox made their first contribution in https://github.com/sissbruecker/linkding/pull/844

**Full Changelog**: https://github.com/sissbruecker/linkding/compare/v1.34.0...v1.35.0

---

## v1.34.0 (16/09/2024)

### What's Changed
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ test:
format:
black bookmarks
black siteroot
npx prettier bookmarks/frontend --write
npx prettier bookmarks/frontend --write
npx prettier bookmarks/styles --write

install:
.venv/bin/pip install -r requirements.txt --upgrade
.venv/bin/pip install -r requirements.txt --upgrade
232 changes: 16 additions & 216 deletions README.md

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes
41 changes: 41 additions & 0 deletions assets/header.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes
20 changes: 8 additions & 12 deletions bookmarks/api/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,12 @@ def get_queryset(self):
return Bookmark.objects.all().filter(owner=user)

def get_serializer_context(self):
return {"request": self.request, "user": self.request.user}
disable_scraping = "disable_scraping" in self.request.GET
return {
"request": self.request,
"user": self.request.user,
"disable_scraping": disable_scraping,
}

@action(methods=["get"], detail=False)
def archived(self, request):
Expand Down Expand Up @@ -101,16 +106,7 @@ def check(self, request):
self.get_serializer(bookmark).data if bookmark else None
)

# Either return metadata from existing bookmark, or scrape from URL
if bookmark:
metadata = WebsiteMetadata(
url,
bookmark.website_title,
bookmark.website_description,
None,
)
else:
metadata = website_loader.load_website_metadata(url)
metadata = website_loader.load_website_metadata(url)

# Return tags that would be automatically applied to the bookmark
profile = request.user.profile
Expand All @@ -120,7 +116,7 @@ def check(self, request):
auto_tags = auto_tagging.get_tags(profile.auto_tagging_rules, url)
except Exception as e:
logger.error(
f"Failed to auto-tag bookmark. url={bookmark.url}",
f"Failed to auto-tag bookmark. url={url}",
exc_info=e,
)

Expand Down
88 changes: 56 additions & 32 deletions bookmarks/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
from rest_framework.serializers import ListSerializer

from bookmarks.models import Bookmark, Tag, build_tag_string, UserProfile
from bookmarks.services.bookmarks import create_bookmark, update_bookmark
from bookmarks.services.bookmarks import (
create_bookmark,
update_bookmark,
enhance_with_website_metadata,
)
from bookmarks.services.tags import get_or_create_tag


Expand All @@ -29,8 +33,6 @@ class Meta:
"title",
"description",
"notes",
"website_title",
"website_description",
"web_archive_snapshot_url",
"favicon_url",
"preview_image_url",
Expand All @@ -40,29 +42,29 @@ class Meta:
"tag_names",
"date_added",
"date_modified",
]
read_only_fields = [
"website_title",
"website_description",
]
read_only_fields = [
"web_archive_snapshot_url",
"favicon_url",
"preview_image_url",
"tag_names",
"date_added",
"date_modified",
"website_title",
"website_description",
]
list_serializer_class = BookmarkListSerializer

# 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="")
notes = serializers.CharField(required=False, allow_blank=True, default="")
is_archived = serializers.BooleanField(required=False, default=False)
unread = serializers.BooleanField(required=False, default=False)
shared = 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=[])
# Custom tag_names field to allow passing a list of tag names to create/update
tag_names = TagListField(required=False)
# Custom fields to return URLs for favicon and preview image
favicon_url = serializers.SerializerMethodField()
preview_image_url = serializers.SerializerMethodField()
# Add dummy website title and description fields for backwards compatibility but keep them empty
website_title = serializers.SerializerMethodField()
website_description = serializers.SerializerMethodField()

def get_favicon_url(self, obj: Bookmark):
if not obj.favicon_file:
Expand All @@ -80,31 +82,53 @@ def get_preview_image_url(self, obj: Bookmark):
preview_image_url = request.build_absolute_uri(preview_image_file_path)
return preview_image_url

def get_website_title(self, obj: Bookmark):
return None

def get_website_description(self, obj: Bookmark):
return None

def create(self, validated_data):
bookmark = Bookmark()
bookmark.url = validated_data["url"]
bookmark.title = validated_data["title"]
bookmark.description = validated_data["description"]
bookmark.notes = validated_data["notes"]
bookmark.is_archived = validated_data["is_archived"]
bookmark.unread = validated_data["unread"]
bookmark.shared = validated_data["shared"]
tag_string = build_tag_string(validated_data["tag_names"])
return create_bookmark(bookmark, tag_string, self.context["user"])
tag_names = validated_data.pop("tag_names", [])
tag_string = build_tag_string(tag_names)
bookmark = Bookmark(**validated_data)

saved_bookmark = create_bookmark(bookmark, tag_string, self.context["user"])
# Unless scraping is explicitly disabled, enhance bookmark with website
# metadata to preserve backwards compatibility with clients that expect
# title and description to be populated automatically when left empty
if not self.context.get("disable_scraping", False):
enhance_with_website_metadata(saved_bookmark)
return saved_bookmark

def update(self, instance: Bookmark, validated_data):
# Update fields if they were provided in the payload
for key in ["url", "title", "description", "notes", "unread", "shared"]:
if key in validated_data:
setattr(instance, key, validated_data[key])
tag_names = validated_data.pop("tag_names", instance.tag_names)
tag_string = build_tag_string(tag_names)

# 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"])
for field_name, field in self.fields.items():
if not field.read_only and field_name in validated_data:
setattr(instance, field_name, validated_data[field_name])

return update_bookmark(instance, tag_string, self.context["user"])

def validate(self, attrs):
# When creating a bookmark, the service logic prevents duplicate URLs by
# updating the existing bookmark instead. When editing a bookmark,
# there is no assumption that it would update a different bookmark if
# the URL is a duplicate, so raise a validation error in that case.
if self.instance and "url" in attrs:
is_duplicate = (
Bookmark.objects.filter(owner=self.instance.owner, url=attrs["url"])
.exclude(pk=self.instance.pk)
.exists()
)
if is_duplicate:
raise serializers.ValidationError(
{"url": "A bookmark with this URL already exists."}
)

return attrs


class TagSerializer(serializers.ModelSerializer):
class Meta:
Expand Down
3 changes: 3 additions & 0 deletions bookmarks/e2e/e2e_test_bookmark_details_modal.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ def test_delete(self):

details_modal = self.open_details_modal(bookmark)

# Wait for confirm button to be initialized
self.page.wait_for_timeout(1000)

# Delete bookmark, verify return url
with self.page.expect_navigation(url=self.live_server_url + url):
details_modal.get_by_text("Delete...").click()
Expand Down
Loading

0 comments on commit 0f16ae7

Please sign in to comment.