2023-10-27 17:59:06 +00:00
|
|
|
import unicodedata
|
2022-07-23 21:20:27 +00:00
|
|
|
from dataclasses import dataclass
|
|
|
|
|
|
|
|
from django.contrib.syndication.views import Feed
|
2024-08-31 20:41:22 +00:00
|
|
|
from django.db.models import QuerySet, prefetch_related_objects
|
|
|
|
from django.http import HttpRequest
|
2022-07-23 21:20:27 +00:00
|
|
|
from django.urls import reverse
|
|
|
|
|
|
|
|
from bookmarks import queries
|
2024-03-17 10:55:34 +00:00
|
|
|
from bookmarks.models import Bookmark, BookmarkSearch, FeedToken, UserProfile
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class FeedContext:
|
2024-08-31 20:41:22 +00:00
|
|
|
request: HttpRequest
|
2024-03-17 10:55:34 +00:00
|
|
|
feed_token: FeedToken | None
|
2022-07-23 21:20:27 +00:00
|
|
|
query_set: QuerySet[Bookmark]
|
|
|
|
|
|
|
|
|
2023-10-27 17:59:06 +00:00
|
|
|
def sanitize(text: str):
|
2023-11-04 08:56:06 +00:00
|
|
|
if not text:
|
2024-01-27 10:29:16 +00:00
|
|
|
return ""
|
2023-10-27 17:59:06 +00:00
|
|
|
# remove control characters
|
2024-01-27 10:29:16 +00:00
|
|
|
valid_chars = ["\n", "\r", "\t"]
|
|
|
|
return "".join(
|
|
|
|
ch for ch in text if ch in valid_chars or unicodedata.category(ch)[0] != "C"
|
|
|
|
)
|
2023-10-27 17:59:06 +00:00
|
|
|
|
|
|
|
|
2022-07-23 21:20:27 +00:00
|
|
|
class BaseBookmarksFeed(Feed):
|
2024-08-31 20:41:22 +00:00
|
|
|
def get_object(self, request, feed_key: str | None):
|
|
|
|
feed_token = FeedToken.objects.get(key__exact=feed_key) if feed_key else None
|
2024-08-31 20:58:41 +00:00
|
|
|
search = BookmarkSearch(
|
|
|
|
q=request.GET.get("q", ""),
|
|
|
|
unread=request.GET.get("unread", ""),
|
|
|
|
shared=request.GET.get("shared", ""),
|
|
|
|
)
|
2024-08-31 20:41:22 +00:00
|
|
|
query_set = self.get_query_set(feed_token, search)
|
|
|
|
return FeedContext(request, feed_token, query_set)
|
|
|
|
|
|
|
|
def get_query_set(self, feed_token: FeedToken, search: BookmarkSearch):
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
|
|
def items(self, context: FeedContext):
|
|
|
|
limit = context.request.GET.get("limit", 100)
|
|
|
|
if limit:
|
|
|
|
data = context.query_set[: int(limit)]
|
|
|
|
else:
|
|
|
|
data = list(context.query_set)
|
|
|
|
prefetch_related_objects(data, "tags")
|
|
|
|
return data
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
def item_title(self, item: Bookmark):
|
2023-10-27 17:59:06 +00:00
|
|
|
return sanitize(item.resolved_title)
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
def item_description(self, item: Bookmark):
|
2023-10-27 17:59:06 +00:00
|
|
|
return sanitize(item.resolved_description)
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
def item_link(self, item: Bookmark):
|
|
|
|
return item.url
|
|
|
|
|
|
|
|
def item_pubdate(self, item: Bookmark):
|
|
|
|
return item.date_added
|
|
|
|
|
2024-08-31 20:41:22 +00:00
|
|
|
def item_categories(self, item: Bookmark):
|
|
|
|
return item.tag_names
|
|
|
|
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
class AllBookmarksFeed(BaseBookmarksFeed):
|
2024-01-27 10:29:16 +00:00
|
|
|
title = "All bookmarks"
|
|
|
|
description = "All bookmarks"
|
2022-07-23 21:20:27 +00:00
|
|
|
|
2024-08-31 20:41:22 +00:00
|
|
|
def get_query_set(self, feed_token: FeedToken, search: BookmarkSearch):
|
|
|
|
return queries.query_bookmarks(feed_token.user, feed_token.user.profile, search)
|
|
|
|
|
2022-07-23 21:20:27 +00:00
|
|
|
def link(self, context: FeedContext):
|
2024-01-27 10:29:16 +00:00
|
|
|
return reverse("bookmarks:feeds.all", args=[context.feed_token.key])
|
2022-07-23 21:20:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
class UnreadBookmarksFeed(BaseBookmarksFeed):
|
2024-01-27 10:29:16 +00:00
|
|
|
title = "Unread bookmarks"
|
|
|
|
description = "All unread bookmarks"
|
2022-07-23 21:20:27 +00:00
|
|
|
|
2024-08-31 20:41:22 +00:00
|
|
|
def get_query_set(self, feed_token: FeedToken, search: BookmarkSearch):
|
|
|
|
return queries.query_bookmarks(
|
|
|
|
feed_token.user, feed_token.user.profile, search
|
|
|
|
).filter(unread=True)
|
|
|
|
|
2022-07-23 21:20:27 +00:00
|
|
|
def link(self, context: FeedContext):
|
2024-01-27 10:29:16 +00:00
|
|
|
return reverse("bookmarks:feeds.unread", args=[context.feed_token.key])
|
2022-07-23 21:20:27 +00:00
|
|
|
|
2024-03-17 10:55:34 +00:00
|
|
|
|
|
|
|
class SharedBookmarksFeed(BaseBookmarksFeed):
|
|
|
|
title = "Shared bookmarks"
|
|
|
|
description = "All shared bookmarks"
|
|
|
|
|
2024-08-31 20:41:22 +00:00
|
|
|
def get_query_set(self, feed_token: FeedToken, search: BookmarkSearch):
|
|
|
|
return queries.query_shared_bookmarks(
|
2024-03-17 10:55:34 +00:00
|
|
|
None, feed_token.user.profile, search, False
|
|
|
|
)
|
|
|
|
|
|
|
|
def link(self, context: FeedContext):
|
|
|
|
return reverse("bookmarks:feeds.shared", args=[context.feed_token.key])
|
|
|
|
|
|
|
|
|
|
|
|
class PublicSharedBookmarksFeed(BaseBookmarksFeed):
|
|
|
|
title = "Public shared bookmarks"
|
|
|
|
description = "All public shared bookmarks"
|
|
|
|
|
|
|
|
def get_object(self, request):
|
2024-08-31 20:41:22 +00:00
|
|
|
return super().get_object(request, None)
|
|
|
|
|
|
|
|
def get_query_set(self, feed_token: FeedToken, search: BookmarkSearch):
|
|
|
|
return queries.query_shared_bookmarks(None, UserProfile(), search, True)
|
2024-03-17 10:55:34 +00:00
|
|
|
|
|
|
|
def link(self, context: FeedContext):
|
|
|
|
return reverse("bookmarks:feeds.public_shared")
|