Skip to content

Commit b2a5462

Browse files
authored
Only return published blog entries. (#1757)
1 parent 337100a commit b2a5462

File tree

2 files changed

+185
-5
lines changed

2 files changed

+185
-5
lines changed

blog/tests.py

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from io import StringIO
44

55
import time_machine
6-
from django.contrib.auth.models import User
6+
from django.contrib.auth.models import Permission, User
7+
from django.contrib.contenttypes.models import ContentType
78
from django.core.files.base import ContentFile
89
from django.test import TestCase
910
from django.urls import reverse
@@ -187,6 +188,72 @@ def test_past_future_ordering(self):
187188

188189

189190
class ViewsTestCase(DateTimeMixin, TestCase):
191+
def test_staff_with_change_permission_can_see_unpublished_detail_view(self):
192+
"""
193+
Staff users with change permission on BlogEntry can't see unpublished entries
194+
in the list, but can view the detail page
195+
"""
196+
e1 = Entry.objects.create(
197+
pub_date=self.yesterday, is_active=False, headline="inactive", slug="a"
198+
)
199+
user = User.objects.create(username="staff", is_staff=True)
200+
# Add blog entry change permission
201+
202+
content_type = ContentType.objects.get_for_model(Entry)
203+
change_permission = Permission.objects.get(
204+
content_type=content_type, codename="change_entry"
205+
)
206+
user.user_permissions.add(change_permission)
207+
self.client.force_login(user)
208+
self.assertEqual(Entry.objects.all().count(), 1)
209+
response = self.client.get(reverse("weblog:index"))
210+
self.assertEqual(response.status_code, 404)
211+
212+
response = self.client.get(
213+
reverse(
214+
"weblog:entry",
215+
kwargs={
216+
"year": e1.pub_date.year,
217+
"month": e1.pub_date.strftime("%b").lower(),
218+
"day": e1.pub_date.day,
219+
"slug": e1.slug,
220+
},
221+
)
222+
)
223+
request = response.context["request"]
224+
self.assertTrue(request.user.is_staff)
225+
self.assertTrue(request.user.has_perm("blog.change_entry"))
226+
self.assertEqual(response.status_code, 200)
227+
228+
def test_staff_without_change_permission_cannot_see_unpublished_detail_view(self):
229+
"""
230+
Staff users without change permission on BlogEntry can't see unpublished entries
231+
"""
232+
e1 = Entry.objects.create(
233+
pub_date=self.yesterday, is_active=False, headline="inactive", slug="a"
234+
)
235+
user = User.objects.create(username="staff-no-perm", is_staff=True)
236+
# No permissions added
237+
self.client.force_login(user)
238+
self.assertEqual(Entry.objects.all().count(), 1)
239+
240+
# Test detail view for unpublished entry - should return 404
241+
response = self.client.get(
242+
reverse(
243+
"weblog:entry",
244+
kwargs={
245+
"year": e1.pub_date.year,
246+
"month": e1.pub_date.strftime("%b").lower(),
247+
"day": e1.pub_date.day,
248+
"slug": e1.slug,
249+
},
250+
)
251+
)
252+
request = response.context["request"]
253+
self.assertTrue(request.user.is_staff)
254+
self.assertFalse(request.user.has_perm("blog.change_entry"))
255+
self.assertEqual(response.status_code, 404)
256+
190257
def test_no_past_upcoming_events(self):
191258
"""
192259
Make sure there are no past event in the "upcoming events" sidebar (#399)
@@ -232,6 +299,113 @@ def test_no_unpublished_future_events(self):
232299
self.assertEqual(response.status_code, 200)
233300
self.assertQuerySetEqual(response.context["events"], [])
234301

302+
def test_anonymous_user_cannot_see_unpublished_entries(self):
303+
"""
304+
Anonymous users can't see unpublished entries at all (list or detail view)
305+
"""
306+
# Create a published entry to ensure the list view works
307+
published_entry = Entry.objects.create(
308+
pub_date=self.yesterday,
309+
is_active=True,
310+
headline="published",
311+
slug="published",
312+
)
313+
314+
# Create an unpublished entry
315+
unpublished_entry = Entry.objects.create(
316+
pub_date=self.tomorrow,
317+
is_active=True,
318+
headline="unpublished",
319+
slug="unpublished",
320+
)
321+
322+
# Test list view - should return 200 but not include the unpublished entry
323+
response = self.client.get(reverse("weblog:index"))
324+
self.assertEqual(response.status_code, 200)
325+
self.assertContains(response, "published")
326+
self.assertNotContains(response, "unpublished")
327+
328+
# Test detail view for unpublished entry - should return 404
329+
unpublished_url = reverse(
330+
"weblog:entry",
331+
kwargs={
332+
"year": unpublished_entry.pub_date.year,
333+
"month": unpublished_entry.pub_date.strftime("%b").lower(),
334+
"day": unpublished_entry.pub_date.day,
335+
"slug": unpublished_entry.slug,
336+
},
337+
)
338+
response = self.client.get(unpublished_url)
339+
self.assertEqual(response.status_code, 404)
340+
341+
# Test detail view for published entry - should return 200
342+
published_url = reverse(
343+
"weblog:entry",
344+
kwargs={
345+
"year": published_entry.pub_date.year,
346+
"month": published_entry.pub_date.strftime("%b").lower(),
347+
"day": published_entry.pub_date.day,
348+
"slug": published_entry.slug,
349+
},
350+
)
351+
response = self.client.get(published_url)
352+
self.assertEqual(response.status_code, 200)
353+
354+
def test_user_cannot_see_unpublished_entries(self):
355+
"""
356+
Non-staff users can't see unpublished entries at all (list or detail view)
357+
"""
358+
user = User.objects.create(username="non-staff", is_staff=False)
359+
self.client.force_login(user)
360+
361+
# Create a published entry to ensure the list view works
362+
published_entry = Entry.objects.create(
363+
pub_date=self.yesterday,
364+
is_active=True,
365+
headline="published",
366+
slug="published",
367+
)
368+
369+
# Create an unpublished entry
370+
unpublished_entry = Entry.objects.create(
371+
pub_date=self.tomorrow,
372+
is_active=True,
373+
headline="unpublished",
374+
slug="unpublished",
375+
)
376+
377+
# Test list view - should return 200 but not include the unpublished entry
378+
response = self.client.get(reverse("weblog:index"))
379+
self.assertEqual(response.status_code, 200)
380+
self.assertContains(response, "published")
381+
self.assertNotContains(response, "unpublished")
382+
383+
# Test detail view for unpublished entry - should return 404
384+
unpublished_url = reverse(
385+
"weblog:entry",
386+
kwargs={
387+
"year": unpublished_entry.pub_date.year,
388+
"month": unpublished_entry.pub_date.strftime("%b").lower(),
389+
"day": unpublished_entry.pub_date.day,
390+
"slug": unpublished_entry.slug,
391+
},
392+
)
393+
response = self.client.get(unpublished_url)
394+
self.assertEqual(response.status_code, 404)
395+
396+
# Test detail view for published entry - should return 200
397+
published_url = reverse(
398+
"weblog:entry",
399+
kwargs={
400+
"year": published_entry.pub_date.year,
401+
"month": published_entry.pub_date.strftime("%b").lower(),
402+
"day": published_entry.pub_date.day,
403+
"slug": published_entry.slug,
404+
},
405+
)
406+
response = self.client.get(published_url)
407+
self.assertEqual(response.status_code, 200)
408+
235409

236410
class SitemapTests(DateTimeMixin, TestCase):
237411
def test_sitemap(self):

blog/views.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ def get_allow_future(self):
1818
return self.request.user.is_staff
1919

2020
def get_queryset(self):
21-
if self.request.user.is_staff:
22-
return Entry.objects.all()
23-
else:
24-
return Entry.objects.published()
21+
return Entry.objects.published()
2522

2623
def get_context_data(self, **kwargs):
2724
context = super().get_context_data(**kwargs)
@@ -52,3 +49,12 @@ class BlogDayArchiveView(BlogViewMixin, DayArchiveView):
5249

5350
class BlogDateDetailView(BlogViewMixin, DateDetailView):
5451
banner_is_title = False
52+
53+
def get_queryset(self):
54+
"""Allows staff users with blog write permission to view unpublished entries."""
55+
if self.request.user.is_staff and self.request.user.has_perm(
56+
"blog.change_entry"
57+
):
58+
return Entry.objects.all()
59+
else:
60+
return super().get_queryset()

0 commit comments

Comments
 (0)