We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Un simple membre ne peut pas ajouter une raison au masquage
Lorsqu'on est un simple membre, on n'a pas la possibilité d'ajouter une raison au masquage d'un message sur le forum (pour justifier => HS / déjà répondu / a côté de la plaque) alors qu'un staff peut ajouter un commentaire sur la raison du masquage.
Un simple membre ne peut pas ajouter une raison au masquage
Lorsqu'on est un simple membre, on n'a pas la possibilité d'ajouter une raison au masquage d'un message sur le forum (pour justifier => HS / déjà répondu / a côté de la plaque) alors qu'un staff peut ajouter un commentaire sur la raison du masquage.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `zds/forum/commons.py`
Content:
```
1 from datetime import datetime
2 
3 from django.contrib import messages
4 from django.core.exceptions import PermissionDenied
5 from django.http import Http404
6 from django.shortcuts import get_object_or_404
7 from django.utils.translation import ugettext as _
8 from django.views.generic.detail import SingleObjectMixin
9 from django.contrib.auth.decorators import permission_required
10 
11 from zds.forum.models import Forum, Post, TopicRead
12 from zds.notification import signals
13 from zds.notification.models import TopicAnswerSubscription, Notification, NewTopicSubscription
14 from zds.utils.models import Alert, CommentEdit, get_hat_from_request
15 
16 
17 class ForumEditMixin(object):
18     @staticmethod
19     def perform_follow(forum_or_tag, user):
20         return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user).is_active
21 
22     @staticmethod
23     def perform_follow_by_email(forum_or_tag, user):
24         return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user, True).is_active
25 
26 
27 class TopicEditMixin(object):
28     @staticmethod
29     def perform_follow(topic, user):
30         return TopicAnswerSubscription.objects.toggle_follow(topic, user)
31 
32     @staticmethod
33     def perform_follow_by_email(topic, user):
34         return TopicAnswerSubscription.objects.toggle_follow(topic, user, True)
35 
36     @staticmethod
37     def perform_solve_or_unsolve(user, topic):
38         if user == topic.author or user.has_perm('forum.change_topic'):
39             topic.solved_by = None if topic.solved_by else user
40             return topic.is_solved
41         else:
42             raise PermissionDenied
43 
44     @staticmethod
45     @permission_required('forum.change_topic', raise_exception=True)
46     def perform_lock(request, topic):
47         topic.is_locked = request.POST.get('lock') == 'true'
48         if topic.is_locked:
49             success_message = _('Le sujet « {0} » est désormais verrouillé.').format(topic.title)
50         else:
51             success_message = _('Le sujet « {0} » est désormais déverrouillé.').format(topic.title)
52         messages.success(request, success_message)
53 
54     @staticmethod
55     @permission_required('forum.change_topic', raise_exception=True)
56     def perform_sticky(request, topic):
57         topic.is_sticky = request.POST.get('sticky') == 'true'
58         if topic.is_sticky:
59             success_message = _('Le sujet « {0} » est désormais épinglé.').format(topic.title)
60         else:
61             success_message = _("Le sujet « {0} » n'est désormais plus épinglé.").format(topic.title)
62         messages.success(request, success_message)
63 
64     def perform_move(self):
65         if self.request.user.has_perm('forum.change_topic'):
66             try:
67                 forum_pk = int(self.request.POST.get('forum'))
68             except (KeyError, ValueError, TypeError) as e:
69                 raise Http404('Forum not found', e)
70             forum = get_object_or_404(Forum, pk=forum_pk)
71             self.object.forum = forum
72 
73             # Save topic to update update_index_date
74             self.object.save()
75 
76             signals.edit_content.send(sender=self.object.__class__, instance=self.object, action='move')
77             message = _('Le sujet « {0} » a bien été déplacé dans « {1} ».').format(self.object.title, forum.title)
78             messages.success(self.request, message)
79         else:
80             raise PermissionDenied()
81 
82     @staticmethod
83     def perform_edit_info(request, topic, data, editor):
84         topic.title = data.get('title')
85         topic.subtitle = data.get('subtitle')
86         topic.save()
87 
88         PostEditMixin.perform_edit_post(request, topic.first_post(), editor, data.get('text'))
89 
90         # add tags
91         topic.tags.clear()
92         if data.get('tags'):
93             topic.add_tags(data.get('tags').split(','))
94 
95         return topic
96 
97 
98 class PostEditMixin(object):
99     @staticmethod
100     def perform_hide_message(request, post, user, data):
101         is_staff = user.has_perm('forum.change_post')
102         if post.author == user or is_staff:
103             for alert in post.alerts_on_this_comment.all():
104                 alert.solve(user, _('Le message a été masqué.'))
105             post.is_visible = False
106             post.editor = user
107 
108             if is_staff:
109                 post.text_hidden = data.get('text_hidden', '')
110 
111             messages.success(request, _('Le message est désormais masqué.'))
112             for user in Notification.objects.get_users_for_unread_notification_on(post):
113                 signals.content_read.send(sender=post.topic.__class__, instance=post.topic, user=user)
114         else:
115             raise PermissionDenied
116 
117     @staticmethod
118     @permission_required('forum.change_post', raise_exception=True)
119     def perform_show_message(request, post):
120         post.is_visible = True
121         post.text_hidden = ''
122 
123     @staticmethod
124     def perform_alert_message(request, post, user, alert_text):
125         alert = Alert(
126             author=user,
127             comment=post,
128             scope='FORUM',
129             text=alert_text,
130             pubdate=datetime.now())
131         alert.save()
132 
133         messages.success(request, _("Une alerte a été envoyée à l'équipe concernant ce message."))
134 
135     @staticmethod
136     def perform_useful(post):
137         post.is_useful = not post.is_useful
138         post.save()
139 
140     @staticmethod
141     def perform_unread_message(post, user):
142         """
143         Marks a post unread so we create a notification between the user and the topic host of the post.
144         But, if there is only one post in the topic, we mark the topic unread but we don't create a notification.
145         """
146         topic_read = TopicRead.objects.filter(topic=post.topic, user=user).first()
147         # issue 3227 proves that you can have post.position==1 AND topic_read to None
148         # it can happen whether on double click (the event "mark as not read" is therefore sent twice)
149         # or if you have two tabs in your browser.
150         if topic_read is None and post.position > 1:
151             unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()
152             topic_read = TopicRead(post=unread, topic=unread.topic, user=user)
153             topic_read.save()
154         else:
155             if post.position > 1:
156                 unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()
157                 topic_read.post = unread
158                 topic_read.save()
159             elif topic_read:
160                 topic_read.delete()
161 
162         signals.answer_unread.send(sender=post.topic.__class__, instance=post, user=user)
163 
164     @staticmethod
165     def perform_edit_post(request, post, user, text):
166         # create an archive
167         edit = CommentEdit()
168         edit.comment = post
169         edit.editor = user
170         edit.original_text = post.text
171         edit.save()
172 
173         post.update_content(
174             text,
175             on_error=lambda m: messages.error(
176                 request,
177                 _('Erreur du serveur Markdown:\n{}').format('\n- '.join(m))))
178         post.hat = get_hat_from_request(request, post.author)
179         post.update = datetime.now()
180         post.editor = user
181         post.save()
182 
183         if post.position == 1:
184             # Save topic to update update_index_date
185             post.topic.save()
186         return post
187 
188 
189 class SinglePostObjectMixin(SingleObjectMixin):
190     object = None
191 
192     def get_object(self, queryset=None):
193         try:
194             post_pk = int(self.request.GET.get('message'))
195         except (KeyError, ValueError, TypeError):
196             raise Http404
197         return get_object_or_404(Post, pk=post_pk)
198
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/zds/forum/commons.py b/zds/forum/commons.py
--- a/zds/forum/commons.py
+++ b/zds/forum/commons.py
@@ -104,9 +104,7 @@
                 alert.solve(user, _('Le message a été masqué.'))
             post.is_visible = False
             post.editor = user
-
-            if is_staff:
-                post.text_hidden = data.get('text_hidden', '')
+            post.text_hidden = data.get('text_hidden', '')
 
             messages.success(request, _('Le message est désormais masqué.'))
             for user in Notification.objects.get_users_for_unread_notification_on(post):

{"golden_diff": "diff --git a/zds/forum/commons.py b/zds/forum/commons.py\n--- a/zds/forum/commons.py\n+++ b/zds/forum/commons.py\n@@ -104,9 +104,7 @@\n                 alert.solve(user, _('Le message a \u00e9t\u00e9 masqu\u00e9.'))\n             post.is_visible = False\n             post.editor = user\n-\n-            if is_staff:\n-                post.text_hidden = data.get('text_hidden', '')\n+            post.text_hidden = data.get('text_hidden', '')\n \n             messages.success(request, _('Le message est d\u00e9sormais masqu\u00e9.'))\n             for user in Notification.objects.get_users_for_unread_notification_on(post):\n", "issue": "Un simple membre ne peut pas ajouter une raison au masquage\nLorsqu'on est un simple membre, on n'a pas la possibilit\u00e9 d'ajouter une raison au masquage d'un message sur le forum (pour justifier => HS / d\u00e9j\u00e0 r\u00e9pondu / a c\u00f4t\u00e9 de la plaque) alors qu'un staff peut ajouter un commentaire sur la raison du masquage.\nUn simple membre ne peut pas ajouter une raison au masquage\nLorsqu'on est un simple membre, on n'a pas la possibilit\u00e9 d'ajouter une raison au masquage d'un message sur le forum (pour justifier => HS / d\u00e9j\u00e0 r\u00e9pondu / a c\u00f4t\u00e9 de la plaque) alors qu'un staff peut ajouter un commentaire sur la raison du masquage.\n", "before_files": [{"content": "from datetime import datetime\n\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import ugettext as _\nfrom django.views.generic.detail import SingleObjectMixin\nfrom django.contrib.auth.decorators import permission_required\n\nfrom zds.forum.models import Forum, Post, TopicRead\nfrom zds.notification import signals\nfrom zds.notification.models import TopicAnswerSubscription, Notification, NewTopicSubscription\nfrom zds.utils.models import Alert, CommentEdit, get_hat_from_request\n\n\nclass ForumEditMixin(object):\n    @staticmethod\n    def perform_follow(forum_or_tag, user):\n        return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user).is_active\n\n    @staticmethod\n    def perform_follow_by_email(forum_or_tag, user):\n        return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user, True).is_active\n\n\nclass TopicEditMixin(object):\n    @staticmethod\n    def perform_follow(topic, user):\n        return TopicAnswerSubscription.objects.toggle_follow(topic, user)\n\n    @staticmethod\n    def perform_follow_by_email(topic, user):\n        return TopicAnswerSubscription.objects.toggle_follow(topic, user, True)\n\n    @staticmethod\n    def perform_solve_or_unsolve(user, topic):\n        if user == topic.author or user.has_perm('forum.change_topic'):\n            topic.solved_by = None if topic.solved_by else user\n            return topic.is_solved\n        else:\n            raise PermissionDenied\n\n    @staticmethod\n    @permission_required('forum.change_topic', raise_exception=True)\n    def perform_lock(request, topic):\n        topic.is_locked = request.POST.get('lock') == 'true'\n        if topic.is_locked:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais verrouill\u00e9.').format(topic.title)\n        else:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais d\u00e9verrouill\u00e9.').format(topic.title)\n        messages.success(request, success_message)\n\n    @staticmethod\n    @permission_required('forum.change_topic', raise_exception=True)\n    def perform_sticky(request, topic):\n        topic.is_sticky = request.POST.get('sticky') == 'true'\n        if topic.is_sticky:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais \u00e9pingl\u00e9.').format(topic.title)\n        else:\n            success_message = _(\"Le sujet \u00ab\u00a0{0}\u00a0\u00bb n'est d\u00e9sormais plus \u00e9pingl\u00e9.\").format(topic.title)\n        messages.success(request, success_message)\n\n    def perform_move(self):\n        if self.request.user.has_perm('forum.change_topic'):\n            try:\n                forum_pk = int(self.request.POST.get('forum'))\n            except (KeyError, ValueError, TypeError) as e:\n                raise Http404('Forum not found', e)\n            forum = get_object_or_404(Forum, pk=forum_pk)\n            self.object.forum = forum\n\n            # Save topic to update update_index_date\n            self.object.save()\n\n            signals.edit_content.send(sender=self.object.__class__, instance=self.object, action='move')\n            message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb a bien \u00e9t\u00e9 d\u00e9plac\u00e9 dans \u00ab\u00a0{1}\u00a0\u00bb.').format(self.object.title, forum.title)\n            messages.success(self.request, message)\n        else:\n            raise PermissionDenied()\n\n    @staticmethod\n    def perform_edit_info(request, topic, data, editor):\n        topic.title = data.get('title')\n        topic.subtitle = data.get('subtitle')\n        topic.save()\n\n        PostEditMixin.perform_edit_post(request, topic.first_post(), editor, data.get('text'))\n\n        # add tags\n        topic.tags.clear()\n        if data.get('tags'):\n            topic.add_tags(data.get('tags').split(','))\n\n        return topic\n\n\nclass PostEditMixin(object):\n    @staticmethod\n    def perform_hide_message(request, post, user, data):\n        is_staff = user.has_perm('forum.change_post')\n        if post.author == user or is_staff:\n            for alert in post.alerts_on_this_comment.all():\n                alert.solve(user, _('Le message a \u00e9t\u00e9 masqu\u00e9.'))\n            post.is_visible = False\n            post.editor = user\n\n            if is_staff:\n                post.text_hidden = data.get('text_hidden', '')\n\n            messages.success(request, _('Le message est d\u00e9sormais masqu\u00e9.'))\n            for user in Notification.objects.get_users_for_unread_notification_on(post):\n                signals.content_read.send(sender=post.topic.__class__, instance=post.topic, user=user)\n        else:\n            raise PermissionDenied\n\n    @staticmethod\n    @permission_required('forum.change_post', raise_exception=True)\n    def perform_show_message(request, post):\n        post.is_visible = True\n        post.text_hidden = ''\n\n    @staticmethod\n    def perform_alert_message(request, post, user, alert_text):\n        alert = Alert(\n            author=user,\n            comment=post,\n            scope='FORUM',\n            text=alert_text,\n            pubdate=datetime.now())\n        alert.save()\n\n        messages.success(request, _(\"Une alerte a \u00e9t\u00e9 envoy\u00e9e \u00e0 l'\u00e9quipe concernant ce message.\"))\n\n    @staticmethod\n    def perform_useful(post):\n        post.is_useful = not post.is_useful\n        post.save()\n\n    @staticmethod\n    def perform_unread_message(post, user):\n        \"\"\"\n        Marks a post unread so we create a notification between the user and the topic host of the post.\n        But, if there is only one post in the topic, we mark the topic unread but we don't create a notification.\n        \"\"\"\n        topic_read = TopicRead.objects.filter(topic=post.topic, user=user).first()\n        # issue 3227 proves that you can have post.position==1 AND topic_read to None\n        # it can happen whether on double click (the event \"mark as not read\" is therefore sent twice)\n        # or if you have two tabs in your browser.\n        if topic_read is None and post.position > 1:\n            unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()\n            topic_read = TopicRead(post=unread, topic=unread.topic, user=user)\n            topic_read.save()\n        else:\n            if post.position > 1:\n                unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()\n                topic_read.post = unread\n                topic_read.save()\n            elif topic_read:\n                topic_read.delete()\n\n        signals.answer_unread.send(sender=post.topic.__class__, instance=post, user=user)\n\n    @staticmethod\n    def perform_edit_post(request, post, user, text):\n        # create an archive\n        edit = CommentEdit()\n        edit.comment = post\n        edit.editor = user\n        edit.original_text = post.text\n        edit.save()\n\n        post.update_content(\n            text,\n            on_error=lambda m: messages.error(\n                request,\n                _('Erreur du serveur Markdown:\\n{}').format('\\n- '.join(m))))\n        post.hat = get_hat_from_request(request, post.author)\n        post.update = datetime.now()\n        post.editor = user\n        post.save()\n\n        if post.position == 1:\n            # Save topic to update update_index_date\n            post.topic.save()\n        return post\n\n\nclass SinglePostObjectMixin(SingleObjectMixin):\n    object = None\n\n    def get_object(self, queryset=None):\n        try:\n            post_pk = int(self.request.GET.get('message'))\n        except (KeyError, ValueError, TypeError):\n            raise Http404\n        return get_object_or_404(Post, pk=post_pk)\n", "path": "zds/forum/commons.py"}], "after_files": [{"content": "from datetime import datetime\n\nfrom django.contrib import messages\nfrom django.core.exceptions import PermissionDenied\nfrom django.http import Http404\nfrom django.shortcuts import get_object_or_404\nfrom django.utils.translation import ugettext as _\nfrom django.views.generic.detail import SingleObjectMixin\nfrom django.contrib.auth.decorators import permission_required\n\nfrom zds.forum.models import Forum, Post, TopicRead\nfrom zds.notification import signals\nfrom zds.notification.models import TopicAnswerSubscription, Notification, NewTopicSubscription\nfrom zds.utils.models import Alert, CommentEdit, get_hat_from_request\n\n\nclass ForumEditMixin(object):\n    @staticmethod\n    def perform_follow(forum_or_tag, user):\n        return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user).is_active\n\n    @staticmethod\n    def perform_follow_by_email(forum_or_tag, user):\n        return NewTopicSubscription.objects.toggle_follow(forum_or_tag, user, True).is_active\n\n\nclass TopicEditMixin(object):\n    @staticmethod\n    def perform_follow(topic, user):\n        return TopicAnswerSubscription.objects.toggle_follow(topic, user)\n\n    @staticmethod\n    def perform_follow_by_email(topic, user):\n        return TopicAnswerSubscription.objects.toggle_follow(topic, user, True)\n\n    @staticmethod\n    def perform_solve_or_unsolve(user, topic):\n        if user == topic.author or user.has_perm('forum.change_topic'):\n            topic.solved_by = None if topic.solved_by else user\n            return topic.is_solved\n        else:\n            raise PermissionDenied\n\n    @staticmethod\n    @permission_required('forum.change_topic', raise_exception=True)\n    def perform_lock(request, topic):\n        topic.is_locked = request.POST.get('lock') == 'true'\n        if topic.is_locked:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais verrouill\u00e9.').format(topic.title)\n        else:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais d\u00e9verrouill\u00e9.').format(topic.title)\n        messages.success(request, success_message)\n\n    @staticmethod\n    @permission_required('forum.change_topic', raise_exception=True)\n    def perform_sticky(request, topic):\n        topic.is_sticky = request.POST.get('sticky') == 'true'\n        if topic.is_sticky:\n            success_message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb est d\u00e9sormais \u00e9pingl\u00e9.').format(topic.title)\n        else:\n            success_message = _(\"Le sujet \u00ab\u00a0{0}\u00a0\u00bb n'est d\u00e9sormais plus \u00e9pingl\u00e9.\").format(topic.title)\n        messages.success(request, success_message)\n\n    def perform_move(self):\n        if self.request.user.has_perm('forum.change_topic'):\n            try:\n                forum_pk = int(self.request.POST.get('forum'))\n            except (KeyError, ValueError, TypeError) as e:\n                raise Http404('Forum not found', e)\n            forum = get_object_or_404(Forum, pk=forum_pk)\n            self.object.forum = forum\n\n            # Save topic to update update_index_date\n            self.object.save()\n\n            signals.edit_content.send(sender=self.object.__class__, instance=self.object, action='move')\n            message = _('Le sujet \u00ab\u00a0{0}\u00a0\u00bb a bien \u00e9t\u00e9 d\u00e9plac\u00e9 dans \u00ab\u00a0{1}\u00a0\u00bb.').format(self.object.title, forum.title)\n            messages.success(self.request, message)\n        else:\n            raise PermissionDenied()\n\n    @staticmethod\n    def perform_edit_info(request, topic, data, editor):\n        topic.title = data.get('title')\n        topic.subtitle = data.get('subtitle')\n        topic.save()\n\n        PostEditMixin.perform_edit_post(request, topic.first_post(), editor, data.get('text'))\n\n        # add tags\n        topic.tags.clear()\n        if data.get('tags'):\n            topic.add_tags(data.get('tags').split(','))\n\n        return topic\n\n\nclass PostEditMixin(object):\n    @staticmethod\n    def perform_hide_message(request, post, user, data):\n        is_staff = user.has_perm('forum.change_post')\n        if post.author == user or is_staff:\n            for alert in post.alerts_on_this_comment.all():\n                alert.solve(user, _('Le message a \u00e9t\u00e9 masqu\u00e9.'))\n            post.is_visible = False\n            post.editor = user\n            post.text_hidden = data.get('text_hidden', '')\n\n            messages.success(request, _('Le message est d\u00e9sormais masqu\u00e9.'))\n            for user in Notification.objects.get_users_for_unread_notification_on(post):\n                signals.content_read.send(sender=post.topic.__class__, instance=post.topic, user=user)\n        else:\n            raise PermissionDenied\n\n    @staticmethod\n    @permission_required('forum.change_post', raise_exception=True)\n    def perform_show_message(request, post):\n        post.is_visible = True\n        post.text_hidden = ''\n\n    @staticmethod\n    def perform_alert_message(request, post, user, alert_text):\n        alert = Alert(\n            author=user,\n            comment=post,\n            scope='FORUM',\n            text=alert_text,\n            pubdate=datetime.now())\n        alert.save()\n\n        messages.success(request, _(\"Une alerte a \u00e9t\u00e9 envoy\u00e9e \u00e0 l'\u00e9quipe concernant ce message.\"))\n\n    @staticmethod\n    def perform_useful(post):\n        post.is_useful = not post.is_useful\n        post.save()\n\n    @staticmethod\n    def perform_unread_message(post, user):\n        \"\"\"\n        Marks a post unread so we create a notification between the user and the topic host of the post.\n        But, if there is only one post in the topic, we mark the topic unread but we don't create a notification.\n        \"\"\"\n        topic_read = TopicRead.objects.filter(topic=post.topic, user=user).first()\n        # issue 3227 proves that you can have post.position==1 AND topic_read to None\n        # it can happen whether on double click (the event \"mark as not read\" is therefore sent twice)\n        # or if you have two tabs in your browser.\n        if topic_read is None and post.position > 1:\n            unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()\n            topic_read = TopicRead(post=unread, topic=unread.topic, user=user)\n            topic_read.save()\n        else:\n            if post.position > 1:\n                unread = Post.objects.filter(topic=post.topic, position=(post.position - 1)).first()\n                topic_read.post = unread\n                topic_read.save()\n            elif topic_read:\n                topic_read.delete()\n\n        signals.answer_unread.send(sender=post.topic.__class__, instance=post, user=user)\n\n    @staticmethod\n    def perform_edit_post(request, post, user, text):\n        # create an archive\n        edit = CommentEdit()\n        edit.comment = post\n        edit.editor = user\n        edit.original_text = post.text\n        edit.save()\n\n        post.update_content(\n            text,\n            on_error=lambda m: messages.error(\n                request,\n                _('Erreur du serveur Markdown:\\n{}').format('\\n- '.join(m))))\n        post.hat = get_hat_from_request(request, post.author)\n        post.update = datetime.now()\n        post.editor = user\n        post.save()\n\n        if post.position == 1:\n            # Save topic to update update_index_date\n            post.topic.save()\n        return post\n\n\nclass SinglePostObjectMixin(SingleObjectMixin):\n    object = None\n\n    def get_object(self, queryset=None):\n        try:\n            post_pk = int(self.request.GET.get('message'))\n        except (KeyError, ValueError, TypeError):\n            raise Http404\n        return get_object_or_404(Post, pk=post_pk)\n", "path": "zds/forum/commons.py"}]}