]> git.lizzy.rs Git - cheatdb.git/commitdiff
Add ability for moderators to delete comments
authorrubenwardy <rw@rubenwardy.com>
Sat, 11 Jul 2020 02:29:33 +0000 (03:29 +0100)
committerrubenwardy <rw@rubenwardy.com>
Sat, 11 Jul 2020 02:29:38 +0000 (03:29 +0100)
app/blueprints/admin/audit.py
app/blueprints/threads/__init__.py
app/models.py
app/templates/admin/audit.html
app/templates/admin/audit_view.html [new file with mode: 0644]
app/templates/macros/threads.html
app/templates/threads/delete_reply.html [new file with mode: 0644]
app/utils.py
migrations/versions/86512692b770_.py [new file with mode: 0644]

index 64dc3a7440a721d7abaa2ccbb66a904bb4a1e064..5ccac568c5a5cc714be09284a3768bca947be4e0 100644 (file)
 
 
 from flask import Blueprint, render_template, redirect, url_for
-from flask_user import current_user, login_required
+from flask_user import current_user
 from app.models import db, AuditLogEntry, UserRank
 from app.utils import rank_required
 
 from . import bp
 
+
 @bp.route("/admin/audit/")
-@login_required
 @rank_required(UserRank.MODERATOR)
 def audit():
        log = AuditLogEntry.query.order_by(db.desc(AuditLogEntry.created_at)).all()
        return render_template("admin/audit.html", log=log)
+
+
+@bp.route("/admin/audit/<int:id>/")
+@rank_required(UserRank.MODERATOR)
+def audit_view(id):
+       entry = AuditLogEntry.query.get(id)
+       return render_template("admin/audit_view.html", entry=entry)
index e3043c0d68f4fff29dbcf82d55f3ee24d801924b..28a7fde72de1f1937067126550baeac74488cc90 100644 (file)
@@ -107,6 +107,40 @@ def set_lock(id):
        return redirect(thread.getViewURL())
 
 
+@bp.route("/threads/<int:id>/delete/", methods=["GET", "POST"])
+@login_required
+def delete_reply(id):
+       thread = Thread.query.get(id)
+       if thread is None:
+               abort(404)
+
+       reply_id = request.args.get("reply")
+       if reply_id is None:
+               abort(404)
+
+       reply = ThreadReply.query.get(reply_id)
+       if reply is None or reply.thread != thread:
+               abort(404)
+
+       if thread.replies[0] == reply:
+               flash("Cannot delete thread opening post!", "danger")
+               return redirect(thread.getViewURL())
+
+       if not thread.checkPerm(current_user, Permission.DELETE_REPLY):
+               abort(403)
+
+       if request.method == "GET":
+               return render_template("threads/delete_reply.html", thread=thread, reply=reply)
+
+       msg = "Deleted reply by {}".format(reply.author.display_name)
+       addAuditLog(AuditSeverity.MODERATION, current_user, msg, thread.getViewURL(), thread.package, reply.comment)
+
+       db.session.delete(reply)
+       db.session.commit()
+
+       return redirect(thread.getViewURL())
+
+
 @bp.route("/threads/<int:id>/", methods=["GET", "POST"])
 def view(id):
        thread = Thread.query.get(id)
@@ -152,6 +186,7 @@ class ThreadForm(FlaskForm):
        private = BooleanField("Private")
        submit  = SubmitField("Open Thread")
 
+
 @bp.route("/threads/new/", methods=["GET", "POST"])
 @login_required
 def new():
index 13d1fdd986b9ece48be40cae96464b46b73660ff..9e1840e1a31c0377a50f74b74014237fc9ea04ce 100644 (file)
@@ -92,6 +92,7 @@ class Permission(enum.Enum):
        CREATE_THREAD      = "CREATE_THREAD"
        COMMENT_THREAD     = "COMMENT_THREAD"
        LOCK_THREAD        = "LOCK_THREAD"
+       DELETE_REPLY       = "DELETE_REPLY"
        UNAPPROVE_PACKAGE  = "UNAPPROVE_PACKAGE"
        TOPIC_DISCARD      = "TOPIC_DISCARD"
        CREATE_TOKEN       = "CREATE_TOKEN"
@@ -1123,7 +1124,7 @@ class Thread(db.Model):
                elif perm == Permission.COMMENT_THREAD:
                        return canSee and (not self.locked or user.rank.atLeast(UserRank.MODERATOR))
 
-               elif perm == Permission.LOCK_THREAD:
+               elif perm == Permission.LOCK_THREAD or perm == Permission.DELETE_REPLY:
                        return user.rank.atLeast(UserRank.MODERATOR)
 
                else:
@@ -1201,7 +1202,9 @@ class AuditLogEntry(db.Model):
        package_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
        package    = db.relationship("Package", foreign_keys=[package_id])
 
-       def __init__(self, causer, severity, title, url, package=None):
+       description = db.Column(db.Text, nullable=True, default=None)
+
+       def __init__(self, causer, severity, title, url, package=None, description=None):
                if len(title) > 100:
                        title = title[:99] + "…"
 
