diff options
Diffstat (limited to 'apioforum/forum.py')
-rw-r--r-- | apioforum/forum.py | 194 |
1 files changed, 87 insertions, 107 deletions
diff --git a/apioforum/forum.py b/apioforum/forum.py index 988c9a5..f88900f 100644 --- a/apioforum/forum.py +++ b/apioforum/forum.py @@ -8,6 +8,7 @@ from flask import ( ) from .db import get_db +from .orm import DBObj from .mdrender import render from .roles import get_forum_roles,has_permission,is_bureaucrat,get_user_role, permissions as role_permissions from .permissions import is_admin @@ -16,6 +17,15 @@ import datetime import math import functools +class Forum(DBObj,table="forums"): + fields = ["id","name","parent","description","unlisted"] + + def avail_tags(self): + db = get_db() + tags = db.execute("select * from tags where forum = ?",(self.id,)).fetchall() + return tags + + THREADS_PER_PAGE = 35 bp = Blueprint("forum", __name__, url_prefix="/") @@ -24,30 +34,6 @@ bp = Blueprint("forum", __name__, url_prefix="/") def not_actual_index(): return redirect("/1") -def get_avail_tags(forum_id): - db = get_db() - tags = db.execute(""" - WITH RECURSIVE fs AS - (SELECT * FROM forums WHERE id = ? - UNION ALL - SELECT forums.* FROM forums, fs WHERE fs.parent=forums.id) - SELECT * FROM tags - WHERE tags.forum in (SELECT id FROM fs) - ORDER BY id; - """,(forum_id,)).fetchall() - return tags - -def forum_path(forum_id): - db = get_db() - ancestors = db.execute(""" - WITH RECURSIVE fs AS - (SELECT * FROM forums WHERE id = ? - UNION ALL - SELECT forums.* FROM forums, fs WHERE fs.parent=forums.id) - SELECT * FROM fs; - """,(forum_id,)).fetchall() - ancestors.reverse() - return ancestors def forum_route(relative_path, pagination=False, **kwargs): def decorator(f): @@ -58,9 +44,7 @@ def forum_route(relative_path, pagination=False, **kwargs): @bp.route(path, **kwargs) @functools.wraps(f) def wrapper(forum_id, *args, **kwargs): - db = get_db() - forum = db.execute("SELECT * FROM forums WHERE id = ?", - (forum_id,)).fetchone() + forum = Forum.fetch(id=forum_id) if forum == None: abort(404) return f(forum, *args, **kwargs) @@ -74,7 +58,7 @@ def requires_permission(permission, login_required=True): def decorator(f): @functools.wraps(f) def wrapper(forum, *args, **kwargs): - if not has_permission(forum['id'],g.user,permission,login_required): + if not has_permission(forum.id,g.user,permission,login_required): abort(403) return f(forum, *args, **kwargs) return wrapper @@ -84,7 +68,7 @@ def requires_bureaucrat(f): @functools.wraps(f) @requires_permission("p_view_forum") def wrapper(forum, *args, **kwargs): - if not is_bureaucrat(forum['id'], g.user): + if not is_bureaucrat(forum.id, g.user): abort(403) return f(forum, *args, **kwargs) return wrapper @@ -102,9 +86,9 @@ def view_forum(forum,page=1): sortby_dir = {'d':'DESC','a':'ASC'}[sortby[1]] sortby_by = {'a':'threads.updated','c':'threads.created'}[sortby[0]] except KeyError: - return redirect(url_for('forum.view_forum',forum_id=forum['id'])) + return redirect(url_for('forum.view_forum',forum_id=forum.id)) - avail_tags = get_avail_tags(forum['id']) + avail_tags = forum.avail_tags() tagfilter = request.args.get("tagfilter",None) if tagfilter == "": @@ -116,7 +100,7 @@ def view_forum(forum,page=1): tagfilter = int(tagfilter) except ValueError: flash(f'invalid tag id "{tagfilter}"') - return redirect(url_for('forum.view_forum',forum_id=forum['id'])) + abort(400) else: # there is no risk of sql injection because # we just checked it is an int @@ -127,7 +111,7 @@ def view_forum(forum,page=1): break else: flash("that tag doesn't exist or isn't available here") - return redirect(url_for('forum.view_forum',forum_id=forum['id'])) + abort(400) threads = db.execute( @@ -148,7 +132,7 @@ def view_forum(forum,page=1): ORDER BY {sortby_by} {sortby_dir} LIMIT ? OFFSET ?; """,( - forum['id'], + forum.id, THREADS_PER_PAGE, (page-1)*THREADS_PER_PAGE, )).fetchall() @@ -157,15 +141,13 @@ def view_forum(forum,page=1): SELECT count(*) AS count FROM threads LEFT OUTER JOIN thread_tags ON threads.id = thread_tags.thread WHERE threads.forum = ? {tagfilter_clause}; - """,(forum['id'],)).fetchone()['count'] + """,(forum.id,)).fetchone()['count'] max_pageno = math.ceil(num_threads/THREADS_PER_PAGE) thread_tags = {} thread_polls = {} - - #todo: somehow optimise this for thread in threads: thread_tags[thread['id']] = db.execute( """SELECT tags.* FROM tags @@ -197,13 +179,17 @@ def view_forum(forum,page=1): thread_polls[thread['id']]=poll - subforums_rows = db.execute(""" - SELECT max(threads.updated) as updated, forums.* FROM forums - LEFT OUTER JOIN threads ON threads.forum=forums.id - WHERE parent = ? AND unlisted = 0 - GROUP BY forums.id - ORDER BY name ASC - """,(forum['id'],)).fetchall() + # subforums don't exist any more + # forums will be able to link to other forums though, eventually + # + subforums_rows = [] + # subforums_rows = db.execute(""" + # SELECT max(threads.updated) as updated, forums.* FROM forums + # LEFT OUTER JOIN threads ON threads.forum=forums.id + # WHERE parent = ? AND unlisted = 0 + # GROUP BY forums.id + # ORDER BY name ASC + # """,(forum.id,)).fetchall() subforums = [] for s in subforums_rows: a={} @@ -216,7 +202,7 @@ def view_forum(forum,page=1): bureaucrats = db.execute(""" SELECT user FROM role_assignments WHERE role = 'bureaucrat' AND forum = ? - """,(forum['id'],)).fetchall() + """,(forum.id,)).fetchall() bureaucrats = [b[0] for b in bureaucrats] return render_template("view_forum.html", @@ -226,7 +212,6 @@ def view_forum(forum,page=1): thread_tags=thread_tags, bureaucrats=bureaucrats, thread_polls=thread_polls, - avail_tags=avail_tags, max_pageno=max_pageno, page=page, current_sortby=sortby, @@ -238,43 +223,38 @@ def view_forum(forum,page=1): @requires_permission("p_view_forum") def create_thread(forum): db = get_db() - forum = db.execute("SELECT * FROM forums WHERE id = ?",(forum['id'],)).fetchone() - if forum is None: - flash("that forum doesn't exist") - return redirect(url_for('index')) + + # i want to immortalize this + #forum = db.execute("SELECT * FROM forums WHERE id = ?",(forum.id,)).fetchone() + if g.user is None: - flash("you need to be logged in to create a thread") - return redirect(url_for('index')) + abort(403,"you need to be logged in to create a thread") if request.method == "POST": title = request.form['title'] content = request.form['content'] - err = None if len(title.strip()) == 0 or len(content.strip()) == 0: - err = "title and content can't be empty" - - if err is None: - cur = db.cursor() - cur.execute( - "INSERT INTO threads (title,creator,created,updated,forum) VALUES (?,?,current_timestamp,current_timestamp,?);", - (title,g.user,forum['id']) - ) - thread_id = cur.lastrowid - cur.execute( - "INSERT INTO posts (thread,created,author,content) VALUES (?,current_timestamp,?,?);", - (thread_id,g.user,content) - ) - db.commit() + abort(400,"title and content can't be empty") + + cur = db.cursor() + cur.execute( + "INSERT INTO threads (title,creator,created,updated,forum) VALUES (?,?,current_timestamp,current_timestamp,?);", + (title,g.user,forum.id) + ) + thread_id = cur.lastrowid + cur.execute( + "INSERT INTO posts (thread,created,author,content) VALUES (?,current_timestamp,?,?);", + (thread_id,g.user,content) + ) + db.commit() - from . import webhooks - thread = db.execute("select * from threads where id = ?",(thread_id,)).fetchone() - webhooks.do_webhooks_thread(forum['id'],thread) - return redirect(url_for('thread.view_thread',thread_id=thread_id)) - flash(err) - - - return render_template("create_thread.html") + from . import webhooks + thread = db.execute("select * from threads where id = ?",(thread_id,)).fetchone() + webhooks.do_webhooks_thread(forum.id,thread) + return redirect(url_for('thread.view_thread',thread_id=thread_id)) + else: + return render_template("create_thread.html") @forum_route("roles",methods=("GET","POST")) @requires_bureaucrat @@ -282,14 +262,14 @@ def edit_roles(forum): db = get_db() 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: if 'delete_' + config['role'] in request.form: db.execute( "DELETE FROM role_config WHERE forum = ? AND role = ?", - (forum['id'],config['role'])) + (forum.id,config['role'])) elif 'roleconfig_' + config['role'] in request.form: for p in role_permissions: permission_setting =\ @@ -298,13 +278,13 @@ def edit_roles(forum): 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, @@ -319,28 +299,28 @@ def add_role(forum): 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'])) + username=request.form['user'],forum_id=forum.id)) else: return render_template("role_assignment.html",forum=forum) @@ -352,27 +332,27 @@ def edit_user_role(forum, username): 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'])) + username=username,forum_id=forum.id)) role = request.form['role'] - if role not in get_forum_roles(forum['id']) and role != "" and role != "bureaucrat": + 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 != "": + 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 = ? AND forum = ?;", - (username,forum['id'])).fetchone() + (username,forum.id)).fetchone() if existing: - db.execute("DELETE FROM role_assignments WHERE user = ? AND forum = ?;",(username,forum['id'])) + 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'])) + (username,role,forum.id)) db.commit() flash("role assigned assignedly") - return redirect(url_for('forum.view_forum',forum_id=forum['id'])) + 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: @@ -380,14 +360,14 @@ def edit_user_role(forum, username): forum=forum,user=username,invalid_user=True) r = db.execute( "SELECT role FROM role_assignments WHERE user = ? AND forum = ?;", - (username,forum['id'])).fetchone() + (username,forum.id)).fetchone() if not r: assigned_role = "" else: assigned_role = r[0] - role = get_user_role(forum['id'], username) - if is_bureaucrat(forum['id'], g.user): - roles = get_forum_roles(forum['id']) + 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: @@ -403,19 +383,19 @@ def forum_config_page(forum, create=False): desc = request.form["description"] if len(name) > 100 or len(name.strip()) == 0: flash("invalid name") - return redirect(url_for('forum.edit_forum',forum_id=forum['id'])) + 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'])) + return redirect(url_for('forum.edit_forum',forum_id=forum.id)) if not create: db.execute("UPDATE forums SET name = ?, description = ? WHERE id = ?", - (name,desc,forum['id'])) - fid = forum['id'] + (name,desc,forum.id)) + fid = forum.id else: cur = db.cursor() cur.execute( "INSERT INTO forums (name,description,parent) VALUES (?,?,?)", - (name,desc,forum['id'])) + (name,desc,forum.id)) new = cur.lastrowid # creator becomes bureaucrat of new forum db.execute("INSERT INTO role_assignments (role,user,forum) VALUES (?,?,?)", @@ -428,9 +408,9 @@ def forum_config_page(forum, create=False): name = "" desc = "" else: - name = forum['name'] - desc = forum['description'] - cancel_link = url_for('forum.view_forum',forum_id=forum['id']) + 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) @@ -449,7 +429,7 @@ def create_forum(forum): # if not is_admin: abort(403) # why doesn't this fucking work # db = get_db() # unlisted = db.execute( -# "SELECT * FROM forums WHERE unlisted = 1 AND parent = ?",(forum['id'],)) +# "SELECT * FROM forums WHERE unlisted = 1 AND parent = ?",(forum.id,)) # return render_template('view_unlisted.html',forum=forum,unlisted=unlisted) @bp.route("/search") |