diff --git a/CHANGELOG.md b/CHANGELOG.md index 40a4377..be61e29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,8 +12,9 @@ - Moderators (and admins) have now access to mod tools + Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members + Site administrators and guild owners can add moderators -- Administrators can claim ownership of abandoned guilds - Guilds can have restricted posting/commenting now. Unmoderated guilds always have. +- Administrators can claim ownership of abandoned guilds +- Admins can now suspend users from admin panel - Implemented guild subscriptions (not as in $$$, yes as in the follow button) - Minimum karma requirement for creating a guild is now configurable via env variable `FREAK_CREATE_GUILD_THRESHOLD` (previously hardcoded at 15) - Users can now set their display name, biography and color theme in `/settings` diff --git a/freak/models.py b/freak/models.py index 6e68213..25f4485 100644 --- a/freak/models.py +++ b/freak/models.py @@ -54,7 +54,7 @@ post_report_reasons = [ REPORT_REASON_STRINGS = { **{x.num_code: x.description for x in post_report_reasons}, **{x.code: x.description for x in post_report_reasons} } -REPORT_REASONS = {x.code: x.num_code for x in post_report_reasons} +REPORT_REASONS: dict[str, int] = {x.code: x.num_code for x in post_report_reasons} REPORT_TARGET_POST = 1 REPORT_TARGET_COMMENT = 2 @@ -175,7 +175,13 @@ class User(Base): @property def is_disabled(self): - return (self.banned_at is not None and (self.banned_until is None or self.banned_until <= datetime.datetime.now())) or self.is_disabled_by_user + now = datetime.datetime.now() + return ( + # suspended + (self.banned_at is not None and (self.banned_until is None or self.banned_until >= now)) or + # self-disabled + self.is_disabled_by_user + ) @property def is_active(self): diff --git a/freak/static/sass/layout.sass b/freak/static/sass/layout.sass index 9325fb7..ae79fd1 100644 --- a/freak/static/sass/layout.sass +++ b/freak/static/sass/layout.sass @@ -310,6 +310,12 @@ button, [type="submit"], [type="reset"], [type="button"] &[disabled] opacity: .5 cursor: not-allowed + border: var(--border) + color: var(--border) + + &.primary[disabled] + color: var(--background) + background-color: var(--border) &:first-child margin-inline-start: 0 diff --git a/freak/templates/403.html b/freak/templates/403.html index f13fb50..ee4f511 100644 --- a/freak/templates/403.html +++ b/freak/templates/403.html @@ -2,7 +2,7 @@ {% from "macros/title.html" import title_tag with context %} {% block title %} - {{ title_tag('X _ X') }} + {{ title_tag('X _ X') }} {% endblock %} {% block body %} diff --git a/freak/templates/404.html b/freak/templates/404.html index e3b427a..4a9f92b 100644 --- a/freak/templates/404.html +++ b/freak/templates/404.html @@ -2,7 +2,7 @@ {% from "macros/title.html" import title_tag with context %} {% block title %} - {{ title_tag('O _ O') }} + {{ title_tag('O _ O') }} {% endblock %} {% block body %} diff --git a/freak/templates/405.html b/freak/templates/405.html index 0151dcc..02c926b 100644 --- a/freak/templates/405.html +++ b/freak/templates/405.html @@ -2,7 +2,7 @@ {% from "macros/title.html" import title_tag with context %} {% block title %} - {{ title_tag('O _ O') }} + {{ title_tag('O _ O') }} {% endblock %} {% block body %} diff --git a/freak/templates/admin/admin_user_detail.html b/freak/templates/admin/admin_user_detail.html index 52b1cd3..36f8cb3 100644 --- a/freak/templates/admin/admin_user_detail.html +++ b/freak/templates/admin/admin_user_detail.html @@ -24,7 +24,23 @@ {% endif %} +

Quick Actions

+ + +
+ {% if u.banned_at %} + + {% else %} + + + {% endif %}

Strikes

diff --git a/freak/website/admin.py b/freak/website/admin.py index 1a75ed1..682f749 100644 --- a/freak/website/admin.py +++ b/freak/website/admin.py @@ -10,10 +10,12 @@ from markupsafe import Markup from sqlalchemy import insert, select, update from suou import additem, not_implemented -from ..models import REPORT_REASON_STRINGS, REPORT_TARGET_COMMENT, REPORT_TARGET_POST, REPORT_UPDATE_COMPLETE, REPORT_UPDATE_ON_HOLD, REPORT_UPDATE_REJECTED, Comment, Post, PostReport, User, UserStrike, db +from ..models import REPORT_REASON_STRINGS, REPORT_REASONS, REPORT_TARGET_COMMENT, REPORT_TARGET_POST, REPORT_UPDATE_COMPLETE, REPORT_UPDATE_ON_HOLD, REPORT_UPDATE_REJECTED, Comment, Post, PostReport, User, UserStrike, db bp = Blueprint('admin', __name__) +current_user: User + ## TODO make admin interface def admin_required(func: Callable): @@ -191,7 +193,26 @@ def user_detail(id: int): if u is None: abort(404) if request.method == 'POST': - abort(501) + action = request.form['do'] + if action == 'suspend': + u.banned_at = datetime.datetime.now() + u.banned_by_id = current_user.id + u.banned_reason = REPORT_REASONS.get(request.form.get('reason'), 0) + db.session.commit() + elif action == 'unsuspend': + u.banned_at = None + u.banned_by_id = None + u.banned_until = None + u.banned_reason = None + db.session.commit() + elif action == 'to_3d': + u.banned_at = datetime.datetime.now() + u.banned_until = datetime.datetime.now() + datetime.timedelta(days=3) + u.banned_by_id = current_user.id + u.banned_reason = REPORT_REASONS.get(request.form.get('reason'), 0) + db.session.commit() + else: + abort(400) strikes = db.session.execute(select(UserStrike).where(UserStrike.user_id == id).order_by(UserStrike.id.desc())).scalars() return render_template('admin/admin_user_detail.html', u=u, report_reasons=REPORT_REASON_STRINGS, account_status_string=colorized_account_status_string, strikes=strikes)