Resolve PR review gaps across comments, security, feeds, and UX
All checks were successful
CI / nightly-e2e (pull_request) Has been skipped
CI / ci (pull_request) Successful in 48s

This commit is contained in:
Codex_B
2026-02-28 13:47:21 +00:00
parent 932b05cc02
commit 36ac487cbd
15 changed files with 325 additions and 7 deletions

View File

@@ -11,6 +11,10 @@ class AllArticlesFeed(Feed):
link = "/articles/"
description = "Honest AI coding tool reviews for developers."
def get_object(self, request):
self.request = request
return None
def items(self):
return ArticlePage.objects.live().order_by("-first_published_at")[:20]
@@ -27,11 +31,16 @@ class AllArticlesFeed(Feed):
return item.author.name
def item_link(self, item: ArticlePage):
return f"{settings.WAGTAILADMIN_BASE_URL}{item.url}"
if hasattr(self, "request") and self.request is not None:
full_url = item.get_full_url(self.request)
if full_url:
return full_url
return f"{settings.WAGTAILADMIN_BASE_URL.rstrip('/')}{item.url}"
class TagArticlesFeed(AllArticlesFeed):
def get_object(self, request, tag_slug: str):
self.request = request
return get_object_or_404(Tag, slug=tag_slug)
def title(self, obj):

View File

@@ -6,10 +6,10 @@ from typing import Any
from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.db import models
from django.db.models import CASCADE, PROTECT, SET_NULL
from django.db.models import CASCADE, PROTECT, SET_NULL, Prefetch
from modelcluster.contrib.taggit import ClusterTaggableManager
from modelcluster.fields import ParentalKey
from taggit.models import TaggedItemBase
from taggit.models import Tag, TaggedItemBase
from wagtail.admin.panels import FieldPanel, PageChooserPanel
from wagtail.fields import RichTextField, StreamField
from wagtail.models import Page
@@ -62,6 +62,9 @@ class ArticleIndexPage(Page):
ctx = super().get_context(request, *args, **kwargs)
tag_slug = request.GET.get("tag")
articles = self.get_articles()
available_tags = (
Tag.objects.filter(id__in=articles.values_list("tags__id", flat=True)).distinct().order_by("name")
)
if tag_slug:
articles = articles.filter(tags__slug=tag_slug)
paginator = Paginator(articles, self.ARTICLES_PER_PAGE)
@@ -75,6 +78,7 @@ class ArticleIndexPage(Page):
ctx["articles"] = page_obj
ctx["paginator"] = paginator
ctx["active_tag"] = tag_slug
ctx["available_tags"] = available_tags
return ctx
@@ -168,8 +172,11 @@ class ArticlePage(SeoMixin, Page):
def get_context(self, request, *args, **kwargs):
ctx = super().get_context(request, *args, **kwargs)
ctx["related_articles"] = self.get_related_articles()
ctx["approved_comments"] = self.comments.filter(is_approved=True, parent__isnull=True).select_related(
"parent"
from apps.comments.models import Comment
approved_replies = Comment.objects.filter(is_approved=True).select_related("parent")
ctx["approved_comments"] = self.comments.filter(is_approved=True, parent__isnull=True).prefetch_related(
Prefetch("replies", queryset=approved_replies)
)
return ctx

View File

@@ -1,4 +1,8 @@
import pytest
from django.test import override_settings
from apps.blog.models import ArticleIndexPage, ArticlePage
from apps.blog.tests.factories import AuthorFactory
@pytest.mark.django_db
@@ -6,3 +10,25 @@ def test_feed_endpoint(client):
resp = client.get("/feed/")
assert resp.status_code == 200
assert resp["Content-Type"].startswith("application/rss+xml")
@pytest.mark.django_db
@override_settings(WAGTAILADMIN_BASE_URL="http://wrong-host.example")
def test_feed_uses_request_host_for_item_links(client, home_page):
index = ArticleIndexPage(title="Articles", slug="articles")
home_page.add_child(instance=index)
author = AuthorFactory()
article = ArticlePage(
title="Feed Article",
slug="feed-article",
author=author,
summary="summary",
body=[("rich_text", "<p>Body</p>")],
)
index.add_child(instance=article)
article.save_revision().publish()
resp = client.get("/feed/")
body = resp.content.decode()
assert resp.status_code == 200
assert "http://localhost/articles/feed-article/" in body

View File

@@ -1,7 +1,9 @@
import pytest
from taggit.models import Tag
from apps.blog.models import ArticleIndexPage, ArticlePage
from apps.blog.tests.factories import AuthorFactory
from apps.comments.models import Comment
@pytest.mark.django_db
@@ -29,6 +31,7 @@ def test_article_index_pagination_and_tag_filter(client, home_page):
resp = client.get("/articles/?page=2")
assert resp.status_code == 200
assert resp.context["articles"].number == 2
assert "Pagination" in resp.content.decode()
@pytest.mark.django_db
@@ -92,3 +95,65 @@ def test_article_page_renders_share_links_and_newsletter_form(client, home_page)
assert "Share on LinkedIn" in html
assert 'data-copy-link' in html
assert 'name="source" value="article"' in html
@pytest.mark.django_db
def test_article_page_renders_approved_comments_and_reply_form(client, home_page):
index = ArticleIndexPage(title="Articles", slug="articles")
home_page.add_child(instance=index)
author = AuthorFactory()
article = ArticlePage(
title="Main",
slug="main",
author=author,
summary="summary",
body=[("rich_text", "<p>body</p>")],
)
index.add_child(instance=article)
article.save_revision().publish()
comment = Comment.objects.create(
article=article,
author_name="A",
author_email="a@example.com",
body="Top level",
is_approved=True,
)
Comment.objects.create(
article=article,
parent=comment,
author_name="B",
author_email="b@example.com",
body="Reply",
is_approved=True,
)
resp = client.get("/articles/main/")
html = resp.content.decode()
assert resp.status_code == 200
assert "Top level" in html
assert "Reply" in html
assert f'name="parent_id" value="{comment.id}"' in html
@pytest.mark.django_db
def test_article_index_renders_tag_filter_controls(client, home_page):
index = ArticleIndexPage(title="Articles", slug="articles")
home_page.add_child(instance=index)
author = AuthorFactory()
article = ArticlePage(
title="Main",
slug="main",
author=author,
summary="summary",
body=[("rich_text", "<p>body</p>")],
)
index.add_child(instance=article)
article.save_revision().publish()
tag = Tag.objects.create(name="TagOne", slug="tag-one")
article.tags.add(tag)
article.save_revision().publish()
resp = client.get("/articles/")
html = resp.content.decode()
assert resp.status_code == 200
assert "/articles/?tag=tag-one" in html