summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-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: