add user settings
This commit is contained in:
parent
bd556d102b
commit
dde1139eed
@ -57,6 +57,9 @@ def create_app():
|
|||||||
|
|
||||||
app.config["SECRET_KEY"] = os.getenv("FLASK_SECRET_KEY")
|
app.config["SECRET_KEY"] = os.getenv("FLASK_SECRET_KEY")
|
||||||
|
|
||||||
|
app.config['AVATAR_UPLOAD_PATH'] = 'data/static/avatars/'
|
||||||
|
app.config['MAX_CONTENT_LENGTH'] = 1000 * 1000
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(app.config["DB_PATH"]), exist_ok = True)
|
os.makedirs(os.path.dirname(app.config["DB_PATH"]), exist_ok = True)
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
from .schema import create as create_tables
|
from .schema import create as create_tables
|
||||||
|
@ -7,6 +7,9 @@ class Users(Model):
|
|||||||
def get_avatar_url(self):
|
def get_avatar_url(self):
|
||||||
return Avatars.find({"id": self.avatar_id}).file_path
|
return Avatars.find({"id": self.avatar_id}).file_path
|
||||||
|
|
||||||
|
def is_default_avatar(self):
|
||||||
|
return int(Avatars.find({'id': self.avatar_id}).id) == 1
|
||||||
|
|
||||||
def is_guest(self):
|
def is_guest(self):
|
||||||
return self.permission == PermissionLevel.GUEST.value
|
return self.permission == PermissionLevel.GUEST.value
|
||||||
|
|
||||||
|
@ -3,16 +3,50 @@ from flask import (
|
|||||||
)
|
)
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from ..db import db
|
from ..db import db
|
||||||
from ..models import Users, Sessions, Subscriptions
|
from ..lib.babycode import babycode_to_html
|
||||||
|
from ..models import Users, Sessions, Subscriptions, Avatars
|
||||||
from ..constants import InfoboxKind, PermissionLevel
|
from ..constants import InfoboxKind, PermissionLevel
|
||||||
from ..auth import digest, verify
|
from ..auth import digest, verify
|
||||||
|
from wand.image import Image
|
||||||
|
from wand.exceptions import WandException
|
||||||
import secrets
|
import secrets
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
|
||||||
bp = Blueprint("users", __name__, url_prefix = "/users/")
|
bp = Blueprint("users", __name__, url_prefix = "/users/")
|
||||||
|
|
||||||
|
|
||||||
|
def validate_and_create_avatar(input_image, filename):
|
||||||
|
try:
|
||||||
|
with Image(blob=input_image) as img:
|
||||||
|
img.strip()
|
||||||
|
img.gravity = 'center'
|
||||||
|
|
||||||
|
width, height = img.width, img.height
|
||||||
|
min_dim = min(width, height)
|
||||||
|
if min_dim > 256:
|
||||||
|
ratio = 256.0 / min_dim
|
||||||
|
new_width = int(width * ratio)
|
||||||
|
new_height = int(height * ratio)
|
||||||
|
img.resize(new_width, new_height)
|
||||||
|
|
||||||
|
width, height = img.width, img.height
|
||||||
|
crop_size = min(width, height)
|
||||||
|
x_offset = (width - crop_size) // 2
|
||||||
|
y_offset = (height - crop_size) // 2
|
||||||
|
img.crop(left=x_offset, top=y_offset,
|
||||||
|
width=crop_size, height=crop_size)
|
||||||
|
|
||||||
|
img.resize(256, 256)
|
||||||
|
img.format = 'webp'
|
||||||
|
img.compression_quality = 85
|
||||||
|
img.save(filename=filename)
|
||||||
|
return True
|
||||||
|
except WandException:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def is_logged_in():
|
def is_logged_in():
|
||||||
return "pyrom_session_key" in session
|
return "pyrom_session_key" in session
|
||||||
|
|
||||||
@ -197,10 +231,92 @@ def page(username):
|
|||||||
return render_template("users/user.html", target_user = target_user)
|
return render_template("users/user.html", target_user = target_user)
|
||||||
|
|
||||||
|
|
||||||
@bp.get("/<username>/setings")
|
@bp.get("/<username>/settings")
|
||||||
@login_required
|
@login_required
|
||||||
def settings(username):
|
def settings(username):
|
||||||
return "stub"
|
target_user = Users.find({'username': username})
|
||||||
|
if target_user.id != get_active_user().id:
|
||||||
|
return redirect('.settings', username = get_active_user().username)
|
||||||
|
|
||||||
|
return render_template('users/settings.html')
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/<username>/settings')
|
||||||
|
@login_required
|
||||||
|
def settings_form(username):
|
||||||
|
# we silently ignore the passed username
|
||||||
|
# and grab the correct user from the session
|
||||||
|
user = get_active_user()
|
||||||
|
topic_sort_by = request.form.get('topic_sort_by', default='activity')
|
||||||
|
if topic_sort_by == 'activity' or topic_sort_by == 'thread':
|
||||||
|
sort_by = session['sort_by'] = topic_sort_by
|
||||||
|
status = request.form.get('status', default="")[:100]
|
||||||
|
original_sig = request.form.get('signature', default='')
|
||||||
|
rendered_sig = babycode_to_html(original_sig)
|
||||||
|
session['subscribe_by_default'] = request.form.get('subscribe_by_default', default='on') == 'on'
|
||||||
|
|
||||||
|
user.update({
|
||||||
|
'status': status,
|
||||||
|
'signature_original_markup': original_sig,
|
||||||
|
'signature_rendered': rendered_sig,
|
||||||
|
})
|
||||||
|
flash('Settings updated.', InfoboxKind.INFO)
|
||||||
|
return redirect(url_for('.settings', username=user.username))
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/<username>/set_avatar')
|
||||||
|
@login_required
|
||||||
|
def set_avatar(username):
|
||||||
|
user = get_active_user()
|
||||||
|
if user.is_guest():
|
||||||
|
return 'no'
|
||||||
|
if 'avatar' not in request.files:
|
||||||
|
return 'no!...'
|
||||||
|
|
||||||
|
file = request.files['avatar']
|
||||||
|
|
||||||
|
if file.filename == '':
|
||||||
|
return 'no..?'
|
||||||
|
|
||||||
|
file_bytes = file.read()
|
||||||
|
|
||||||
|
now = int(time.time())
|
||||||
|
filename = f"u{user.id}d{now}.webp"
|
||||||
|
output_path = os.path.join(current_app.config['AVATAR_UPLOAD_PATH'], filename)
|
||||||
|
proxied_filename = f"/static/avatars/{filename}"
|
||||||
|
res = validate_and_create_avatar(file_bytes, output_path)
|
||||||
|
if res:
|
||||||
|
flash('Avatar updated.', InfoboxKind.INFO)
|
||||||
|
avatar = Avatars.create({
|
||||||
|
'file_path': proxied_filename,
|
||||||
|
'uploaded_at': now,
|
||||||
|
})
|
||||||
|
old_avatar = Avatars.find({'id': user.avatar_id})
|
||||||
|
user.update({'avatar_id': avatar.id})
|
||||||
|
if int(old_avatar.id) != 1:
|
||||||
|
# delete old avi, but not default
|
||||||
|
filename = os.path.join(current_app.config['AVATAR_UPLOAD_PATH'], os.path.basename(old_avatar.file_path))
|
||||||
|
os.remove(filename)
|
||||||
|
old_avatar.delete()
|
||||||
|
return redirect(url_for('.settings', username=user.username))
|
||||||
|
else:
|
||||||
|
return 'uhhhh no'
|
||||||
|
|
||||||
|
|
||||||
|
@bp.post('/<username>/clear_avatar')
|
||||||
|
@login_required
|
||||||
|
def clear_avatar(username):
|
||||||
|
user = get_active_user()
|
||||||
|
if user.is_default_avatar():
|
||||||
|
return 'no'
|
||||||
|
|
||||||
|
old_avatar = Avatars.find({'id': user.avatar_id})
|
||||||
|
user.update({'avatar_id': 1})
|
||||||
|
# delete old avi
|
||||||
|
filename = os.path.join(current_app.config['AVATAR_UPLOAD_PATH'], os.path.basename(old_avatar.file_path))
|
||||||
|
os.remove(filename)
|
||||||
|
old_avatar.delete()
|
||||||
|
return redirect(url_for('.settings', username=user.username))
|
||||||
|
|
||||||
|
|
||||||
@bp.post("/log_out")
|
@bp.post("/log_out")
|
||||||
|
@ -76,7 +76,7 @@
|
|||||||
{{babycode_editor_component(ta_name, prefill = prefill)}}
|
{{babycode_editor_component(ta_name, prefill = prefill)}}
|
||||||
{% if not cancel_url %}
|
{% if not cancel_url %}
|
||||||
<span>
|
<span>
|
||||||
<input type="checkbox" id="subscribe" name="subscribe" {{ "checked" if session['subscribe_by_default'] else "" }}>
|
<input type="checkbox" id="subscribe" name="subscribe" {{ "checked" if session.get('subscribe_by_default', default=true) else "" }}>
|
||||||
<label for="subscribe">Subscribe to thread</label>
|
<label for="subscribe">Subscribe to thread</label>
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
32
app/templates/users/settings.html
Normal file
32
app/templates/users/settings.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
{% from 'common/macros.html' import babycode_editor_component %}
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block title %}settings{% endblock %}
|
||||||
|
{% block content %}
|
||||||
|
{% set disable_avatar = not is_logged_in() %}
|
||||||
|
<div class='darkbg settings-container'>
|
||||||
|
<h1>User settings</h1>
|
||||||
|
<form class='avatar-form' method='post' action='{{ url_for('users.set_avatar', username=active_user.username) }}' enctype='multipart/form-data'>
|
||||||
|
<span>Set avatar (1mb max)</span>
|
||||||
|
<img src='{{ active_user.get_avatar_url() }}'>
|
||||||
|
<input id='file' type='file' name='avatar' accept='image/*' required>
|
||||||
|
<div>
|
||||||
|
<input type='submit' value='Upload avatar' {{ 'disabled' if disable_avatar else '' }}>
|
||||||
|
<input type='submit' value='Clear avatar' formaction='{{ url_for('users.clear_avatar', username=active_user.username) }}' formnovalidate {{ 'disabled' if active_user.is_default_avatar() else '' }}>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<form method='post'>
|
||||||
|
<label for='topic_sort_by'>Sort threads by:</label>
|
||||||
|
<select id='topic_sort_by' name='topic_sort_by'>
|
||||||
|
<option value='activity' {{ 'selected' if session['sort_by'] == 'activity' else '' }}>Latest activity</option>
|
||||||
|
<option value='thread' {{ 'selected' if session['sort_by'] == 'thread' else '' }}>Thread creation date</option>
|
||||||
|
</select>
|
||||||
|
<label for='status'>Status</label>
|
||||||
|
<input type='text' id='status' name='status' value='{{ active_user.status }}' maxlength=100 placeholder='Will be shown under your name. Max 100 characters.'>
|
||||||
|
<label for='babycode-content'>Signature</label>
|
||||||
|
{{ babycode_editor_component(ta_name='signature', prefill=active_user.signature_original_markup, ta_placeholder='Will be shown under each of your posts', optional=true) }}
|
||||||
|
<input autocomplete='off' type='checkbox' id='subscribe_by_default' {{ 'checked' if session.get('subscribe_by_default', default=true) else '' }}>
|
||||||
|
<label for='subscribe_by_default'>Subscribe to thread by default when responding</label><br>
|
||||||
|
<input type='submit' value='Save settings'>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user