diff --git a/app/blueprints/threads/__init__.py b/app/blueprints/threads/__init__.py index a4728a03..e54c7c82 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//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//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//", 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 acd427a3..d37dea30 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 4732af20..f9f298a5 100644 --- a/app/templates/macros/threads.html +++ b/app/templates/macros/threads.html @@ -8,7 +8,7 @@ -
+
+{% if thread.locked %} +

+ + {{ _("This thread has been locked by a moderator.") }} +

+{% endif %} + {% if current_user.is_authenticated %}
- {% if current_user.canCommentRL() %} + {% if not current_user.canCommentRL() %} +
+
+ +
+ {% elif not thread.checkPerm(current_user, "COMMENT_THREAD") %} +
+ {% if thread.locked %} +
+ {% else %} +
+ {% endif %} + +
+ {% else %}

- {% else %} -
-
- -
{% endif %}
diff --git a/app/templates/threads/view.html b/app/templates/threads/view.html index ef53d1f5..f92ef399 100644 --- a/app/templates/threads/view.html +++ b/app/templates/threads/view.html @@ -17,6 +17,19 @@ {% endif %} + {% if thread and thread.checkPerm(current_user, "LOCK_THREAD") %} + {% if thread.locked %} +
+ + +
+ {% else %} +
+ + +
+ {% 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 00000000..337d0fc7 --- /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 ###