aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorcitrons <citrons>2021-08-05 11:28:53 +0000
committercitrons <citrons>2021-08-05 11:28:53 +0000
commit76cb3a6be912e55ffe7f6e7c221000f57cff6d4a (patch)
tree7b3f47687d9b41e112c15055ea71a279a322ff60
parentfd04b1ea444b2b77cb56ed7a67b8ac2225cfa6bd (diff)
make many of the permissions do things. somewhat functional menus for role configuration and assignment. big brother thoughtcrime message deletion. mark roles next to usernames on posts. other things I may have forgot
-rw-r--r--apioforum/__init__.py7
-rw-r--r--apioforum/db.py4
-rw-r--r--apioforum/forum.py98
-rw-r--r--apioforum/roles.py2
-rw-r--r--apioforum/static/style.css38
-rw-r--r--apioforum/templates/common.html45
-rw-r--r--apioforum/templates/delete_thread.html18
-rw-r--r--apioforum/templates/edit_permissions.html9
-rw-r--r--apioforum/templates/role_assignment.html53
-rw-r--r--apioforum/templates/view_forum.html27
-rw-r--r--apioforum/templates/view_thread.html9
-rw-r--r--apioforum/thread.py107
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>
&nbsp;
<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: