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 %}
- {% for topic in topics %}
+ {% for topic in topics|topics_sort %}
{% include "forum/includes/topic_row.part.html" %}
{% endfor %}
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])