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

+ 1 - 1
misago/forums/migrations/0001_initial.py

@@ -32,7 +32,7 @@ class Migration(migrations.Migration):
                 ('last_thread_title', models.CharField(max_length=255, null=True, blank=True)),
                 ('last_thread_slug', models.CharField(max_length=255, null=True, blank=True)),
                 ('last_poster_name', models.CharField(max_length=255, null=True, blank=True)),
-                ('last_poster_slug', models.SlugField(max_length=255, null=True, blank=True)),
+                ('last_poster_slug', models.CharField(max_length=255, null=True, blank=True)),
                 ('last_post_on', models.DateTimeField(null=True, blank=True)),
                 ('prune_started_after', models.PositiveIntegerField(default=0)),
                 ('prune_replied_after', models.PositiveIntegerField(default=0)),

+ 1 - 1
misago/forums/migrations/0003_forums_roles.py

@@ -83,7 +83,7 @@ def create_default_forums_roles(apps, schema_editor):
     standard.save()
 
     standard_with_polls = ForumRole(
-        name=_('Start and reply threads, make pols'))
+        name=_('Start and reply threads, make polls'))
     pickle_permissions(standard_with_polls,
         {
             # forums perms

+ 1 - 1
misago/forums/models.py

@@ -74,7 +74,7 @@ class Forum(MPTTModel):
                                     null=True, blank=True,
                                     on_delete=models.SET_NULL)
     last_poster_name = models.CharField(max_length=255, null=True, blank=True)
-    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.CharField(max_length=255, null=True, blank=True)
     prune_started_after = models.PositiveIntegerField(default=0)
     prune_replied_after = models.PositiveIntegerField(default=0)
     archive_pruned_in = models.ForeignKey('self',

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

@@ -5,6 +5,8 @@ from django.conf import settings
 from django.db import models, migrations
 import django.db.models.deletion
 
+from misago.core.pgutils import CreatePartialIndex
+
 
 class Migration(migrations.Migration):
 
@@ -43,8 +45,8 @@ class Migration(migrations.Migration):
                 ('edits', models.PositiveIntegerField(default=0)),
                 ('last_editor_name', models.CharField(max_length=255, null=True, blank=True)),
                 ('last_editor_slug', models.SlugField(max_length=255, null=True, blank=True)),
-                ('is_reported', models.BooleanField(default=False, db_index=True)),
-                ('is_moderated', models.BooleanField(default=False)),
+                ('is_reported', 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)),
                 ('forum', models.ForeignKey(to='misago_forums.Forum')),
@@ -56,25 +58,35 @@ class Migration(migrations.Migration):
             },
             bases=(models.Model,),
         ),
+        CreatePartialIndex(
+            field='Post.is_reported',
+            index_name='misago_post_is_reported_partial',
+            condition='is_reported = TRUE',
+        ),
+        CreatePartialIndex(
+            field='Post.is_hidden',
+            index_name='misago_post_is_hidden_partial',
+            condition='is_hidden = FALSE',
+        ),
         migrations.CreateModel(
             name='Thread',
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
-                ('weight', models.PositiveIntegerField(default=0)),
+                ('weight', models.PositiveIntegerField(default=0, db_index=True)),
                 ('title', models.CharField(max_length=255)),
-                ('slug', models.SlugField(max_length=255)),
-                ('replies', models.PositiveIntegerField(default=0)),
+                ('slug', models.CharField(max_length=255)),
+                ('replies', models.PositiveIntegerField(default=0, db_index=True)),
                 ('has_reported_posts', models.BooleanField(default=False)),
                 ('has_moderated_posts', models.BooleanField(default=False)),
                 ('has_hidden_posts', models.BooleanField(default=False)),
-                ('started_on', models.DateTimeField(db_index=True)),
+                ('started_on', models.DateTimeField()),
                 ('starter_name', models.CharField(max_length=255)),
-                ('starter_slug', models.SlugField(max_length=255)),
-                ('last_post_on', models.DateTimeField(db_index=True)),
+                ('starter_slug', models.CharField(max_length=255)),
+                ('last_post_on', models.DateTimeField()),
                 ('last_poster_name', models.CharField(max_length=255, null=True, blank=True)),
-                ('last_poster_slug', models.SlugField(max_length=255, null=True, blank=True)),
+                ('last_poster_slug', models.CharField(max_length=255, null=True, blank=True)),
                 ('is_poll', models.BooleanField(default=False)),
-                ('is_moderated', models.BooleanField(default=False)),
+                ('is_moderated', models.BooleanField(default=False, db_index=True)),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('is_closed', models.BooleanField(default=False)),
             ],
@@ -82,6 +94,21 @@ class Migration(migrations.Migration):
             },
             bases=(models.Model,),
         ),
