diff options
-rw-r--r-- | apioforum/__init__.py | 3 | ||||
-rw-r--r-- | apioforum/auth.py | 5 | ||||
-rw-r--r-- | apioforum/db.py | 4 | ||||
-rw-r--r-- | apioforum/static/style.css | 21 | ||||
-rw-r--r-- | apioforum/templates/base.html | 10 | ||||
-rw-r--r-- | apioforum/templates/common.html | 7 | ||||
-rw-r--r-- | apioforum/templates/user_settings.html | 28 | ||||
-rw-r--r-- | apioforum/templates/view_forum.html | 6 | ||||
-rw-r--r-- | apioforum/templates/view_user.html | 37 | ||||
-rw-r--r-- | apioforum/user.py | 65 |
10 files changed, 171 insertions, 15 deletions
diff --git a/apioforum/__init__.py b/apioforum/__init__.py index 4283796..54d18c3 100644 --- a/apioforum/__init__.py +++ b/apioforum/__init__.py @@ -34,6 +34,9 @@ def create_app(): from . import admin app.register_blueprint(admin.bp) + from . import user + app.register_blueprint(user.bp) + from .fuzzy import fuzzy app.jinja_env.filters['fuzzy']=fuzzy diff --git a/apioforum/auth.py b/apioforum/auth.py index 80407eb..dae7b03 100644 --- a/apioforum/auth.py +++ b/apioforum/auth.py @@ -5,6 +5,7 @@ from flask import ( from werkzeug.security import check_password_hash, generate_password_hash from .db import get_db import functools +import datetime bp = Blueprint("auth", __name__, url_prefix="/auth") @@ -57,8 +58,8 @@ def register(): if err is None: db.execute( - "INSERT INTO users (username, password) VALUES (?,?);", - (username,generate_password_hash(password)) + "INSERT INTO users (username, password, joined) VALUES (?,?,?);", + (username,generate_password_hash(password),datetime.datetime.now()) ) db.commit() flash("successfully created account") diff --git a/apioforum/db.py b/apioforum/db.py index e1e8fa3..910118d 100644 --- a/apioforum/db.py +++ b/apioforum/db.py @@ -80,6 +80,10 @@ CREATE TABLE thread_tags ( """, """CREATE INDEX thread_tags_thread ON thread_tags (thread);""", """ALTER TABLE users ADD COLUMN admin INT NOT NULL DEFAULT 0""", +""" +ALTER TABLE users ADD COLUMN bio TEXT; +ALTER TABLE users ADD COLUMN joined TIMESTAMP; +""", ] def init_db(): diff --git a/apioforum/static/style.css b/apioforum/static/style.css index 935bde1..c4a6d40 100644 --- a/apioforum/static/style.css +++ b/apioforum/static/style.css @@ -1,4 +1,4 @@ -body { font-family: sans-serif } +body { font-family: sans-serif; word-wrap: break-word; } :root { --alternating-colour-even: hsl(0,0%,96%); @@ -16,9 +16,9 @@ body { font-family: sans-serif } } .post:last-of-type { border-bottom: 1px solid black; } -.post-heading { - color: hsl(0,0%,25%); - font-size: smaller; +.post-heading { font-size: smaller; } +.post-heading,.post-heading .username,.post-heading a:visited { + color: hsl(0,0%,25%); } .post-heading-em { font-weight: bold; } .post-content * { margin-bottom: 8px; margin-top: 8px; } @@ -30,7 +30,7 @@ body { font-family: sans-serif } .post-anchor-link { color: hsl(0,0%,25%); } -.thread-top-bar { +.thread-top-bar, .user-top-bar { margin-bottom: 4px; } @@ -43,6 +43,17 @@ body { font-family: sans-serif } font-size: .9rem; } +.user_info { + border: 1px solid black; + background-color: var(--alternating-colour-even); + width: 100%; + padding: 4px; +} +.user_bio_quote { width: max-content; max-width: 100% } +.user_bio_attribution { text-align: right; font-style: italic; } + +dt { font-weight: bold } + .un-col-1 { color: hsl(0, 100%, 30%) } .un-col-2 { color: hsl(22.5, 100%, 30%) } diff --git a/apioforum/templates/base.html b/apioforum/templates/base.html index bf3748f..3eb112e 100644 --- a/apioforum/templates/base.html +++ b/apioforum/templates/base.html @@ -19,25 +19,25 @@ <p><a href="{{url_for('index')}}">home</a></p> {% if g.user %} - <p>{{ g.user }}</p> + <p><a href="{{url_for('user.view_user', username=g.user)}}">{{g.user}}</a></p> {% if is_admin %} <p><a href="{{url_for('admin.admin_page')}}">admin</a></p> {% endif %} <p> - <a href="{{ url_for('auth.logout',next=path_for_next) }}"> + <a href="{{url_for('auth.logout',next=path_for_next)}}"> logout </a> </p> {% else %} <p> - <a href="{{ url_for('auth.login',next=path_for_next) }}"> + <a href="{{url_for('auth.login',next=path_for_next)}}"> login </a> </p> <p> - <a href="{{ url_for('auth.register',next=path_for_next) }}"> + <a href="{{url_for('auth.register',next=path_for_next)}}"> register </a> </p> @@ -60,6 +60,8 @@ </main> {% endblock %} <script>/* bees */</script> + <!-- citrons was here --> + <!-- Complete hybridisation of various species of wild duck gene pools could result in the extinction of many indigenous waterfowl. --> </body> </html> diff --git a/apioforum/templates/common.html b/apioforum/templates/common.html index 2e59b2c..c484a9d 100644 --- a/apioforum/templates/common.html +++ b/apioforum/templates/common.html @@ -1,8 +1,13 @@ +{% macro disp_user(username) -%} +<a href="{{url_for('user.view_user',username=username)}}" class="username">{{username}}</a> +{%- endmacro %} + {% macro disp_post(post, buttons=False) %} <div class="post" id="post_{{post.id}}"> <div class="post-heading"> <span class="post-heading-a"> - <span class="post-heading-em">{{post.author}}</span> {{ts(post.created)}} + <span class="post-heading-em">{{disp_user(post.author)}}</span> + {{ts(post.created)}} {% if post.edited %} (edited {{ts(post.updated)}}) {% endif %} diff --git a/apioforum/templates/user_settings.html b/apioforum/templates/user_settings.html new file mode 100644 index 0000000..ad93036 --- /dev/null +++ b/apioforum/templates/user_settings.html @@ -0,0 +1,28 @@ +{% extends 'base.html' %} +{% block header %}<h1>{% block title %}user settings{% endblock %}</h1>{% endblock %} +{% block content %} +<form method="post"> +<fieldset> +<legend>change password</legend> +<p>if you want to change your password, make sure you check the "change password?" box.</p> +<label for="do_chpass">change password?</label> +<input type="checkbox" id="do_chpass" name="do_chpass"><br> +<label for="password">current password</label> +<input type="text" id="password" name="password"><br> +<label for="new_password">new password</label> +<input type="text" id="new_password" name="new_password"> +</fieldset> +<fieldset> +<legend>change bio</legend> +<p>if you want to change your bio, make sure you check the "change bio?" box.</p> +<label for="do_chbio">change bio?</label> +<input type="checkbox" name="do_chbio" id="do_chbio"><br> +<textarea class="new-post-box" name="bio" maxlength="4000"> + {{- user.bio or "hail GEORGE" -}} +</textarea> +</fieldset> +<p>confirm changes?</p> +<input type="submit" value="confirm"> +<a href="{{url_for('user.view_user',username=user.username)}}">cancel</a> +</form> +{% endblock %} diff --git a/apioforum/templates/view_forum.html b/apioforum/templates/view_forum.html index 3edb7f0..59c594b 100644 --- a/apioforum/templates/view_forum.html +++ b/apioforum/templates/view_forum.html @@ -1,5 +1,5 @@ {% extends 'base.html' %} -{% from 'common.html' import ts, tag %} +{% from 'common.html' import ts, tag, disp_user %} {% block header %}<h1>{% block title %}apioforum{%endblock%}</h1>{%endblock%} {%block nmcontent%} <main class="widemain"> @@ -50,10 +50,10 @@ {{tag(the_tag)}} {% endfor %} </div> - <div class="threadlisting-part threadlisting-part-creator">{{thread.creator}}</div> + <div class="threadlisting-part threadlisting-part-creator">{{disp_user(thread.creator)}}</div> <div class="threadlisting-part threadlisting-part-created">{{ts(thread.created)}}</div> <div class="threadlisting-part threadlisting-part-updated">{{ts(thread.updated)}}</div> - <div class="threadlisting-part threadlisting-part-lastactivityby">{{thread.last_user}}</div> + <div class="threadlisting-part threadlisting-part-lastactivityby">{{disp_user(thread.last_user)}}</div> <div class="threadlisting-part threadlisting-part-numreplies">{{thread.num_replies}}</div> </div> {%endfor%} diff --git a/apioforum/templates/view_user.html b/apioforum/templates/view_user.html new file mode 100644 index 0000000..f773978 --- /dev/null +++ b/apioforum/templates/view_user.html @@ -0,0 +1,37 @@ +{% from 'common.html' import disp_post,ts %} +{% extends 'base.html' %} +{% block header %} +<h1>{%block title %}{{user.username|e}}{% endblock %}</h1> +{% endblock %} + +{%block content%} +<div class="user-top-bar"> + {% if g.user == user.username %} + <a class="actionbutton" href="{{url_for('user.edit_user',username=user.username)}}">settings</a> + {% endif %} +</div> +<div class="user_info"> + <div class="user_bio_quote"> + <div class="user_bio">{{rendered_bio|safe}}</div> + <p class="user_bio_attribution">— {{user.username|e}}</p> + </div> + <dl> + <dt>joined</dt> + {% if user.joined %} + <dd>{{ts(user.joined)}} ago</dd> + {% else %} + <dd>a very long time ago</dd> + {% endif %} + </dl> +</div> +{% if posts %} + <h2>recent posts</h2> + <div class="user_posts"> + {% for post in posts %} + {% call disp_post(post, False) %} + {{ rendered_posts[loop.index0] | safe}} + {% endcall %} + {% endfor %} + </div> +{% endif %} +{% endblock %} diff --git a/apioforum/user.py b/apioforum/user.py new file mode 100644 index 0000000..c4a6998 --- /dev/null +++ b/apioforum/user.py @@ -0,0 +1,65 @@ +# user pages + +from flask import ( + Blueprint, render_template, abort, g, flash, redirect, url_for, request +) + +from werkzeug.security import check_password_hash, generate_password_hash +from .db import get_db +from .mdrender import render + +bp = Blueprint("user", __name__, url_prefix="/user") + + +@bp.route("/<username>") +def view_user(username): + db = get_db() + user = db.execute("SELECT * FROM users WHERE username = ?;",(username,)).fetchone() + if user is None: + abort(404) + posts = db.execute( + "SELECT * FROM posts WHERE author = ? ORDER BY created DESC LIMIT 25;",(username,)).fetchall() + rendered_posts = [render(post['content']) for post in posts] + return render_template("view_user.html", + user=user, + rendered_bio=render(user['bio'] or "hail GEORGE"), + posts=posts, + rendered_posts=rendered_posts) + +@bp.route("/<username>/edit", methods=["GET","POST"]) +def edit_user(username): + db = get_db() + user = db.execute("SELECT * FROM users WHERE username = ?;",(username,)).fetchone() + if user is None: + abort(404) + if username != g.user: + flash("you cannot modify other people") + return redirect(url_for("user.view_user",username=username)) + + if request.method == "POST": + err = [] + if 'do_chpass' in request.form: + if not check_password_hash(user['password'],request.form['password']): + err.append("entered password does not match current password") + else: + db.execute("update users set password = ? where username = ?", + (generate_password_hash(request.form["new_password"]), username)) + db.commit() + flash("password changed changefully") + if 'do_chbio' in request.form: + if len(request.form['bio'].strip()) == 0: + err.append("please submit nonempty bio") + elif len(request.form['bio']) > 4500: + err.append("bio is too long!!") + else: + db.execute("update users set bio = ? where username = ?", (request.form['bio'], username)) + db.commit() + flash("bio updated successfully") + + if len(err) > 0: + for e in err: + flash(e) + else: + return redirect(url_for("user.view_user",username=username)) + + return render_template("user_settings.html",user=user) |