Rafał Pitoń 10 лет назад
Родитель
Сommit
2e42a16fcd

+ 2 - 2
misago/templates/misago/thread/post.html

@@ -69,10 +69,10 @@
       </div>
       {% endif %}
 
-      {% if post.is_reported and thread.acl.can_see_reports %}
+      {% 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 was reported to moderators." %}
+        {% trans "This post has open reports." %}
       </div>
       {% endif %}
 

+ 2 - 2
misago/templates/misago/threads/base.html

@@ -93,8 +93,8 @@
 
               <ul class="list-unstyled thread-flags">
                 {% block thread-flags %}
-                {% if thread.has_reported_posts %}
-                <li class="tooltip-top" title="{% trans "Reported posts" %}">
+                {% if thread.has_open_reports %}
+                <li class="tooltip-top" title="{% trans "Open reports" %}">
                   <span class="fa fa-exclamation-triangle fa-fw fa-lg"></span>
                 </li>
                 {% endif %}

+ 15 - 0
misago/threads/checksums.py

@@ -16,6 +16,21 @@ def update_post_checksum(post):
     return post.checksum
 
 
+def is_report_valid(report):
+    valid_checksum = make_report_checksum(report)
+    return report.checksum == valid_checksum
+
+
+def make_report_checksum(report):
+    report_seeds = [unicode(v) for v in (report.id, report.reported_by_ip)]
+    return checksums.make_checksum(report.message, report_seeds)
+
+
+def update_report_checksum(report):
+    report.checksum = make_report_checksum(report)
+    return report.checksum
+
+
 def is_event_valid(event):
     valid_checksum = make_event_checksum(event)
     return event.checksum == valid_checksum

+ 37 - 17
misago/threads/migrations/0001_initial.py

@@ -6,7 +6,7 @@ from django.db import models, migrations
 import django.db.models.deletion
 import django.utils.timezone
 
-from misago.core.pgutils import CreatePartialIndex
+from misago.core.pgutils import CreatePartialIndex, CreatePartialCompositeIndex
 
 
 class Migration(migrations.Migration):
@@ -50,7 +50,8 @@ class Migration(migrations.Migration):
                 ('hidden_by_name', models.CharField(max_length=255, null=True, blank=True)),
                 ('hidden_by_slug', models.SlugField(max_length=255, null=True, blank=True)),
                 ('hidden_on', models.DateTimeField(default=django.utils.timezone.now)),
-                ('is_reported', models.BooleanField(default=False)),
+                ('has_reports', models.BooleanField(default=False)),
+                ('has_open_reports', models.BooleanField(default=False)),
                 ('is_moderated', models.BooleanField(default=False, db_index=True)),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('is_protected', models.BooleanField(default=False)),
@@ -64,9 +65,9 @@ class Migration(migrations.Migration):
             bases=(models.Model,),
         ),
         CreatePartialIndex(
-            field='Post.is_reported',
-            index_name='misago_post_is_reported_partial',
-            condition='is_reported = TRUE',
+            field='Post.has_open_reports',
+            index_name='misago_post_has_open_reports_partial',
+            condition='has_open_reports = TRUE',
         ),
         CreatePartialIndex(
             field='Post.is_hidden',
@@ -81,6 +82,7 @@ class Migration(migrations.Migration):
                 ('slug', models.CharField(max_length=255)),
                 ('replies', models.PositiveIntegerField(default=0, db_index=True)),
                 ('has_reported_posts', models.BooleanField(default=False)),
+                ('has_open_reports', models.BooleanField(default=False)),
                 ('has_moderated_posts', models.BooleanField(default=False)),
                 ('has_hidden_posts', models.BooleanField(default=False)),
                 ('has_events', models.BooleanField(default=False)),
@@ -118,18 +120,6 @@ class Migration(migrations.Migration):
             field=models.ManyToManyField(related_name='private_thread_set', through='misago_threads.ThreadParticipant', through_fields=('thread', 'user'), to=settings.AUTH_USER_MODEL),
             preserve_default=True,
         ),
