Merge pull request #2610 from Giebisch/rss-feed

Add RSS feed for reviews, quotes, comments only
This commit is contained in:
Hugh Rundle 2023-01-25 20:04:34 +11:00 committed by GitHub
commit 578de27515
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 227 additions and 5 deletions

View file

@ -70,10 +70,52 @@
<h2 class="title column">{% trans "User Activity" %}</h2>
{% if user.local %}
<div class="column is-narrow">
<a target="_blank" href="{{ user.local_path }}/rss" rel="nofollow noopener noreferrer">
<span class="icon icon-rss" aria-hidden="true"></span>
<span class="is-hidden-mobile">{% trans "RSS feed" %}</span>
</a>
<details class="dropdown">
<summary
class="is-relative pulldown-menu dropdown-trigger"
aria-label="{% trans 'Show RSS Options' %}"
role="button"
aria-haspopup="menu"
>
<span class="">
<span class="icon icon-rss" aria-hidden="true"></span>
<span class="">{% trans "RSS feed" %}</span>
</span>
<span class="icon icon-arrow-down is-hidden-mobile" aria-hidden="true"></span>
<span class="summary-on-open">
<span class="icon icon-arrow-left is-small" aria-hidden="true"></span>
{% trans "Back" %}
</span>
</summary>
<div class="dropdown-menu">
<ul
class="dropdown-content"
role="menu"
>
<li role="menuitem">
<a target="_blank" href="{{ user.local_path }}/rss" class="navbar-item" rel="nofollow noopener noreferrer">
{% trans "Complete feed" %}
</a>
</li>
<li role="menuitem">
<a target="_blank" href="{{ user.local_path }}/rss-reviews" class="navbar-item" rel="nofollow noopener noreferrer">
{% trans "Reviews only" %}
</a>
</li>
<li role="menuitem">
<a target="_blank" href="{{ user.local_path }}/rss-quotes" class="navbar-item" rel="nofollow noopener noreferrer">
{% trans "Quotes only" %}
</a>
</li>
<li role="menuitem">
<a target="_blank" href="{{ user.local_path }}/rss-comments" class="navbar-item" rel="nofollow noopener noreferrer">
{% trans "Comments only" %}
</a>
</li>
</ul>
</div>
</details>
</div>
{% endif %}
</div>

View file

@ -82,3 +82,48 @@ class RssFeedView(TestCase):
self.assertEqual(result.status_code, 200)
self.assertIn(b"a sickening sense", result.content)
def test_rss_comment_only(self, *_):
"""load an rss feed"""
models.Comment.objects.create(
content="comment test content",
user=self.local_user,
book=self.book,
)
view = rss_feed.RssCommentsOnlyFeed()
request = self.factory.get("/user/rss_user/rss")
request.user = self.local_user
result = view(request, username=self.local_user.username)
self.assertEqual(result.status_code, 200)
self.assertIn(b"Example Edition", result.content)
def test_rss_review_only(self, *_):
"""load an rss feed"""
models.Review.objects.create(
name="Review name",
content="test content",
rating=3,
user=self.local_user,
book=self.book,
)
view = rss_feed.RssReviewsOnlyFeed()
request = self.factory.get("/user/rss_user/rss")
request.user = self.local_user
result = view(request, username=self.local_user.username)
self.assertEqual(result.status_code, 200)
def test_rss_quotation_only(self, *_):
"""load an rss feed"""
models.Quotation.objects.create(
quote="a sickening sense",
content="test content",
user=self.local_user,
book=self.book,
)
view = rss_feed.RssQuotesOnlyFeed()
request = self.factory.get("/user/rss_user/rss")
request.user = self.local_user
result = view(request, username=self.local_user.username)
self.assertEqual(result.status_code, 200)
self.assertIn(b"a sickening sense", result.content)

View file

