moderation: added locking capability to topics and programs
parent
b9becbf21f
commit
3c671da85c
|
@ -25,6 +25,7 @@
|
|||
# delete.accounts
|
||||
# delete.shared-files
|
||||
# move.posts
|
||||
# lock.threads
|
||||
#
|
||||
# Shoutbox:
|
||||
# shoutbox.kick
|
||||
|
@ -58,7 +59,7 @@
|
|||
publish.schedule-posts publish.pin-posts publish.shared-files
|
||||
edit.posts edit.tests edit.accounts edit.trophies
|
||||
delete.posts delete.tests delete.accounts delete.shared-files
|
||||
move.posts
|
||||
move.posts lock.threads
|
||||
shoutbox.kick shoutbox.ban
|
||||
misc.unlimited-pms misc.dev-infos misc.admin-panel
|
||||
misc.no-upload-limits misc.arbitrary-login
|
||||
|
@ -69,7 +70,7 @@
|
|||
privs: forum.access.admin
|
||||
edit.posts edit.tests
|
||||
delete.posts delete.tests
|
||||
move.posts
|
||||
move.posts lock.threads
|
||||
shoutbox.kick shoutbox.ban
|
||||
misc.unlimited-pms misc.no-upload-limits
|
||||
-
|
||||
|
|
|
@ -28,6 +28,12 @@ class Comment(Post):
|
|||
def is_top_comment(self):
|
||||
return self.id == self.thread.top_comment_id
|
||||
|
||||
@property
|
||||
def is_metacontent(self):
|
||||
"""Whether if this post is metacontent (topic, program) or actual content"""
|
||||
|
||||
return False
|
||||
|
||||
def __init__(self, author, text, thread):
|
||||
"""
|
||||
Create a new Comment in a thread.
|
||||
|
|
|
@ -29,6 +29,12 @@ class Post(db.Model):
|
|||
'polymorphic_on': type
|
||||
}
|
||||
|
||||
@property
|
||||
def is_metacontent(self):
|
||||
"""Whether if this post is metacontent (topic, program) or actual content"""
|
||||
|
||||
return True
|
||||
|
||||
def __init__(self, author):
|
||||
"""
|
||||
Create a new Post.
|
||||
|
|
|
@ -18,6 +18,9 @@ class Thread(db.Model):
|
|||
owner_topic = db.relationship('Topic')
|
||||
owner_program = db.relationship('Program')
|
||||
|
||||
# Whether the thread is locked
|
||||
locked = db.Column(db.Boolean, default=False)
|
||||
|
||||
# Other fields populated automatically through relations:
|
||||
# <comments> The list of comments (of type Comment)
|
||||
|
||||
|
|
|
@ -263,6 +263,13 @@ class Member(User):
|
|||
post = comment.thread.owner_post
|
||||
return self.can_edit_post(post) and (comment.author == post.author)
|
||||
|
||||
def can_lock_thread(self, post):
|
||||
"""Whether this member can lock the thread associated with the post"""
|
||||
print(post.id, post.is_metacontent)
|
||||
if not post.is_metacontent:
|
||||
return False
|
||||
return self.priv("lock.threads")
|
||||
|
||||
def can_access_file(self, file):
|
||||
"""Whether this member can access the file."""
|
||||
return self.can_access_post(file.comment)
|
||||
|
|
|
@ -31,7 +31,7 @@ def forum_topic(f, page):
|
|||
else:
|
||||
form = AnonymousCommentForm()
|
||||
|
||||
if form.validate_on_submit() and (
|
||||
if form.validate_on_submit() and not t.thread.locked and (
|
||||
V5Config.ENABLE_GUEST_POST or \
|
||||
(current_user.is_authenticated and current_user.can_post_in_forum(f))):
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ from app.models.topic import Topic
|
|||
from app.models.user import Member
|
||||
from app.utils.render import render
|
||||
from app.utils.check_csrf import check_csrf
|
||||
from app.utils.priv_required import priv_required
|
||||
from app.forms.forum import CommentEditForm, AnonymousCommentEditForm, TopicEditForm
|
||||
from app.forms.post import MovePost, SearchThread
|
||||
from wtforms import BooleanField
|
||||
|
@ -24,7 +25,7 @@ def edit_post(postid):
|
|||
referrer = urlparse(request.args.get('r', default = '/', type = str)).path
|
||||
print(referrer)
|
||||
|
||||
p = Post.query.filter_by(id=postid).first_or_404()
|
||||
p = Post.query.get_or_404(postid)
|
||||
|
||||
# Check permissions
|
||||
if not current_user.can_edit_post(p):
|
||||
|
@ -104,7 +105,7 @@ def edit_post(postid):
|
|||
@check_csrf
|
||||
def delete_post(postid):
|
||||
next_page = request.referrer
|
||||
p = Post.query.filter_by(id=postid).first_or_404()
|
||||
p = Post.query.get_or_404(postid)
|
||||
xp = -1
|
||||
|
||||
if not current_user.can_delete_post(p):
|
||||
|
@ -142,7 +143,7 @@ def delete_post(postid):
|
|||
@login_required
|
||||
@check_csrf
|
||||
def set_post_topcomment(postid):
|
||||
comment = Post.query.filter_by(id=postid).first_or_404()
|
||||
comment = Post.query.get_or_404(postid)
|
||||
|
||||
if current_user.can_set_topcomment(comment):
|
||||
comment.thread.top_comment = comment
|
||||
|
@ -155,7 +156,7 @@ def set_post_topcomment(postid):
|
|||
@app.route('/post/deplacer/<int:postid>', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def move_post(postid):
|
||||
comment = Post.query.filter_by(id=postid).first_or_404()
|
||||
comment = Post.query.get_or_404(postid)
|
||||
|
||||
if not current_user.can_edit_post(comment):
|
||||
abort(403)
|
||||
|
@ -194,3 +195,25 @@ def move_post(postid):
|
|||
|
||||
return render('post/move_post.html', comment=comment,
|
||||
search_form=search_form, move_form=move_form)
|
||||
|
||||
@app.route('/post/verrouiller/<int:postid>', methods=['GET'])
|
||||
@priv_required("lock.threads")
|
||||
@check_csrf
|
||||
def lock_thread(postid):
|
||||
post = Post.query.get_or_404(postid)
|
||||
|
||||
if not post.is_metacontent:
|
||||
flash("Vous ne pouvez pas verrouiller ce contenu (n'est pas de type metacontenu)", 'error')
|
||||
abort(403)
|
||||
|
||||
post.thread.locked = not post.thread.locked
|
||||
|
||||
db.session.add(post.thread)
|
||||
db.session.commit()
|
||||
|
||||
if post.thread.locked:
|
||||
flash(f"Le thread a été verrouillé")
|
||||
else:
|
||||
flash(f"Le thread a été déverrouillé")
|
||||
|
||||
return redirect(request.referrer)
|
|
@ -19,7 +19,7 @@ def program_view(page):
|
|||
else:
|
||||
form = AnonymousCommentForm()
|
||||
|
||||
if form.validate_on_submit() and (
|
||||
if form.validate_on_submit() and not p.thread.locked and (
|
||||
V5Config.ENABLE_GUEST_POST or current_user.is_authenticated):
|
||||
|
||||
# Manage author
|
||||
|
|
|
@ -36,7 +36,9 @@
|
|||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if V5Config.ENABLE_GUEST_POST
|
||||
{% if t.thread.locked %}
|
||||
<div class="locked">Les commentaires sont verrouillés</div>
|
||||
{% elif V5Config.ENABLE_GUEST_POST
|
||||
or (current_user.is_authenticated and current_user.can_post_in_forum(t.forum)) %}
|
||||
<div class=form>
|
||||
<h3>Commenter le sujet</h3>
|
||||
|
|
|
@ -60,7 +60,10 @@
|
|||
|
||||
{{ widget_pagination.paginate(comments, 'program_view', p) }}
|
||||
|
||||
{% if V5Config.ENABLE_GUEST_POST or current_user.is_authenticated %}
|
||||
|
||||
{% if p.thread.locked %}
|
||||
<div class="locked">Les commentaires sont verrouillés</div>
|
||||
{% elif V5Config.ENABLE_GUEST_POST or current_user.is_authenticated %}
|
||||
<div class=form>
|
||||
<h3>Commenter le programme</h3>
|
||||
<form action="" method="post" enctype="multipart/form-data">
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
{% set can_punish = auth and current_user.can_punish_post(post) %}
|
||||
{% set can_topcomm = auth and current_user.can_set_topcomment(post) %}
|
||||
{% set can_move = auth and current_user.can_edit_post(post) and post.type == "comment" %}
|
||||
{% set can_lock = auth and current_user.can_lock_thread(post) %}
|
||||
|
||||
{% if post.type == "topic" %}
|
||||
{% set suffix = " le sujet" %}
|
||||
|
@ -19,7 +20,7 @@
|
|||
{% set suffix = " le programme" %}
|
||||
{% endif %}
|
||||
|
||||
{% if can_edit or can_move or can_delete or can_punish or can_topcomm %}
|
||||
{% if can_edit or can_move or can_delete or can_punish or can_topcomm or can_lock %}
|
||||
<details>
|
||||
<summary><b>⋮</b></summary>
|
||||
<div class='context-menu'>
|
||||
|
@ -41,6 +42,11 @@
|
|||
{% if can_topcomm %}
|
||||
<a href="{{ url_for('set_post_topcomment', postid=post.id, csrf_token=csrf_token()) }}">Utiliser comme en-tête</a>
|
||||
{% endif %}
|
||||
|
||||
{% if can_lock %}
|
||||
{% set prefix = "Déverrouiller" if post.thread.locked else "Verrouiller" %}
|
||||
<a href="{{ url_for('lock_thread', postid=post.id, csrf_token=csrf_token()) }}">{{ prefix }}{{ suffix }}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
|
|
|
@ -73,8 +73,7 @@ def run_migrations_online():
|
|||
context.configure(connection=connection,
|
||||
target_metadata=target_metadata,
|
||||
process_revision_directives=process_revision_directives,
|
||||
**current_app.extensions['migrate'].configure_args,
|
||||
compare_type=True)
|
||||
**current_app.extensions['migrate'].configure_args)
|
||||
|
||||
try:
|
||||
with context.begin_transaction():
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
"""Threads: add locking capability
|
||||
|
||||
Revision ID: 5ffc4e562ed8
|
||||
Revises: ba47de949e59
|
||||
Create Date: 2023-06-06 21:19:27.950523
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '5ffc4e562ed8'
|
||||
down_revision = 'ba47de949e59'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('thread', schema=None) as batch_op:
|
||||
batch_op.add_column(sa.Column('locked', sa.Boolean(), nullable=True))
|
||||
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
with op.batch_alter_table('thread', schema=None) as batch_op:
|
||||
batch_op.drop_column('locked')
|
||||
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue