Get the article editor working
This commit is contained in:
parent
33459925ce
commit
241ec45430
|
@ -1,76 +1,75 @@
|
||||||
|
|
||||||
html, body {
|
html, body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
div#wrapper {
|
div#wrapper {
|
||||||
max-width: 1128px;
|
height: 100%;
|
||||||
height: 100%;
|
display: flex;
|
||||||
display:flex;
|
flex-direction: row;
|
||||||
flex-direction: row;
|
align-items: stretch;
|
||||||
align-items: stretch;
|
|
||||||
}
|
}
|
||||||
div.column {
|
div.column {
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
div#editor-left {
|
div#editor-left {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
div#editor-left section {
|
div#editor-left section {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 10px 5px 10px 10px;
|
margin: 10px 5px 10px 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
div#editor-left div#editor-header {
|
div#editor-left div#editor-header {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
div#editor-left div#editor-charselect {
|
div#editor-left div#editor-charselect {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
div#editor-left div#editor-buttons {
|
div#editor-left div#editor-buttons {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
div#editor-left input#editor-title {
|
div#editor-left input#editor-title {
|
||||||
font-size: 2em;
|
font-size: 2em;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
textarea#editor-content {
|
textarea#editor-content {
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
resize: none;
|
resize: none;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
width: initial;
|
width: initial;
|
||||||
}
|
}
|
||||||
div#editor-right {
|
div#editor-right {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
div#editor-right section {
|
div#editor-right section {
|
||||||
margin: 10px 5px 10px 10px;
|
margin: 10px 5px 10px 10px;
|
||||||
}
|
}
|
||||||
span.message-warning {
|
span.message-warning {
|
||||||
color: #808000;
|
color: #808000;
|
||||||
}
|
}
|
||||||
span.message-error {
|
span.message-error {
|
||||||
color: #ff0000;
|
color: #ff0000;
|
||||||
}
|
}
|
||||||
@media only screen and (max-width: 816px) {
|
@media only screen and (max-width: 816px) {
|
||||||
div#wrapper {
|
div#wrapper {
|
||||||
max-width: 564px;
|
max-width: 564px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: inherit;
|
padding: inherit;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
div.column {
|
div.column {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
div#editor-left {
|
div#editor-left {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
div#editor-right {
|
div#editor-right {
|
||||||
overflow-y: inherit;
|
overflow-y: inherit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,126 +1,152 @@
|
||||||
// Reduce unnecessary requests by checking for no further changes being made
|
(function(){
|
||||||
// before updating in response to a change.
|
/** Article submission state. */
|
||||||
var nonce = 0;
|
const ArticleState = {
|
||||||
|
DRAFT: 0,
|
||||||
|
SUBMITTED: 1,
|
||||||
|
APPROVED: 2
|
||||||
|
};
|
||||||
|
|
||||||
function ifNoFurtherChanges(callback, timeout=2000) {
|
/** Article state to be tracked in addition to the editable content. */
|
||||||
var nonce_local = Math.random();
|
var article = {
|
||||||
nonce = nonce_local;
|
state: undefined,
|
||||||
setTimeout(() => {
|
ersatz: false
|
||||||
if (nonce == nonce_local) {
|
};
|
||||||
callback();
|
|
||||||
nonce = 0;
|
|
||||||
}
|
|
||||||
}, timeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read data out of params and initialize editor
|
/** Article content as last received from the server. */
|
||||||
window.onload = function() {
|
var preview = {
|
||||||
// Kill noscript message first
|
title: undefined,
|
||||||
document.getElementById("preview").innerHTML = "<p> </p>";
|
rendered: undefined,
|
||||||
|
citations: [],
|
||||||
|
errors: []
|
||||||
|
}
|
||||||
|
|
||||||
if (document.body.contains(document.getElementById("editor-content"))) {
|
/** The nonce of the last-made update request. */
|
||||||
onContentChange(0);
|
let nonce = 0;
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function buildArticleObject() {
|
/**
|
||||||
var title = document.getElementById("editor-title").value;
|
* Update request debounce wrapper that executes the callback if no further
|
||||||
var contents = document.getElementById("editor-content").value;
|
* calls are made during the timeout period. If a new call is made, any
|
||||||
return {
|
* previous calls are skipped.
|
||||||
aid: params.article.aid,
|
*/
|
||||||
title: title,
|
function ifNoFurtherChanges(callback, timeout)
|
||||||
status: params.article.status,
|
{
|
||||||
contents: contents
|
// Stake a claim on the nonce, potentially overwriting a previous
|
||||||
};
|
// nonce value.
|
||||||
}
|
const nonce_local = 1 + Math.random();
|
||||||
|
nonce = nonce_local;
|
||||||
|
// Wait to see if this call is overwritten in turn.
|
||||||
|
setTimeout(() => {
|
||||||
|
if (nonce == nonce_local)
|
||||||
|
{
|
||||||
|
callback();
|
||||||
|
nonce = 0;
|
||||||
|
}
|
||||||
|
}, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
function update(article) {
|
/** Update the editor controls and preview to match the current state. */
|
||||||
var req = new XMLHttpRequest();
|
function refreshEditor()
|
||||||
req.open("POST", params.updateURL, true);
|
{
|
||||||
req.setRequestHeader("Content-type", "application/json");
|
// Enable or disable controls
|
||||||
req.responseType = "json";
|
const isEditable = article.state == ArticleState.DRAFT;
|
||||||
req.onreadystatechange = function () {
|
const blocked = preview.errors.filter(err => err.severity == 2).length > 0;
|
||||||
if (req.readyState == 4 && req.status == 200) {
|
document.getElementById("editor-title").disabled = !isEditable;
|
||||||
// Update internal state with the returned article object
|
document.getElementById("editor-content").disabled = !isEditable;
|
||||||
params.status = req.response.status;
|
document.getElementById("button-submit").innerText = isEditable ? "Submit article" : "Edit article";
|
||||||
params.errors = req.response.error.length;
|
document.getElementById("button-submit").disabled = blocked;
|
||||||
document.getElementById("editor-title").value = req.response.title;
|
|
||||||
// Set editor editability based on article status
|
|
||||||
updateEditorStatus();
|
|
||||||
// Update the preview with the parse information
|
|
||||||
updatePreview(req.response);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var payload = { article: article };
|
|
||||||
req.send(JSON.stringify(payload));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateEditorStatus() {
|
// Update the preview
|
||||||
var ready = !!params.status.ready || !!params.status.approved;
|
const previewHtml = "<h1>" + preview.title + "</h1>\n" + preview.rendered;
|
||||||
document.getElementById("editor-title").disabled = ready;
|
document.getElementById("preview").innerHTML = previewHtml;
|
||||||
document.getElementById("editor-content").disabled = ready;
|
|
||||||
var hasErrors = params.errors > 0;
|
|
||||||
var submitButton = document.getElementById("button-submit");
|
|
||||||
submitButton.innerText = ready ? "Edit article" : "Submit article";
|
|
||||||
submitButton.disabled = hasErrors;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updatePreview(response) {
|
// Fill in the citation block
|
||||||
var previewHtml = "<h1>" + response.title + "</h1>\n" + response.rendered;
|
let citations = "<ol>";
|
||||||
document.getElementById("preview").innerHTML = previewHtml;
|
preview.citations.forEach(cit => citations += "<li>" + JSON.stringify(cit) + "</li>");
|
||||||
|
citations += "</ol>";
|
||||||
|
document.getElementById("preview-citations").innerHTML = citations;
|
||||||
|
|
||||||
var citations = "<ol>";
|
// Fill in the status message block
|
||||||
for (var i = 0; i < response.citations.length; i++) {
|
let statuses = "<ol>";
|
||||||
citations += "<li>" + response.citations[i] + "</li>";
|
preview.errors.forEach(err => statuses += "<li>" + JSON.stringify(err) + "</li>");
|
||||||
}
|
statuses += "<ol>";
|
||||||
citations += "</ol>";
|
document.getElementById("preview-control").innerHTML = statuses;
|
||||||
document.getElementById("preview-citations").innerHTML = citations;
|
}
|
||||||
|
|
||||||
var info = "";
|
/** Update the current state with the given data and refresh the editor. */
|
||||||
for (var i = 0; i < response.info.length; i++) {
|
function updateState(data)
|
||||||
info += "<span class=\"message-info\">" + response.info[i] + "</span><br>";
|
{
|
||||||
}
|
article.state = data.state;
|
||||||
var warning = "";
|
article.ersatz = data.ersatz;
|
||||||
for (var i = 0; i < response.warning.length; i++) {
|
preview.title = data.title;
|
||||||
warning += "<span class=\"message-warning\">" +
|
preview.rendered = data.rendered;
|
||||||
response.warning[i] + "</span><br>";
|
preview.citations = data.citations;
|
||||||
}
|
preview.errors = data.errors;
|
||||||
var error = "";
|
refreshEditor();
|
||||||
for (var i = 0; i < response.error.length; i++) {
|
}
|
||||||
error += "<span class=\"message-error\">" + response.error[i] + "</span><br>";
|
|
||||||
}
|
|
||||||
var control = info + warning + error;
|
|
||||||
document.getElementById("preview-control").innerHTML = control;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onContentChange(timeout=2000) {
|
/** Send the article's current content to the server. */
|
||||||
ifNoFurtherChanges(() => {
|
function update()
|
||||||
var article = buildArticleObject();
|
{
|
||||||
update(article);
|
const updateUrl = document.body.dataset.amanuensisUpdateUrl;
|
||||||
}, timeout);
|
const data = {
|
||||||
}
|
title: document.getElementById("editor-title").value,
|
||||||
|
body: document.getElementById("editor-content").value,
|
||||||
|
state: article.state
|
||||||
|
};
|
||||||
|
fetch(updateUrl, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
body: JSON.stringify(data)
|
||||||
|
}).then(response => response.json()).then(updateState);
|
||||||
|
}
|
||||||
|
|
||||||
function submitArticle() {
|
function onContentChange()
|
||||||
ifNoFurtherChanges(() => {
|
{
|
||||||
params.article.status.ready = !params.article.status.ready;
|
ifNoFurtherChanges(update, /* timeout: */ 2000);
|
||||||
var article = buildArticleObject();
|
}
|
||||||
update(article);
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function(e) {
|
function submitArticle()
|
||||||
if (nonce != 0) {
|
{
|
||||||
e.returnValue = "Are you sure?";
|
ifNoFurtherChanges(() => {
|
||||||
}
|
article.state = ArticleState.SUBMITTED;
|
||||||
});
|
update();
|
||||||
|
},
|
||||||
|
/* timeout: */ 0);
|
||||||
|
}
|
||||||
|
|
||||||
window.addEventListener("keydown", function(event) {
|
/** Initialize the editor on page load. */
|
||||||
if (event.ctrlKey || event.metaKey)
|
function initializeEditor()
|
||||||
{
|
{
|
||||||
if (String.fromCharCode(event.which).toLowerCase() == 's')
|
// Kill the noscript message
|
||||||
{
|
document.getElementById("preview").innerHTML = "<p>Loading...</p>";
|
||||||
event.preventDefault();
|
document.getElementById("preview-citations").innerHTML = "<p>Loading...</p>";
|
||||||
onContentChange(0);
|
document.getElementById("preview-control").innerHTML = "<p>Loading...</p>";
|
||||||
}
|
|
||||||
}
|
// Hook up the controls
|
||||||
});
|
document.getElementById("button-submit").onclick = submitArticle;
|
||||||
|
document.getElementById("editor-title").oninput = onContentChange;
|
||||||
|
document.getElementById("editor-content").oninput = onContentChange;
|
||||||
|
window.addEventListener("beforeunload", e =>
|
||||||
|
{
|
||||||
|
if (nonce > 0)
|
||||||
|
{
|
||||||
|
e.returnValue = "Are you sure?";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.addEventListener("keydown", e =>
|
||||||
|
{
|
||||||
|
if (e.ctrlKey && e.key == 's')
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
onContentChange(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Get the article status information.
|
||||||
|
const updateUrl = document.body.dataset.amanuensisUpdateUrl;
|
||||||
|
fetch(updateUrl).then(response => response.json()).then(updateState);
|
||||||
|
}
|
||||||
|
window.onload = initializeEditor;
|
||||||
|
})();
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
{% if current_page == "contents" %}class="current-page"
|
{% if current_page == "contents" %}class="current-page"
|
||||||
{% else %}href="{{ url_for('lexicon.contents', lexicon_name=g.lexicon.name) }}"
|
{% else %}href="{{ url_for('lexicon.contents', lexicon_name=g.lexicon.name) }}"
|
||||||
{% endif %}>Contents</a>{% endblock %}
|
{% endif %}>Contents</a>{% endblock %}
|
||||||
|
{% block sb_editor %}<a
|
||||||
|
href="{{ url_for('lexicon.editor.editor', lexicon_name=g.lexicon.name) }}"
|
||||||
|
>Editor</a>{% endblock %}
|
||||||
{% block sb_posts %}<a
|
{% block sb_posts %}<a
|
||||||
{% if current_page == "posts" %}class="current-page"
|
{% if current_page == "posts" %}class="current-page"
|
||||||
{% else %}href="{{ url_for('lexicon.posts.list', lexicon_name=g.lexicon.name) }}"
|
{% else %}href="{{ url_for('lexicon.posts.list', lexicon_name=g.lexicon.name) }}"
|
||||||
|
@ -35,6 +38,7 @@
|
||||||
{% set template_sidebar_rows = [
|
{% set template_sidebar_rows = [
|
||||||
self.sb_characters(),
|
self.sb_characters(),
|
||||||
self.sb_contents(),
|
self.sb_contents(),
|
||||||
|
self.sb_editor(),
|
||||||
self.sb_posts(),
|
self.sb_posts(),
|
||||||
self.sb_rules(),
|
self.sb_rules(),
|
||||||
self.sb_settings(),
|
self.sb_settings(),
|
||||||
|
|
|
@ -7,6 +7,7 @@ from amanuensis.errors import ArgumentError
|
||||||
from amanuensis.server.helpers import lexicon_param, player_required_if_not_public
|
from amanuensis.server.helpers import lexicon_param, player_required_if_not_public
|
||||||
|
|
||||||
from .characters import bp as characters_bp
|
from .characters import bp as characters_bp
|
||||||
|
from .editor import bp as editor_bp
|
||||||
from .forms import LexiconJoinForm
|
from .forms import LexiconJoinForm
|
||||||
from .posts import bp as posts_bp
|
from .posts import bp as posts_bp
|
||||||
from .settings import bp as settings_bp
|
from .settings import bp as settings_bp
|
||||||
|
@ -16,6 +17,7 @@ bp = Blueprint(
|
||||||
"lexicon", __name__, url_prefix="/lexicon/<lexicon_name>", template_folder="."
|
"lexicon", __name__, url_prefix="/lexicon/<lexicon_name>", template_folder="."
|
||||||
)
|
)
|
||||||
bp.register_blueprint(characters_bp)
|
bp.register_blueprint(characters_bp)
|
||||||
|
bp.register_blueprint(editor_bp)
|
||||||
bp.register_blueprint(posts_bp)
|
bp.register_blueprint(posts_bp)
|
||||||
bp.register_blueprint(settings_bp)
|
bp.register_blueprint(settings_bp)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
from flask import Blueprint, render_template, g
|
||||||
|
from sqlalchemy import select
|
||||||
|
|
||||||
|
from amanuensis.backend import *
|
||||||
|
from amanuensis.db import *
|
||||||
|
from amanuensis.parser.core import *
|
||||||
|
from amanuensis.server.helpers import lexicon_param, player_required
|
||||||
|
|
||||||
|
|
||||||
|
bp = Blueprint("editor", __name__, url_prefix="/editor", template_folder=".")
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("/<uuid:article_id>")
|
||||||
|
@lexicon_param
|
||||||
|
@player_required
|
||||||
|
def open(lexicon_name, article_id):
|
||||||
|
db: DbContext = g.db
|
||||||
|
article: Article = db(
|
||||||
|
select(Article).where(Article.public_id == article_id)
|
||||||
|
).scalar_one()
|
||||||
|
return render_template(
|
||||||
|
"session.editor.jinja",
|
||||||
|
lexicon_name=lexicon_name,
|
||||||
|
article=article,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.get("/<uuid:article_id>/update")
|
||||||
|
@lexicon_param
|
||||||
|
@player_required
|
||||||
|
def load(lexicon_name, article_id):
|
||||||
|
db: DbContext = g.db
|
||||||
|
article: Article = db(
|
||||||
|
select(Article).where(Article.public_id == article_id)
|
||||||
|
).scalar_one()
|
||||||
|
citations = [
|
||||||
|
{'title': 'Citation Title', 'type': 'phantom'}
|
||||||
|
]
|
||||||
|
errors = [
|
||||||
|
{'severity': 0, 'message': "OK"},
|
||||||
|
{'severity': 1, 'message': "Warning"},
|
||||||
|
{'severity': 2, 'message': "Error"},
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
'title': article.title,
|
||||||
|
'rendered': article.body,
|
||||||
|
'state': article.state.value,
|
||||||
|
'ersatz': article.ersatz,
|
||||||
|
'citations': citations,
|
||||||
|
'errors': errors,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post("/<uuid:article_id>/update")
|
||||||
|
def update(lexicon_name, article_id):
|
||||||
|
db: DbContext = g.db
|
||||||
|
article: Article = db(
|
||||||
|
select(Article).where(Article.public_id == article_id)
|
||||||
|
).scalar_one()
|
||||||
|
citations = [
|
||||||
|
{'title': 'Citation Title', 'type': 'phantom'}
|
||||||
|
]
|
||||||
|
errors = [
|
||||||
|
{'severity': 0, 'message': "OK"},
|
||||||
|
{'severity': 1, 'message': "Warning"},
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
'title': article.title,
|
||||||
|
'rendered': article.body,
|
||||||
|
'state': article.state.value,
|
||||||
|
'ersatz': article.ersatz,
|
||||||
|
'citations': citations,
|
||||||
|
'errors': errors,
|
||||||
|
}
|
|
@ -1,121 +1,64 @@
|
||||||
{% if character and not article %}
|
|
||||||
{% set characters = [character] %}
|
|
||||||
{% endif %}
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>Editor</title>
|
<title>Editor</title>
|
||||||
<link rel="icon" type="image/png" href="{{ url_for('static', filename='amanuensis.png') }}">
|
<link rel="icon" type="image/png" href="{{ url_for('static', filename='amanuensis.png') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='page.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='page.css') }}">
|
||||||
<link rel="stylesheet" href="{{ url_for('static', filename='editor.css') }}">
|
<link rel="stylesheet" href="{{ url_for('static', filename='editor.css') }}">
|
||||||
<script>
|
<script type="text/javascript" src="{{ url_for('static', filename='editor.js') }}"></script>
|
||||||
params = {
|
|
||||||
updateURL: "{{ url_for('session.editor_update', name=g.lexicon.cfg.name) }}",
|
|
||||||
{% if character %}
|
|
||||||
character: {{ jsonfmt(character) }},
|
|
||||||
{% else %}
|
|
||||||
character: null,
|
|
||||||
{% endif %}
|
|
||||||
{% if article %}
|
|
||||||
article: {
|
|
||||||
aid: {{ jsonfmt(article.aid) }},
|
|
||||||
status: {{ jsonfmt(article.status) }},
|
|
||||||
errors: 1,
|
|
||||||
}
|
|
||||||
{% else %}
|
|
||||||
article: null
|
|
||||||
{% endif %}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script type="text/javascript" src="{{ url_for('static', filename='editor.js') }}"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body
|
||||||
<div id="wrapper">
|
data-amanuensis-update-url="{{ url_for('lexicon.editor.load', lexicon_name=lexicon_name, article_id=article.public_id) }}"
|
||||||
<div id="editor-left" class="column">
|
>
|
||||||
<section>
|
<div id="wrapper">
|
||||||
{# Thin header bar #}
|
<div id="editor-left" class="column">
|
||||||
<div id="editor-header">
|
<section>
|
||||||
{# Header always includes backlink to lexicon #}
|
{# Thin header bar #}
|
||||||
<a href="{{ url_for('session.session', name=g.lexicon.cfg.name) }}">
|
<div id="editor-header">
|
||||||
{{ g.lexicon.title }}
|
{# Header always includes backlink to lexicon #}
|
||||||
</a>
|
<a href="{{ url_for('lexicon.contents', lexicon_name=lexicon_name) }}">
|
||||||
{# If article is not finalized, show button to submit and retract #}
|
{{ g.lexicon.full_title }}
|
||||||
{% if article and not article.status.approved %}
|
</a>
|
||||||
<button id="button-submit" onclick="submitArticle()" disabled>Submit article</button>
|
{# If article is not finalized, show button to submit and retract #}
|
||||||
{% endif %}
|
{# {% if article and not article.status.approved %} #}
|
||||||
{# Header always includes character/player info #}
|
{% if article %}
|
||||||
<span>
|
<button
|
||||||
<b>
|
id="button-submit"
|
||||||
{% if character %}
|
disabled
|
||||||
{{ character.name }} /
|
>Submit article</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{{ current_user.cfg.username }}
|
{# Header always includes character/player info #}
|
||||||
</b>
|
<span>{{ article.character.name }}</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
{% if article %}
|
||||||
{# In load mode, `characters` is specified and `article` is #}
|
{# <div id="editor-buttons">
|
||||||
{# not, and the main body of the editor column contains a #}
|
Character literals:
|
||||||
{# list of articles that can be loaded. #}
|
<button>*</button>
|
||||||
{% for char in characters %}
|
<button>/</button>
|
||||||
<div id="editor-charselect">
|
<button>[</button>
|
||||||
<b>{{ char.name }}</b>
|
<button>]</button>
|
||||||
<ul>
|
<button>~</button>
|
||||||
{% for article in articles %}
|
</div> #}
|
||||||
{% if article.character == char.cid %}
|
<input id="editor-title" placeholder="Title" disabled value="{{ article.title }}">
|
||||||
<li>
|
<textarea id="editor-content" class="fullwidth" disabled>
|
||||||
<a href="{{ url_for('session.editor', name=g.lexicon.cfg.name, aid=article.aid) }}">
|
{{- article.body -}}
|
||||||
{{ article.title if article.title.strip() else "Untitled" }}</a>
|
</textarea>
|
||||||
<span>
|
{% endif %}
|
||||||
{% if not article.status.ready %}
|
</section>
|
||||||
[Draft]
|
</div>
|
||||||
{% elif not article.status.approved %}
|
<div id="editor-right" class="column">
|
||||||
[Pending]
|
<section id="preview">
|
||||||
{% else %}
|
<p>This editor requires Javascript to function.</p>
|
||||||
[Approved]
|
</section>
|
||||||
{% endif %}
|
<section id="preview-citations">
|
||||||
</span>
|
<p> </p>
|
||||||
</li>
|
</section>
|
||||||
{% endif %}
|
<section id="preview-control">
|
||||||
{% endfor %}
|
<p> </p>
|
||||||
<li>
|
</fieldset>
|
||||||
<a href="{{ url_for('session.editor_new', name=g.lexicon.cfg.name, cid=char.cid) }}">
|
</div>
|
||||||
New
|
</div>
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{# In edit mode, `article` is specified and `characters` is #}
|
|
||||||
{# not, and the editor pane contains the article editor. #}
|
|
||||||
{% if article %}
|
|
||||||
{# <div id="editor-buttons">
|
|
||||||
Character literals:
|
|
||||||
<button>*</button>
|
|
||||||
<button>/</button>
|
|
||||||
<button>[</button>
|
|
||||||
<button>]</button>
|
|
||||||
<button>~</button>
|
|
||||||
</div> #}
|
|
||||||
<input id="editor-title" placeholder="Title" oninput="onContentChange()" disabled value="{{ article.title }}">
|
|
||||||
<textarea id="editor-content" class="fullwidth" oninput="onContentChange()" disabled>
|
|
||||||
{# #}{{ article.contents }}{#
|
|
||||||
#}</textarea>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
<div id="editor-right" class="column">
|
|
||||||
<section id="preview">
|
|
||||||
<p>This editor requires Javascript to function.</p>
|
|
||||||
</div>
|
|
||||||
<section id="preview-citations">
|
|
||||||
<p> </p>
|
|
||||||
</div>
|
|
||||||
<section id="preview-control">
|
|
||||||
<p> </p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -8,17 +8,9 @@
|
||||||
<span style="color:#ff0000">{{ message }}</span><br>
|
<span style="color:#ff0000">{{ message }}</span><br>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% for index in indexed %}
|
{% for article in current_lexicon.articles %}
|
||||||
<b>{{ index }}</b>
|
<p>{{ article.title }} - {{ article.public_id }}</p>
|
||||||
{% if indexed[index] %}
|
<p>{{ article.body }}</p>
|
||||||
<ul>
|
|
||||||
{% for article in indexed[index] %}
|
|
||||||
<li><a href="{{ article.title|articlelink }}" class="{{ 'phantom' if not article.character else '' }}">
|
|
||||||
{{ article.title }}
|
|
||||||
</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Reference in New Issue