refactor(usermanager): migrate to flask-wtf
This commit is contained in:
parent
997327338e
commit
7b1196f09d
@ -4,18 +4,29 @@
|
||||
<h1>User: {{ user.username }}</h1>
|
||||
<img src="{{ url_for('static', filename='images/users/' + user.username + '/full.jpg') }}" alt="profile picture for user {{ user.username }}">
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<label for="email">Email</label>
|
||||
<input name="email" id="email" type="email" placeholder="{{ user.email }}">
|
||||
<label for="first_name">First name</label>
|
||||
<input name="first_name" id="first_name" placeholder="{{ user.first_name }}">
|
||||
<label for="last_name">Last name</label>
|
||||
<input name="last_name" id="last_name" placeholder="{{ user.last_name }}">
|
||||
<label for="display_name">Nickname</label>
|
||||
<input name="display_name" id="display_name" placeholder="{{ user.display_name }}">
|
||||
<label for="password">Password</label>
|
||||
<input name="password" id="password" type="password" placeholder="********">
|
||||
<label for="picture">Picture</label>
|
||||
<input name="picture" id="picture" type="file" accept="image/jpeg">
|
||||
{{ form.csrf_token }}
|
||||
|
||||
{{ form.email.label }}
|
||||
{{ form.email }}
|
||||
|
||||
{{ form.first_name.label }}
|
||||
{{ form.first_name }}
|
||||
|
||||
{{ form.last_name.label }}
|
||||
{{ form.last_name }}
|
||||
|
||||
{{ form.display_name.label }}
|
||||
{{ form.display_name }}
|
||||
|
||||
{{ form.password.label }}
|
||||
{{ form.password }}
|
||||
|
||||
{{ form.password_confirmation.label }}
|
||||
{{ form.password_confirmation }}
|
||||
|
||||
{{ form.picture.label }}
|
||||
{{ form.picture }}
|
||||
|
||||
<input type="submit" value="Update">
|
||||
</form>
|
||||
{% endblock content %}
|
||||
|
@ -4,9 +4,13 @@ from pathlib import Path
|
||||
from tempfile import TemporaryDirectory
|
||||
|
||||
from flask import (
|
||||
Blueprint, render_template, abort, request, flash
|
||||
Blueprint, render_template, abort, request, flash, redirect
|
||||
)
|
||||
from PIL import Image, UnidentifiedImageError
|
||||
from flask_wtf import FlaskForm
|
||||
from flask_wtf.file import FileField, FileAllowed
|
||||
from wtforms import ValidationError, StringField, PasswordField
|
||||
from wtforms.validators import InputRequired, Email, EqualTo
|
||||
|
||||
import lumi2.ldap as ldap
|
||||
from lumi2.usermodel import User, Group
|
||||
@ -22,52 +26,39 @@ def index():
|
||||
return render_template('usermanager/index.html')
|
||||
|
||||
|
||||
class InvalidImageException(Exception):
|
||||
"""Raised when an image's filename or contents are invalid."""
|
||||
pass
|
||||
class UserEditForm(FlaskForm):
|
||||
@staticmethod
|
||||
def validate_name(form, field) -> None:
|
||||
if not User.is_valid_person_name(field.data):
|
||||
raise ValidationError("Invalid name.")
|
||||
|
||||
|
||||
def _get_image_from_uploaded_file(file) -> Image.Image:
|
||||
"""Extracts a JPEG image from a file submitted via POST request.
|
||||
|
||||
The file's file extension and content is checked for validity as a JPEG image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file
|
||||
A file object taken from a POST request.
|
||||
|
||||
Returns
|
||||
-------
|
||||
PIL.Image.Image
|
||||
A valid JPEG Image object.
|
||||
|
||||
Raises
|
||||
------
|
||||
InvalidImageException
|
||||
When the file's file extension or contents are not valid for a JPEG image.
|
||||
"""
|
||||
|
||||
def _file_extension_is_valid(filename: str):
|
||||
allowed_extensions = ["jpg", "jpeg"]
|
||||
if '.' not in filename:
|
||||
return False
|
||||
if filename.rsplit('.', 1)[1].lower() not in allowed_extensions:
|
||||
return False
|
||||
return True
|
||||
|
||||
if not _file_extension_is_valid(file.filename):
|
||||
raise InvalidImageException("Invalid file extension.")
|
||||
|
||||
with TemporaryDirectory() as tempdir:
|
||||
path_to_file = Path(tempdir) / "upload.jpg"
|
||||
file.save(path_to_file)
|
||||
try:
|
||||
return Image.open(path_to_file, formats=['JPEG'])
|
||||
except UnidentifiedImageError:
|
||||
raise InvalidImageException(
|
||||
"Image is either not a JPEG, or its contents are corrupted."
|
||||
)
|
||||
email = StringField(
|
||||
'Email',
|
||||
[InputRequired(), Email()]
|
||||
)
|
||||
first_name = StringField(
|
||||
'First Name',
|
||||
[InputRequired(), validate_name]
|
||||
)
|
||||
last_name = StringField(
|
||||
'Last Name',
|
||||
[InputRequired(), validate_name]
|
||||
)
|
||||
display_name = StringField(
|
||||
'Nick Name',
|
||||
[InputRequired(), validate_name]
|
||||
)
|
||||
password = PasswordField(
|
||||
'Password',
|
||||
[EqualTo('password_confirmation', message='Passwords must match')],
|
||||
)
|
||||
password_confirmation = PasswordField(
|
||||
'Password (repeat)',
|
||||
)
|
||||
picture = FileField(
|
||||
'Picture',
|
||||
[FileAllowed(['jpg', 'jpeg'], 'JPEG images only.')]
|
||||
)
|
||||
|
||||
|
||||
@bp.route("/user/<string:username>", methods=("GET", "POST"))
|
||||
@ -87,52 +78,19 @@ def user_detail(username: str):
|
||||
|
||||
user._generate_static_images()
|
||||
|
||||
if request.method == 'POST':
|
||||
form_is_valid = True
|
||||
# data = {
|
||||
# "email": user.email,
|
||||
# "first_name": user.first_name,
|
||||
# "last_name": user.last_name,
|
||||
# "display_name": user.display_name,
|
||||
# }
|
||||
|
||||
if request.form['email']:
|
||||
user.email = request.form['email']
|
||||
if not User.is_valid_email(user.email):
|
||||
flash("Invalid email address.")
|
||||
form_is_valid = False
|
||||
form = UserEditForm(obj=user)
|
||||
if form.validate_on_submit():
|
||||
conn.unbind()
|
||||
|
||||
if request.form['first_name']:
|
||||
user.first_name = request.form['first_name']
|
||||
if not User.is_valid_person_name(user.first_name):
|
||||
flash("Invalid first name.")
|
||||
form_is_valid = False
|
||||
|
||||
if request.form['last_name']:
|
||||
user.last_name = request.form['last_name']
|
||||
if not User.is_valid_person_name(user.last_name):
|
||||
flash("Invalid last name.")
|
||||
form_is_valid = False
|
||||
|
||||
if request.form['display_name']:
|
||||
user.display_name = request.form['display_name']
|
||||
if not User.is_valid_person_name(user.display_name):
|
||||
flash("Invalid nickname.")
|
||||
form_is_valid = False
|
||||
|
||||
if request.form['password']:
|
||||
user.password_hash = User.generate_password_hash(request.form['password'])
|
||||
|
||||
new_picture = None
|
||||
if 'picture' in request.files:
|
||||
file = request.files['picture']
|
||||
if len(file.filename):
|
||||
try:
|
||||
new_picture = _get_image_from_uploaded_file(file)
|
||||
user.picture = new_picture
|
||||
except InvalidImageException as e:
|
||||
flash(f"Invalid picture: {e}")
|
||||
form_is_valid = False
|
||||
|
||||
if form_is_valid:
|
||||
ldap.update_user(conn, user)
|
||||
flash("User information was updated!")
|
||||
if new_picture is not None:
|
||||
ldap.get_user(conn, user.username)._generate_static_images(force=True)
|
||||
# TODO update user
|
||||
return redirect(request.url)
|
||||
|
||||
conn.unbind()
|
||||
return render_template('usermanager/user_detail.html', user=user)
|
||||
return render_template('usermanager/user_detail.html', form=form, user=user)
|
||||
|
@ -3,3 +3,6 @@ ldap3==2.9.1
|
||||
pytest==7.2.0
|
||||
coverage==6.5.0
|
||||
Pillow==9.3.0
|
||||
WTForms==3.0.1
|
||||
wtforms[email]==3.0.1
|
||||
Flask-WTF==1.0.1
|
||||
|
Loading…
Reference in New Issue
Block a user