diff --git a/app/lib/babycode.py b/app/lib/babycode.py index 234ef82..4c7ea7e 100644 --- a/app/lib/babycode.py +++ b/app/lib/babycode.py @@ -218,10 +218,14 @@ def should_collapse(text, surrounding): return False -def babycode_to_html(s): +def babycode_to_html(s, banned_tags=None): + allowed_tags = list(TAGS.keys()) + if banned_tags is not None: + for tag in banned_tags: + allowed_tags.remove(tag) subj = escape(s.strip().replace('\r\n', '\n').replace('\r', '\n')) parser = Parser(subj) - parser.valid_bbcode_tags = TAGS.keys() + parser.valid_bbcode_tags = allowed_tags parser.bbcode_tags_only_text_children = TEXT_ONLY parser.valid_emotes = EMOJI.keys() diff --git a/app/models.py b/app/models.py index 500b086..0b90639 100644 --- a/app/models.py +++ b/app/models.py @@ -394,3 +394,19 @@ class BookmarkedThreads(Model): def get_thread(self): return Threads.find({'id': self.thread_id}) + + +class MOTD(Model): + table = 'motd' + + @classmethod + def has_motd(cls): + q = 'SELECT EXISTS(SELECT 1 FROM motd) as e' + res = db.fetch_one(q)['e'] + return int(res) == 1 + + @classmethod + def get_all(cls): + q = 'SELECT id FROM motd' + res = db.query(q) + return [MOTD.find({'id': i['id']}) for i in res] diff --git a/app/routes/api.py b/app/routes/api.py index a59b769..3442906 100644 --- a/app/routes/api.py +++ b/app/routes/api.py @@ -40,7 +40,8 @@ def babycode_preview(): markup = request.json.get('markup') if not markup or not isinstance(markup, str): return {'error': 'markup field missing or invalid type'}, 400 - rendered = babycode_to_html(markup) + banned_tags = request.json.get('banned_tags', []) + rendered = babycode_to_html(markup, banned_tags) return {'html': rendered} diff --git a/app/routes/mod.py b/app/routes/mod.py index cecf8fe..0971ce2 100644 --- a/app/routes/mod.py +++ b/app/routes/mod.py @@ -1,8 +1,11 @@ from flask import ( - Blueprint, render_template, request, redirect, url_for + Blueprint, render_template, request, redirect, url_for, + flash ) from .users import get_active_user, is_logged_in -from ..models import Users, PasswordResetLinks +from ..models import Users, PasswordResetLinks, MOTD +from ..constants import InfoboxKind +from ..lib.babycode import babycode_to_html, BABYCODE_VERSION from ..db import db import secrets import time @@ -55,3 +58,47 @@ def create_reset_pass(user_id): @bp.get('/panel') def panel(): return render_template('mod/panel.html') + + +@bp.get('/motd') +def motd_editor(): + current = MOTD.get_all()[0] if MOTD.has_motd() else None + return render_template('mod/motd.html', current=current) + + +@bp.post('/motd') +def motd_editor_form(): + orig_body = request.form.get('body', default='') + title = request.form.get('title', default='') + data = { + 'title': title, + 'body_original_markup': orig_body, + 'body_rendered': babycode_to_html(orig_body, banned_tags=['img', 'spoiler']), + 'format_version': BABYCODE_VERSION, + 'edited_at': int(time.time()), + } + + if MOTD.has_motd(): + motd = MOTD.get_all()[0] + motd.update(data) + message = 'MOTD updated.' + else: + data['created_at'] = int(time.time()) + data['user_id'] = get_active_user().id + motd = MOTD.create(data) + message = 'MOTD created.' + + flash(message, InfoboxKind.INFO) + return redirect(url_for('.motd_editor')) + + +@bp.post('/motd/delete') +def motd_delete(): + if not MOTD.has_motd(): + flash('No MOTD to delete.', InfoboxKind.WARN) + return redirect(url_for('.motd_editor')) + + current = MOTD.get_all()[0] + current.delete() + flash('MOTD deleted.', InfoboxKind.INFO) + return redirect(url_for('.motd_editor')) diff --git a/app/schema.py b/app/schema.py index 54ba328..6158477 100644 --- a/app/schema.py +++ b/app/schema.py @@ -120,6 +120,18 @@ SCHEMA = [ UNIQUE(collection_id, thread_id) )""", + """CREATE TABLE IF NOT EXISTS "motd" ( + "id" INTEGER NOT NULL PRIMARY KEY, + "title" TEXT NOT NULL, + "body_original_markup" TEXT NOT NULL, + "body_rendered" TEXT NOT NULL, + "markup_language" TEXT NOT NULL DEFAULT 'babycode', + "format_version" INTEGER DEFAULT NULL, + "created_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "edited_at" INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP)), + "user_id" REFERENCES users(id) ON DELETE CASCADE + )""", + # INDEXES "CREATE INDEX IF NOT EXISTS idx_post_history_post_id ON post_history(post_id)", "CREATE INDEX IF NOT EXISTS idx_posts_thread ON posts(thread_id, created_at, id)", diff --git a/app/templates/common/icons.html b/app/templates/common/icons.html index 6e88cf4..4582473 100644 --- a/app/templates/common/icons.html +++ b/app/templates/common/icons.html @@ -47,3 +47,9 @@ {%- endmacro %} + +{% macro icn_megaphone(width=60) -%} + + + +{%- endmacro %} diff --git a/app/templates/common/macros.html b/app/templates/common/macros.html index 4771958..9809cc5 100644 --- a/app/templates/common/macros.html +++ b/app/templates/common/macros.html @@ -39,18 +39,24 @@ {% macro infobox(message, kind=InfoboxKind.INFO) %}
-
- {%- if kind == InfoboxKind.INFO -%} - {{- icn_info() -}} - {%- elif kind == InfoboxKind.LOCK -%} - {{- icn_lock() -}} - {%- elif kind == InfoboxKind.WARN -%} - {{- icn_warn() -}} - {%- elif kind == InfoboxKind.ERROR -%} - {{- icn_error() -}} - {%- endif -%} -
- {{ message }} +
+ {%- if kind == InfoboxKind.INFO -%} + {{- icn_info() -}} + {%- elif kind == InfoboxKind.LOCK -%} + {{- icn_lock() -}} + {%- elif kind == InfoboxKind.WARN -%} + {{- icn_warn() -}} + {%- elif kind == InfoboxKind.ERROR -%} + {{- icn_error() -}} + {%- endif -%} +
+ + {% set m = message.split(';', maxsplit=1) %} + {{ m[0] }} + {%- if m[1] %} + {{ m[1] -}} + {%- endif -%} +
{% endmacro %} @@ -59,27 +65,38 @@ {{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} ST {%- endmacro %} -{% macro babycode_editor_component(ta_name, ta_placeholder="Post body", optional=False, prefill="") %} +{% macro babycode_editor_component(ta_name, ta_placeholder="Post body", optional=False, prefill="", banned_tags=[]) %}
+
- - - - - - - - - - + + + + + + + + + + - + babycode guide + {% if banned_tags %} +
Forbidden tags:
+
+
    + {% for tag in banned_tags | unique %} +
  • [{{ tag }}]
  • + {% endfor %} +
+
+ {% endif %}
Type something!
diff --git a/app/templates/mod/motd.html b/app/templates/mod/motd.html new file mode 100644 index 0000000..fe35e5c --- /dev/null +++ b/app/templates/mod/motd.html @@ -0,0 +1,17 @@ +{% from 'common/macros.html' import babycode_editor_component %} +{% extends 'base.html' %} +{% block title %}editing MOTD{% endblock %} +{% block content %} +
+

Edit Message of the Day

+

The Message of the Day will show up on the main page and in every topic.

+
+ +
+ + {{ babycode_editor_component('body', ta_placeholder='MOTD body (required)', banned_tags=['img', 'spoiler'], prefill=current.body_original_markup) }} + + +
+
+{% endblock %} diff --git a/app/templates/mod/panel.html b/app/templates/mod/panel.html index f39c4dd..fb6f715 100644 --- a/app/templates/mod/panel.html +++ b/app/templates/mod/panel.html @@ -6,6 +6,7 @@
{% endblock %} diff --git a/app/templates/topics/topic.html b/app/templates/topics/topic.html index eeb1e8a..cfa3170 100644 --- a/app/templates/topics/topic.html +++ b/app/templates/topics/topic.html @@ -24,7 +24,7 @@ {% if topic['is_locked'] %} - {{ infobox("This topic is locked. Only moderators can create new threads.", InfoboxKind.INFO) }} + {{ infobox("This topic is locked.;Only moderators can create new threads.", InfoboxKind.INFO) }} {% endif %} {% if threads_list | length == 0 %} diff --git a/data/static/js/babycode-editor.js b/data/static/js/babycode-editor.js index 6efff87..09681ac 100644 --- a/data/static/js/babycode-editor.js +++ b/data/static/js/babycode-editor.js @@ -147,13 +147,17 @@ if (markup === "" || markup === previousMarkup) { return; } + const bannedTags = JSON.parse(document.getElementById('babycode-banned-tags').value); previousMarkup = markup; const req = await fetch(previewEndpoint, { method: "POST", headers: { "Content-Type": "application/json", }, - body: JSON.stringify({markup: markup}) + body: JSON.stringify({ + markup: markup, + banned_tags: bannedTags, + }) }) if (!req.ok) { switch (req.status) { diff --git a/sass/_default.scss b/sass/_default.scss index 4201c6d..9fc447d 100644 --- a/sass/_default.scss +++ b/sass/_default.scss @@ -1082,7 +1082,6 @@ ul.horizontal, ol.horizontal { } } - .new-concept-notification.hidden { display: none; }