mea culpa function to de-escalate

This commit is contained in:
rimu 2023-12-14 21:22:46 +13:00
parent b32be0127e
commit fb2dc055d3
12 changed files with 149 additions and 32 deletions

View file

@ -379,6 +379,7 @@ def shared_inbox():
if post_reply is not None: if post_reply is not None:
post = Post.query.get(post_id) post = Post.query.get(post_id)
if post.comments_enabled:
db.session.add(post_reply) db.session.add(post_reply)
post.reply_count += 1 post.reply_count += 1
community.post_reply_count += 1 community.post_reply_count += 1
@ -388,6 +389,8 @@ def shared_inbox():
vote = PostReplyVote(user_id=user.id, author_id=post_reply.user_id, post_reply_id=post_reply.id, vote = PostReplyVote(user_id=user.id, author_id=post_reply.user_id, post_reply_id=post_reply.id,
effect=instance_weight(user.ap_domain)) effect=instance_weight(user.ap_domain))
db.session.add(vote) db.session.add(vote)
else:
activity_log.exception_message = 'Comments disabled'
else: else:
activity_log.exception_message = 'Unacceptable type (kbin): ' + object_type activity_log.exception_message = 'Unacceptable type (kbin): ' + object_type
@ -472,9 +475,16 @@ def shared_inbox():
post_reply.body = html_to_markdown(post_reply.body_html) post_reply.body = html_to_markdown(post_reply.body_html)
if post_reply is not None: if post_reply is not None:
post = Post.query.get(post_id)
if post.comments_enabled:
db.session.add(post_reply) db.session.add(post_reply)
community.post_reply_count += 1 community.post_reply_count += 1
community.last_active = utcnow()
post.last_active = utcnow()
activity_log.result = 'success'
db.session.commit() db.session.commit()
else:
activity_log.exception_message = 'Comments disabled'
else: else:
activity_log.exception_message = 'Unacceptable type: ' + object_type activity_log.exception_message = 'Unacceptable type: ' + object_type

View file

@ -429,6 +429,7 @@ class Post(db.Model):
body_html = db.Column(db.Text) body_html = db.Column(db.Text)
type = db.Column(db.Integer) type = db.Column(db.Integer)
comments_enabled = db.Column(db.Boolean, default=True) comments_enabled = db.Column(db.Boolean, default=True)
mea_culpa = db.Column(db.Boolean, default=False)
has_embed = db.Column(db.Boolean, default=False) has_embed = db.Column(db.Boolean, default=False)
reply_count = db.Column(db.Integer, default=0) reply_count = db.Column(db.Integer, default=0)
score = db.Column(db.Integer, default=0, index=True) score = db.Column(db.Integer, default=0, index=True)

View file

