Semantic HTML and post feed #23

Merged
Jaculabilis merged 10 commits from tvb/post-feed into develop 2021-10-02 00:18:32 +00:00
7 changed files with 129 additions and 1 deletions
Showing only changes of commit b749357657 - Show all commits

View File

@ -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 #

View File

@ -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;

View File

@ -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()] %}

View File

@ -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)

View File

@ -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,
)

View File

@ -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")

View File

@ -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 %}