diff options
-rw-r--r-- | apioforum/orm.py | 6 | ||||
-rw-r--r-- | apioforum/post.py | 33 | ||||
-rw-r--r-- | apioforum/templates/common.html | 27 | ||||
-rw-r--r-- | apioforum/thread.py | 99 |
4 files changed, 97 insertions, 68 deletions
diff --git a/apioforum/orm.py b/apioforum/orm.py index c87dbdd..97124c7 100644 --- a/apioforum/orm.py +++ b/apioforum/orm.py @@ -8,8 +8,9 @@ class DBObj: # DO NOT pass anything with sql special characters in as the table name super().__init_subclass__(**kwargs) cls.table_name = table + @classmethod - def fetch(cls,*,id): + def fetch(cls, *, id): """fetch an object from the database, looked up by id.""" db = get_db() # xxx this could be sped up by caching this query maybe instead of @@ -19,8 +20,9 @@ class DBObj: return None item = cls.from_row(row) return item + @classmethod - def from_row(cls,row): + def from_row(cls, row): # doesn't handle the ability to set fields yet # we will use something like properties instead # so this is somewhat bleh for now diff --git a/apioforum/post.py b/apioforum/post.py new file mode 100644 index 0000000..86753ad --- /dev/null +++ b/apioforum/post.py @@ -0,0 +1,33 @@ +# wow, a dedicated module +# + +from .orm import DBObj +from .thread import POSTS_PER_PAGE +from flask import url_for + +class Post(DBObj,table="posts"): + fields = ["id","content","thread","author","created","edited","updated","vote","deleted"] + + def which_page(self): + """ return what page of a thread the given post is on + + assumes post ids within a thread are monotonically increasing, which + is probably correct + """ + db = get_db() + amt_before = db.execute(""" + select count(*) as c from posts + where thread = ? and id < ?""", + (self.thread,self.id)).fetchone()['c'] + + page = 1+math.floor(amt_before/POSTS_PER_PAGE) + return page + + def jump_url(self,*,external=False): + page = self.which_page() + return url_for( + "thread.view_thread", + thread_id = self.thread, + page=page, + _external=external)+"#post"+str(self.id) + diff --git a/apioforum/templates/common.html b/apioforum/templates/common.html index fae4b7c..5677073 100644 --- a/apioforum/templates/common.html +++ b/apioforum/templates/common.html @@ -87,33 +87,6 @@ <a class="actionbutton" href="{{href}}">{{name}}</a> {%- endmacro %} -{% macro breadcrumb() %} -<nav aria-label="Breadcrumb"> -<ol class="breadcrumbs"> - {{- caller() -}} -</ol> -</nav> -{% endmacro %} - -{% macro forum_bc_entries(forum_id) -%} - {%- for f in forum_path(forum_id) -%} - <li><a href="{{url_for('forum.view_forum',forum_id=f.id)}}">{{ f.name }}</a></li> - {%- endfor %} -{%- endmacro %} - -{% macro forum_breadcrumb(forum) %} - {%- call breadcrumb() %} - {{ forum_bc_entries(forum.id) }} - {% endcall -%} -{% endmacro %} - -{% macro thread_breadcrumb(thread) %} - {%- call breadcrumb() %} - {{ forum_bc_entries(thread.forum) }} - <li>{{ thread.title }}</li> - {% endcall -%} -{% endmacro %} - {% macro vote_meter(poll) %} {% set total_votes = poll.total_votes %} {% set n = namespace() %} diff --git a/apioforum/thread.py b/apioforum/thread.py index a2ba6f5..f80786c 100644 --- a/apioforum/thread.py +++ b/apioforum/thread.py @@ -11,9 +11,34 @@ from .db import get_db from .roles import has_permission from . import webhooks from .forum import Forum +from .orm import DBObj -class Thread: +POSTS_PER_PAGE = 28 + +class Thread(DBObj,table="threads"): fields = ["id","title","creator","created","updated","forum","poll"] + + # maybe this should be on Post instead????? + @staticmethod + def which_page(post): + """ return what page of a thread the given post is on + + assumes post ids within a thread are monotonically increasing, which + is probably correct + """ + db = get_db() + amt_before = db.execute(""" + select count(*) as c from posts + where thread = ? and id < ?""", + (post.thread,post.id)).fetchone()['c'] + + page = 1+math.floor(amt_before/POSTS_PER_PAGE) + return page + + + + + @@ -29,17 +54,16 @@ def thread_route(relative_path, pagination=False, **kwargs): @bp.route(path, **kwargs) @functools.wraps(f) def wrapper(thread_id, *args, **kwargs): - thread = Thread.fetch(id=forum_id) + thread = Thread.fetch(id=thread_id) if thread == None: abort(404) - return f(forum, *args, **kwargs) + return f(thread, *args, **kwargs) if pagination: wrapper = bp.route(path+"/page/<int:page>", **kwargs)(wrapper) return decorator -POSTS_PER_PAGE = 28 def which_page(post_id,return_thread_id=False): # on which page lieth the post in question? @@ -69,8 +93,6 @@ def post_jump(post_id,*,external=False): page,thread_id=which_page(post_id,True) return url_for("thread.view_thread",thread_id=thread_id,page=page,_external=external)+"#post_"+str(post_id) -#@bp.route("/<int:thread_id>") -#@bp.route("/<int:thread_id>/page/<int:page>") @thread_route("",pagination=True) def view_thread(thread,page=1): if page < 1: @@ -91,14 +113,14 @@ def view_thread(thread,page=1): (page-1)*POSTS_PER_PAGE, )).fetchall() - num_posts = db.execute("SELECT count(*) as count FROM posts WHERE posts.thread = ?",(thread_id,)).fetchone()['count'] + num_posts = db.execute("SELECT count(*) as count FROM posts WHERE posts.thread = ?",(thread.id,)).fetchone()['count'] max_pageno = math.ceil(num_posts/POSTS_PER_PAGE) 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() + ORDER BY tags.id""",(thread.id,)).fetchall() poll = None votes = None if thread.poll is not None: @@ -158,12 +180,12 @@ def register_vote(thread,pollval): UPDATE votes SET current = 0 WHERE poll = ? AND user = ?; - """,(thread['poll'],g.user)) + """,(thread.poll,g.user)) cur.execute(""" INSERT INTO votes (user,poll,option_idx,time,current,is_retraction) VALUES (?,?,?,current_timestamp,1,?); - """,(g.user,thread['poll'],option_idx,is_retraction)) + """,(g.user,thread.poll,option_idx,is_retraction)) vote_id = cur.lastrowid return vote_id @@ -182,16 +204,16 @@ def create_poll(thread_id): err = "that thread does not exist" elif g.user is None: err = "you need to be logged in to do that" - elif g.user != thread['creator'] and \ - not has_permission(thread['forum'],g.user,"p_manage_threads"): + elif g.user != thread.creator and \ + not has_permission(thread.forum,g.user,"p_manage_threads"): err = "you can only create polls on threads that you own" - elif thread['poll'] is not None: + elif thread.poll is not None: err = "a poll already exists for that thread" elif not len(polltitle) > 0: err = "poll title can't be empty" elif len(polloptions) < 2: err = "you must provide at least 2 options" - elif not has_permission(thread['forum'], g.user, "p_create_polls"): + elif not has_permission(thread.forum, g.user, "p_create_polls"): err = "you do not have permission to do that" if err is not None: @@ -222,17 +244,17 @@ def delete_poll(thread_id): err = "that thread does not exist" elif g.user is None: err = "you need to be logged in to do that" - elif g.user != thread['creator'] and not \ - has_permission(thread['forum'], g.user, "p_manage_threads"): + elif g.user != thread.creator and not \ + has_permission(thread.forum, g.user, "p_manage_threads"): err = "you can only delete polls on threads that you own" - elif thread['poll'] is None: + elif thread.poll is None: err = "there is no poll to delete on this thread" if err is not None: flash(err) return fail else: - pollid = thread['poll'] + pollid = thread.poll db.execute("UPDATE posts SET vote = NULL WHERE thread = ?",(thread_id,)) # this assumes only max one poll per thread db.execute("DELETE FROM votes WHERE poll = ?",(pollid,)) db.execute("DELETE FROM poll_options WHERE poll = ?",(pollid,)) @@ -253,16 +275,16 @@ def create_post(thread_id): 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"): + 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"): + elif not has_permission(thread.forum, g.user, "p_view_threads"): flash("you do not have permission to do this") - elif not has_permission(thread['forum'], g.user, "p_vote") \ + elif not has_permission(thread.forum, g.user, "p_vote") \ and 'poll' in request.form: flash("you do not have permission to do this") else: vote_id = None - if thread['poll'] is not None: + if thread.poll is not None: pollval = request.form.get('poll') try: vote_id = register_vote(thread,pollval) @@ -282,7 +304,7 @@ def create_post(thread_id): ) db.commit() post = db.execute("select * from posts where id = ?",(post_id,)).fetchone() - webhooks.do_webhooks_post(thread['forum'],post) + webhooks.do_webhooks_post(thread.forum,post) flash("post posted postfully") return redirect(post_jump(post_id)) return redirect(url_for('thread.view_thread',thread_id=thread_id)) @@ -295,7 +317,7 @@ def delete_post(post_id): if post is None: flash("that post doesn't exist") return redirect("/") - if post['author'] != g.user and not has_permission(thread['forum'], g.user, "p_delete_posts"): + 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": @@ -317,7 +339,7 @@ def delete_thread(thread_id): if thread is None: flash("that thread doesn't exist") return redirect("/") - if not has_permission(thread['forum'], g.user, "p_delete_posts"): + 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=thread_id)) if request.method == "POST": @@ -325,7 +347,7 @@ def delete_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'])) + 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] @@ -380,33 +402,32 @@ def view_post(post_id): -@bp.route("/<int:thread_id>/config",methods=["GET","POST"]) -def config_thread(thread_id): +@thread_route("config",methods=["GET","POST"]) +def config_thread(thread): db = get_db() - thread = db.execute("select * from threads where id = ?",(thread_id,)).fetchone() - thread_tags = [r['tag'] for r in db.execute("select tag from thread_tags where thread = ?",(thread_id,)).fetchall()] - forum = Forum.fetch(id=thread['forum']) + thread_tags = [r['tag'] for r in db.execute("select tag from thread_tags where thread = ?",(thread.id,)).fetchall()] + forum = Forum.fetch(id=thread.forum) avail_tags = forum.avail_tags() err = None if g.user is None: err = "you need to be logged in to do that" - elif not has_permission(thread['forum'], g.user, "p_view_threads"): + elif not has_permission(thread.forum, g.user, "p_view_threads"): err = "you do not have permission to do that" - elif g.user != thread['creator'] and not has_permission(thread['forum'], g.user, "p_manage_threads"): + elif g.user != thread.creator and not has_permission(thread.forum, g.user, "p_manage_threads"): err = "you can only configure threads that you own" if err is not None: flash(err) - return redirect(url_for("thread.view_thread",thread_id=thread_id)) + return redirect(url_for("thread.view_thread",thread_id=thread.id)) if request.method == "POST": err = [] - if request.form['title'] != thread['title']: + if request.form['title'] != thread.title: title = request.form['title'] if len(title.strip()) == 0: err.append("title can't be empty") else: - db.execute("update threads set title = ? where id = ?;",(title,thread_id)) + db.execute("update threads set title = ? where id = ?;",(title,thread.id)) flash("title updated successfully") db.commit() changed = False @@ -415,10 +436,10 @@ def config_thread(thread_id): current = tagid in thread_tags wanted = f'tag_{tagid}' in request.form if wanted and not current: - db.execute("insert into thread_tags (thread, tag) values (?,?)",(thread_id,tagid)) + db.execute("insert into thread_tags (thread, tag) values (?,?)",(thread.id,tagid)) changed = True elif current and not wanted: - db.execute("delete from thread_tags where thread = ? and tag = ?",(thread_id,tagid)) + db.execute("delete from thread_tags where thread = ? and tag = ?",(thread.id,tagid)) changed = True if changed: db.commit() @@ -428,7 +449,7 @@ def config_thread(thread_id): for e in err: flash(e) else: - return redirect(url_for("thread.view_thread",thread_id=thread_id)) + return redirect(url_for("thread.view_thread",thread_id=thread.id)) return render_template("config_thread.html", thread=thread,thread_tags=thread_tags,avail_tags=avail_tags) |