@ -12,7 +12,6 @@ class NewReplyForm(FlaskForm):
submit = SubmitField(_l('Comment')) submit = SubmitField(_l('Comment'))
class ReportPostForm(FlaskForm): class ReportPostForm(FlaskForm):
reason_choices = [('1', _l('Breaks community rules')), ('7', _l('Spam')), ('2', _l('Harassment')), reason_choices = [('1', _l('Breaks community rules')), ('7', _l('Spam')), ('2', _l('Harassment')),
('3', _l('Threatening violence')), ('4', _l('Hate / genocide')), ('3', _l('Threatening violence')), ('4', _l('Hate / genocide')),
@ -35,3 +34,7 @@ class ReportPostForm(FlaskForm):
if choice[0] == reason_id: if choice[0] == reason_id:
result.append(str(choice[1])) result.append(str(choice[1]))
return ', '.join(result) return ', '.join(result)
class MeaCulpaForm(FlaskForm):
submit = SubmitField(_l('I changed my mind'))

View file

@ -9,7 +9,7 @@ from app import db, constants
from app.activitypub.signature import HttpSignature from app.activitypub.signature import HttpSignature
from app.activitypub.util import default_context from app.activitypub.util import default_context
from app.community.util import save_post from app.community.util import save_post
from app.post.forms import NewReplyForm, ReportPostForm from app.post.forms import NewReplyForm, ReportPostForm, MeaCulpaForm
from app.community.forms import CreatePostForm from app.community.forms import CreatePostForm
from app.post.util import post_replies, get_comment_branch, post_reply_count from app.post.util import post_replies, get_comment_branch, post_reply_count
from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE
@ -29,12 +29,20 @@ def show_post(post_id: int):
if current_user.is_anonymous and request_etag_matches(current_etag): if current_user.is_anonymous and request_etag_matches(current_etag):
return return_304(current_etag) return return_304(current_etag)
if post.mea_culpa:
flash(_('%(name)s has indicated they made a mistake in this post.', name=post.author.user_name), 'warning')
mods = post.community.moderators() mods = post.community.moderators()
is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods) is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods)
# handle top-level comments/replies # handle top-level comments/replies
form = NewReplyForm() form = NewReplyForm()
if current_user.is_authenticated and current_user.verified and form.validate_on_submit(): if current_user.is_authenticated and current_user.verified and form.validate_on_submit():
if not post.comments_enabled:
flash('Comments have been disabled.', 'warning')
return redirect(url_for('activitypub.post_ap', post_id=post_id))
reply = PostReply(user_id=current_user.id, post_id=post.id, community_id=post.community.id, body=form.body.data, reply = PostReply(user_id=current_user.id, post_id=post.id, community_id=post.community.id, body=form.body.data,
body_html=markdown_to_html(form.body.data), body_html_safe=True, body_html=markdown_to_html(form.body.data), body_html_safe=True,
from_bot=current_user.bot, up_votes=1, nsfw=post.nsfw, nsfl=post.nsfl, from_bot=current_user.bot, up_votes=1, nsfw=post.nsfw, nsfl=post.nsfl,
@ -247,6 +255,11 @@ def continue_discussion(post_id, comment_id):
@login_required @login_required
def add_reply(post_id: int, comment_id: int): def add_reply(post_id: int, comment_id: int):
post = Post.query.get_or_404(post_id) post = Post.query.get_or_404(post_id)
if not post.comments_enabled:
flash('The author of the post has changed their mind so comments have been disabled.', 'warning')
return redirect(url_for('activitypub.post_ap', post_id=post_id))
in_reply_to = PostReply.query.get_or_404(comment_id) in_reply_to = PostReply.query.get_or_404(comment_id)
mods = post.community.moderators() mods = post.community.moderators()
is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods) is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods)
@ -477,3 +490,19 @@ def post_block_instance(post_id: int):
db.session.commit() db.session.commit()
flash(_('Content from %(name)s will be hidden.', name=post.instance.domain)) flash(_('Content from %(name)s will be hidden.', name=post.instance.domain))
return redirect(post.community.local_url()) return redirect(post.community.local_url())
@login_required
@bp.route('/post/<int:post_id>/mea_culpa', methods=['GET', 'POST'])
def post_mea_culpa(post_id: int):
post = Post.query.get_or_404(post_id)
form = MeaCulpaForm()
if form.validate_on_submit():
post.comments_enabled = False
post.mea_culpa = True
post.community.last_active = utcnow()
post.last_active = utcnow()
db.session.commit()
return redirect(url_for('activitypub.post_ap', post_id=post.id))
return render_template('post/post_mea_culpa.html', title=_('I changed my mind'), form=form, post=post)

View file

@ -178,6 +178,10 @@
content: "\ea03"; content: "\ea03";
} }
.fe-mea-culpa::before {
content: "\e997";
}
.fe-block::before { .fe-block::before {
content: "\ea04"; content: "\ea04";
} }

View file

@ -181,6 +181,10 @@ nav, etc which are used site-wide */
content: "\ea03"; content: "\ea03";
} }
.fe-mea-culpa::before {
content: "\e997";
}
.fe-block::before { .fe-block::before {
content: "\ea04"; content: "\ea04";
} }

View file

@ -180,6 +180,10 @@
content: "\ea03"; content: "\ea03";
} }
.fe-mea-culpa::before {
content: "\e997";
}
.fe-block::before { .fe-block::before {
content: "\ea04"; content: "\ea04";
} }

View file

@ -36,7 +36,9 @@
</div> </div>
</div> </div>
<div class="comment_actions hidable"> <div class="comment_actions hidable">
{% if post.comments_enabled %}
<a href="{{ url_for('post.add_reply', post_id=post.id, comment_id=comment['comment'].id) }}" rel="nofollow"><span class="fe fe-reply"></span> reply</a> <a href="{{ url_for('post.add_reply', post_id=post.id, comment_id=comment['comment'].id) }}" rel="nofollow"><span class="fe fe-reply"></span> reply</a>
{% endif %}
</div> </div>
{% if comment['replies'] %} {% if comment['replies'] %}
<div class="replies hidable"> <div class="replies hidable">

View file

