Kick off the server refactor with home and auth
This commit is contained in:
parent
07e62b9665
commit
3077b02508
|
@ -69,14 +69,17 @@ def command_run(args):
|
||||||
The default Flask server is not secure, and should
|
The default Flask server is not secure, and should
|
||||||
only be used for development.
|
only be used for development.
|
||||||
"""
|
"""
|
||||||
from amanuensis.server import app
|
from amanuensis.server import get_app
|
||||||
from amanuensis.config import get, logger
|
|
||||||
|
|
||||||
if get("secret_key") is None:
|
root: RootConfigDirectoryContext = args.root
|
||||||
logger.error("Can't run server without a secret_key. Run generate-sec"
|
|
||||||
"ret first")
|
with root.read_config() as cfg:
|
||||||
|
if cfg.secret_key is None:
|
||||||
|
logger.error("Can't run server without a secret_key. "
|
||||||
|
"Run generate-secet first.")
|
||||||
return -1
|
return -1
|
||||||
app.run(host=args.address, port=args.port, debug=args.debug)
|
|
||||||
|
get_app(root).run(host=args.address, port=args.port, debug=args.debug)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"address": "127.0.0.1",
|
"address": "127.0.0.1",
|
||||||
"port": "5000",
|
"port": "5000",
|
||||||
"lexicon_data": "./lexicon",
|
"lexicon_data": "./lexicon",
|
||||||
"static_root": "./static",
|
"static_root": "../resources",
|
||||||
"email": {
|
"email": {
|
||||||
"server": null,
|
"server": null,
|
||||||
"port": null,
|
"port": null,
|
||||||
|
|
|
@ -1,41 +1,38 @@
|
||||||
import os
|
from flask import Flask
|
||||||
|
|
||||||
from flask import Flask, render_template
|
from amanuensis.config import RootConfigDirectoryContext
|
||||||
from flask_login import LoginManager
|
|
||||||
|
|
||||||
from amanuensis.config import get, root
|
|
||||||
# from amanuensis.server.auth import get_bp as get_auth_bp
|
|
||||||
from amanuensis.server.home import get_bp as get_home_bp
|
|
||||||
from amanuensis.server.helpers import register_custom_filters
|
|
||||||
from amanuensis.server.lexicon import get_bp as get_lex_bp
|
|
||||||
from amanuensis.user import AnonymousUserModel
|
|
||||||
from amanuensis.models import ModelFactory
|
from amanuensis.models import ModelFactory
|
||||||
|
from amanuensis.server.auth import get_login_manager, bp_auth
|
||||||
|
from amanuensis.server.helpers import register_custom_filters
|
||||||
|
from amanuensis.server.home import bp_home
|
||||||
|
# from amanuensis.server.lexicon import bp_lexicon
|
||||||
|
|
||||||
|
|
||||||
|
def get_app(root: RootConfigDirectoryContext) -> Flask:
|
||||||
# Flask app init
|
# Flask app init
|
||||||
static_root = os.path.abspath(get("static_root"))
|
with root.read_config() as cfg:
|
||||||
app = Flask(
|
app = Flask(
|
||||||
__name__,
|
__name__,
|
||||||
template_folder="../templates",
|
template_folder='../templates',
|
||||||
static_folder=static_root)
|
static_folder=cfg.static_root
|
||||||
app.secret_key = bytes.fromhex(get('secret_key'))
|
)
|
||||||
|
app.secret_key = bytes.fromhex(cfg.secret_key)
|
||||||
|
app.config['root'] = root
|
||||||
app.config['model_factory'] = ModelFactory(root)
|
app.config['model_factory'] = ModelFactory(root)
|
||||||
app.jinja_options['trim_blocks'] = True
|
app.jinja_options['trim_blocks'] = True
|
||||||
app.jinja_options['lstrip_blocks'] = True
|
app.jinja_options['lstrip_blocks'] = True
|
||||||
register_custom_filters(app)
|
register_custom_filters(app)
|
||||||
|
|
||||||
# Flask-Login init
|
# Flask-Login init
|
||||||
login = LoginManager(app)
|
login_manager = get_login_manager(root)
|
||||||
login.login_view = 'auth.login'
|
login_manager.init_app(app)
|
||||||
login.anonymous_user = AnonymousUserModel
|
|
||||||
|
|
||||||
# Blueprint inits
|
# Blueprint inits
|
||||||
from amanuensis.server.auth import bp as auth_bp
|
app.register_blueprint(bp_auth)
|
||||||
from amanuensis.server.auth import login_manager as login_manager
|
app.register_blueprint(bp_home)
|
||||||
login_manager.init_app(app)
|
# app.register_blueprint(bp_lexicon)
|
||||||
app.register_blueprint(auth_bp)
|
|
||||||
|
|
||||||
home_bp = get_home_bp()
|
# import code
|
||||||
app.register_blueprint(home_bp)
|
# code.interact(local=locals())
|
||||||
|
|
||||||
lex_bp = get_lex_bp()
|
return app
|
||||||
app.register_blueprint(lex_bp)
|
|
||||||
|
|
|
@ -1,42 +1,64 @@
|
||||||
|
import logging
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from flask import Blueprint, render_template, redirect, url_for, flash, current_app
|
from flask import (
|
||||||
from flask_login import login_user, logout_user, login_required, LoginManager
|
Blueprint,
|
||||||
|
render_template,
|
||||||
|
redirect,
|
||||||
|
url_for,
|
||||||
|
flash,
|
||||||
|
current_app)
|
||||||
|
from flask_login import (
|
||||||
|
login_user,
|
||||||
|
logout_user,
|
||||||
|
login_required,
|
||||||
|
LoginManager)
|
||||||
|
|
||||||
from amanuensis.config import logger, json_rw
|
from amanuensis.config import RootConfigDirectoryContext
|
||||||
from amanuensis.server.forms import LoginForm
|
from amanuensis.server.forms import LoginForm
|
||||||
from amanuensis.user import UserModel, AnonymousUserModel
|
from amanuensis.models import ModelFactory, AnonymousUserModel
|
||||||
|
|
||||||
# TODO refactor login init into a func that takes a root cdc
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def get_login_manager(root: RootConfigDirectoryContext) -> LoginManager:
|
||||||
|
"""
|
||||||
|
Creates a login manager
|
||||||
|
"""
|
||||||
login_manager = LoginManager()
|
login_manager = LoginManager()
|
||||||
login_manager.login_view = 'auth.login'
|
login_manager.login_view = 'auth.login'
|
||||||
login_manager.anonymous_user = AnonymousUserModel
|
login_manager.anonymous_user = AnonymousUserModel
|
||||||
|
|
||||||
bp = Blueprint('auth', __name__, url_prefix='/auth')
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(uid):
|
def load_user(uid):
|
||||||
return UserModel.by(uid=str(uid))
|
return current_app.config['model_factory'].user(str(uid))
|
||||||
|
|
||||||
@bp.route('/login/', methods=['GET', 'POST'])
|
return login_manager
|
||||||
|
|
||||||
|
|
||||||
|
bp_auth = Blueprint('auth', __name__, url_prefix='/auth')
|
||||||
|
|
||||||
|
|
||||||
|
@bp_auth.route('/login/', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
|
model_factory: ModelFactory = current_app.config['model_factory']
|
||||||
form = LoginForm()
|
form = LoginForm()
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
username = form.username.data
|
username = form.username.data
|
||||||
u = UserModel.by(name=username)
|
user = model_factory.try_user(username)
|
||||||
if u is not None and u.check_password(form.password.data):
|
if user is not None and user.check_password(form.password.data):
|
||||||
remember_me = form.remember.data
|
remember_me = form.remember.data
|
||||||
login_user(u, remember=remember_me)
|
login_user(user, remember=remember_me)
|
||||||
with json_rw(u.config_path) as cfg:
|
with user.ctx.edit_config() as cfg:
|
||||||
cfg.last_login = int(time.time())
|
cfg.last_login = int(time.time())
|
||||||
logger.info("Logged in user '{}' ({})".format(
|
logger.info('Logged in user "{0.username}" ({0.uid})'
|
||||||
u.username, u.uid))
|
.format(user.cfg))
|
||||||
return redirect(url_for('home.home'))
|
return redirect(url_for('home.home'))
|
||||||
flash("Login not recognized")
|
flash("Login not recognized")
|
||||||
return render_template('auth/login.html', form=form)
|
return render_template('auth/login.html', form=form)
|
||||||
|
|
||||||
@bp.route("/logout/", methods=['GET'])
|
|
||||||
|
@bp_auth.route("/logout/", methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
logout_user()
|
logout_user()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from flask import current_app
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import (
|
from wtforms import (
|
||||||
StringField, PasswordField, BooleanField, SubmitField, TextAreaField,
|
StringField, PasswordField, BooleanField, SubmitField, TextAreaField,
|
||||||
|
@ -5,9 +6,10 @@ from wtforms import (
|
||||||
from wtforms.validators import DataRequired, ValidationError, Optional
|
from wtforms.validators import DataRequired, ValidationError, Optional
|
||||||
from wtforms.widgets.html5 import NumberInput
|
from wtforms.widgets.html5 import NumberInput
|
||||||
|
|
||||||
from amanuensis.config import root
|
# from amanuensis.config import root
|
||||||
from amanuensis.lexicon.manage import add_character
|
# from amanuensis.lexicon.manage import add_character
|
||||||
from amanuensis.user import UserModel
|
# from amanuensis.user import UserModel
|
||||||
|
from amanuensis.config import RootConfigDirectoryContext
|
||||||
|
|
||||||
|
|
||||||
# Custom validators
|
# Custom validators
|
||||||
|
@ -15,10 +17,13 @@ def user(exists=True):
|
||||||
template = 'User "{{}}" {}'.format(
|
template = 'User "{{}}" {}'.format(
|
||||||
"not found" if exists else "already exists")
|
"not found" if exists else "already exists")
|
||||||
should_exist = bool(exists)
|
should_exist = bool(exists)
|
||||||
|
|
||||||
def validate_user(form, field):
|
def validate_user(form, field):
|
||||||
with root.user.index() as index:
|
root: RootConfigDirectoryContext = current_app.config['root']
|
||||||
|
with root.user.read_index() as index:
|
||||||
if (field.data in index.keys()) != should_exist:
|
if (field.data in index.keys()) != should_exist:
|
||||||
raise ValidationError(template.format(field.data))
|
raise ValidationError(template.format(field.data))
|
||||||
|
|
||||||
return validate_user
|
return validate_user
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,17 +31,21 @@ def lexicon(exists=True):
|
||||||
template = 'Lexicon "{{}}" {}'.format(
|
template = 'Lexicon "{{}}" {}'.format(
|
||||||
"not found" if exists else "already exists")
|
"not found" if exists else "already exists")
|
||||||
should_exist = bool(exists)
|
should_exist = bool(exists)
|
||||||
|
|
||||||
def validate_lexicon(form, field):
|
def validate_lexicon(form, field):
|
||||||
with root.lexicon.index() as index:
|
root: RootConfigDirectoryContext = current_app.config['root']
|
||||||
|
with root.lexicon.read_index() as index:
|
||||||
if (field.data in index.keys()) != should_exist:
|
if (field.data in index.keys()) != should_exist:
|
||||||
raise ValidationError(template.format(field.data))
|
raise ValidationError(template.format(field.data))
|
||||||
|
|
||||||
return validate_lexicon
|
return validate_lexicon
|
||||||
|
|
||||||
|
|
||||||
# Forms
|
# Forms
|
||||||
class LoginForm(FlaskForm):
|
class LoginForm(FlaskForm):
|
||||||
"""/auth/login/"""
|
"""/auth/login/"""
|
||||||
username = StringField('Username', validators=[DataRequired(), user(exists=True)])
|
username = StringField('Username',
|
||||||
|
validators=[DataRequired(), user(exists=True)])
|
||||||
password = PasswordField('Password', validators=[DataRequired()])
|
password = PasswordField('Password', validators=[DataRequired()])
|
||||||
remember = BooleanField('Stay logged in')
|
remember = BooleanField('Stay logged in')
|
||||||
submit = SubmitField('Log in')
|
submit = SubmitField('Log in')
|
||||||
|
@ -54,194 +63,199 @@ class LexiconCreateForm(FlaskForm):
|
||||||
submit = SubmitField('Create')
|
submit = SubmitField('Create')
|
||||||
|
|
||||||
|
|
||||||
class LexiconConfigForm(FlaskForm):
|
# class LexiconConfigForm(FlaskForm):
|
||||||
"""/lexicon/<name>/session/settings/"""
|
# """/lexicon/<name>/session/settings/"""
|
||||||
# General
|
# # General
|
||||||
title = StringField('Title override', validators=[Optional()])
|
# title = StringField('Title override', validators=[Optional()])
|
||||||
editor = SelectField('Editor', validators=[DataRequired(), user(exists=True)])
|
# editor = SelectField('Editor', validators=[DataRequired(), user(exists=True)])
|
||||||
prompt = TextAreaField('Prompt', validators=[DataRequired()])
|
# prompt = TextAreaField('Prompt', validators=[DataRequired()])
|
||||||
# Turn
|
# # Turn
|
||||||
turnCurrent = IntegerField('Current turn', widget=NumberInput(), validators=[Optional()])
|
# turnCurrent = IntegerField('Current turn', widget=NumberInput(), validators=[Optional()])
|
||||||
turnMax = IntegerField('Number of turns', widget=NumberInput(), validators=[DataRequired()])
|
# turnMax = IntegerField('Number of turns', widget=NumberInput(), validators=[DataRequired()])
|
||||||
# Join
|
# # Join
|
||||||
joinPublic = BooleanField("Show game on public pages")
|
# joinPublic = BooleanField("Show game on public pages")
|
||||||
joinOpen = BooleanField("Allow players to join game")
|
# joinOpen = BooleanField("Allow players to join game")
|
||||||
joinPassword = StringField("Password to join game", validators=[Optional()])
|
# joinPassword = StringField("Password to join game", validators=[Optional()])
|
||||||
joinMaxPlayers = IntegerField(
|
# joinMaxPlayers = IntegerField(
|
||||||
"Maximum number of players",
|
# "Maximum number of players",
|
||||||
widget=NumberInput(),
|
# widget=NumberInput(),
|
||||||
validators=[DataRequired()])
|
# validators=[DataRequired()])
|
||||||
joinCharsPerPlayer = IntegerField(
|
# joinCharsPerPlayer = IntegerField(
|
||||||
"Characters per player",
|
# "Characters per player",
|
||||||
widget=NumberInput(),
|
# widget=NumberInput(),
|
||||||
validators=[DataRequired()])
|
# validators=[DataRequired()])
|
||||||
# Publish
|
# # Publish
|
||||||
publishNotifyEditorOnReady = BooleanField(
|
# publishNotifyEditorOnReady = BooleanField(
|
||||||
"Notify the editor when a player marks an article as ready")
|
# "Notify the editor when a player marks an article as ready")
|
||||||
publishNotifyPlayerOnReject = BooleanField(
|
# publishNotifyPlayerOnReject = BooleanField(
|
||||||
"Notify a player when their article is rejected by the editor")
|
# "Notify a player when their article is rejected by the editor")
|
||||||
publishNotifyPlayerOnAccept = BooleanField(
|
# publishNotifyPlayerOnAccept = BooleanField(
|
||||||
"Notify a player when their article is accepted by the editor")
|
# "Notify a player when their article is accepted by the editor")
|
||||||
publishDeadlines = StringField(
|
# publishDeadlines = StringField(
|
||||||
"Turn deadline, as a crontab specification", validators=[Optional()])
|
# "Turn deadline, as a crontab specification", validators=[Optional()])
|
||||||
publishAsap = BooleanField(
|
# publishAsap = BooleanField(
|
||||||
"Publish the turn immediately when the last article is accepted")
|
# "Publish the turn immediately when the last article is accepted")
|
||||||
publishQuorum = IntegerField(
|
# publishQuorum = IntegerField(
|
||||||
"Quorum to publish incomplete turn", widget=NumberInput(), validators=[Optional()])
|
# "Quorum to publish incomplete turn", widget=NumberInput(), validators=[Optional()])
|
||||||
publishBlockOnReady = BooleanField(
|
# publishBlockOnReady = BooleanField(
|
||||||
"Block turn publish if any articles are awaiting editor review")
|
# "Block turn publish if any articles are awaiting editor review")
|
||||||
# Article
|
# # Article
|
||||||
articleIndexList = TextAreaField("Index specifications")
|
# articleIndexList = TextAreaField("Index specifications")
|
||||||
articleIndexCapacity = IntegerField(
|
# articleIndexCapacity = IntegerField(
|
||||||
"Index capacity override", widget=NumberInput(), validators=[Optional()])
|
# "Index capacity override", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationAllowSelf = BooleanField(
|
# articleCitationAllowSelf = BooleanField(
|
||||||
"Allow players to cite themselves")
|
# "Allow players to cite themselves")
|
||||||
articleCitationMinExtant = IntegerField(
|
# articleCitationMinExtant = IntegerField(
|
||||||
"Minimum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
# "Minimum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMaxExtant = IntegerField(
|
# articleCitationMaxExtant = IntegerField(
|
||||||
"Maximum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
# "Maximum number of extant articles to cite", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMinPhantom = IntegerField(
|
# articleCitationMinPhantom = IntegerField(
|
||||||
"Minimum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
# "Minimum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMaxPhantom = IntegerField(
|
# articleCitationMaxPhantom = IntegerField(
|
||||||
"Maximum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
# "Maximum number of phantom articles to cite", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMinTotal = IntegerField(
|
# articleCitationMinTotal = IntegerField(
|
||||||
"Minimum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
# "Minimum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMaxTotal = IntegerField(
|
# articleCitationMaxTotal = IntegerField(
|
||||||
"Maximum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
# "Maximum number of articles to cite in total", widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMinChars = IntegerField(
|
# articleCitationMinChars = IntegerField(
|
||||||
"Minimum number of characters to cite articles by",
|
# "Minimum number of characters to cite articles by",
|
||||||
widget=NumberInput(), validators=[Optional()])
|
# widget=NumberInput(), validators=[Optional()])
|
||||||
articleCitationMaxChars = IntegerField(
|
# articleCitationMaxChars = IntegerField(
|
||||||
"Maximum number of characters to cite articles by",
|
# "Maximum number of characters to cite articles by",
|
||||||
widget=NumberInput(), validators=[Optional()])
|
# widget=NumberInput(), validators=[Optional()])
|
||||||
articleWordLimitSoft = IntegerField(
|
# articleWordLimitSoft = IntegerField(
|
||||||
"Soft word limit", widget=NumberInput(), validators=[Optional()])
|
# "Soft word limit", widget=NumberInput(), validators=[Optional()])
|
||||||
articleWordLimitHard = IntegerField(
|
# articleWordLimitHard = IntegerField(
|
||||||
"Hard word limit", widget=NumberInput(), validators=[Optional()])
|
# "Hard word limit", widget=NumberInput(), validators=[Optional()])
|
||||||
articleAddendumAllowed = BooleanField("Allow addendum articles")
|
# articleAddendumAllowed = BooleanField("Allow addendum articles")
|
||||||
articleAddendumMax = IntegerField(
|
# articleAddendumMax = IntegerField(
|
||||||
"Maximum number of addendum articles per character per turn",
|
# "Maximum number of addendum articles per character per turn",
|
||||||
widget=NumberInput(), validators=[Optional()])
|
# widget=NumberInput(), validators=[Optional()])
|
||||||
# And finally, the submit button
|
# # And finally, the submit button
|
||||||
submit = SubmitField("Submit")
|
# submit = SubmitField("Submit")
|
||||||
|
|
||||||
def validate_publishDeadlines(form, field):
|
# def validate_publishDeadlines(form, field):
|
||||||
if form.publishAsap.data:
|
# if form.publishAsap.data:
|
||||||
raise ValidationError('Cannot specify deadline if immediate publishing is enabled')
|
# raise ValidationError('Cannot specify deadline if immediate publishing is enabled')
|
||||||
|
|
||||||
# TODO add validators that call into extant valid check methods
|
# # TODO add validators that call into extant valid check methods
|
||||||
|
|
||||||
def set_options(self, lexicon):
|
# def set_options(self, lexicon):
|
||||||
self.editor.choices = list(map(lambda x: (x, x), map(
|
# self.editor.choices = list(map(lambda x: (x, x), map(
|
||||||
lambda uid: UserModel.by(uid=uid).username,
|
# lambda uid: UserModel.by(uid=uid).username,
|
||||||
lexicon.join.joined)))
|
# lexicon.join.joined)))
|
||||||
|
|
||||||
def populate_from_lexicon(self, lexicon):
|
# def populate_from_lexicon(self, lexicon):
|
||||||
self.title.data = lexicon.title
|
# self.title.data = lexicon.title
|
||||||
self.editor.data = UserModel.by(uid=lexicon.editor).username
|
# self.editor.data = UserModel.by(uid=lexicon.editor).username
|
||||||
self.prompt.data = lexicon.prompt
|
# self.prompt.data = lexicon.prompt
|
||||||
self.turnCurrent.data = lexicon.turn.current
|
# self.turnCurrent.data = lexicon.turn.current
|
||||||
self.turnMax.data = lexicon.turn.max
|
# self.turnMax.data = lexicon.turn.max
|
||||||
self.joinPublic.data = lexicon.join.public
|
# self.joinPublic.data = lexicon.join.public
|
||||||
self.joinOpen.data = lexicon.join.open
|
# self.joinOpen.data = lexicon.join.open
|
||||||
self.joinPassword.data = lexicon.join.password
|
# self.joinPassword.data = lexicon.join.password
|
||||||
self.joinMaxPlayers.data = lexicon.join.max_players
|
# self.joinMaxPlayers.data = lexicon.join.max_players
|
||||||
self.joinCharsPerPlayer.data = lexicon.join.chars_per_player
|
# self.joinCharsPerPlayer.data = lexicon.join.chars_per_player
|
||||||
self.publishNotifyEditorOnReady.data = lexicon.publish.notify.editor_on_ready
|
# self.publishNotifyEditorOnReady.data = lexicon.publish.notify.editor_on_ready
|
||||||
self.publishNotifyPlayerOnReject.data = lexicon.publish.notify.player_on_reject
|
# self.publishNotifyPlayerOnReject.data = lexicon.publish.notify.player_on_reject
|
||||||
self.publishNotifyPlayerOnAccept.data = lexicon.publish.notify.player_on_accept
|
# self.publishNotifyPlayerOnAccept.data = lexicon.publish.notify.player_on_accept
|
||||||
self.publishDeadlines.data = lexicon.publish.deadlines
|
# self.publishDeadlines.data = lexicon.publish.deadlines
|
||||||
self.publishAsap.data = lexicon.publish.asap
|
# self.publishAsap.data = lexicon.publish.asap
|
||||||
self.publishQuorum.data = lexicon.publish.quorum
|
# self.publishQuorum.data = lexicon.publish.quorum
|
||||||
self.publishBlockOnReady.data = lexicon.publish.block_on_ready
|
# self.publishBlockOnReady.data = lexicon.publish.block_on_ready
|
||||||
self.articleIndexList.data = lexicon.article.index.list
|
# self.articleIndexList.data = lexicon.article.index.list
|
||||||
self.articleIndexCapacity.data = lexicon.article.index.capacity
|
# self.articleIndexCapacity.data = lexicon.article.index.capacity
|
||||||
self.articleCitationAllowSelf.data = lexicon.article.citation.allow_self
|
# self.articleCitationAllowSelf.data = lexicon.article.citation.allow_self
|
||||||
self.articleCitationMinExtant.data = lexicon.article.citation.min_extant
|
# self.articleCitationMinExtant.data = lexicon.article.citation.min_extant
|
||||||
self.articleCitationMaxExtant.data = lexicon.article.citation.max_extant
|
# self.articleCitationMaxExtant.data = lexicon.article.citation.max_extant
|
||||||
self.articleCitationMinPhantom.data = lexicon.article.citation.min_phantom
|
# self.articleCitationMinPhantom.data = lexicon.article.citation.min_phantom
|
||||||
self.articleCitationMaxPhantom.data = lexicon.article.citation.max_phantom
|
# self.articleCitationMaxPhantom.data = lexicon.article.citation.max_phantom
|
||||||
self.articleCitationMinTotal.data = lexicon.article.citation.min_total
|
# self.articleCitationMinTotal.data = lexicon.article.citation.min_total
|
||||||
self.articleCitationMaxTotal.data = lexicon.article.citation.max_total
|
# self.articleCitationMaxTotal.data = lexicon.article.citation.max_total
|
||||||
self.articleCitationMinChars.data = lexicon.article.citation.min_chars
|
# self.articleCitationMinChars.data = lexicon.article.citation.min_chars
|
||||||
self.articleCitationMaxChars.data = lexicon.article.citation.max_chars
|
# self.articleCitationMaxChars.data = lexicon.article.citation.max_chars
|
||||||
self.articleWordLimitSoft.data = lexicon.article.word_limit.soft
|
# self.articleWordLimitSoft.data = lexicon.article.word_limit.soft
|
||||||
self.articleWordLimitHard.data = lexicon.article.word_limit.hard
|
# self.articleWordLimitHard.data = lexicon.article.word_limit.hard
|
||||||
self.articleAddendumAllowed.data = lexicon.article.addendum.allowed
|
# self.articleAddendumAllowed.data = lexicon.article.addendum.allowed
|
||||||
self.articleAddendumMax.data = lexicon.article.addendum.max
|
# self.articleAddendumMax.data = lexicon.article.addendum.max
|
||||||
|
|
||||||
def update_lexicon(self, lexicon):
|
# def update_lexicon(self, lexicon):
|
||||||
with lexicon.edit() as l:
|
# with lexicon.edit() as l:
|
||||||
l.title = self.title.data
|
# l.title = self.title.data
|
||||||
l.editor = UserModel.by(name=self.editor.data).uid
|
# l.editor = UserModel.by(name=self.editor.data).uid
|
||||||
l.prompt = self.prompt.data
|
# l.prompt = self.prompt.data
|
||||||
l.turn.current = self.turnCurrent.data
|
# l.turn.current = self.turnCurrent.data
|
||||||
l.turn.max = self.turnMax.data
|
# l.turn.max = self.turnMax.data
|
||||||
l.join.public = self.joinPublic.data
|
# l.join.public = self.joinPublic.data
|
||||||
l.join.open = self.joinOpen.data
|
# l.join.open = self.joinOpen.data
|
||||||
l.join.password = self.joinPassword.data
|
# l.join.password = self.joinPassword.data
|
||||||
l.join.max_players = self.joinMaxPlayers.data
|
# l.join.max_players = self.joinMaxPlayers.data
|
||||||
l.join.chars_per_player = self.joinCharsPerPlayer.data
|
# l.join.chars_per_player = self.joinCharsPerPlayer.data
|
||||||
l.publish.notify.editor_on_ready = self.publishNotifyEditorOnReady.data
|
# l.publish.notify.editor_on_ready = self.publishNotifyEditorOnReady.data
|
||||||
l.publish.notify.player_on_reject = self.publishNotifyPlayerOnReject.data
|
# l.publish.notify.player_on_reject = self.publishNotifyPlayerOnReject.data
|
||||||
l.publish.notify.player_on_accept = self.publishNotifyPlayerOnAccept.data
|
# l.publish.notify.player_on_accept = self.publishNotifyPlayerOnAccept.data
|
||||||
l.publish.deadlines = self.publishDeadlines.data
|
# l.publish.deadlines = self.publishDeadlines.data
|
||||||
l.publish.asap = self.publishAsap.data
|
# l.publish.asap = self.publishAsap.data
|
||||||
l.publish.quorum = self.publishQuorum.data
|
# l.publish.quorum = self.publishQuorum.data
|
||||||
l.publish.block_on_ready = self.publishBlockOnReady.data
|
# l.publish.block_on_ready = self.publishBlockOnReady.data
|
||||||
l.article.index.list = self.articleIndexList.data
|
# l.article.index.list = self.articleIndexList.data
|
||||||
l.article.index.capacity = self.articleIndexCapacity.data
|
# l.article.index.capacity = self.articleIndexCapacity.data
|
||||||
l.article.citation.allow_self = self.articleCitationAllowSelf.data
|
# l.article.citation.allow_self = self.articleCitationAllowSelf.data
|
||||||
l.article.citation.min_extant = self.articleCitationMinExtant.data
|
# l.article.citation.min_extant = self.articleCitationMinExtant.data
|
||||||
l.article.citation.max_extant = self.articleCitationMaxExtant.data
|
# l.article.citation.max_extant = self.articleCitationMaxExtant.data
|
||||||
l.article.citation.min_phantom = self.articleCitationMinPhantom.data
|
# l.article.citation.min_phantom = self.articleCitationMinPhantom.data
|
||||||
l.article.citation.max_phantom = self.articleCitationMaxPhantom.data
|
# l.article.citation.max_phantom = self.articleCitationMaxPhantom.data
|
||||||
l.article.citation.min_total = self.articleCitationMinTotal.data
|
# l.article.citation.min_total = self.articleCitationMinTotal.data
|
||||||
l.article.citation.max_total = self.articleCitationMaxTotal.data
|
# l.article.citation.max_total = self.articleCitationMaxTotal.data
|
||||||
l.article.citation.min_chars = self.articleCitationMinChars.data
|
# l.article.citation.min_chars = self.articleCitationMinChars.data
|
||||||
l.article.citation.max_chars = self.articleCitationMaxChars.data
|
# l.article.citation.max_chars = self.articleCitationMaxChars.data
|
||||||
l.article.word_limit.soft = self.articleWordLimitSoft.data
|
# l.article.word_limit.soft = self.articleWordLimitSoft.data
|
||||||
l.article.word_limit.hard = self.articleWordLimitHard.data
|
# l.article.word_limit.hard = self.articleWordLimitHard.data
|
||||||
l.article.addendum.allowed = self.articleAddendumAllowed.data
|
# l.article.addendum.allowed = self.articleAddendumAllowed.data
|
||||||
l.article.addendum.max = self.articleAddendumMax.data
|
# l.article.addendum.max = self.articleAddendumMax.data
|
||||||
return True
|
# return True
|
||||||
|
|
||||||
|
|
||||||
class LexiconJoinForm(FlaskForm):
|
# class LexiconJoinForm(FlaskForm):
|
||||||
"""/lexicon/<name>/join/"""
|
# """/lexicon/<name>/join/"""
|
||||||
password = StringField('Password')
|
# password = StringField('Password')
|
||||||
submit = SubmitField("Submit")
|
# submit = SubmitField("Submit")
|
||||||
|
|
||||||
|
|
||||||
class LexiconCharacterForm(FlaskForm):
|
# class LexiconCharacterForm(FlaskForm):
|
||||||
"""/lexicon/<name>/session/character/"""
|
# """/lexicon/<name>/session/character/"""
|
||||||
characterName = StringField("Character name", validators=[DataRequired()])
|
# characterName = StringField("Character name", validators=[DataRequired()])
|
||||||
defaultSignature = TextAreaField("Default signature")
|
# defaultSignature = TextAreaField("Default signature")
|
||||||
submit = SubmitField("Submit")
|
# submit = SubmitField("Submit")
|
||||||
|
|
||||||
def for_new(self):
|
# def for_new(self):
|
||||||
self.characterName.data = ""
|
# self.characterName.data = ""
|
||||||
self.defaultSignature.data = "~"
|
# self.defaultSignature.data = "~"
|
||||||
|
|
||||||
def for_character(self, lexicon, cid):
|
# def for_character(self, lexicon, cid):
|
||||||
char = lexicon.character.get(cid)
|
# char = lexicon.character.get(cid)
|
||||||
self.characterName.data = char.name
|
# self.characterName.data = char.name
|
||||||
self.defaultSignature.data = char.signature
|
# self.defaultSignature.data = char.signature
|
||||||
|
|
||||||
def add_character(self, lexicon, user):
|
# def add_character(self, lexicon, user):
|
||||||
add_character(lexicon, user, {
|
# add_character(lexicon, user, {
|
||||||
'name': self.characterName.data,
|
# 'name': self.characterName.data,
|
||||||
'signature': self.defaultSignature.data,
|
# 'signature': self.defaultSignature.data,
|
||||||
})
|
# })
|
||||||
|
|
||||||
def update_character(self, lexicon, cid):
|
# def update_character(self, lexicon, cid):
|
||||||
with lexicon.edit() as l:
|
# with lexicon.edit() as l:
|
||||||
char = l.character.get(cid)
|
# char = l.character.get(cid)
|
||||||
char.name = self.characterName.data
|
# char.name = self.characterName.data
|
||||||
char.signature = self.defaultSignature.data
|
# char.signature = self.defaultSignature.data
|
||||||
|
|
||||||
|
|
||||||
class LexiconReviewForm(FlaskForm):
|
# class LexiconReviewForm(FlaskForm):
|
||||||
"""/lexicon/<name>/session/review/"""
|
# """/lexicon/<name>/session/review/"""
|
||||||
approved = RadioField("Buttons", choices=(("Y", "Approved"), ("N", "Rejected")))
|
# approved = RadioField("Buttons", choices=(("Y", "Approved"), ("N", "Rejected")))
|
||||||
submit = SubmitField("Submit")
|
# submit = SubmitField("Submit")
|
||||||
|
|
||||||
|
|
||||||
|
# class LexiconPublishTurnForm(FlaskForm):
|
||||||
|
# """/lexicon/<name>/session/"""
|
||||||
|
# submit = SubmitField("Publish turn")
|
||||||
|
|
|
@ -7,18 +7,18 @@ from flask import g, flash, redirect, url_for, current_app
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
# Module imports
|
# Module imports
|
||||||
from amanuensis.lexicon import LexiconModel
|
|
||||||
from amanuensis.parser import filesafe_title
|
from amanuensis.parser import filesafe_title
|
||||||
from amanuensis.user import UserModel
|
from amanuensis.models import ModelFactory, UserModel
|
||||||
from amanuensis.models import ModelFactory
|
|
||||||
|
|
||||||
def register_custom_filters(app):
|
def register_custom_filters(app):
|
||||||
"""Adds custom filters to the Flask app"""
|
"""Adds custom filters to the Flask app"""
|
||||||
|
|
||||||
@app.template_filter("user_attr")
|
@app.template_filter("user_attr")
|
||||||
def get_user_attr(uid, attr):
|
def get_user_attr(uid, attr):
|
||||||
user = UserModel.by(uid=uid)
|
factory: ModelFactory = current_app.config['model_factory']
|
||||||
val = getattr(user, attr)
|
user: UserModel = factory.user(uid)
|
||||||
|
val = getattr(user.cfg, attr)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
@app.template_filter("asdate")
|
@app.template_filter("asdate")
|
||||||
|
@ -30,23 +30,26 @@ def register_custom_filters(app):
|
||||||
|
|
||||||
@app.template_filter("articlelink")
|
@app.template_filter("articlelink")
|
||||||
def article_link(title):
|
def article_link(title):
|
||||||
return url_for('lexicon.article', name=g.lexicon.name, title=filesafe_title(title))
|
return url_for(
|
||||||
|
'lexicon.article',
|
||||||
|
name=g.lexicon.name,
|
||||||
|
title=filesafe_title(title))
|
||||||
|
|
||||||
|
|
||||||
def lexicon_param(route):
|
# def lexicon_param(route):
|
||||||
"""Wrapper for loading a route's lexicon"""
|
# """Wrapper for loading a route's lexicon"""
|
||||||
@wraps(route)
|
# @wraps(route)
|
||||||
def with_lexicon(**kwargs):
|
# def with_lexicon(**kwargs):
|
||||||
name = kwargs.get('name')
|
# name = kwargs.get('name')
|
||||||
g.lexicon = LexiconModel.by(name=name)
|
# g.lexicon = LexiconModel.by(name=name)
|
||||||
if g.lexicon is None:
|
# if g.lexicon is None:
|
||||||
flash("Couldn't find a lexicon with the name '{}'".format(name))
|
# flash("Couldn't find a lexicon with the name '{}'".format(name))
|
||||||
return redirect(url_for("home.home"))
|
# return redirect(url_for("home.home"))
|
||||||
# TODO transition to new model
|
# # TODO transition to new model
|
||||||
model_factory: ModelFactory = current_app.config['model_factory']
|
# model_factory: ModelFactory = current_app.config['model_factory']
|
||||||
g.lexicon_ = model_factory.lexicon(name)
|
# g.lexicon_ = model_factory.lexicon(name)
|
||||||
return route(**kwargs)
|
# return route(**kwargs)
|
||||||
return with_lexicon
|
# return with_lexicon
|
||||||
|
|
||||||
|
|
||||||
def admin_required(route):
|
def admin_required(route):
|
||||||
|
@ -55,7 +58,7 @@ def admin_required(route):
|
||||||
"""
|
"""
|
||||||
@wraps(route)
|
@wraps(route)
|
||||||
def admin_route(*args, **kwargs):
|
def admin_route(*args, **kwargs):
|
||||||
if not current_user.is_admin:
|
if not current_user.cfg.is_admin:
|
||||||
flash("You must be an admin to view this page")
|
flash("You must be an admin to view this page")
|
||||||
return redirect(url_for('home.home'))
|
return redirect(url_for('home.home'))
|
||||||
return route(*args, **kwargs)
|
return route(*args, **kwargs)
|
||||||
|
@ -68,10 +71,10 @@ def player_required(route):
|
||||||
"""
|
"""
|
||||||
@wraps(route)
|
@wraps(route)
|
||||||
def player_route(*args, **kwargs):
|
def player_route(*args, **kwargs):
|
||||||
if not current_user.in_lexicon(g.lexicon):
|
if current_user.uid not in g.lexicon.cfg.join.joined:
|
||||||
flash("You must be a player to view this page")
|
flash("You must be a player to view this page")
|
||||||
return (redirect(url_for('lexicon.contents', name=g.lexicon.name))
|
return (redirect(url_for('lexicon.contents', name=g.lexicon.cfg.name))
|
||||||
if g.lexicon.join.public
|
if g.lexicon.cfg.join.public
|
||||||
else redirect(url_for('home.home')))
|
else redirect(url_for('home.home')))
|
||||||
return route(*args, **kwargs)
|
return route(*args, **kwargs)
|
||||||
return player_route
|
return player_route
|
||||||
|
@ -84,8 +87,8 @@ def player_required_if_not_public(route):
|
||||||
"""
|
"""
|
||||||
@wraps(route)
|
@wraps(route)
|
||||||
def player_route(*args, **kwargs):
|
def player_route(*args, **kwargs):
|
||||||
if ((not g.lexicon.join.public)
|
if ((not g.lexicon.cfg.join.public)
|
||||||
and not current_user.in_lexicon(g.lexicon)):
|
and current_user.uid not in g.lexicon.cfg.join.joined):
|
||||||
flash("You must be a player to view this page")
|
flash("You must be a player to view this page")
|
||||||
return redirect(url_for('home.home'))
|
return redirect(url_for('home.home'))
|
||||||
return route(*args, **kwargs)
|
return route(*args, **kwargs)
|
||||||
|
@ -99,7 +102,7 @@ def editor_required(route):
|
||||||
"""
|
"""
|
||||||
@wraps(route)
|
@wraps(route)
|
||||||
def editor_route(*args, **kwargs):
|
def editor_route(*args, **kwargs):
|
||||||
if current_user.id != g.lexicon.editor:
|
if current_user.uid != g.lexicon.cfg.editor:
|
||||||
flash("You must be the editor to view this page")
|
flash("You must be the editor to view this page")
|
||||||
return redirect(url_for('lexicon.contents', name=g.lexicon.name))
|
return redirect(url_for('lexicon.contents', name=g.lexicon.name))
|
||||||
return route(*args, **kwargs)
|
return route(*args, **kwargs)
|
||||||
|
|
|
@ -1,49 +1,45 @@
|
||||||
from flask import Blueprint, render_template, redirect, url_for
|
from flask import Blueprint, render_template, redirect, url_for, current_app
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
from amanuensis.config import json_ro, json_rw
|
from amanuensis.config import RootConfigDirectoryContext
|
||||||
from amanuensis.lexicon import LexiconModel
|
from amanuensis.lexicon import create_lexicon, load_all_lexicons
|
||||||
from amanuensis.lexicon.manage import create_lexicon, get_all_lexicons
|
from amanuensis.models import UserModel
|
||||||
from amanuensis.server.forms import LexiconCreateForm
|
from amanuensis.server.forms import LexiconCreateForm
|
||||||
from amanuensis.server.helpers import admin_required
|
from amanuensis.server.helpers import admin_required
|
||||||
from amanuensis.user import UserModel
|
from amanuensis.user import load_all_users
|
||||||
|
|
||||||
|
|
||||||
def get_bp():
|
bp_home = Blueprint('home', __name__, url_prefix='/home')
|
||||||
"""Create a blueprint for pages outside of a specific lexicon"""
|
|
||||||
bp = Blueprint('home', __name__, url_prefix='/home')
|
|
||||||
|
|
||||||
@bp.route('/', methods=['GET'])
|
|
||||||
|
@bp_home.route('/', methods=['GET'])
|
||||||
def home():
|
def home():
|
||||||
|
root: RootConfigDirectoryContext = current_app.config['root']
|
||||||
|
user: UserModel = current_user
|
||||||
user_lexicons = []
|
user_lexicons = []
|
||||||
public_lexicons = []
|
public_lexicons = []
|
||||||
for lexicon in get_all_lexicons():
|
for lexicon in load_all_lexicons(root):
|
||||||
if current_user.in_lexicon(lexicon):
|
if user.uid in lexicon.cfg.join.joined:
|
||||||
user_lexicons.append(lexicon)
|
user_lexicons.append(lexicon)
|
||||||
elif lexicon.join.public:
|
elif lexicon.cfg.join.public:
|
||||||
public_lexicons.append(lexicon)
|
public_lexicons.append(lexicon)
|
||||||
return render_template(
|
return render_template(
|
||||||
'home/home.html',
|
'home/home.html',
|
||||||
user_lexicons=user_lexicons,
|
user_lexicons=user_lexicons,
|
||||||
public_lexicons=public_lexicons)
|
public_lexicons=public_lexicons)
|
||||||
|
|
||||||
@bp.route('/admin/', methods=['GET'])
|
|
||||||
|
@bp_home.route('/admin/', methods=['GET'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin():
|
def admin():
|
||||||
users = []
|
root: RootConfigDirectoryContext = current_app.config['root']
|
||||||
with json_ro('user', 'index.json') as index:
|
users = list(load_all_users(root))
|
||||||
for name, uid in index.items():
|
lexicons = list(load_all_lexicons(root))
|
||||||
users.append(UserModel.by(uid=uid))
|
|
||||||
|
|
||||||
lexicons = []
|
|
||||||
with json_ro('lexicon', 'index.json') as index:
|
|
||||||
for name, lid in index.items():
|
|
||||||
lexicons.append(LexiconModel.by(lid=lid))
|
|
||||||
|
|
||||||
return render_template('home/admin.html', users=users, lexicons=lexicons)
|
return render_template('home/admin.html', users=users, lexicons=lexicons)
|
||||||
|
|
||||||
@bp.route("/admin/create/", methods=['GET', 'POST'])
|
|
||||||
|
@bp_home.route("/admin/create/", methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
def admin_create():
|
def admin_create():
|
||||||
|
@ -55,10 +51,8 @@ def get_bp():
|
||||||
prompt = form.promptText.data
|
prompt = form.promptText.data
|
||||||
editor = UserModel.by(name=editor_name)
|
editor = UserModel.by(name=editor_name)
|
||||||
lexicon = create_lexicon(lexicon_name, editor)
|
lexicon = create_lexicon(lexicon_name, editor)
|
||||||
with json_rw(lexicon.config_path) as cfg:
|
with lexicon.ctx.edit_config() as cfg:
|
||||||
cfg.prompt = prompt
|
cfg.prompt = prompt
|
||||||
return redirect(url_for('lexicon.session', name=lexicon_name))
|
return redirect(url_for('lexicon.session', name=lexicon_name))
|
||||||
|
|
||||||
return render_template('home/create.html', form=form)
|
return render_template('home/create.html', form=form)
|
||||||
|
|
||||||
return bp
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% set template_content_blocks = [self.main()] %}
|
{% set template_content_blocks = [self.main()] %}
|
||||||
|
|
||||||
{% if current_user.is_admin %}
|
{% if current_user.cfg.is_admin %}
|
||||||
{% block admin_dash %}
|
{% block admin_dash %}
|
||||||
<a href="{{ url_for('home.admin') }}" style="display:block; text-align:center;">Admin dashboard</a>
|
<a href="{{ url_for('home.admin') }}" style="display:block; text-align:center;">Admin dashboard</a>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
{% macro dashboard_lexicon_item(lexicon) %}
|
{% macro dashboard_lexicon_item(lexicon) %}
|
||||||
<div class="dashboard-lexicon-item dashboard-lexicon-{{ lexicon.status() }}">
|
<div class="dashboard-lexicon-item dashboard-lexicon-{{ lexicon.status }}">
|
||||||
<p>
|
<p>
|
||||||
<span class="dashboard-lexicon-item-title">
|
<span class="dashboard-lexicon-item-title">
|
||||||
<a href="{{ url_for('lexicon.contents', name=lexicon.name) }}">
|
{# <a href="{{ url_for('lexicon.contents', name=lexicon.cfg.name) }}"> #}
|
||||||
Lexicon {{ lexicon.name }}</a>
|
Lexicon {{ lexicon.cfg.name }}{# </a> #}
|
||||||
</span>
|
</span>
|
||||||
[{{ lexicon.status().capitalize() }}]
|
[{{ lexicon.status.capitalize() }}]
|
||||||
</p>
|
</p>
|
||||||
<p><i>{{ lexicon.prompt }}</i></p>
|
<p><i>{{ lexicon.cfg.prompt }}</i></p>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<p>
|
<p>
|
||||||
{%
|
{%
|
||||||
if current_user.in_lexicon(lexicon)
|
if current_user.uid in lexicon.cfg.join.joined
|
||||||
or current_user.is_admin
|
or current_user.cfg.is_admin
|
||||||
%}
|
%}
|
||||||
Editor: {{ lexicon.editor|user_attr('username') }} /
|
Editor: {{ lexicon.cfg.editor|user_attr('username') }} /
|
||||||
Players:
|
Players:
|
||||||
{% for uid in lexicon.join.joined %}
|
{% for uid in lexicon.cfg.join.joined %}
|
||||||
{{ uid|user_attr('username') }}{% if not loop.last %}, {% endif %}
|
{{ uid|user_attr('username') }}{% if not loop.last %}, {% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
({{ lexicon.join.joined|count }}/{{ lexicon.join.max_players }})
|
({{ lexicon.cfg.join.joined|count }}/{{ lexicon.cfg.join.max_players }})
|
||||||
{% else %}
|
{% else %}
|
||||||
Players: {{ lexicon.join.joined|count }}/{{ lexicon.join.max_players }}
|
Players: {{ lexicon.cfg.join.joined|count }}/{{ lexicon.cfg.join.max_players }}
|
||||||
{% if lexicon.join.public and lexicon.join.open %}
|
{% if lexicon.cfg.join.public and lexicon.cfg.join.open %}
|
||||||
/ <a href="{{ url_for('lexicon.join', name=lexicon.name) }}">
|
{# / <a href="{{ url_for('lexicon.join', name=lexicon.cfg.name) }}"> #}
|
||||||
Join game
|
Join game
|
||||||
</a>
|
{# </a> #}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
@ -36,10 +36,10 @@
|
||||||
{% macro dashboard_user_item(user) %}
|
{% macro dashboard_user_item(user) %}
|
||||||
<div class="dashboard-lexicon-item">
|
<div class="dashboard-lexicon-item">
|
||||||
<p>
|
<p>
|
||||||
<b>{{ user.username }}</b>
|
<b>{{ user.cfg.username }}</b>
|
||||||
{% if user.username != user.displayname %} / {{ user.displayname }}{% endif %}
|
{% if user.cfg.username != user.cfg.displayname %} / {{ user.cfg.displayname }}{% endif %}
|
||||||
({{user.uid}})
|
({{user.uid}})
|
||||||
</p>
|
</p>
|
||||||
<p>Last activity: {{ user.last_activity|asdate }} — Last login: {{ user.last_login|asdate }}</p>
|
<p>Last activity: {{ user.cfg.last_activity|asdate }} — Last login: {{ user.cfg.last_login|asdate }}</p>
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
|
@ -11,7 +11,7 @@
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div id="login-status" {% block login_status_attr %}{% endblock %}>
|
<div id="login-status" {% block login_status_attr %}{% endblock %}>
|
||||||
{% if current_user.is_authenticated %}
|
{% if current_user.is_authenticated %}
|
||||||
<b>{{ current_user.username -}}</b>
|
<b>{{ current_user.cfg.username -}}</b>
|
||||||
(<a href="{{ url_for('auth.logout') }}">Logout</a>)
|
(<a href="{{ url_for('auth.logout') }}">Logout</a>)
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ url_for('auth.login') }}">Login</a>
|
<a href="{{ url_for('auth.login') }}">Login</a>
|
||||||
|
|
Loading…
Reference in New Issue