make create guild threshold configurable, add savepoint

This commit is contained in:
Yusur 2025-07-17 22:07:12 +02:00
parent 71042a720c
commit a88b12e844
5 changed files with 53 additions and 15 deletions

View file

@ -6,15 +6,18 @@
- Users can now block each other - Users can now block each other
+ Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile + Blocking a user prevents them from seeing your comments, posts (standalone or in feed) and profile
- Added user strikes: a strike logs the content of a removed message for future use - Added user strikes: a strike logs the content of a removed message for future use
- Added ✨**color themes**✨
- Posts may now be deleted by author. If it has comments, comments are not spared - Posts may now be deleted by author. If it has comments, comments are not spared
- If a user for some reason can't post, their post is blocked and they can choose to post it onto another community. Previously it got posted to the user page.
- Moderators (and admins) have now access to mod tools - Moderators (and admins) have now access to mod tools
+ Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members + Allowed operations: change display name, description, restriction status, and exile (guild-local ban) members
+ Site administrators and guild owners can add moderators + Site administrators and guild owners can add moderators
- Administrators can claim ownership of abandoned guilds - Administrators can claim ownership of abandoned guilds
- Implemented guild subscriptions (not as in $$$, yes as in the follow button) - Implemented guild subscriptions (not as in $$$, yes as in the follow button)
- Added ✨color themes✨ - 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` - Users can now set their display name, biography and color theme in `/settings`
- You can now add an impressum in .env, e.g. `IMPRESSUM='Acme Ltd.::1 Short Island::Old York, Divided States::Responsible: ::Donald Duck'` Lines are separated by two colons. Version before 0.4.0 CAN'T BE RUN in German-speaking countries as of 2025. - Impressum can now be set in .env, e.g. `IMPRESSUM='Acme Ltd.::1 Short Island::Old York, Divided States::Responsible: ::Donald Duck'` Lines are separated by two colons. **Versions before 0.4.0 CAN'T BE RUN in German-speaking countries** as of 2025.
- Several aesthetic improvements
## 0.3.3 ## 0.3.3

View file

@ -40,6 +40,7 @@ class AppConfig(ConfigOptions):
jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js') jquery_url = ConfigValue(default='https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js')
app_is_behind_proxy = ConfigValue(cast=bool, default=False) app_is_behind_proxy = ConfigValue(cast=bool, default=False)
impressum = ConfigValue(cast=twocolon_list, default='') impressum = ConfigValue(cast=twocolon_list, default='')
create_guild_threshold = ConfigValue(cast=int, default=15, prefix='freak_')
app_config = AppConfig() app_config = AppConfig()

View file

@ -220,8 +220,8 @@ class User(Base):
db.session.commit() db.session.commit()
def can_create_guild(self): def can_create_guild(self):
## TODO make guild creation requirements configurable ## TODO make guild creation requirements fully configurable
return self.karma > 15 or self.is_administrator return self.karma > app_config.create_guild_threshold or self.is_administrator
can_create_community = deprecated('use .can_create_guild()')(can_create_guild) can_create_community = deprecated('use .can_create_guild()')(can_create_guild)
@ -356,6 +356,19 @@ class Guild(Base):
u = db.session.execute(select(Member).where(Member.user_id == other.id, Member.guild_id == self.id)).scalar() 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 return u.is_banned if u else False
def allows_posting(self, other: User) -> bool:
if self.owner is None:
return False
if other.is_disabled:
return False
mem: Member | None = db.session.execute(select(Member).where(Member.user_id == other.id, Member.guild_id == self.id)).scalar() if other else None
if mem and mem.is_banned:
return False
if self.is_restricted:
return mem and mem.is_approved
return True
def moderators(self): def moderators(self):
if self.owner: if self.owner:
yield ModeratorInfo(self.owner, True) yield ModeratorInfo(self.owner, True)

View file

@ -14,15 +14,16 @@
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"> <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<div> <div>
<p>Posting as <strong>{{ current_user.handle() }}</strong></p> <p>Posting as <strong>{{ current_user.handle() }}</strong></p>
<p>Post to: <input type="text" name="to" placeholder="{{ current_user.handle() }}"></p> <p>Post to: <input type="text" name="to" placeholder="{{ current_user.handle() }}" value="{{ sv_target }}"></p>
<div> <div>
<input type="text" name="title" placeholder="An interesting title" maxlength="256" class="fullwidth" /> <input type="text" name="title" placeholder="An interesting title" maxlength="256" class="fullwidth" value="{{ sv_title }}" />
</div> </div>
<hr />
<div> <div>
<textarea name="text" placeholder="What's happening?" class="create_text fullwidth">{{ request.args['preload'] }}</textarea> <textarea name="text" placeholder="What's happening ~" class="create_text fullwidth">{{ sv_content }}</textarea>
</div> </div>
{#<dd id="fileInputContainer"><a href="javascript:attachFileInput();">Add a file...</a>#} {#<dd id="fileInputContainer"><a href="javascript:attachFileInput();">Add a file...</a>#}
<div>{{ privacy_select() }}</div> <div>{{ privacy_select(sv_privacy) }}</div>
<div><button type="submit" class="primary">Create</button></div> <div><button type="submit" class="primary">Create</button></div>
</div> </div>
</form> </form>

View file

@ -7,23 +7,43 @@ from flask_login import current_user, login_required
from sqlalchemy import insert, select from sqlalchemy import insert, select
from ..models import User, db, Guild, Post from ..models import User, db, Guild, Post
current_user: User
bp = Blueprint('create', __name__) bp = Blueprint('create', __name__)
def create_savepoint(
target = '', title = '', content = '',
privacy = 0
):
return render_template('create.html',
sv_target = target,
sv_title = title,
sv_content = content,
sv_privacy = privacy
)
@bp.route('/create/', methods=['GET', 'POST']) @bp.route('/create/', methods=['GET', 'POST'])
@login_required @login_required
def create(): def create():
user: User = current_user user: User = current_user
if request.method == 'POST' and 'title' in request.form: if request.method == 'POST' and 'title' in request.form:
gname = request.form['to'] gname = request.form['to']
if gname:
guild: Guild | None = db.session.execute(select(Guild).where(Guild.name == gname)).scalar()
if guild is None:
flash(f'Guild +{gname} not found or inaccessible, posting to your user page instead')
else:
guild = None
title = request.form['title'] title = request.form['title']
text = request.form['text'] text = request.form['text']
privacy = int(request.form.get('privacy', '0')) privacy = int(request.form.get('privacy', '0'))
if gname:
guild: Guild | None = db.session.execute(select(Guild).where(Guild.name == gname)).scalar()
if guild is None:
flash(f'Guild +{gname} not found or inaccessible')
return create_savepoint('', title, text, privacy)
if guild.has_exiled(user):
flash(f'You are banned from +{gname}')
return create_savepoint('', title, text, privacy)
if not guild.allows_posting(user):
flash(f'You can\'t post on +{gname}')
return create_savepoint('', title, text, privacy)
else:
guild = None
try: try:
new_post: Post = db.session.execute(insert(Post).values( new_post: Post = db.session.execute(insert(Post).values(
author_id = user.id, author_id = user.id,
@ -40,7 +60,7 @@ def create():
except Exception as e: except Exception as e:
sys.excepthook(*sys.exc_info()) sys.excepthook(*sys.exc_info())
flash('Unable to publish!') flash('Unable to publish!')
return render_template('create.html') return create_savepoint(target=request.args.get('on',''))
@bp.route('/createguild/', methods=['GET', 'POST']) @bp.route('/createguild/', methods=['GET', 'POST'])