Add bundles for organizing bookmarks (#1097)

* add bundle model and query logic

* cleanup tests

* add basic form

* add success message

* Add form tests

* Add bundle list view

* fix edit view

* Add remove button

* Add basic preview logic

* Make pagination use absolute URLs

* Hide bookmark edits when rendering preview

* Render bookmark list in preview

* Reorder bundles

* Show bundles in bookmark view

* Make bookmark search respect selected bundle

* UI tweaks

* Fix bookmark scope

* Improve bundle preview

* Skip preview if form is submitted

* Show correct preview after invalid form submission

* Add option to hide bundles

* Merge new migrations

* Add tests for bundle menu

* Improve check for preview being removed
This commit is contained in:
Sascha Ißbrücker
2025-06-19 16:47:29 +02:00
committed by GitHub
parent 8be72a5d1f
commit 1672dc0152
59 changed files with 2290 additions and 267 deletions

View File

@@ -9,7 +9,6 @@ from bookmarks.tests.helpers import (
BookmarkFactoryMixin,
BookmarkListTestMixin,
TagCloudTestMixin,
collapse_whitespace,
)
@@ -60,7 +59,23 @@ class BookmarkArchivedViewTestCase(
)
response = self.client.get(reverse("linkding:bookmarks.archived") + "?q=foo")
html = collapse_whitespace(response.content.decode())
self.assertVisibleBookmarks(response, visible_bookmarks)
self.assertInvisibleBookmarks(response, invisible_bookmarks)
def test_should_list_bookmarks_matching_bundle(self):
visible_bookmarks = self.setup_numbered_bookmarks(
3, prefix="foo", archived=True
)
invisible_bookmarks = self.setup_numbered_bookmarks(
3, prefix="bar", archived=True
)
bundle = self.setup_bundle(search="foo")
response = self.client.get(
reverse("linkding:bookmarks.archived") + f"?bundle={bundle.id}"
)
self.assertVisibleBookmarks(response, visible_bookmarks)
self.assertInvisibleBookmarks(response, invisible_bookmarks)
@@ -105,6 +120,26 @@ class BookmarkArchivedViewTestCase(
self.assertVisibleTags(response, visible_tags)
self.assertInvisibleTags(response, invisible_tags)
def test_should_list_tags_for_bookmarks_matching_bundle(self):
visible_bookmarks = self.setup_numbered_bookmarks(
3, with_tags=True, archived=True, prefix="foo", tag_prefix="foo"
)
invisible_bookmarks = self.setup_numbered_bookmarks(
3, with_tags=True, archived=True, prefix="bar", tag_prefix="bar"
)
visible_tags = self.get_tags_from_bookmarks(visible_bookmarks)
invisible_tags = self.get_tags_from_bookmarks(invisible_bookmarks)
bundle = self.setup_bundle(search="foo")
response = self.client.get(
reverse("linkding:bookmarks.archived") + f"?bundle={bundle.id}"
)
self.assertVisibleTags(response, visible_tags)
self.assertInvisibleTags(response, invisible_tags)
def test_should_list_bookmarks_and_tags_for_search_preferences(self):
user_profile = self.user.profile
user_profile.search_preferences = {
@@ -515,3 +550,20 @@ class BookmarkArchivedViewTestCase(
feed = soup.select_one('head link[type="application/rss+xml"]')
self.assertIsNone(feed)
def test_hide_bundles_when_enabled_in_profile(self):
# visible by default
response = self.client.get(reverse("linkding:bookmarks.archived"))
html = response.content.decode()
self.assertInHTML('<h2 id="bundles-heading">Bundles</h2>', html)
# hidden when disabled in profile
user_profile = self.get_or_create_test_user().profile
user_profile.hide_bundles = True
user_profile.save()
response = self.client.get(reverse("linkding:bookmarks.archived"))
html = response.content.decode()
self.assertInHTML('<h2 id="bundles-heading">Bundles</h2>', html, count=0)