Skip to content

Commit edbee97

Browse files
authored
Story 2293: Posts Feed (boostorg#2355)
1 parent 2d17a49 commit edbee97

11 files changed

Lines changed: 576 additions & 67 deletions

File tree

config/urls.py

Lines changed: 3 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -52,27 +52,7 @@
5252
CommitEmailResendView,
5353
)
5454
from news.feeds import AtomNewsFeed, RSSNewsFeed
55-
from news.views import (
56-
AllTypesCreateView,
57-
BlogPostCreateView,
58-
BlogPostListView,
59-
EntryApproveView,
60-
EntryDeleteView,
61-
EntryDetailView,
62-
EntryListView,
63-
EntryModerationDetailView,
64-
EntryModerationListView,
65-
EntryModerationMagicApproveView,
66-
EntryUpdateView,
67-
LinkCreateView,
68-
LinkListView,
69-
NewsCreateView,
70-
NewsListView,
71-
PollCreateView,
72-
PollListView,
73-
VideoCreateView,
74-
VideoListView,
75-
)
55+
from news.views import V3AllTypesCreateView
7656
from users.views import (
7757
CurrentUserAPIView,
7858
CurrentUserProfileView,
@@ -288,49 +268,8 @@
288268
LibraryDetail.as_view(redirect_to_docs=True),
289269
name="library-docs-redirect",
290270
),
291-
path("news/", EntryListView.as_view(), name="news"),
292-
path("news/blogpost/", BlogPostListView.as_view(), name="news-blogpost-list"),
293-
path("news/link/", LinkListView.as_view(), name="news-link-list"),
294-
path("news/news/", NewsListView.as_view(), name="news-news-list"),
295-
path("news/poll/", PollListView.as_view(), name="news-poll-list"),
296-
path("news/video/", VideoListView.as_view(), name="news-video-list"),
297-
path("news/add/", AllTypesCreateView.as_view(), name="news-create"),
298-
path("news/add/news/", NewsCreateView.as_view(), name="news-news-create"),
299-
path(
300-
"news/add/blogpost/",
301-
BlogPostCreateView.as_view(),
302-
name="news-blogpost-create",
303-
),
304-
path("news/add/link/", LinkCreateView.as_view(), name="news-link-create"),
305-
path("news/add/poll/", PollCreateView.as_view(), name="news-poll-create"),
306-
path("news/add/video/", VideoCreateView.as_view(), name="news-video-create"),
307-
path("news/moderate/", EntryModerationListView.as_view(), name="news-moderate"),
308-
path(
309-
"news/moderate/<slug:slug>/",
310-
EntryModerationDetailView.as_view(),
311-
name="news-moderate-detail",
312-
),
313-
path(
314-
"news/moderate/magic/<str:token>/",
315-
EntryModerationMagicApproveView.as_view(),
316-
name="news-magic-approve",
317-
),
318-
path("news/entry/<slug:slug>/", EntryDetailView.as_view(), name="news-detail"),
319-
path(
320-
"news/entry/<slug:slug>/approve/",
321-
EntryApproveView.as_view(),
322-
name="news-approve",
323-
),
324-
path(
325-
"news/entry/<slug:slug>/delete/",
326-
EntryDeleteView.as_view(),
327-
name="news-delete",
328-
),
329-
path(
330-
"news/entry/<slug:slug>/update/",
331-
EntryUpdateView.as_view(),
332-
name="news-update",
333-
),
271+
path("news/", include("news.urls")),
272+
path("v3/news/add/", V3AllTypesCreateView.as_view(), name="v3-news-create"),
334273
path(
335274
"people/detail/",
336275
TemplateView.as_view(template_name="boost/people_detail.html"),

news/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,14 @@ def is_approved(self):
126126
def is_published(self):
127127
return self.is_approved and self.publish_at <= now()
128128

129+
@property
130+
def video_thumbnail(self):
131+
try:
132+
result = self.video.thumbnail
133+
except Video.DoesNotExist:
134+
result = None
135+
return result
136+
129137
@cached_property
130138
def tag(self):
131139
return getattr(self, "_tag", self.news_type)

news/urls.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from django.urls import path
2+
from news.views import (
3+
AllTypesCreateView,
4+
BlogPostCreateView,
5+
BlogPostListView,
6+
EntryApproveView,
7+
EntryDeleteView,
8+
EntryDetailView,
9+
EntryListView,
10+
EntryModerationDetailView,
11+
EntryModerationListView,
12+
EntryModerationMagicApproveView,
13+
EntryUpdateView,
14+
LinkCreateView,
15+
LinkListView,
16+
NewsCreateView,
17+
NewsListView,
18+
PollCreateView,
19+
PollListView,
20+
VideoCreateView,
21+
VideoListView,
22+
)
23+
24+
25+
urlpatterns = [
26+
path("", EntryListView.as_view(), name="news"),
27+
path("blogpost/", BlogPostListView.as_view(), name="news-blogpost-list"),
28+
path("link/", LinkListView.as_view(), name="news-link-list"),
29+
path("news/", NewsListView.as_view(), name="news-news-list"),
30+
path("poll/", PollListView.as_view(), name="news-poll-list"),
31+
path("video/", VideoListView.as_view(), name="news-video-list"),
32+
path("add/", AllTypesCreateView.as_view(), name="news-create"),
33+
path("add/news/", NewsCreateView.as_view(), name="news-news-create"),
34+
path(
35+
"add/blogpost/",
36+
BlogPostCreateView.as_view(),
37+
name="news-blogpost-create",
38+
),
39+
path("add/link/", LinkCreateView.as_view(), name="news-link-create"),
40+
path("add/poll/", PollCreateView.as_view(), name="news-poll-create"),
41+
path("add/video/", VideoCreateView.as_view(), name="news-video-create"),
42+
path("moderate/", EntryModerationListView.as_view(), name="news-moderate"),
43+
path(
44+
"moderate/<slug:slug>/",
45+
EntryModerationDetailView.as_view(),
46+
name="news-moderate-detail",
47+
),
48+
path(
49+
"moderate/magic/<str:token>/",
50+
EntryModerationMagicApproveView.as_view(),
51+
name="news-magic-approve",
52+
),
53+
path("entry/<slug:slug>/", EntryDetailView.as_view(), name="news-detail"),
54+
path(
55+
"entry/<slug:slug>/approve/",
56+
EntryApproveView.as_view(),
57+
name="news-approve",
58+
),
59+
path(
60+
"entry/<slug:slug>/delete/",
61+
EntryDeleteView.as_view(),
62+
name="news-delete",
63+
),
64+
path(
65+
"entry/<slug:slug>/update/",
66+
EntryUpdateView.as_view(),
67+
name="news-update",
68+
),
69+
]

news/views.py

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from django.shortcuts import redirect, get_object_or_404
1313
from django.template.defaultfilters import date as datefilter
1414
from django.urls import reverse_lazy
15+
from django.utils.functional import cached_property
1516
from django.utils.http import url_has_allowed_host_and_scheme
1617
from django.utils.timezone import localtime, now
1718
from django.utils.translation import gettext as _
@@ -38,6 +39,8 @@
3839
send_email_news_posted,
3940
)
4041

