Rewrite settings page to reduce boilerplate
This commit is contained in:
parent
7488a8ca79
commit
64b7ef0dee
|
@ -26,8 +26,8 @@ from amanuensis.server.helpers import (
|
||||||
from .forms import (
|
from .forms import (
|
||||||
LexiconCharacterForm,
|
LexiconCharacterForm,
|
||||||
LexiconReviewForm,
|
LexiconReviewForm,
|
||||||
LexiconPublishTurnForm)
|
LexiconPublishTurnForm,
|
||||||
from .settings import LexiconConfigForm
|
LexiconConfigForm)
|
||||||
|
|
||||||
from .editor import load_editor, new_draft, update_draft
|
from .editor import load_editor, new_draft, update_draft
|
||||||
|
|
||||||
|
@ -142,24 +142,23 @@ def character(name):
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@editor_required
|
@editor_required
|
||||||
def settings(name):
|
def settings(name):
|
||||||
form = LexiconConfigForm()
|
form: LexiconConfigForm = LexiconConfigForm(g.lexicon)
|
||||||
form.set_options(g.lexicon)
|
|
||||||
|
|
||||||
# Load the config for the lexicon on load
|
|
||||||
if not form.is_submitted():
|
if not form.is_submitted():
|
||||||
form.populate_from_lexicon(g.lexicon)
|
# GET
|
||||||
|
form.load(g.lexicon)
|
||||||
return render_template('session.settings.jinja', form=form)
|
return render_template('session.settings.jinja', form=form)
|
||||||
|
|
||||||
if form.validate():
|
if not form.validate():
|
||||||
if not form.update_lexicon(g.lexicon):
|
# POST with invalid data
|
||||||
flash("Error updating settings")
|
flash('Validation error')
|
||||||
return render_template("lexicon.settings.jinja", form=form)
|
|
||||||
flash("Settings updated")
|
|
||||||
return redirect(url_for('session.session', name=name))
|
|
||||||
|
|
||||||
flash("Validation error")
|
|
||||||
return render_template('session.settings.jinja', form=form)
|
return render_template('session.settings.jinja', form=form)
|
||||||
|
|
||||||
|
# POST with valid data
|
||||||
|
form.save(g.lexicon)
|
||||||
|
flash('Settings updated')
|
||||||
|
return redirect(url_for('session.settings', name=name))
|
||||||
|
|
||||||
|
|
||||||
@bp_session.route('/review/', methods=['GET', 'POST'])
|
@bp_session.route('/review/', methods=['GET', 'POST'])
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
|
|
|
@ -3,6 +3,8 @@ from wtforms import (
|
||||||
StringField, SubmitField, TextAreaField, RadioField)
|
StringField, SubmitField, TextAreaField, RadioField)
|
||||||
from wtforms.validators import DataRequired
|
from wtforms.validators import DataRequired
|
||||||
|
|
||||||
|
from .settings import ConfigFormBase
|
||||||
|
|
||||||
|
|
||||||
class LexiconCharacterForm(FlaskForm):
|
class LexiconCharacterForm(FlaskForm):
|
||||||
"""/lexicon/<name>/session/character/"""
|
"""/lexicon/<name>/session/character/"""
|
||||||
|
@ -36,3 +38,8 @@ class LexiconReviewForm(FlaskForm):
|
||||||
class LexiconPublishTurnForm(FlaskForm):
|
class LexiconPublishTurnForm(FlaskForm):
|
||||||
"""/lexicon/<name>/session/"""
|
"""/lexicon/<name>/session/"""
|
||||||
submit = SubmitField('Publish turn')
|
submit = SubmitField('Publish turn')
|
||||||
|
|
||||||
|
|
||||||
|
class LexiconConfigForm(ConfigFormBase):
|
||||||
|
"""/lexicon/<name>/session/settings/"""
|
||||||
|
submit = SubmitField('Save settings')
|
||||||
|
|
|
@ -1,161 +1,277 @@
|
||||||
|
from typing import cast
|
||||||
|
|
||||||
|
from flask import current_app
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
StringField, BooleanField, SubmitField, TextAreaField,
|
Field,
|
||||||
IntegerField, SelectField)
|
StringField,
|
||||||
from wtforms.validators import DataRequired, ValidationError, Optional
|
BooleanField,
|
||||||
|
TextAreaField,
|
||||||
|
IntegerField,
|
||||||
|
SelectField)
|
||||||
|
from wtforms.validators import DataRequired, Optional
|
||||||
from wtforms.widgets.html5 import NumberInput
|
from wtforms.widgets.html5 import NumberInput
|
||||||
|
|
||||||
|
from amanuensis.config import ReadOnlyOrderedDict, AttrOrderedDict
|
||||||
|
from amanuensis.models import ModelFactory, UserModel
|
||||||
from amanuensis.server.forms import User
|
from amanuensis.server.forms import User
|
||||||
|
|
||||||
|
|
||||||
class LexiconConfigForm(FlaskForm):
|
class SettingTranslator():
|
||||||
"""/lexicon/<name>/session/settings/"""
|
"""
|
||||||
# General
|
Base class for the translation layer between internal config data
|
||||||
title = StringField('Title override', validators=[Optional()])
|
and user-friendly display in the settings form. By default the data
|
||||||
editor = SelectField('Editor', validators=[DataRequired(), User(True)])
|
is returned as-is.
|
||||||
prompt = TextAreaField('Prompt', validators=[DataRequired()])
|
"""
|
||||||
# Turn
|
def load(self, cfg_value):
|
||||||
turnCurrent = IntegerField('Current turn', widget=NumberInput(), validators=[Optional()])
|
return cfg_value
|
||||||
turnMax = IntegerField('Number of turns', widget=NumberInput(), validators=[DataRequired()])
|
|
||||||
# Join
|
def save(self, field_data):
|
||||||
joinPublic = BooleanField("Show game on public pages")
|
return field_data
|
||||||
joinOpen = BooleanField("Allow players to join game")
|
|
||||||
joinPassword = StringField("Password to join game", validators=[Optional()])
|
|
||||||
joinMaxPlayers = IntegerField(
|
class UsernameTranslator(SettingTranslator):
|
||||||
"Maximum number of players",
|
"""
|
||||||
|
Converts an internal user id to a public-facing username.
|
||||||
|
"""
|
||||||
|
def load(self, cfg_value):
|
||||||
|
model_factory: ModelFactory = current_app.config['model_factory']
|
||||||
|
user: UserModel = model_factory.user(cfg_value)
|
||||||
|
return user.cfg.username
|
||||||
|
|
||||||
|
def save(self, field_data):
|
||||||
|
model_factory: ModelFactory = current_app.config['model_factory']
|
||||||
|
user: UserModel = model_factory.try_user(field_data)
|
||||||
|
if user:
|
||||||
|
return user.uid
|
||||||
|
|
||||||
|
|
||||||
|
class Setting():
|
||||||
|
"""
|
||||||
|
Represents a relation between a node in a lexicon config and a
|
||||||
|
field in a public-facing form that exposes it to the editor for
|
||||||
|
modification.
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
cfg_key: str,
|
||||||
|
field: Field,
|
||||||
|
translator: SettingTranslator = SettingTranslator()):
|
||||||
|
"""
|
||||||
|
Creates a setting. Optionally, defines a nontrivial translation
|
||||||
|
between internal and public values.
|
||||||
|
"""
|
||||||
|
self.cfg_path = cfg_key.split('.')
|
||||||
|
self.field = field
|
||||||
|
self.translator = translator
|
||||||
|
|
||||||
|
def load(self, cfg: ReadOnlyOrderedDict, field: Field):
|
||||||
|
"""
|
||||||
|
Sets the field's value to the corresponding config node
|
||||||
|
"""
|
||||||
|
for key in self.cfg_path[:-1]:
|
||||||
|
cfg = cast(ReadOnlyOrderedDict, cfg.get(key))
|
||||||
|
data = cfg.get(self.cfg_path[-1])
|
||||||
|
field.data = self.translator.load(data)
|
||||||
|
|
||||||
|
def save(self, cfg: AttrOrderedDict, field: Field):
|
||||||
|
"""
|
||||||
|
Updates the editable config with this field's value
|
||||||
|
"""
|
||||||
|
for key in self.cfg_path[:-1]:
|
||||||
|
cfg = cast(AttrOrderedDict, cfg.get(key))
|
||||||
|
data = field.data
|
||||||
|
cfg[self.cfg_path[-1]] = self.translator.save(data)
|
||||||
|
|
||||||
|
|
||||||
|
class Settings():
|
||||||
|
@staticmethod
|
||||||
|
def settings():
|
||||||
|
for name, setting in vars(Settings).items():
|
||||||
|
if name.startswith('s_'):
|
||||||
|
yield name, setting
|
||||||
|
|
||||||
|
s_title = Setting('title',
|
||||||
|
StringField('Title override', validators=[Optional()]))
|
||||||
|
|
||||||
|
s_editor = Setting('editor',
|
||||||
|
SelectField('Editor', validators=[DataRequired(), User(True)]),
|
||||||
|
translator=UsernameTranslator())
|
||||||
|
|
||||||
|
s_prompt = Setting('prompt',
|
||||||
|
TextAreaField('Prompt', validators=[DataRequired()]))
|
||||||
|
|
||||||
|
s_turnCurrent = Setting('turn.current',
|
||||||
|
IntegerField(
|
||||||
|
'Current turn',
|
||||||
widget=NumberInput(),
|
widget=NumberInput(),
|
||||||
validators=[DataRequired()])
|
validators=[Optional()]))
|
||||||
joinCharsPerPlayer = IntegerField(
|
|
||||||
"Characters per player",
|
s_turnMax = Setting('turn.max',
|
||||||
|
IntegerField(
|
||||||
|
'Number of turns',
|
||||||
widget=NumberInput(),
|
widget=NumberInput(),
|
||||||
validators=[DataRequired()])
|
validators=[DataRequired()]))
|
||||||
# Publish
|
|
||||||
publishNotifyEditorOnReady = BooleanField(
|
|
||||||
"Notify the editor when a player marks an article as ready")
|
|
||||||
publishNotifyPlayerOnReject = BooleanField(
|
|
||||||
"Notify a player when their article is rejected by the editor")
|
|
||||||
publishNotifyPlayerOnAccept = BooleanField(
|
|
||||||
"Notify a player when their article is accepted by the editor")
|
|
||||||
publishDeadlines = StringField(
|
|
||||||
"Turn deadline, as a crontab specification", validators=[Optional()])
|
|
||||||
publishAsap = BooleanField(
|
|
||||||
"Publish the turn immediately when the last article is accepted")
|
|
||||||
publishQuorum = IntegerField(
|
|
||||||
"Quorum to publish incomplete turn", widget=NumberInput(), validators=[Optional()])
|
|
||||||
publishBlockOnReady = BooleanField(
|
|
||||||
"Block turn publish if any articles are awaiting editor review")
|
|
||||||
# Article
|
|
||||||
articleIndexList = TextAreaField("Index specifications")
|
|
||||||
articleIndexCapacity = IntegerField(
|
|
||||||
"Index capacity override", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationAllowSelf = BooleanField(
|
|
||||||
"Allow players to cite themselves")
|
|
||||||
articleCitationMinExtant = IntegerField(
|
|
||||||
"Minimum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMaxExtant = IntegerField(
|
|
||||||
"Maximum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMinPhantom = IntegerField(
|
|
||||||
"Minimum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMaxPhantom = IntegerField(
|
|
||||||
"Maximum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMinTotal = IntegerField(
|
|
||||||
"Minimum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMaxTotal = IntegerField(
|
|
||||||
"Maximum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMinChars = IntegerField(
|
|
||||||
"Minimum number of characters to cite articles by",
|
|
||||||
widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleCitationMaxChars = IntegerField(
|
|
||||||
"Maximum number of characters to cite articles by",
|
|
||||||
widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleWordLimitSoft = IntegerField(
|
|
||||||
"Soft word limit", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleWordLimitHard = IntegerField(
|
|
||||||
"Hard word limit", widget=NumberInput(), validators=[Optional()])
|
|
||||||
articleAddendumAllowed = BooleanField("Allow addendum articles")
|
|
||||||
articleAddendumMax = IntegerField(
|
|
||||||
"Maximum number of addendum articles per character per turn",
|
|
||||||
widget=NumberInput(), validators=[Optional()])
|
|
||||||
# And finally, the submit button
|
|
||||||
submit = SubmitField("Submit")
|
|
||||||
|
|
||||||
def validate_publishDeadlines(form, field):
|
s_joinPublic = Setting('join.public',
|
||||||
if form.publishAsap.data:
|
BooleanField('Show game on public pages'))
|
||||||
raise ValidationError('Cannot specify deadline if immediate publishing is enabled')
|
|
||||||
|
|
||||||
# TODO add validators that call into extant valid check methods
|
s_joinOpen = Setting('join.open',
|
||||||
|
BooleanField('Allow players to join game'))
|
||||||
|
|
||||||
# def set_options(self, lexicon):
|
s_joinPassword = Setting('join.password',
|
||||||
# self.editor.choices = list(map(lambda x: (x, x), map(
|
StringField('Password to join game', validators=[Optional()]))
|
||||||
# lambda uid: UserModel.by(uid=uid).username,
|
|
||||||
# lexicon.join.joined)))
|
|
||||||
|
|
||||||
# def populate_from_lexicon(self, lexicon):
|
s_joinMaxPlayers = Setting('join.max_players',
|
||||||
# self.title.data = lexicon.cfg.title
|
IntegerField(
|
||||||
# self.editor.data = ModelFactory(lexicon.ctx.root).user(lexicon.cfg.editor).cfg.username
|
'Maximum number of players',
|
||||||
# self.prompt.data = lexicon.prompt
|
widget=NumberInput(),
|
||||||
# self.turnCurrent.data = lexicon.turn.current
|
validators=[DataRequired()]))
|
||||||
# self.turnMax.data = lexicon.turn.max
|
|
||||||
# self.joinPublic.data = lexicon.join.public
|
|
||||||
# self.joinOpen.data = lexicon.join.open
|
|
||||||
# self.joinPassword.data = lexicon.join.password
|
|
||||||
# self.joinMaxPlayers.data = lexicon.join.max_players
|
|
||||||
# self.joinCharsPerPlayer.data = lexicon.join.chars_per_player
|
|
||||||
# self.publishNotifyEditorOnReady.data = lexicon.publish.notify.editor_on_ready
|
|
||||||
# self.publishNotifyPlayerOnReject.data = lexicon.publish.notify.player_on_reject
|
|
||||||
# self.publishNotifyPlayerOnAccept.data = lexicon.publish.notify.player_on_accept
|
|
||||||
# self.publishDeadlines.data = lexicon.publish.deadlines
|
|
||||||
# self.publishAsap.data = lexicon.publish.asap
|
|
||||||
# self.publishQuorum.data = lexicon.publish.quorum
|
|
||||||
# self.publishBlockOnReady.data = lexicon.publish.block_on_ready
|
|
||||||
# self.articleIndexList.data = lexicon.article.index.list
|
|
||||||
# self.articleIndexCapacity.data = lexicon.article.index.capacity
|
|
||||||
# self.articleCitationAllowSelf.data = lexicon.article.citation.allow_self
|
|
||||||
# self.articleCitationMinExtant.data = lexicon.article.citation.min_extant
|
|
||||||
# self.articleCitationMaxExtant.data = lexicon.article.citation.max_extant
|
|
||||||
# self.articleCitationMinPhantom.data = lexicon.article.citation.min_phantom
|
|
||||||
# self.articleCitationMaxPhantom.data = lexicon.article.citation.max_phantom
|
|
||||||
# self.articleCitationMinTotal.data = lexicon.article.citation.min_total
|
|
||||||
# self.articleCitationMaxTotal.data = lexicon.article.citation.max_total
|
|
||||||
# self.articleCitationMinChars.data = lexicon.article.citation.min_chars
|
|
||||||
# self.articleCitationMaxChars.data = lexicon.article.citation.max_chars
|
|
||||||
# self.articleWordLimitSoft.data = lexicon.article.word_limit.soft
|
|
||||||
# self.articleWordLimitHard.data = lexicon.article.word_limit.hard
|
|
||||||
# self.articleAddendumAllowed.data = lexicon.article.addendum.allowed
|
|
||||||
# self.articleAddendumMax.data = lexicon.article.addendum.max
|
|
||||||
|
|
||||||
# def update_lexicon(self, lexicon):
|
s_joinCharsPerPlayer = Setting('join.chars_per_player',
|
||||||
# with lexicon.edit() as l:
|
IntegerField(
|
||||||
# l.title = self.title.data
|
'Characters per player',
|
||||||
# l.editor = UserModel.by(name=self.editor.data).uid
|
widget=NumberInput(),
|
||||||
# l.prompt = self.prompt.data
|
validators=[DataRequired()]))
|
||||||
# l.turn.current = self.turnCurrent.data
|
|
||||||
# l.turn.max = self.turnMax.data
|
s_publishNotifyEditorOnReady = Setting('publish.notify_editor_on_ready',
|
||||||
# l.join.public = self.joinPublic.data
|
BooleanField(
|
||||||
# l.join.open = self.joinOpen.data
|
'Notify the editor when a player marks an article as ready'))
|
||||||
# l.join.password = self.joinPassword.data
|
|
||||||
# l.join.max_players = self.joinMaxPlayers.data
|
s_publishNotifyPlayerOnReject = Setting('publish.notify_player_on_reject',
|
||||||
# l.join.chars_per_player = self.joinCharsPerPlayer.data
|
BooleanField(
|
||||||
# l.publish.notify.editor_on_ready = self.publishNotifyEditorOnReady.data
|
'Notify a player when their article is rejected by the editor'))
|
||||||
# l.publish.notify.player_on_reject = self.publishNotifyPlayerOnReject.data
|
|
||||||
# l.publish.notify.player_on_accept = self.publishNotifyPlayerOnAccept.data
|
s_publishNotifyPlayerOnAccept = Setting('publish.notify_player_on_accept',
|
||||||
# l.publish.deadlines = self.publishDeadlines.data
|
BooleanField(
|
||||||
# l.publish.asap = self.publishAsap.data
|
'Notify a player when their article is accepted by the editor'))
|
||||||
# l.publish.quorum = self.publishQuorum.data
|
|
||||||
# l.publish.block_on_ready = self.publishBlockOnReady.data
|
s_publishDeadlines = Setting('publish.deadlines',
|
||||||
# l.article.index.list = self.articleIndexList.data
|
StringField(
|
||||||
# l.article.index.capacity = self.articleIndexCapacity.data
|
'Turn deadline, as a crontab specification',
|
||||||
# l.article.citation.allow_self = self.articleCitationAllowSelf.data
|
validators=[Optional()]))
|
||||||
# l.article.citation.min_extant = self.articleCitationMinExtant.data
|
|
||||||
# l.article.citation.max_extant = self.articleCitationMaxExtant.data
|
s_publishAsap = Setting('publish.asap',
|
||||||
# l.article.citation.min_phantom = self.articleCitationMinPhantom.data
|
BooleanField(
|
||||||
# l.article.citation.max_phantom = self.articleCitationMaxPhantom.data
|
'Publish the turn immediately when the last article is accepted'))
|
||||||
# l.article.citation.min_total = self.articleCitationMinTotal.data
|
|
||||||
# l.article.citation.max_total = self.articleCitationMaxTotal.data
|
s_publishQuorum = Setting('publish.quorum',
|
||||||
# l.article.citation.min_chars = self.articleCitationMinChars.data
|
IntegerField(
|
||||||
# l.article.citation.max_chars = self.articleCitationMaxChars.data
|
'Quorum to publish incomplete turn',
|
||||||
# l.article.word_limit.soft = self.articleWordLimitSoft.data
|
widget=NumberInput(),
|
||||||
# l.article.word_limit.hard = self.articleWordLimitHard.data
|
validators=[Optional()]))
|
||||||
# l.article.addendum.allowed = self.articleAddendumAllowed.data
|
|
||||||
# l.article.addendum.max = self.articleAddendumMax.data
|
s_publishBlockOnReady = Setting('publish.block_on_ready',
|
||||||
# return True
|
BooleanField(
|
||||||
|
'Block turn publish if any articles are awaiting editor review'))
|
||||||
|
|
||||||
|
s_articleIndexList = Setting('article.index.list',
|
||||||
|
TextAreaField('Index specifications'))
|
||||||
|
|
||||||
|
s_articleIndexCapacity = Setting('article.index.capacity',
|
||||||
|
IntegerField(
|
||||||
|
'Index capacity override',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationAllowSelf = Setting('article.citation.allow_self',
|
||||||
|
BooleanField('Allow players to cite themselves'))
|
||||||
|
|
||||||
|
s_articleCitationMinExtant = Setting('article.citation.min_extant',
|
||||||
|
IntegerField(
|
||||||
|
'Minimum number of extant articles to cite',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMaxExtant = Setting('article.citation.max_extant',
|
||||||
|
IntegerField(
|
||||||
|
'Maximum number of extant articles to cite',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMinPhantom = Setting('article.citation.min_phantom',
|
||||||
|
IntegerField(
|
||||||
|
'Minimum number of phantom articles to cite',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMaxPhantom = Setting('article.citation.max_phantom',
|
||||||
|
IntegerField(
|
||||||
|
'Maximum number of phantom articles to cite',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMinTotal = Setting('article.citation.min_total',
|
||||||
|
IntegerField(
|
||||||
|
'Minimum number of articles to cite in total',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMaxTotal = Setting('article.citation.max_total',
|
||||||
|
IntegerField(
|
||||||
|
'Maximum number of articles to cite in total',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMinChars = Setting('article.citation.min_chars',
|
||||||
|
IntegerField(
|
||||||
|
'Minimum number of characters to cite articles by',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleCitationMaxChars = Setting('article.citation.max_chars',
|
||||||
|
IntegerField(
|
||||||
|
'Maximum number of characters to cite articles by',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleWordLimitSoft = Setting('article.word_limit.soft',
|
||||||
|
IntegerField(
|
||||||
|
'Soft word limit',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleWordLimitHard = Setting('article.word_limit.hard',
|
||||||
|
IntegerField(
|
||||||
|
'Hard word limit',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
s_articleAddendumAllowed = Setting('article.addendum.allowed',
|
||||||
|
BooleanField('Allow addendum articles'))
|
||||||
|
|
||||||
|
s_articleAddendumMax = Setting('article.addendum.max',
|
||||||
|
IntegerField(
|
||||||
|
'Maximum number of addendum articles per character per turn',
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()]))
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigFormBase(FlaskForm):
|
||||||
|
def __init__(self, lexicon):
|
||||||
|
super().__init__()
|
||||||
|
editor_field = getattr(self, 'editor', None)
|
||||||
|
if editor_field:
|
||||||
|
model_factory: ModelFactory = current_app.config['model_factory']
|
||||||
|
editor_field.choices = list(map(
|
||||||
|
lambda s: (s, s),
|
||||||
|
map(
|
||||||
|
lambda uid: model_factory.user(uid).cfg.username,
|
||||||
|
lexicon.cfg.join.joined)))
|
||||||
|
|
||||||
|
def load(self, lexicon):
|
||||||
|
for k, v in Settings.settings():
|
||||||
|
field = getattr(self, k[2:], None)
|
||||||
|
if field:
|
||||||
|
v.load(lexicon.cfg, field)
|
||||||
|
|
||||||
|
def save(self, lexicon):
|
||||||
|
with lexicon.ctx.edit_config() as cfg:
|
||||||
|
for k, v in Settings.settings():
|
||||||
|
field = getattr(self, k[2:], None)
|
||||||
|
if field:
|
||||||
|
v.save(cfg, field)
|
||||||
|
|
||||||
|
|
||||||
|
for k, v in Settings.settings():
|
||||||
|
setattr(ConfigFormBase, k[2:], v.field)
|
||||||
|
|
Loading…
Reference in New Issue