Source code for users.feeds
"""RSS and Atom feeds for the users app."""
from typing import TYPE_CHECKING, Iterable
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.syndication.views import Feed
from django.db.models.expressions import Subquery
from django.http import HttpResponse
from django.utils import timezone as tz
from django.utils.cache import patch_vary_headers
from django.utils.feedgenerator import Atom1Feed
from django.utils.http import http_date
from MangAdventure.utils import HttpResponseUnauthorized
from reader.models import Chapter
if TYPE_CHECKING: # pragma: no cover
from datetime import datetime # isort:skip
from django.http import HttpRequest # isort:skip
[docs]class BookmarksRSS(Feed):
"""RSS feed for a user's bookmarks."""
ttl = 600
link = '/user/bookmarks/'
author_name = settings.CONFIG['NAME']
title = f'Bookmarks - {author_name}'
description = 'Updates when a bookmarked series has a new release'
item_guid_is_permalink = True
[docs] def __call__(self, request: 'HttpRequest', *args, **kwargs) -> HttpResponse:
"""
Get the HTTP response of the feed.
:param request: The original request.
:return: | A :status:`401` response if the token is missing.
| A :status:`403` response if the provided token is invalid.
| A :status:`200` response with the bookmarks feed otherwise.
"""
token = request.GET.get('token')
if not token:
header = request.META.get('HTTP_AUTHORIZATION')
if not header:
return HttpResponseUnauthorized(
b'A token is required to access the feed.',
content_type='text/plain', realm='bookmarks feed'
)
if header[:7] != 'Bearer ':
return HttpResponse(
b'Authorization header format is invalid.',
status=403, content_type='text/plain'
)
token = header[7:]
try:
obj = User.objects.get(profile__token=token)
except User.DoesNotExist:
return HttpResponse(
b'The provided token is invalid.',
status=403, content_type='text/plain'
)
feedgen = self.get_feed(obj, request)
res = HttpResponse(content_type=feedgen.content_type)
patch_vary_headers(res, ('Authorization',))
res['Last-Modified'] = http_date(
feedgen.latest_post_date().timestamp()
)
feedgen.write(res, 'utf-8')
return res
[docs] def feed_url(self, obj: User) -> str:
"""
Get the feed's own URL.
:param obj: The object of the feed.
:return: The feed's URL.
"""
return '/user/bookmarks.rss?token=' + obj.profile.token
[docs] def items(self, obj: User) -> Iterable[Chapter]:
"""
Get an iterable of the feed's items.
:param obj: The object of the feed.
:return: An iterable of ``Chapter`` objects.
"""
return Chapter.objects.filter(
published__lte=tz.now(), id__in=Subquery(
obj.bookmarks.values('series__chapters')
)
).select_related('series').order_by('-published')
[docs] def item_description(self, item: Chapter) -> str:
"""
Get the description of the item.
:param item: A ``Chapter`` object.
:return: The ``Chapter`` object as a string.
"""
desc = str(item)
if settings.CONFIG['ALLOW_DLS']:
domain = settings.CONFIG["DOMAIN"]
scheme = settings.ACCOUNT_DEFAULT_HTTP_PROTOCOL
url = item.get_absolute_url()[:-1] + '.cbz'
desc = f'<a href="{scheme}://{domain}{url}">{desc}</a>'
return desc
[docs] def item_pubdate(self, item: Chapter) -> 'datetime':
"""
Get the publication date of the item.
:param item: A ``Chapter`` object.
:return: The date the chapter was published.
"""
return item.published
[docs] def item_updateddate(self, item: Chapter) -> 'datetime':
"""
Get the update date of the item.
:param item: A ``Chapter`` object.
:return: The date the chapter was modified.
"""
return item.modified
[docs]class BookmarksAtom(BookmarksRSS):
"""Atom feed for a user's bookmarks."""
feed_type = Atom1Feed
subtitle = BookmarksRSS.description
[docs] def feed_url(self, obj: User) -> str:
"""
Get the feed's own URL.
:param obj: The object of the feed.
:return: The feed's URL.
"""
return '/user/bookmarks.atom?token=' + obj.profile.token
__all__ = ['BookmarksRSS', 'BookmarksAtom']