42+
from libraries.models import Library
43+
4144
User = get_user_model()
4245

4346

@@ -77,12 +80,80 @@ def display_publish_at(publish_at, since=None):
7780
return humanize.naturaltime(truncated).replace("\xa0", " ")
7881

7982

80-
class EntryListView(ListView):
83+
class EntryListView(V3Mixin, ListView):
8184
model = Entry
8285
template_name = "news/list.html"
86+
v3_template_name = "v3/posts_list.html"
8387
ordering = ["-publish_at"]
8488
paginate_by = None # XXX: use pagination in the template! Issue #377
8589
context_object_name = "entry_list" # Ensure children use the same name
90+
header_text = "Latest Posts"
91+
filter_value = "all"
92+
93+
@cached_property
94+
def libary_values(self):
95+
return [(x.slug, x.name) for x in Library.objects.all().order_by("name")]
96+
97+
def render_v3_response(self):
98+
"""Render the v3 template through Django's standard TemplateView pipeline."""
99+
if post_filter := self.request.GET.get("post-filter"):
100+
match post_filter:
101+
case "all":
102+
return HttpResponseRedirect(reverse_lazy("news"))
103+
case "blogpost":
104+
return HttpResponseRedirect(reverse_lazy("news-blogpost-list"))
105+
case "video":
106+
return HttpResponseRedirect(reverse_lazy("news-video-list"))
107+
case "news":
108+
return HttpResponseRedirect(reverse_lazy("news-news-list"))
109+
case "link":
110+
return HttpResponseRedirect(reverse_lazy("news-link-list"))
111+
112+
context = self.get_context_data(
113+
**self.get_v3_context_data(), object_list=self.get_queryset()
114+
)
115+
return self.render_to_response(context)
116+
117+
def get_v3_context_data(self, **kwargs):
118+
return {
119+
"filter_terms": [
120+
{
121+
"label": "All",
122+
"value": "all",
123+
},
124+
{
125+
"label": "News",
126+
"value": "news",
127+
},
128+
{
129+
"label": "Blogs",
130+
"value": "blogpost",
131+
},
132+
{
133+
"label": "Links",
134+
"value": "link",
135+
},
136+
{
137+
"label": "Videos",
138+
"value": "video",
139+
},
140+
{
141+
"label": "Discussions",
142+
"value": "discussions",
143+
},
144+
{
145+
"label": "Achievements",
146+
"value": "achievements",
147+
},
148+
{
149+
"label": "Issues",
150+
"value": "issues",
151+
},
152+
],
153+
"libraries": self.libary_values,
154+
"header_text": self.header_text,
155+
"filter_value": self.filter_value,
156+
}
86157

87158
def get_queryset(self):
88159
result = (
@@ -106,23 +177,32 @@ def get_context_data(self, **kwargs):
106177

107178

108179
class BlogPostListView(EntryListView):
180+
header_text = "Blogs"
109181
model = BlogPost
182+
filter_value = "blogpost"
110183

111184

112185
class LinkListView(EntryListView):
186+
header_text = "Links"
113187
model = Link
188+
filter_value = "link"
114189

115190

116191
class NewsListView(EntryListView):
192+
header_text = "News"
117193
model = News
194+
filter_value = "news"
118195

119196

120197
class PollListView(EntryListView):
198+
header_text = "Polls"
121199
model = Poll
122200

123201

124202
class VideoListView(EntryListView):
203+
header_text = "Videos"
125204
model = Video
205+
filter_value = "video"
126206

127207

128208
class EntryModerationListView(LoginRequiredMixin, UserPassesTestMixin, ListView):

static/css/v3/post-filter.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,11 @@
6363
outline: 2px solid var(--color-text-link-accent);
6464
outline-offset: -2px;
6565
}
66+
67+
68+
/* -- Mobile Padding Change -------------- */
69+
@media (max-width: 768px) {
70+
.post-filter__option {
71+
padding: var(--space-card) var(--space-medium);
72+
}
73+
}

0 commit comments

Comments
 (0)