Browse Source

Report post to forum team.

Rafał Pitoń 10 years ago
parent
commit
7480e867ad

+ 9 - 2
misago/static/misago/js/misago-posts.js

@@ -6,6 +6,7 @@ $(function() {
   MisagoPost = function($element) {
   MisagoPost = function($element) {
 
 
     this.$e = $element;
     this.$e = $element;
+    this.$alerts = this.$e.find('.post-alerts');
     this.$content = this.$e.find('article.post-body');
     this.$content = this.$e.find('article.post-body');
     this.id = this.$e.data('id');
     this.id = this.$e.data('id');
 
 
@@ -59,10 +60,16 @@ $(function() {
 
 
     });
     });
 
 
-    this.$e.find('.btn-report').click(function() {
+    this.$report = this.$e.find('.btn-report');
+    this.$report.click(function() {
 
 
       if (!Misago.ReportPost.is_open()) {
       if (!Misago.ReportPost.is_open()) {
-        Misago.ReportPost.open(_this.$e.data('report-url'));
+        Misago.ReportPost.open(_this, _this.$e.data('report-url'), function(data) {
+          _this.$alerts.html(data.alerts);
+          _this.$alerts.fadeIn();
+          _this.$report.attr('disabled', 'disabled');
+          _this.$report.find('.btn-label').text(data.label);
+        });
       }
       }
 
 
     });
     });

+ 4 - 3
misago/static/misago/js/misago-report-post.js

@@ -18,12 +18,13 @@ $(function() {
 
 
     var _this = this;
     var _this = this;
 
 
-    this.open = function(api_url, on_report) {
+    this.open = function(post, api_url, on_report) {
 
 
       this._clear();
       this._clear();
 
 
       if (!this.is_open()) {
       if (!this.is_open()) {
 
 
+        this.post = post;
         this.api_url = api_url;
         this.api_url = api_url;
         this.on_report = on_report;
         this.on_report = on_report;
 
 
@@ -71,8 +72,8 @@ $(function() {
           Misago.Modal.close();
           Misago.Modal.close();
           Misago.Alerts.success(data.message);
           Misago.Alerts.success(data.message);
 
 
-          if (this.on_report) {
-            this.on_report(data);
+          if (!data.is_reported && _this.on_report) {
+            _this.on_report(data);
           }
           }
 
 
         }
         }

+ 15 - 33
misago/templates/misago/thread/post.html

@@ -58,29 +58,9 @@
 
 
       </div>
       </div>
 
 
-      {% if post.is_moderated %}
-      <div class="alert alert-info">
-        <span class="fa fa-question-circle fa-fw fa-lg"></span>
-        {% if post.id == thread.first_post_id %}
-        {% trans "This thread won't be visible to other users until its approved by moderator." %}
-        {% else %}
-        {% trans "This post won't be visible to other users until its approved by moderator." %}
-        {% endif %}
-      </div>
-      {% endif %}
-
-      {% if post.has_open_reports and thread.acl.can_see_reports %}
-      <div class="alert alert-danger">
-        <span class="fa fa-exclamation-triangle fa-fw fa-lg"></span>
-        {% trans "This post has open reports." %}
+      <div class="post-alerts">
+        {% include "misago/thread/post_alerts.html" %}
       </div>
       </div>
-      {% endif %}
-
-      {% if post.is_hidden and post.acl.can_see_hidden %}
-      <div class="alert alert-default">
-        {% include "misago/thread/hidden_message.html" %}
-      </div>
-      {% endif %}
 
 
       {% if not post.is_hidden or post.acl.can_see_hidden %}
       {% if not post.is_hidden or post.acl.can_see_hidden %}
         {% if post.is_valid %}
         {% if post.is_valid %}
@@ -96,18 +76,20 @@
         </div>
         </div>
         {% endif %}
         {% endif %}
 
 
+        <div class="post-reports"></div>
+
         <div class="panel-footer">
         <div class="panel-footer">
 
 
           {% if thread.acl.can_reply %}
           {% if thread.acl.can_reply %}
           <button type="button" class="btn btn-reply btn-primary btn-flat pull-right">
           <button type="button" class="btn btn-reply btn-primary btn-flat pull-right">
-            <span class="fa fa-reply">
+            <span class="fa fa-reply"></span>
             {% trans "Reply" %}
             {% trans "Reply" %}
           </button>
           </button>
           {% endif %}
           {% endif %}
 
 
           {% if post.acl.can_edit %}
           {% if post.acl.can_edit %}
           <button type="button" class="btn btn-edit btn-default btn-flat pull-right">
           <button type="button" class="btn btn-edit btn-default btn-flat pull-right">
-            <span class="fa fa-pencil">
+            <span class="fa fa-pencil"></span>
             {% trans "Edit" %}
             {% trans "Edit" %}
           </button>
           </button>
           {% endif %}
           {% endif %}
@@ -116,7 +98,7 @@
           <form action="{{ post.get_unhide_url }}" method="post">
           <form action="{{ post.get_unhide_url }}" method="post">
             {% csrf_token %}
             {% csrf_token %}
             <button type="submit" class="btn btn-default btn-flat pull-right">
             <button type="submit" class="btn btn-default btn-flat pull-right">
-              <span class="fa fa-eye">
+              <span class="fa fa-eye"></span>
               {% trans "Reveal" %}
               {% trans "Reveal" %}
             </button>
             </button>
           </form>
           </form>
@@ -126,7 +108,7 @@
           <form action="{{ post.get_hide_url }}" method="post">
           <form action="{{ post.get_hide_url }}" method="post">
             {% csrf_token %}
             {% csrf_token %}
             <button type="submit" class="btn btn-warning btn-flat pull-right">
             <button type="submit" class="btn btn-warning btn-flat pull-right">
-              <span class="fa fa-eye-slash">
+              <span class="fa fa-eye-slash"></span>
               {% trans "Hide" %}
               {% trans "Hide" %}
             </button>
             </button>
           </form>
           </form>
@@ -136,16 +118,16 @@
           <form action="{{ post.get_delete_url }}" method="post" data-prompt="{% trans "Are you sure you want to delete this post?" %}">
           <form action="{{ post.get_delete_url }}" method="post" data-prompt="{% trans "Are you sure you want to delete this post?" %}">
             {% csrf_token %}
             {% csrf_token %}
             <button type="submit" class="btn btn-danger btn-flat pull-right">
             <button type="submit" class="btn btn-danger btn-flat pull-right">
-              <span class="fa fa-times">
+              <span class="fa fa-times"></span>
               {% trans "Delete" %}
               {% trans "Delete" %}
             </button>
             </button>
           </form>
           </form>
           {% endif %}
           {% endif %}
 
 
           {% if post.acl.can_report %}
           {% if post.acl.can_report %}
-          <button type="submit" class="btn btn-warning btn-report btn-flat pull-right">
-            <span class="fa fa-exclamation-triangle">
-            {% trans "Report" %}
+          <button type="submit" class="btn btn-warning btn-report btn-flat pull-right" {% if post.is_reported %}disabled="disabled"{% endif %}>
+            <span class="fa fa-exclamation-triangle"></span>
+            <span class="btn-label">{% if post.is_reported %}{% trans "Reported" %}{% else %}{% trans "Report" %}{% endif %}</span>
           </button>
           </button>
           {% endif %}
           {% endif %}
 
 
@@ -153,7 +135,7 @@
           <form action="{{ post.get_approve_url }}" method="post">
           <form action="{{ post.get_approve_url }}" method="post">
             {% csrf_token %}
             {% csrf_token %}
             <button type="submit" class="btn btn-success btn-flat pull-right">
             <button type="submit" class="btn btn-success btn-flat pull-right">
-              <span class="fa fa-check">
+              <span class="fa fa-check"></span>
               {% trans "Approve" %}
               {% trans "Approve" %}
             </button>
             </button>
           </form>
           </form>
@@ -162,8 +144,8 @@
           <form action="" method="post">
           <form action="" method="post">
             {% csrf_token %}
             {% csrf_token %}
             <button type="submit" class="btn btn-success btn-flat pull-left">
             <button type="submit" class="btn btn-success btn-flat pull-left">
-              <span class="fa fa-heart">
-              {% trans "Like" %}
+              <span class="fa fa-heart"></span>
+              <span class="btn-label">{% trans "Like" %}</span>
             </button>
             </button>
           </form>
           </form>
         </div>
         </div>

+ 24 - 0
misago/templates/misago/thread/post_alerts.html

@@ -0,0 +1,24 @@
+{% load i18n %}
+{% if post.is_moderated %}
+<div class="alert alert-info">
+  <span class="fa fa-question-circle fa-fw fa-lg"></span>
+  {% if post.id == thread.first_post_id %}
+  {% trans "This thread won't be visible to other users until its approved by moderator." %}
+  {% else %}
+  {% trans "This post won't be visible to other users until its approved by moderator." %}
+  {% endif %}
+</div>
+{% endif %}
+
+{% if post.has_open_reports and thread.acl.can_see_reports %}
+<div class="alert alert-danger">
+  <span class="fa fa-exclamation-triangle fa-fw fa-lg"></span>
+  {% trans "This post has open reports." %}
+</div>
+{% endif %}
+
+{% if post.is_hidden and post.acl.can_see_hidden %}
+<div class="alert alert-default">
+  {% include "misago/thread/hidden_message.html" %}
+</div>
+{% endif %}

+ 24 - 24
misago/threads/migrations/0001_initial.py

@@ -139,6 +139,30 @@ class Migration(migrations.Migration):
             },
             },
             bases=(models.Model,),
             bases=(models.Model,),
         ),
         ),
+        migrations.CreateModel(
+            name='Report',
+            fields=[
+                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+                ('reported_by_name', models.CharField(max_length=255)),
+                ('reported_by_slug', models.CharField(max_length=255)),
+                ('reported_by_ip', models.GenericIPAddressField()),
+                ('reported_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('message', models.TextField()),
+                ('checksum', models.CharField(default=b'-', max_length=64)),
+                ('is_closed', models.BooleanField(default=False)),
+                ('closed_by_name', models.CharField(max_length=255)),
+                ('closed_by_slug', models.CharField(max_length=255)),
+                ('closed_by', models.ForeignKey(related_name='closedreport_set', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
+                ('closed_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('post', models.ForeignKey(to='misago_threads.Post')),
+                ('reported_by', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
+                ('thread', models.ForeignKey(to='misago_threads.Thread')),
+            ],
+            options={
+            },
+            bases=(models.Model,),
+        ),
         CreatePartialIndex(
         CreatePartialIndex(
             field='Thread.has_reported_posts',
             field='Thread.has_reported_posts',
             index_name='misago_thread_has_reported_posts_partial',
             index_name='misago_thread_has_reported_posts_partial',
@@ -204,30 +228,6 @@ class Migration(migrations.Migration):
                 ('forum', 'replies'),
                 ('forum', 'replies'),
             ]),
             ]),
         ),
         ),
-        migrations.CreateModel(
-            name='Report',
-            fields=[
-                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('reported_by_name', models.CharField(max_length=255)),
-                ('reported_by_slug', models.CharField(max_length=255)),
-                ('reported_by_ip', models.GenericIPAddressField()),
-                ('reported_on', models.DateTimeField(default=django.utils.timezone.now)),
-                ('parsed', models.TextField()),
-                ('checksum', models.CharField(default=b'-', max_length=64)),
-                ('is_closed', models.BooleanField(default=False)),
-                ('closed_by_name', models.CharField(max_length=255)),
-                ('closed_by_slug', models.CharField(max_length=255)),
-                ('closed_by', models.ForeignKey(related_name='closedreport_set', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
-                ('closed_on', models.DateTimeField(default=django.utils.timezone.now)),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
-                ('post', models.ForeignKey(to='misago_threads.Post')),
-                ('reported_by', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
-                ('thread', models.ForeignKey(to='misago_threads.Thread')),
-            ],
-            options={
-            },
-            bases=(models.Model,),
-        ),
         CreatePartialCompositeIndex(
         CreatePartialCompositeIndex(
             model='Report',
             model='Report',
             fields=('post_id', 'is_closed'),
             fields=('post_id', 'is_closed'),

+ 32 - 8
misago/threads/reports.py

@@ -4,30 +4,54 @@ from misago.threads.checksums import update_report_checksum
 from misago.threads.models import Report
 from misago.threads.models import Report
 
 
 
 
+def make_posts_reports_aware(user, thread, posts):
+    make_posts_aware = (
+        user.is_authenticated() and
+        thread.has_reported_posts and
+        thread.acl['can_report']
+    )
+
+    if not make_posts_aware:
+        return
+
+    posts_dict = {}
+    for post in posts:
+        post.is_reported = False
+        post.report = None
+        posts_dict[post.pk] = post
+
+    for report in user.report_set.filter(post_id__in=posts_dict.keys()):
+        posts_dict[report.post_id].is_reported = True
+        posts_dict[report.post_id].report = report
+
+
 def user_has_reported_post(user, post):
 def user_has_reported_post(user, post):
     if not post.has_reports:
     if not post.has_reports:
         return False
         return False
 
 
-    # todo: do exists in the post reports set
+    return post.report_set.filter(reported_by=user).exists()
+
 
 
 def report_post(request, post, message):
 def report_post(request, post, message):
     if message:
     if message:
-        message = parse(message, request, request.user)['parsed_text']
+        message = parse(message, request, request.user,
+                        allow_images=False, allow_blocks=False)
+        message = message['parsed_text']
 
 
     report = Report.objects.create(
     report = Report.objects.create(
         forum=post.forum,
         forum=post.forum,
         thread=post.thread,
         thread=post.thread,
         post=post,
         post=post,
-        reported_by=post.user,
-        reported_by_name=post.user.username,
-        reported_by_slug=post.user.slug,
-        reported_by_ip=post.user.ip,
+        reported_by=request.user,
+        reported_by_name=request.user.username,
+        reported_by_slug=request.user.slug,
+        reported_by_ip=request.user.ip,
         message=message,
         message=message,
         checksum=''
         checksum=''
     )
     )
 
 
     if message:
     if message:
-        update_report_checksum(report.checksum)
+        update_report_checksum(report)
         report.save(update_fields=['checksum'])
         report.save(update_fields=['checksum'])
 
 
     post.thread.has_reported_posts = True
     post.thread.has_reported_posts = True
@@ -36,6 +60,6 @@ def report_post(request, post, message):
 
 
     post.has_reports = True
     post.has_reports = True
     post.has_open_reports = True
     post.has_open_reports = True
-    post.thread.save(update_fields=['has_reports', 'has_open_reports'])
+    post.save(update_fields=['has_reports', 'has_open_reports'])
 
 
     return report
     return report

+ 43 - 0
misago/threads/tests/test_post_views.py

@@ -171,3 +171,46 @@ class DeletePostViewTests(PostViewTestCase):
 
 
         with self.assertRaises(Post.DoesNotExist):
         with self.assertRaises(Post.DoesNotExist):
             Post.objects.get(id=post.id)
             Post.objects.get(id=post.id)
+
+
+class ReportPostViewTests(PostViewTestCase):
+    ajax_header = {'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'}
+
+    def test_cant_report_post(self):
+        """can't access to report post view"""
+        post_link = reverse('misago:report_post', kwargs={
+            'post_id': self.thread.first_post_id
+        })
+
+        self.override_acl({'can_report_content': 0})
+        response = self.client.get(post_link, **self.ajax_header)
+        self.assertEqual(response.status_code, 403)
+
+    def test_report_post_get_form(self):
+        """fetch report post form"""
+        post_link = reverse('misago:report_post', kwargs={
+            'post_id': self.thread.first_post_id
+        })
+
+        self.override_acl({'can_report_content': 1})
+        response = self.client.get(post_link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn("Report post", response.content)
+
+    def test_report_post_post_form(self):
+        """post report post form"""
+        post_link = reverse('misago:report_post', kwargs={
+            'post_id': self.thread.first_post_id
+        })
+
+        self.override_acl({'can_report_content': 1})
+        response = self.client.post(post_link, data={
+            'message': 'Lorem ipsum dolor met!',
+        }, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+
+        # attempt reporting post again
+        self.override_acl({'can_report_content': 1})
+        response = self.client.get(post_link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn("already reported this post", response.content)

+ 23 - 9
misago/threads/views/generic/post.py

@@ -2,7 +2,7 @@ from django.contrib import messages
 from django.core.exceptions import PermissionDenied
 from django.core.exceptions import PermissionDenied
 from django.db.transaction import atomic
 from django.db.transaction import atomic
 from django.http import JsonResponse
 from django.http import JsonResponse
-from django.shortcuts import redirect
+from django.shortcuts import render, redirect
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
@@ -10,7 +10,7 @@ from misago.core.errorpages import not_allowed
 
 
 from misago.threads import permissions, moderation, goto
 from misago.threads import permissions, moderation, goto
 from misago.threads.forms.report import ReportPostForm
 from misago.threads.forms.report import ReportPostForm
-from misago.threads.reports import report_post
+from misago.threads.reports import user_has_reported_post, report_post
 from misago.threads.views.generic.base import ViewBase
 from misago.threads.views.generic.base import ViewBase
 
 
 
 
@@ -143,10 +143,10 @@ class DeletePostView(PostView):
 
 
 
 
 class ReportPostView(PostView):
 class ReportPostView(PostView):
-    is_atomic = False
     require_post = False
     require_post = False
 
 
     template = 'misago/thread/report_modal.html'
     template = 'misago/thread/report_modal.html'
+    alerts_template = 'misago/thread/post_alerts.html'
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
         if not request.is_ajax():
         if not request.is_ajax():
@@ -158,20 +158,27 @@ class ReportPostView(PostView):
         if not post.acl['can_report']:
         if not post.acl['can_report']:
             raise PermissionDenied(_("You can't report posts."))
             raise PermissionDenied(_("You can't report posts."))
 
 
+        if user_has_reported_post(request.user, post):
+            return JsonResponse({
+                'is_reported': True,
+                'message': _("You have already reported this post.")})
+
         form = ReportPostForm()
         form = ReportPostForm()
         if request.method == 'POST':
         if request.method == 'POST':
             form = ReportPostForm(request.POST)
             form = ReportPostForm(request.POST)
             if form.is_valid():
             if form.is_valid():
-                with atomic():
-                    post = self.get_post(request, True, **kwargs)
-                    report_post(request.user,
-                                post,
-                                form.cleaned_data['report_message'])
+                report_post(request,
+                            post,
+                            form.cleaned_data['report_message'])
 
 
                 message = _("%(user)s's post has been "
                 message = _("%(user)s's post has been "
                             "reported to moderators.")
                             "reported to moderators.")
                 message = message % {'user': post.poster_name}
                 message = message % {'user': post.poster_name}
-                return JsonResponse({'message': message})
+                return JsonResponse({
+                    'message': message,
+                    'label': _("Reported"),
+                    'alerts': self.render_alerts(request, post)
+                })
             else:
             else:
                 field_errors = form.errors.get('report_message')
                 field_errors = form.errors.get('report_message')
                 if field_errors:
                 if field_errors:
@@ -183,4 +190,11 @@ class ReportPostView(PostView):
 
 
         return self.render(request, {'form': form})
         return self.render(request, {'form': form})
 
 
+    def render_alerts(self, request, post):
+        return render(request, self.alerts_template, {
+            'forum': post.forum,
+            'thread': post.thread,
+            'post': post
+        }).content
+
 
 

+ 2 - 0
misago/threads/views/generic/thread/view.py

@@ -11,6 +11,7 @@ from misago.users.online.utils import get_user_state
 from misago.threads.events import add_events_to_posts
 from misago.threads.events import add_events_to_posts
 from misago.threads.paginator import paginate
 from misago.threads.paginator import paginate
 from misago.threads.permissions import allow_reply_thread
 from misago.threads.permissions import allow_reply_thread
+from misago.threads.reports import make_posts_reports_aware
 from misago.threads.views.generic.base import ViewBase
 from misago.threads.views.generic.base import ViewBase
 from misago.threads.views.generic.thread.postsactions import PostsActions
 from misago.threads.views.generic.thread.postsactions import PostsActions
 from misago.threads.views.generic.thread.threadactions import ThreadActions
 from misago.threads.views.generic.thread.threadactions import ThreadActions
@@ -92,6 +93,7 @@ class ThreadView(ViewBase):
                     return response
                     return response
 
 
         page, posts = self.get_posts(request.user, forum, thread, kwargs)
         page, posts = self.get_posts(request.user, forum, thread, kwargs)
+        make_posts_reports_aware(request.user, thread, posts)
 
 
         threadstracker.make_posts_read_aware(request.user, thread, posts)
         threadstracker.make_posts_read_aware(request.user, thread, posts)
         threadstracker.read_thread(request.user, thread, posts[-1])
         threadstracker.read_thread(request.user, thread, posts[-1])