forum: add an action to change the top comment of a topic

This also prepares the thread_leader macro for top comments of
topics, programs, etc. which have extra stuff to render and more
specific actions.
This commit is contained in:
Lephe 2021-07-12 17:49:54 +02:00 committed by Gitea
parent 7423e522c3
commit 9727c2a986
6 changed files with 71 additions and 13 deletions

View File

@ -16,7 +16,7 @@
# publish.shared-files
#
# Moderation:
# edit.posts
# edit.posts (includes top comment selection)
# edit.tests
# edit.accounts
# edit.trophies

View File

@ -227,6 +227,13 @@ class Member(User):
return self.can_access_post(post) and \
((post.author == self) or self.priv("delete.posts"))
def can_set_topcomment(self, comment):
"""Whether this member can designate the comment as top comment."""
if comment.type != "comment":
return False
post = comment.thread.owner_post
return self.can_edit_post(post) and (comment.author == post.author)
def update(self, **data):
"""
Update all or part of the user's metadata. The [data] dictionary

View File

@ -2,6 +2,8 @@ from app import app, db
from app.models.user import Member
from app.models.post import Post
from app.models.attachment import Attachment
from app.models.topic import Topic
from app.models.program import Program
from app.utils.render import render
from app.utils.check_csrf import check_csrf
from app.forms.forum import CommentEditForm, AnonymousCommentEditForm
@ -78,3 +80,16 @@ def delete_post(postid):
p.delete()
db.session.commit()
return redirect(request.referrer)
@app.route('/post/entete/<int:postid>', methods=['GET'])
@login_required
@check_csrf
def set_post_topcomment(postid):
comment = Post.query.filter_by(id=postid).first_or_404()
if current_user.can_set_topcomment(comment):
comment.thread.top_comment = comment
db.session.add(comment.thread)
db.session.commit()
return redirect(request.referrer)

View File

@ -128,6 +128,12 @@ table.thread td.message img {
max-width: 100%;
}
table.thread .topcomment-placeholder div {
font-style: italic;
opacity: 0.5;
padding: 8px 0;
}
table.thread div.info {
text-align: right;
position: relative;
@ -147,6 +153,7 @@ table.thread div.info summary {
cursor: pointer;
user-select: none;
}
table.thread .context-menu {
position: absolute;
right: 0;

View File

@ -11,7 +11,10 @@
{% block content %}
<section>
<h1>{{ t.title }}</h1>
{{ widget_thread.thread([t.thread.top_comment], None) }}
{% call widget_thread.thread_leader(t.thread.top_comment) %}
{{ t.thread.top_comment.text | md }}
{% endcall %}
{{ widget_pagination.paginate(comments, 'forum_topic', t, {'f': t.forum}) }}

View File

@ -1,11 +1,14 @@
{% import "widgets/user.html" as widget_user %}
{% import "widgets/attachments.html" as widget_attachments %}
{% macro thread(comments, top_comment) %}
<table class="thread {{ 'topcomment' if top_comment == None else ''}} ">
{% if top_comment == None %}
<tr></tr>
{% endif %}
{# Thread widget: this widget expands to a table that shows a list of comments
from a thread, along with message controls.
comments: List of comments to render
top_comment: Thread's top comment (will be elided if encountered) #}
{% macro thread(comments, top_comment, owner=None) %}
<table class="thread">
{% for c in comments %}
{% if c != top_comment %}
<tr id="{{ c.id }}">
@ -18,9 +21,10 @@
{% endif %}
{# TODO: Let guests edit their posts #}
{% set can_edit = current_user.is_authenticated and current_user.can_edit_post(c) %}
{% set can_delete = current_user.is_authenticated and current_user.can_delete_post(c) %}
{% set can_punish = current_user.is_authenticated and current_user.priv("delete.posts") %}
{% set can_edit = current_user.is_authenticated and current_user.can_edit_post(c) %}
{% set can_delete = current_user.is_authenticated and current_user.can_delete_post(c) %}
{% set can_punish = current_user.is_authenticated and current_user.priv("delete.posts") %}
{% set can_topcomm = current_user.is_authenticated and current_user.can_set_topcomment(c) %}
{% if can_edit or can_delete or can_punish %}
<details>
@ -29,12 +33,17 @@
{% if can_edit %}
<a href="{{ url_for('edit_post', postid=c.id, r=request.path) }}">Modifier</a>
{% endif %}
{% if can_punish %}
<a href="{{ url_for('delete_post', postid=c.id, penalty=False, csrf_token=csrf_token()) }}" onclick="return confirm('Le message sera supprimé.')">Supprimer (normal)</a>
<a href="{{ url_for('delete_post', postid=c.id, penalty=True, csrf_token=csrf_token()) }}" onclick="return confirm('Le message sera supprimé avec pénalité d\'XP.')">Supprimer (pénalité)</a>
{% elif can_delete %}
<a href="{{ url_for('delete_post', postid=c.id, penalty=False, csrf_token=csrf_token()) }}" onclick="return confirm('Le message sera supprimé !')">Supprimer</a>
{% endif %}
{% if can_topcomm %}
<a href="{{ url_for('set_post_topcomment', postid=c.id, csrf_token=csrf_token()) }}">Utiliser comme en-tête</a>
{% endif %}
</div>
</details>
{% endif %}
@ -51,11 +60,28 @@
</td>
</tr>
{% elif loop.index0 != 0 %}
<tr id="{{ c.id }}">
<div>Ce message est le top comment</div>
<tr class="topcomment-placeholder">
<td></td>
<td><div>Le commentaire à cet endroit est actuellement utilisé comme en-tête.</div></td>
</tr>
{% endif %}
</tr>
{% endfor %}
</table>
{% endmacro %}
{# Thread leader widget: this widget expands to a single-message thread which
can show more text when called. This is intended for programs and similar
objects which display metadata before description and comments.
leader: Posts's top comment (actual rendering is delegated to caller) #}
{% macro thread_leader(leader) %}
<table class="thread topcomment">
{# Empty line to get normal background (instead of alternate one) #}
<tr></tr>
<tr id="{{ leader.id }}">
<td class="author">{{ widget_user.profile(leader.author) }}</td>
<td class="message">{{ caller() }}</td>
</tr>
</table>
{% endmacro %}