-        migrations.AddField(
-            model_name='thread',
-            name='report_for',
-            field=models.ForeignKey(related_name='report_set', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='misago_threads.Post', null=True),
-            preserve_default=True,
-        ),
-        migrations.AddField(
-            model_name='thread',
-            name='report_in',
-            field=models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, blank=True, to='misago_forums.Forum', null=True),
-            preserve_default=True,
-        ),
         migrations.CreateModel(
             name='Event',
             fields=[
@@ -214,4 +204,34 @@ class Migration(migrations.Migration):
                 ('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(
+            model='Report',
+            fields=('post_id', 'is_closed'),
+            index_name='misago_report_active_reports',
+            condition='is_closed = FALSE',
+        ),
 ]

+ 1 - 0
misago/threads/models/__init__.py

@@ -1,6 +1,7 @@
 # flake8: noqa
 from misago.threads.models.label import *
 from misago.threads.models.post import *
+from misago.threads.models.report import *
 from misago.threads.models.thread import *
 from misago.threads.models.threadparticipant import *
 from misago.threads.models.event import *

+ 2 - 1
misago/threads/models/post.py

@@ -42,7 +42,8 @@ class Post(models.Model):
     hidden_by_slug = models.SlugField(max_length=255, null=True, blank=True)
     hidden_on = models.DateTimeField(default=timezone.now)
 
-    is_reported = models.BooleanField(default=False)
+    has_reports = models.BooleanField(default=False)
+    has_open_reports = models.BooleanField(default=False)
     is_moderated = models.BooleanField(default=False, db_index=True)
     is_hidden = models.BooleanField(default=False)
     is_protected = models.BooleanField(default=False)

+ 28 - 0
misago/threads/models/report.py

@@ -0,0 +1,28 @@
+from django.db import models
+from django.utils import timezone
+
+from misago.conf import settings
+
+
+class Report(models.Model):
+    forum = models.ForeignKey('misago_forums.Forum')
+    thread = models.ForeignKey('misago_threads.Thread')
+    post = models.ForeignKey('misago_threads.Post')
+    reported_by = models.ForeignKey(settings.AUTH_USER_MODEL,
+                                    null=True, blank=True,
+                                    on_delete=models.SET_NULL)
+    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=timezone.now)
+    message = models.TextField()
+    checksum = models.CharField(max_length=64, default='-')
+
+    is_closed = models.BooleanField(default=False)
+    closed_by = models.ForeignKey(settings.AUTH_USER_MODEL,
+                                  null=True, blank=True, db_index=True,
+                                  on_delete=models.SET_NULL,
+                                  related_name='closedreport_set')
+    closed_by_name = models.CharField(max_length=255)
+    closed_by_slug = models.CharField(max_length=255)
+    closed_on = models.DateTimeField(default=timezone.now)

+ 8 - 9
misago/threads/models/thread.py

@@ -13,6 +13,7 @@ class Thread(models.Model):
     slug = models.CharField(max_length=255)
     replies = models.PositiveIntegerField(default=0, db_index=True)
     has_reported_posts = models.BooleanField(default=False)
+    has_open_reports = models.BooleanField(default=False)
     has_moderated_posts = models.BooleanField(default=False)
     has_hidden_posts = models.BooleanField(default=False)
     has_events = models.BooleanField(default=False)
@@ -46,14 +47,6 @@ class Thread(models.Model):
                                           through='ThreadParticipant',
                                           through_fields=('thread', 'user'))
 
