Merge lp:~widelands-dev/widelands-website/hidden_topics into lp:widelands-website

Proposed by kaputtnik
Status: Merged
Merged at revision: 506
Proposed branch: lp:~widelands-dev/widelands-website/hidden_topics
Merge into: lp:widelands-website
Diff against target: 922 lines (+403/-324)
8 files modified
pybb/feeds.py (+2/-2)
pybb/models.py (+54/-8)
pybb/templatetags/pybb_extras.py (+6/-3)
pybb/urls.py (+2/-0)
pybb/views.py (+31/-30)
templates/pybb/forum.html (+4/-0)
templates/pybb/inlines/display_category.html (+6/-4)
templates/pybb/topic.html (+298/-277)
To merge this branch: bzr merge lp:~widelands-dev/widelands-website/hidden_topics
Reviewer Review Type Date Requested Status
GunChleoc Approve
Review via email: mp+358944@code.launchpad.net

Commit message

Implement possibility to hide whole topics

Description of the change

Implement possibility to hide whole topics. The hiding is done by hiding the first post of a topic. This is implemented as follows:

- Beside the buttons "Stick Topic"/"Close Topic" an additional button "Toggle Visibility" was added, this button is only shown for forum moderators or superusers

- Showing hidden topics or it's last post is prevented in all forum related pages, like forum categories, in the "Latest Post" box and the Feeds

- Moderators can see hidden topics ONLY in a forums list of topics as a row showing "Hidden Topic: topics_name", and can enter this topic to toggle visibility. In all other places showing hidden topics, or the last post of a hidden topic is prevented

- Because topics are hidden also if it was caught by our spam filter, an additional sentence is shown if this topic was caught by the spam filter.

- If one try's to open a hidden topic by URL (e.g. by a link in another topic), a page with an explanation is shown.

Spam posts appended to an existing topics are now shown for moderators and superusers with a grayish background. For all other users they are not shown.

Showing the forums categories is much faster now by preventing unneeded queries to the database.

To post a comment you must log in.
Revision history for this message
GunChleoc (gunchleoc) wrote :

Just some few nits for the English language. Thanks for taking care of this so fast!

review: Approve
517. By kaputtnik

addressed code review

Revision history for this message
kaputtnik (franku) wrote :

