shii.moe

Source for `shii.moe' website.
git clone git://git.knutsen.co/shii.moe
Log | Files | Refs

commit c04733457fb6c966f7750689b0c43d6747bf6072
parent 523b0e8876de9cbc4bc0e3cc20a980ace2f7fa28
Author: Demonstrandum <moi@knutsen.co>
Date:   Tue, 12 Jan 2021 23:37:11 +0000

Add Markdown support for guestbook.

Diffstat:
Arequirements.txt | 5+++++
Mshiimoe.py | 37++++++++++++++++++++++++++++++++++++-
Mstyle.css | 44++++++++++++++++++++++++++++++++++++--------
Mtemplates/guestbook.html | 49+++++++++++++++++++++++++++++++++++++++----------
4 files changed, 116 insertions(+), 19 deletions(-)

diff --git a/requirements.txt b/requirements.txt @@ -0,0 +1,5 @@ +Flask +Pygments +markdown +bleach +pymdown-extensions diff --git a/shiimoe.py b/shiimoe.py @@ -1,9 +1,38 @@ from flask import Flask, render_template, send_from_directory, request, redirect + +import markdown +import markdown.extensions.codehilite +import markdown.extensions.fenced_code +import pymdownx, pymdownx.emoji +import bleach + from datetime import datetime from os import path +import json + app = Flask(__name__, static_url_path="", static_folder="./") +md = markdown.Markdown( + extensions=['fenced_code', 'codehilite', 'pymdownx.emoji'], + extension_configs={ + 'pymdownx.emoji': { + 'emoji_index': pymdownx.emoji.gemoji, + 'emoji_generator': pymdownx.emoji.to_png, + } + }) -import json + +ALLOWED_TAGS = list(set(bleach.sanitizer.ALLOWED_TAGS + [ + 'ul', 'ol', 'li', 'p', 'pre', 'code', 'blockquote', + 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'br', + 'strong', 'em', 'a', 'img' +])) +ALLOWED_ATTRIBUTES = { + **bleach.sanitizer.ALLOWED_ATTRIBUTES, + 'a': ['href', 'title'], + 'img': ['src', 'title', 'alt'] +} +ALLOWED_PROTOCOLS = list(set(bleach.sanitizer.ALLOWED_PROTOCOLS + + ['http', 'https', 'mailto'])) COMMENT_FILE = "comments.json" DATE_FORMAT = "%Y-%m-%d %H:%M" @@ -27,6 +56,12 @@ def render_guestbook(**kw): comments = read_comments() kw['comments'] = reversed(comments) + for comment in comments: + comment['comment'] = bleach.clean(comment['comment'], + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + protocols=ALLOWED_PROTOCOLS) + comment['comment'] = md.convert(comment['comment']) return render_template('guestbook.html', **kw) @app.route('/guestbook') diff --git a/style.css b/style.css @@ -1,4 +1,5 @@ @import url('https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,600;1,400;1,600&display=swap'); +@import url('https://cdn.jsdelivr.net/npm/pygments-css@1.0.0/github.css'); ::selection { background: pink; /* WebKit/Blink Browsers */ @@ -29,6 +30,34 @@ body { margin-right: auto; } +hr { + background: none; + border: none; + border-top: 2pt dashed #8dacca52; +} + +img.emoji, img.emojione, img.gemoji { + display: inline; + padding: 0; + margin: 0; + height: 1.3em; + vertical-align: top; + border: none; + filter: none; +} + +code { + background: #00000012; + border: 1px solid #00000029; + border-radius: 2pt; + padding: 0 2pt; +} +pre { width: 100%; } +pre code { + padding: 1em; + display: block; +} + .titleBar { background-color: #679dd74d; border-radius: 5pt; @@ -86,7 +115,7 @@ body { } .guitarPicture { - width:30em; + width:30em; float: right; margin-left: 2em; border: 2pt solid #0006; @@ -98,10 +127,10 @@ body { } #mascot { - height: 20em; + height: 20em; position: fixed; - bottom:0em; - right:0; + bottom:0em; + right:0; opacity: .85; } @@ -125,7 +154,7 @@ body { .titleButton { width: 50% } - + .pdf { height: 300pt; } @@ -140,7 +169,7 @@ body { #mascot { height: 10em; } - + #content { padding-bottom: 10em; } @@ -163,4 +192,4 @@ a { p { font-size: large; -}- \ No newline at end of file +} diff --git a/templates/guestbook.html b/templates/guestbook.html @@ -9,6 +9,7 @@ } .date { float: right; + font-family: monospace; } .date, .name { color: #0007; @@ -19,11 +20,28 @@ .comments { margin-top: 3em; } - .comment-input { - width: 30%; - height: 8em; - margin: 0.5em 0em; - } + .comments a { + color: #4e87bd; + text-decoration: underline; + } + .comments a:hover { + color: #3d6f9f; + } + .comments h1, .comments h2, .comments h3, + .comments h4, .comments h5, .comments h6 { + text-align: left; + margin: 0.4em 0 0 0; + border-bottom: 1pt solid #0000000f; + } + .comments img { + max-width: 100%; + max-height: 20em; + display: block; + border: 2pt solid #0000001a; + border-radius: 5pt; + margin: 0.5em 0; + filter: drop-shadow(0 4pt 8pt #0004); + } .input { font-family: 'Lora', serif; padding: 0.4em 0.6em; @@ -31,9 +49,18 @@ width: 30%; border: 1px solid #0004; } + .comment-input { + font-family: monospace; + width: 30%; + height: 8em; + margin: 0.5em 0em; + } .comment { - margin: 1.5em 0; + margin: 2em 0 1.5em 0; } + .comment-text p:nth-child(1) { + margin-top: 0; + } .error { margin: 1em 0 2em 0; padding: 1em 1.5em; @@ -66,7 +93,7 @@ <form action="/postcomment" method="POST" name="commentBox"> <input class="name-input input" name="name" placeholder="Your name" required> <br> - <textarea class="comment-input input" name="comment" placeholder="Your comment" required></textarea> + <textarea class="comment-input input" name="comment" placeholder="Your comment (Markdown)" required></textarea> <br> <input type="submit" value="Post"> </form> @@ -112,10 +139,12 @@ <div class="comments"> {% for comment in comments %} <div class="comment"> - <span class="name">~ {{ comment['name'] }}</span><span class="date">{{ comment['date'] }}</span> + <span class="name">~ {{ comment['name'] }}</span><span class="date">~{{ comment['date'] }}</span> <div style="clear: left;"></div> - <p class="comment-text" style="font-size: 120%;">{{ comment['comment'] }}</p> - <hr style="border: none; height: 3pt; background: #0002;"> + <div class="comment-text" style="font-size: 120%;"> + {{ comment['comment'] | safe }} + </div> + <hr style="border: none; height: 3pt; background: #0002;" /> </div> {% endfor %} </div>