diff options
-rw-r--r-- | apioforum/db.py | 3 | ||||
-rw-r--r-- | apioforum/forum.py | 78 | ||||
-rw-r--r-- | apioforum/roles.py | 9 | ||||
-rw-r--r-- | apioforum/static/style.css | 2 | ||||
-rw-r--r-- | apioforum/templates/common.html | 4 | ||||
-rw-r--r-- | apioforum/templates/edit_forum.html | 27 | ||||
-rw-r--r-- | apioforum/templates/role_assignment.html | 2 | ||||
-rw-r--r-- | apioforum/templates/view_forum.html | 20 | ||||
-rw-r--r-- | apioforum/templates/view_unlisted.html | 24 | ||||
-rw-r--r-- | apioforum/user.py | 7 |
10 files changed, 161 insertions, 15 deletions
diff --git a/apioforum/db.py b/apioforum/db.py index d501159..899c6b4 100644 --- a/apioforum/db.py +++ b/apioforum/db.py @@ -146,6 +146,9 @@ CREATE TABLE role_assignments ( """, """ ALTER TABLE posts ADD COLUMN deleted NOT NULL DEFAULT 0; +""", +""" +ALTER TABLE forums ADD COLUMN unlisted NOT NULL DEFAULT 0; """ ] diff --git a/apioforum/forum.py b/apioforum/forum.py index f86629d..410bee5 100644 --- a/apioforum/forum.py +++ b/apioforum/forum.py @@ -98,7 +98,7 @@ def view_forum(forum): subforums_rows = db.execute(""" SELECT max(threads.updated) as updated, forums.* FROM forums LEFT OUTER JOIN threads ON threads.forum=forums.id - WHERE parent = ? + WHERE parent = ? AND unlisted = 0 GROUP BY forums.id ORDER BY name ASC """,(forum['id'],)).fetchall() @@ -109,12 +109,19 @@ def view_forum(forum): if a['updated'] is not None: a['updated'] = datetime.datetime.fromisoformat(a['updated']) subforums.append(a) + + bureaucrats = db.execute(""" + SELECT user FROM role_assignments + WHERE role = 'bureaucrat' AND forum = ? + """,(forum['id'],)).fetchall() + bureaucrats = [b[0] for b in bureaucrats] return render_template("view_forum.html", forum=forum, subforums=subforums, threads=threads, thread_tags=thread_tags, + bureaucrats=bureaucrats ) @forum_route("create_thread",methods=("GET","POST")) @@ -232,15 +239,18 @@ def edit_user_role(forum, username): return redirect(url_for('forum.edit_user_role', username=username,forum_id=forum['id'])) if not is_bureaucrat(forum['id'],g.user) and role != "approved" and role != "": + # only bureaucrats can assign arbitrary roles abort(403) - existing = db.execute("SELECT * FROM role_assignments WHERE user = ?;",(username,)).fetchone() + existing = db.execute( + "SELECT * FROM role_assignments WHERE user = ? AND forum = ?;", + (username,forum['id'])).fetchone() if existing: - db.execute("DELETE FROM role_assignments WHERE user = ?;",(username,)) + db.execute("DELETE FROM role_assignments WHERE user = ? AND forum = ?;",(username,forum['id'])) if role != "": db.execute( "INSERT INTO role_assignments (user,role,forum) VALUES (?,?,?);", (username,role,forum['id'])) - db.commit() + db.commit() flash("role assigned assignedly") return redirect(url_for('forum.view_forum',forum_id=forum['id'])) else: @@ -249,7 +259,8 @@ def edit_user_role(forum, username): return render_template("role_assignment.html", forum=forum,user=username,invalid_user=True) r = db.execute( - "SELECT role FROM role_assignments WHERE user = ?;",(username,)).fetchone() + "SELECT role FROM role_assignments WHERE user = ? AND forum = ?;", + (username,forum['id'])).fetchone() if not r: assigned_role = "" else: @@ -265,6 +276,63 @@ def edit_user_role(forum, username): forum=forum,user=username,role=role, assigned_role=assigned_role,forum_roles=roles) +def forum_config_page(forum, create=False): + db = get_db() + if request.method == "POST": + name = request.form["name"] + desc = request.form["description"] + unlisted = "unlisted" in request.form + if len(name) > 100 or len(name.strip()) == 0: + flash("invalid name") + return redirect(url_for('forum.edit_forum',forum_id=forum['id'])) + elif len(desc) > 6000: + flash("invalid description") + return redirect(url_for('forum.edit_forum',forum_id=forum['id'])) + if not create: + db.execute("UPDATE forums SET name = ?, description = ?, unlisted = ? WHERE id = ?", + (name,desc,forum['id'])) + fid = forum['id'] + else: + cur = db.cursor() + cur.execute( + "INSERT INTO forums (name,description,parent,unlisted) VALUES (?,?,?,?)", + (name,desc,forum['id'],unlisted)) + new = cur.lastrowid + # creator becomes bureaucrat of new forum + db.execute("INSERT INTO role_assignments (role,user,forum) VALUES (?,?,?)", + ("bureaucrat",g.user,new)) + fid = new + db.commit() + return redirect(url_for('forum.view_forum',forum_id=fid)) + else: + if create: + name = "" + desc = "" + else: + name = forum['name'] + desc = forum['description'] + cancel_link = url_for('forum.view_forum',forum_id=forum['id']) + return render_template("edit_forum.html",create=create, + name=name,description=desc,cancel_link=cancel_link) + +@forum_route("edit",methods=["GET","POST"]) +@requires_bureaucrat +def edit_forum(forum): + return forum_config_page(forum) + +@forum_route("create",methods=["GET","POST"]) +@requires_permission("p_create_subforum") +def create_forum(forum): + return forum_config_page(forum,create=True) + +@forum_route("unlisted") +@requires_bureaucrat +def view_unlisted(forum): + db = get_db() + unlisted = db.execute( + "SELECT * FROM forums WHERE unlisted = 1 AND parent = ?",(forum['id'],)) + return render_template('view_unlisted.html',forum=forum,unlisted=unlisted) + @bp.route("/search") def search(): db = get_db() diff --git a/apioforum/roles.py b/apioforum/roles.py index bda6704..ae47e31 100644 --- a/apioforum/roles.py +++ b/apioforum/roles.py @@ -40,11 +40,16 @@ def get_user_role(forum_id, user): fid = forum_id the = None - while the == None and fid != None: - the = db.execute(""" + while fid != None: + r = db.execute(""" SELECT * FROM role_assignments WHERE forum = ? AND user = ?; """,(fid,user)).fetchone() + # the user's role is equal to the role assignnment of the closest + # ancestor unless the user's role is "bureaucrat" in any ancestor + # in which case, the users role is "bureaucrat" + if the == None or (r and r['role'] == "bureaucrat"): + the = r fid = db.execute(""" SELECT * FROM forums WHERE id = ? """,(fid,)).fetchone()['parent'] diff --git a/apioforum/static/style.css b/apioforum/static/style.css index 931ac9a..2ed2e7a 100644 --- a/apioforum/static/style.css +++ b/apioforum/static/style.css @@ -188,7 +188,7 @@ nav#navbar .links { display: flex; } } .actionbutton { color:blue } -.new-post-box { +.new-post-box, .forum-desc-box { height:20em; resize:vertical; width:100%; diff --git a/apioforum/templates/common.html b/apioforum/templates/common.html index 9e60e81..7144667 100644 --- a/apioforum/templates/common.html +++ b/apioforum/templates/common.html @@ -67,6 +67,10 @@ <span class="tag" style="color: {{the_tag.text_colour}}; background-color: {{the_tag.bg_colour}}">{{the_tag.name}}</span> {%- endmacro %} +{% macro ab(name,href) -%} +<a class="actionbutton" href="{{href}}">{{name}}</a> +{%- endmacro %} + {% macro breadcrumb() %} <nav aria-label="Breadcrumb"> <ol class="breadcrumbs"> diff --git a/apioforum/templates/edit_forum.html b/apioforum/templates/edit_forum.html new file mode 100644 index 0000000..c8027e7 --- /dev/null +++ b/apioforum/templates/edit_forum.html @@ -0,0 +1,27 @@ +{% extends 'base.html' %} +{% block header %} +<h1>{% block title %}{%if create %}create{% else %}edit{%endif%} forum{% endblock %}</h1> +{% endblock %} + +{% block content %} +<form method="POST"> + <label for="name">forum name</label> + <input name="name" id="name" value="{{name}}" placeholder="apioforum" required maxlength="100"/> + <br> + <label for="description">forum description (markdown enabled)</label> + <textarea + name="description" + id="description" + class="forum-desc-box" + placeholder="this is a forum for discussing bees" + maxlength="6000" + required + >{{description}}</textarea> + <input type="checkbox" id="unlisted" name="unlisted"/> + <label for="unlisted">unlisted?</label> + <p> + <input type="submit" value="confirm"> + <a href="{{cancel_link}}">cancel</a> + </p> +</form> +{% endblock %} diff --git a/apioforum/templates/role_assignment.html b/apioforum/templates/role_assignment.html index b212606..74dc3cd 100644 --- a/apioforum/templates/role_assignment.html +++ b/apioforum/templates/role_assignment.html @@ -29,7 +29,7 @@ {% if can_change %} <label for="role">assigned role: </label> <select name="role" id="role" autocomplete="off"> - <option value="">(no assigned role)</option> + <option value="" {% if not assigned_role %}selected{% endif %}>(no assigned role)</option> {% for role in forum_roles %} <option value="{{role}}" {% if role == assigned_role %}selected{% endif %}> diff --git a/apioforum/templates/view_forum.html b/apioforum/templates/view_forum.html index a3563be..863f91c 100644 --- a/apioforum/templates/view_forum.html +++ b/apioforum/templates/view_forum.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% from 'common.html' import ts, tag, disp_user, post_url, forum_breadcrumb %} +{% from 'common.html' import ts, tag, disp_user, post_url, forum_breadcrumb, ab %} {% block header %} <h1>{% block title %}{{forum.name}}{% endblock %} <span class="thing-id">#{{forum.id}}</span></h1> {% if forum.id != 1 %} @@ -9,13 +9,25 @@ {%block content%} {{forum.description|md|safe}} +{% if bureaucrats|length > 0 %} + <p> + bureaucrats in this forum: + {% for b in bureaucrats %} + {{disp_user(b)}} + {% endfor %} + </p> +{% endif %} <p> {% if is_bureaucrat(forum.id, g.user) %} - <a class="actionbutton" href="{{url_for('forum.edit_roles',forum_id=forum.id)}}">role/permission settings</a> - <a class="actionbutton" href="{{url_for('forum.view_user_role',forum_id=forum.id)}}">assign roles</a> + {{ab("forum settings",url_for('forum.edit_forum',forum_id=forum.id))}} + {{ab("role/permission settings",url_for('forum.edit_roles',forum_id=forum.id))}} + {{ab("assign roles",url_for('forum.view_user_role',forum_id=forum.id))}} + {% endif %} + {% if has_permission(forum.id, g.user, "p_create_subforum") %} + {{ab("create subforum",url_for('forum.create_forum',forum_id=forum.id))}} {% endif %} {% if not is_bureaucrat(forum.id, g.user) and has_permission(forum.id, g.user, "p_approve") %} - <a class="actionbutton" href="{{url_for('forum.view_user_role',forum_id=forum.id)}}">approve users</a> + {{ab("approve users",url_for('forum.view_user_role',forum_id=forum.id))}} {% endif %} </p> {% if subforums %} diff --git a/apioforum/templates/view_unlisted.html b/apioforum/templates/view_unlisted.html new file mode 100644 index 0000000..c0fd074 --- /dev/null +++ b/apioforum/templates/view_unlisted.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% from 'common.html' import forum_breadcrumb %} +{% block header %} +<h1>{% block title %}unlisted subforæ in '{{forum.name}}'{% endblock %}</h1> +{% if forum.id != 1 %} + {{ forum_breadcrumb(forum) }} +{% endif %} +{% endblock %} + +{% block content %} +<form method="POST"> + {% if unlisted %} + <ul> + {% for f in unlisted %} + <li> + <a href="{{url_for('forum.view_forum',forum_id=f.id)}}">{{f.name}}</a> + </li> + {% endfor %} + </ul> + {% else %} + <p>there are no unlisted subforæ in '{{forum.name}}'</p> + {% endif %} +</form> +{% endblock %} diff --git a/apioforum/user.py b/apioforum/user.py index 9f4bc5b..bbdd060 100644 --- a/apioforum/user.py +++ b/apioforum/user.py @@ -16,8 +16,11 @@ def view_user(username): user = db.execute("SELECT * FROM users WHERE username = ?;",(username,)).fetchone() if user is None: abort(404) - posts = db.execute( - "SELECT * FROM posts WHERE author = ? ORDER BY created DESC LIMIT 25;",(username,)).fetchall() + posts = db.execute(""" + SELECT * FROM posts + WHERE author = ? AND deleted = 0 + ORDER BY created DESC + LIMIT 25;""",(username,)).fetchall() return render_template("view_user.html", user=user, posts=posts) @bp.route("/<username>/edit", methods=["GET","POST"]) |