aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--apioforum/__init__.py3
-rw-r--r--apioforum/auth.py5
-rw-r--r--apioforum/db.py4
-rw-r--r--apioforum/static/style.css21
-rw-r--r--apioforum/templates/base.html10
-rw-r--r--apioforum/templates/common.html7
-rw-r--r--apioforum/templates/user_settings.html28
-rw-r--r--apioforum/templates/view_forum.html6
-rw-r--r--apioforum/templates/view_user.html37
-rw-r--r--apioforum/user.py65
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)