escalate or resolve reports #21

This commit is contained in:
rimu 2024-03-27 20:20:08 +13:00
parent 84d98e2e55
commit 090b6d5a68
6 changed files with 105 additions and 6 deletions

View file

@ -4,7 +4,7 @@ from time import sleep
from flask import request, flash, json, url_for, current_app, redirect, g from flask import request, flash, json, url_for, current_app, redirect, g
from flask_login import login_required, current_user from flask_login import login_required, current_user
from flask_babel import _ from flask_babel import _
from sqlalchemy import text, desc from sqlalchemy import text, desc, or_
from app import db, celery, cache from app import db, celery, cache
from app.activitypub.routes import process_inbox_request, process_delete_request from app.activitypub.routes import process_inbox_request, process_delete_request
@ -15,6 +15,7 @@ from app.admin.forms import FederationForm, SiteMiscForm, SiteProfileForm, EditC
from app.admin.util import unsubscribe_from_everything_then_delete, unsubscribe_from_community, send_newsletter, \ from app.admin.util import unsubscribe_from_everything_then_delete, unsubscribe_from_community, send_newsletter, \
topic_tree, topics_for_form topic_tree, topics_for_form
from app.community.util import save_icon_file, save_banner_file from app.community.util import save_icon_file, save_banner_file
from app.constants import REPORT_STATE_NEW, REPORT_STATE_ESCALATED
from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow, Site, Community, CommunityMember, \ from app.models import AllowedInstances, BannedInstances, ActivityPubLog, utcnow, Site, Community, CommunityMember, \
User, Instance, File, Report, Topic, UserRegistration, Role, Post User, Instance, File, Report, Topic, UserRegistration, Role, Post
from app.utils import render_template, permission_required, set_setting, get_setting, gibberish, markdown_to_html, \ from app.utils import render_template, permission_required, set_setting, get_setting, gibberish, markdown_to_html, \
@ -674,7 +675,7 @@ def admin_reports():
search = request.args.get('search', '') search = request.args.get('search', '')
local_remote = request.args.get('local_remote', '') local_remote = request.args.get('local_remote', '')
reports = Report.query.filter_by(status=0) reports = Report.query.filter(or_(Report.status == REPORT_STATE_NEW, Report.status == REPORT_STATE_ESCALATED))
if local_remote == 'local': if local_remote == 'local':
reports = reports.filter_by(ap_id=None) reports = reports.filter_by(ap_id=None)
if local_remote == 'remote': if local_remote == 'remote':

View file

@ -61,6 +61,17 @@ class AddModeratorForm(FlaskForm):
submit = SubmitField(_l('Add')) submit = SubmitField(_l('Add'))
class EscalateReportForm(FlaskForm):
reason = StringField(_l('Amend the report description if necessary'), validators=[DataRequired()])
submit = SubmitField(_l('Escalate report'))
class ResolveReportForm(FlaskForm):
note = StringField(_l('Note for mod log'), validators=[Optional()])
also_resolve_others = BooleanField(_l('Also resolve all other reports about the same thing.'), default=True)
submit = SubmitField(_l('Resolve report'))
class SearchRemoteCommunity(FlaskForm): class SearchRemoteCommunity(FlaskForm):
address = StringField(_l('Community address'), render_kw={'placeholder': 'e.g. !name@server', 'autofocus': True}, validators=[DataRequired()]) address = StringField(_l('Community address'), render_kw={'placeholder': 'e.g. !name@server', 'autofocus': True}, validators=[DataRequired()])
submit = SubmitField(_l('Search')) submit = SubmitField(_l('Search'))

View file