@ -29,7 +29,7 @@
<p><a href="{{ url_for('auth.login') }}">{{ _('Log in to comment') }}</a></p> <p><a href="{{ url_for('auth.login') }}">{{ _('Log in to comment') }}</a></p>
{% endif %} {% endif %}
{% else %} {% else %}
<p>{{ _('Comments are disabled for this post.') }}</p> <p>{{ _('Comments are disabled.') }}</p>
{% endif %} {% endif %}
{% if replies %} {% if replies %}
<div class="row post_replies"> <div class="row post_replies">
@ -72,7 +72,9 @@
</div> </div>
{% if current_user.is_authenticated and current_user.verified %} {% if current_user.is_authenticated and current_user.verified %}
<div class="comment_actions hidable"> <div class="comment_actions hidable">
{% if post.comments_enabled %}
<a href="{{ url_for('post.add_reply', post_id=post.id, comment_id=comment['comment'].id) }}" rel="nofollow"><span class="fe fe-reply"></span> reply</a> <a href="{{ url_for('post.add_reply', post_id=post.id, comment_id=comment['comment'].id) }}" rel="nofollow"><span class="fe fe-reply"></span> reply</a>
{% endif %}
</div> </div>
{% endif %} {% endif %}
{% if comment['replies'] %} {% if comment['replies'] %}

View file

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% from 'bootstrap/form.html' import render_form %}
{% block app_content %}
<div class="row">
<div class="col col-login mx-auto">
<div class="card mt-5">
<div class="card-body p-6">
<div class="card-title">{{ _('I changed my mind') }}</div>
<div class="card-body">
<p>{{ _('If you wish to de-escalate the discussion on your post and now feel like it was a mistake, click the button below.') }}</p>
<p>{{ _('No further comments will be posted and a message saying you made a mistake in this post will be displayed.') }}</p>
<p><a href="https://nickpunt.com/blog/deescalating-social-media/" target="_blank">More about this</a></p>
{{ render_form(form) }}
</div>
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -8,12 +8,17 @@
<div class="card-body p-6"> <div class="card-body p-6">
<div class="card-title">{{ _('Options for "%(post_title)s"', post_title=post.title) }}</div> <div class="card-title">{{ _('Options for "%(post_title)s"', post_title=post.title) }}</div>
<ul class="option_list"> <ul class="option_list">
{% if current_user.is_authenticated and (post.user_id == current_user.id or post.community.is_moderator()) %} {% if current_user.is_authenticated %}
{% if post.user_id == current_user.id or post.community.is_moderator() %}
<li><a href="{{ url_for('post.post_edit', post_id=post.id) }}" class="no-underline" rel="nofollow"><span class="fe fe-edit"></span> <li><a href="{{ url_for('post.post_edit', post_id=post.id) }}" class="no-underline" rel="nofollow"><span class="fe fe-edit"></span>
{{ _('Edit') }}</a></li> {{ _('Edit') }}</a></li>
<li><a href="{{ url_for('post.post_delete', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span> <li><a href="{{ url_for('post.post_delete', post_id=post.id) }}" class="no-underline confirm_first" rel="nofollow"><span class="fe fe-delete"></span>
{{ _('Delete') }}</a></li> {{ _('Delete') }}</a></li>
{% endif %} {% endif %}
{% if post.user_id == current_user.id and not post.mea_culpa %}
<li><a href="{{ url_for('post.post_mea_culpa', post_id=post.id) }}" class="no-underline"><span class="fe fe-mea-culpa"></span>
{{ _("I changed my mind") }}</a></li>
{% endif %}
{% if post.user_id != current_user.id %} {% if post.user_id != current_user.id %}
<li><a href="{{ url_for('post.post_block_user', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span> <li><a href="{{ url_for('post.post_block_user', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>
{{ _('Block post author @%(author_name)s', author_name=post.author.user_name) }}</a></li> {{ _('Block post author @%(author_name)s', author_name=post.author.user_name) }}</a></li>
@ -25,6 +30,7 @@
<li><a href="{{ url_for('post.post_block_instance', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span> <li><a href="{{ url_for('post.post_block_instance', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>
{{ _('Hide every post from %(name)s', name=post.instance.domain) }}</a></li> {{ _('Hide every post from %(name)s', name=post.instance.domain) }}</a></li>
{% endif %} {% endif %}
{% endif %}
<li><a href="{{ url_for('post.post_report', post_id=post.id) }}" class="no-underline"><span class="fe fe-report"></span> <li><a href="{{ url_for('post.post_report', post_id=post.id) }}" class="no-underline"><span class="fe fe-report"></span>
{{ _('Report to moderators') }}</a></li> {{ _('Report to moderators') }}</a></li>

View file

@ -0,0 +1,32 @@
"""mea culpa on posts
Revision ID: 238faf5c9b8d
Revises: 5fb8f21295da
Create Date: 2023-12-14 20:50:05.043660
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '238faf5c9b8d'
down_revision = '5fb8f21295da'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('post', schema=None) as batch_op:
batch_op.add_column(sa.Column('mea_culpa', sa.Boolean(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('post', schema=None) as batch_op:
batch_op.drop_column('mea_culpa')
# ### end Alembic commands ###