aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/blueprints/threads/__init__.py38
-rw-r--r--app/models.py20
-rw-r--r--app/templates/macros/threads.html30
-rw-r--r--app/templates/threads/view.html13
-rw-r--r--migrations/versions/8679442b8dde_.py28
5 files changed, 110 insertions, 19 deletions
diff --git a/app/blueprints/threads/__init__.py b/app/blueprints/threads/__init__.py
index a4728a0..e54c7c8 100644
--- a/app/blueprints/threads/__init__.py
+++ b/app/blueprints/threads/__init__.py
@@ -21,7 +21,7 @@ bp = Blueprint("threads", __name__)
from flask_user import *
from app.models import *
-from app.utils import addNotification, clearNotifications
+from app.utils import addNotification, clearNotifications, isYes
import datetime
@@ -60,7 +60,7 @@ def subscribe(id):
thread.watchers.append(current_user)
db.session.commit()
- return redirect(url_for("threads.view", id=id))
+ return redirect(thread.getViewURL())
@bp.route("/threads/<int:id>/unsubscribe/", methods=["POST"])
@@ -75,9 +75,30 @@ def unsubscribe(id):
thread.watchers.remove(current_user)
db.session.commit()
else:
- flash("Not subscribed to thread", "success")
+ flash("Already not subscribed!", "success")
- return redirect(url_for("threads.view", id=id))
+ return redirect(thread.getViewURL())
+
+
+@bp.route("/threads/<int:id>/set-lock/", methods=["POST"])
+@login_required
+def set_lock(id):
+ thread = Thread.query.get(id)
+ if thread is None or not thread.checkPerm(current_user, Permission.LOCK_THREAD):
+ abort(404)
+
+ thread.locked = isYes(request.args.get("lock"))
+ if thread.locked is None:
+ abort(400)
+
+ db.session.commit()
+
+ if thread.locked:
+ flash("Locked thread", "success")
+ else:
+ flash("Unlocked thread", "success")
+
+ return redirect(thread.getViewURL())
@bp.route("/threads/<int:id>/", methods=["GET", "POST"])
@@ -89,12 +110,13 @@ def view(id):
if current_user.is_authenticated and request.method == "POST":
comment = request.form["comment"]
+ if not thread.checkPerm(current_user, Permission.COMMENT_THREAD):
+ flash("You cannot comment on this thread", "danger")
+ return redirect(thread.getViewURL())
+
if not current_user.canCommentRL():
flash("Please wait before commenting again", "danger")
- if package:
- return redirect(package.getDetailsURL())
- else:
- return redirect(url_for("homepage.home"))
+ return redirect(thread.getViewURL())
if len(comment) <= 500 and len(comment) > 3:
reply = ThreadReply()
diff --git a/app/models.py b/app/models.py
index acd427a..d37dea3 100644
--- a/app/models.py
+++ b/app/models.py
@@ -90,6 +90,8 @@ class Permission(enum.Enum):
EDIT_EDITREQUEST = "EDIT_EDITREQUEST"
SEE_THREAD = "SEE_THREAD"
CREATE_THREAD = "CREATE_THREAD"
+ COMMENT_THREAD = "COMMENT_THREAD"
+ LOCK_THREAD = "LOCK_THREAD"
UNAPPROVE_PACKAGE = "UNAPPROVE_PACKAGE"
TOPIC_DISCARD = "TOPIC_DISCARD"
CREATE_TOKEN = "CREATE_TOKEN"
@@ -1075,12 +1077,14 @@ class Thread(db.Model):
package_id = db.Column(db.Integer, db.ForeignKey("package.id"), nullable=True)
package = db.relationship("Package", foreign_keys=[package_id])
- review_id = db.Column(db.Integer, db.ForeignKey("package_review.id"), nullable=True)
- review = db.relationship("PackageReview", foreign_keys=[review_id])
+ review_id = db.Column(db.Integer, db.ForeignKey("package_review.id"), nullable=True)
+ review = db.relationship("PackageReview", foreign_keys=[review_id])
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
title = db.Column(db.String(100), nullable=False)
- private = db.Column(db.Boolean, server_default="0")
+ private = db.Column(db.Boolean, server_default="0", nullable=False)
+
+ locked = db.Column(db.Boolean, server_default="0", nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
@@ -1111,8 +1115,16 @@ class Thread(db.Model):
if self.package:
isMaintainer = isMaintainer or user in self.package.maintainers
+ canSee = not self.private or isMaintainer or user.rank.atLeast(UserRank.EDITOR)
+
if perm == Permission.SEE_THREAD:
- return not self.private or isMaintainer or user.rank.atLeast(UserRank.EDITOR)
+ return canSee
+
+ elif perm == Permission.COMMENT_THREAD:
+ return canSee and (not self.locked or user.rank.atLeast(UserRank.MODERATOR))
+
+ elif perm == Permission.LOCK_THREAD:
+ return user.rank.atLeast(UserRank.MODERATOR)
else:
raise Exception("Permission {} is not related to threads".format(perm.name))
diff --git a/app/templates/macros/threads.html b/app/templates/macros/threads.html
index 4732af2..f9f298a 100644
--- a/app/templates/macros/threads.html
+++ b/app/templates/macros/threads.html
@@ -8,7 +8,7 @@
<img class="img-responsive user-photo img-thumbnail img-thumbnail-1" src="{{ r.author.getProfilePicURL() }}">
</a>
</div>
- <div class="col">
+ <div class="col pr-0">
<div class="card">
<div class="card-header">
<a class="author {{ r.author.rank.name }}"
@@ -30,6 +30,13 @@
{% endfor %}
</ul>
+{% if thread.locked %}
+ <p class="my-0 py-4 text-center">
+ <i class="fas fa-lock mr-3"></i>
+ {{ _("This thread has been locked by a moderator.") }}
+ </p>
+{% endif %}
+
{% if current_user.is_authenticated %}
<div class="row mt-0 mb-4 comments mx-0">
<div class="col-md-1 p-1">
@@ -42,17 +49,26 @@
<a name="reply"></a>
</div>
- {% if current_user.canCommentRL() %}
+ {% if not current_user.canCommentRL() %}
+ <div class="card-body">
+ <textarea class="form-control" readonly disabled>{{ _("Please wait before commenting again.") }}</textarea><br />
+ <input class="btn btn-primary" type="submit" disabled value="Comment" />
+ </div>
+ {% elif not thread.checkPerm(current_user, "COMMENT_THREAD") %}
+ <div class="card-body">
+ {% if thread.locked %}
+ <textarea class="form-control" readonly disabled>{{ _("This thread has been locked.") }}</textarea><br />
+ {% else %}
+ <textarea class="form-control" readonly disabled>{{ _("You don't have permission to post.") }}</textarea><br />
+ {% endif %}
+ <input class="btn btn-primary" type="submit" disabled value="Comment" />
+ </div>
+ {% else %}
<form method="post" action="{{ url_for('threads.view', id=thread.id)}}" class="card-body">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<textarea class="form-control markdown" required maxlength=500 name="comment"></textarea><br />
<input class="btn btn-primary" type="submit" value="Comment" />
</form>
- {% else %}
- <div class="card-body">
- <textarea class="form-control" readonly disabled>Please wait before commenting again.</textarea><br />
- <input class="btn btn-primary" type="submit" disabled value="Comment" />
- </div>
{% endif %}
</div>
</div>
diff --git a/app/templates/threads/view.html b/app/templates/threads/view.html
index ef53d1f..f92ef39 100644
--- a/app/templates/threads/view.html
+++ b/app/templates/threads/view.html
@@ -17,6 +17,19 @@
<input type="submit" class="btn btn-primary" value="Subscribe" />
</form>
{% endif %}
+ {% if thread and thread.checkPerm(current_user, "LOCK_THREAD") %}
+ {% if thread.locked %}
+ <form method="post" action="{{ url_for('threads.set_lock', id=thread.id, lock=0) }}" class="float-right mr-2">
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+ <input type="submit" class="btn btn-secondary" value="{{ _('Unlock Thread') }}" />
+ </form>
+ {% else %}
+ <form method="post" action="{{ url_for('threads.set_lock', id=thread.id, lock=1) }}" class="float-right mr-2">
+ <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
+ <input type="submit" class="btn btn-secondary" value="{{ _('Lock Thread') }}" />
+ </form>
+ {% endif %}
+ {% endif %}
{% endif %}
{% if current_user == thread.author and thread.review %}
diff --git a/migrations/versions/8679442b8dde_.py b/migrations/versions/8679442b8dde_.py
new file mode 100644
index 0000000..337d0fc
--- /dev/null
+++ b/migrations/versions/8679442b8dde_.py
@@ -0,0 +1,28 @@
+"""empty message
+
+Revision ID: 8679442b8dde
+Revises: f612e293070a
+Create Date: 2020-07-11 00:14:02.330903
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '8679442b8dde'
+down_revision = 'f612e293070a'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('thread', sa.Column('locked', sa.Boolean(), server_default='0', nullable=False))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('thread', 'locked')
+ # ### end Alembic commands ###