diff options
-rw-r--r-- | apioforum/__init__.py | 7 | ||||
-rw-r--r-- | apioforum/db.py | 4 | ||||
-rw-r--r-- | apioforum/forum.py | 98 | ||||
-rw-r--r-- | apioforum/roles.py | 2 | ||||
-rw-r--r-- | apioforum/static/style.css | 38 | ||||
-rw-r--r-- | apioforum/templates/common.html | 45 | ||||
-rw-r--r-- | apioforum/templates/delete_thread.html | 18 | ||||
-rw-r--r-- | apioforum/templates/edit_permissions.html | 9 | ||||
-rw-r--r-- | apioforum/templates/role_assignment.html | 53 | ||||
-rw-r--r-- | apioforum/templates/view_forum.html | 27 | ||||
-rw-r--r-- | apioforum/templates/view_thread.html | 9 | ||||
-rw-r--r-- | apioforum/thread.py | 107 |
12 files changed, 331 insertions, 86 deletions
diff --git a/apioforum/__init__.py b/apioforum/__init__.py index f28471f..1d96d8c 100644 --- a/apioforum/__init__.py +++ b/apioforum/__init__.py @@ -48,8 +48,11 @@ def create_app(): return dict(path_for_next=p) app.jinja_env.globals.update(forum_path=forum.forum_path) - from .roles import has_permission, is_bureaucrat - app.jinja_env.globals.update(has_permission=has_permission,is_bureaucrat=is_bureaucrat) + from .roles import has_permission, is_bureaucrat, get_user_role + app.jinja_env.globals.update( + has_permission=has_permission, + is_bureaucrat=is_bureaucrat, + get_user_role=get_user_role) from .mdrender import render @app.template_filter('md') diff --git a/apioforum/db.py b/apioforum/db.py index cfb5646..d501159 100644 --- a/apioforum/db.py +++ b/apioforum/db.py @@ -127,6 +127,7 @@ CREATE TABLE role_config ( p_reply_threads INT NOT NULL DEFAULT 1, p_view_threads INT NOT NULL DEFAULT 1, p_manage_threads INT NOT NULL DEFAULT 0, + p_delete_posts INT NOT NULL DEFAULT 0, p_vote INT NOT NULL DEFAULT 1, p_create_polls INT NOT NULL DEFAULT 1, p_approve INT NOT NULL DEFAULT 0, @@ -142,6 +143,9 @@ CREATE TABLE role_assignments ( forum NOT NULL REFERENCES forums(id), role TEXT NOT NULL ); +""", +""" +ALTER TABLE posts ADD COLUMN deleted NOT NULL DEFAULT 0; """ ] diff --git a/apioforum/forum.py b/apioforum/forum.py index 1c9b4ed..9d84a69 100644 --- a/apioforum/forum.py +++ b/apioforum/forum.py @@ -3,12 +3,12 @@ from flask import ( Blueprint, render_template, request, - g, redirect, url_for, flash + g, redirect, url_for, flash, abort ) from .db import get_db from .mdrender import render -from .roles import get_forum_roles,has_permission,is_bureaucrat, permissions as role_permissions +from .roles import get_forum_roles,has_permission,is_bureaucrat,get_user_role, permissions as role_permissions from sqlite3 import OperationalError import datetime import functools @@ -55,6 +55,17 @@ def requires_permission(permission): def wrapper(forum, *args, **kwargs): if not has_permission(forum['id'], g.user, permission): abort(403) + return f(forum, *args, **kwargs) + return wrapper + return decorator + +def requires_bureaucrat(f): + @functools.wraps(f) + def wrapper(forum, *args, **kwargs): + if not is_bureaucrat(forum['id'], g.user): + abort(403) + return f(forum, *args, **kwargs) + return wrapper @forum_route("") def view_forum(forum): @@ -66,7 +77,8 @@ def view_forum(forum): most_recent_posts.created as mrp_created, most_recent_posts.author as mrp_author, most_recent_posts.id as mrp_id, - most_recent_posts.content as mrp_content + most_recent_posts.content as mrp_content, + most_recent_posts.deleted as mrp_deleted FROM threads INNER JOIN most_recent_posts ON most_recent_posts.thread = threads.id INNER JOIN number_of_posts ON number_of_posts.thread = threads.id @@ -142,13 +154,13 @@ def create_thread(forum): return render_template("create_thread.html") -@bp.route("/<int:forum_id>/roles",methods=("GET","POST")) -def edit_roles(forum_id): +@forum_route("roles",methods=("GET","POST")) +@requires_bureaucrat +def edit_roles(forum): db = get_db() - forum = db.execute("SELECT * FROM forums WHERE id = ?",(forum_id,)).fetchone() role_configs = db.execute( "SELECT * FROM role_config WHERE forum = ? ORDER BY ID ASC", - (forum_id,)).fetchall() + (forum['id'],)).fetchall() if request.method == "POST": for config in role_configs: @@ -160,13 +172,13 @@ def edit_roles(forum_id): UPDATE role_config SET {p} = ? WHERE forum = ? AND role = ?; """, - (permission_setting,forum_id, config['role'])) + (permission_setting,forum['id'], config['role'])) db.commit() flash('roles sucessfully enroled') - return redirect(url_for('forum.view_forum',forum_id=forum_id)) + return redirect(url_for('forum.view_forum',forum_id=forum['id'])) role_config_roles = [c['role'] for c in role_configs] - other_roles = [role for role in get_forum_roles(forum_id) if not role in role_config_roles] + other_roles = [role for role in get_forum_roles(forum['id']) if not role in role_config_roles] return render_template("edit_permissions.html", forum=forum, @@ -174,27 +186,79 @@ def edit_roles(forum_id): other_roles=other_roles ) -@bp.route("/<int:forum_id>/roles/new",methods=["POST"]) -def add_role(forum_id): +@forum_route("roles/new",methods=["POST"]) +def add_role(forum): name = request.form['role'].strip() if not all(c in (" ","-","_") or c.isalnum() for c in name) \ or len(name) > 32: flash("role name must contain no special characters") - return redirect(url_for('forum.edit_roles',forum_id=forum_id)) + return redirect(url_for('forum.edit_roles',forum_id=forum['id'])) if name == "bureaucrat": flash("cannot configure permissions for bureaucrat") - return redirect(url_for('forum.edit_roles',forum_id=forum_id)) + return redirect(url_for('forum.edit_roles',forum_id=forum['id'])) db = get_db() existing_config = db.execute(""" SELECT * FROM role_config WHERE forum = ? AND role = ? - """,(forum_id,name)).fetchone() + """,(forum['id'],name)).fetchone() if not existing_config: db.execute("INSERT INTO role_config (forum,role) VALUES (?,?)", - (forum_id,name)) + (forum['id'],name)) db.commit() - return redirect(url_for('forum.edit_roles',forum_id=forum_id)) + return redirect(url_for('forum.edit_roles',forum_id=forum['id'])) + +@forum_route("role",methods=["GET","POST"]) +@requires_permission("p_approve") +def view_user_role(forum): + if request.method == "POST": + return redirect(url_for( 'forum.edit_user_role', + username=request.form['user'],forum_id=forum['id'])) + else: + return render_template("role_assignment.html",forum=forum) + +@forum_route("role/<username>",methods=["GET","POST"]) +@requires_permission("p_approve") +def edit_user_role(forum, username): + db = get_db() + if request.method == "POST": + user = db.execute("SELECT * FROM users WHERE username = ?;",(username,)).fetchone() + if user == None: + return redirect(url_for('forum.edit_user_role', + username=username,forum_id=forum['id'])) + role = request.form['role'] + if role not in get_forum_roles(forum['id']) and role != "" and role != "bureaucrat": + flash("no such role") + 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 != "": + abort(403) + existing = db.execute("SELECT * FROM role_assignments WHERE user = ?;",(username,)).fetchone() + if existing: + if role == "": + db.execute("DELETE FROM role_assignments WHERE user = ?;",(username,)) + else: + db.execute("UPDATE role_assignments SET role = ? WHERE user = ?;",(role,username)) + db.commit() + elif role != "": + db.execute("INSERT INTO role_assignments (user,role) VALUES (?,?);",(username,role)) + db.commit() + flash("role assigned assignedly") + return redirect(url_for('forum.view_forum',forum_id=forum['id'])) + else: + user = db.execute("SELECT * FROM users WHERE username = ?;",(username,)).fetchone() + if user == None: + return render_template("role_assignment.html", + forum=forum,user=username,invalid_user=True) + role = get_user_role(forum['id'], username) + if is_bureaucrat(forum['id'], g.user): + roles = get_forum_roles(forum['id']) + roles.remove("other") + roles.add("bureaucrat") + else: + roles = ["approved"] + return render_template("role_assignment.html", + forum=forum,user=username,role=role,forum_roles=roles) @bp.route("/search") def search(): diff --git a/apioforum/roles.py b/apioforum/roles.py index 6d20316..bda6704 100644 --- a/apioforum/roles.py +++ b/apioforum/roles.py @@ -5,6 +5,7 @@ permissions = [ "p_create_threads", "p_reply_threads", "p_manage_threads", + "p_delete_posts", "p_view_threads", "p_vote", "p_create_polls", @@ -68,6 +69,7 @@ def get_forum_roles(forum_id): def has_permission(forum_id, user, permission): role = get_user_role(forum_id, user) if user != None else "other" + if role == "bureaucrat": return True config = get_role_config(forum_id, role) return config[permission] diff --git a/apioforum/static/style.css b/apioforum/static/style.css index 3813d63..931ac9a 100644 --- a/apioforum/static/style.css +++ b/apioforum/static/style.css @@ -17,10 +17,10 @@ body { font-family: sans-serif; word-wrap: break-word; } .post:last-of-type { border-bottom: 1px solid black; } .post-heading { font-size: smaller; } -.post-heading,a.username { +.post-heading,.username { color: hsl(0,0%,25%); } -a.username { +.username { font-weight: bold; text-decoration: underline; } @@ -34,6 +34,23 @@ a.username { .post-anchor-link { color: hsl(0,0%,25%); } +.deleted-post { + color:white; + background-color: hsl(0,0%,15%) !important; + border-left: 1px solid darkgray; + border-right: 1px solid darkgray; + border-top: 1px solid darkgray; +} +.deleted-post > .post-heading > * { + color: hsl(0,0%,85%); +} +.deleted-post > .post-heading > .post-heading-b > .post-anchor-link { + color: hsl(0,0%,60%); +} +.deleted-post > .post-heading > .post-heading-a > .username { + color: hsl(0,0%,80%); +} + .thread-top-bar, .user-top-bar { margin-bottom: 4px; } @@ -79,7 +96,16 @@ dt { font-weight: bold } img { max-width: 100% } -nav#navbar { float: right; padding: 5px; margin: 2px; border: 1px solid black; display:flex; align-items: center; flex-wrap: wrap } +nav#navbar { + float: right; + padding: 5px; + margin: 2px; + margin-bottom: 20px; + border: 1px solid black; + display:flex; + align-items: center; + flex-wrap: wrap; +} nav#navbar p { margin-left: 15px; margin-top: 0; margin-bottom: 0; margin-right: 10px; padding: 0 } nav#navbar p:first-of-type { margin-left:0.5em } nav#navbar a { color: blue; text-decoration: none } @@ -185,6 +211,8 @@ label { user-select: none; } fieldset { margin-bottom: 15px; } +.warning { color: red; font-weight: bold } + .search-form { display: inline-block; } @@ -195,7 +223,9 @@ fieldset { margin-bottom: 15px; } border: 1px solid black; } -.role-input { width: 12ch; } +.role-input, .name-input { width: 12ch; } + +.thing-id { color: darkgray; font-size: smaller; font-weight: normal; } .breadcrumbs { list-style: none; diff --git a/apioforum/templates/common.html b/apioforum/templates/common.html index b0bf713..9e60e81 100644 --- a/apioforum/templates/common.html +++ b/apioforum/templates/common.html @@ -6,28 +6,55 @@ {{url_for('thread.view_thread', thread_id=post.thread)}}#post_{{post.id}} {%- endmacro %} -{% macro disp_post(post, buttons=False) %} -<div class="post" id="post_{{post.id}}"> +{% macro disp_post(post, buttons=False, forum=None) %} +<div class="post {% if post.deleted %}deleted-post{% endif %}" id="post_{{post.id}}"> <div class="post-heading"> <span class="post-heading-a"> - {{disp_user(post.author)}} + {% if not post.deleted %} + {{disp_user(post.author)}} + {% else %} + <span class="username">big brother</span> + {% endif %} + + {% if forum != None %} + {% set role = get_user_role(forum, post.author) %} + {% if post.deleted %} + <span class="user-role"> + (bureaucrat) + </span> + {% elif role != "other" %} + <span class="user-role"> + ({{ role }}) + </span> + {% endif %} + {% endif %} + {{ts(post.created)}} + {% if post.edited %} (edited {{ts(post.updated)}}) {% endif %} </span> <span class="post-heading-b"> - {% if buttons and post.author == g.user %} - <a class="actionbutton" - href="{{url_for('thread.edit_post',post_id=post.id)}}">edit</a> - <a class="actionbutton" - href="{{url_for('thread.delete_post',post_id=post.id)}}">delete</a> + {% if buttons and not post.deleted %} + {% if post.author == g.user %} + <a class="actionbutton" + href="{{url_for('thread.edit_post',post_id=post.id)}}">edit</a> + {% endif %} + {% if post.author == g.user or (forum and has_permission(forum, g.user, "p_delete_posts")) %} + <a class="actionbutton" + href="{{url_for('thread.delete_post',post_id=post.id)}}">delete</a> + {% endif %} {% endif %} <a class="post-anchor-link" href="{{post_url(post)}}">#{{post.id}}</a> </span> </div> <div class="post-content"> - {{ post.content|md|safe }} + {% if not post.deleted %} + {{ post.content|md|safe }} + {% else %} + this post never existed. + {% endif %} </div> </div> {% endmacro %} diff --git a/apioforum/templates/delete_thread.html b/apioforum/templates/delete_thread.html new file mode 100644 index 0000000..aaf1de3 --- /dev/null +++ b/apioforum/templates/delete_thread.html @@ -0,0 +1,18 @@ +{% from 'common.html' import ts %} +{% extends 'base.html' %} +{% block header %} +<h1>{% block title %}delete thread '{{thread.title}}'{% endblock %}</h1> +{% endblock %} + +{% block content %} + +<form method="post"> +<p>deleting thread created {{ts(thread.created)}} ago with {{post_count}} posts</p> +{% if post_count > 50 %} +<p class="warning">thread contains more than 50 posts!</p> +{% endif %} +<p>confirm delete?</p> +<input type="submit" value="delete"> +<a href="{{url_for('thread.view_thread',thread_id=thread.id)}}">cancel</a> +</form> +{% endblock %} diff --git a/apioforum/templates/edit_permissions.html b/apioforum/templates/edit_permissions.html index 1e4e848..f91c710 100644 --- a/apioforum/templates/edit_permissions.html +++ b/apioforum/templates/edit_permissions.html @@ -1,5 +1,4 @@ {% extends 'base.html' %} -{% from 'common.html' import tag %} {% block header %}<h1>{% block title %}role permissions for '{{forum.name}}'{% endblock %}</h1>{% endblock %} {% block content %} <p> @@ -37,11 +36,13 @@ {{perm("p_view_threads","view threads", "allow users with the role to view threads in the forum")}} {{perm("p_manage_threads","configure others' threads", - "allow users with the role to delete, lock, or modify the title/tags for others' threads")}} + "allow users with the role to modify the title/tags for others' threads or lock it to prevent new posts")}} + {{perm("p_delete_posts","delete others' posts and threads", + "allow users with the role to delete others' posts and threads")}} {{perm("p_create_polls","create polls", - "allow users with the role to create poll threads")}} + "allow users with the role to add a poll to a thread")}} {{perm("p_vote","vote", - "allow users with the role to vote on poll threads")}} + "allow users with the role to vote in polls")}} {{perm("p_create_subforum","create subforæ", "allow users with the role to create subforæ in this forum. " + "they will automatically become a bureaucrat in this subforum.")}} diff --git a/apioforum/templates/role_assignment.html b/apioforum/templates/role_assignment.html new file mode 100644 index 0000000..d56c060 --- /dev/null +++ b/apioforum/templates/role_assignment.html @@ -0,0 +1,53 @@ +{% extends 'base.html' %} +{% block header %}<h1>{% block title %}configure user role in '{{forum.name}}'{% endblock %}</h1>{% endblock %} +{% block content %} +<p> + each user has a role in the forum. + here, a user may be assigned a role in the forum. + otherwise, the user's role is the same as the parent forum. + everyone's role is "other" by default. +</p> +{% if not is_bureaucrat(forum.id, g.user) %} + <p> + you are only allowed to approve members in this forum. + </p> +{% endif %} +<form method="post" action="{{url_for('forum.view_user_role',forum_id=forum.id)}}"> + <label for="user">role settings for user: </label> + <input type="text" class="name-input" id="user" name="user" value="{{user}}"/> + <input type="submit" value="view"/> +</form> + +{% if invalid_user %} + <p>requested user does not exist.</p> + <p> + <a href="{{url_for('forum.view_forum',forum_id=forum.id)}}">cancel</a> + </p> +{% elif user %} +<hr/> +<form method="post"> + <p>{{user}}'s role in this forum is "{{role}}"</p> + {% if role == "other" or is_bureaucrat(forum.id, g.user) %} + <label for="role">assign role: </label> + <select name="role" id="role" value=""> + <option value="">(no assigned role)</option> + {% for role in forum_roles %} + <option value="{{role}}">{{role}}</option> + {% endfor %} + </select> + {% else %} + <p>you do not have permission to change the role of this user</p> + {% endif %} + <p>confirm changes?</p> + <p> + <input type="submit" value="confirm"> + <a href="{{url_for('forum.view_forum',forum_id=forum.id)}}">cancel</a> + </p> +</form> +{% else %} +<p> + <a href="{{url_for('forum.view_forum',forum_id=forum.id)}}">cancel</a> +</p> +{% endif %} + +{% endblock %} diff --git a/apioforum/templates/view_forum.html b/apioforum/templates/view_forum.html index c5666c8..a3563be 100644 --- a/apioforum/templates/view_forum.html +++ b/apioforum/templates/view_forum.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% from 'common.html' import ts, tag, disp_user, post_url, forum_breadcrumb %} {% block header %} -<h1>{% block title %}{{forum.name}}{%endblock%}</h1> +<h1>{% block title %}{{forum.name}}{% endblock %} <span class="thing-id">#{{forum.id}}</span></h1> {% if forum.id != 1 %} {{ forum_breadcrumb(forum) }} {% endif %} @@ -9,11 +9,15 @@ {%block content%} {{forum.description|md|safe}} -{% if is_bureaucrat(forum.id, g.user) %} - <p><a class="actionbutton" href="{{url_for('forum.edit_roles',forum_id=forum.id)}}">role/permission settings</a></p> -{% endif %} -<hr/> - +<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> + {% 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> + {% endif %} +</p> {% if subforums %} <h2>subforæ</h2> <div class="forum-list"> @@ -67,7 +71,7 @@ {{ ts(thread.created) }} </div> </div> - {#{% if thread.mrp_id %}#} + {% if not thread.mrp_deleted %} <div class="listing-caption"> {{ disp_user(thread.mrp_author) }} <span class="thread-preview-ts"> @@ -79,7 +83,14 @@ </a> </span> </div> - {#{% endif %}#} + {% else %} + <div class="listing-caption"> + <a class="thread-preview-post" + href="{{url_for('thread.view_thread',thread_id=thread.id)}}#post_{{thread.mrp_id}}"> + latest post + </a> + </div> + {% endif %} </div> {%endfor%} </div> diff --git a/apioforum/templates/view_thread.html b/apioforum/templates/view_thread.html index dd41d87..da8df74 100644 --- a/apioforum/templates/view_thread.html +++ b/apioforum/templates/view_thread.html @@ -1,16 +1,19 @@ {% from 'common.html' import disp_post,tag,thread_breadcrumb %} {% extends 'base.html' %} {% block header %} -<h1>{%block title %}{{thread.title}}{% endblock %}</h1> +<h1>{%block title %}{{thread.title}}{% endblock %} <span class="thing-id">#{{thread.id}}</span></h1> {{ thread_breadcrumb(thread) }} {% endblock %} {%block content%} <div class="thread-top-bar"> <span class="thread-top-bar-a"> - {% if g.user == thread.creator %} + {% if g.user == thread.creator or has_permission(thread.forum, g.user, "p_manage_threads") %} <a class="actionbutton" href="{{url_for('thread.config_thread',thread_id=thread.id)}}">configure thread</a> {% endif %} + {% if has_permission(thread.forum, g.user, "p_delete_posts") %} + <a class="actionbutton" href="{{url_for('thread.delete_thread',thread_id=thread.id)}}">delete thread</a> + {% endif %} </span> <span class="thread-top-bar-b"> @@ -22,7 +25,7 @@ <div class="posts"> {% for post in posts %} - {{ disp_post(post, True) }} + {{ disp_post(post, buttons=True, forum=thread.forum) }} {% endfor %} </div> {% if g.user %} diff --git a/apioforum/thread.py b/apioforum/thread.py index 4bb3c86..1291adf 100644 --- a/apioforum/thread.py +++ b/apioforum/thread.py @@ -5,6 +5,7 @@ from flask import ( url_for, flash ) from .db import get_db +from .roles import has_permission bp = Blueprint("thread", __name__, url_prefix="/thread") @@ -17,66 +18,94 @@ def view_thread(thread_id): thread = db.execute("SELECT * FROM threads WHERE id = ?;",(thread_id,)).fetchone() if thread is None: abort(404) - else: - posts = db.execute( - "SELECT * FROM posts WHERE thread = ? ORDER BY created ASC;", - (thread_id,) - ).fetchall() - tags = db.execute( - """SELECT tags.* FROM tags - INNER JOIN thread_tags ON thread_tags.tag = tags.id - WHERE thread_tags.thread = ? - ORDER BY tags.id""",(thread_id,)).fetchall() - return render_template("view_thread.html",posts=posts,thread=thread,tags=tags) + if not has_permission(thread['forum'], g.user, "p_view_threads"): + abort(403) + posts = db.execute( + "SELECT * FROM posts WHERE thread = ? ORDER BY created ASC;", + (thread_id,) + ).fetchall() + tags = db.execute( + """SELECT tags.* FROM tags + INNER JOIN thread_tags ON thread_tags.tag = tags.id + WHERE thread_tags.thread = ? + ORDER BY tags.id""",(thread_id,)).fetchall() + return render_template("view_thread.html",posts=posts,thread=thread,tags=tags) @bp.route("/<int:thread_id>/create_post", methods=("POST",)) def create_post(thread_id): if g.user is None: flash("you need to log in before you can post") - return redirect(url_for('thread.view_thread',thread_id=thread_id)) + db = get_db() + content = request.form['content'] + thread = db.execute("SELECT * FROM threads WHERE id = ?;",(thread_id,)).fetchone() + if len(content.strip()) == 0: + flash("you cannot post an empty message") + elif not thread: + flash("that thread does not exist") + elif not has_permission(thread['forum'], g.user, "p_reply_threads"): + flash("you do not have permission to do this") + elif not has_permission(thread['forum'], g.user, "p_view_threads"): + flash("you do not have permission to do this") else: - db = get_db() - content = request.form['content'] - thread = db.execute("SELECT * FROM threads WHERE id = ?;",(thread_id,)).fetchone() - if len(content.strip()) == 0: - flash("you cannot post an empty message") - elif not thread: - flash("that thread does not exist") - else: - cur = db.cursor() - cur.execute( - "INSERT INTO posts (thread,author,content,created) VALUES (?,?,?,current_timestamp);", - (thread_id,g.user,content) - ) - post_id = cur.lastrowid - cur.execute( - "UPDATE threads SET updated = current_timestamp WHERE id = ?;", - (thread_id,) - ) - db.commit() - flash("post posted postfully") - return redirect(post_jump(thread_id, post_id)) + cur = db.cursor() + cur.execute( + "INSERT INTO posts (thread,author,content,created) VALUES (?,?,?,current_timestamp);", + (thread_id,g.user,content) + ) + post_id = cur.lastrowid + cur.execute( + "UPDATE threads SET updated = current_timestamp WHERE id = ?;", + (thread_id,) + ) + db.commit() + flash("post posted postfully") + return redirect(post_jump(thread_id, post_id)) + return redirect(url_for('thread.view_thread',thread_id=thread_id)) @bp.route("/delete_post/<int:post_id>", methods=["GET","POST"]) def delete_post(post_id): db = get_db() post = db.execute("SELECT * FROM posts WHERE id = ?",(post_id,)).fetchone() + thread = db.execute("SELECT * FROM threads WHERE id = ?",(post['thread'],)).fetchone() if post is None: flash("that post doesn't exist") return redirect("/") - if post['author'] != g.user: - flash("you can only delete posts that you created") + if post['author'] != g.user and not has_permission(thread['forum'], g.user, "p_delete_posts"): + flash("you do not have permission to do that") return redirect(url_for("thread.view_thread",thread_id=post["thread"])) if request.method == "POST": - # todo: don't actually delete, just mark as deleted or something (and wipe content) - # so that you can have a "this post was deleted" thing - db.execute("DELETE FROM posts WHERE id = ?",(post_id,)) + db.execute(""" + UPDATE posts SET + content = '', + deleted = 1 + WHERE id = ?""",(post_id,)) db.commit() flash("post deleted deletedly") return redirect(url_for("thread.view_thread",thread_id=post["thread"])) else: return render_template("delete_post.html",post=post) +@bp.route("/delete_thread/<int:thread_id>", methods=["GET","POST"]) +def delete_thread(thread_id): + db = get_db() + thread = db.execute("SELECT * FROM threads WHERE id = ?",(thread_id,)).fetchone() + if thread is None: + flash("that thread doesn't exist") + return redirect("/") + if not has_permission(thread['forum'], g.user, "p_delete_posts"): + flash("you do not have permission to do that") + return redirect(url_for("thread.view_thread",thread_id=post["thread"])) + if request.method == "POST": + db.execute("DELETE FROM posts WHERE thread = ?",(thread_id,)) + db.execute("DELETE FROM threads WHERE id = ?",(thread_id,)) + db.commit() + flash("thread deleted deletedly") + return redirect(url_for("forum.view_forum",forum_id=thread['forum'])) + else: + count = db.execute("SELECT num_replies FROM number_of_posts WHERE thread = ?", + (thread_id,)).fetchone()[0] + return render_template("delete_thread.html",thread=thread,post_count=count) + @bp.route("/edit_post/<int:post_id>",methods=["GET","POST"]) def edit_post(post_id): @@ -117,7 +146,7 @@ def config_thread(thread_id): err = None if g.user is None: err = "you need to be logged in to do that" - elif g.user != thread['creator']: + elif g.user != thread['creator'] and not has_permission(thread['forum'], g.user, "g_manage_threads"): err = "you can only configure threads that you own" if err is not None: |