diff --git a/app/__init__.py b/app/__init__.py index d7a415a..b7c6ded 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -203,12 +203,14 @@ def create_app(): from app.routes.guides import bp as guides_bp from app.routes.mod import bp as mod_bp from app.routes.posts import bp as posts_bp + from app.routes.api import bp as api_bp app.register_blueprint(topics_bp) app.register_blueprint(threads_bp) app.register_blueprint(users_bp) app.register_blueprint(guides_bp) app.register_blueprint(mod_bp) app.register_blueprint(posts_bp) + app.register_blueprint(api_bp) with app.app_context(): from .schema import create as create_tables diff --git a/app/auth.py b/app/auth.py index bfb88ef..2e83818 100644 --- a/app/auth.py +++ b/app/auth.py @@ -105,6 +105,14 @@ def login_required(view_func): return view_func(*args, **kwargs) return wrapper +def hard_login_required(view_func): + @wraps(view_func) + def wrapper(*args, **kwargs): + if not is_logged_in(): + abort(403) + return view_func(*args, **kwargs) + return wrapper + def mod_only(view_func): @wraps(view_func) def wrapper(*args, **kwargs): diff --git a/app/routes/api.py b/app/routes/api.py new file mode 100644 index 0000000..40a14a4 --- /dev/null +++ b/app/routes/api.py @@ -0,0 +1,21 @@ +from flask import Blueprint, request +from ..auth import is_logged_in, hard_login_required, get_active_user +from ..lib.babycode import babycode_to_html +from ..models import APIRateLimits + +bp = Blueprint('api', __name__, url_prefix='/api/') + +@bp.post('/babycode-preview/') +@hard_login_required +def babycode_preview(): + user = get_active_user() + if not APIRateLimits.is_allowed(user.id, 'babycode_preview', 5): + return {'error': 'too many requests'}, 429 + markup = str(request.json.get('markup', '')) + if not markup: + return {'error': 'markup field missing or invalid type'}, 400 + banned_tags = request.json.get('banned_tags', []) + if not isinstance(banned_tags, list): + return {'error': 'banned_tags field is invalid type'}, 400 + rendered = babycode_to_html(markup, banned_tags).result + return {'html': rendered} diff --git a/app/templates/common/macros.html b/app/templates/common/macros.html index 99aad5a..126ab7a 100644 --- a/app/templates/common/macros.html +++ b/app/templates/common/macros.html @@ -72,15 +72,15 @@ {%- endmacro %} -{% macro tabs(prefix='', labels = []) -%} +{% macro tabs(prefix='', labels=[], signal_ss=[], signal_rs=[]) -%}
{%- for tab_label in labels -%} - + {%- endfor -%}
{%- for tab_label in labels -%} -
+
{{- caller(loop.index0) -}}
{%- endfor -%} @@ -94,7 +94,7 @@ id='babycode-content', banned_tags=[] ) -%} -{%- call(idx) tabs(prefix='babycode', labels=['Write', 'Preview']) -%} +{%- call(idx) tabs(prefix='babycode', labels=['Write', 'Preview'], signal_ss=[none, 'babycodePreviewInit'], signal_rs=[none, 'babycodePreview']) -%} {%- if idx == 0 -%} @@ -109,10 +109,10 @@ - {# stub: char count #} + stub: char count - + {%- if banned_tags -%}
Forbidden tags: @@ -124,6 +124,8 @@
{%- endif -%} babycode help +{%- else -%} +
{%- endif -%} {%- endcall -%} {%- endmacro %} diff --git a/app/templates/mod/panel.html b/app/templates/mod/panel.html index ff71139..a9612fc 100644 --- a/app/templates/mod/panel.html +++ b/app/templates/mod/panel.html @@ -17,6 +17,7 @@ +{{babycode_editor_component(placeholder='test', id='test-content')}}
Sort topics

Drag topics around to reorder them. Press "Save order" when done.

diff --git a/data/static/js/bits/ui.js b/data/static/js/bits/ui.js index 3611853..ef6d2ef 100644 --- a/data/static/js/bits/ui.js +++ b/data/static/js/bits/ui.js @@ -1,4 +1,6 @@ -export const b = {} +export const b = { + babycodePreviewEndpoint: '/api/babycode-preview/', +} export function setTab(_, sender, el) { if (sender.ariaSelected === 'true') { @@ -78,3 +80,64 @@ export function insertBabycode(_, sender, el) { } el.focus(); } + +export function babycodePreviewInit(ev, sender, el) { + if (!sender.parentNode.parentNode.contains(el)) { // tab container > tab bar > button + return; + } + + b.send({ text: el.value, sender: sender, bannedTags: JSON.parse(el.dataset.bannedTags) }, 'babycodePreview'); +} + +export async function babycodePreview(payload, _, el) { + if (!payload.sender.parentNode.parentNode.contains(el)) { + return; + } + + if (!payload.text.trim()) { + b.send({ plain: 'Type something to get a preview.', sender: el }, 'showBabycodePreview'); + return; + } + const options = { + method: 'POST', + headers: { + 'content-type': 'application/json', + }, + body: JSON.stringify({ + markup: payload.text, + banned_tags: payload.bannedTags, + }), + } + + const f = await fetch(b.babycodePreviewEndpoint, options); + try { + if (!f.ok) { + console.error(f); + let msg = ''; + switch (f.status) { + case 429: + return; + default: + msg = '(Something went wrong. Try again later.)' + } + b.send({ plain: msg, sender: el }, 'showBabycodePreview'); + return; + } + b.send({ ...(await f.json()), sender: el }, 'showBabycodePreview'); + } catch (error) { + b.send({ plain: '(Something went wrong. Try again later.)', sender: el }, 'showBabycodePreview'); + console.error(error); + return; + } +} + +export function showBabycodePreview(payload, _, el) { + if (!payload.sender.parentNode.contains(el)) { + return; + } + if (payload.plain) { + el.innerHTML = `

${payload.plain}

`; + } else { + el.innerHTML = payload.html; + } +}