mirror of
https://github.com/sissbruecker/linkding
synced 2024-11-26 13:20:23 +00:00
be789ea9e6
* Extract bookmark view contexts * Implement basic partial updates for bookmark list and tag cloud * Refactor confirm button JS into web component * Refactor bulk edit JS into web component * Refactor tag autocomplete JS into web component * Refactor bookmark page JS into web component * Refactor global shortcuts JS into web component * Update tests * Add E2E test for partial updates * Add partial updates for archived bookmarks * Add partial updates for shared bookmarks * Cleanup helpers * Improve naming in bulk edit * Refactor shared components into behaviors * Refactor bulk edit components into behaviors * Refactor bookmark list components into behaviors * Update tests * Combine all scripts into bundle * Fix E2E CI
168 lines
6.4 KiB
Python
168 lines
6.4 KiB
Python
import urllib.parse
|
|
from typing import Set, List
|
|
|
|
from django.core.handlers.wsgi import WSGIRequest
|
|
from django.core.paginator import Paginator
|
|
from django.db import models
|
|
from django.urls import reverse
|
|
|
|
from bookmarks import queries
|
|
from bookmarks.models import BookmarkFilters, User, UserProfile, Tag
|
|
from bookmarks.utils import unique
|
|
|
|
DEFAULT_PAGE_SIZE = 30
|
|
|
|
|
|
class BookmarkListContext:
|
|
def __init__(self, request: WSGIRequest) -> None:
|
|
self.request = request
|
|
self.filters = BookmarkFilters(self.request)
|
|
|
|
query_set = self.get_bookmark_query_set()
|
|
page_number = request.GET.get('page')
|
|
paginator = Paginator(query_set, DEFAULT_PAGE_SIZE)
|
|
bookmarks_page = paginator.get_page(page_number)
|
|
# Prefetch related objects, this avoids n+1 queries when accessing fields in templates
|
|
models.prefetch_related_objects(bookmarks_page.object_list, 'owner', 'tags')
|
|
|
|
self.is_empty = paginator.count == 0
|
|
self.bookmarks_page = bookmarks_page
|
|
self.return_url = self.generate_return_url(page_number)
|
|
self.link_target = request.user_profile.bookmark_link_target
|
|
self.date_display = request.user_profile.bookmark_date_display
|
|
self.show_url = request.user_profile.display_url
|
|
self.show_favicons = request.user_profile.enable_favicons
|
|
self.show_notes = request.user_profile.permanent_notes
|
|
|
|
def generate_return_url(self, page: int):
|
|
base_url = self.get_base_url()
|
|
url_query = {}
|
|
if self.filters.query:
|
|
url_query['q'] = self.filters.query
|
|
if self.filters.user:
|
|
url_query['user'] = self.filters.user
|
|
if page is not None:
|
|
url_query['page'] = page
|
|
url_params = urllib.parse.urlencode(url_query)
|
|
return_url = base_url if url_params == '' else base_url + '?' + url_params
|
|
return urllib.parse.quote_plus(return_url)
|
|
|
|
def get_base_url(self):
|
|
raise Exception(f'Must be implemented by subclass')
|
|
|
|
def get_bookmark_query_set(self):
|
|
raise Exception(f'Must be implemented by subclass')
|
|
|
|
|
|
class ActiveBookmarkListContext(BookmarkListContext):
|
|
def get_base_url(self):
|
|
return reverse('bookmarks:index')
|
|
|
|
def get_bookmark_query_set(self):
|
|
return queries.query_bookmarks(self.request.user,
|
|
self.request.user_profile,
|
|
self.filters.query)
|
|
|
|
|
|
class ArchivedBookmarkListContext(BookmarkListContext):
|
|
def get_base_url(self):
|
|
return reverse('bookmarks:archived')
|
|
|
|
def get_bookmark_query_set(self):
|
|
return queries.query_archived_bookmarks(self.request.user,
|
|
self.request.user_profile,
|
|
self.filters.query)
|
|
|
|
|
|
class SharedBookmarkListContext(BookmarkListContext):
|
|
def get_base_url(self):
|
|
return reverse('bookmarks:shared')
|
|
|
|
def get_bookmark_query_set(self):
|
|
user = User.objects.filter(username=self.filters.user).first()
|
|
public_only = not self.request.user.is_authenticated
|
|
return queries.query_shared_bookmarks(user,
|
|
self.request.user_profile,
|
|
self.filters.query,
|
|
public_only)
|
|
|
|
|
|
class TagGroup:
|
|
def __init__(self, char: str):
|
|
self.tags = []
|
|
self.char = char
|
|
|
|
@staticmethod
|
|
def create_tag_groups(tags: Set[Tag]):
|
|
# Ensure groups, as well as tags within groups, are ordered alphabetically
|
|
sorted_tags = sorted(tags, key=lambda x: str.lower(x.name))
|
|
group = None
|
|
groups = []
|
|
|
|
# Group tags that start with a different character than the previous one
|
|
for tag in sorted_tags:
|
|
tag_char = tag.name[0].lower()
|
|
|
|
if not group or group.char != tag_char:
|
|
group = TagGroup(tag_char)
|
|
groups.append(group)
|
|
|
|
group.tags.append(tag)
|
|
|
|
return groups
|
|
|
|
|
|
class TagCloudContext:
|
|
def __init__(self, request: WSGIRequest) -> None:
|
|
self.request = request
|
|
self.filters = BookmarkFilters(self.request)
|
|
|
|
query_set = self.get_tag_query_set()
|
|
tags = list(query_set)
|
|
selected_tags = self.get_selected_tags(tags)
|
|
unique_tags = unique(tags, key=lambda x: str.lower(x.name))
|
|
unique_selected_tags = unique(selected_tags, key=lambda x: str.lower(x.name))
|
|
has_selected_tags = len(unique_selected_tags) > 0
|
|
unselected_tags = set(unique_tags).symmetric_difference(unique_selected_tags)
|
|
groups = TagGroup.create_tag_groups(unselected_tags)
|
|
|
|
self.tags = unique_tags
|
|
self.groups = groups
|
|
self.selected_tags = unique_selected_tags
|
|
self.has_selected_tags = has_selected_tags
|
|
|
|
def get_tag_query_set(self):
|
|
raise Exception(f'Must be implemented by subclass')
|
|
|
|
def get_selected_tags(self, tags: List[Tag]):
|
|
parsed_query = queries.parse_query_string(self.filters.query)
|
|
tag_names = parsed_query['tag_names']
|
|
if self.request.user_profile.tag_search == UserProfile.TAG_SEARCH_LAX:
|
|
tag_names = tag_names + parsed_query['search_terms']
|
|
tag_names = [tag_name.lower() for tag_name in tag_names]
|
|
|
|
return [tag for tag in tags if tag.name.lower() in tag_names]
|
|
|
|
|
|
class ActiveTagCloudContext(TagCloudContext):
|
|
def get_tag_query_set(self):
|
|
return queries.query_bookmark_tags(self.request.user,
|
|
self.request.user_profile,
|
|
self.filters.query)
|
|
|
|
|
|
class ArchivedTagCloudContext(TagCloudContext):
|
|
def get_tag_query_set(self):
|
|
return queries.query_archived_bookmark_tags(self.request.user,
|
|
self.request.user_profile,
|
|
self.filters.query)
|
|
|
|
|
|
class SharedTagCloudContext(TagCloudContext):
|
|
def get_tag_query_set(self):
|
|
user = User.objects.filter(username=self.filters.user).first()
|
|
public_only = not self.request.user.is_authenticated
|
|
return queries.query_shared_bookmark_tags(user,
|
|
self.request.user_profile,
|
|
self.filters.query,
|
|
public_only)
|