Skip to content

Commit e13f471

Browse files
fix: filter out unpublished pages in cms_page and news_article queries (#4578)
Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Marco Acierno <marcoacierno@users.noreply.github.com>
1 parent 4c3a02c commit e13f471

4 files changed

Lines changed: 93 additions & 2 deletions

File tree

backend/api/cms/news/queries/news_article.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def news_article(hostname: str, slug: str, language: str) -> NewsArticle | None:
1111
if not site:
1212
raise ValueError(f"Site {hostname} not found")
1313

14-
article = NewsArticleModel.objects.in_site(site).filter(slug=slug).first()
14+
article = NewsArticleModel.objects.in_site(site).filter(slug=slug, live=True).first()
1515

1616
if not article:
1717
return None

backend/api/cms/page/queries/cms_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def cms_page(
2020
if not site:
2121
return SiteNotFoundError(message=f"Site `{hostname}` not found")
2222

23-
page = GenericPageModel.objects.in_site(site).filter(slug=slug).first()
23+
page = GenericPageModel.objects.in_site(site).filter(slug=slug, live=True).first()
2424

2525
if not page:
2626
return None

backend/api/cms/tests/news/test_queries.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,53 @@ def test_cannot_get_draft_news_article(
212212
assert response["data"]["newsArticle"] is None
213213

214214

215+
def test_cannot_get_draft_base_article_even_if_translation_is_live(
216+
graphql_client,
217+
locale,
218+
):
219+
"""
220+
Test that a draft base article cannot be fetched even if a translation exists that is live.
221+
This ensures the live=True filter is applied to the initial queryset.
222+
"""
223+
user = UserFactory(full_name="marco world")
224+
parent = GenericPageFactory()
225+
# Create a draft base article (live=False)
226+
article = NewsArticleFactory(
227+
title="Article 1",
228+
parent=parent,
229+
owner=user,
230+
slug="slug",
231+
first_published_at=None,
232+
live=False,
233+
)
234+
SiteFactory(hostname="pycon", port=80, root_page=parent)
235+
236+
# Create a live Italian translation
237+
it_article = article.copy_for_translation(locale=locale("it"))
238+
it_article.title = "Article Italian"
239+
it_article.save_revision().publish()
240+
241+
query = """query NewsArticle(
242+
$hostname: String!,
243+
$slug: String!,
244+
$language: String!
245+
) {
246+
newsArticle(hostname: $hostname, slug: $slug, language: $language) {
247+
id
248+
title
249+
authorFullname
250+
}
251+
}"""
252+
253+
# Even though the Italian translation is live, the base article is draft
254+
# so it should not be fetchable
255+
response = graphql_client.query(
256+
query, variables={"hostname": "pycon", "slug": article.slug, "language": "it"}
257+
)
258+
259+
assert response["data"]["newsArticle"] is None
260+
261+
215262
def test_get_news_article_another_locale(
216263
graphql_client,
217264
locale,

backend/api/cms/tests/page/queries/test_cms_page.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,50 @@ def test_cannot_fetch_draft_pages(graphql_client, locale):
304304
assert response["data"]["cmsPage"] is None
305305

306306

307+
def test_cannot_fetch_draft_base_page_even_if_translation_is_live(graphql_client, locale):
308+
"""
309+
Test that a draft base page cannot be fetched even if a translation exists that is live.
310+
This ensures the live=True filter is applied to the initial queryset.
311+
"""
312+
parent = GenericPageFactory()
313+
# Create a draft base page (live=False)
314+
page = GenericPageFactory(
315+
slug="bubble-tea",
316+
locale=locale("en"),
317+
parent=parent,
318+
title="Bubble",
319+
body__0__text_section__title__value="I've Got a Lovely Bunch of Coconuts",
320+
live=False,
321+
)
322+
323+
SiteFactory(hostname="pycon", port=80, root_page=parent)
324+
325+
# Create a live Italian translation
326+
it_page = page.copy_for_translation(locale=locale("it"))
327+
it_page.title = "Bubble Italian"
328+
it_page.save_revision().publish()
329+
330+
query = """
331+
query Page ($hostname: String!, $language: String!, $slug: String!) {
332+
cmsPage(hostname: $hostname, language: $language, slug: $slug){
333+
...on GenericPage {
334+
title
335+
slug
336+
}
337+
}
338+
}
339+
"""
340+
341+
# Even though the Italian translation is live, the base page is draft
342+
# so it should not be fetchable
343+
response = graphql_client.query(
344+
query, variables={"hostname": "pycon", "slug": "bubble-tea", "language": "it"}
345+
)
346+
347+
assert not response.get("errors")
348+
assert response["data"]["cmsPage"] is None
349+
350+
307351
def test_page_for_unknown_locale(graphql_client, locale):
308352
parent = GenericPageFactory()
309353
page = GenericPageFactory(

0 commit comments

Comments
 (0)