diff --git a/templates/forum/category/forum.html b/templates/forum/category/forum.html index 999bc73fb0..bd9c027102 100644 --- a/templates/forum/category/forum.html +++ b/templates/forum/category/forum.html @@ -1,6 +1,7 @@ {% extends "forum/base.html" %} {% load i18n %} {% load interventions %} +{% load topics_sort %} {% block title %} @@ -86,7 +87,7 @@ {% if topics %} diff --git a/zds/forum/managers.py b/zds/forum/managers.py index af99a596b2..54308d853d 100644 --- a/zds/forum/managers.py +++ b/zds/forum/managers.py @@ -1,6 +1,6 @@ from django.conf import settings from django.db import models -from django.db.models import Q, F +from django.db.models import Q, F, Max from model_utils.managers import InheritanceManager from zds.utils import get_current_user @@ -93,7 +93,8 @@ def get_last_topics(self): def get_all_topics_of_a_forum(self, forum_pk, is_sticky=False): return ( self.filter(forum__pk=forum_pk, is_sticky=is_sticky) - .order_by("-last_message__pubdate") + .annotate(last_visible_update=Max("post__pubdate", filter=Q(post__is_visible=True))) + .order_by("-last_visible_update") .select_related("author__profile") .prefetch_related("last_message", "tags") .all() diff --git a/zds/forum/models.py b/zds/forum/models.py index 8cd5830c01..1f6a0660b7 100644 --- a/zds/forum/models.py +++ b/zds/forum/models.py @@ -127,10 +127,15 @@ def get_post_count(self): def get_last_message(self): """ - :return: the last message on the forum, if there are any. + :return: the last visible message on the forum, if there are any. """ try: - last_post = Post.objects.select_related("topic").filter(topic__forum=self).order_by("-pubdate").all()[0] + last_post = ( + Post.objects.select_related("topic") + .filter(topic__forum=self, is_visible=True) + .order_by("-pubdate") + .all()[0] + ) last_post.topic.forum = self return last_post except IndexError: @@ -232,6 +237,11 @@ def meta_description(self): return first_post.text return Topic.__remove_greetings(first_post)[: settings.ZDS_APP["forum"]["description_size"]] + @property + def last_update(self): + last_visible_post = self.get_last_visible_post() + return last_visible_post.pubdate + @staticmethod def __remove_greetings(post): greetings = settings.ZDS_APP["forum"]["greetings"] @@ -257,6 +267,15 @@ def get_post_count(self): """ return Post.objects.filter(topic__pk=self.pk).count() + def get_last_visible_post(self): + """ + :return: the last visible post in the thread. + """ + try: + return self.post_set.filter(is_visible=True).latest("pubdate") + except Post.DoesNotExist: + return None + def get_last_post(self): """ :return: the last post in the thread. @@ -270,7 +289,7 @@ def get_last_answer(self): return `None`. :return: the last answer in the thread, if any. """ - last_post = self.get_last_post() + last_post = self.get_last_visible_post() if last_post == self.first_post(): return None diff --git a/zds/forum/tests/tests_views.py b/zds/forum/tests/tests_views.py index 078994acee..e028959605 100644 --- a/zds/forum/tests/tests_views.py +++ b/zds/forum/tests/tests_views.py @@ -1042,6 +1042,24 @@ def test_failure_new_post_stopped_by_anti_spam(self): self.assertEqual(403, response.status_code) + def test_new_post_stopped_by_anti_spam_after_hidden_post(self): + profile = ProfileFactory() + _, forum = create_category_and_forum() + topic = create_topic_in_forum(forum, profile) + + post_to_hide = PostFactory(topic=topic, author=profile.user, position=topic.last_message.position + 1) + + staff = StaffProfileFactory() + self.client.force_login(staff.user) + text_hidden_expected = "Bad guy!" + data = {"delete_message": "", "text_hidden": text_hidden_expected} + response = self.client.post(reverse("post-edit") + "?message={}".format(post_to_hide.pk), data, follow=False) + + self.client.force_login(profile.user) + response = self.client.get(reverse("post-new") + "?sujet={}".format(topic.pk)) + + self.assertEqual(403, response.status_code) + def test_success_new_post_method_get(self): another_profile = ProfileFactory() _, forum = create_category_and_forum() @@ -1345,6 +1363,22 @@ def test_success_edit_post_hide_message_by_staff(self): self.assertEqual(staff.user, post.editor) self.assertEqual(text_hidden_expected, post.text_hidden) + def test_last_post_update_after_hiding(self): + profile = ProfileFactory() + _, forum = create_category_and_forum() + topic = create_topic_in_forum(forum, profile) + expected_last_post = topic.last_message + post_to_hide = PostFactory(topic=topic, author=profile.user, position=2) + + staff = StaffProfileFactory() + self.client.force_login(staff.user) + text_hidden_expected = "Bad guy!" + data = {"delete_message": "", "text_hidden": text_hidden_expected} + response = self.client.post(reverse("post-edit") + "?message={}".format(post_to_hide.pk), data, follow=False) + + last_post = Post.objects.get(pk=topic.get_last_visible_post().pk) + self.assertEqual(last_post.pk, expected_last_post.pk) + def test_hide_helpful_message(self): profile = ProfileFactory() _, forum = create_category_and_forum() diff --git a/zds/utils/templatetags/topics_sort.py b/zds/utils/templatetags/topics_sort.py new file mode 100644 index 0000000000..80ced43961 --- /dev/null +++ b/zds/utils/templatetags/topics_sort.py @@ -0,0 +1,13 @@ +from django import template +from django.core.cache import cache + + +register = template.Library() + + +@register.filter("topics_sort") +def topics_sort(topics): + """ + :return: the topics sorted by last update (last updated first) + """ + return sorted(topics, key=lambda topic: topic.last_update, reverse=True) diff --git a/zds/utils/tests/test_misc.py b/zds/utils/tests/test_misc.py index 4492b833b5..6eb09f90ba 100644 --- a/zds/utils/tests/test_misc.py +++ b/zds/utils/tests/test_misc.py @@ -5,6 +5,8 @@ from zds.utils.misc import contains_utf8mb4 from zds.utils.models import Alert from zds.utils.context_processor import get_header_notifications +from zds.utils.templatetags.topics_sort import topics_sort +from zds.forum.factories import TopicFactory, create_category_and_forum, create_topic_in_forum class Misc(TestCase): @@ -31,3 +33,11 @@ def test_intervention_filter_for_tribunes(self): filter_result = get_header_notifications(staff.user)["alerts"] self.assertEqual(1, filter_result["total"]) self.assertEqual(alert.text, filter_result["list"][0]["text"]) + + def test_topics_sort(self): + author = ProfileFactory() + _, forum = create_category_and_forum() + topic1 = create_topic_in_forum(forum, author) + topic2 = create_topic_in_forum(forum, author) + + self.assertEqual(topics_sort([topic1, topic2]), [topic2, topic1])