-    report_for = models.ForeignKey('misago_threads.Post',
-                                   related_name='report_set',
-                                   null=True, blank=True,
-                                   on_delete=models.SET_NULL)
-    report_in = models.ForeignKey('misago_forums.Forum', related_name='+',
-                                  null=True, blank=True,
-                                  on_delete=models.SET_NULL)
-
     class Meta:
         index_together = [
             ['forum', 'id'],
@@ -91,9 +84,15 @@ class Thread(models.Model):
         if self.replies > 0:
             self.replies -= 1
 
-        reported_post_qs = self.post_set.filter(is_reported=True)
+        reported_post_qs = self.post_set.filter(has_reports=True)
         self.has_reported_posts = reported_post_qs.exists()
 
+        if self.has_reported_posts:
+            open_reports_qs = self.post_set.filter(has_open_reports=True)
+            self.has_open_reports = open_reports_qs.exists()
+        else:
+            self.has_open_reports = False
+
         moderated_post_qs = self.post_set.filter(is_moderated=True)
         self.has_moderated_posts = moderated_post_qs.exists()
 

+ 41 - 0
misago/threads/reports.py

@@ -0,0 +1,41 @@
+from misago.markup import parse
+
+from misago.threads.checksums import update_report_checksum
+from misago.threads.models import Report
+
+
+def user_has_reported_post(user, post):
+    if not post.has_reports:
+        return False
+
+    # todo: do exists in the post reports set
+
+def report_post(request, post, message):
+    if message:
+        message = parse(message, request, request.user)['parsed_text']
+
+    report = Report.objects.create(
+        forum=post.forum,
+        thread=post.thread,
+        post=post,
+        reported_by=post.user,
+        reported_by_name=post.user.username,
+        reported_by_slug=post.user.slug,
+        reported_by_ip=post.user.ip,
+        message=message,
+        checksum=''
+    )
+
+    if message:
+        update_report_checksum(report.checksum)
+        report.save(update_fields=['checksum'])
+
+    post.thread.has_reported_posts = True
+    post.thread.has_open_reports = True
+    post.thread.save(update_fields=['has_reported_posts', 'has_open_reports'])
+
+    post.has_reports = True
+    post.has_open_reports = True
+    post.thread.save(update_fields=['has_reports', 'has_open_reports'])
+
+    return report

+ 2 - 2
misago/threads/tests/test_gotolists_views.py

@@ -113,7 +113,7 @@ class GotoListsViewsTests(AuthenticatedUserTestCase):
         # post 10 reported posts
         posts = []
         for i in xrange(10):
-            posts.append(reply_thread(self.thread, is_reported=True))
+            posts.append(reply_thread(self.thread, has_reports=True))
 
         # assert that posts show
         self.override_acl({'can_see_reports': True})
@@ -130,7 +130,7 @@ class GotoListsViewsTests(AuthenticatedUserTestCase):
         # overflow list via posting extra 20 reported posts
         posts = []
         for i in xrange(20):
-            posts.append(reply_thread(self.thread, is_reported=True))
+            posts.append(reply_thread(self.thread, has_reports=True))
 
         # assert that posts don't show
         self.override_acl({'can_see_reports': True})

+ 2 - 2
misago/threads/testutils.py

@@ -51,7 +51,7 @@ def post_thread(forum, title='Test thread', poster='Tester', is_pinned=False,
 
 
 def reply_thread(thread, poster="Tester", message='I am test message',
-                 is_moderated=False, is_hidden=False, is_reported=False,
+                 is_moderated=False, is_hidden=False, has_reports=False,
                  posted_on=None, poster_ip='127.0.0.1'):
     posted_on = posted_on or thread.last_post_on + timedelta(minutes=5)
 
@@ -66,7 +66,7 @@ def reply_thread(thread, poster="Tester", message='I am test message',
         'updated_on': posted_on,
         'is_moderated': is_moderated,
         'is_hidden': is_hidden,
-        'is_reported': is_reported,
+        'has_reports': has_reports,
     }
 
     try:

+ 1 - 1
misago/threads/views/generic/gotopostslist.py

@@ -56,4 +56,4 @@ class ReportedPostsListView(ModeratedPostsListView):
             raise PermissionDenied(message)
 
     def filter_posts_queryset(self, queryset):
-        return queryset.filter(is_reported=True)
+        return queryset.filter(has_reports=True)

+ 7 - 0
misago/threads/views/generic/post.py

@@ -10,6 +10,7 @@ from misago.core.errorpages import not_allowed
 
 from misago.threads import permissions, moderation, goto
 from misago.threads.forms.report import ReportPostForm
+from misago.threads.reports import report_post
 from misago.threads.views.generic.base import ViewBase
 
 
@@ -161,6 +162,12 @@ class ReportPostView(PostView):
         if request.method == 'POST':
             form = ReportPostForm(request.POST)
             if form.is_valid():
+                with atomic():
+                    post = self.get_post(request, True, **kwargs)
+                    report_post(request.user,
+                                post,
+                                form.cleaned_data['report_message'])
+
                 message = _("%(user)s's post has been "
                             "reported to moderators.")
                 message = message % {'user': post.poster_name}