This merged and deployed now.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'media/forum/img/topic_hide.png'
2Binary files media/forum/img/topic_hide.png 1970-01-01 00:00:00 +0000 and media/forum/img/topic_hide.png 2018-11-18 10:28:19 +0000 differ
3=== added file 'media/forum/img/topic_show.png'
4Binary files media/forum/img/topic_show.png 1970-01-01 00:00:00 +0000 and media/forum/img/topic_show.png 2018-11-18 10:28:19 +0000 differ
5=== modified file 'pybb/feeds.py'
6--- pybb/feeds.py 2018-09-12 07:45:35 +0000
7+++ pybb/feeds.py 2018-11-18 10:28:19 +0000
8@@ -56,10 +56,10 @@
9 title_template = 'pybb/feeds/posts_title.html'
10 description_template = 'pybb/feeds/posts_description.html'
11
12- all_objects = Post.objects.filter(hidden=False)
13+ all_objects = Post.objects.exclude(topic__in=Post.hidden_topics.all()).filter(hidden=False)
14
15 def items_for_object(self, obj):
16- return Post.objects.filter(hidden=False, topic__forum=obj).order_by('-created')[:15]
17+ return Post.objects.exclude(topic__in=Post.hidden_topics.all()).filter(hidden=False, topic__forum=obj).order_by('-created')[:15]
18
19 # Validated through http://validator.w3.org/feed/
20
21
22=== modified file 'pybb/models.py'
23--- pybb/models.py 2018-10-03 09:01:09 +0000
24+++ pybb/models.py 2018-11-18 10:28:19 +0000
25@@ -17,6 +17,8 @@
26 from django.conf import settings
27 from notification.models import send
28 from django.contrib.auth.models import User
29+from check_input.models import SuspiciousInput
30+
31
32 try:
33 from notification import models as notification
34@@ -91,8 +93,16 @@
35
36 @property
37 def last_post(self):
38- posts = self.posts.exclude(hidden=True).order_by(
39+ # This has better performance than using the posts manager hidden_topics
40+ # We search only for the last 10 topics
41+ topics = self.topics.order_by('-updated')[:10]
42+ for topic in topics:
43+ if topic.is_hidden:
44+ continue
45+ posts = topic.posts.exclude(hidden=True).order_by(
46 '-created').select_related()
47+ break
48+
49 try:
50 return posts[0]
51 except IndexError:
52@@ -122,21 +132,22 @@
53
54 @property
55 def head(self):
56- return self.posts.all().order_by('created').select_related()[0]
57+ try:
58+ return self.posts.all().order_by('created').select_related()[0]
59+ except:
60+ return None
61
62 @property
63 def last_post(self):
64 return self.posts.exclude(hidden=True).order_by('-created').select_related()[0]
65
66- # If the first post of this topic is hidden, the topic is hidden
67 @property
68 def is_hidden(self):
69+ # If the first post of this topic is hidden, the topic is hidden
70 try:
71- p = self.posts.order_by('created').filter(
72- hidden=False).select_related()[0]
73- except IndexError:
74- return True
75- return False
76+ return self.posts.first().hidden
77+ except:
78+ return False
79
80 @property
81 def post_count(self):
82@@ -191,6 +202,30 @@
83 self.body_html = urlize(self.body_html)
84
85
86+class HiddenTopicsManager(models.Manager):
87+ """Find all hidden topics by posts.
88+
89+ A whole topic is hidden, if the first post is hidden.
90+ This manager returns the hidden topics and can be used to filter them out
91+ like so:
92+
93+ Post.objects.exclude(topic__in=Post.hidden_topics.all()).filter(...)
94+
95+ Use this with caution, because it affects performance, see:
96+ https://docs.djangoproject.com/en/dev/ref/models/querysets/#in
97+ """
98+
99+ def get_queryset(self, *args, **kwargs):
100+ qs = super(HiddenTopicsManager,
101+ self).get_queryset().filter(hidden=True)
102+
103+ hidden_topics = []
104+ for post in qs:
105+ if post.topic.is_hidden:
106+ hidden_topics.append(post.topic)
107+ return hidden_topics
108+
109+
110 class Post(RenderableItem):
111 topic = models.ForeignKey(
112 Topic, related_name='posts', verbose_name=_('Topic'))
113@@ -205,6 +240,9 @@
114 body_text = models.TextField(_('Text version'))
115 hidden = models.BooleanField(_('Hidden'), blank=True, default=False)
116
117+ objects = models.Manager() # Normal manager
118+ hidden_topics = HiddenTopicsManager() # Custom manager
119+
120 class Meta:
121 ordering = ['created']
122 verbose_name = _('Post')
123@@ -260,6 +298,14 @@
124 if self_id == head_post_id:
125 self.topic.delete()
126
127+ def is_spam(self):
128+ try:
129+ SuspiciousInput.objects.get(object_id = self.pk)
130+ return True
131+ except:
132+ pass
133+ return False
134+
135
136 class Read(models.Model):
137 """For each topic that user has entered the time is logged to this
138
139=== modified file 'pybb/templatetags/pybb_extras.py'
140--- pybb/templatetags/pybb_extras.py 2018-09-11 20:16:31 +0000
141+++ pybb/templatetags/pybb_extras.py 2018-11-18 10:28:19 +0000
142@@ -82,14 +82,17 @@
143
144 @register.inclusion_tag('pybb/last_posts.html', takes_context=True)
145 def pybb_last_posts(context, number=8):
146+
147 last_posts = Post.objects.filter(hidden=False).order_by(
148 '-created').select_related()[:45]
149+
150 check = []
151 answer = []
152 for post in last_posts:
153- if (post.topic_id not in check) and len(check) < number:
154- check = check + [post.topic_id]
155- answer = answer + [post]
156+ if not post.topic.is_hidden:
157+ if (post.topic_id not in check) and len(check) < number:
158+ check = check + [post.topic_id]
159+ answer = answer + [post]
160 return {
161 'posts': answer,
162 }
163
164=== modified file 'pybb/urls.py'
165--- pybb/urls.py 2018-04-08 15:20:01 +0000
166+++ pybb/urls.py 2018-11-18 10:28:19 +0000
167@@ -30,6 +30,8 @@
168 views.close_topic, name='pybb_close_topic'),
169 url('^topic/(?P<topic_id>\d+)/open/$',
170 views.open_topic, name='pybb_open_topic'),
171+ url('^topic/(?P<topic_id>\d+)/unhide/$',
172+ views.toggle_hidden_topic, name='pybb_toggle_hid_topic'),
173
174 # Post
175 url('^topic/(?P<topic_id>\d+)/post/add/$', views.add_post,
176
177=== modified file 'pybb/views.py'
178--- pybb/views.py 2018-10-03 09:01:09 +0000
179+++ pybb/views.py 2018-11-18 10:28:19 +0000
180@@ -10,7 +10,7 @@
181 from django.urls import reverse
182 from django.db import connection
183 from django.utils import translation
184-from django.shortcuts import render
185+from django.shortcuts import render, redirect
186
187
188 from pybb.util import render_to, paged, build_form, quote_text, ajax, urlize
189@@ -29,52 +29,32 @@
190
191
192 def index_ctx(request):
193- quick = {'posts': Post.objects.count(),
194- 'topics': Topic.objects.count(),
195- 'users': User.objects.count(),
196- 'last_topics': Topic.objects.all().select_related()[:pybb_settings.QUICK_TOPICS_NUMBER],
197- 'last_posts': Post.objects.order_by('-created').select_related()[:pybb_settings.QUICK_POSTS_NUMBER],
198- }
199-
200 cats = Category.objects.all().select_related()
201
202- return {'cats': cats,
203- 'quick': quick,
204- }
205+ return {'cats': cats }
206 index = render_to('pybb/index.html')(index_ctx)
207
208
209 def show_category_ctx(request, category_id):
210 category = get_object_or_404(Category, pk=category_id)
211- quick = {'posts': category.posts.count(),
212- 'topics': category.topics.count(),
213- 'last_topics': category.topics.select_related()[:pybb_settings.QUICK_TOPICS_NUMBER],
214- 'last_posts': category.posts.order_by('-created').select_related()
215- [:pybb_settings.QUICK_POSTS_NUMBER],
216- }
217- return {'category': category,
218- 'quick': quick,
219- }
220+
221+ return {'category': category }
222 show_category = render_to('pybb/category.html')(show_category_ctx)
223
224
225 def show_forum_ctx(request, forum_id):
226 forum = get_object_or_404(Forum, pk=forum_id)
227
228- quick = {'posts': forum.post_count,
229- 'topics': forum.topics.count(),
230- 'last_topics': forum.topics.all().select_related()[:pybb_settings.QUICK_TOPICS_NUMBER],
231- 'last_posts': forum.posts.order_by('-created').select_related()
232- [:pybb_settings.QUICK_POSTS_NUMBER],
233- }
234+ moderator = (request.user.is_superuser or
235+ request.user in forum.moderators.all())
236
237 topics = forum.topics.order_by(
238- '-sticky', '-updated').exclude(posts__hidden=True).select_related()
239+ '-sticky', '-updated').select_related()
240
241 return {'forum': forum,
242 'topics': topics,
243- 'quick': quick,
244 'page_size': pybb_settings.FORUM_PAGE_SIZE,
245+ 'moderator': moderator,
246 }
247 show_forum = render_to('pybb/forum.html')(show_forum_ctx)
248
249@@ -106,8 +86,16 @@
250 subscribed = (request.user.is_authenticated and
251 request.user in topic.subscribers.all())
252
253- posts = topic.posts.exclude(hidden=True).select_related()
254-
255+
256+ is_spam = False
257+ if topic.is_hidden:
258+ is_spam = topic.posts.first().is_spam()
259+
260+ if moderator:
261+ posts = topic.posts.select_related()
262+ else:
263+ posts = topic.posts.exclude(hidden=True).select_related()
264+
265 # TODO: fetch profiles
266 # profiles = Profile.objects.filter(user__pk__in=
267 # set(x.user.id for x in page.object_list))
268@@ -127,6 +115,7 @@
269 'posts': posts,
270 'page_size': pybb_settings.TOPIC_PAGE_SIZE,
271 'form_url': reverse('pybb_add_post', args=[topic.id]),
272+ 'is_spam': is_spam,
273 }
274 show_topic = render_to('pybb/topic.html')(show_topic_ctx)
275
276@@ -393,3 +382,15 @@
277
278 def pybb_moderate_info(request):
279 return render(request, 'pybb/pybb_moderate_info.html')
280+
281+
282+def toggle_hidden_topic(request, topic_id):
283+ topic = get_object_or_404(Topic, pk=topic_id)
284+ first_post = topic.posts.all()[0]
285+ if first_post.hidden:
286+ first_post.hidden = False
287+ else:
288+ first_post.hidden = True
289+ first_post.save()
290+
291+ return redirect(topic)
292\ No newline at end of file
293
294=== modified file 'templates/pybb/forum.html'
295--- templates/pybb/forum.html 2018-10-15 16:11:43 +0000
296+++ templates/pybb/forum.html 2018-11-18 10:28:19 +0000
297@@ -51,6 +51,7 @@
298 <tbody>
299 {% for topic in object_list %}
300 <tr class="{% cycle 'odd' 'even' %}">
301+ {% if not topic.is_hidden %}
302 <td class="forumIcon center">
303 {% if topic|pybb_has_unreads:user %}
304 <img src="{{ MEDIA_URL }}forum/img/doc_big_work_star.png" style="margin: 0px;" alt="" class="middle" />
305@@ -74,6 +75,9 @@
306 <span class="small">on {{ topic.last_post.created|custom_date:user }}</span>
307 {% endif %}
308 </td>
309+ {% elif moderator %}
310+ <td colspan="4" class="center errormessage">Hidden Topic: <a href="{{ topic.get_absolute_url }}">{{ topic.name }}</a></td>
311+ {% endif %}
312 </tr>
313 {% endfor %}
314 </tbody>
315
316=== modified file 'templates/pybb/inlines/display_category.html'
317--- templates/pybb/inlines/display_category.html 2016-10-29 20:47:11 +0000
318+++ templates/pybb/inlines/display_category.html 2018-11-18 10:28:19 +0000
319@@ -30,13 +30,15 @@
320 Posts: {{ forum.posts.count }}
321 </td>
322 <td class="lastPost">
323- {%if forum.last_post %}
324- <a href="{{forum.last_post.get_absolute_url}}">{{ forum.last_post.topic.name }}</a><br />
325- <span class="small">by {{ forum.last_post.user|user_link }}<br />
326- on {{ forum.last_post.created|custom_date:user}}</span>
327+ {% with last_post=forum.last_post %}
328+ {% if last_post %}
329+ <a href="{{last_post.get_absolute_url}}">{{ last_post.topic.name }}</a><br />
330+ <span class="small">by {{ last_post.user|user_link }}<br />
331+ on {{ last_post.created|custom_date:user}}</span>
332 {% else %}
333 &nbsp;
334 {% endif %}
335+ {% endwith %}
336 </td>
337 </tr>
338 {% endfor %}
339
340=== modified file 'templates/pybb/topic.html'
341--- templates/pybb/topic.html 2018-10-14 13:24:15 +0000
342+++ templates/pybb/topic.html 2018-11-18 10:28:19 +0000
343@@ -24,281 +24,302 @@
344 {% block content_main %}
345 <div class="blogEntry">
346 <div class="breadCrumb">
347- <a href="{% url 'pybb_index' %}">Forums</a> &#187;
348- {% pybb_link topic.forum.category %} &#187;
349- <a href="{{ topic.forum.get_absolute_url }}">{{ topic.forum.name }}</a> &#187;
350- {{ topic }}
351- </div>
352-
353- <div class="posRight">
354- {% if moderator %}
355- {% if topic.sticky %}
356- <a class="button" href="{% url 'pybb_unstick_topic' topic.id %}">
357- <img src="{{ MEDIA_URL }}forum/img/unstick.png" alt ="" class="middle" />
358- <span class="middle">{% trans "Unstick Topic" %}</span>
359- </a>
360- {% else %}
361- <a class="button" href="{% url 'pybb_stick_topic' topic.id %}">
362- <img src="{{ MEDIA_URL }}forum/img/sticky.png" alt ="" class="middle" />
363- <span class="middle">{% trans "Stick Topic" %}</span>
364- </a>
365- {% endif %}
366- {% if topic.closed %}
367- <a class="button" href="{% url 'pybb_open_topic' topic.id %}">
368- <img src="{{ MEDIA_URL }}forum/img/open.png" alt ="" class="middle" />
369- <span class="middle">{% trans "Open Topic" %}</span>
370- </a>
371- {% else %}
372- <a class="button" href="{% url 'pybb_close_topic' topic.id %}">
373- <img src="{{ MEDIA_URL }}forum/img/closed.png" alt ="" class="middle" />
374- <span class="middle">{% trans "Close Topic" %}</span>
375- </a>
376- {% endif %}
377- {% endif %}
378- {% if user.is_authenticated %}
379- {% if subscribed %}
380- <a class="button" href="{% url 'pybb_delete_subscription' topic.id %}?from_topic">
381- <img src="{{ MEDIA_URL }}forum/img/unsubscribe.png" alt ="" class="middle" />
382- <span class="middle">{% trans "Unsubscribe" %}</span>
383- </a>
384- {% else %}
385- <a class="button" href="{% url 'pybb_add_subscription' topic.id %}">
386- <img src="{{ MEDIA_URL }}forum/img/subscribe.png" alt ="" class="middle" />
387- <span class="middle">{% trans "Subscribe" %}</span>
388- </a>
389- {% endif %}
390- <a class="button" href="{% url 'pybb_add_post' topic.id %}">
391- <img src="{{ MEDIA_URL }}forum/img/send.png" alt ="" class="middle" />
392- <span class="middle">{% trans "New Reply" %}</span>
393- </a>
394- {% endif %}
395- </div>
396- {% autopaginate posts page_size as object_list %}
397- {% paginate using "pagination/pagination_mod.html" %}
398-
399-{% if first_post %}
400- {% ifnotequal first_post posts.0 %}
401- {% with first_post as post %}
402- {% trans "First Post" %}:
403- <table class="forum">
404- <tbody>
405- <tr class="odd">
406- <td class="author">
407- {{ post.user|user_link }}<br />
408- {% if post.user.wlprofile_extras.avatar %}
409- <a href="{% url 'profile_view' post.user %}">
410- <img src="{{ post.user.wlprofile.avatar.url }}" alt="Avatar" />
411- </a>
412- {% endif %}
413- <div class="authorStats">
414- <strong>Joined:</strong> {{ post.user.date_joined|custom_date:user|title }}<br />
415- <strong>Posts:</strong> {{ post.user.wlprofile.post_count }}<br />
416- <img src="{{ MEDIA_URL }}img/{{ post.user.wlprofile.user_status.image }}" alt="Ranking" /><br />
417- <strong>{{ post.user.wlprofile.user_status.text }}</strong><br />
418- {% if post.user.wlprofile.location %}
419- <strong>Location:</strong> {{ post.user.wlprofile.location }}<br />
420- {% endif %}
421- </div>
422- </td>
423- <td class="post">
424- <a id="post-{{ post.id }}" href="{{post.get_absolute_url}}" title="{% trans "Permalink" %}" class="posRight small permalink">&nbsp;</a>
425- <span class="small">Posted at: {{ post.created|custom_date:user}}</span>
426- <hr />
427- <div class="post">
428- {{ post.body_html|safe }}
429- </div>
430-
431- {% if post.attachment_cache %}
432- {% for attach in post.attachment_cache %}
433- {% trans "Attachment" %}: <a href="{{ attach.get_absolute_url }}">{{ attach.name }}</a> ({{ attach.size_display }})
434- {% endfor %}
435- {% endif %}
436-
437- {% if post.updated %}
438- <span class="small">{% trans "Edited" %}: {{ post.updated|custom_date:user|title}}</span>
439- {% endif %}
440- <hr />
441- {% if user.is_authenticated %}
442- {% ifequal user.wlprofile.show_signatures 1 %}
443- {% if post.user.wlprofile.signature %}
444- {{ post.user.wlprofile.signature|urlize|linebreaks }}
445- {% endif %}
446- {% endifequal %}
447- {% else %}
448- {% if post.user.wlprofile.signature %}
449- {{ post.user.wlprofile.signature|urlize|linebreaks }}
450- {% endif %}
451- {% endif %}
452-
453- <button onclick="window.location.href='#top';" class="posRight">
454- <img src="{{ MEDIA_URL }}forum/img/top.png" alt ="" class="middle" />
455- <span class="middle">{% trans "Top" %}</span>
456- </button>
457-
458- <button onclick="window.location.href='{% url 'pybb_add_post' topic.id %}?quote_id={{ post.id }}';">
459- <img src="{{ MEDIA_URL }}forum/img/quote.png" alt ="" class="middle" />
460- <span class="middle">{% trans "Quote" %}</span>
461- </button>
462- {% if moderator or post|pybb_posted_by:user %}
463- <button onclick="window.location.href='{% url 'pybb_edit_post' post.id %}';">
464- <img src="{{ MEDIA_URL }}forum/img/edit.png" alt ="" class="middle" />
465- <span class="middle">{% trans "Edit" %}</span>
466- </button>
467- {% if moderator or post|pybb_equal_to:last_post %}
468- <button onclick="window.location.href='{% url 'pybb_delete_post' post.id %}';">
469- <img src="{{ MEDIA_URL }}forum/img/delete.png" alt ="" class="middle" />
470- <span class="middle">{% trans "Delete" %}</span>
471- </button>
472- {% endif %}
473- {% endif %}
474- </td>
475- </tr>
476- </tbody>
477- </table>
478- <br /><hr /><br />
479- {% endwith %}
480- {% endifnotequal %}
481-{% endif %}
482-
483- <table class="forum">
484- <tbody>
485- {% for post in object_list %}
486- {% comment %}
487- TODO (Franku): use
488- {% include 'pybb/inlines/post.html' %}
489- {% endcomment %}
490- <tr class="{% cycle 'odd' 'even' %}">
491- <td class="author">
492- {{ post.user|user_link }}<br />
493- {% if post.user.wlprofile.avatar %}
494- <a href="{% url 'profile_view' post.user %}">
495- <img src="{{ post.user.wlprofile.avatar.url }}" alt="Avatar" />
496- </a>
497- {% endif %}
498- <div class="authorStats">
499- <strong>Joined:</strong> {{ post.user.date_joined|custom_date:user|title }}<br />
500- <strong>Posts:</strong> {{ post.user.wlprofile.post_count }}<br />
501- <img src="{{ MEDIA_URL }}img/{{ post.user.wlprofile.user_status.image }}" alt="Ranking" /><br />
502- <strong>{{ post.user.wlprofile.user_status.text }}</strong><br />
503- {% if post.user.wlprofile.location %}
504- <strong>Location:</strong> {{ post.user.wlprofile.location }}<br />
505- {% endif %}
506- </div>
507- </td>
508- <td class="post">
509- <a id="post-{{ post.id }}" href="{{post.get_absolute_url}}" title="{% trans "Permalink" %}" class="posRight small permalink">&nbsp;</a>
510- <span class="small">Posted at: {{ post.created|custom_date:user}}</span>
511- <hr />
512- <div class="post">
513- {{ post.body_html|safe }}
514- </div>
515-
516- {% if post.attachment_cache %}
517- {% for attach in post.attachment_cache %}
518- {% trans "Attachment" %}: <a href="{{ attach.get_absolute_url }}">{{ attach.name }}</a> ({{ attach.size_display }})
519- {% endfor %}
520- {% endif %}
521-
522- {% if post.updated %}
523- <span class="small">{% trans "Edited" %}: {{ post.updated|custom_date:user|title}}</span>
524- {% endif %}
525- <hr />
526- {% if user.is_authenticated %}
527- {% ifequal user.wlprofile.show_signatures 1 %}
528- {% if post.user.wlprofile.signature %}
529- {{ post.user.wlprofile.signature|urlize|linebreaks }}
530- {% endif %}
531- {% endifequal %}
532- {% else %}
533- {% if post.user.wlprofile.signature %}
534- {{ post.user.wlprofile.signature|urlize|linebreaks }}
535- {% endif %}
536- {% endif %}
537-
538- <a class="button posRight" href="#top">
539- <img src="{{ MEDIA_URL }}forum/img/top.png" alt ="" class="middle" />
540- <span class="middle">{% trans "Top" %}</span>
541- </a>
542-
543- <a class="button" href="{% url 'pybb_add_post' topic.id %}?quote_id={{ post.id }}">
544- <img src="{{ MEDIA_URL }}forum/img/quote.png" alt ="" class="middle" />
545- <span class="middle">{% trans "Quote" %}</span>
546- </a>
547- {% if moderator or post|pybb_posted_by:user %}
548- <a class="button" href="{% url 'pybb_edit_post' post.id %}">
549- <img src="{{ MEDIA_URL }}forum/img/edit.png" alt ="" class="middle" />
550- <span class="middle">{% trans "Edit" %}</span>
551- </a>
552- {% if moderator or post|pybb_equal_to:last_post %}
553- <a class="button" href="{% url 'pybb_delete_post' post.id %}">
554- <img src="{{ MEDIA_URL }}forum/img/delete.png" alt ="" class="middle" />
555- <span class="middle">{% trans "Delete" %}</span>
556- </a>
557- {% endif %}
558- {% endif %}
559- </td>
560- </tr>
561- {% if not forloop.last %}
562- {# no spacer at end of table #}
563- <tr class="spacer">
564- <td></td>
565- <td></td>
566- </tr>
567- {% endif %}
568- {% endfor %}
569- </tbody>
570- </table>
571-
572- <div class="posRight">
573- {% if moderator %}
574- {% if topic.sticky %}
575- <a class="button" href="{% url 'pybb_unstick_topic' topic.id %}">
576- <img src="{{ MEDIA_URL }}forum/img/unstick.png" alt ="" class="middle" />
577- <span class="middle">{% trans "Unstick Topic" %}</span>
578- </a>
579- {% else %}
580- <a class="button" href="{% url 'pybb_stick_topic' topic.id %}">
581- <img src="{{ MEDIA_URL }}forum/img/sticky.png" alt ="" class="middle" />
582- <span class="middle">{% trans "Stick Topic" %}</span>
583- </a>
584- {% endif %}
585- {% if topic.closed %}
586- <a class="button" href="{% url 'pybb_open_topic' topic.id %}">
587- <img src="{{ MEDIA_URL }}forum/img/open.png" alt ="" class="middle" />
588- <span class="middle">{% trans "Open Topic" %}</span>
589- </a>
590- {% else %}
591- <a class="button" href="{% url 'pybb_close_topic' topic.id %}">
592- <img src="{{ MEDIA_URL }}forum/img/closed.png" alt ="" class="middle" />
593- <span class="middle">{% trans "Close Topic" %}</span>
594- </a>
595- {% endif %}
596- {% endif %}
597- {% if user.is_authenticated %}
598- {% if subscribed %}
599- <a class="button" href="{% url 'pybb_delete_subscription' topic.id %}?from_topic">
600- <img src="{{ MEDIA_URL }}forum/img/unsubscribe.png" alt ="" class="middle" />
601- <span class="middle">{% trans "Unsubscribe" %}</span>
602- </a>
603- {% else %}
604- <a class="button" href="{% url 'pybb_add_subscription' topic.id %}">
605- <img src="{{ MEDIA_URL }}forum/img/subscribe.png" alt ="" class="middle" />
606- <span class="middle">{% trans "Subscribe" %}</span>
607- </a>
608- {% endif %}
609- <a class="button" href="{% url 'pybb_add_post' topic.id %}">
610- <img src="{{ MEDIA_URL }}forum/img/send.png" alt ="" class="middle" />
611- <span class="middle">{% trans "New Reply" %}</span>
612- </a>
613- {% endif %}
614- </div>
615- {% paginate %}
616-</div>
617-
618-{% if user.is_authenticated %}
619- {% if not topic.closed %}
620- {% include "pybb/inlines/add_post_form.html" %}
621- {% endif %}
622-{% endif %}
623-
624+ <a href="{% url 'pybb_index' %}">Forums</a> &#187;
625+ {% pybb_link topic.forum.category %} &#187;
626+ <a href="{{ topic.forum.get_absolute_url }}">{{ topic.forum.name }}</a> &#187;
627+ {{ topic }}
628+ </div>
629+ {% if topic.is_hidden %}
630+ <p>This topic is hidden. It is either waiting for a review or was hidden by a moderator.</p>
631+ {% if posts.0.is_spam and moderator %}
632+ <p>This topic's first post is possible spam. Toggle the visibility to show the post. If it is indeed spam, consider deleting the user:</p>
633+ <p>To delete the user, go to the <a href="/admin/auth/user/{{posts.0.user.pk}}/change/">admin user-page for the post's author</a></p>
634+ {% endif %}
635+ <div class="posRight">
636+ {% if moderator %}
637+ <a class="button" href="{% url 'pybb_toggle_hid_topic' topic.id %}">
638+ <img src="{{ MEDIA_URL }}forum/img/topic_show.png" alt ="" class="middle" />
639+ <span class="middle">{% trans "Toggle Visibility" %}</span>
640+ </a>
641+ {% endif %}
642+ {% else %}
643+ <div class="posRight">
644+ {% if moderator %}
645+ <a class="button" href="{% url 'pybb_toggle_hid_topic' topic.id %}">
646+ <img src="{{ MEDIA_URL }}forum/img/topic_hide.png" alt ="" class="middle" />
647+ <span class="middle">{% trans "Toggle Visibility" %}</span>
648+ </a>
649+ {% if topic.sticky %}
650+ <a class="button" href="{% url 'pybb_unstick_topic' topic.id %}">
651+ <img src="{{ MEDIA_URL }}forum/img/unstick.png" alt ="" class="middle" />
652+ <span class="middle">{% trans "Unstick Topic" %}</span>
653+ </a>
654+ {% else %}
655+ <a class="button" href="{% url 'pybb_stick_topic' topic.id %}">
656+ <img src="{{ MEDIA_URL }}forum/img/sticky.png" alt ="" class="middle" />
657+ <span class="middle">{% trans "Stick Topic" %}</span>
658+ </a>
659+ {% endif %}
660+ {% if topic.closed %}
661+ <a class="button" href="{% url 'pybb_open_topic' topic.id %}">
662+ <img src="{{ MEDIA_URL }}forum/img/open.png" alt ="" class="middle" />
663+ <span class="middle">{% trans "Open Topic" %}</span>
664+ </a>
665+ {% else %}
666+ <a class="button" href="{% url 'pybb_close_topic' topic.id %}">
667+ <img src="{{ MEDIA_URL }}forum/img/closed.png" alt ="" class="middle" />
668+ <span class="middle">{% trans "Close Topic" %}</span>
669+ </a>
670+ {% endif %}
671+ {% endif %}
672+ {% if user.is_authenticated %}
673+ {% if subscribed %}
674+ <a class="button" href="{% url 'pybb_delete_subscription' topic.id %}?from_topic">
675+ <img src="{{ MEDIA_URL }}forum/img/unsubscribe.png" alt ="" class="middle" />
676+ <span class="middle">{% trans "Unsubscribe" %}</span>
677+ </a>
678+ {% else %}
679+ <a class="button" href="{% url 'pybb_add_subscription' topic.id %}">
680+ <img src="{{ MEDIA_URL }}forum/img/subscribe.png" alt ="" class="middle" />
681+ <span class="middle">{% trans "Subscribe" %}</span>
682+ </a>
683+ {% endif %}
684+ <a class="button" href="{% url 'pybb_add_post' topic.id %}">
685+ <img src="{{ MEDIA_URL }}forum/img/send.png" alt ="" class="middle" />
686+ <span class="middle">{% trans "New Reply" %}</span>
687+ </a>
688+ {% endif %}
689+ </div>
690+ {% autopaginate posts page_size as object_list %}
691+ {% paginate using "pagination/pagination_mod.html" %}
692+
693+ {% if first_post %}
694+ {% ifnotequal first_post posts.0 %}
695+ {% with first_post as post %}
696+ {% trans "First Post" %}:
697+ <table class="forum">
698+ <tbody>
699+ <tr class="odd">
700+ <td class="author">
701+ {{ post.user|user_link }}<br />
702+ {% if post.user.wlprofile_extras.avatar %}
703+ <a href="{% url 'profile_view' post.user %}">
704+ <img src="{{ post.user.wlprofile.avatar.url }}" alt="Avatar" />
705+ </a>
706+ {% endif %}
707+ <div class="authorStats">
708+ <strong>Joined:</strong> {{ post.user.date_joined|custom_date:user|title }}<br />
709+ <strong>Posts:</strong> {{ post.user.wlprofile.post_count }}<br />
710+ <img src="{{ MEDIA_URL }}img/{{ post.user.wlprofile.user_status.image }}" alt="Ranking" /><br />
711+ <strong>{{ post.user.wlprofile.user_status.text }}</strong><br />
712+ {% if post.user.wlprofile.location %}
713+ <strong>Location:</strong> {{ post.user.wlprofile.location }}<br />
714+ {% endif %}
715+ </div>
716+ </td>
717+ <td class="post">
718+ <a id="post-{{ post.id }}" href="{{post.get_absolute_url}}" title="{% trans "Permalink" %}" class="posRight small permalink">&nbsp;</a>
719+ <span class="small">Posted at: {{ post.created|custom_date:user}}</span>
720+ <hr />
721+ <div class="post">
722+ {{ post.body_html|safe }}
723+ </div>
724+
725+ {% if post.attachment_cache %}
726+ {% for attach in post.attachment_cache %}
727+ {% trans "Attachment" %}: <a href="{{ attach.get_absolute_url }}">{{ attach.name }}</a> ({{ attach.size_display }})
728+ {% endfor %}
729+ {% endif %}
730+
731+ {% if post.updated %}
732+ <span class="small">{% trans "Edited" %}: {{ post.updated|custom_date:user|title}}</span>
733+ {% endif %}
734+ <hr />
735+ {% if user.is_authenticated %}
736+ {% ifequal user.wlprofile.show_signatures 1 %}
737+ {% if post.user.wlprofile.signature %}
738+ {{ post.user.wlprofile.signature|urlize|linebreaks }}
739+ {% endif %}
740+ {% endifequal %}
741+ {% else %}
742+ {% if post.user.wlprofile.signature %}
743+ {{ post.user.wlprofile.signature|urlize|linebreaks }}
744+ {% endif %}
745+ {% endif %}
746+
747+ <button onclick="window.location.href='#top';" class="posRight">
748+ <img src="{{ MEDIA_URL }}forum/img/top.png" alt ="" class="middle" />
749+ <span class="middle">{% trans "Top" %}</span>
750+ </button>
751+
752+ <button onclick="window.location.href='{% url 'pybb_add_post' topic.id %}?quote_id={{ post.id }}';">
753+ <img src="{{ MEDIA_URL }}forum/img/quote.png" alt ="" class="middle" />
754+ <span class="middle">{% trans "Quote" %}</span>
755+ </button>
756+ {% if moderator or post|pybb_posted_by:user %}
757+ <button onclick="window.location.href='{% url 'pybb_edit_post' post.id %}';">
758+ <img src="{{ MEDIA_URL }}forum/img/edit.png" alt ="" class="middle" />
759+ <span class="middle">{% trans "Edit" %}</span>
760+ </button>
761+ {% if moderator or post|pybb_equal_to:last_post %}
762+ <button onclick="window.location.href='{% url 'pybb_delete_post' post.id %}';">
763+ <img src="{{ MEDIA_URL }}forum/img/delete.png" alt ="" class="middle" />
764+ <span class="middle">{% trans "Delete" %}</span>
765+ </button>
766+ {% endif %}
767+ {% endif %}
768+ </td>
769+ </tr>
770+ </tbody>
771+ </table>
772+ <br /><hr /><br />
773+ {% endwith %}
774+ {% endifnotequal %}
775+ {% endif %}
776+
777+ <table class="forum">
778+ <tbody>
779+ {% for post in object_list %}
780+ {% comment %}
781+ TODO (Franku): use
782+ {% include 'pybb/inlines/post.html' %}
783+ {% endcomment %}
784+ <tr class="{% cycle 'odd' 'even' %}" {% if post.is_spam %} style="background-color: gray" {% endif %}>
785+ <td class="author">
786+ {{ post.user|user_link }}<br />
787+ {% if post.user.wlprofile.avatar %}
788+ <a href="{% url 'profile_view' post.user %}">
789+ <img src="{{ post.user.wlprofile.avatar.url }}" alt="Avatar" />
790+ </a>
791+ {% endif %}
792+ <div class="authorStats">
793+ <strong>Joined:</strong> {{ post.user.date_joined|custom_date:user|title }}<br />
794+ <strong>Posts:</strong> {{ post.user.wlprofile.post_count }}<br />
795+ <img src="{{ MEDIA_URL }}img/{{ post.user.wlprofile.user_status.image }}" alt="Ranking" /><br />
796+ <strong>{{ post.user.wlprofile.user_status.text }}</strong><br />
797+ {% if post.user.wlprofile.location %}
798+ <strong>Location:</strong> {{ post.user.wlprofile.location }}<br />
799+ {% endif %}
800+ </div>
801+ </td>
802+ <td class="post">
803+ <a id="post-{{ post.id }}" href="{{post.get_absolute_url}}" title="{% trans "Permalink" %}" class="posRight small permalink">&nbsp;</a>
804+ <span class="small">Posted at: {{ post.created|custom_date:user}}</span>
805+ <hr />
806+ <div class="post">
807+ {{ post.body_html|safe }}
808+ </div>
809+
810+ {% if post.attachment_cache %}
811+ {% for attach in post.attachment_cache %}
812+ {% trans "Attachment" %}: <a href="{{ attach.get_absolute_url }}">{{ attach.name }}</a> ({{ attach.size_display }})
813+ {% endfor %}
814+ {% endif %}
815+
816+ {% if post.updated %}
817+ <span class="small">{% trans "Edited" %}: {{ post.updated|custom_date:user|title}}</span>
818+ {% endif %}
819+ <hr />
820+ {% if user.is_authenticated %}
821+ {% ifequal user.wlprofile.show_signatures 1 %}
822+ {% if post.user.wlprofile.signature %}
823+ {{ post.user.wlprofile.signature|urlize|linebreaks }}
824+ {% endif %}
825+ {% endifequal %}
826+ {% else %}
827+ {% if post.user.wlprofile.signature %}
828+ {{ post.user.wlprofile.signature|urlize|linebreaks }}
829+ {% endif %}
830+ {% endif %}
831+
832+ <a class="button posRight" href="#top">
833+ <img src="{{ MEDIA_URL }}forum/img/top.png" alt ="" class="middle" />
834+ <span class="middle">{% trans "Top" %}</span>
835+ </a>
836+
837+ <a class="button" href="{% url 'pybb_add_post' topic.id %}?quote_id={{ post.id }}">
838+ <img src="{{ MEDIA_URL }}forum/img/quote.png" alt ="" class="middle" />
839+ <span class="middle">{% trans "Quote" %}</span>
840+ </a>
841+ {% if moderator or post|pybb_posted_by:user %}
842+ <a class="button" href="{% url 'pybb_edit_post' post.id %}">
843+ <img src="{{ MEDIA_URL }}forum/img/edit.png" alt ="" class="middle" />
844+ <span class="middle">{% trans "Edit" %}</span>
845+ </a>
846+ {% if moderator or post|pybb_equal_to:last_post %}
847+ <a class="button" href="{% url 'pybb_delete_post' post.id %}">
848+ <img src="{{ MEDIA_URL }}forum/img/delete.png" alt ="" class="middle" />
849+ <span class="middle">{% trans "Delete" %}</span>
850+ </a>
851+ {% endif %}
852+ {% endif %}
853+ </td>
854+ </tr>
855+ {% if not forloop.last %}
856+ {# no spacer at end of table #}
857+ <tr class="spacer">
858+ <td></td>
859+ <td></td>
860+ </tr>
861+ {% endif %}
862+ {% endfor %}
863+ </tbody>
864+ </table>
865+
866+ <div class="posRight">
867+ {% if moderator %}
868+ <a class="button" href="{% url 'pybb_toggle_hid_topic' topic.id %}">
869+ <img src="{{ MEDIA_URL }}forum/img/topic_hide.png" alt ="" class="middle" />
870+ <span class="middle">{% trans "Toggle Visibility" %}</span>
871+ </a>
872+ {% if topic.sticky %}
873+ <a class="button" href="{% url 'pybb_unstick_topic' topic.id %}">
874+ <img src="{{ MEDIA_URL }}forum/img/unstick.png" alt ="" class="middle" />
875+ <span class="middle">{% trans "Unstick Topic" %}</span>
876+ </a>
877+ {% else %}
878+ <a class="button" href="{% url 'pybb_stick_topic' topic.id %}">
879+ <img src="{{ MEDIA_URL }}forum/img/sticky.png" alt ="" class="middle" />
880+ <span class="middle">{% trans "Stick Topic" %}</span>
881+ </a>
882+ {% endif %}
883+ {% if topic.closed %}
884+ <a class="button" href="{% url 'pybb_open_topic' topic.id %}">
885+ <img src="{{ MEDIA_URL }}forum/img/open.png" alt ="" class="middle" />
886+ <span class="middle">{% trans "Open Topic" %}</span>
887+ </a>
888+ {% else %}
889+ <a class="button" href="{% url 'pybb_close_topic' topic.id %}">
890+ <img src="{{ MEDIA_URL }}forum/img/closed.png" alt ="" class="middle" />
891+ <span class="middle">{% trans "Close Topic" %}</span>
892+ </a>
893+ {% endif %}
894+ {% endif %}
895+ {% if user.is_authenticated %}
896+ {% if subscribed %}
897+ <a class="button" href="{% url 'pybb_delete_subscription' topic.id %}?from_topic">
898+ <img src="{{ MEDIA_URL }}forum/img/unsubscribe.png" alt ="" class="middle" />
899+ <span class="middle">{% trans "Unsubscribe" %}</span>
900+ </a>
901+ {% else %}
902+ <a class="button" href="{% url 'pybb_add_subscription' topic.id %}">
903+ <img src="{{ MEDIA_URL }}forum/img/subscribe.png" alt ="" class="middle" />
904+ <span class="middle">{% trans "Subscribe" %}</span>
905+ </a>
906+ {% endif %}
907+ <a class="button" href="{% url 'pybb_add_post' topic.id %}">
908+ <img src="{{ MEDIA_URL }}forum/img/send.png" alt ="" class="middle" />
909+ <span class="middle">{% trans "New Reply" %}</span>
910+ </a>
911+ {% endif %}
912+ </div>
913+ {% paginate %}
914+ </div>
915+
916+ {% if user.is_authenticated %}
917+ {% if not topic.closed %}
918+ {% include "pybb/inlines/add_post_form.html" %}
919+ {% endif %}
920+ {% endif %}
921+ {% endif %}
922 {% endblock %}

Subscribers

People subscribed via source and target branches