diff --git a/lumi2/templates/base.html b/lumi2/templates/base.html index 6f5fcb4..4379ccb 100644 --- a/lumi2/templates/base.html +++ b/lumi2/templates/base.html @@ -1,38 +1,39 @@ - - - - + + + - {{ config.SITE_TITLE }} - - + {{ config.SITE_TITLE }} + + - - - - - + + + + + - - - + + + - - + + + - - {% with messages = get_flashed_messages() %} - {% if messages %} - - {% endif %} - {% endwith %} - {% block content %} - {% endblock content %} - + + {% with messages = get_flashed_messages() %} + {% if messages %} + {% for message in messages %} +
{{ message }}
+ {% endfor %} + {% endif %} + {% endwith %} +
+ {% block content %} + {% endblock content %} +
+ + diff --git a/lumi2/templates/usermanager/user_update.html b/lumi2/templates/usermanager/user_update.html index 01f65f2..29b023e 100644 --- a/lumi2/templates/usermanager/user_update.html +++ b/lumi2/templates/usermanager/user_update.html @@ -1,32 +1,81 @@ {% extends 'base.html' %} {% block content %} -

User: {{ user.username }}

-profile picture for user {{ user.username }} +
+
+

Edit user: {{ username }}

+
+
{{ 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 }} - - {{ form.submit }} +
+ {{ form.email.label(class="form-label") }} + {{ form.email(class="form-control" + (" is-invalid" if form.email.errors else "")) }} + {% if form.email.errors %} + {% for error in form.email.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.first_name.label(class="form-label") }} + {{ form.first_name(class="form-control" + (" is-invalid" if form.first_name.errors else "")) }} + {% if form.first_name.errors %} + {% for error in form.first_name.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.last_name.label(class="form-label") }} + {{ form.last_name(class="form-control" + (" is-invalid" if form.last_name.errors else "")) }} + {% if form.last_name.errors %} + {% for error in form.last_name.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.display_name.label(class="form-label") }} + {{ form.display_name(class="form-control" + (" is-invalid" if form.display_name.errors else "")) }} + {% if form.display_name.errors %} + {% for error in form.display_name.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.password.label(class="form-label") }} + {{ form.password(class="form-control" + (" is-invalid" if form.password.errors else "")) }} + {% if form.password.errors %} + {% for error in form.password.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.password_confirmation.label(class="form-label") }} + {{ form.password_confirmation(class="form-control" + (" is-invalid" if form.password_confirmation.errors else "")) }} + {% if form.password_confirmation.errors %} + {% for error in form.password_confirmation.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ {{ form.picture.label(class="form-label") }} + {{ form.picture(class="form-control" + (" is-invalid" if form.picture.errors else "")) }} + {% if form.picture.errors %} + {% for error in form.picture.errors %} +
{{ error }}
+ {% endfor %} + {% endif %} +
+
+ Cancel + {{ form.submit(class_="btn btn-primary") }} +
{% endblock content %} diff --git a/lumi2/templates/usermanager/user_view.html b/lumi2/templates/usermanager/user_view.html new file mode 100644 index 0000000..91051c2 --- /dev/null +++ b/lumi2/templates/usermanager/user_view.html @@ -0,0 +1,44 @@ +{% extends 'base.html' %} + +{% block content %} +
+
+ profile picture for user {{ user.username }} +
+
+
+
+

{{ user.username }}

+
+
+
+
Username:
+
{{ user.username }}
+
+
+
Email:
+
{{ user.email }}
+
+
+
First Name:
+
{{ user.first_name }}
+
+
+
Last Name:
+
{{ user.last_name }}
+
+
+
Nickname:
+
{{ user.display_name }}
+
+
+
+ Edit +
+
+{% endblock content %} diff --git a/lumi2/usermanager.py b/lumi2/usermanager.py index 57cfa39..4926844 100644 --- a/lumi2/usermanager.py +++ b/lumi2/usermanager.py @@ -1,10 +1,10 @@ """Views for lumi2.""" from pathlib import Path -from tempfile import TemporaryDirectory +from tempfile import TemporaryFile from flask import ( - Blueprint, render_template, abort, request, flash, redirect + Blueprint, render_template, abort, request, flash, redirect, url_for ) from PIL import Image, UnidentifiedImageError from flask_wtf import FlaskForm @@ -27,31 +27,55 @@ def index(): return render_template('usermanager/index.html') +@bp.route("/user/view/") +def user_view(username: str): + """Detail view for a specific User. + + Shows the user's information. + """ + + try: + conn = ldap.get_connection() + except Exception: + abort(500) + + try: + user = ldap.get_user(conn, username) + except ldap.EntryNotFoundException: + conn.unbind() + abort(404) + + user._generate_static_images() + conn.unbind() + return render_template('usermanager/user_view.html',user=user) + + class UserUpdateForm(FlaskForm): @staticmethod def validate_name(form, field) -> None: try: User.assert_is_valid_name(field.data) except InvalidStringFormatException as e: - raise ValidationError from e + raise ValidationError(str(e)) @staticmethod def validate_password(form, field) -> None: - try: - User.assert_is_valid_password(field.data) - except InvalidStringFormatException as e: - raise ValidationError from e + if field.data: + try: + User.assert_is_valid_password(field.data) + except InvalidStringFormatException as e: + raise ValidationError(str(e)) @staticmethod def validate_picture(form, field) -> None: - if field.data: + if field.data and field.data.filename: try: - with TemporaryDirectory() as temp_dir: - temp_file = Path(temp_dir) / "upload.jpg" - field.data.save(temp_file) - Image.open(temp_file, formats=['JPEG']) + Image.open(field.data, formats=['JPEG']) + field.data.seek(0) except UnidentifiedImageError as e: - raise ValidationError from e + raise ValidationError( + "Invalid JPEG file. It may be corrupted." + ) email = StringField( 'Email', @@ -70,7 +94,7 @@ class UserUpdateForm(FlaskForm): [InputRequired(), validate_name] ) password = PasswordField( - 'Password', + 'Password (leave empty to keep the same)', [ EqualTo('password_confirmation', message='Passwords must match'), validate_password, @@ -90,7 +114,10 @@ class UserUpdateForm(FlaskForm): @bp.route("/user/update/", methods=("GET", "POST")) def user_update(username: str): - """Update view for a specific User.""" + """Update view for a specific User. + + Provides a form which can be used to edit that user's details. + """ try: conn = ldap.get_connection() @@ -105,41 +132,33 @@ def user_update(username: str): user._generate_static_images() - 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 = UserUpdateForm(obj=user) + if form.validate_on_submit(): + if form.email.data: + user.email = form.email.data + if form.first_name.data: + user.first_name = form.first_name.data + if form.last_name.data: + user.last_name = form.last_name.data + if form.display_name.data: + user.display_name = form.display_name.data + if form.password.data: + user.password_hash = User.generate_password_hash(form.password.data) + picture_updated = False + if form.picture.data and form.picture.data.filename: + user.picture = Image.open(form.picture.data, formats=['JPEG']) + picture_updated = True - 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) + ldap.update_user(conn, user) + if picture_updated: + user._generate_static_images(force=True) + conn.unbind() + flash(f"Information for user '{user.username}' was updated.") + return redirect(url_for('usermanager.user_view', username=user.username)) conn.unbind() - return render_template('usermanager/user_update.html', form=form, user=user) + return render_template( + 'usermanager/user_update.html', + form=form, + username=user.username + )