add motd editor and motd display
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
from flask import Blueprint, abort, redirect, url_for, request, render_template, flash
|
from flask import Blueprint, abort, redirect, url_for, request, render_template, flash
|
||||||
from ..constants import InfoboxKind, PermissionLevel
|
from ..constants import InfoboxKind, PermissionLevel, MOTD_BANNED_TAGS
|
||||||
from ..auth import is_logged_in, get_active_user, csrf_verified
|
from ..auth import is_logged_in, get_active_user, csrf_verified
|
||||||
from ..models import Topics, Threads, Users
|
from ..models import Topics, Threads, Users, MOTD
|
||||||
|
from ..lib.babycode import babycode_to_html, BABYCODE_VERSION
|
||||||
from slugify import slugify
|
from slugify import slugify
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
import time
|
import time
|
||||||
@@ -24,7 +25,49 @@ def admin_only(view_func):
|
|||||||
|
|
||||||
@bp.get('/')
|
@bp.get('/')
|
||||||
def index():
|
def index():
|
||||||
return 'stub'
|
motd = MOTD.get_all()[0] if MOTD.has_motd() else None
|
||||||
|
return render_template('mod/panel.html', MOTD_BANNED_TAGS=MOTD_BANNED_TAGS, motd=motd)
|
||||||
|
|
||||||
|
@bp.post('/motd/set/')
|
||||||
|
def set_motd():
|
||||||
|
title = request.form.get('motd_title', '')
|
||||||
|
if not title:
|
||||||
|
flash('MOTD must have a title.', InfoboxKind.ERROR)
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
orig_body = request.form.get('babycode_content', '')
|
||||||
|
if not orig_body:
|
||||||
|
flash('MOTD must have a body.', InfoboxKind.ERROR)
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
user = get_active_user()
|
||||||
|
data = {
|
||||||
|
'title': title.strip(),
|
||||||
|
'body_original_markup': orig_body,
|
||||||
|
'body_rendered': babycode_to_html(orig_body, banned_tags=MOTD_BANNED_TAGS).result,
|
||||||
|
'format_version': BABYCODE_VERSION,
|
||||||
|
'edited_at': int(time.time()),
|
||||||
|
'user_id': user.id,
|
||||||
|
}
|
||||||
|
if MOTD.has_motd():
|
||||||
|
motd = MOTD.get_all()[0]
|
||||||
|
motd.update(data)
|
||||||
|
message = 'MOTD updated.'
|
||||||
|
else:
|
||||||
|
data['created_at'] = int(time.time())
|
||||||
|
motd = MOTD.create(data)
|
||||||
|
message = 'MOTD created.'
|
||||||
|
|
||||||
|
flash(message, InfoboxKind.INFO)
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
|
||||||
|
@bp.post('/motd/clear/')
|
||||||
|
def clear_motd():
|
||||||
|
if not MOTD.has_motd():
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
|
||||||
|
current = MOTD.get_all()[0]
|
||||||
|
current.delete()
|
||||||
|
flash('MOTD cleared.', InfoboxKind.INFO)
|
||||||
|
return redirect(url_for('.index'))
|
||||||
|
|
||||||
@bp.get('/topics/new/')
|
@bp.get('/topics/new/')
|
||||||
def new_topic():
|
def new_topic():
|
||||||
@@ -35,10 +78,6 @@ def new_topic_post():
|
|||||||
topic = Topics.new(request.form.get('name'), request.form.get('description'))
|
topic = Topics.new(request.form.get('name'), request.form.get('description'))
|
||||||
return redirect(url_for('topics.topic_by_id', topic_id=topic.id))
|
return redirect(url_for('topics.topic_by_id', topic_id=topic.id))
|
||||||
|
|
||||||
@bp.get('/topics/sort/')
|
|
||||||
def sort_topics():
|
|
||||||
return 'stub'
|
|
||||||
|
|
||||||
@bp.get('/topics/<int:topic_id>/edit/')
|
@bp.get('/topics/<int:topic_id>/edit/')
|
||||||
def edit_topic(topic_id):
|
def edit_topic(topic_id):
|
||||||
topic = Topics.find({'id': topic_id})
|
topic = Topics.find({'id': topic_id})
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
<img src="/static/icons/error.svg" alt="error" style="width: {{width}}px;">
|
<img src="/static/icons/error.svg" alt="error" style="width: {{width}}px;">
|
||||||
{%- endmacro -%}
|
{%- endmacro -%}
|
||||||
|
|
||||||
|
{%- macro icn_megaphone(width=48) -%}
|
||||||
|
<img src="/static/icons/megaphone.svg" alt="megaphone" style="width: {{width}}px;">
|
||||||
|
{%- endmacro -%}
|
||||||
|
|
||||||
{%- macro icn_bookmark(width=16) -%}
|
{%- macro icn_bookmark(width=16) -%}
|
||||||
<img src="/static/icons/bookmark.svg" alt="bookmark" style="width: {{width}}px;">
|
<img src="/static/icons/bookmark.svg" alt="bookmark" style="width: {{width}}px;">
|
||||||
{%- endmacro -%}
|
{%- endmacro -%}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{%- from 'common/icons.html' import icn_info, icn_warn, icn_error, icn_bookmark -%}
|
{%- from 'common/icons.html' import icn_info, icn_warn, icn_error, icn_bookmark, icn_megaphone -%}
|
||||||
|
|
||||||
{% macro timestamp(unix_ts) -%}
|
{% macro timestamp(unix_ts) -%}
|
||||||
<time datetime="{{ unix_ts | iso8601 }}">{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} <abbr title="Server Time">ST</abbr></time>
|
<time datetime="{{ unix_ts | iso8601 }}">{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} <abbr title="Server Time">ST</abbr></time>
|
||||||
@@ -238,3 +238,18 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro motd(motd_objs) -%}
|
||||||
|
{%- if motd_objs -%}
|
||||||
|
<div class="motd plank contrast-bg">
|
||||||
|
<div class="contain-svg">
|
||||||
|
{{ icn_megaphone(64) }}
|
||||||
|
<i><abbr title="Message of the Day">MOTD</abbr></i>
|
||||||
|
</div>
|
||||||
|
<div class="motd-content mobile-fill-flex">
|
||||||
|
<h2 class="info">{{ motd_objs[0].title }}</h2>
|
||||||
|
<div class="motd-body">{{ motd_objs[0].body_rendered | safe }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endmacro %}
|
||||||
|
|||||||
23
app/templates/mod/panel.html
Normal file
23
app/templates/mod/panel.html
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{%- from 'common/macros.html' import subheader, babycode_editor_component -%}
|
||||||
|
{%- extends 'base.html' -%}
|
||||||
|
{%- block title -%}settings{%- endblock -%}
|
||||||
|
{%- block content -%}
|
||||||
|
{{- subheader('Moderation panel') -}}
|
||||||
|
<fieldset class="plank">
|
||||||
|
<legend>Message of the Day</legend>
|
||||||
|
<p>The Message of the Day is shown on the front page and on each topic's thread list.</p>
|
||||||
|
<form class="full-width" method="POST" action="{{url_for('mod.set_motd')}}" id="motd-form">
|
||||||
|
<label for="motd-title">Title</label>
|
||||||
|
<input type="text" id="motd-title" name="motd_title" autocomplete="off" value="{{motd.title}}" required>
|
||||||
|
<label for="motd-content">Body</label>
|
||||||
|
{{ babycode_editor_component(placeholder='MOTD content', prefill=motd.body_original_markup, id='motd-content', banned_tags=MOTD_BANNED_TAGS) }}
|
||||||
|
</form>
|
||||||
|
<input type="submit" value="Save MOTD" form="motd-form">
|
||||||
|
<form method="POST" action="{{url_for('mod.clear_motd')}}">
|
||||||
|
<input type="submit" value="Clear MOTD" class="warn">
|
||||||
|
</form>
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="plank" id="sort-topics">
|
||||||
|
<legend>Sort topics</legend>
|
||||||
|
</fieldset>
|
||||||
|
{%- endblock -%}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
{% from 'common/macros.html' import timestamp, subheader, pager %}
|
{% from 'common/macros.html' import timestamp, subheader, pager, motd %}
|
||||||
{% from 'common/icons.html' import icn_locked, icn_stickied %}
|
{% from 'common/icons.html' import icn_locked, icn_stickied %}
|
||||||
{%- extends 'base.html' -%}
|
{%- extends 'base.html' -%}
|
||||||
{%- block title -%}browsing topic {{topic.name}}{%- endblock -%}
|
{%- block title -%}browsing topic {{topic.name}}{%- endblock -%}
|
||||||
@@ -46,6 +46,7 @@
|
|||||||
{%- if threads | length == 0 -%}
|
{%- if threads | length == 0 -%}
|
||||||
<div class="plank"><p>There are no threads in this topic yet.{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(topic) %} Be the first to start a discussion!{%- endif -%}</p></div>
|
<div class="plank"><p>There are no threads in this topic yet.{%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(topic) %} Be the first to start a discussion!{%- endif -%}</p></div>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
{{ motd(get_motds()) }}
|
||||||
{%- for thread in threads -%}
|
{%- for thread in threads -%}
|
||||||
<div class="topic-info plank">
|
<div class="topic-info plank">
|
||||||
<div class="title-container">
|
<div class="title-container">
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% from 'common/macros.html' import timestamp, subheader %}
|
{% from 'common/macros.html' import timestamp, subheader, motd %}
|
||||||
{% from 'common/icons.html' import icn_locked %}
|
{% from 'common/icons.html' import icn_locked %}
|
||||||
{%- extends 'base.html' -%}
|
{%- extends 'base.html' -%}
|
||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
@@ -7,10 +7,11 @@
|
|||||||
<fieldset class="plank even no-shadow minimal thread-actions">
|
<fieldset class="plank even no-shadow minimal thread-actions">
|
||||||
<legend>Moderation actions</legend>
|
<legend>Moderation actions</legend>
|
||||||
<a href="{{url_for('mod.new_topic')}}" class="linkbutton">New topic</a>
|
<a href="{{url_for('mod.new_topic')}}" class="linkbutton">New topic</a>
|
||||||
<a href="{{url_for('mod.sort_topics')}}" class="linkbutton">Sort topics</a>
|
<a href="{{url_for('mod.index', _anchor='sort-topics')}}" class="linkbutton">Sort topics</a>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
{%- endcall -%}
|
{%- endcall -%}
|
||||||
|
{{ motd(get_motds()) }}
|
||||||
{%- for topic in topics -%}
|
{%- for topic in topics -%}
|
||||||
<div class="topic-info plank">
|
<div class="topic-info plank">
|
||||||
<div class="title-container">
|
<div class="title-container">
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ ul.horizontal, ol.horizontal {
|
|||||||
|
|
||||||
.motd {
|
.motd {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--base-padding);
|
gap: var(--big-padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contain-svg {
|
.contain-svg {
|
||||||
|
|||||||
1
data/static/icons/megaphone.svg
Normal file
1
data/static/icons/megaphone.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="a" gradientUnits="userSpaceOnUse" x1="1.25" x2="28" y1="16" y2="16"><stop offset="0" stop-color="#fcf1d0"/><stop offset=".40270719" stop-color="#fcf1d0"/><stop offset=".40270719" stop-color="#f53232"/><stop offset="1" stop-color="#af2f2f"/></linearGradient><linearGradient id="b" gradientUnits="userSpaceOnUse" x1="19" x2="19.734835" y1="8" y2="10"><stop offset="0" stop-color="#fff" stop-opacity=".320554"/><stop offset="1" stop-color="#fff" stop-opacity="0"/></linearGradient><linearGradient id="c" gradientUnits="userSpaceOnUse" x1=".75" x2="31.5" y1="16.5" y2="16.5"><stop offset="0" stop-color="#c8c6b8"/><stop offset=".36670572" stop-color="#c8c6b8"/><stop offset=".36670572" stop-color="#fec8c8"/><stop offset=".8211382" stop-color="#fec8c8"/><stop offset=".82119328" stop-color="#c8c6b8"/><stop offset=".88617885" stop-color="#c8c6b8"/><stop offset=".88617885" stop-color="#fec8c8"/></linearGradient><g stroke-linecap="round" stroke-linejoin="round"><path d="m12 9.9999997c4 0 16-5.9999997 16-5.9999997 4 0 4 24 0 24 0 0-12-6-16-6l-2-.209922v7.209922h-3v-7.5l-5-.5c-1 0-1-10 0-10z" fill="none" stroke="#000" stroke-opacity=".501633" stroke-width="1.5"/><path d="m12 9.9999997c4 0 16-5.9999997 16-5.9999997 4 0 4 24 0 24 0 0-12-6-16-6l-2-.209922v7.209922h-3v-7.5l-5-.5c-1 0-1-10 0-10z" fill="none" stroke="url(#c)"/><g stroke-width=".25"><path d="m7 21h3v8h-3z" fill="#9e9b90"/><path d="m28 4c4 0 4 24 0 24 0 0-2-2-2-12s2-12 2-12z" fill="#5e1d1d"/><path d="m26 12v8c3 0 3-8 0-8z" fill="#f53232"/><path d="m12 10c4 0 16-6 16-6-2 8-2 16 0 24 0 0-12-6-16-6l-10-1c-1 0-1-10 0-10z" fill="url(#a)"/><path d="m26.005569 4.9644521c-2.005569 3.0355479-2.005569 19.0355479.020489 22.0807919l1.973942.954756c-2-8-2-16 0-24z" fill="#fcf1d0"/><path d="m12 13v-3c4 0 16-6 16-6-1 3.4038059-1.374501 9-1.374501 9z" fill="url(#b)"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
Reference in New Issue
Block a user