From 87798f47813f8ddeb9fe9154274810df986b56ba Mon Sep 17 00:00:00 2001
From: Patrick Jentsch <p.jentsch@uni-bielefeld.de>
Date: Mon, 3 Apr 2023 16:34:03 +0200
Subject: [PATCH] completly move settings logic to users package

---
 app/admin/routes.py                           | 12 +--
 app/settings/routes.py                        | 88 ++----------------
 .../{ => users}/settings/settings.html.j2     |  0
 app/users/settings/__init__.py                |  2 +-
 app/{ => users}/settings/forms.py             |  8 +-
 app/users/settings/json_routes.py             | 20 ++--
 app/users/settings/routes.py                  | 93 +++++++++++++++++++
 7 files changed, 124 insertions(+), 99 deletions(-)
 rename app/templates/{ => users}/settings/settings.html.j2 (100%)
 rename app/{ => users}/settings/forms.py (94%)
 create mode 100644 app/users/settings/routes.py

diff --git a/app/admin/routes.py b/app/admin/routes.py
index 3822dc28..67739edc 100644
--- a/app/admin/routes.py
+++ b/app/admin/routes.py
@@ -2,7 +2,7 @@ from flask import abort, flash, redirect, render_template, url_for
 from flask_breadcrumbs import register_breadcrumb
 from app import db, hashids
 from app.models import Avatar, Corpus, Role, User
-from app.settings.forms import (
+from app.users.settings.forms import (
     UpdateAvatarForm,
     UpdatePasswordForm,
     UpdateNotificationsForm,
@@ -63,12 +63,12 @@ def user(user_id):
 @register_breadcrumb(bp, '.users.entity.settings', '<i class="material-icons left">settings</i>Settings')
 def user_settings(user_id):
     user = User.query.get_or_404(user_id)
-    update_account_information_form = UpdateAccountInformationForm(user=user)
-    update_profile_information_form = UpdateProfileInformationForm(user=user)
+    update_account_information_form = UpdateAccountInformationForm(user)
+    update_profile_information_form = UpdateProfileInformationForm(user)
     update_avatar_form = UpdateAvatarForm()
-    update_password_form = UpdatePasswordForm(user=user)
-    update_notifications_form = UpdateNotificationsForm(user=user)
-    update_user_form = UpdateUserForm(user=user)
+    update_password_form = UpdatePasswordForm(user)
+    update_notifications_form = UpdateNotificationsForm(user)
+    update_user_form = UpdateUserForm(user)
 
     # region handle update profile information form
     if update_profile_information_form.submit.data and update_profile_information_form.validate():
diff --git a/app/settings/routes.py b/app/settings/routes.py
index 70b69d6c..9496e6fd 100644
--- a/app/settings/routes.py
+++ b/app/settings/routes.py
@@ -1,88 +1,14 @@
-from flask import abort, flash, redirect, render_template, url_for
+from flask import url_for
 from flask_breadcrumbs import register_breadcrumb
-from flask_login import current_user, login_required
-from app import db
-from app.models import Avatar
+from flask_login import current_user
+from app.users.settings.routes import settings as settings_route
 from . import bp
-from .forms import (
-    UpdateAvatarForm,
-    UpdatePasswordForm,
-    UpdateNotificationsForm,
-    UpdateAccountInformationForm,
-    UpdateProfileInformationForm
-)
 
 
-@bp.route('', methods=['GET', 'POST'])
+@bp.route('/settings', methods=['GET', 'POST'])
 @register_breadcrumb(bp, '.', '<i class="material-icons left">settings</i>Settings')
-@login_required
 def settings():
-    user = current_user
-    update_account_information_form = UpdateAccountInformationForm()
-    update_profile_information_form = UpdateProfileInformationForm()
-    update_avatar_form = UpdateAvatarForm()
-    update_password_form = UpdatePasswordForm()
-    update_notifications_form = UpdateNotificationsForm()
-
-    # region handle update profile information form
-    if update_profile_information_form.submit.data and update_profile_information_form.validate():
-        user.about_me = update_profile_information_form.about_me.data
-        user.location = update_profile_information_form.location.data
-        user.organization = update_profile_information_form.organization.data
-        user.website = update_profile_information_form.website.data
-        user.full_name = update_profile_information_form.full_name.data
-        db.session.commit()
-        flash('Your changes have been saved')
-        return redirect(url_for('.settings'))
-    # endregion handle update profile information form
-
-    # region handle update avatar form
-    if update_avatar_form.submit.data and update_avatar_form.validate():
-        try:
-            Avatar.create(
-                update_avatar_form.avatar.data,
-                user=user
-            )
-        except (AttributeError, OSError):
-            abort(500)
-        db.session.commit()
-        flash('Your changes have been saved')
-        return redirect(url_for('.settings'))
-    # endregion handle update avatar form
-
-    # region handle update account information form
-    if update_account_information_form.submit.data and update_account_information_form.validate():
-        user.email = update_account_information_form.email.data
-        user.username = update_account_information_form.username.data
-        db.session.commit()
-        flash('Profile settings updated')
-        return redirect(url_for('.settings'))
-    # endregion handle update account information form
-
-    # region handle update password form
-    if update_password_form.submit.data and update_password_form.validate():
-        user.password = update_password_form.new_password.data
-        db.session.commit()
-        flash('Your changes have been saved')
-        return redirect(url_for('.settings'))
-    # endregion handle update password form
-
-    # region handle update notifications form
-    if update_notifications_form.submit.data and update_notifications_form.validate():
-        user.setting_job_status_mail_notification_level = \
-            update_notifications_form.job_status_mail_notification_level.data
-        db.session.commit()
-        flash('Your changes have been saved')
-        return redirect(url_for('.settings'))
-    # endregion handle update notifications form
-
-    return render_template(
-        'settings/settings.html.j2',
-        title='Settings',
-        update_account_information_form=update_account_information_form,
-        update_avatar_form=update_avatar_form,
-        update_notifications_form=update_notifications_form,
-        update_password_form=update_password_form,
-        update_profile_information_form=update_profile_information_form,
-        user=user
+    return settings_route(
+        current_user.id,
+        redirect_location_on_post=url_for('.settings')
     )
diff --git a/app/templates/settings/settings.html.j2 b/app/templates/users/settings/settings.html.j2
similarity index 100%
rename from app/templates/settings/settings.html.j2
rename to app/templates/users/settings/settings.html.j2
diff --git a/app/users/settings/__init__.py b/app/users/settings/__init__.py
index 1dbe44f0..e06bada9 100644
--- a/app/users/settings/__init__.py
+++ b/app/users/settings/__init__.py
@@ -1,2 +1,2 @@
 from .. import bp
-from . import json_routes
+from . import json_routes, routes
diff --git a/app/settings/forms.py b/app/users/settings/forms.py
similarity index 94%
rename from app/settings/forms.py
rename to app/users/settings/forms.py
index 77c8687c..71c29456 100644
--- a/app/settings/forms.py
+++ b/app/users/settings/forms.py
@@ -41,7 +41,7 @@ class UpdateAccountInformationForm(FlaskForm):
     )
     submit = SubmitField()
     
-    def __init__(self, *args, user=current_user, **kwargs):
+    def __init__(self, user, *args, **kwargs):
         if 'data' not in kwargs:
             kwargs['data'] = user.to_json_serializeable()
         if 'prefix' not in kwargs:
@@ -91,7 +91,7 @@ class UpdateProfileInformationForm(FlaskForm):
     )
     submit = SubmitField()
 
-    def __init__(self, *args, user=current_user, **kwargs):
+    def __init__(self, user, *args, **kwargs):
         if 'data' not in kwargs:
             kwargs['data'] = user.to_json_serializeable()
         if 'prefix' not in kwargs:
@@ -132,7 +132,7 @@ class UpdatePasswordForm(FlaskForm):
     )
     submit = SubmitField()
 