@ -424,6 +424,21 @@ urlpatterns = [
re_path(rf"{USER_PATH}/?$", views.User.as_view(), name="user-feed"),
re_path(rf"^@(?P<username>{regex.USERNAME})$", views.user_redirect),
re_path(rf"{USER_PATH}/rss/?$", views.rss_feed.RssFeed(), name="user-rss"),
re_path(
rf"{USER_PATH}/rss-reviews/?$",
views.rss_feed.RssReviewsOnlyFeed(),
name="user-reviews-rss",
),
re_path(
rf"{USER_PATH}/rss-quotes/?$",
views.rss_feed.RssQuotesOnlyFeed(),
name="user-quotes-rss",
),
re_path(
rf"{USER_PATH}/rss-comments/?$",
views.rss_feed.RssCommentsOnlyFeed(),
name="user-comments-rss",
),
re_path(
rf"{USER_PATH}/(?P<direction>(followers|following))(.json)?/?$",
views.Relationships.as_view(),

View file

@ -137,7 +137,12 @@ from .outbox import Outbox
from .reading import ReadThrough, delete_readthrough, delete_progressupdate
from .reading import ReadingStatus
from .report import Report
from .rss_feed import RssFeed
from .rss_feed import (
RssFeed,
RssReviewsOnlyFeed,
RssQuotesOnlyFeed,
RssCommentsOnlyFeed,
)
from .search import Search
from .setup import InstanceConfig, CreateAdmin
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress

View file

@ -3,6 +3,7 @@
from django.contrib.syndication.views import Feed
from django.template.loader import get_template
from django.utils.translation import gettext_lazy as _
from ..models import Review, Quotation, Comment
from .helpers import get_user_from_username
@ -42,3 +43,117 @@ class RssFeed(Feed):
def item_link(self, item):
"""link to the status"""
return item.local_path
class RssReviewsOnlyFeed(Feed):
"""serialize user's reviews in rss feed"""
description_template = "rss/content.html"
def item_title(self, item):
"""render the item title"""
if hasattr(item, "pure_name") and item.pure_name:
return item.pure_name
title_template = get_template("snippets/status/header_content.html")
title = title_template.render({"status": item})
template = get_template("rss/title.html")
return template.render({"user": item.user, "item_title": title}).strip()
def get_object(self, request, username): # pylint: disable=arguments-differ
"""the user who's posts get serialized"""
return get_user_from_username(request.user, username)
def link(self, obj):
"""link to the user's profile"""
return obj.local_path
def title(self, obj):
"""title of the rss feed entry"""
return _(f"Reviews from {obj.display_name}")
def items(self, obj):
"""the user's activity feed"""
return Review.objects.filter(
user=obj,
privacy__in=["public", "unlisted"],
)[:10]
def item_link(self, item):
"""link to the status"""
return item.local_path
class RssQuotesOnlyFeed(Feed):
"""serialize user's quotes in rss feed"""
description_template = "rss/content.html"
def item_title(self, item):
"""render the item title"""
if hasattr(item, "pure_name") and item.pure_name:
return item.pure_name
title_template = get_template("snippets/status/header_content.html")
title = title_template.render({"status": item})
template = get_template("rss/title.html")
return template.render({"user": item.user, "item_title": title}).strip()
def get_object(self, request, username): # pylint: disable=arguments-differ
"""the user who's posts get serialized"""
return get_user_from_username(request.user, username)
def link(self, obj):
"""link to the user's profile"""
return obj.local_path
def title(self, obj):
"""title of the rss feed entry"""
return _(f"Quotes from {obj.display_name}")
def items(self, obj):
"""the user's activity feed"""
return Quotation.objects.filter(
user=obj,
privacy__in=["public", "unlisted"],
)[:10]
def item_link(self, item):
"""link to the status"""
return item.local_path
class RssCommentsOnlyFeed(Feed):
"""serialize user's quotes in rss feed"""
description_template = "rss/content.html"
def item_title(self, item):
"""render the item title"""
if hasattr(item, "pure_name") and item.pure_name:
return item.pure_name
title_template = get_template("snippets/status/header_content.html")
title = title_template.render({"status": item})
template = get_template("rss/title.html")
return template.render({"user": item.user, "item_title": title}).strip()
def get_object(self, request, username): # pylint: disable=arguments-differ
"""the user who's posts get serialized"""
return get_user_from_username(request.user, username)
def link(self, obj):
"""link to the user's profile"""
return obj.local_path
def title(self, obj):
"""title of the rss feed entry"""
return _(f"Comments from {obj.display_name}")
def items(self, obj):
"""the user's activity feed"""
return Comment.objects.filter(
user=obj,
privacy__in=["public", "unlisted"],
)[:10]
def item_link(self, item):
"""link to the status"""
return item.local_path