From cd7233f566a153f2cb0f03868efeb8564d6a26b4 Mon Sep 17 00:00:00 2001 From: Julian Lobbes Date: Thu, 17 Nov 2022 19:07:56 +0100 Subject: [PATCH] feat(usermanager): implement user update form --- .../{user_detail.html => user_update.html} | 2 +- lumi2/usermanager.py | 91 ++++++++++++++----- lumi2/usermodel.py | 5 +- 3 files changed, 74 insertions(+), 24 deletions(-) rename lumi2/templates/usermanager/{user_detail.html => user_update.html} (94%) diff --git a/lumi2/templates/usermanager/user_detail.html b/lumi2/templates/usermanager/user_update.html similarity index 94% rename from lumi2/templates/usermanager/user_detail.html rename to lumi2/templates/usermanager/user_update.html index 8b5d013..01f65f2 100644 --- a/lumi2/templates/usermanager/user_detail.html +++ b/lumi2/templates/usermanager/user_update.html @@ -27,6 +27,6 @@ {{ form.picture.label }} {{ form.picture }} - + {{ form.submit }} {% endblock content %} diff --git a/lumi2/usermanager.py b/lumi2/usermanager.py index 646a502..57cfa39 100644 --- a/lumi2/usermanager.py +++ b/lumi2/usermanager.py @@ -9,11 +9,12 @@ from flask import ( 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 import ValidationError, StringField, PasswordField, SubmitField from wtforms.validators import InputRequired, Email, EqualTo import lumi2.ldap as ldap from lumi2.usermodel import User, Group +from lumi2.exceptions import InvalidStringFormatException, InvalidImageException bp = Blueprint('usermanager', __name__) @@ -26,11 +27,31 @@ def index(): return render_template('usermanager/index.html') -class UserEditForm(FlaskForm): +class UserUpdateForm(FlaskForm): @staticmethod def validate_name(form, field) -> None: - if not User.is_valid_person_name(field.data): - raise ValidationError("Invalid name.") + try: + User.assert_is_valid_name(field.data) + except InvalidStringFormatException as e: + raise ValidationError from e + + @staticmethod + def validate_password(form, field) -> None: + try: + User.assert_is_valid_password(field.data) + except InvalidStringFormatException as e: + raise ValidationError from e + + @staticmethod + def validate_picture(form, field) -> None: + if field.data: + try: + with TemporaryDirectory() as temp_dir: + temp_file = Path(temp_dir) / "upload.jpg" + field.data.save(temp_file) + Image.open(temp_file, formats=['JPEG']) + except UnidentifiedImageError as e: + raise ValidationError from e email = StringField( 'Email', @@ -50,7 +71,10 @@ class UserEditForm(FlaskForm): ) password = PasswordField( 'Password', - [EqualTo('password_confirmation', message='Passwords must match')], + [ + EqualTo('password_confirmation', message='Passwords must match'), + validate_password, + ], ) password_confirmation = PasswordField( 'Password (repeat)', @@ -59,11 +83,14 @@ class UserEditForm(FlaskForm): 'Picture', [FileAllowed(['jpg', 'jpeg'], 'JPEG images only.')] ) + submit = SubmitField( + 'Update', + ) -@bp.route("/user/", methods=("GET", "POST")) -def user_detail(username: str): - """Detail view for a specific User.""" +@bp.route("/user/update/", methods=("GET", "POST")) +def user_update(username: str): + """Update view for a specific User.""" try: conn = ldap.get_connection() @@ -78,19 +105,41 @@ def user_detail(username: str): user._generate_static_images() - # data = { - # "email": user.email, - # "first_name": user.first_name, - # "last_name": user.last_name, - # "display_name": user.display_name, - # } + if request.method == 'GET': + form = UserUpdateForm(obj=user) + else: + form = UserUpdateForm(request.form) + if form.validate(): + if form.email.data: + user.email = form.email.data + print("Email updated.") + if form.first_name.data: + user.first_name = form.first_name.data + print("First Name updated.") + if form.last_name.data: + user.last_name = form.last_name.data + print("Last Name updated.") + if form.display_name.data: + user.display_name = form.display_name.data + print("Display Name updated.") + if form.password.data: + user.password_hash = User.generate_password_hash(form.password.data) + print("Password updated.") + picture_updated = False + if form.picture.data: + with TemporaryDirectory() as temp_dir: + temp_file = Path(temp_dir) / "upload.jpg" + form.picture.data.save(temp_file) + user.picture = Image.open(temp_file, formats=['JPEG']) + picture_updated = True + print("Picture updated.") - form = UserEditForm(obj=user) - if form.validate_on_submit(): - conn.unbind() - - # TODO update user - return redirect(request.url) + ldap.update_user(conn, user) + if picture_updated: + user._generate_static_images(force=True) + conn.unbind() + # TODO redirect to user detail view + return redirect(request.url) conn.unbind() - return render_template('usermanager/user_detail.html', form=form, user=user) + return render_template('usermanager/user_update.html', form=form, user=user) diff --git a/lumi2/usermodel.py b/lumi2/usermodel.py index 1ff2599..1a14cf2 100644 --- a/lumi2/usermodel.py +++ b/lumi2/usermodel.py @@ -6,7 +6,8 @@ import hashlib from binascii import Error as Base64DecodeError from pathlib import Path -from PIL.Image import JpegImageFile, Image +from PIL.Image import Image +from PIL.JpegImagePlugin import JpegImageFile from flask import current_app from lumi2.exceptions import InvalidStringFormatException, InvalidImageException @@ -297,7 +298,7 @@ class User: @staticmethod - def _get_default_picture() -> Image.Image: + def _get_default_picture() -> Image: """Returns the default user picture as a PIL Image object. Returns