add user exile to Mod Tools
This commit is contained in:
parent
2214863496
commit
f97e613f7a
6 changed files with 117 additions and 8 deletions
|
|
@ -8,6 +8,7 @@
|
|||
- Added user strikes: a strike logs the content of a removed message for future use
|
||||
- Posts may now be deleted by author. If it has comments, comments are not spared
|
||||
- Moderators (and admins) have now access to mod tools
|
||||
+ Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members
|
||||
- Implemented guild subscriptions
|
||||
- Added ✨color themes✨
|
||||
- Users can now set their display name, biography and color theme in `/settings`
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ from suou.configparse import ConfigOptions, ConfigValue
|
|||
|
||||
from .colors import color_themes, theme_classes
|
||||
|
||||
__version__ = '0.4.0-dev27'
|
||||
__version__ = '0.4.0-dev28'
|
||||
|
||||
APP_BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from functools import partial
|
|||
from operator import or_
|
||||
import re
|
||||
from threading import Lock
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, text, \
|
||||
from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint, insert, text, \
|
||||
CheckConstraint, Date, DateTime, Boolean, func, BigInteger, \
|
||||
SmallInteger, select, update, Table
|
||||
from sqlalchemy.orm import Relationship, relationship
|
||||
|
|
@ -78,7 +78,7 @@ ILLEGAL_USERNAMES = (
|
|||
'pedophile', 'lolicon', 'giphy', 'tenor', 'csam', 'cp', 'pedobear', 'lolita',
|
||||
'loli', 'kkk', 'pnf', 'adl', 'cop', 'tranny', 'google', 'trustandsafety', 'safety', 'ice',
|
||||
## VVVVIP
|
||||
'potus', 'realdonaldtrump', 'elonmusk', 'teddysphotos', 'mrbeast', 'jkrowling'
|
||||
'potus', 'realdonaldtrump', 'elonmusk', 'teddysphotos', 'mrbeast', 'jkrowling', 'pewdiepie'
|
||||
)
|
||||
|
||||
def username_is_legal(username: str) -> bool:
|
||||
|
|
@ -308,6 +308,8 @@ class User(Base):
|
|||
|
||||
## END User
|
||||
|
||||
ModeratorInfo = namedtuple('ModeratorInfo', 'user is_owner')
|
||||
|
||||
class Guild(Base):
|
||||
__tablename__ = 'freak_topic'
|
||||
__table_args__ = (
|
||||
|
|
@ -340,6 +342,7 @@ class Guild(Base):
|
|||
return db.session.execute(select(func.count('*')).select_from(Member).where(Member.guild == self, Member.is_subscribed == True)).scalar()
|
||||
|
||||
# utilities
|
||||
owner = relationship(User, foreign_keys=owner_id)
|
||||
posts = relationship('Post', back_populates='guild')
|
||||
|
||||
def has_subscriber(self, other: User) -> bool:
|
||||
|
|
@ -347,6 +350,35 @@ class Guild(Base):
|
|||
return False
|
||||
return bool(db.session.execute(select(Member).where(Member.user_id == other.id, Member.guild_id == self.id, Member.is_subscribed == True)).scalar())
|
||||
|
||||
def has_exiled(self, other: User) -> bool:
|
||||
if other is None or not other.is_authenticated:
|
||||
return False
|
||||
u = db.session.execute(select(Member).where(Member.user_id == other.id, Member.guild_id == self.id)).scalar()
|
||||
return u.is_banned if u else False
|
||||
|
||||
def moderators(self):
|
||||
if self.owner:
|
||||
yield ModeratorInfo(self.owner, True)
|
||||
for mem in db.session.execute(select(Member).where(Member.guild_id == self.id, Member.is_moderator == True)).scalars():
|
||||
if mem.user != self.owner and not mem.user.is_banned:
|
||||
yield ModeratorInfo(mem.user, False)
|
||||
|
||||
def update_member(self, u: User | Member, /, **values):
|
||||
if isinstance(u, User):
|
||||
m = db.session.execute(select(Member).where(Member.user_id == u.id, Member.guild_id == self.id)).scalar()
|
||||
if m is None:
|
||||
return db.session.execute(insert(Member).values(
|
||||
guild_id = self.id,
|
||||
user_id = u.id,
|
||||
**values
|
||||
).returning(Member)).scalar()
|
||||
else:
|
||||
m = u
|
||||
if len(values):
|
||||
db.session.execute(update(Member).where(Member.user_id == u.id, Member.guild_id == self.id).values(**values))
|
||||
return m
|
||||
|
||||
|
||||
Topic = deprecated('renamed to Guild')(Guild)
|
||||
|
||||
## END Guild
|
||||
|
|
|
|||
|
|
@ -14,12 +14,42 @@
|
|||
<section class="card">
|
||||
<h2>Community Identity</h2>
|
||||
<div>
|
||||
<label for="GS__display_name">Display name:</label>
|
||||
<input type="text" name="display_name" id="GS__display_name" value="{{ gu.display_name or '' }}" />
|
||||
<label>Display name:
|
||||
<input type="text" name="display_name" value="{{ gu.display_name or '' }}" />
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label for="GS__description">Description:</label>
|
||||
<textarea name="description" id="GS__description">{{ gu.description or '' }}</textarea>
|
||||
<label>Description:
|
||||
<textarea name="description">{{ gu.description or '' }}</textarea>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="primary">Save</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<h2>Safety</h2>
|
||||
<div>
|
||||
<label>
|
||||
<input type="checkbox" name="restricted" value="1" {{ checked_if(gu.is_restricted) }} />
|
||||
Allow only approved members to post and comment
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
Ban user from participating in {{ gu.handle() }}:
|
||||
<input type="text" name="exile_name" placeholder="username" />
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" name="exile_reverse" value="1" />
|
||||
Remove ban on given user
|
||||
</label>
|
||||
<small class="faint">
|
||||
Bans (aka: exiles) are permanent and reversible.<br />
|
||||
Banned (exiled) users are not allowed to post or comment on {{ gu.handle() }}.<br />
|
||||
Reverse the ban by checking “Remove ban on given user”.
|
||||
</small>
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit" class="primary">Save</button>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,31 @@
|
|||
<a href="{{ gu.url() }}/settings"><button class="card">{{ icon('settings') }} Mod Tools</button></a>
|
||||
{% endif %}
|
||||
{{ subscribe_button(gu, gu.has_subscriber(current_user)) }}
|
||||
{% if not gu.owner %}
|
||||
<aside class="card">
|
||||
<p class="centered">{{ gu.handle() }} is currently unmoderated</p>
|
||||
</aside>
|
||||
{% elif gu.has_exiled(current_user) %}
|
||||
<aside class="card">
|
||||
<p class="centered">Moderator list is hidden because you are banned.</p>
|
||||
<!-- TODO appeal button -->
|
||||
</aside>
|
||||
{% else %}
|
||||
<aside class="card">
|
||||
<h3>Moderators of {{ gu.handle() }}</h3>
|
||||
<div>
|
||||
<ul>
|
||||
{% for moder in gu.moderators() %}
|
||||
<li><a href="{{ moder.user.url() }}">{{ moder.user.handle() }}</a>
|
||||
{% if moder.is_owner %}
|
||||
<span>{{ icon('mod_mode') }} <small>Owner</small></span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@
|
|||
from flask import Blueprint, abort, flash, render_template, request
|
||||
from flask_login import current_user, login_required
|
||||
from sqlalchemy import select
|
||||
import datetime
|
||||
|
||||
from ..models import db, User, Guild
|
||||
from ..models import Member, db, User, Guild
|
||||
|
||||
current_user: User
|
||||
|
||||
|
|
@ -21,11 +22,31 @@ def guild_settings(name: str):
|
|||
changes = False
|
||||
display_name = request.form.get('display_name')
|
||||
description = request.form.get('description')
|
||||
exile_name = request.form.get('exile_name')
|
||||
exile_reverse = 'exile_reverse' in request.form
|
||||
restricted = 'restricted' in request.form
|
||||
|
||||
if description and description != gu.description:
|
||||
changes, gu.description = True, description.strip()
|
||||
if display_name and display_name != gu.display_name:
|
||||
changes, gu.display_name = True, display_name.strip()
|
||||
if exile_name:
|
||||
exile_user = db.session.execute(select(User).where(User.username == exile_name)).scalar()
|
||||
if exile_user:
|
||||
if exile_reverse:
|
||||
mem = gu.update_member(exile_user, banned_at = None, banned_by_id = None)
|
||||
if mem.banned_at == None:
|
||||
flash(f'Removed ban on {exile_user.handle()}')
|
||||
changes = True
|
||||
else:
|
||||
mem = gu.update_member(exile_user, banned_at = datetime.datetime.now(), banned_by_id = current_user.id)
|
||||
if mem.banned_at != None:
|
||||
flash(f'{exile_user.handle()} has been exiled')
|
||||
changes = True
|
||||
else:
|
||||
flash(f'User \'{exile_name}\' not found, can\'t exile')
|
||||
if restricted and restricted != gu.is_restricted:
|
||||
changes, gu.is_restricted = True, restricted
|
||||
|
||||
if changes:
|
||||
db.session.add(gu)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue