mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
community moderation - view reports and list of banned people
This commit is contained in:
parent
388177c7c1
commit
f5f5f593a5
8 changed files with 263 additions and 3 deletions
|
@ -852,3 +852,59 @@ def community_notification(community_id: int):
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return render_template('community/_notification_toggle.html', community=community)
|
return render_template('community/_notification_toggle.html', community=community)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<actor>/moderate', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def community_moderate(actor):
|
||||||
|
community = actor_to_community(actor)
|
||||||
|
|
||||||
|
if community is not None:
|
||||||
|
if community.is_moderator() or current_user.is_admin():
|
||||||
|
|
||||||
|
page = request.args.get('page', 1, type=int)
|
||||||
|
search = request.args.get('search', '')
|
||||||
|
local_remote = request.args.get('local_remote', '')
|
||||||
|
|
||||||
|
reports = Report.query.filter_by(status=0, in_community_id=community.id)
|
||||||
|
if local_remote == 'local':
|
||||||
|
reports = reports.filter_by(ap_id=None)
|
||||||
|
if local_remote == 'remote':
|
||||||
|
reports = reports.filter(Report.ap_id != None)
|
||||||
|
reports = reports.order_by(desc(Report.created_at)).paginate(page=page, per_page=1000, error_out=False)
|
||||||
|
|
||||||
|
next_url = url_for('admin.admin_reports', page=reports.next_num) if reports.has_next else None
|
||||||
|
prev_url = url_for('admin.admin_reports', page=reports.prev_num) if reports.has_prev and page != 1 else None
|
||||||
|
|
||||||
|
return render_template('community/community_moderate.html', title=_('Moderation of %(community)s', community=community.display_name()),
|
||||||
|
community=community, reports=reports, current='reports',
|
||||||
|
next_url=next_url, prev_url=prev_url,
|
||||||
|
moderating_communities=moderating_communities(current_user.get_id()),
|
||||||
|
joined_communities=joined_communities(current_user.get_id()),
|
||||||
|
inoculation=inoculation[randint(0, len(inoculation) - 1)]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
abort(401)
|
||||||
|
else:
|
||||||
|
abort(404)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/<actor>/moderate/banned', methods=['GET'])
|
||||||
|
@login_required
|
||||||
|
def community_moderate_banned(actor):
|
||||||
|
community = actor_to_community(actor)
|
||||||
|
|
||||||
|
if community is not None:
|
||||||
|
if community.is_moderator() or current_user.is_admin():
|
||||||
|
banned_people = User.query.join(CommunityBan, CommunityBan.user_id == User.id).filter(CommunityBan.community_id == community.id).all()
|
||||||
|
return render_template('community/community_moderate_banned.html',
|
||||||
|
title=_('People banned from of %(community)s', community=community.display_name()),
|
||||||
|
community=community, banned_people=banned_people, current='banned',
|
||||||
|
moderating_communities=moderating_communities(current_user.get_id()),
|
||||||
|
joined_communities=joined_communities(current_user.get_id()),
|
||||||
|
inoculation=inoculation[randint(0, len(inoculation) - 1)]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
abort(401)
|
||||||
|
else:
|
||||||
|
abort(404)
|
|
@ -1120,11 +1120,12 @@ class Report(db.Model):
|
||||||
status = db.Column(db.Integer, default=0)
|
status = db.Column(db.Integer, default=0)
|
||||||
type = db.Column(db.Integer, default=0) # 0 = user, 1 = post, 2 = reply, 3 = community, 4 = conversation
|
type = db.Column(db.Integer, default=0) # 0 = user, 1 = post, 2 = reply, 3 = community, 4 = conversation
|
||||||
reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
reporter_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||||
suspect_community_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
suspect_community_id = db.Column(db.Integer, db.ForeignKey('community.id'))
|
||||||
suspect_user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
suspect_user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||||
suspect_post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
suspect_post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
|
||||||
suspect_post_reply_id = db.Column(db.Integer, db.ForeignKey('post_reply.id'))
|
suspect_post_reply_id = db.Column(db.Integer, db.ForeignKey('post_reply.id'))
|
||||||
suspect_conversation_id = db.Column(db.Integer, db.ForeignKey('conversation.id'))
|
suspect_conversation_id = db.Column(db.Integer, db.ForeignKey('conversation.id'))
|
||||||
|
in_community_id = db.Column(db.Integer, db.ForeignKey('community.id'))
|
||||||
created_at = db.Column(db.DateTime, default=utcnow)
|
created_at = db.Column(db.DateTime, default=utcnow)
|
||||||
updated = db.Column(db.DateTime, default=utcnow)
|
updated = db.Column(db.DateTime, default=utcnow)
|
||||||
|
|
||||||
|
|
|
@ -795,7 +795,7 @@ def post_report(post_id: int):
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
report = Report(reasons=form.reasons_to_string(form.reasons.data), description=form.description.data,
|
report = Report(reasons=form.reasons_to_string(form.reasons.data), description=form.description.data,
|
||||||
type=1, reporter_id=current_user.id, suspect_user_id=post.author.id, suspect_post_id=post.id,
|
type=1, reporter_id=current_user.id, suspect_user_id=post.author.id, suspect_post_id=post.id,
|
||||||
suspect_community_id=post.community.id)
|
suspect_community_id=post.community.id, in_community_id=post.community.id)
|
||||||
db.session.add(report)
|
db.session.add(report)
|
||||||
|
|
||||||
# Notify moderators
|
# Notify moderators
|
||||||
|
|
14
app/templates/community/_community_moderation_nav.html
Normal file
14
app/templates/community/_community_moderation_nav.html
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<div class="btn-group mt-1 mb-2">
|
||||||
|
<a href="/community/{{ community.link() }}/moderate" aria-label="{{ _('Sort by hot') }}" class="btn {{ 'btn-primary' if current == '' or current == 'reports' else 'btn-outline-secondary' }}" rel="nofollow noindex">
|
||||||
|
{{ _('Reports') }}
|
||||||
|
</a>
|
||||||
|
<a href="/community/{{ community.link() }}/moderate/banned" class="btn {{ 'btn-primary' if current == 'banned' else 'btn-outline-secondary' }}" rel="nofollow noindex">
|
||||||
|
{{ _('Banned people') }}
|
||||||
|
</a>
|
||||||
|
<a href="/community/{{ community.link() }}/moderate/appeals" class="btn {{ 'btn-primary' if current == 'appeals' else 'btn-outline-secondary' }}" rel="nofollow noindex">
|
||||||
|
{{ _('Appeals') }}
|
||||||
|
</a>
|
||||||
|
<a href="/community/{{ community.link() }}/moderate/modlog" class="btn {{ 'btn-primary' if current == 'modlog' else 'btn-outline-secondary' }}" rel="nofollow noindex">
|
||||||
|
{{ _('Mod log') }}
|
||||||
|
</a>
|
||||||
|
</div>
|
|
@ -175,7 +175,9 @@
|
||||||
<h2>{{ _('Community Settings') }}</h2>
|
<h2>{{ _('Community Settings') }}</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<p><a href="#" class="btn btn-primary">{{ _('Moderate') }}</a></p>
|
{% if is_moderator or is_owner or is_admin %}
|
||||||
|
<p><a href="/community/{{ community.link() }}/moderate" class="btn btn-primary">{{ _('Moderate') }}</a></p>
|
||||||
|
{% endif %}
|
||||||
{% if is_owner or is_admin %}
|
{% if is_owner or is_admin %}
|
||||||
<p><a href="{{ url_for('community.community_edit', community_id=community.id) }}" class="btn btn-primary">{{ _('Settings') }}</a></p>
|
<p><a href="{{ url_for('community.community_edit', community_id=community.id) }}" class="btn btn-primary">{{ _('Settings') }}</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
84
app/templates/community/community_moderate.html
Normal file
84
app/templates/community/community_moderate.html
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
{% 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_field %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-8 position-relative main_pane">
|
||||||
|
<nav aria-label="breadcrumb" id="breadcrumb_nav" title="Navigation">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/">{{ _('Home') }}</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not none else community.name) }}">{{ (community.title + '@' + community.ap_domain)|shorten }}</a></li>
|
||||||
|
<li class="breadcrumb-item active">{{ _('Moderation') }}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-10">
|
||||||
|
<h1 class="mt-2">{{ _('Moderation of %(community)s', community=community.display_name()) }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2 text-right">
|
||||||
|
<!-- <a class="btn btn-primary" href="{{ url_for('community.community_add_moderator', community_id=community.id) }}">{{ _('Add moderator') }}</a> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include "community/_community_moderation_nav.html" %}
|
||||||
|
<h2>{{ _('Reports') }}</h2>
|
||||||
|
{% if reports.items %}
|
||||||
|
<form method="get">
|
||||||
|
<input type="search" name="search" value="{{ search }}">
|
||||||
|
<input type="radio" name="local_remote" value="local" id="local_remote_local" {{ 'checked' if local_remote == 'local' }}><label for="local_remote_local"> Local</label>
|
||||||
|
<input type="radio" name="local_remote" value="remote" id="local_remote_remote" {{ 'checked' if local_remote == 'remote' }}><label for="local_remote_remote"> Remote</label>
|
||||||
|
<input type="submit" name="submit" value="Search" class="btn btn-primary">
|
||||||
|
</form>
|
||||||
|
<table class="table table-striped">
|
||||||
|
<tr>
|
||||||
|
<th>Local/Remote</th>
|
||||||
|
<th>Reasons</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Created</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
{% for report in reports.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ 'Local' if report.is_local() else 'Remote' }}</td>
|
||||||
|
<td>{{ report.reasons }}</td>
|
||||||
|
<td>{{ report.description }}</td>
|
||||||
|
<td>{{ report.type_text() }}</td>
|
||||||
|
<td>{{ moment(report.created_at).fromNow() }}</td>
|
||||||
|
<td>
|
||||||
|
{% if report.suspect_conversation_id %}
|
||||||
|
<a href="/chat/{{ report.suspect_conversation_id }}#message">View</a>
|
||||||
|
{% elif report.suspect_post_reply_id %}
|
||||||
|
<a href="/post/{{ report.suspect_post_id }}#comment_{{ report.suspect_post_reply_id }}">View</a>
|
||||||
|
{% elif report.suspect_post_id %}
|
||||||
|
<a href="/post/{{ report.suspect_post_id }}">View</a>
|
||||||
|
{% elif report.suspect_user_id %}
|
||||||
|
<a href="/user/{{ report.suspect_user_id }}">View</a>
|
||||||
|
{% elif report.suspect_community_id %}
|
||||||
|
<a href="/user/{{ report.suspect_community_id }}">View</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
<nav aria-label="Pagination" class="mt-4" role="navigation">
|
||||||
|
{% if prev_url %}
|
||||||
|
<a href="{{ prev_url }}" class="btn btn-primary">
|
||||||
|
<span aria-hidden="true">←</span> {{ _('Previous page') }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if next_url %}
|
||||||
|
<a href="{{ next_url }}" class="btn btn-primary">
|
||||||
|
{{ _('Next page') }} <span aria-hidden="true">→</span>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</nav>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ _('No reports yet') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
65
app/templates/community/community_moderate_banned.html
Normal file
65
app/templates/community/community_moderate_banned.html
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
{% 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_field %}
|
||||||
|
|
||||||
|
{% block app_content %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-8 position-relative main_pane">
|
||||||
|
<nav aria-label="breadcrumb" id="breadcrumb_nav" title="Navigation">
|
||||||
|
<ol class="breadcrumb">
|
||||||
|
<li class="breadcrumb-item"><a href="/">{{ _('Home') }}</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{{ url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not none else community.name) }}">{{ (community.title + '@' + community.ap_domain)|shorten }}</a></li>
|
||||||
|
<li class="breadcrumb-item active">{{ _('Moderation') }}</li>
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-10">
|
||||||
|
<h1 class="mt-2">{{ _('Moderation of %(community)s', community=community.display_name()) }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2 text-right">
|
||||||
|
<!-- <a class="btn btn-primary" href="{{ url_for('community.community_add_moderator', community_id=community.id) }}">{{ _('Add moderator') }}</a> -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% include "community/_community_moderation_nav.html" %}
|
||||||
|
<h2>{{ _('Banned people') }}</h2>
|
||||||
|
{% if banned_people %}
|
||||||
|
<form method="get">
|
||||||
|
<input type="search" name="search" value="{{ search }}">
|
||||||
|
<input type="radio" name="local_remote" value="local" id="local_remote_local" {{ 'checked' if local_remote == 'local' }}><label for="local_remote_local"> Local</label>
|
||||||
|
<input type="radio" name="local_remote" value="remote" id="local_remote_remote" {{ 'checked' if local_remote == 'remote' }}><label for="local_remote_remote"> Remote</label>
|
||||||
|
<input type="submit" name="submit" value="Search" class="btn btn-primary">
|
||||||
|
</form>
|
||||||
|
<table class="table table-striped mt-1">
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Local/Remote</th>
|
||||||
|
<th>Reports</th>
|
||||||
|
<th>IP</th>
|
||||||
|
<th>Actions</th>
|
||||||
|
</tr>
|
||||||
|
{% for user in banned_people %}
|
||||||
|
<tr>
|
||||||
|
<td><img src="{{ user.avatar_thumbnail() }}" class="community_icon rounded-circle" loading="lazy" />
|
||||||
|
{{ user.display_name() }}</td>
|
||||||
|
<td>{{ 'Local' if user.is_local() else 'Remote' }}</td>
|
||||||
|
<td>{{ user.reports if user.reports > 0 }} </td>
|
||||||
|
<td>{{ user.ip_address if user.ip_address }} </td>
|
||||||
|
<td>{% if user.is_local() %}
|
||||||
|
<a href="/u/{{ user.link() }}">View local</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{{ user.ap_profile_id }}">View remote</a>
|
||||||
|
{% endif %}
|
||||||
|
| <a href="#" class="confirm_first">{{ _('Un ban') }}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% else %}
|
||||||
|
<p>{{ _('No banned people yet') }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
38
migrations/versions/81175e11c083_report_in_community.py
Normal file
38
migrations/versions/81175e11c083_report_in_community.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
"""report in community
|
||||||
|
|
||||||
|
Revision ID: 81175e11c083
|
||||||
|
Revises: e72aa356e4d0
|
||||||
|
Create Date: 2024-03-18 20:37:43.216482
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '81175e11c083'
|
||||||
|
down_revision = 'e72aa356e4d0'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('report', schema=None) as batch_op:
|
||||||
|
batch_op.add_column(sa.Column('in_community_id', sa.Integer(), nullable=True))
|
||||||
|
batch_op.drop_constraint('report_suspect_community_id_fkey', type_='foreignkey')
|
||||||
|
batch_op.create_foreign_key(None, 'community', ['suspect_community_id'], ['id'])
|
||||||
|
batch_op.create_foreign_key(None, 'community', ['in_community_id'], ['id'])
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
with op.batch_alter_table('report', schema=None) as batch_op:
|
||||||
|
batch_op.drop_constraint(None, type_='foreignkey')
|
||||||
|
batch_op.drop_constraint(None, type_='foreignkey')
|
||||||
|
batch_op.create_foreign_key('report_suspect_community_id_fkey', 'user', ['suspect_community_id'], ['id'])
|
||||||
|
batch_op.drop_column('in_community_id')
|
||||||
|
|
||||||
|
# ### end Alembic commands ###
|
Loading…
Add table
Reference in a new issue