From a12e1b97cd07646c5809eb49f92c27a2bbd44be1 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Tue, 19 Mar 2024 14:52:24 +0000 Subject: [PATCH] Add the ability to include moderator notes on feeds. --- hamnadmin/hamnadmin/register/admin.py | 7 +- .../migrations/0007_moderatornotes.py | 31 ++++++ hamnadmin/hamnadmin/register/models.py | 15 +++ .../hamnadmin/register/templates/edit.html | 34 ++++++- .../register/templates/moderate.html | 20 ++++ hamnadmin/hamnadmin/register/views.py | 98 ++++++++++--------- 6 files changed, 157 insertions(+), 48 deletions(-) create mode 100644 hamnadmin/hamnadmin/register/migrations/0007_moderatornotes.py diff --git a/hamnadmin/hamnadmin/register/admin.py b/hamnadmin/hamnadmin/register/admin.py index 7ca4a1f..7dde100 100644 --- a/hamnadmin/hamnadmin/register/admin.py +++ b/hamnadmin/hamnadmin/register/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin -from hamnadmin.register.models import Blog, Team, Post, AggregatorLog +from hamnadmin.register.models import Blog, Team, Post, AggregatorLog, ModeratorNotes class TeamAdmin(admin.ModelAdmin): @@ -29,7 +29,12 @@ class AggregatorLogAdmin(admin.ModelAdmin): list_display = ['ts', 'success', 'feed', 'info'] +class ModeratorNotesAdmin(admin.ModelAdmin): + list_display = ['ts', 'user', 'feed', 'note'] + + admin.site.register(Team, TeamAdmin) admin.site.register(Blog, BlogAdmin) admin.site.register(Post, PostAdmin) admin.site.register(AggregatorLog, AggregatorLogAdmin) +admin.site.register(ModeratorNotes, ModeratorNotesAdmin) diff --git a/hamnadmin/hamnadmin/register/migrations/0007_moderatornotes.py b/hamnadmin/hamnadmin/register/migrations/0007_moderatornotes.py new file mode 100644 index 0000000..cbc28a8 --- /dev/null +++ b/hamnadmin/hamnadmin/register/migrations/0007_moderatornotes.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.16 on 2024-03-19 14:51 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('register', '0006_blog_lastsuccess'), + ] + + operations = [ + migrations.CreateModel( + name='ModeratorNotes', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('ts', models.DateTimeField(auto_now=True)), + ('note', models.TextField()), + ('feed', models.ForeignKey(db_column='feed', on_delete=django.db.models.deletion.CASCADE, to='register.blog')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name_plural': 'Moderator notes', + 'db_table': 'moderatornotes', + 'ordering': ['-ts'], + }, + ), + ] diff --git a/hamnadmin/hamnadmin/register/models.py b/hamnadmin/hamnadmin/register/models.py index 1ce886d..a06c7a0 100644 --- a/hamnadmin/hamnadmin/register/models.py +++ b/hamnadmin/hamnadmin/register/models.py @@ -136,3 +136,18 @@ class AggregatorLog(models.Model): def __str__(self): return "Log entry for %s (%s)" % (self.feed.name, self.ts) + + +class ModeratorNotes(models.Model): + ts = models.DateTimeField(auto_now=True) + user = models.ForeignKey(User, null=False, blank=False, on_delete=models.CASCADE) + feed = models.ForeignKey(Blog, null=False, blank=False, db_column='feed', on_delete=models.CASCADE) + note = models.TextField() + + class Meta: + db_table = 'moderatornotes' + verbose_name_plural = 'Moderator notes' + ordering = ['-ts'] + + def __str__(self): + return "Note for %s by %s at %s" % (self.feed.name, self.user, self.ts) diff --git a/hamnadmin/hamnadmin/register/templates/edit.html b/hamnadmin/hamnadmin/register/templates/edit.html index 190dbd2..d39bc20 100644 --- a/hamnadmin/hamnadmin/register/templates/edit.html +++ b/hamnadmin/hamnadmin/register/templates/edit.html @@ -30,8 +30,40 @@ automatically submitted for approval. {% endif %}{#has_entries#} {% endif %}{#approved#} +

{% endif %}{#not new#} -

+ + {% if user.is_superuser %} +

Moderator Notes

+ {% if notes %} + + + + + + + {% for n in notes %} + + + + + + {% endfor %} +
NoteModeratorDate/Time
{{ n.note }}{{ n.user }}{{ n.ts|date:"Y-m-d H:i:s" }}
+ {% endif %} + +
{% csrf_token %} +
+
+ + +
+
+ +
+
+
+ {% endif %} {% if messages %}

Results

diff --git a/hamnadmin/hamnadmin/register/templates/moderate.html b/hamnadmin/hamnadmin/register/templates/moderate.html index 4dbe38a..1efb582 100644 --- a/hamnadmin/hamnadmin/register/templates/moderate.html +++ b/hamnadmin/hamnadmin/register/templates/moderate.html @@ -38,6 +38,26 @@ {% if blog.authorfilter %}
Author filter: {{ blog.authorfilter }}{% endif %}
  + + {% if user.is_superuser and blog.moderatornotes_set.all %} + +
+
Moderator notes
+
+ + + {% for n in blog.moderatornotes_set.all %} + + + + + + {% endfor %} +
NotesModeratorDate/Time
{{ n.note }}{{ n.user }}{{ n.ts|date:"Y-m-d H:i:s" }}
+
+
+ {% endif %} +
Posts
diff --git a/hamnadmin/hamnadmin/register/views.py b/hamnadmin/hamnadmin/register/views.py index 299be79..f5d38af 100644 --- a/hamnadmin/hamnadmin/register/views.py +++ b/hamnadmin/hamnadmin/register/views.py @@ -6,7 +6,7 @@ from django.db import transaction from django.db.models import Count, Max, Q, Subquery, OuterRef, Exists, FilteredRelation from django.contrib import messages -from hamnadmin.register.models import Post, Blog, Team, AggregatorLog, AuditEntry +from hamnadmin.register.models import Post, Blog, Team, AggregatorLog, AuditEntry, ModeratorNotes from hamnadmin.mailqueue.util import send_simple_mail from hamnadmin.util.varnish import purge_url, purge_xkey, purge_root_and_feeds @@ -87,51 +87,56 @@ def edit(request, id=None): blog = Blog(user=request.user, name="{0} {1}".format(request.user.first_name, request.user.last_name)) if request.method == 'POST': - saved_url = blog.feedurl - saved_filter = blog.authorfilter - saved_team = blog.team - form = BlogEditForm(request, data=request.POST, instance=blog) - if form.is_valid(): - if id: - # This is an existing one. If we change the URL of the blog, it needs to be - # de-moderated if it was previously approved. - if blog.approved: - if saved_url != form.cleaned_data['feedurl'] or saved_filter != form.cleaned_data['authorfilter']: - obj = form.save() - obj.approved = False - obj.save(update_fields=['approved']) - - send_simple_mail( - settings.EMAIL_SENDER, - settings.NOTIFICATION_RECEIVER, - "A blog was edited on Planet PostgreSQL", - "The blog at {0}\nwas edited by {1} in a way that needs new moderation.\n\nTo moderate: https://planet.postgresql.org/register/moderate/\n\n".format(blog.feedurl, blog.user), - sendername="Planet PostgreSQL", - receivername="Planet PostgreSQL Moderators", - ) - - messages.warning(request, "Blog has been resubmitted for moderation, and is temporarily disabled.") - - purge_root_and_feeds() - purge_url('/feeds.html') - - return HttpResponseRedirect("/register/edit/{0}/".format(obj.id)) - - obj = form.save() - - if obj.team and obj.team != saved_team: - # We allow anybody to join a team by default, and will just send a notice - # so the team manager can undo it. - send_simple_mail(settings.EMAIL_SENDER, - obj.team.manager.email, - "A blog joined your team on Planet PostgreSQL", - "The blog at {0} by {1} {2}\nhas been added to your team {3} on Planet PostgreSQL\n\nIf this is correct, you do not need to do anything.\n\nIf this is incorrect, please go to\n\nhttps://planet.postgresql.org/register/\n\nand click the button to remove the blog from your team.\nWe apologize if this causes work for you.\n\n".format( - obj.feedurl, - obj.user.first_name, obj.user.last_name, - obj.team.name), - sendername="Planet PostgreSQL", - receivername="{0} {1}".format(obj.team.manager.first_name, obj.team.manager.last_name), - ) + if 'note' in request.POST: + form = BlogEditForm(request, instance=blog) + note = ModeratorNotes(feed=blog, note=request.POST['note'], user=request.user) + note.save() + else: + saved_url = blog.feedurl + saved_filter = blog.authorfilter + saved_team = blog.team + form = BlogEditForm(request, data=request.POST, instance=blog) + if form.is_valid(): + if id: + # This is an existing one. If we change the URL of the blog, it needs to be + # de-moderated if it was previously approved. + if blog.approved: + if saved_url != form.cleaned_data['feedurl'] or saved_filter != form.cleaned_data['authorfilter']: + obj = form.save() + obj.approved = False + obj.save(update_fields=['approved']) + + send_simple_mail( + settings.EMAIL_SENDER, + settings.NOTIFICATION_RECEIVER, + "A blog was edited on Planet PostgreSQL", + "The blog at {0}\nwas edited by {1} in a way that needs new moderation.\n\nTo moderate: https://planet.postgresql.org/register/moderate/\n\n".format(blog.feedurl, blog.user), + sendername="Planet PostgreSQL", + receivername="Planet PostgreSQL Moderators", + ) + + messages.warning(request, "Blog has been resubmitted for moderation, and is temporarily disabled.") + + purge_root_and_feeds() + purge_url('/feeds.html') + + return HttpResponseRedirect("/register/edit/{0}/".format(obj.id)) + + obj = form.save() + + if obj.team and obj.team != saved_team: + # We allow anybody to join a team by default, and will just send a notice + # so the team manager can undo it. + send_simple_mail(settings.EMAIL_SENDER, + obj.team.manager.email, + "A blog joined your team on Planet PostgreSQL", + "The blog at {0} by {1} {2}\nhas been added to your team {3} on Planet PostgreSQL\n\nIf this is correct, you do not need to do anything.\n\nIf this is incorrect, please go to\n\nhttps://planet.postgresql.org/register/\n\nand click the button to remove the blog from your team.\nWe apologize if this causes work for you.\n\n".format( + obj.feedurl, + obj.user.first_name, obj.user.last_name, + obj.team.name), + sendername="Planet PostgreSQL", + receivername="{0} {1}".format(obj.team.manager.first_name, obj.team.manager.last_name), + ) return HttpResponseRedirect("/register/edit/{0}/".format(obj.id)) else: @@ -142,6 +147,7 @@ def edit(request, id=None): 'form': form, 'blog': blog, 'log': AggregatorLog.objects.filter(feed=blog).order_by('-ts')[:30], + 'notes': ModeratorNotes.objects.filter(feed=blog).order_by('-ts'), 'posts': Post.objects.filter(feed=blog).order_by('-dat')[:10], 'title': 'Edit blog: %s' % blog.name, }) -- 2.39.5