diff --git a/app/models.py b/app/models.py index f7bda98..731c576 100644 --- a/app/models.py +++ b/app/models.py @@ -54,6 +54,14 @@ class Users(Model): q = f'{Posts.FULL_POSTS_QUERY} WHERE posts.user_id = ? ORDER BY posts.created_at DESC LIMIT ? OFFSET ?' return db.query(q, self.id, per_page, (page - 1) * per_page) + def get_started_threads(self, per_page: int, page: int) -> list: + q = f"""{Posts.FULL_POSTS_QUERY} WHERE threads.user_id = ? AND posts.created_at = ( + SELECT MIN(p2.created_at) + FROM posts p2 + WHERE p2.thread_id = posts.thread_id + ) ORDER BY threads.created_at DESC LIMIT ? OFFSET ?""" + return db.query(q, self.id, per_page, (page - 1) * per_page) + def get_all_subscriptions(self): q = """ SELECT threads.title AS thread_title, threads.slug AS thread_slug @@ -289,6 +297,7 @@ class Posts(Model): users.id AS user_id, post_history.original_markup, users.signature_rendered, threads.slug AS thread_slug, threads.is_locked AS thread_is_locked, threads.title AS thread_title, + topics.id AS topic_id, topics.name AS topic_name, COALESCE(user_badges.badges_json, '[]') AS badges_json FROM posts @@ -296,6 +305,8 @@ class Posts(Model): post_history ON posts.current_revision_id = post_history.id JOIN users ON posts.user_id = users.id + JOIN + topics ON threads.topic_id = topics.id JOIN threads ON posts.thread_id = threads.id LEFT JOIN diff --git a/app/routes/users.py b/app/routes/users.py index 29a3af4..453b006 100644 --- a/app/routes/users.py +++ b/app/routes/users.py @@ -7,7 +7,7 @@ from ..auth import ( is_logged_in, parse_username, is_password_valid, login_required ) -from ..models import Users, Posts, Reactions +from ..models import Users, Posts, Reactions, Threads from ..constants import PermissionLevel from secrets import compare_digest as compare_timesafe import math @@ -117,6 +117,8 @@ def posts(username): if username == 'deleteduser': abort(404) target_user = Users.find({'username': username}) + if not target_user: + abort(404) PER_PAGE = 10 posts_count = Posts.count({'user_id': target_user.id}) page_count = max(1, math.ceil(posts_count / PER_PAGE)) @@ -137,7 +139,23 @@ def threads(username): username = username.lower() if username == 'deleteduser': abort(404) - return 'stub' + target_user = Users.find({'username': username}) + if not target_user: + abort(404) + PER_PAGE = 10 + threads_count = Threads.count({'user_id': target_user.id}) + page_count = max(1, math.ceil(threads_count / PER_PAGE)) + page = 1 + try: + page = max(1, min(int(request.args.get('page', default=1)), page_count)) + except ValueError: + abort(404) + threads = target_user.get_started_threads(PER_PAGE, page) + return render_template( + 'users/threads.html', threads=threads, + page=page, page_count=page_count, + target_user=target_user, Reactions=Reactions + ) @bp.get('//comments/') def comments(username): diff --git a/app/templates/common/macros.html b/app/templates/common/macros.html index 612578e..ba6386b 100644 --- a/app/templates/common/macros.html +++ b/app/templates/common/macros.html @@ -113,7 +113,7 @@ {% macro full_post( post, render_sig=true, is_latest=false, show_toolbar=true, is_editing=false, thread=none, - show_reactions=true, show_thread=false + show_reactions=true, show_thread=false, allow_reacting=true ) -%} {%- if is_logged_in() -%} {%- set can_delete = post.user_id == get_active_user().id or is_mod() -%} @@ -190,10 +190,10 @@ {% set reactors_str = reactors_str + '\n...and many others' %} {% endif %} {% set has_reacted = get_active_user() is not none and get_active_user().username in reactors %} - + {%- endfor -%} - {%- if is_logged_in() -%}{%- endif -%} + {%- if is_logged_in() and allow_reacting -%}{%- endif -%} {%- elif is_editing -%} Cancel diff --git a/app/templates/threads/thread.html b/app/templates/threads/thread.html index 0086528..63c9b44 100644 --- a/app/templates/threads/thread.html +++ b/app/templates/threads/thread.html @@ -74,6 +74,14 @@ {{- pager(page, page_count) -}} + {%- if is_logged_in() and get_active_user().can_post_to_thread_or_topic(thread) -%}

Reply to "{{thread.title}}"

diff --git a/app/templates/users/posts.html b/app/templates/users/posts.html index 9c874c1..5fa0733 100644 --- a/app/templates/users/posts.html +++ b/app/templates/users/posts.html @@ -4,19 +4,19 @@ {%- block title -%}{{ target_user.get_readable_name() }}'s posts{%- endblock -%} {%- block content -%} {%- set td -%} - Back to profile + ← Back to profile +{%- endset -%} +{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%} {%- if posts -%}
Page {{- pager(page, page_count) -}}
{%- endif -%} -{%- endset -%} -{%- call() subheader("%s's posts" % target_user.get_readable_name(), td) -%} {%- endcall -%} {%- if posts -%} {%- for post in posts -%} -
{{full_post(post, show_toolbar=false, show_thread=true)}}
+
{{full_post(post, show_toolbar=false, show_thread=true, allow_reacting=false)}}
{%- endfor -%}
diff --git a/app/templates/users/threads.html b/app/templates/users/threads.html new file mode 100644 index 0000000..e0ea0ec --- /dev/null +++ b/app/templates/users/threads.html @@ -0,0 +1,33 @@ +{%- from 'common/macros.html' import full_post with context -%} +{%- from 'common/macros.html' import subheader, pager -%} +{%- extends 'base.html' -%} +{%- block title -%}threads by {{ target_user.get_readable_name() }}{%- endblock -%} +{%- block content -%} +{%- set td -%} + ← Back to profile +{%- endset -%} +{%- call() subheader("%s's started threads" % target_user.get_readable_name(), td) -%} + {%- if threads -%} +
+ Page + {{- pager(page, page_count) -}} +
+ {%- endif -%} +{%- endcall -%} +{%- if threads -%} + {%- for post in threads -%} +
+

"{{post.thread_title}}" in topic {{post.topic_name}}

+
{{full_post(post, show_toolbar=false, allow_reacting=false)}}
+
+ {%- endfor -%} +
+
+ Page + {{- pager(page, page_count) -}} +
+
+{%- else -%} +
{{target_user.get_readable_name()}} has not started any threads.
+{%- endif -%} +{%- endblock -%} diff --git a/data/static/css/style.css b/data/static/css/style.css index 47d3254..e44a408 100644 --- a/data/static/css/style.css +++ b/data/static/css/style.css @@ -301,7 +301,7 @@ a.site-title { } .info { - margin: 0; + margin: var(--base-padding) 0; font-size: 1.5em; font-weight: bold; }