+        CreatePartialIndex(
+            field='Thread.has_reported_posts',
+            index_name='misago_thread_has_reported_posts_partial',
+            condition='has_reported_posts = TRUE',
+        ),
+        CreatePartialIndex(
+            field='Thread.has_moderated_posts',
+            index_name='misago_thread_has_moderated_posts_partial',
+            condition='has_moderated_posts = TRUE',
+        ),
+        CreatePartialIndex(
+            field='Thread.is_hidden',
+            index_name='misago_thread_is_hidden_partial',
+            condition='is_hidden = FALSE',
+        ),
         migrations.AddField(
             model_name='post',
             name='thread',

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

@@ -27,8 +27,8 @@ class Post(models.Model):
                                     on_delete=models.SET_NULL)
     last_editor_name = models.CharField(max_length=255, null=True, blank=True)
     last_editor_slug = models.SlugField(max_length=255, null=True, blank=True)
-    is_reported = models.BooleanField(default=False, db_index=True)
-    is_moderated = models.BooleanField(default=False)
+    is_reported = 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)
 

+ 6 - 6
misago/threads/models/thread.py

@@ -14,13 +14,13 @@ PINNED = 1
 
 class Thread(models.Model):
     forum = models.ForeignKey('misago_forums.Forum')
-    weight = models.PositiveIntegerField(default=0)
+    weight = models.PositiveIntegerField(default=0, db_index=True)
     prefix = models.ForeignKey('misago_threads.Prefix',
                                null=True, blank=True,
                                on_delete=models.SET_NULL)
     title = models.CharField(max_length=255)
-    slug = models.SlugField(max_length=255)
-    replies = models.PositiveIntegerField(default=0)
+    slug = models.CharField(max_length=255)
+    replies = models.PositiveIntegerField(default=0, db_index=True)
     has_reported_posts = models.BooleanField(default=False)
     has_moderated_posts = models.BooleanField(default=False)
     has_hidden_posts = models.BooleanField(default=False)
@@ -32,7 +32,7 @@ class Thread(models.Model):
                                 null=True, blank=True,
                                 on_delete=models.SET_NULL)
     starter_name = models.CharField(max_length=255)
-    starter_slug = models.SlugField(max_length=255)
+    starter_slug = models.CharField(max_length=255)
     last_post_on = models.DateTimeField()
     last_post = models.ForeignKey('misago_threads.Post', related_name='+',
                                   null=True, blank=True,
@@ -41,9 +41,9 @@ class Thread(models.Model):
                                     null=True, blank=True,
                                     on_delete=models.SET_NULL)
     last_poster_name = models.CharField(max_length=255, null=True, blank=True)
-    last_poster_slug = models.SlugField(max_length=255, null=True, blank=True)
+    last_poster_slug = models.CharField(max_length=255, null=True, blank=True)
     is_poll = models.BooleanField(default=False)
-    is_moderated = models.BooleanField(default=False)
+    is_moderated = models.BooleanField(default=False, db_index=True)
     is_hidden = models.BooleanField(default=False)
     is_closed = models.BooleanField(default=False)
 

+ 105 - 1
misago/threads/permissions.py

@@ -33,11 +33,65 @@ class PermissionsForm(forms.Form):
         coerce=int,
         initial=0,
         choices=((0, _("No")), (1, _("Own threads")), (2, _("All threads"))))
+    can_hide_own_threads = forms.TypedChoiceField(
+        label=_("Can hide own threads"),
+        help_text=_("Only threads started within time limit and "
+                    "with no replies can be hidden."),
+        coerce=int,
+        initial=0,
+        choices=(
+            (0, _("No")),
+            (1, _("Hide threads")),
+            (2, _("Delete threads"))
+        ))
+    thread_edit_time = forms.IntegerField(
+        label=_("Min. time for own thread edit, in minutes"),
+        help_text=_("Enter 0 to don't limit time for editing own threads."),
+        initial=0,
+        min_value=0)
+    can_hide_threads = forms.TypedChoiceField(
+        label=_("Can hide threads"),
+        coerce=int,
+        initial=0,
+        choices=(
+            (0, _("No")),
+            (1, _("Hide threads")),
+            (2, _("Delete threads"))
+        ))
     can_edit_replies = forms.TypedChoiceField(
         label=_("Can edit replies"),
         coerce=int,
         initial=0,
         choices=((0, _("No")), (1, _("Own replies")), (2, _("All replies"))))