@@ -1210,6 +1213,7 @@ class AuditLogEntry(db.Model):
                self.title    = title
                self.url      = url
                self.package  = package
+               self.description = description
 
 
 
index 4255b72e4903bebc52dcef7aad35f2391d7255ca..1ac793b5e8474d457d76a56c9f9ce42306f7eb93 100644 (file)
@@ -9,7 +9,13 @@ Audit Log
 
        <div class="list-group mt-3">
                {% for entry in log %}
-                       <a class="list-group-item list-group-item-action" href="{{ entry.url }}">
+                       <a class="list-group-item list-group-item-action"
+                               {% if entry.description %}
+                                       href="{{ url_for('admin.audit_view', id=entry.id) }}">
+                               {% else %}
+                                       href="{{ entry.url }}">
+                               {% endif %}
+
                                <div class="row {% if entry.severity == entry.severity.NORMAL %}text-muted{% endif %}">
                                        <div class="col-sm-auto text-center" style="width: 50px;">
                                                {% if entry.severity == entry.severity.MODERATION %}
@@ -30,6 +36,10 @@ Audit Log
 
                                        <div class="col-sm">
                                                {{ entry.title}}
+
+                                               {% if entry.description %}
+                                                       <i class="fas fa-paperclip ml-3"></i>
+                                               {% endif %}
                                        </div>
 
                                        {% if entry.package %}
@@ -54,5 +64,5 @@ Audit Log
                {% else %}
                        <p class="list-group-item"><i>No audit log entires.</i></p>
                {% endfor %}
-       </ul>
+       </div>
 {% endblock %}
diff --git a/app/templates/admin/audit_view.html b/app/templates/admin/audit_view.html
new file mode 100644 (file)
index 0000000..72e0f27
--- /dev/null
@@ -0,0 +1,19 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{ entry.title }}
+{% endblock %}
+
+{% block content %}
+       {% if entry.url %}
+               <a class="float-right btn btn-primary" href="{{ entry.url }}">View</a>
+       {% endif %}
+
+       <h1>{{ entry.title }}</h1>
+       <p class="text-muted mb-4">
+               {{ _("Caused by %(author)s.", author=entry.causer.display_name) }}
+       </p>
+
+       <pre><code>{{ entry.description }}</code></pre>
+
+{% endblock %}
index f9f298a5f7e49b9e3d09771ede1b76f152f87320..32acbc4c7625b7356ea8bc16ab3632ddcb4ff043 100644 (file)
                                </div>
 
                                <div class="card-body">
+                                       {% if r != thread.replies[0] and thread.checkPerm(current_user, "DELETE_REPLY") %}
+                                               <a class="float-right btn btn-secondary btn-sm"
+                                                               href="{{ url_for('threads.delete_reply', id=thread.id, reply=r.id) }}">
+                                                       <i class="fas fa-trash"></i>
+                                               </a>
+                                       {% endif %}
+
                                        {{ r.comment | markdown }}
                                </div>
                        </div>
diff --git a/app/templates/threads/delete_reply.html b/app/templates/threads/delete_reply.html
new file mode 100644 (file)
index 0000000..6c145df
--- /dev/null
@@ -0,0 +1,22 @@
+{% extends "base.html" %}
+
+{% block title %}
+       Delete reply in {{ thread.title }}
+{% endblock %}
+
+{% block content %}
+       <form method="POST" action="" class="card box_grey">
+               <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+
+               <h3 class="card-header">Delete reply by {{ reply.author.display_name }}</h3>
+               <div class="card-body">
+                       {{ reply.comment | markdown }}
+               </div>
+               <div class="card-body">
+                       <p>Deleting is permanent</p>
+
+                       <a class="btn btn-secondary mr-3" href="{{ thread.getViewURL() }}">Cancel</a>
+                       <input type="submit" value="Delete" class="btn btn-danger" />
+               </div>
+       </form>
+{% endblock %}
index 0f5a91672e09e5a1eb015846f8b72a669148ba6a..ed369b74ba8414d16127c3faee4477125b293a6c 100644 (file)
@@ -204,8 +204,8 @@ def addNotification(target, causer, title, url, package=None):
                db.session.add(notif)
 
 
-def addAuditLog(severity, causer, title, url, package=None):
-       entry = AuditLogEntry(causer, severity, title, url, package)
+def addAuditLog(severity, causer, title, url, package=None, description=None):
+       entry = AuditLogEntry(causer, severity, title, url, package, description)
        db.session.add(entry)
 
 
diff --git a/migrations/versions/86512692b770_.py b/migrations/versions/86512692b770_.py
new file mode 100644 (file)
index 0000000..cbfc990
--- /dev/null
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 86512692b770
+Revises: ba730ce1dc3e
+Create Date: 2020-07-11 01:56:28.634661
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '86512692b770'
+down_revision = 'ba730ce1dc3e'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.add_column('audit_log_entry', sa.Column('description', sa.Text, nullable=True))
+    # ### end Alembic commands ###
+
+
+def downgrade():
+    # ### commands auto generated by Alembic - please adjust! ###
+    op.drop_column('audit_log_entry', 'description')
+    # ### end Alembic commands ###