Implement player-specific and game setup settings
This commit is contained in:
parent
ba722562a8
commit
7e54b14b7f
|
@ -84,7 +84,11 @@ def password_check(db: DbContext, lexicon_id: int, password: str) -> bool:
|
||||||
def password_set(db: DbContext, lexicon_id: int, new_password: Optional[str]) -> None:
|
def password_set(db: DbContext, lexicon_id: int, new_password: Optional[str]) -> None:
|
||||||
"""Set or clear a lexicon's password."""
|
"""Set or clear a lexicon's password."""
|
||||||
password_hash = generate_password_hash(new_password) if new_password else None
|
password_hash = generate_password_hash(new_password) if new_password else None
|
||||||
db(update(Lexicon).where(Lexicon.id == lexicon_id).values(password=password_hash))
|
db(
|
||||||
|
update(Lexicon)
|
||||||
|
.where(Lexicon.id == lexicon_id)
|
||||||
|
.values(join_password=password_hash)
|
||||||
|
)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,10 @@ textarea.fullwidth {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
input.fullwidth {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
input.smallnumber {
|
input.smallnumber {
|
||||||
width: 4em;
|
width: 4em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ def edit(lexicon_name, character_id: uuid.UUID):
|
||||||
# Data is valid
|
# Data is valid
|
||||||
character.name = form.name.data
|
character.name = form.name.data
|
||||||
character.signature = form.signature.data
|
character.signature = form.signature.data
|
||||||
g.db.session.commit()
|
g.db.session.commit() # TODO refactor into backend
|
||||||
return redirect(
|
return redirect(
|
||||||
url_for("lexicon.characters.list", lexicon_name=lexicon_name)
|
url_for("lexicon.characters.list", lexicon_name=lexicon_name)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
from flask import Blueprint, render_template, url_for, redirect
|
from flask import Blueprint, render_template, url_for, g, flash, redirect
|
||||||
|
|
||||||
from amanuensis.backend import *
|
from amanuensis.backend import *
|
||||||
from amanuensis.db import *
|
from amanuensis.db import *
|
||||||
from amanuensis.server.helpers import editor_required, lexicon_param, player_required
|
from amanuensis.server.helpers import (
|
||||||
|
editor_required,
|
||||||
|
lexicon_param,
|
||||||
|
player_required,
|
||||||
|
current_membership,
|
||||||
|
current_lexicon,
|
||||||
|
)
|
||||||
|
|
||||||
|
from .forms import PlayerSettingsForm, SetupSettingsForm
|
||||||
|
|
||||||
|
|
||||||
bp = Blueprint("settings", __name__, url_prefix="/settings", template_folder=".")
|
bp = Blueprint("settings", __name__, url_prefix="/settings", template_folder=".")
|
||||||
|
@ -15,27 +23,99 @@ def page(lexicon_name):
|
||||||
return redirect(url_for("lexicon.settings.player", lexicon_name=lexicon_name))
|
return redirect(url_for("lexicon.settings.player", lexicon_name=lexicon_name))
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/player/")
|
@bp.route("/player/", methods=["GET", "POST"])
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@player_required
|
@player_required
|
||||||
def player(lexicon_name):
|
def player(lexicon_name):
|
||||||
return render_template("settings.jinja", lexicon_name=lexicon_name, page_name=player.__name__)
|
form = PlayerSettingsForm()
|
||||||
|
mem: Membership = current_membership
|
||||||
|
|
||||||
|
if not form.is_submitted():
|
||||||
@bp.get("/general/")
|
# GET
|
||||||
@lexicon_param
|
form.notify_ready.data = mem.notify_ready
|
||||||
@editor_required
|
form.notify_reject.data = mem.notify_reject
|
||||||
def general(lexicon_name):
|
form.notify_approve.data = mem.notify_approve
|
||||||
return render_template(
|
return render_template(
|
||||||
"settings.jinja", lexicon_name=lexicon_name, page_name=general.__name__
|
"settings.jinja",
|
||||||
|
lexicon_name=lexicon_name,
|
||||||
|
page_name=player.__name__,
|
||||||
|
form=form,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# POST
|
||||||
|
if form.validate():
|
||||||
|
# Data is valid
|
||||||
|
mem.notify_ready = form.notify_ready.data
|
||||||
|
mem.notify_reject = form.notify_reject.data
|
||||||
|
mem.notify_approve = form.notify_approve.data
|
||||||
|
g.db.session.commit() # TODO refactor into backend
|
||||||
|
flash("Settings saved")
|
||||||
|
return redirect(
|
||||||
|
url_for("lexicon.settings.player", lexicon_name=lexicon_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Invalid POST data
|
||||||
|
return render_template(
|
||||||
|
"settings.jinja",
|
||||||
|
lexicon_name=lexicon_name,
|
||||||
|
page_name=player.__name__,
|
||||||
|
form=form,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/join/")
|
@bp.route("/setup/", methods=["GET", "POST"])
|
||||||
@lexicon_param
|
@lexicon_param
|
||||||
@editor_required
|
@editor_required
|
||||||
def join(lexicon_name):
|
def setup(lexicon_name):
|
||||||
return render_template("settings.jinja", lexicon_name=lexicon_name, page_name=join.__name__)
|
form = SetupSettingsForm()
|
||||||
|
lexicon: Lexicon = current_lexicon
|
||||||
|
|
||||||
|
if not form.is_submitted():
|
||||||
|
# GET
|
||||||
|
form.title.data = lexicon.title
|
||||||
|
form.prompt.data = lexicon.prompt
|
||||||
|
form.public.data = lexicon.public
|
||||||
|
form.joinable.data = lexicon.joinable
|
||||||
|
form.has_password.data = lexicon.join_password is not None
|
||||||
|
form.turn_count.data = lexicon.turn_count
|
||||||
|
form.player_limit.data = lexicon.player_limit
|
||||||
|
form.character_limit.data = lexicon.character_limit
|
||||||
|
return render_template(
|
||||||
|
"settings.jinja",
|
||||||
|
lexicon_name=lexicon_name,
|
||||||
|
page_name=setup.__name__,
|
||||||
|
form=form,
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# POST
|
||||||
|
if form.validate():
|
||||||
|
# Data is valid
|
||||||
|
lexicon.title = form.title.data
|
||||||
|
lexicon.prompt = form.prompt.data
|
||||||
|
lexicon.public = form.public.data
|
||||||
|
lexicon.joinable = form.joinable.data
|
||||||
|
new_password = form.password.data if form.has_password.data else None
|
||||||
|
lexiq.password_set(g.db, lexicon.id, new_password)
|
||||||
|
lexicon.turn_count = form.turn_count.data
|
||||||
|
lexicon.player_limit = form.player_limit.data
|
||||||
|
lexicon.character_limit = form.character_limit.data
|
||||||
|
g.db.session.commit() # TODO refactor into backend
|
||||||
|
flash("Settings saved")
|
||||||
|
return redirect(
|
||||||
|
url_for("lexicon.settings.setup", lexicon_name=lexicon_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Invalid POST data
|
||||||
|
return render_template(
|
||||||
|
"settings.jinja",
|
||||||
|
lexicon_name=lexicon_name,
|
||||||
|
page_name=setup.__name__,
|
||||||
|
form=form,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/progress/")
|
@bp.get("/progress/")
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
from flask_wtf import FlaskForm
|
||||||
|
from wtforms import (
|
||||||
|
BooleanField,
|
||||||
|
IntegerField,
|
||||||
|
PasswordField,
|
||||||
|
StringField,
|
||||||
|
SubmitField,
|
||||||
|
TextAreaField,
|
||||||
|
)
|
||||||
|
from wtforms.validators import Optional, DataRequired
|
||||||
|
from wtforms.widgets.html5 import NumberInput
|
||||||
|
|
||||||
|
|
||||||
|
class PlayerSettingsForm(FlaskForm):
|
||||||
|
"""/lexicon/<name>/settings/player/"""
|
||||||
|
|
||||||
|
notify_ready = BooleanField("Notify me when an article is submitted for review")
|
||||||
|
notify_reject = BooleanField("Notify me when an editor rejects one of my articles")
|
||||||
|
notify_approve = BooleanField(
|
||||||
|
"Notify me when an editor approves one of my articles"
|
||||||
|
)
|
||||||
|
submit = SubmitField("Submit")
|
||||||
|
|
||||||
|
|
||||||
|
class SetupSettingsForm(FlaskForm):
|
||||||
|
"""/lexicon/<name>/settings/setup/"""
|
||||||
|
|
||||||
|
title = StringField("Title override")
|
||||||
|
prompt = TextAreaField("Prompt", validators=[DataRequired()])
|
||||||
|
public = BooleanField("Make game publicly visible")
|
||||||
|
joinable = BooleanField("Allow players to join game")
|
||||||
|
has_password = BooleanField("Require password to join the game")
|
||||||
|
password = PasswordField("Game password")
|
||||||
|
turn_count = IntegerField(
|
||||||
|
"Number of turns", widget=NumberInput(), validators=[DataRequired()]
|
||||||
|
)
|
||||||
|
player_limit = IntegerField(
|
||||||
|
"Maximum number of players", widget=NumberInput(), validators=[Optional()]
|
||||||
|
)
|
||||||
|
character_limit = IntegerField(
|
||||||
|
"Maximum number of characters per player",
|
||||||
|
widget=NumberInput(),
|
||||||
|
validators=[Optional()],
|
||||||
|
)
|
||||||
|
submit = SubmitField("Submit")
|
|
@ -5,12 +5,24 @@
|
||||||
<a{% if page_name != page %} href="{{ url_for('lexicon.settings.' + page, lexicon_name=lexicon_name) }}"{% endif %}>{{ text }}</a>
|
<a{% if page_name != page %} href="{{ url_for('lexicon.settings.' + page, lexicon_name=lexicon_name) }}"{% endif %}>{{ text }}</a>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro flag_setting(field) %}
|
||||||
|
{{ field() }}
|
||||||
|
{{ field.label }}<br>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro number_setting(field) %}
|
||||||
|
{{ field(autocomplete="off", class_="smallnumber") }}
|
||||||
|
{{ field.label }}<br>
|
||||||
|
{% for error in field.errors %}
|
||||||
|
<span style="color: #ff0000">{{ error }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
{% if current_membership.is_editor %}
|
{% if current_membership.is_editor %}
|
||||||
<ul class="unordered-tabs">
|
<ul class="unordered-tabs">
|
||||||
<li>{{ settings_page_link("player", "Player") }}</li>
|
<li>{{ settings_page_link("player", "Player") }}</li>
|
||||||
<li>{{ settings_page_link("general", "General") }}</li>
|
<li>{{ settings_page_link("setup", "Game Setup") }}</li>
|
||||||
<li>{{ settings_page_link("join", "Visibility and Joining") }}</li>
|
|
||||||
<li>{{ settings_page_link("progress", "Game Progress") }}</li>
|
<li>{{ settings_page_link("progress", "Game Progress") }}</li>
|
||||||
<li>{{ settings_page_link("publish", "Turn Publishing") }}</li>
|
<li>{{ settings_page_link("publish", "Turn Publishing") }}</li>
|
||||||
<li>{{ settings_page_link("article", "Article Requirements") }}</li>
|
<li>{{ settings_page_link("article", "Article Requirements") }}</li>
|
||||||
|
@ -19,14 +31,56 @@
|
||||||
|
|
||||||
{% if page_name == "player" %}
|
{% if page_name == "player" %}
|
||||||
<h3>Player Settings</h3>
|
<h3>Player Settings</h3>
|
||||||
|
<form action="" method="post" novalidate>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
<p>
|
||||||
|
{% if current_membership.is_editor %}{{ flag_setting(form.notify_ready) }}{% endif %}
|
||||||
|
{{ flag_setting(form.notify_reject) }}
|
||||||
|
{{ flag_setting(form.notify_approve) }}
|
||||||
|
</p>
|
||||||
|
<p>{{ form.submit() }}</p>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<span style="color:#ff0000">{{ message }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if page_name == "general" %}
|
{% if page_name == "setup" %}
|
||||||
<h3>General Settings</h3>
|
<h3>Game Setup</h3>
|
||||||
{% endif %}
|
<form action="" method="post" novalidate>
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
{% if page_name == "join" %}
|
<p>
|
||||||
<h3>Visibility and Joining</h3>
|
{{ form.title.label }}:<br>
|
||||||
|
{{ form.title(autocomplete="off", placeholder="Lexicon " + lexicon_name, class_="fullwidth") }}<br>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ form.prompt.label }}: {{ form.prompt(class_="fullwidth") }}
|
||||||
|
{% for error in form.prompt.errors %}
|
||||||
|
<span style="color: #ff0000">{{ error }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ flag_setting(form.public) }}
|
||||||
|
{{ flag_setting(form.joinable) }}
|
||||||
|
{{ form.has_password() }}
|
||||||
|
{{ form.has_password.label }}:<br>
|
||||||
|
{{ form.password(autocomplete="off") }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ number_setting(form.turn_count) }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ number_setting(form.player_limit) }}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
{{ number_setting(form.character_limit) }}
|
||||||
|
</p>
|
||||||
|
<p>{{ form.submit() }}</p>
|
||||||
|
</form>
|
||||||
|
{% for message in get_flashed_messages() %}
|
||||||
|
<span style="color:#ff0000">{{ message }}</span><br>
|
||||||
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if page_name == "progress" %}
|
{% if page_name == "progress" %}
|
||||||
|
|
Loading…
Reference in New Issue