+    can_hide_own_replies = forms.TypedChoiceField(
+        label=_("Can hide own replies"),
+        help_text=_("Only last replies to thread made within "
+                    "edit time limit can be hidden."),
+        coerce=int,
+        initial=0,
+        choices=(
+            (0, _("No")),
+            (1, _("Hide replies")),
+            (2, _("Delete replies"))
+        ))
+    reply_edit_time = forms.IntegerField(
+        label=_("Min. time for own reply edit, in minutes"),
+        help_text=_("Enter 0 to don't limit time for editing own replies."),
+        initial=0,
+        min_value=0)
+    can_hide_replies = forms.TypedChoiceField(
+        label=_("Can hide replies"),
+        coerce=int,
+        initial=0,
+        choices=(
+            (0, _("No")),
+            (1, _("Hide replies")),
+            (2, _("Delete replies"))
+        ))
+    can_protect_posts = forms.YesNoSwitch(
+        label=_("Can protect posts"),
+        help_text=_("Only users with this permission "
+                    "can edit protected posts."))
     can_change_threads_weight = forms.TypedChoiceField(
         label=_("Can change threads weight"), coerce=int, initial=0,
         choices=(
@@ -50,6 +104,9 @@ class PermissionsForm(forms.Form):
         coerce=int,
         initial=0,
         choices=((0, _("No")), (1, _("Own threads")), (2, _("All threads"))))
+    can_review_moderated_content = forms.YesNoSwitch(
+        label=_("Can review moderated content"),
+        help_text=_("Will see and be able to accept moderated content."))
 
 
 def change_permissions_form(role):
@@ -63,6 +120,7 @@ def change_permissions_form(role):
 ACL Builder
 """
 def build_acl(acl, roles, key_name):
+    acl['moderated_forums'] = []
     forums_roles = get_forums_roles(roles)
 
     for forum in Forum.objects.all_forums():
@@ -79,16 +137,38 @@ def build_forum_acl(acl, forum, forums_roles, key_name):
     final_acl = {
         'can_see_all_threads': 0,
         'can_start_threads': 0,
+        'can_reply_threads': 0,
+        'can_edit_threads': 0,
+        'can_edit_replies': 0,
+        'can_hide_own_threads': 0,
+        'can_hide_own_replies': 0,
+        'thread_edit_time': 0,
+        'reply_edit_time': 0,
+        'can_hide_threads': 0,
+        'can_hide_replies': 0,
+        'can_protect_posts': 0,
         'can_change_threads_weight': 0,
         'can_close_threads': 0,
+        'can_review_moderated_content': 0,
     }
     final_acl.update(acl)
 
     algebra.sum_acls(final_acl, roles=forum_roles, key=key_name,
         can_see_all_threads=algebra.greater,
         can_start_threads=algebra.greater,
+        can_reply_threads=algebra.greater,
+        can_edit_threads=algebra.greater,
+        can_edit_replies=algebra.greater,
+        can_hide_threads=algebra.greater,
+        can_hide_replies=algebra.greater,
+        can_hide_own_threads=algebra.greater,
+        can_hide_own_replies=algebra.greater,
+        thread_edit_time=algebra.greater_or_zero,
+        reply_edit_time=algebra.greater_or_zero,
+        can_protect_posts=algebra.greater,
         can_change_threads_weight=algebra.greater,
         can_close_threads=algebra.greater,
+        can_review_moderated_content=algebra.greater,
     )
 
     return final_acl
@@ -109,21 +189,45 @@ def add_acl_to_target(user, target):
 def add_acl_to_forum(user, forum):
     forum_acl = user.acl['forums'].get(forum.pk, {})
 
-    forum.acl['can_see_all_threads'] = forum_acl.get('can_see_all_threads', 0)
     forum.acl.update({
+        'can_see_all_threads': 0,
         'can_start_threads': 0,
+        'can_reply_threads': 0,
+        'can_edit_threads': 0,
+        'can_edit_replies': 0,
+        'can_hide_own_threads': 0,
+        'can_hide_own_replies': 0,
+        'thread_edit_time': 0,
+        'reply_edit_time': 0,
+        'can_hide_threads': 0,
+        'can_hide_replies': 0,
+        'can_protect_posts': 0,
         'can_change_threads_weight': 0,
         'can_close_threads': 0,
+        'can_review_moderated_content': 0,
     })
 
     if user.is_authenticated():
         algebra.sum_acls(forum.acl, acls=[forum_acl],
             can_see_all_threads=algebra.greater,
             can_start_threads=algebra.greater,
+            can_reply_threads=algebra.greater,
+            can_edit_threads=algebra.greater,
+            can_edit_replies=algebra.greater,
+            can_hide_threads=algebra.greater,
+            can_hide_replies=algebra.greater,
+            can_hide_own_threads=algebra.greater,
+            can_hide_own_replies=algebra.greater,
+            thread_edit_time=algebra.greater_or_zero,
+            reply_edit_time=algebra.greater_or_zero,
+            can_protect_posts=algebra.greater,
             can_change_threads_weight=algebra.greater,
             can_close_threads=algebra.greater,
+            can_review_moderated_content=algebra.greater,
         )
 
+    forum.acl['can_see_own_threads'] = not forum.acl['can_see_all_threads']
+
 
 def add_acl_to_thread(user, thread):
     pass