Merge pull request 'Clean up Admin--"Monitoring content"' (#404) from h3ndrik/pyfedi:admin_content_cleanup into main

Reviewed-on: https://codeberg.org/rimu/pyfedi/pulls/404
This commit is contained in:
rimu 2024-12-30 00:22:30 +00:00
commit 2dea250e8c
8 changed files with 149 additions and 226 deletions

View file

@ -1192,92 +1192,69 @@ def admin_users():
)
@bp.route('/content/trash', methods=['GET'])
@bp.route('/content', methods=['GET'])
@login_required
@permission_required('administer all users')
def admin_content_trash():
page = request.args.get('page', 1, type=int)
posts = Post.query.filter(Post.posted_at > utcnow() - timedelta(days=3), Post.deleted == False, Post.down_votes > 0).order_by(Post.score)
posts = posts.paginate(page=page, per_page=100, error_out=False)
next_url = url_for('admin.admin_content_trash', page=posts.next_num) if posts.has_next else None
prev_url = url_for('admin.admin_content_trash', page=posts.prev_num) if posts.has_prev and page != 1 else None
return render_template('admin/posts.html', title=_('Bad posts'), next_url=next_url, prev_url=prev_url, posts=posts,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
menu_topics=menu_topics(),
site=g.site
)
@bp.route('/content/spam', methods=['GET'])
@login_required
@permission_required('administer all users')
def admin_content_spam():
# Essentially the same as admin_content_trash() except only shows heavily downvoted posts by new users - who are usually spammers
def admin_content():
page = request.args.get('page', 1, type=int)
replies_page = request.args.get('replies_page', 1, type=int)
posts_replies = request.args.get('posts_replies', '')
show = request.args.get('show', 'trash')
days = request.args.get('days', 3, type=int)
posts = Post.query.join(User, User.id == Post.user_id).filter(Post.deleted == False)
post_replies = PostReply.query.join(User, User.id == PostReply.user_id).filter(PostReply.deleted == False)
if show == 'trash':
title = _('Bad / Most downvoted')
posts = posts.filter(Post.down_votes > 1, Post.score < 10)
if days > 0:
posts = posts.filter(Post.posted_at > utcnow() - timedelta(days=days))
posts = posts.order_by(Post.score)
post_replies = post_replies.filter(PostReply.down_votes > 1, PostReply.score < 10)
if days > 0:
post_replies = post_replies.filter(PostReply.posted_at > utcnow() - timedelta(days=days))
post_replies = post_replies.order_by(PostReply.score)
elif show == 'spammy':
title = _('Likely spam')
posts = posts.filter(Post.score <= 0)
if days > 0:
posts = posts.filter(Post.posted_at > utcnow() - timedelta(days=days),
User.created > utcnow() - timedelta(days=days))
posts = posts.order_by(Post.score)
post_replies = post_replies.filter(PostReply.score <= 0)
if days > 0:
post_replies = post_replies.filter(PostReply.posted_at > utcnow() - timedelta(days=days),
User.created > utcnow() - timedelta(days=days))
post_replies = post_replies.order_by(PostReply.score)
elif show == 'deleted':
title = _('Deleted content')
posts = Post.query.filter(Post.deleted == True)
if days > 0:
posts = posts.filter(Post.posted_at > utcnow() - timedelta(days=days))
posts = posts.order_by(desc(Post.posted_at))
post_replies = PostReply.query.filter(PostReply.deleted == True)
if days > 0:
post_replies = post_replies.filter(PostReply.posted_at > utcnow() - timedelta(days=days))
post_replies = post_replies.order_by(desc(PostReply.posted_at))
if posts_replies == 'posts':
post_replies = post_replies.filter(False)
elif posts_replies == 'replies':
posts = posts.filter(False)
posts = Post.query.join(User, User.id == Post.user_id).\
filter(User.created > utcnow() - timedelta(days=3)).\
filter(Post.posted_at > utcnow() - timedelta(days=3)).\
filter(Post.deleted == False).\
filter(Post.score <= 0).order_by(Post.score)
posts = posts.paginate(page=page, per_page=100, error_out=False)
post_replies = PostReply.query.join(User, User.id == PostReply.user_id). \
filter(User.created > utcnow() - timedelta(days=3)). \
filter(PostReply.posted_at > utcnow() - timedelta(days=3)). \
filter(PostReply.deleted == False). \
filter(PostReply.score <= 0).order_by(PostReply.score)
post_replies = post_replies.paginate(page=replies_page, per_page=100, error_out=False)
next_url = url_for('admin.admin_content_spam', page=posts.next_num) if posts.has_next else None
prev_url = url_for('admin.admin_content_spam', page=posts.prev_num) if posts.has_prev and page != 1 else None
next_url_replies = url_for('admin.admin_content_spam', replies_page=post_replies.next_num) if post_replies.has_next else None
prev_url_replies = url_for('admin.admin_content_spam', replies_page=post_replies.prev_num) if post_replies.has_prev and replies_page != 1 else None
next_url = url_for('admin.admin_content', page=posts.next_num, replies_page=replies_page, posts_replies=posts_replies, show=show, days=days) if posts.has_next else None
prev_url = url_for('admin.admin_content', page=posts.prev_num, replies_page=replies_page, posts_replies=posts_replies, show=show, days=days) if posts.has_prev and page != 1 else None
next_url_replies = url_for('admin.admin_content', replies_page=post_replies.next_num, page=page, posts_replies=posts_replies, show=show, days=days) if post_replies.has_next else None
prev_url_replies = url_for('admin.admin_content', replies_page=post_replies.prev_num, page=page, posts_replies=posts_replies, show=show, days=days) if post_replies.has_prev and replies_page != 1 else None
return render_template('admin/spam_posts.html', title=_('Likely spam'),
next_url=next_url, prev_url=prev_url,
next_url_replies=next_url_replies, prev_url_replies=prev_url_replies,
posts=posts, post_replies=post_replies,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
menu_topics=menu_topics(),
site=g.site
)
@bp.route('/content/deleted', methods=['GET'])
@login_required
@permission_required('administer all users')
def admin_content_deleted():
# Shows all soft deleted posts
page = request.args.get('page', 1, type=int)
replies_page = request.args.get('replies_page', 1, type=int)
posts = Post.query.\
filter(Post.deleted == True).\
order_by(desc(Post.posted_at))
posts = posts.paginate(page=page, per_page=100, error_out=False)
post_replies = PostReply.query. \
filter(PostReply.deleted == True). \
order_by(desc(PostReply.posted_at))
post_replies = post_replies.paginate(page=replies_page, per_page=100, error_out=False)
next_url = url_for('admin.admin_content_deleted', page=posts.next_num) if posts.has_next else None
prev_url = url_for('admin.admin_content_deleted', page=posts.prev_num) if posts.has_prev and page != 1 else None
next_url_replies = url_for('admin.admin_content_deleted', replies_page=post_replies.next_num) if post_replies.has_next else None
prev_url_replies = url_for('admin.admin_content_deleted', replies_page=post_replies.prev_num) if post_replies.has_prev and replies_page != 1 else None
return render_template('admin/deleted_posts.html', title=_('Deleted content'),
return render_template('admin/content.html', title=title,
next_url=next_url, prev_url=prev_url,
next_url_replies=next_url_replies, prev_url_replies=prev_url_replies,
posts=posts, post_replies=post_replies,
posts_replies=posts_replies, show=show, days=days,
moderating_communities=moderating_communities(current_user.get_id()),
joined_communities=joined_communities(current_user.get_id()),
menu_topics=menu_topics(),

View file

@ -6,6 +6,7 @@
<a href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a> |
<a href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a> |
<a href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a> |
<a href="{{ url_for('admin.admin_content') }}">{{ _('Content') }}</a> |
{% if site.registration_mode == 'RequireApplication' %}
<a href="{{ url_for('admin.admin_approve_registrations') }}">{{ _('Registration applications') }}</a> |
{% endif %}
@ -13,7 +14,7 @@
<a href="{{ url_for('admin.admin_federation') }}">{{ _('Federation') }}</a> |
<a href="{{ url_for('admin.newsletter') }}">{{ _('Newsletter') }}</a> |
<a href="{{ url_for('admin.admin_permissions') }}">{{ _('Permissions') }}</a> |
<a href="{{ url_for('admin.admin_activities') }}">{{ _('Activities') }}</a>
<a href="{{ url_for('admin.admin_activities') }}">{{ _('Activities') }}</a> |
<a href="{{ url_for('main.modlog') }}">{{ _('Modlog') }}</a>
{% if debug_mode %}
| <a href="{{ url_for('dev.tools') }}">{{ _('Dev Tools') }}</a>

View file

@ -0,0 +1,94 @@
{% 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 %}
{% set active_child = 'admin_content' %}
{% block app_content %}
<div class="row">
<div class="col">
<h1>{{ _('Content') }}</h1>
<form method="get">
<div>
<input type="radio" class="form-check-input submit_on_change" name="show" value="trash" id="show_trash" {{ 'checked' if show == 'trash' }}><label for="show_trash" class="form-check-label">&nbsp;Trash</label>
<input type="radio" class="form-check-input submit_on_change" name="show" value="spammy" id="show_spammy" {{ 'checked' if show == 'spammy' }}><label for="show_spammy" class="form-check-label">&nbsp;Spammy</label>
<input type="radio" class="form-check-input submit_on_change" name="show" value="deleted" id="show_deleted" {{ 'checked' if show == 'deleted' }}><label for="show_deleted" class="form-check-label">&nbsp;Deleted</label>
</div>
<br />
<div>
<label for="posts_replies" class="form-label">Posts/Comments:</label>
<select name="posts_replies" class="form-select-sm submit_on_change">
<option value="">All</option>
<option value="posts" {{ 'selected' if posts_replies == 'posts' }}>Posts</option>
<option value="replies" {{ 'selected' if posts_replies == 'replies' }}>Comments</option>
</select>
<label for="days" class="form-label">Days:</label>
<select name="days" class="form-select-sm submit_on_change">
<option value="3" {{ 'selected' if days == 3 }}>3 days</option>
<option value="7" {{ 'selected' if days == 7 }}>7 days</option>
<option value="14" {{ 'selected' if days == 14 }}>14 days</option>
</select>
</div>
</form>
<h2>{{ title }}</h2>
{% if posts_replies != 'replies' %}
{% if post_replies %}<h3 class="mt-4" id="comments">Posts</h3>{% endif %}
<div class="post_list">
{% for post in posts.items %}
{% with disable_voting=True %}
{% include 'post/_post_teaser.html' %}
{% endwith %}
{% else %}
<p>{{ _('No posts.') }}</p>
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4" role="navigation">
{% if prev_url %}
<a href="{{ prev_url }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% endif %}
{% if posts_replies != 'posts' %}
{% if posts %}<h3 class="mt-4" id="comments">Comments</h3>{% endif %}
<div class="post_list">
{% for post_reply in post_replies.items %}
{% with teaser=True, disable_voting=True, no_collapse=True, show_deleted=True %}
{% include 'post/_post_reply_teaser.html' %}
{% endwith %}
<hr />
{% else %}
<p>{{ _('No comments.') }}</p>
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4" role="navigation">
{% if prev_url_replies %}
<a href="{{ prev_url_replies }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url_replies %}
<a href="{{ next_url_replies }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% endif %}
</div>
</div>
<hr />
<div class="row">
<div class="col">
{% include 'admin/_nav.html' %}
</div>
</div>
<hr />
{% endblock %}

View file

@ -1,68 +0,0 @@
{% 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 %}
{% set active_child = 'admin_content_deleted' %}
{% block app_content %}
<div class="row">
<div class="col">
<h1>{{ _('Deleted posts') }}</h1>
<div class="post_list">
{% for post in posts.items %}
{% include 'post/_post_teaser.html' %}
{% else %}
<p>{{ _('No deleted posts.') }}</p>
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4" role="navigation">
{% if prev_url %}
<a href="{{ prev_url }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% if post_replies %}
<h2 class="mt-4" id="comments">Deleted comments</h2>
<div class="post_list">
{% for post_reply in post_replies.items %}
{% with teaser=True, disable_voting=True, no_collapse=True, show_deleted=True %}
{% include 'post/_post_reply_teaser.html' %}
{% endwith %}
<hr />
{% else %}
<p>{{ _('No deleted comments.') }}</p>
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4" role="navigation">
{% if prev_url_replies %}
<a href="{{ prev_url_replies }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url_replies %}
<a href="{{ next_url_replies }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% else %}
<p>{{ _('No comments yet.') }}</p>
{% endif %}
</div>
</div>
<hr />
<div class="row">
<div class="col">
{% include 'admin/_nav.html' %}
</div>
</div>
<hr />
{% endblock %}

View file

@ -1,39 +0,0 @@
{% 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 %}
{% set active_child = 'admin_content_trash' %}
{% block app_content %}
<div class="row">
<div class="col">
<h1>{{ _('Most downvoted posts in the last 3 days') }}</h1>
<div class="post_list">
{% for post in posts.items %}
{% include 'post/_post_teaser.html' %}
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4" role="navigation">
{% if prev_url %}
<a href="{{ prev_url }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
</div>
</div>
<hr />
<div class="row">
<div class="col">
{% include 'admin/_nav.html' %}
</div>
</div>
<hr />
{% endblock %}

View file

@ -1,40 +0,0 @@
{% 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 %}
{% set active_child = 'admin_content_spam' %}
{% block app_content %}
<div class="row">
<div class="col">
<h1>{{ _('Most downvoted posts in the last 3 days') }}</h1>
<div class="post_list">
{% for post in posts.items %}
{% include 'post/_post_teaser.html' %}
{% endfor %}
</div>
{% if post_replies %}
<h2 class="mt-4" id="comments">Downvoted comments</h2>
<div class="post_list">
{% for post_reply in post_replies.items %}
{% with teaser=True, disable_voting=True, no_collapse=True, show_deleted=True %}
{% include 'post/_post_reply_teaser.html' %}
{% endwith %}
<hr />
{% endfor %}
</div>
{% else %}
<p>{{ _('No comments yet.') }}</p>
{% endif %}
</div>
</div>
<hr />
<div class="row">
<div class="col">
{% include 'admin/_nav.html' %}
</div>
</div>
<hr />
{% endblock %}

View file

@ -32,7 +32,7 @@
<option value="30" {{ 'selected' if last_seen == 30 }}>30 days</option>
</select>
</div>
<input type="hidden" name="sort_by" value="{{ sort_by }}"></button>
<input type="hidden" name="sort_by" value="{{ sort_by }}">
</form>
<table class="table table-striped mt-1">
<tr>

View file

@ -229,9 +229,7 @@
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_communities' }}" href="{{ url_for('admin.admin_communities') }}">{{ _('Communities') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_topics' }}" href="{{ url_for('admin.admin_topics') }}">{{ _('Topics') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_users' }}" href="{{ url_for('admin.admin_users', local_remote='local') }}">{{ _('Users') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_trash' }}" href="{{ url_for('admin.admin_content_trash') }}">{{ _('Monitoring - content') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_spam' }}" href="{{ url_for('admin.admin_content_spam') }}">{{ _('Monitoring - spammy content') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content_deleted' }}" href="{{ url_for('admin.admin_content_deleted') }}">{{ _('Deleted content') }}</a></li>
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_content' }}" href="{{ url_for('admin.admin_content') }}">{{ _('Content') }}</a></li>
{% if g.site.registration_mode == 'RequireApplication' %}
<li><a class="dropdown-item{{ ' active' if active_child == 'admin_approve_registrations' }}" href="{{ url_for('admin.admin_approve_registrations') }}">{{ _('Registration applications') }}</a></li>
{% endif %}