added /manage/ and /manage/accounts/ views, added TOC to template
This commit is contained in:
parent
4536f7fbd9
commit
191e235137
9 changed files with 162 additions and 21 deletions
|
|
@ -10,6 +10,9 @@
|
||||||
caution message when viewing them.
|
caution message when viewing them.
|
||||||
+ Added Terms and Privacy Policy.
|
+ Added Terms and Privacy Policy.
|
||||||
+ Changed user page URLs (contributions page) from `/u/user` to `/@user`.
|
+ Changed user page URLs (contributions page) from `/u/user` to `/@user`.
|
||||||
|
+ `/manage/` is now a list of all managing options, including export/import and the brand new
|
||||||
|
`/manage/accounts`.
|
||||||
|
+ TOC is now shown in pages where screen width is greater than 960 pixels.
|
||||||
+ Style changes: added a top bar with the site title. It replaces the floating menu on the top right.
|
+ Style changes: added a top bar with the site title. It replaces the floating menu on the top right.
|
||||||
+ Added a built-in installer (`app_init.py`). You still need to manually create `site.conf`.
|
+ Added a built-in installer (`app_init.py`). You still need to manually create `site.conf`.
|
||||||
|
|
||||||
|
|
|
||||||
62
app.py
62
app.py
|
|
@ -161,6 +161,12 @@ class User(BaseModel):
|
||||||
def is_authenticated(self):
|
def is_authenticated(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def groups(self):
|
||||||
|
return (
|
||||||
|
UserGroup.select().join(UserGroupMembership, on=UserGroupMembership.group)
|
||||||
|
.where(UserGroupMembership.user == self)
|
||||||
|
)
|
||||||
|
|
||||||
class UserGroup(BaseModel):
|
class UserGroup(BaseModel):
|
||||||
name = CharField(32, unique=True)
|
name = CharField(32, unique=True)
|
||||||
permissions = BitField()
|
permissions = BitField()
|
||||||
|
|
@ -295,6 +301,8 @@ class PageRevision(BaseModel):
|
||||||
return self.textref.get_content()
|
return self.textref.get_content()
|
||||||
def html(self, *, math=True):
|
def html(self, *, math=True):
|
||||||
return md(self.text, math=self.page.is_math_enabled and math)
|
return md(self.text, math=self.page.is_math_enabled and math)
|
||||||
|
def html_and_toc(self, *, math=True):
|
||||||
|
return md_and_toc(self.text, math=self.page.is_math_enabled and math)
|
||||||
def human_pub_date(self):
|
def human_pub_date(self):
|
||||||
delta = datetime.datetime.now() - self.pub_date
|
delta = datetime.datetime.now() - self.pub_date
|
||||||
T = partial(get_string, g.lang)
|
T = partial(get_string, g.lang)
|
||||||
|
|
@ -484,7 +492,7 @@ def init_db_and_create_first_user():
|
||||||
|
|
||||||
#### WIKI SYNTAX ####
|
#### WIKI SYNTAX ####
|
||||||
|
|
||||||
def md(text, expand_magic=False, toc=True, math=True):
|
def md_and_toc(text, expand_magic=False, toc=True, math=True):
|
||||||
if expand_magic:
|
if expand_magic:
|
||||||
# DEPRECATED seeking for a better solution.
|
# DEPRECATED seeking for a better solution.
|
||||||
warnings.warn('Magic words are no more supported.', DeprecationWarning)
|
warnings.warn('Magic words are no more supported.', DeprecationWarning)
|
||||||
|
|
@ -502,9 +510,16 @@ def md(text, expand_magic=False, toc=True, math=True):
|
||||||
'insert_fonts_css': not _getconf('site', 'katex_url')
|
'insert_fonts_css': not _getconf('site', 'katex_url')
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
return markdown.Markdown(extensions=extensions, extension_configs=extension_configs).convert(text)
|
converter = markdown.Markdown(extensions=extensions, extension_configs=extension_configs)
|
||||||
|
if toc:
|
||||||
|
return converter.convert(text), converter.toc
|
||||||
|
else:
|
||||||
|
return converter.convert(text), ''
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return '<p class="error">There was an error during rendering: {e.__class__.__name__}: {e}</p>'.format(e=e)
|
return '<p class="error">There was an error during rendering: {e.__class__.__name__}: {e}</p>'.format(e=e), ''
|
||||||
|
|
||||||
|
def md(text, expand_magic=False, toc=True, math=True):
|
||||||
|
return md_and_toc(text, expand_magic=expand_magic, toc=toc, math=math)[0]
|
||||||
|
|
||||||
def remove_tags(text, convert=True, headings=True):
|
def remove_tags(text, convert=True, headings=True):
|
||||||
if headings:
|
if headings:
|
||||||
|
|
@ -538,7 +553,7 @@ def is_url_available(url):
|
||||||
|
|
||||||
forbidden_urls = [
|
forbidden_urls = [
|
||||||
'about', 'accounts', 'ajax', 'backlinks', 'calendar', 'circles', 'create',
|
'about', 'accounts', 'ajax', 'backlinks', 'calendar', 'circles', 'create',
|
||||||
'easter', 'edit', 'embed', 'help', 'history', 'init-config', 'kt',
|
'easter', 'edit', 'embed', 'group', 'help', 'history', 'init-config', 'kt',
|
||||||
'manage', 'media', 'p', 'privacy', 'protect', 'search', 'static', 'stats',
|
'manage', 'media', 'p', 'privacy', 'protect', 'search', 'static', 'stats',
|
||||||
'tags', 'terms', 'u', 'upload', 'upload-info'
|
'tags', 'terms', 'u', 'upload', 'upload-info'
|
||||||
]
|
]
|
||||||
|
|
@ -783,11 +798,7 @@ def edit(id):
|
||||||
|
|
||||||
@app.route("/__sync_start")
|
@app.route("/__sync_start")
|
||||||
def __sync_start():
|
def __sync_start():
|
||||||
if _getconf("sync", "master", "this") == "this":
|
flash("Sync is unavailable. Please import and export pages manually.")
|
||||||
abort(403)
|
|
||||||
from app_sync import main
|
|
||||||
main()
|
|
||||||
flash("Successfully synced messages.")
|
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
|
|
||||||
@app.route('/_jsoninfo/<int:id>', methods=['GET', 'POST'])
|
@app.route('/_jsoninfo/<int:id>', methods=['GET', 'POST'])
|
||||||
|
|
@ -895,7 +906,15 @@ def contributions(username):
|
||||||
user = User.get(User.username == username)
|
user = User.get(User.username == username)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
abort(404)
|
abort(404)
|
||||||
return render_template('contributions.jinja2', u=user, contributions=user.contributions.order_by(PageRevision.pub_date.desc()))
|
contributions = user.contributions.order_by(PageRevision.pub_date.desc())
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
return render_template('contributions.jinja2',
|
||||||
|
u=user,
|
||||||
|
contributions=contributions.paginate(page),
|
||||||
|
page_n=page,
|
||||||
|
total_count=contributions.count(),
|
||||||
|
min=min
|
||||||
|
)
|
||||||
|
|
||||||
@app.route('/calendar/')
|
@app.route('/calendar/')
|
||||||
def calendar_view():
|
def calendar_view():
|
||||||
|
|
@ -1083,6 +1102,12 @@ def easter_y(y=None):
|
||||||
else:
|
else:
|
||||||
return render_template('easter.jinja2')
|
return render_template('easter.jinja2')
|
||||||
|
|
||||||
|
## administration ##
|
||||||
|
|
||||||
|
@app.route('/manage/')
|
||||||
|
def manage_main():
|
||||||
|
return render_template('administration.jinja2')
|
||||||
|
|
||||||
## import / export ##
|
## import / export ##
|
||||||
|
|
||||||
class Exporter(object):
|
class Exporter(object):
|
||||||
|
|
@ -1214,6 +1239,23 @@ def importpages():
|
||||||
flash('Pages can be imported by Administrators only!')
|
flash('Pages can be imported by Administrators only!')
|
||||||
return render_template('importpages.jinja2')
|
return render_template('importpages.jinja2')
|
||||||
|
|
||||||
|
@app.route('/manage/accounts/', methods=['GET', 'POST'])
|
||||||
|
@login_required
|
||||||
|
def manage_accounts():
|
||||||
|
users = User.select().order_by(User.join_date.desc())
|
||||||
|
page = int(request.args.get('page', 1))
|
||||||
|
if request.method == 'POST':
|
||||||
|
if current_user.is_admin:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
flash('Operation not permitted!')
|
||||||
|
return render_template('manageaccounts.jinja2',
|
||||||
|
users=users.paginate(page),
|
||||||
|
page_n=page,
|
||||||
|
total_count=users.count(),
|
||||||
|
min=min
|
||||||
|
)
|
||||||
|
|
||||||
## terms / privacy ##
|
## terms / privacy ##
|
||||||
|
|
||||||
@app.route('/terms/')
|
@app.route('/terms/')
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
/* basic styles */
|
/* basic styles */
|
||||||
* {box-sizing: border-box;}
|
* {box-sizing: border-box;}
|
||||||
body{font-family:sans-serif;background-color:var(--bg-main); color: var(--fg-main);margin:0;position:relative}
|
body{font-family:sans-serif;background-color:var(--bg-main); color: var(--fg-main);margin:0;position:relative}
|
||||||
.content{margin: 3em 1.3em}
|
.content{margin: 3em 1.3em; position: relative}
|
||||||
.footer{text-align:center;}
|
.footer{text-align:center;}
|
||||||
|
|
||||||
/* header styles */
|
/* header styles */
|
||||||
|
|
@ -112,9 +112,10 @@ ul.inline > li:last-child::after {content: ""}
|
||||||
|
|
||||||
|
|
||||||
/* floating elements */
|
/* floating elements */
|
||||||
.toc{float:right}
|
nav.toc{display:none}
|
||||||
@media (max-width:639px){
|
@media only screen and (min-width:960px){
|
||||||
.toc{display:none}
|
nav.toc{display:block;position:absolute; top: 0; right: 0; width: 320px;}
|
||||||
|
.inner-content {margin-right: 320px}
|
||||||
}
|
}
|
||||||
.backontop{position:fixed;bottom:0;right:0}
|
.backontop{position:fixed;bottom:0;right:0}
|
||||||
@media print{
|
@media print{
|
||||||
|
|
|
||||||
23
templates/administration.jinja2
Normal file
23
templates/administration.jinja2
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
{% extends "base.jinja2" %}
|
||||||
|
|
||||||
|
{% block title %}Administrative tools — {{ app_name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Administrative tools</h1>
|
||||||
|
|
||||||
|
{% if current_user and current_user.is_admin %}
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<a href="/manage/export">Export pages</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/manage/import">Import pages</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="/manage/accounts">Manage accounts</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>Administrative tools can be accessed by administrator users only.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -52,6 +52,7 @@
|
||||||
<div class="flash">{{ msg }}</div>
|
<div class="flash">{{ msg }}</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% block content %}{% endblock %}
|
{% block content %}{% endblock %}
|
||||||
|
{% block toc %}{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="footer-copyright">© 2020–2023 Sakuragasaki46.</div>
|
<div class="footer-copyright">© 2020–2023 Sakuragasaki46.</div>
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,17 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>Contributions of {{ u.username }}</div>
|
<h1>Contributions of {{ u.username }}</h1>
|
||||||
|
|
||||||
|
<p class="nl-pagination">Showing results <strong>{{ page_n * 20 - 19 }}</strong> to <strong>{{ min(page_n * 20, total_count) }}</strong> of <strong>{{ total_count }}</strong> total.</p>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{% for rev in contributions %}
|
{% if page_n > 1 %}
|
||||||
|
<li class="nl-prev"><a href="?page={{ page_n - 1}}">« Previous page</a></li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% for rev in contributions %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/history/revision/{{ rev.id }}/">
|
<a href="/history/revision/{{ rev.id }}/">
|
||||||
#{{ rev.id }}
|
#{{ rev.id }}
|
||||||
|
|
@ -25,6 +32,10 @@
|
||||||
{{ rev.page.title }}
|
{{ rev.page.title }}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if page_n < total_count // 20 %}
|
||||||
|
<li class="nl-next"><a href="?page={{ page_n + 1 }}">Next page »</a></li>
|
||||||
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
52
templates/manageaccounts.jinja2
Normal file
52
templates/manageaccounts.jinja2
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
{% extends "base.jinja2" %}
|
||||||
|
|
||||||
|
{% block title %}Manage accounts - {{ app_name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>Manage accounts</h1>
|
||||||
|
|
||||||
|
{% if current_user.is_admin %}
|
||||||
|
<p>
|
||||||
|
Here is the list of users registered on {{ app_name }}, in reverse chronological order.
|
||||||
|
<strong>Beware: you are managing sensitive informations.</strong>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="nl-pagination">Showing results <strong>{{ page_n * 20 - 19 }}</strong> to <strong>{{ min(page_n * 20, total_count) }}</strong> of <strong>{{ total_count }}</strong> total.</p>
|
||||||
|
|
||||||
|
<form enctype="multipart/form-data" method="POST">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% if page_n > 1 %}
|
||||||
|
<li class="nl-prev"><a href="?page={{ page_n - 1}}">« Previous page</a></li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% for u in users %}
|
||||||
|
<li>
|
||||||
|
<input type="checkbox" name="u{{ u.id }}">
|
||||||
|
<a href="/@{{ u.username }}">{{ u.username }}</a>
|
||||||
|
{% if u == current_user %}<strong>(you)</strong>{% endif %}
|
||||||
|
-
|
||||||
|
Groups:
|
||||||
|
<ul class="inline">
|
||||||
|
{% for ug in u.groups() %}
|
||||||
|
<li>{{ ug.name }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
-
|
||||||
|
Registered on:
|
||||||
|
{{ u.join_date }}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% if page_n < total_count // 20 %}
|
||||||
|
<li class="nl-next"><a href="?page={{ page_n + 1 }}">Next page »</a></li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<p>Managing accounts can be done by users with Admin permissions only.</p>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{% extends "base.jinja2" %}
|
{% extends "base.jinja2" %}
|
||||||
|
|
||||||
{% block title %}Not found - {{ app_name }}{% endblock %}
|
{% block title %}{{ T('not-found') }} - {{ app_name }}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>{{ T('not-found') }}</h1>
|
<h1>{{ T('not-found') }}</h1>
|
||||||
|
|
||||||
<p>{{ T('not-found-text-1')}} <strong>{{ request.path }}</strong> {{ T('not-found-text-2' )}}.</p>
|
<p>{{ T('not-found-text-1') }} <strong>{{ request.path }}</strong> {{ T('not-found-text-2') }}.</p>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,14 @@
|
||||||
|
|
||||||
{% block json_info %}<script>window.page_info={{ p.js_info()|tojson|safe }};</script>{% endblock %}
|
{% block json_info %}<script>window.page_info={{ p.js_info()|tojson|safe }};</script>{% endblock %}
|
||||||
|
|
||||||
|
{% set html_and_toc = rev.html_and_toc(math = request.args.get('math') not in ['0', 'false', 'no', 'off']) %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<article>
|
<article>
|
||||||
<h1 id="firstHeading">{{ p.title }}</h1>
|
<h1 id="firstHeading">{{ p.title }}</h1>
|
||||||
|
|
||||||
{% if p.calendar %}
|
{% if p.calendar %}
|
||||||
<p class="calendar-subtitle"><span class="material-icons">calendar_today</span>{{ p.calendar.strftime('%B %-d, %Y') }}</div>
|
<p class="calendar-subtitle"><span class="material-icons">calendar_today</span>{{ p.calendar.strftime('%B %-d, %Y') }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="jump-to-actions">
|
<div class="jump-to-actions">
|
||||||
|
|
@ -28,7 +30,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="inner-content">
|
<div class="inner-content">
|
||||||
{{ rev.html(math = request.args.get('math') not in ['0', 'false', 'no', 'off'])|safe }}
|
{{ html_and_toc[0]|safe }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if p.tags %}
|
{% if p.tags %}
|
||||||
|
|
@ -54,6 +56,12 @@
|
||||||
{{ T('owner') }}: {{ p.owner.username }}
|
{{ T('owner') }}: {{ p.owner.username }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block toc %}
|
||||||
|
<nav class="toc">
|
||||||
|
{{ html_and_toc[1] }}
|
||||||
|
</nav>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
<script src="/static/content.js"></script>
|
<script src="/static/content.js"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue