Add temp banning and ban messages

This commit is contained in:
rubenwardy 2022-02-13 10:37:54 +00:00
parent 7a650eb1e4
commit 3d35f6507a
6 changed files with 144 additions and 18 deletions

@ -13,6 +13,7 @@
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
import datetime
from flask import *
@ -26,7 +27,6 @@ from flask_login import logout_user, current_user, LoginManager
import os, redis
from app.markdown import init_markdown, MARKDOWN_EXTENSIONS, MARKDOWN_EXTENSION_CONFIG
app = Flask(__name__, static_folder="public/static")
app.config["FLATPAGES_ROOT"] = "flatpages"
app.config["FLATPAGES_EXTENSION"] = ".md"
@ -86,27 +86,39 @@ def load_user(user_id):
from .blueprints import create_blueprints
create_blueprints(app)
@app.route("/uploads/<path:path>")
def send_upload(path):
return send_from_directory(app.config["UPLOAD_DIR"], path)
@app.route("/<path:path>/")
def flatpage(path):
page = pages.get_or_404(path)
template = page.meta.get("template", "flatpage.html")
return render_template(template, page=page)
@app.before_request
def check_for_ban():
if current_user.is_authenticated:
if current_user.rank == models.UserRank.BANNED:
flash(gettext("You have been banned."), "danger")
if current_user.ban and current_user.ban.has_expired:
models.db.session.delete(current_user.ban)
if current_user.rank == models.UserRank.BANNED:
current_user.rank = models.UserRank.MEMBER
models.db.session.commit()
elif current_user.ban or current_user.rank == models.UserRank.BANNED:
if current_user.ban:
flash(gettext("Banned:") + " " + current_user.ban.message, "danger")
else:
flash(gettext("You have been banned."), "danger")
logout_user()
return redirect(url_for("users.login"))
elif current_user.rank == models.UserRank.NOT_JOINED:
current_user.rank = models.UserRank.MEMBER
models.db.session.commit()
from .utils import clearNotifications, is_safe_url
@ -115,6 +127,7 @@ def check_for_notifications():
if current_user.is_authenticated:
clearNotifications(request.path)
@app.errorhandler(404)
def page_not_found(e):
return render_template("404.html"), 404
@ -145,7 +158,6 @@ def get_locale():
return locale
@app.route("/set-locale/", methods=["POST"])
@csrf.exempt
def set_locale():

@ -358,11 +358,45 @@ def modtools_ban(username):
if not user.checkPerm(current_user, Permission.CHANGE_RANK):
abort(403)
user.rank = UserRank.BANNED
message = request.form["message"]
expires_at = request.form.get("expires_at")
addAuditLog(AuditSeverity.MODERATION, current_user, f"Banned {user.username}",
user.ban = UserBan()
user.ban.banned_by = current_user
user.ban.message = message
if expires_at and expires_at != "":
user.ban.expires_at = expires_at
else:
user.rank = UserRank.BANNED
addAuditLog(AuditSeverity.MODERATION, current_user, f"Banned {user.username}, expires {user.ban.expires_at or '-'}, message: {message}",
url_for("users.profile", username=user.username), None)
db.session.commit()
flash(f"Banned {user.username}", "success")
return redirect(url_for("users.modtools", username=username))
return redirect(url_for("users.modtools", username=username))
@bp.route("/users/<username>/modtools/unban/", methods=["POST"])
@rank_required(UserRank.MODERATOR)
def modtools_unban(username):
user: User = User.query.filter_by(username=username).first()
if not user:
abort(404)
if not user.checkPerm(current_user, Permission.CHANGE_RANK):
abort(403)
if user.ban:
db.session.delete(user.ban)
if user.rank == UserRank.BANNED:
user.rank = UserRank.MEMBER
addAuditLog(AuditSeverity.MODERATION, current_user, f"Unbanned {user.username}",
url_for("users.profile", username=user.username), None)
db.session.commit()
flash(f"Unbanned {user.username}", "success")
return redirect(url_for("users.modtools", username=username))

@ -183,6 +183,8 @@ class User(db.Model, UserMixin):
replies = db.relationship("ThreadReply", back_populates="author", lazy="dynamic", cascade="all, delete, delete-orphan", order_by=db.desc("created_at"))
forum_topics = db.relationship("ForumTopic", back_populates="author", lazy="dynamic", cascade="all, delete, delete-orphan")
ban = db.relationship("UserBan", foreign_keys="UserBan.user_id", back_populates="user", uselist=False)
def __init__(self, username=None, active=False, email=None, password=None):
self.username = username
self.display_name = username
@ -482,3 +484,21 @@ class UserNotificationPreferences(db.Model):
value = 1 if value else 0
setattr(self, "pref_" + notification_type.toName(), value)
class UserBan(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), primary_key=True)
user = db.relationship("User", foreign_keys=[user_id], back_populates="ban")
message = db.Column(db.UnicodeText, nullable=False)
banned_by_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
banned_by = db.relationship("User", foreign_keys=[banned_by_id])
created_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow)
expires_at = db.Column(db.DateTime, nullable=True, default=None)
@property
def has_expired(self):
return self.expires_at and datetime.datetime.now() > self.expires_at

@ -434,15 +434,18 @@
{% if package.type == package.type.MOD %}
<h3>{{ _("Compatible Games") }}</h3>
{% for support in package.getSortedSupportedGames() %}
<a class="badge badge-secondary"
href="{{ support.game.getURL('packages.view') }}">
{{ _("%(title)s by %(display_name)s",
title=support.game.title, display_name=support.game.author.display_name) }}
</a>
{% else %}
{{ _("No specific game is required") }}
{% endfor %}
<div style="max-height: 300px; overflow: hidden auto;">
{% for support in package.getSortedSupportedGames() %}
<a class="badge badge-secondary"
href="{{ support.game.getURL('packages.view') }}">
{{ _("%(title)s by %(display_name)s",
title=support.game.title, display_name=support.game.author.display_name) }}
</a>
{% else %}
{{ _("No specific game is required") }}
{% endfor %}
</div>
<p class="text-muted small mt-2 mb-0">
{{ _("This is an experimental feature.") }}
{{ _("Supported games are determined by an algorithm, and may not be correct.") }}

@ -41,13 +41,37 @@
{% if not user.rank.atLeast(current_user.rank) %}
<h3>{{ _("Ban") }}</h3>
{% if user.rank.name == "BANNED" %}
{% if user.ban %}
<p>
Banned.
Banned by {{ user.ban.banned_by.display_name }} at {{ user.ban.created_at | full_datetime }}
{% if user.ban.expires_at %}
until {{ user.ban.expires_at | date }}
{% endif %}
</p>
<blockquote>
{{ user.ban.message }}
</blockquote>
<form method="POST" action="{{ url_for('users.modtools_unban', username=user.username) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="submit" value="{{ _('Unban') }}" class="btn btn-primary" />
</form>
{% else %}
<form method="POST" action="{{ url_for('users.modtools_ban', username=user.username) }}">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<div class="form-group">
<label for="message">{{ _("Message") }}</label>
<input id="message" class="form-control" type="text" name="message" required minlength="5">
<small class="form-text text-muted">
{{ _("Message to display to banned user") }}
</small>
</div>
<div class="form-group">
<label for="expires_at">{{ _("Expires At") }}</label>
<input id="expires_at" class="form-control" type="date" name="expires_at">
<small class="form-text text-muted">
{{ _("Expiry date. Leave blank for permanent ban") }}
</small>
</div>
<input type="submit" value="{{ _('Ban') }}" class="btn btn-danger" />
</form>
{% endif %}

@ -0,0 +1,33 @@
"""empty message
Revision ID: 01f8d5de29e1
Revises: e571b3498f9e
Create Date: 2022-02-13 10:12:20.150232
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = '01f8d5de29e1'
down_revision = 'e571b3498f9e'
branch_labels = None
depends_on = None
def upgrade():
op.create_table('user_ban',
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('message', sa.UnicodeText(), nullable=False),
sa.Column('banned_by_id', sa.Integer(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('expires_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['banned_by_id'], ['user.id'], ),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('user_id')
)
def downgrade():
op.drop_table('user_ban')