Semantic HTML and post feed #23
|
@ -251,7 +251,7 @@ class Lexicon(ModelBase):
|
|||
indices = relationship("ArticleIndex", back_populates="lexicon")
|
||||
index_rules = relationship("ArticleIndexRule", back_populates="lexicon")
|
||||
content_rules = relationship("ArticleContentRule", back_populates="lexicon")
|
||||
posts = relationship("Post", back_populates="lexicon")
|
||||
posts = relationship("Post", back_populates="lexicon", order_by="Post.created.desc()")
|
||||
|
||||
#######################
|
||||
# Derived information #
|
||||
|
|
|
@ -215,6 +215,10 @@ details.setting-help {
|
|||
#index-definition-table td input[type=number] {
|
||||
width: 4em;
|
||||
}
|
||||
p.post-byline {
|
||||
color: #606060;
|
||||
text-align: right;
|
||||
}
|
||||
@media only screen and (max-width: 816px) {
|
||||
main {
|
||||
padding: 5px;
|
||||
|
|
|
@ -15,6 +15,10 @@
|
|||
{% if current_page == "contents" %}class="current-page"
|
||||
{% else %}href="{{ url_for('lexicon.contents', lexicon_name=g.lexicon.name) }}"
|
||||
{% endif %}>Contents</a>{% endblock %}
|
||||
{% block sb_posts %}<a
|
||||
{% if current_page == "posts" %}class="current-page"
|
||||
{% else %}href="{{ url_for('lexicon.posts.list', lexicon_name=g.lexicon.name) }}"
|
||||
{% endif %}>Posts</a>{% endblock %}
|
||||
{% block sb_rules %}<a
|
||||
{% if current_page == "rules" %}class="current-page"
|
||||
{% else %}href="{{ url_for('lexicon.rules', lexicon_name=g.lexicon.name) }}"
|
||||
|
@ -31,6 +35,7 @@
|
|||
{% set template_sidebar_rows = [
|
||||
self.sb_characters(),
|
||||
self.sb_contents(),
|
||||
self.sb_posts(),
|
||||
self.sb_rules(),
|
||||
self.sb_settings(),
|
||||
self.sb_stats()] %}
|
||||
|
|
|
@ -8,6 +8,7 @@ from amanuensis.server.helpers import lexicon_param, player_required_if_not_publ
|
|||
|
||||
from .characters import bp as characters_bp
|
||||
from .forms import LexiconJoinForm
|
||||
from .posts import bp as posts_bp
|
||||
from .settings import bp as settings_bp
|
||||
|
||||
|
||||
|
@ -15,6 +16,7 @@ bp = Blueprint(
|
|||
"lexicon", __name__, url_prefix="/lexicon/<lexicon_name>", template_folder="."
|
||||
)
|
||||
bp.register_blueprint(characters_bp)
|
||||
bp.register_blueprint(posts_bp)
|
||||
bp.register_blueprint(settings_bp)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
from flask import Blueprint, render_template, g
|
||||
from flask.helpers import url_for
|
||||
from flask_login import current_user
|
||||
from werkzeug.utils import redirect
|
||||
|
||||
from amanuensis.backend import postq
|
||||
from amanuensis.db import Post
|
||||
from amanuensis.parser import RenderableVisitor, parse_raw_markdown
|
||||
from amanuensis.parser.core import *
|
||||
from amanuensis.server.helpers import lexicon_param, player_required, current_lexicon
|
||||
|
||||
from .forms import CreatePostForm
|
||||
|
||||
|
||||
bp = Blueprint("posts", __name__, url_prefix="/posts", template_folder=".")
|
||||
|
||||
|
||||
class PostFormatter(RenderableVisitor):
|
||||
"""Parses stylistic markdown into HTML without links."""
|
||||
|
||||
def TextSpan(self, span: TextSpan):
|
||||
return span.innertext
|
||||
|
||||
def LineBreak(self, span: LineBreak):
|
||||
return '<br>'
|
||||
|
||||
def ParsedArticle(self, span: ParsedArticle):
|
||||
return '\n'.join(span.recurse(self))
|
||||
|
||||
def BodyParagraph(self, span: BodyParagraph):
|
||||
return f'<p>{"".join(span.recurse(self))}</p>'
|
||||
|
||||
def SignatureParagraph(self, span: SignatureParagraph):
|
||||
return (
|
||||
'<hr><span class="signature"><p>'
|
||||
f'{"".join(span.recurse(self))}'
|
||||
'</p></span>'
|
||||
)
|
||||
|
||||
def BoldSpan(self, span: BoldSpan):
|
||||
return f'<b>{"".join(span.recurse(self))}</b>'
|
||||
|
||||
def ItalicSpan(self, span: ItalicSpan):
|
||||
return f'<i>{"".join(span.recurse(self))}</i>'
|
||||
|
||||
def CitationSpan(self, span: CitationSpan):
|
||||
return "".join(span.recurse(self))
|
||||
|
||||
|
||||
def render_post_body(post: Post) -> str:
|
||||
"""Parse and render the body of a post into post-safe HTML."""
|
||||
renderable: ParsedArticle = parse_raw_markdown(post.body)
|
||||
rendered: str = renderable.render(PostFormatter())
|
||||
return rendered
|
||||
|
||||
|
||||
@bp.get("/")
|
||||
@lexicon_param
|
||||
@player_required
|
||||
def list(lexicon_name):
|
||||
form = CreatePostForm()
|
||||
return render_template(
|
||||
"posts.jinja",
|
||||
lexicon_name=lexicon_name,
|
||||
form=form,
|
||||
render_post_body=render_post_body,
|
||||
)
|
||||
|
||||
|
||||
@bp.post("/")
|
||||
@lexicon_param
|
||||
@player_required
|
||||
def create(lexicon_name):
|
||||
form = CreatePostForm()
|
||||
if form.validate():
|
||||
# Data is valid
|
||||
postq.create(g.db, current_lexicon.id, current_user.id, form.body.data)
|
||||
return redirect(url_for("lexicon.posts.list", lexicon_name=lexicon_name))
|
||||
|
||||
else:
|
||||
# POST received invalid data
|
||||
return render_template(
|
||||
"posts.jinja",
|
||||
lexicon_name=lexicon_name,
|
||||
form=form,
|
||||
render_post_body=render_post_body,
|
||||
)
|
|
@ -0,0 +1,10 @@
|
|||
from flask_wtf import FlaskForm
|
||||
from wtforms import SubmitField, TextAreaField
|
||||
from wtforms.validators import DataRequired
|
||||
|
||||
|
||||
class CreatePostForm(FlaskForm):
|
||||
"""/lexicon/<name>/posts/"""
|
||||
|
||||
body = TextAreaField(validators=[DataRequired()])
|
||||
submit = SubmitField("Post")
|
|
@ -0,0 +1,20 @@
|
|||
{% extends "lexicon.jinja" %}
|
||||
{% set current_page = "characters" %}
|
||||
{% block title %}Character | {{ lexicon_title }}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<section>
|
||||
<form action="" method="post" novalidate>
|
||||
{{ form.hidden_tag() }}
|
||||
<p>{{ form.body(class_='fullwidth') }}</p>
|
||||
<p>{{ form.submit() }}</p>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
{% for post in current_lexicon.posts %}
|
||||
<section>
|
||||
<p>{{ render_post_body(post) }}</p>
|
||||
<p class="post-byline">Posted {% if post.user_id %}by {{ post.user.display_name }} {% endif %}at {{ post.created }}</p>
|
||||
</section>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
Loading…
Reference in New Issue