mirror of
https://github.com/sissbruecker/linkding.git
synced 2025-08-08 03:08:29 +02:00
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:
@@ -7,12 +7,21 @@ from django.db.models import Q, QuerySet, Exists, OuterRef, Case, When, CharFiel
|
||||
from django.db.models.expressions import RawSQL
|
||||
from django.db.models.functions import Lower
|
||||
|
||||
from bookmarks.models import Bookmark, BookmarkSearch, Tag, UserProfile
|
||||
from bookmarks.models import (
|
||||
Bookmark,
|
||||
BookmarkBundle,
|
||||
BookmarkSearch,
|
||||
Tag,
|
||||
UserProfile,
|
||||
parse_tag_string,
|
||||
)
|
||||
from bookmarks.utils import unique
|
||||
|
||||
|
||||
def query_bookmarks(
|
||||
user: User, profile: UserProfile, search: BookmarkSearch
|
||||
user: User,
|
||||
profile: UserProfile,
|
||||
search: BookmarkSearch,
|
||||
) -> QuerySet:
|
||||
return _base_bookmarks_query(user, profile, search).filter(is_archived=False)
|
||||
|
||||
@@ -36,8 +45,51 @@ def query_shared_bookmarks(
|
||||
return _base_bookmarks_query(user, profile, search).filter(conditions)
|
||||
|
||||
|
||||
def _filter_bundle(query_set: QuerySet, bundle: BookmarkBundle) -> QuerySet:
|
||||
# Search terms
|
||||
search_terms = parse_query_string(bundle.search)["search_terms"]
|
||||
for term in search_terms:
|
||||
conditions = (
|
||||
Q(title__icontains=term)
|
||||
| Q(description__icontains=term)
|
||||
| Q(notes__icontains=term)
|
||||
| Q(url__icontains=term)
|
||||
)
|
||||
query_set = query_set.filter(conditions)
|
||||
|
||||
# Any tags - at least one tag must match
|
||||
any_tags = parse_tag_string(bundle.any_tags, " ")
|
||||
if len(any_tags) > 0:
|
||||
tag_conditions = Q()
|
||||
for tag in any_tags:
|
||||
tag_conditions |= Q(tags__name__iexact=tag)
|
||||
|
||||
query_set = query_set.filter(
|
||||
Exists(Bookmark.objects.filter(tag_conditions, id=OuterRef("id")))
|
||||
)
|
||||
|
||||
# All tags - all tags must match
|
||||
all_tags = parse_tag_string(bundle.all_tags, " ")
|
||||
for tag in all_tags:
|
||||
query_set = query_set.filter(tags__name__iexact=tag)
|
||||
|
||||
# Excluded tags - no tags must match
|
||||
exclude_tags = parse_tag_string(bundle.excluded_tags, " ")
|
||||
if len(exclude_tags) > 0:
|
||||
tag_conditions = Q()
|
||||
for tag in exclude_tags:
|
||||
tag_conditions |= Q(tags__name__iexact=tag)
|
||||
query_set = query_set.exclude(
|
||||
Exists(Bookmark.objects.filter(tag_conditions, id=OuterRef("id")))
|
||||
)
|
||||
|
||||
return query_set
|
||||
|
||||
|
||||
def _base_bookmarks_query(
|
||||
user: Optional[User], profile: UserProfile, search: BookmarkSearch
|
||||
user: Optional[User],
|
||||
profile: UserProfile,
|
||||
search: BookmarkSearch,
|
||||
) -> QuerySet:
|
||||
query_set = Bookmark.objects
|
||||
|
||||
@@ -102,6 +154,10 @@ def _base_bookmarks_query(
|
||||
elif search.shared == BookmarkSearch.FILTER_SHARED_UNSHARED:
|
||||
query_set = query_set.filter(shared=False)
|
||||
|
||||
# Filter by bundle
|
||||
if search.bundle:
|
||||
query_set = _filter_bundle(query_set, search.bundle)
|
||||
|
||||
# Sort
|
||||
if (
|
||||
search.sort == BookmarkSearch.SORT_TITLE_ASC
|
||||
|
Reference in New Issue
Block a user