aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrubenwardy <rw@rubenwardy.com>2020-07-11 03:29:33 +0100
committerrubenwardy <rw@rubenwardy.com>2020-07-11 03:29:38 +0100
commit31b8a7931bdb95b296e236c11705206507b035d8 (patch)
treedafcdca8f5dea95a326c08125f7d035be812ddd9
parenta4dd4f04293b6ad6dab5d3dc0a4c52a3290b4394 (diff)
downloadcheatdb-31b8a7931bdb95b296e236c11705206507b035d8.tar.xz
Add ability for moderators to delete comments
-rw-r--r--app/blueprints/admin/audit.py11
-rw-r--r--app/blueprints/threads/__init__.py35
-rw-r--r--app/models.py8
-rw-r--r--app/templates/admin/audit.html14
-rw-r--r--app/templates/admin/audit_view.html19
-rw-r--r--app/templates/macros/threads.html7
-rw-r--r--app/templates/threads/delete_reply.html22
-rw-r--r--app/utils.py4
-rw-r--r--migrations/versions/86512692b770_.py28
9 files changed, 140 insertions, 8 deletions
diff --git a/app/blueprints/admin/audit.py b/app/blueprints/admin/audit.py
index 64dc3a7..5ccac56 100644
--- a/app/blueprints/admin/audit.py
+++ b/app/blueprints/admin/audit.py
@@ -16,15 +16,22 @@
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)
diff --git a/app/blueprints/threads/__init__.py b/app/blueprints/threads/__init__.py
index e3043c0..28a7fde 100644
--- a/app/blueprints/threads/__init__.py
+++ b/app/blueprints/threads/__init__.py
@@ -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():
diff --git a/app/models.py b/app/models.py
index 13d1fdd..9e1840e 100644
--- a/app/models.py
+++ b/app/models.py
@@ -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
diff --git a/app/templates/admin/audit.html b/app/templates/admin/audit.html
index 4255b72..1ac793b 100644
--- a/app/templates/admin/audit.html
+++ b/app/templates/admin/audit.html
@@ -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
index 0000000..72e0f27
--- /dev/null
+++ b/app/templates/admin/audit_view.html
@@ -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 %}
diff --git a/app/templates/macros/threads.html b/app/templates/macros/threads.html
index f9f298a..32acbc4 100644
--- a/app/templates/macros/threads.html
+++ b/app/templates/macros/threads.html
@@ -22,6 +22,13 @@
</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
index 0000000..6c145df
--- /dev/null
+++ b/app/templates/threads/delete_reply.html
@@ -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 %}
diff --git a/app/utils.py b/app/utils.py
index 0f5a916..ed369b7 100644
--- a/app/utils.py
+++ b/app/utils.py
@@ -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
index 0000000..cbfc990
--- /dev/null
+++ b/migrations/versions/86512692b770_.py
@@ -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 ###