add infobox support
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
from flask import Blueprint, abort, redirect, url_for, request, render_template
|
from flask import Blueprint, abort, redirect, url_for, request, render_template, flash
|
||||||
|
from ..constants import InfoboxKind
|
||||||
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
|
from ..models import Topics, Threads
|
||||||
|
from slugify import slugify
|
||||||
bp = Blueprint('mod', __name__, url_prefix='/mod/')
|
bp = Blueprint('mod', __name__, url_prefix='/mod/')
|
||||||
|
|
||||||
@bp.before_request
|
@bp.before_request
|
||||||
@@ -39,9 +41,21 @@ def edit_topic_post(topic_id):
|
|||||||
topic = Topics.find({'id': topic_id})
|
topic = Topics.find({'id': topic_id})
|
||||||
if not topic:
|
if not topic:
|
||||||
abort(404)
|
abort(404)
|
||||||
|
|
||||||
|
target_name = request.form.get('name').strip()
|
||||||
|
|
||||||
|
name_exists = Topics.count([
|
||||||
|
('lower(name)', '=', target_name.lower()),
|
||||||
|
('id', '!=', topic.id)
|
||||||
|
]) > 0
|
||||||
|
if name_exists:
|
||||||
|
flash(f'A topic named "{target_name}" already exists.', InfoboxKind.ERROR)
|
||||||
|
return redirect(url_for('.edit_topic', topic_id=topic_id))
|
||||||
|
|
||||||
topic.update({
|
topic.update({
|
||||||
'name': request.form.get('name').strip(),
|
'name': target_name,
|
||||||
'description': request.form.get('description').strip(),
|
'description': request.form.get('description').strip(),
|
||||||
|
'slug': slugify(target_name[:50]),
|
||||||
})
|
})
|
||||||
return redirect(url_for('topics.topic_by_id', topic_id=topic.id))
|
return redirect(url_for('topics.topic_by_id', topic_id=topic.id))
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{%- from 'common/macros.html' import infobox with context -%}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@@ -13,6 +14,13 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{%- include 'common/topnav.html' -%}
|
{%- include 'common/topnav.html' -%}
|
||||||
|
{%- with messages = get_flashed_messages(with_categories=true) -%}
|
||||||
|
{%- if messages -%}
|
||||||
|
{%- for category, message in messages -%}
|
||||||
|
{{- infobox(message, category) -}}
|
||||||
|
{%- endfor -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- endwith -%}
|
||||||
{%- block content -%}{%- endblock -%}
|
{%- block content -%}{%- endblock -%}
|
||||||
{%- include 'common/footer.html' -%}
|
{%- include 'common/footer.html' -%}
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
11
app/templates/common/icons.html
Normal file
11
app/templates/common/icons.html
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{%- macro icn_info(width=48) -%}
|
||||||
|
<svg height="{{width}}" viewBox="0 0 32 32" width="{{width}}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><filter id="if1" color-interpolation-filters="sRGB" height="1.09976" width="1" x="0" y="0"><feFlood flood-opacity=".14902" in="SourceGraphic" result="flood"/><feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="0"/><feOffset dx="0" dy="1.5" in="blur" result="offset"/><feComposite in="flood" in2="offset" operator="in" result="comp1"/><feComposite in="SourceGraphic" in2="comp1" operator="over" result="comp2"/></filter><linearGradient id="ilg1" gradientTransform="matrix(.921875 0 0 .9225 1.249998 1.239999)" gradientUnits="userSpaceOnUse" x1="16" x2="16" y1="0" y2="32"><stop offset=".15427744" stop-color="#4b8be1" stop-opacity=".992157"/><stop offset="1" stop-color="#1078e8" stop-opacity=".992157"/></linearGradient><linearGradient id="ilg2" gradientUnits="userSpaceOnUse" x1="15.99999832" x2="15.99999832" y1=".126983" y2="31.87301564"><stop offset="0" stop-color="#a9cef8"/><stop offset=".73127526" stop-color="#4f99e7"/></linearGradient><g><circle cx="16" cy="16" fill="#0e3459" fill-opacity=".500866" r="16"/><circle cx="16" cy="16" fill="url(#ilg2)" r="15.625" stroke="#4d677d" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25"/><ellipse cx="16" cy="16" fill="url(#ilg1)" rx="14.75" ry="14.76"/><g fill="#fff" stroke-linecap="round" stroke-linejoin="round"><path d="m29.494141 10.042735a14.75 14.76 0 0 0 -13.494141-8.8027352 14.75 14.76 0 0 0 -13.49414 8.8027352c2.987994-2.3171382 8.240661-4.0429692 13.49414-4.0429692s10.506146 1.725831 13.494141 4.0429692z" fill-opacity=".216645"/><path d="m15.716477 10.189534q-.789603 0-1.344793-.5428521-.542852-.5551897-.542852-1.3447928 0-.7772656.542852-1.3201177.55519-.5551897 1.344793-.5551897.777266 0 1.320118.5551897.55519.5428521.55519 1.3201177 0 .7896031-.55519 1.3447928-.542852.5428521-1.320118.5428521zm1.529856 14.780383h-2.282446v-11.091456h-1.233755v-1.86297h3.516201z" filter="url(#if1)" stroke-width=".25"/></g></g></svg>
|
||||||
|
{%- endmacro -%}
|
||||||
|
|
||||||
|
{%- macro icn_warn(width=48) -%}
|
||||||
|
<svg height="{{width}}" viewBox="0 0 32 32" width="{{width}}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><filter id="wf1" color-interpolation-filters="sRGB" height="1.09976" width="1" x="0" y="0"><feFlood flood-opacity=".14902" in="SourceGraphic" result="flood"/><feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="0"/><feOffset dx="0" dy="1.5" in="blur" result="offset"/><feComposite in="flood" in2="offset" operator="in" result="comp1"/><feComposite in="SourceGraphic" in2="comp1" operator="over" result="comp2"/></filter><linearGradient id="wlg1" gradientTransform="matrix(1.01415 0 0 .99686492 -.226398 -.52981)" gradientUnits="userSpaceOnUse" x1="16" x2="15.999999" y1="1.302475" y2="30.000002"><stop offset="0" stop-color="#f4f4ed"/><stop offset=".72657222" stop-color="#ddcf76"/></linearGradient><linearGradient id="wlg2" gradientUnits="userSpaceOnUse" x1="16" x2="16" y1="2.369791" y2="29.630209"><stop offset=".25" stop-color="#edd86d"/><stop offset="1" stop-color="#d5901a"/></linearGradient><g><path d="m14.960961 2.718822-14.675898 25.36219a1.2153386 1.2001658 0 0 0 1.055209 1.795616h29.319455a1.2153386 1.2001658 0 0 0 1.05521-1.795616l-14.6759-25.36219a1.1967125 1.1817724 0 0 0 -2.078076 0z" fill="#443c09" fill-opacity=".500632"/><path d="m14.99368 3.1949619-14.21375986 24.4501341a1.1770681 1.1570063 0 0 0 1.02198136 1.731044h28.3961965a1.1770681 1.1570063 0 0 0 1.021982-1.731044l-14.213761-24.4501341a1.1590285 1.1392744 0 0 0 -2.012639 0z" fill="url(#wlg1)" stroke="#6b5b07" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25"/><path d="m15.11254 4.0052032-12.6044459 22.9058128c-.3954388.72251.1042399 1.621487.901271 1.621487l25.1812709-.0031c.79703-.000097 1.296709-.898977.90127-1.621487l-12.604448-22.9027128c-.39239-.7169421-1.382527-.7169421-1.774918 0z" fill="url(#wlg2)"/><g stroke-linecap="round" stroke-linejoin="round"><path d="m11.150645 10.205078c1.849355-1.205078 3.775283-1.205078 4.848141-1.205078 1.072971 0 3.001214 0 4.851104 1.205078l-3.962432-6.1998748c-.39239-.716943-1.383-.716943-1.775391 0z" fill="#fff" fill-opacity=".397479"/><path d="m16.00535 25.361656q-.620708 0-1.059484-.428074-.428074-.438776-.428074-1.059484 0-.610006.428074-1.03808.438776-.438776 1.059484-.438776.610005 0 1.03808.438776.438776.428074.438776 1.03808 0 .620708-.438776 1.059484-.428075.428074-1.03808.428074zm.791937-4.494779h-1.551769l-.310354-10.541328h2.129669z" fill="#443c09" filter="url(#wf1)" stroke-width=".220327" transform="matrix(1.1346739 0 0 1.1346739 -2.154783 -2.497989)"/></g></g></svg>
|
||||||
|
{%- endmacro -%}
|
||||||
|
|
||||||
|
{%- macro icn_error(width=48) -%}
|
||||||
|
<svg height="{{width}}" viewBox="0 0 32 32" width="{{width}}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><linearGradient id="elg1" gradientTransform="matrix(.921875 0 0 .9225 1.249998 1.239999)" gradientUnits="userSpaceOnUse" x1="16" x2="16" y1="0" y2="32"><stop offset=".15427744" stop-color="#de4747"/><stop offset="1" stop-color="#b52222"/></linearGradient><linearGradient id="elg2" gradientUnits="userSpaceOnUse" x1="15.99999832" x2="15.99999832" y1=".126983" y2="31.87301564"><stop offset="0" stop-color="#f09797"/><stop offset=".73127526" stop-color="#d24141"/></linearGradient><filter id="ef1" color-interpolation-filters="sRGB" height="1.09976" width="1" x="0" y="0"><feFlood flood-opacity=".14902" in="SourceGraphic" result="flood"/><feGaussianBlur in="SourceGraphic" result="blur" stdDeviation="0"/><feOffset dx="0" dy="1.5" in="blur" result="offset"/><feComposite in="flood" in2="offset" operator="in" result="comp1"/><feComposite in="SourceGraphic" in2="comp1" operator="over" result="comp2"/></filter><g><circle cx="16" cy="16" fill="#590e0e" fill-opacity=".500866" r="16"/><circle cx="16" cy="16" fill="url(#elg2)" r="15.625" stroke="#7d4d4d" stroke-linecap="round" stroke-linejoin="round" stroke-width=".25"/><ellipse cx="16" cy="16" fill="url(#elg1)" rx="14.75" ry="14.76"/><g fill="#fff"><path d="m8 10 2-2 6 6 6-6 2 2-6 6 6 6-2 2-6-6-6 6-2-2 6-6z" filter="url(#ef1)"/><path d="m29.494141 10.042735a14.75 14.76 0 0 0 -13.494141-8.8027352 14.75 14.76 0 0 0 -13.49414 8.8027352c2.987994-2.3171382 8.240661-4.0429692 13.49414-4.0429692s10.506146 1.725831 13.494141 4.0429692z" fill-opacity=".216645" stroke-linecap="round" stroke-linejoin="round"/></g></g></svg>
|
||||||
|
{%- endmacro -%}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
{%- from 'common/icons.html' import icn_info, icn_warn, icn_error -%}
|
||||||
|
|
||||||
{% macro timestamp(unix_ts) -%}
|
{% macro timestamp(unix_ts) -%}
|
||||||
<span class="timestamp" data-utc="{{ unix_ts }}">{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} <abbr title="Server Time">ST</abbr></span>
|
<span class="timestamp" data-utc="{{ unix_ts }}">{{ unix_ts | ts_datetime('%Y-%m-%d %H:%M')}} <abbr title="Server Time">ST</abbr></span>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
@@ -180,3 +182,20 @@
|
|||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
</div>
|
</div>
|
||||||
{%- endmacro %}
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{% macro infobox(message, kind=InfoboxKind.INFO) -%}
|
||||||
|
<div class="infobox plank top contain-svg horizontal {{InfoboxHTMLClass[kind]}}">
|
||||||
|
{%- if kind == InfoboxKind.INFO -%}
|
||||||
|
{{- icn_info() -}}
|
||||||
|
{%- elif kind == InfoboxKind.WARN -%}
|
||||||
|
{{- icn_warn() -}}
|
||||||
|
{%- elif kind == InfoboxKind.ERROR -%}
|
||||||
|
{{- icn_error() -}}
|
||||||
|
{%- endif -%}
|
||||||
|
{%- set m = message.split(';', maxsplit=1) -%}
|
||||||
|
<strong>{{m[0]}}</strong>
|
||||||
|
{%- if m[1] -%}
|
||||||
|
{{m[1]}}
|
||||||
|
{%- endif -%}
|
||||||
|
</div>
|
||||||
|
{%- endmacro %}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<legend>Actions</legend>
|
<legend>Actions</legend>
|
||||||
{%- if is_logged_in() -%}
|
{%- if is_logged_in() -%}
|
||||||
{%- if thread.user_id == get_active_user().id -%}
|
{%- if thread.user_id == get_active_user().id -%}
|
||||||
<a class="linkbutton" href="{{url_for('threads.edit', thread_id=thread.id)}}">Edit…</a>
|
<a class="linkbutton" href="{{url_for('threads.edit', thread_id=thread.id)}}">Edit</a>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<button>Subscribe</button>
|
<button>Subscribe</button>
|
||||||
<button disabled title="This feature requires JavaScript to be enabled.">Bookmark…</button>
|
<button disabled title="This feature requires JavaScript to be enabled.">Bookmark…</button>
|
||||||
@@ -32,7 +32,7 @@
|
|||||||
<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>
|
||||||
{%- if thread.user_id != get_active_user().id -%}
|
{%- if thread.user_id != get_active_user().id -%}
|
||||||
<a class="linkbutton warn" href="{{url_for('threads.edit', thread_id=thread.id)}}">Edit…</a>
|
<a class="linkbutton warn" href="{{url_for('threads.edit', thread_id=thread.id)}}">Edit</a>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<input type="hidden" name="lock" value="{{(not thread.locked()) | int}}">
|
<input type="hidden" name="lock" value="{{(not thread.locked()) | int}}">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% from 'common/macros.html' import timestamp, subheader %}
|
{% from 'common/macros.html' import timestamp, subheader %}
|
||||||
|
{% from 'common/icons.html' import icn_info, icn_warn, icn_error %}
|
||||||
{%- extends 'base.html' -%}
|
{%- extends 'base.html' -%}
|
||||||
{%- block content -%}
|
{%- block content -%}
|
||||||
{%- call() subheader('All topics') -%}
|
{%- call() subheader('All topics') -%}
|
||||||
|
|||||||
@@ -411,11 +411,11 @@ ul.horizontal, ol.horizontal {
|
|||||||
color: oklch(from var(--main-color) round(1.21 - L) 0 0);
|
color: oklch(from var(--main-color) round(1.21 - L) 0 0);
|
||||||
|
|
||||||
&.critical {
|
&.critical {
|
||||||
--main-color: hsl(from var(--critical-color) h 50% calc(l * 0.7));
|
--main-color: hsl(from var(--critical-color) h 50% l);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warn {
|
&.warn {
|
||||||
--main-color: hsl(from var(--warn-color) h 50% calc(l * 1.2));
|
--main-color: hsl(from var(--warn-color) h 50% 75%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user