@ -5,19 +5,20 @@ from random import randint
from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, g, json from flask import redirect, url_for, flash, request, make_response, session, Markup, current_app, abort, g, json
from flask_login import current_user, login_required from flask_login import current_user, login_required
from flask_babel import _ from flask_babel import _
from sqlalchemy import or_, desc from sqlalchemy import or_, desc, text
from app import db, constants, cache from app import db, constants, cache
from app.activitypub.signature import RsaKeys, post_request from app.activitypub.signature import RsaKeys, post_request
from app.activitypub.util import default_context, notify_about_post, find_actor_or_create, make_image_sizes from app.activitypub.util import default_context, notify_about_post, find_actor_or_create, make_image_sizes
from app.chat.util import send_message from app.chat.util import send_message
from app.community.forms import SearchRemoteCommunity, CreatePostForm, ReportCommunityForm, \ from app.community.forms import SearchRemoteCommunity, CreatePostForm, ReportCommunityForm, \
DeleteCommunityForm, AddCommunityForm, EditCommunityForm, AddModeratorForm, BanUserCommunityForm DeleteCommunityForm, AddCommunityForm, EditCommunityForm, AddModeratorForm, BanUserCommunityForm, \
EscalateReportForm, ResolveReportForm
from app.community.util import search_for_community, community_url_exists, actor_to_community, \ from app.community.util import search_for_community, community_url_exists, actor_to_community, \
opengraph_parse, url_to_thumbnail_file, save_post, save_icon_file, save_banner_file, send_to_remote_instance, \ opengraph_parse, url_to_thumbnail_file, save_post, save_icon_file, save_banner_file, send_to_remote_instance, \
delete_post_from_community, delete_post_reply_from_community delete_post_from_community, delete_post_reply_from_community
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, \
SUBSCRIPTION_PENDING, SUBSCRIPTION_MODERATOR SUBSCRIPTION_PENDING, SUBSCRIPTION_MODERATOR, REPORT_STATE_NEW, REPORT_STATE_ESCALATED, REPORT_STATE_RESOLVED
from app.inoculation import inoculation from app.inoculation import inoculation
from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, \ from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, \
File, PostVote, utcnow, Report, Notification, InstanceBlock, ActivityPubLog, Topic, Conversation, PostReply File, PostVote, utcnow, Report, Notification, InstanceBlock, ActivityPubLog, Topic, Conversation, PostReply
@ -965,3 +966,48 @@ def community_moderate_banned(actor):
abort(401) abort(401)
else: else:
abort(404) abort(404)
@bp.route('/community/<int:community_id>/moderate_report/<int:report_id>/escalate', methods=['GET', 'POST'])
@login_required
def community_moderate_report_escalate(community_id, report_id):
community = Community.query.get_or_404(community_id)
if community.is_moderator() or current_user.is_admin():
report = Report.query.filter_by(in_community_id=community.id, id=report_id, status=REPORT_STATE_NEW).first()
if report:
form = EscalateReportForm()
if form.validate_on_submit():
notify = Notification(title='Escalated report', url='/admin/reports', user_id=1,
author_id=current_user.id)
db.session.add(notify)
report.description = form.reason.data
report.status = REPORT_STATE_ESCALATED
db.session.commit()
flash(_('Admin has been notified about this report.'))
# todo: remove unread notifications about this report
# todo: append to mod log
return redirect(url_for('community.community_moderate', actor=community.link()))
else:
form.reason.data = report.description
return render_template('community/community_moderate_report_escalate.html', form=form)
else:
abort(401)
@bp.route('/community/<int:community_id>/moderate_report/<int:report_id>/resolve', methods=['GET', 'POST'])
@login_required
def community_moderate_report_resolve(community_id, report_id):
community = Community.query.get_or_404(community_id)
if community.is_moderator() or current_user.is_admin():
report = Report.query.filter_by(in_community_id=community.id, id=report_id).first()
if report:
form = ResolveReportForm()
if form.validate_on_submit():
report.status = REPORT_STATE_RESOLVED
db.session.commit()
# todo: remove unread notifications about this report
# todo: append to mod log
flash(_('Report resolved.'))
return redirect(url_for('community.community_moderate', actor=community.link()))
else:
return render_template('community/community_moderate_report_resolve.html', form=form)

View file

@ -57,7 +57,9 @@
<a href="/post/{{ report.suspect_post_id }}">View</a> <a href="/post/{{ report.suspect_post_id }}">View</a>
{% elif report.suspect_user_id %} {% elif report.suspect_user_id %}
<a href="/user/{{ report.suspect_user_id }}">View</a> <a href="/user/{{ report.suspect_user_id }}">View</a>
{% endif %} {% endif %} |
<a href="{{ url_for('community.community_moderate_report_escalate', community_id=community.id, report_id=report.id) }}">{{ _('Escalate') }}</a> |
<a href="{{ url_for('community.community_moderate_report_resolve', community_id=community.id, report_id=report.id) }}">{{ _('Resolve') }}</a>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}

View file

@ -0,0 +1,20 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% 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">{{ _('Escalate report to admins') }}</div>
<p>{{ _('For reports that could potentially involve legal issues or where you are unsure how to respond, you may prefer to let admins handle it.') }}</p>
{{ render_form(form) }}
</div>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,19 @@
{% if theme() and file_exists('app/templates/themes/' + theme() + '/base.html') %}
{% extends 'themes/' + theme() + '/base.html' %}
{% else %}
{% extends "base.html" %}
{% endif %} %}
{% 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">{{ _('Resolve report') }}</div>
{{ render_form(form) }}
</div>
</div>
</div>
</div>
{% endblock %}