From 4fc5f8053833edae40cb13db8ebabbd6f6d52a91 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Fri, 24 Apr 2020 23:07:17 -0700 Subject: [PATCH] Refactor lexicon submodule into two --- amanuensis/server/forms.py | 8 +- amanuensis/server/helpers.py | 26 +- amanuensis/server/home/__init__.py | 2 +- .../lexicon => server}/lexicon.jinja | 0 amanuensis/server/lexicon.py | 335 ------------------ amanuensis/server/lexicon/__init__.py | 87 +++++ .../lexicon/lexicon.article.jinja} | 2 +- .../lexicon/lexicon.contents.jinja} | 2 +- .../lexicon/lexicon.join.jinja} | 2 +- .../lexicon/lexicon.rules.jinja} | 2 +- .../lexicon/lexicon.statistics.jinja} | 2 +- amanuensis/server/session/__init__.py | 303 ++++++++++++++++ .../session/session.character.jinja} | 2 +- .../session/session.editor.jinja} | 0 .../session/session.review.jinja} | 2 +- .../session/session.session.jinja} | 2 +- .../session/session.settings.jinja} | 2 +- 17 files changed, 416 insertions(+), 363 deletions(-) rename amanuensis/{templates/lexicon => server}/lexicon.jinja (100%) delete mode 100644 amanuensis/server/lexicon.py create mode 100644 amanuensis/server/lexicon/__init__.py rename amanuensis/{templates/lexicon/article.jinja => server/lexicon/lexicon.article.jinja} (94%) rename amanuensis/{templates/lexicon/contents.jinja => server/lexicon/lexicon.contents.jinja} (91%) rename amanuensis/{templates/lexicon/join.jinja => server/lexicon/lexicon.join.jinja} (92%) rename amanuensis/{templates/lexicon/rules.jinja => server/lexicon/lexicon.rules.jinja} (83%) rename amanuensis/{templates/lexicon/statistics.jinja => server/lexicon/lexicon.statistics.jinja} (84%) create mode 100644 amanuensis/server/session/__init__.py rename amanuensis/{templates/lexicon/character.jinja => server/session/session.character.jinja} (93%) rename amanuensis/{templates/lexicon/editor.jinja => server/session/session.editor.jinja} (100%) rename amanuensis/{templates/lexicon/review.jinja => server/session/session.review.jinja} (93%) rename amanuensis/{templates/lexicon/session.jinja => server/session/session.session.jinja} (98%) rename amanuensis/{templates/lexicon/settings.jinja => server/session/session.settings.jinja} (99%) diff --git a/amanuensis/server/forms.py b/amanuensis/server/forms.py index 5e8386e..1ea8fac 100644 --- a/amanuensis/server/forms.py +++ b/amanuensis/server/forms.py @@ -216,10 +216,10 @@ class LexiconCreateForm(FlaskForm): # return True -# class LexiconJoinForm(FlaskForm): -# """/lexicon//join/""" -# password = StringField('Password') -# submit = SubmitField("Submit") +class LexiconJoinForm(FlaskForm): + """/lexicon//join/""" + password = StringField('Password') + submit = SubmitField("Submit") # class LexiconCharacterForm(FlaskForm): diff --git a/amanuensis/server/helpers.py b/amanuensis/server/helpers.py index eb8b03e..77cfada 100644 --- a/amanuensis/server/helpers.py +++ b/amanuensis/server/helpers.py @@ -36,20 +36,18 @@ def register_custom_filters(app): title=filesafe_title(title)) -# def lexicon_param(route): -# """Wrapper for loading a route's lexicon""" -# @wraps(route) -# def with_lexicon(**kwargs): -# name = kwargs.get('name') -# g.lexicon = LexiconModel.by(name=name) -# if g.lexicon is None: -# flash("Couldn't find a lexicon with the name '{}'".format(name)) -# return redirect(url_for("home.home")) -# # TODO transition to new model -# model_factory: ModelFactory = current_app.config['model_factory'] -# g.lexicon_ = model_factory.lexicon(name) -# return route(**kwargs) -# return with_lexicon +def lexicon_param(route): + """Wrapper for loading a route's lexicon""" + @wraps(route) + def with_lexicon(**kwargs): + name = kwargs.get('name') + model_factory: ModelFactory = current_app.config['model_factory'] + g.lexicon = model_factory.lexicon(name) + if g.lexicon is None: + flash(f'Couldn\'t find a lexicon with the name "{name}"') + return redirect(url_for("home.home")) + return route(**kwargs) + return with_lexicon def admin_required(route): diff --git a/amanuensis/server/home/__init__.py b/amanuensis/server/home/__init__.py index ebe6e7d..64fa4c1 100644 --- a/amanuensis/server/home/__init__.py +++ b/amanuensis/server/home/__init__.py @@ -55,6 +55,6 @@ def admin_create(): lexicon = create_lexicon(lexicon_name, editor) with lexicon.ctx.edit_config() as cfg: cfg.prompt = prompt - return redirect(url_for('lexicon.session', name=lexicon_name)) + return redirect(url_for('session.session', name=lexicon_name)) return render_template('home.create.jinja', form=form) diff --git a/amanuensis/templates/lexicon/lexicon.jinja b/amanuensis/server/lexicon.jinja similarity index 100% rename from amanuensis/templates/lexicon/lexicon.jinja rename to amanuensis/server/lexicon.jinja diff --git a/amanuensis/server/lexicon.py b/amanuensis/server/lexicon.py deleted file mode 100644 index 3e7ee98..0000000 --- a/amanuensis/server/lexicon.py +++ /dev/null @@ -1,335 +0,0 @@ -import json -import uuid - -from flask import ( - Blueprint, render_template, url_for, redirect, g, flash, request, Markup) -from flask_login import login_required, current_user - -from amanuensis.config import root -from amanuensis.config.loader import ReadOnlyOrderedDict -from amanuensis.errors import MissingConfigError -from amanuensis.lexicon.manage import valid_add, add_player, add_character, attempt_publish -from amanuensis.parser import parse_raw_markdown, PreviewHtmlRenderer, FeatureCounter, filesafe_title -from amanuensis.server.forms import ( - LexiconConfigForm, LexiconJoinForm,LexiconCharacterForm, LexiconReviewForm) -from amanuensis.server.helpers import ( - lexicon_param, player_required, editor_required, - player_required_if_not_public) - - -def jsonfmt(obj): - return Markup(json.dumps(obj)) - - -def get_bp(): - """Create a blueprint for lexicon pages""" - bp = Blueprint('lexicon', __name__, url_prefix='/lexicon/') - - @bp.route("/join/", methods=['GET', 'POST']) - @lexicon_param - @login_required - def join(name): - if not g.lexicon.join.open: - flash("This game isn't open for joining") - return redirect(url_for('home.home')) - - form = LexiconJoinForm() - - if form.validate_on_submit(): - # Gate on password if one is required - if (g.lexicon.join.password - and form.password.data != g.lexicon.join.password): - return redirect(url_for("lexicon.join", name=name)) - # Gate on join validity - if valid_add(g.lexicon, current_user, form.password.data): - add_player(g.lexicon, current_user) - return redirect(url_for("lexicon.session", name=name)) - else: - flash("Could not join game") - return redirect(url_for("home.home", name=name)) - - return render_template('lexicon/join.jinja', form=form) - - @bp.route('/contents/', methods=['GET']) - @lexicon_param - @player_required_if_not_public - def contents(name): - articles = [] - filenames = g.lexicon_.ctx.article.ls() - for filename in filenames: - with g.lexicon_.ctx.article.read(filename) as a: - articles.append({ - 'title': a.title, - 'link': url_for('lexicon.article', name=name, title=filesafe_title(a.title)), - }) - return render_template('lexicon/contents.jinja', articles=articles) - - @bp.route('/article/') - @lexicon_param - @player_required_if_not_public - def article(name, title): - with g.lexicon_.ctx.article.read(title) as a: - article = { **a, 'html': Markup(a['html']) } - return render_template('lexicon/article.jinja', article=article) - - @bp.route('/rules/', methods=['GET']) - @lexicon_param - @player_required_if_not_public - def rules(name): - return render_template('lexicon/rules.jinja') - - @bp.route('/session/', methods=['GET']) - @lexicon_param - @player_required - def session(name): - drafts = [] - approved = [] - draft_ctx = g.lexicon.ctx.draft - draft_filenames = draft_ctx.ls() - for draft_filename in draft_filenames: - with draft_ctx.read(draft_filename) as draft: - if draft.status.ready and not draft.status.approved: - drafts.append(draft) - if draft.status.approved: - approved.append(draft) - return render_template( - 'lexicon/session.jinja', - ready_articles=drafts, - approved_articles=approved) - - def edit_character(name, form, cid): - if form.validate_on_submit(): - # Update character - form.update_character(g.lexicon, cid) - flash('Character updated') - return redirect(url_for('lexicon.session', name=name)) - - if not form.is_submitted(): - # On GET, populate with the character - form.for_character(g.lexicon, cid) - return render_template('lexicon/character.jinja', form=form, action='edit') - - def create_character(name, form): - if form.validate_on_submit(): - # On POST, verify character can be added - if not g.lexicon.can_add_character(current_user.id): - flash('Operation not permitted') - return redirect(url_for('lexicon.session', name=name)) - # Add the character - form.add_character(g.lexicon, current_user) - flash('Character created') - return redirect(url_for('lexicon.session', name=name)) - - if not form.is_submitted(): - # On GET, populate form for new character - form.for_new() - return render_template('lexicon/character.jinja', form=form, action='create') - - @bp.route('/session/character/', methods=['GET', 'POST']) - @lexicon_param - @player_required - def character(name): - form = LexiconCharacterForm() - cid = request.args.get('cid') - if cid: - if cid not in g.lexicon.character: - flash('Character not found') - return redirect(url_for('lexicon.session', name=name)) - if (g.lexicon.character.get(cid).player != current_user.id - and g.lexicon.editor != current_user.id): - flash('Access denied') - return redirect(url_for('lexicon.session', name=name)) - return edit_character(name, form, cid) - return create_character(name, form) - - @bp.route('/session/settings/', methods=['GET', 'POST']) - @lexicon_param - @editor_required - def settings(name): - form = LexiconConfigForm() - form.set_options(g.lexicon) - - # Load the config for the lexicon on load - if not form.is_submitted(): - form.populate_from_lexicon(g.lexicon) - return render_template("lexicon/settings.jinja", form=form) - - if form.validate(): - if not form.update_lexicon(g.lexicon): - flash("Error updating settings") - return render_template("lexicon/settings.jinja", form=form) - flash("Settings updated") - return redirect(url_for('lexicon.session', name=name)) - - flash("Validation error") - return render_template("lexicon/settings.jinja", form=form) - - @bp.route('/session/review/', methods=['GET', 'POST']) - @lexicon_param - @editor_required - def review(name): - aid = request.args.get('aid') - if not aid: - flash("Unknown article id") - return redirect(url_for('lexicon.session', name=name)) - - draft_ctx = g.lexicon.ctx.draft - draft_filename = [fn for fn in draft_ctx.ls() if aid in fn][0] - with draft_ctx.edit(draft_filename) as draft: - # If the article was unreadied in the meantime, abort - if not draft.status.ready: - flash("Article was rescinded") - return redirect(url_for('lexicon.session', name=name)) - - parsed_draft = parse_raw_markdown(draft.contents) - rendered_html = parsed_draft.render(PreviewHtmlRenderer(g.lexicon)) - - # If the article is ready and awaiting review - if not draft.status.approved: - form = LexiconReviewForm() - if form.validate_on_submit(): - if form.approved.data == 'Y': - draft.status.ready = True - draft.status.approved = True - g.lexicon.add_log(f"Article '{draft.title}' approved ({draft.aid})") - if g.lexicon.publish.asap: - attempt_publish(g.lexicon) - else: - draft.status.ready = False - draft.status.approved = False - g.lexicon.add_log(f"Article '{draft.title}' rejected ({draft.aid})") - return redirect(url_for('lexicon.session', name=name)) - - # If the article was already reviewed and this is just the preview - else: - form = None - - return render_template( - "lexicon/review.jinja", - form=form, - article_html=Markup(rendered_html)) - - @bp.route('/statistics/', methods=['GET']) - @lexicon_param - @player_required_if_not_public - def stats(name): - return render_template('lexicon/statistics.jinja') - - @bp.route('/session/editor/', methods=['GET']) - @lexicon_param - @player_required - def editor(name): - """ - cases: - - neither cid nor aid: load all chars and articles - - cid: list articles just for cid - - aid: - """ - cid = request.args.get('cid') - if not cid: - # Character not specified, load all characters and articles - # and return render_template - characters = [ - char for char in g.lexicon.character.values() - if char.player == current_user.id - ] - articles = [ - article for article in g.lexicon.get_drafts_for_player(uid=current_user.id) - if any([article.character == char.cid for char in characters]) - ] - return render_template( - 'lexicon/editor.jinja', - characters=characters, - articles=articles, - jsonfmt=jsonfmt) - - character = g.lexicon.character.get(cid) - if not character: - # Character was specified, but id was invalid - flash("Character not found") - return redirect(url_for('lexicon.session', name=name)) - if character.player != current_user.id: - # Player doesn't control this character - flash("Access forbidden") - return redirect(url_for('lexicon.session', name=name)) - - aid = request.args.get('aid') - if not aid: - # Character specified but not article, load character articles - # and retuen r_t - articles = [ - article for article in g.lexicon.get_drafts_for_player(uid=current_user.id) - if article.character == character.cid - ] - return render_template( - 'lexicon/editor.jinja', - character=character, - articles=articles, - jsonfmt=jsonfmt) - - filename = f'{cid}.{aid}' - try: - with g.lexicon.ctx.draft.read(filename) as a: - article = a - except MissingConfigError: - flash("Draft not found") - return redirect(url_for('lexicon.session', name=name)) - - return render_template( - 'lexicon/editor.jinja', - character=character, - article=article, - jsonfmt=jsonfmt) - - @bp.route('/session/editor/new', methods=['GET']) - @lexicon_param - @player_required - def editor_new(name): - new_aid = uuid.uuid4().hex - # TODO harden this - cid = request.args.get("cid") - character = g.lexicon.character.get(cid) - article = { - "version": "0", - "aid": new_aid, - "lexicon": g.lexicon.id, - "character": cid, - "title": "", - "turn": 1, - "status": { - "ready": False, - "approved": False - }, - "contents": f"\n\n{character.signature}", - } - filename = f"{cid}.{new_aid}" - with g.lexicon.ctx.draft.new(filename) as j: - j.update(article) - return redirect(url_for('lexicon.editor', name=name, cid=cid, aid=new_aid)) - - @bp.route('/session/editor/update', methods=['POST']) - @lexicon_param - @player_required - def editor_update(name): - article = request.json['article'] - # TODO verification - # check if article was previously approved - # check extrinsic constraints for blocking errors - parsed_draft = parse_raw_markdown(article['contents']) - rendered_html = parsed_draft.render(PreviewHtmlRenderer(g.lexicon)) - features = parsed_draft.render(FeatureCounter()) - - filename = f'{article["character"]}.{article["aid"]}' - with g.lexicon.ctx.draft.edit(filename) as a: - a.update(article) - - # TODO return more info - return { - 'article': article, - 'info': { - 'rendered': rendered_html, - 'word_count': features.word_count, - } - } - - return bp diff --git a/amanuensis/server/lexicon/__init__.py b/amanuensis/server/lexicon/__init__.py new file mode 100644 index 0000000..0c7d40b --- /dev/null +++ b/amanuensis/server/lexicon/__init__.py @@ -0,0 +1,87 @@ +from flask import ( + Blueprint, + flash, + redirect, + url_for, + g, + render_template, + Markup) +from flask_login import login_required, current_user + +from amanuensis.lexicon import player_can_join_lexicon, add_player_to_lexicon +from amanuensis.parser import filesafe_title +from amanuensis.server.forms import LexiconJoinForm +from amanuensis.server.helpers import ( + lexicon_param, + player_required_if_not_public) + + +bp_lexicon = Blueprint('lexicon', __name__, + url_prefix='/lexicon/<name>', + template_folder='.') + + +@bp_lexicon.route("/join/", methods=['GET', 'POST']) +@lexicon_param +@login_required +def join(name): + if not g.lexicon.cfg.join.open: + flash("This game isn't open for joining") + return redirect(url_for('home.home')) + + form = LexiconJoinForm() + + if form.validate_on_submit(): + # Gate on password if one is required + if (g.lexicon.cfg.join.password + and form.password.data != g.lexicon.cfg.join.password): + return redirect(url_for("lexicon.join", name=name)) + # Gate on join validity + if player_can_join_lexicon(current_user, g.lexicon, form.password.data): + add_player_to_lexicon(current_user, g.lexicon) + return redirect(url_for("lexicon.contents", name=name)) # SESSION + else: + flash("Could not join game") + return redirect(url_for("home.home", name=name)) + + return render_template('lexicon.join.jinja', form=form) + + +@bp_lexicon.route('/contents/', methods=['GET']) +@lexicon_param +@player_required_if_not_public +def contents(name): + articles = [] + filenames = g.lexicon.ctx.article.ls() + for filename in filenames: + with g.lexicon.ctx.article.read(filename) as a: + articles.append({ + 'title': a.title, + 'link': url_for('lexicon.article', + name=name, + title=filesafe_title(a.title)), + }) + return render_template('lexicon.contents.jinja', articles=articles) + + +@bp_lexicon.route('/article/<title>') +@lexicon_param +@player_required_if_not_public +def article(name, title): + with g.lexicon.ctx.article.read(title) as a: + article = {**a, 'html': Markup(a['html'])} + return render_template('lexicon.article.jinja', article=article) + + +@bp_lexicon.route('/rules/', methods=['GET']) +@lexicon_param +@player_required_if_not_public +def rules(name): + return render_template('lexicon.rules.jinja') + + +@bp_lexicon.route('/statistics/', methods=['GET']) +@lexicon_param +@player_required_if_not_public +def stats(name): + return render_template('lexicon.statistics.jinja') diff --git a/amanuensis/templates/lexicon/article.jinja b/amanuensis/server/lexicon/lexicon.article.jinja similarity index 94% rename from amanuensis/templates/lexicon/article.jinja rename to amanuensis/server/lexicon/lexicon.article.jinja index 7e4710e..95bdbfb 100644 --- a/amanuensis/templates/lexicon/article.jinja +++ b/amanuensis/server/lexicon/lexicon.article.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% block title %}{{ article.title }} | {{ lexicon_title }}{% endblock %} {% block main %} diff --git a/amanuensis/templates/lexicon/contents.jinja b/amanuensis/server/lexicon/lexicon.contents.jinja similarity index 91% rename from amanuensis/templates/lexicon/contents.jinja rename to amanuensis/server/lexicon/lexicon.contents.jinja index 2b7c2f3..dec7b33 100644 --- a/amanuensis/templates/lexicon/contents.jinja +++ b/amanuensis/server/lexicon/lexicon.contents.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% set current_page = "contents" %} {% block title %}Index | {{ lexicon_title }}{% endblock %} diff --git a/amanuensis/templates/lexicon/join.jinja b/amanuensis/server/lexicon/lexicon.join.jinja similarity index 92% rename from amanuensis/templates/lexicon/join.jinja rename to amanuensis/server/lexicon/lexicon.join.jinja index 1a26cfb..5f22f08 100644 --- a/amanuensis/templates/lexicon/join.jinja +++ b/amanuensis/server/lexicon/lexicon.join.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% block title %}Join | {{ lexicon_title }}{% endblock %} {% block main %} diff --git a/amanuensis/templates/lexicon/rules.jinja b/amanuensis/server/lexicon/lexicon.rules.jinja similarity index 83% rename from amanuensis/templates/lexicon/rules.jinja rename to amanuensis/server/lexicon/lexicon.rules.jinja index dca2491..20fc070 100644 --- a/amanuensis/templates/lexicon/rules.jinja +++ b/amanuensis/server/lexicon/lexicon.rules.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% set current_page = "rules" %} {% block title %}Rules | {{ lexicon_title }}{% endblock %} diff --git a/amanuensis/templates/lexicon/statistics.jinja b/amanuensis/server/lexicon/lexicon.statistics.jinja similarity index 84% rename from amanuensis/templates/lexicon/statistics.jinja rename to amanuensis/server/lexicon/lexicon.statistics.jinja index 33ad417..e816653 100644 --- a/amanuensis/templates/lexicon/statistics.jinja +++ b/amanuensis/server/lexicon/lexicon.statistics.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% set current_page = "statistics" %} {% block title %}Session | {{ lexicon_title }}{% endblock %} diff --git a/amanuensis/server/session/__init__.py b/amanuensis/server/session/__init__.py new file mode 100644 index 0000000..8adec1b --- /dev/null +++ b/amanuensis/server/session/__init__.py @@ -0,0 +1,303 @@ +import json +import uuid + +from flask import ( + Blueprint, + render_template, + url_for, + redirect, + g, + flash, + request, + Markup) +from flask_login import login_required, current_user + +from amanuensis.config import root +from amanuensis.config.loader import ReadOnlyOrderedDict +from amanuensis.errors import MissingConfigError +from amanuensis.lexicon.manage import ( + valid_add, + add_player, + add_character, + attempt_publish) +from amanuensis.parser import ( + parse_raw_markdown, + PreviewHtmlRenderer, + FeatureCounter, + filesafe_title) +from amanuensis.server.forms import ( + LexiconConfigForm, + LexiconJoinForm, + LexiconCharacterForm, + LexiconReviewForm) +from amanuensis.server.helpers import ( + lexicon_param, + player_required, + editor_required, + player_required_if_not_public) + + +def jsonfmt(obj): + return Markup(json.dumps(obj)) + + +bp_session = Blueprint('lexicon', __name__, + url_prefix='/lexicon/<name>/session', + template_folder='.') + + +@bp_session.route('/', methods=['GET']) +@lexicon_param +@player_required +def session(name): + drafts = [] + approved = [] + draft_ctx = g.lexicon.ctx.draft + draft_filenames = draft_ctx.ls() + for draft_filename in draft_filenames: + with draft_ctx.read(draft_filename) as draft: + if draft.status.ready and not draft.status.approved: + drafts.append(draft) + if draft.status.approved: + approved.append(draft) + return render_template( + 'session.session.jinja', + ready_articles=drafts, + approved_articles=approved) + + +def edit_character(name, form, cid): + if form.validate_on_submit(): + # Update character + form.update_character(g.lexicon, cid) + flash('Character updated') + return redirect(url_for('session.session', name=name)) + + if not form.is_submitted(): + # On GET, populate with the character + form.for_character(g.lexicon, cid) + return render_template('session.character.jinja', form=form, action='edit') + + +def create_character(name, form): + if form.validate_on_submit(): + # On POST, verify character can be added + if not g.lexicon.can_add_character(current_user.id): + flash('Operation not permitted') + return redirect(url_for('session.session', name=name)) + # Add the character + form.add_character(g.lexicon, current_user) + flash('Character created') + return redirect(url_for('session.session', name=name)) + + if not form.is_submitted(): + # On GET, populate form for new character + form.for_new() + return render_template('session.character.jinja', form=form, action='create') + + +@bp_session.route('/character/', methods=['GET', 'POST']) +@lexicon_param +@player_required +def character(name): + form = LexiconCharacterForm() + cid = request.args.get('cid') + if cid: + if cid not in g.lexicon.character: + flash('Character not found') + return redirect(url_for('session.session', name=name)) + if (g.lexicon.character.get(cid).player != current_user.id + and g.lexicon.editor != current_user.id): + flash('Access denied') + return redirect(url_for('session.session', name=name)) + return edit_character(name, form, cid) + return create_character(name, form) + + +@bp_session.route('/settings/', methods=['GET', 'POST']) +@lexicon_param +@editor_required +def settings(name): + form = LexiconConfigForm() + form.set_options(g.lexicon) + + # Load the config for the lexicon on load + if not form.is_submitted(): + form.populate_from_lexicon(g.lexicon) + return render_template('session.settings.jinja', form=form) + + if form.validate(): + if not form.update_lexicon(g.lexicon): + flash("Error updating settings") + 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) + + +@bp_session.route('/review/', methods=['GET', 'POST']) +@lexicon_param +@editor_required +def review(name): + aid = request.args.get('aid') + if not aid: + flash("Unknown article id") + return redirect(url_for('session.session', name=name)) + + draft_ctx = g.lexicon.ctx.draft + draft_filename = [fn for fn in draft_ctx.ls() if aid in fn][0] + with draft_ctx.edit(draft_filename) as draft: + # If the article was unreadied in the meantime, abort + if not draft.status.ready: + flash("Article was rescinded") + return redirect(url_for('session.session', name=name)) + + parsed_draft = parse_raw_markdown(draft.contents) + rendered_html = parsed_draft.render(PreviewHtmlRenderer(g.lexicon)) + + # If the article is ready and awaiting review + if not draft.status.approved: + form = LexiconReviewForm() + if form.validate_on_submit(): + if form.approved.data == 'Y': + draft.status.ready = True + draft.status.approved = True + g.lexicon.add_log(f"Article '{draft.title}' approved ({draft.aid})") + if g.lexicon.publish.asap: + attempt_publish(g.lexicon) + else: + draft.status.ready = False + draft.status.approved = False + g.lexicon.add_log(f"Article '{draft.title}' rejected ({draft.aid})") + return redirect(url_for('session.session', name=name)) + + # If the article was already reviewed and this is just the preview + else: + form = None + + return render_template( + "session.review.jinja", + form=form, + article_html=Markup(rendered_html)) + + +@bp_session.route('/editor/', methods=['GET']) +@lexicon_param +@player_required +def editor(name): + """ + cases: + - neither cid nor aid: load all chars and articles + - cid: list articles just for cid + - aid: + """ + cid = request.args.get('cid') + if not cid: + # Character not specified, load all characters and articles + # and return render_template + characters = [ + char for char in g.lexicon.character.values() + if char.player == current_user.id + ] + articles = [ + article for article in g.lexicon.get_drafts_for_player(uid=current_user.id) + if any([article.character == char.cid for char in characters]) + ] + return render_template( + 'session.editor.jinja', + characters=characters, + articles=articles, + jsonfmt=jsonfmt) + + character = g.lexicon.character.get(cid) + if not character: + # Character was specified, but id was invalid + flash("Character not found") + return redirect(url_for('session.session', name=name)) + if character.player != current_user.id: + # Player doesn't control this character + flash("Access forbidden") + return redirect(url_for('session.session', name=name)) + + aid = request.args.get('aid') + if not aid: + # Character specified but not article, load character articles + # and retuen r_t + articles = [ + article for article in g.lexicon.get_drafts_for_player(uid=current_user.id) + if article.character == character.cid + ] + return render_template( + 'session.editor.jinja', + character=character, + articles=articles, + jsonfmt=jsonfmt) + + filename = f'{cid}.{aid}' + try: + with g.lexicon.ctx.draft.read(filename) as a: + article = a + except MissingConfigError: + flash("Draft not found") + return redirect(url_for('session.session', name=name)) + + return render_template( + 'session.editor.jinja', + character=character, + article=article, + jsonfmt=jsonfmt) + + +@bp_session.route('/editor/new', methods=['GET']) +@lexicon_param +@player_required +def editor_new(name): + new_aid = uuid.uuid4().hex + # TODO harden this + cid = request.args.get("cid") + character = g.lexicon.character.get(cid) + article = { + "version": "0", + "aid": new_aid, + "lexicon": g.lexicon.id, + "character": cid, + "title": "", + "turn": 1, + "status": { + "ready": False, + "approved": False + }, + "contents": f"\n\n{character.signature}", + } + filename = f"{cid}.{new_aid}" + with g.lexicon.ctx.draft.new(filename) as j: + j.update(article) + return redirect(url_for('session.editor', name=name, cid=cid, aid=new_aid)) + + +@bp_session.route('/editor/update', methods=['POST']) +@lexicon_param +@player_required +def editor_update(name): + article = request.json['article'] + # TODO verification + # check if article was previously approved + # check extrinsic constraints for blocking errors + parsed_draft = parse_raw_markdown(article['contents']) + rendered_html = parsed_draft.render(PreviewHtmlRenderer(g.lexicon)) + features = parsed_draft.render(FeatureCounter()) + + filename = f'{article["character"]}.{article["aid"]}' + with g.lexicon.ctx.draft.edit(filename) as a: + a.update(article) + + # TODO return more info + return { + 'article': article, + 'info': { + 'rendered': rendered_html, + 'word_count': features.word_count, + } + } diff --git a/amanuensis/templates/lexicon/character.jinja b/amanuensis/server/session/session.character.jinja similarity index 93% rename from amanuensis/templates/lexicon/character.jinja rename to amanuensis/server/session/session.character.jinja index 0a8ea14..f96946d 100644 --- a/amanuensis/templates/lexicon/character.jinja +++ b/amanuensis/server/session/session.character.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% block title %}Character | {{ lexicon_title }}{% endblock %} {% block main %} diff --git a/amanuensis/templates/lexicon/editor.jinja b/amanuensis/server/session/session.editor.jinja similarity index 100% rename from amanuensis/templates/lexicon/editor.jinja rename to amanuensis/server/session/session.editor.jinja diff --git a/amanuensis/templates/lexicon/review.jinja b/amanuensis/server/session/session.review.jinja similarity index 93% rename from amanuensis/templates/lexicon/review.jinja rename to amanuensis/server/session/session.review.jinja index 3ee3164..def22cf 100644 --- a/amanuensis/templates/lexicon/review.jinja +++ b/amanuensis/server/session/session.review.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% block title %}Review | {{ lexicon_title }}{% endblock %} {% block article %} diff --git a/amanuensis/templates/lexicon/session.jinja b/amanuensis/server/session/session.session.jinja similarity index 98% rename from amanuensis/templates/lexicon/session.jinja rename to amanuensis/server/session/session.session.jinja index c7a4586..f3998e5 100644 --- a/amanuensis/templates/lexicon/session.jinja +++ b/amanuensis/server/session/session.session.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% set current_page = "session" %} {% block title %}Session | {{ lexicon_title }}{% endblock %} diff --git a/amanuensis/templates/lexicon/settings.jinja b/amanuensis/server/session/session.settings.jinja similarity index 99% rename from amanuensis/templates/lexicon/settings.jinja rename to amanuensis/server/session/session.settings.jinja index 113ad46..94bc6db 100644 --- a/amanuensis/templates/lexicon/settings.jinja +++ b/amanuensis/server/session/session.settings.jinja @@ -1,4 +1,4 @@ -{% extends "lexicon/lexicon.jinja" %} +{% extends "lexicon.jinja" %} {% block title %}Edit | {{ lexicon_title }}{% endblock %} {% block info %}