-    def __init__(self, *args, user=current_user, **kwargs):
+    def __init__(self, user, *args, **kwargs):
         if 'prefix' not in kwargs:
             kwargs['prefix'] = 'update-password-form'
         super().__init__(*args, **kwargs)
@@ -154,7 +154,7 @@ class UpdateNotificationsForm(FlaskForm):
     )
     submit = SubmitField()
 
-    def __init__(self, *args, user=current_user, **kwargs):
+    def __init__(self, user, *args, **kwargs):
         if 'data' not in kwargs:
             kwargs['data'] = user.to_json_serializeable()
         if 'prefix' not in kwargs:
diff --git a/app/users/settings/json_routes.py b/app/users/settings/json_routes.py
index acc7dbc9..7fdfa7fc 100644
--- a/app/users/settings/json_routes.py
+++ b/app/users/settings/json_routes.py
@@ -1,5 +1,5 @@
 from flask import abort, current_app, request
-from flask_login import login_required
+from flask_login import current_user, login_required
 from threading import Thread
 import os
 from app import db
@@ -20,6 +20,8 @@ def delete_user_avatar(user_id):
     user = User.query.get_or_404(user_id)
     if user.avatar is None:
         abort(404)
+    if not (user == current_user or current_user.is_administrator()):
+        abort(403)
     thread = Thread(
         target=_delete_avatar,
         args=(current_app._get_current_object(), user.avatar.id)
@@ -36,10 +38,12 @@ def delete_user_avatar(user_id):
 @content_negotiation(consumes='application/json', produces='application/json')
 def update_user_profile_privacy_setting_is_public(user_id):
     user = User.query.get_or_404(user_id)
-    is_public = request.json
-    if not isinstance(is_public, bool):
+    if not (user == current_user or current_user.is_administrator()):
+        abort(403)
+    enabled = request.json
+    if not isinstance(enabled, bool):
         abort(400)
-    user.is_public = is_public
+    user.is_public = enabled
     db.session.commit()
     response_data = {
         'message': 'Profile privacy settings updated',
@@ -53,13 +57,15 @@ def update_user_profile_privacy_setting_is_public(user_id):
 @content_negotiation(consumes='application/json', produces='application/json')
 def update_user_profile_privacy_settings(user_id, profile_privacy_setting_name):
     user = User.query.get_or_404(user_id)
-    enabled = request.json
-    if not isinstance(enabled, bool):
-        abort(400)
     try:
         profile_privacy_setting = ProfilePrivacySettings[profile_privacy_setting_name]
     except KeyError:
         abort(404)
+    if not (user == current_user or current_user.is_administrator()):
+        abort(403)
+    enabled = request.json
+    if not isinstance(enabled, bool):
+        abort(400)
     if enabled:
         user.add_profile_privacy_setting(profile_privacy_setting)
     else:
diff --git a/app/users/settings/routes.py b/app/users/settings/routes.py
new file mode 100644
index 00000000..f023bf00
--- /dev/null
+++ b/app/users/settings/routes.py
@@ -0,0 +1,93 @@
+from flask import abort, flash, redirect, render_template, url_for
+from flask_breadcrumbs import register_breadcrumb
+from flask_login import current_user, login_required
+from app import db
+from app.models import Avatar, User
+from ..utils import user_endpoint_arguments_constructor as user_eac
+from . import bp
+from .forms import (
+    UpdateAvatarForm,
+    UpdatePasswordForm,
+    UpdateNotificationsForm,
+    UpdateAccountInformationForm,
+    UpdateProfileInformationForm
+)
+
+
+@bp.route('/<hashid:user_id>/settings', methods=['GET', 'POST'])
+@register_breadcrumb(bp, '.entity.settings', '<i class="material-icons left">settings</i>Settings', endpoint_arguments_constructor=user_eac)
+@login_required
+def settings(user_id, redirect_location_on_post=None):
+    user = User.query.get_or_404(user_id)
+    if not (user == current_user or current_user.is_administrator()):
+        abort(403)
+    if redirect_location_on_post is None:
+        redirect_location_on_post = url_for('.settings', user_id=user_id)
+    update_account_information_form = UpdateAccountInformationForm(user)
+    update_profile_information_form = UpdateProfileInformationForm(user)
+    update_avatar_form = UpdateAvatarForm()
+    update_password_form = UpdatePasswordForm(user)
+    update_notifications_form = UpdateNotificationsForm(user)
+
+    # region handle update profile information form
+    if update_profile_information_form.submit.data and update_profile_information_form.validate():
+        user.about_me = update_profile_information_form.about_me.data
+        user.location = update_profile_information_form.location.data
+        user.organization = update_profile_information_form.organization.data
+        user.website = update_profile_information_form.website.data
+        user.full_name = update_profile_information_form.full_name.data
+        db.session.commit()
+        flash('Your changes have been saved')
+        return redirect(redirect_location_on_post)
+    # endregion handle update profile information form
+
+    # region handle update avatar form
+    if update_avatar_form.submit.data and update_avatar_form.validate():
+        try:
+            Avatar.create(
+                update_avatar_form.avatar.data,
+                user=user
+            )
+        except (AttributeError, OSError):
+            abort(500)
+        db.session.commit()
+        flash('Your changes have been saved')
+        return redirect(redirect_location_on_post)
+    # endregion handle update avatar form
+
+    # region handle update account information form
+    if update_account_information_form.submit.data and update_account_information_form.validate():
+        user.email = update_account_information_form.email.data
+        user.username = update_account_information_form.username.data
+        db.session.commit()
+        flash('Profile settings updated')
+        return redirect(redirect_location_on_post)
+    # endregion handle update account information form
+
+    # region handle update password form
+    if update_password_form.submit.data and update_password_form.validate():
+        user.password = update_password_form.new_password.data
+        db.session.commit()
+        flash('Your changes have been saved')
+        return redirect(redirect_location_on_post)
+    # endregion handle update password form
+
+    # region handle update notifications form
+    if update_notifications_form.submit.data and update_notifications_form.validate():
+        user.setting_job_status_mail_notification_level = \
+            update_notifications_form.job_status_mail_notification_level.data
+        db.session.commit()
+        flash('Your changes have been saved')
+        return redirect(redirect_location_on_post)
+    # endregion handle update notifications form
+
+    return render_template(
+        'users/settings/settings.html.j2',
+        title='Settings',
+        update_account_information_form=update_account_information_form,
+        update_avatar_form=update_avatar_form,
+        update_notifications_form=update_notifications_form,
+        update_password_form=update_password_form,
+        update_profile_information_form=update_profile_information_form,
+        user=user
+    )
-- 
GitLab