forum: implement forum tree generation
This commit adds a forum tree YAML file (URL-based rather than an actual tree...) and the 'forums' and 'create-forums' commands for the master script. A page /admin/forums is also used to currently display the forum tree, although this will probably be turned into a full table with forum descriptions, and a form with edition capabilities.
This commit is contained in:
parent
de83f09024
commit
aa75ff09a1
|
@ -29,6 +29,6 @@ from app.models.forum import Forum
|
|||
from app.models.notification import Notification
|
||||
from app.routes import index, search, users # To load routes at initialization
|
||||
from app.routes.account import login, account, notification
|
||||
from app.routes.admin import index, groups, account, trophies
|
||||
from app.routes.admin import index, groups, account, trophies, forums
|
||||
from app.utils import pluralize # To use pluralize into the templates
|
||||
from app.utils import is_title
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# This file is a list of forums to create when setting up Planète Casio.
|
||||
#
|
||||
# * Keys are used as URLs paths and for unique identification.
|
||||
# * Prefixes represent the privilege category for a forum. Owning privileges
|
||||
# with this prefix allows the user to post in this forum and all its
|
||||
# sub-forum regardless of their settings ("forum-root-*" are hyper powerful).
|
||||
# * For open forums, use the prefix "open".
|
||||
|
||||
/:
|
||||
name: Forum de Planète Casio
|
||||
prefix: root
|
||||
|
||||
# News
|
||||
|
||||
/news:
|
||||
name: Actualités
|
||||
prefix: news
|
||||
|
||||
/news/projects:
|
||||
name: Actualités des projets
|
||||
prefix: projectnews
|
||||
descr: Nouveautés des projets de la communauté.
|
||||
|
||||
/news/calc:
|
||||
name: Actualités des constructeurs de calculatrices
|
||||
prefix: calcnews
|
||||
descr: Nouveautés CASIO, nouveaux modèles de calculatrices, mises à jour du
|
||||
système ou nouveautés d'autres constructeurs.
|
||||
|
||||
/news/events:
|
||||
name: Événements organisés par Planète Casio
|
||||
prefix: eventnews
|
||||
descr: Tous les événements organisés par Planète Casio ou la communauté.
|
||||
|
||||
/news/other:
|
||||
name: Autres nouveautés
|
||||
prefix: othernews
|
||||
descr: Actualités non catégorisées.
|
||||
|
||||
# Help
|
||||
|
||||
/help:
|
||||
name: Aide et questions
|
||||
prefix: help
|
||||
|
||||
/help/transfers:
|
||||
name: Questions sur les tranferts
|
||||
prefix: transferhelp
|
||||
descr: Questions sur le transfert de fichiers et l'installation de programmes
|
||||
sur la calculatrice.
|
||||
|
||||
/help/calc:
|
||||
name: Question sur l'utilisation des calculatrices
|
||||
prefix: calchelp
|
||||
descr: Questions sur l'utilisation des applications de la calculatrice,
|
||||
paramètres, formats de fichiers...
|
||||
|
||||
/help/prog:
|
||||
name: Questions de programmation
|
||||
prefix: proghelp
|
||||
descr: Questions sur le développement et le debuggage de programmes.
|
||||
|
||||
/help/other:
|
||||
name: Autres questions
|
||||
prefix: otherhelp
|
||||
descr: Questions non catégorisées.
|
||||
|
||||
# Projects
|
||||
|
||||
/projects:
|
||||
name: Forum des projets
|
||||
prefix: projects
|
||||
|
||||
/projects/games:
|
||||
name: Projets de jeux
|
||||
prefix: gameprojects
|
||||
|
||||
/projects/apps:
|
||||
name: Projets d'applications, utilitaires, outils pour calculatrice
|
||||
prefix: appprojects
|
||||
|
||||
/projects/tools:
|
||||
name: Projets pour d'autres plateformes
|
||||
prefix: toolprojetcs
|
||||
descr: Tous les projets tournant sur ordinateur, téléphone, ou toute autre
|
||||
plateforme que la calculatrice.
|
||||
|
||||
# Discussion
|
||||
|
||||
/discussion:
|
||||
name: Discussion
|
||||
prefix: discussion
|
||||
descr: Sujets hors-sujet et discussion libre.
|
|
@ -5,22 +5,32 @@ class Forum(db.Model):
|
|||
__tablename__ = 'forum'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# Standalone properties
|
||||
# Forum name, as displayed on the site (eg. "Problèmes de transfert")
|
||||
name = db.Column(db.Unicode(64))
|
||||
slug = db.Column(db.Unicode(64))
|
||||
description = db.Column(db.UnicodeText)
|
||||
# Privilege prefix (sort of slug) for single-forum privileges (lowercase)
|
||||
prefix = db.Column(db.Unicode(64))
|
||||
# Forum description, as displayed on the site
|
||||
descr = db.Column(db.UnicodeText)
|
||||
# Forum URL, for dynamic routes
|
||||
url = db.Column(db.String(64))
|
||||
|
||||
# Relationships
|
||||
parent_id = db.Column(db.Integer, db.ForeignKey('forum.id'), nullable=True)
|
||||
parent = db.relationship('Forum', backref='sub_forums', remote_side=id,
|
||||
lazy=True)
|
||||
lazy=True, foreign_keys=parent_id)
|
||||
|
||||
# Also [topics] which is provided by a backref from the Topic class
|
||||
|
||||
def __init__(self, name, description, priv_prefix):
|
||||
def __init__(self, url, name, prefix, descr="", parent=None):
|
||||
self.url = url
|
||||
self.name = name
|
||||
self.description = description
|
||||
self.priv_prefix = priv_prefix
|
||||
self.descr = descr
|
||||
self.prefix = prefix
|
||||
|
||||
if isinstance(parent, str):
|
||||
self.parent = Forum.query.filter_by(url=str).first()
|
||||
else:
|
||||
self.parent = parent
|
||||
|
||||
def __repr__(self):
|
||||
return f'<Forum: {self.name}>'
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
from app.utils.priv_required import priv_required
|
||||
from app.utils.render import render
|
||||
from app.models.forum import Forum
|
||||
from app import app, db
|
||||
|
||||
@app.route('/admin/forums', methods=['GET'])
|
||||
@priv_required('access-admin-panel')
|
||||
def adm_forums():
|
||||
main_forum = Forum.query.filter_by(parent=None).first()
|
||||
|
||||
return render('admin/forums.html', main_forum=main_forum)
|
|
@ -0,0 +1,32 @@
|
|||
{% extends "base/base.html" %}
|
||||
|
||||
{# This macro will allow us to perform recursive HTML generation #}
|
||||
{% macro forumtree(f) %}
|
||||
<li> {{ f.name }}
|
||||
<ul>
|
||||
{% for subf in f.sub_forums %}
|
||||
{{ forumtree(subf) }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endmacro %}
|
||||
|
||||
{% block title %}
|
||||
<a href="{{ url_for('adm') }}">Panneau d'administration</a> » <h1>Forums</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<p>Cette page permet de gérer l'arbre des forums.</p>
|
||||
|
||||
<h2>Arbre des forums</h2>
|
||||
|
||||
{% if main_forum == None %}
|
||||
<p>Il n'y a aucun forum.</p>
|
||||
{% else %}
|
||||
<ul>
|
||||
{{ forumtree(main_forum) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
|
@ -8,8 +8,9 @@
|
|||
<section>
|
||||
<p>Pages générales du panneau d'administration :</p>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
|
||||
<li><a href="{{ url_for('adm_groups') }}">Groupes et privilèges</a></li>
|
||||
<li><a href="{{ url_for('adm_trophies') }}">Titres et trophées</a></li>
|
||||
<li><a href="{{ url_for('adm_forums') }}">Arbre des forums</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
{% endblock %}
|
||||
|
|
49
master.py
49
master.py
|
@ -4,6 +4,7 @@ from app import app, db
|
|||
from app.models.users import Member, Group, GroupPrivilege
|
||||
from app.models.privs import SpecialPrivilege
|
||||
from app.models.trophies import Trophy, Title, TrophyMember
|
||||
from app.models.forum import Forum
|
||||
from app.utils import unicode_names
|
||||
import os
|
||||
import sys
|
||||
|
@ -19,6 +20,7 @@ Type a category name to see a list of elements. Available categories are:
|
|||
'groups' Privilege groups
|
||||
'trophies' Trophies
|
||||
'trophy-members' Trophies owned by members
|
||||
'forums' Forum tree
|
||||
|
||||
Type a category name followed by 'clear' to remove all entries in the category.
|
||||
|
||||
|
@ -36,6 +38,8 @@ the database.
|
|||
Type 'add-group <member> #<group-id>' to add a new member to a group.
|
||||
|
||||
Type 'create-trophies' to reset trophies and titles.
|
||||
|
||||
Type 'create-forums' to reset the forum tree.
|
||||
"""
|
||||
|
||||
#
|
||||
|
@ -84,6 +88,19 @@ def trophy_members(*args):
|
|||
for m in t.owners:
|
||||
print(f" {m}")
|
||||
|
||||
def forums(*args):
|
||||
if args == ("clear",):
|
||||
for f in Forum.query.all():
|
||||
db.session.delete(f)
|
||||
db.session.commit()
|
||||
print("Removed all forums.")
|
||||
return
|
||||
|
||||
for f in Forum.query.all():
|
||||
parent = f"in {f.parent.url}" if f.parent is not None else "root"
|
||||
print(f"{f.url} ({parent}) [{f.prefix}]: {f.name}")
|
||||
print(f" {f.descr}")
|
||||
|
||||
#
|
||||
# Creation and edition
|
||||
#
|
||||
|
@ -163,6 +180,36 @@ def create_trophies():
|
|||
|
||||
print(f"Created {len(tr)} trophies.")
|
||||
|
||||
def create_forums():
|
||||
# Clean up forums
|
||||
forums("clear")
|
||||
|
||||
# Create the forum tree
|
||||
fr = []
|
||||
success = 0
|
||||
with open(os.path.join(app.root_path, "data", "forums.yaml")) as fp:
|
||||
fr = yaml.safe_load(fp.read())
|
||||
|
||||
for url, f in fr.items():
|
||||
if url == "/":
|
||||
parent = None
|
||||
else:
|
||||
parent_url = url.rsplit('/', 1)[0]
|
||||
if parent_url == "":
|
||||
parent_url = "/"
|
||||
parent = Forum.query.filter_by(url=parent_url).first()
|
||||
|
||||
if parent is None:
|
||||
print(f"error: no parent with url {parent_url} for {url}")
|
||||
continue
|
||||
|
||||
f = Forum(url, f['name'], f['prefix'], f.get('descr', ''), parent)
|
||||
db.session.add(f)
|
||||
success += 1
|
||||
|
||||
db.session.commit()
|
||||
print(f"Created {success} forums.")
|
||||
|
||||
def add_group(member, group):
|
||||
if group[0] != "#":
|
||||
print(f"error: group id {group} should start with '#'")
|
||||
|
@ -194,8 +241,10 @@ commands = {
|
|||
"groups": groups,
|
||||
"trophies": trophies,
|
||||
"trophy-members": trophy_members,
|
||||
"forums": forums,
|
||||
"create-groups-and-privs": create_groups_and_privs,
|
||||
"create-trophies": create_trophies,
|
||||
"create-forums": create_forums,
|
||||
"add-group": add_group,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
"""add forum urls
|
||||
|
||||
Revision ID: 49427f8eb285
|
||||
Revises: a7aac1469393
|
||||
Create Date: 2019-09-02 21:16:06.971807
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '49427f8eb285'
|
||||
down_revision = 'a7aac1469393'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('url', sa.String(length=64), nullable=True))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('forum', 'url')
|
||||
# ### end Alembic commands ###
|
|
@ -0,0 +1,34 @@
|
|||
"""forum editions
|
||||
|
||||
Revision ID: a7aac1469393
|
||||
Revises: e3b140752719
|
||||
Create Date: 2019-09-02 21:12:52.236043
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'a7aac1469393'
|
||||
down_revision = 'e3b140752719'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('descr', sa.UnicodeText(), nullable=True))
|
||||
op.add_column('forum', sa.Column('prefix', sa.Unicode(length=64), nullable=True))
|
||||
op.drop_column('forum', 'slug')
|
||||
op.drop_column('forum', 'description')
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('forum', sa.Column('description', sa.TEXT(), autoincrement=False, nullable=True))
|
||||
op.add_column('forum', sa.Column('slug', sa.VARCHAR(length=64), autoincrement=False, nullable=True))
|
||||
op.drop_column('forum', 'prefix')
|
||||
op.drop_column('forum', 'descr')
|
||||
# ### end Alembic commands ###
|
Loading…
Reference in New Issue