From 41096445a623b901cce1f43cbcf061491036211c Mon Sep 17 00:00:00 2001 From: Inga Kirschnick Date: Fri, 17 Mar 2023 15:56:37 +0100 Subject: [PATCH] small update settings page+new package 'settings' --- app/__init__.py | 4 + app/settings/__init__.py | 6 + app/{users => settings}/forms.py | 10 +- app/{users => settings}/json_routes.py | 21 +- app/settings/routes.py | 108 ++++++++ app/settings/utils.py | 6 + app/static/js/Requests/settings/settings.js | 35 +++ app/static/js/Requests/users/users.js | 23 -- app/templates/_navbar.html.j2 | 2 +- app/templates/_scripts.html.j2 | 2 +- app/templates/_sidenav.html.j2 | 2 +- .../{users => settings}/edit_profile.html.j2 | 101 +++++--- .../users/edit_profile_testing.html.j2 | 236 ------------------ app/templates/users/profile.html.j2 | 2 +- app/users/routes.py | 114 +-------- 15 files changed, 256 insertions(+), 416 deletions(-) create mode 100644 app/settings/__init__.py rename app/{users => settings}/forms.py (93%) rename app/{users => settings}/json_routes.py (71%) create mode 100644 app/settings/routes.py create mode 100644 app/settings/utils.py create mode 100644 app/static/js/Requests/settings/settings.js delete mode 100644 app/static/js/Requests/users/users.js rename app/templates/{users => settings}/edit_profile.html.j2 (79%) delete mode 100644 app/templates/users/edit_profile_testing.html.j2 diff --git a/app/__init__.py b/app/__init__.py index bf465cbb..50e70fd2 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -91,6 +91,10 @@ def create_app(config: Config = Config) -> Flask: default_breadcrumb_root(services_blueprint, '.services') app.register_blueprint(services_blueprint, url_prefix='/services') + from .settings import bp as settings_blueprint + default_breadcrumb_root(settings_blueprint, '.settings') + app.register_blueprint(settings_blueprint, url_prefix='/settings') + from .users import bp as users_blueprint default_breadcrumb_root(users_blueprint, '.social_area.users') app.register_blueprint(users_blueprint, url_prefix='/users') diff --git a/app/settings/__init__.py b/app/settings/__init__.py new file mode 100644 index 00000000..aaa1cb1b --- /dev/null +++ b/app/settings/__init__.py @@ -0,0 +1,6 @@ +from flask import Blueprint + + +bp = Blueprint('settings', __name__) +from . import routes, json_routes + diff --git a/app/users/forms.py b/app/settings/forms.py similarity index 93% rename from app/users/forms.py rename to app/settings/forms.py index 73d72c8d..a814feee 100644 --- a/app/users/forms.py +++ b/app/settings/forms.py @@ -56,6 +56,9 @@ class EditProfileSettingsForm(FlaskForm): raise ValidationError('Username already in use') class EditPublicProfileInformationForm(FlaskForm): + show_email = BooleanField('Email') + show_last_seen = BooleanField('Last seen') + show_member_since = BooleanField('Member since') avatar = FileField( 'Image File', [FileSizeLimit(max_size_in_mb=2)] @@ -97,13 +100,6 @@ class EditPublicProfileInformationForm(FlaskForm): if not field.data.filename.lower().endswith('.jpg' or '.png' or '.jpeg'): raise ValidationError('only .jpg, .png and .jpeg!') -class EditPrivacySettingsForm(FlaskForm): - is_public = BooleanField('Public profile') - show_email = BooleanField('Show email') - show_last_seen = BooleanField('Show last seen') - show_member_since = BooleanField('Show member since') - submit = SubmitField() - class ChangePasswordForm(FlaskForm): password = PasswordField('Old password', validators=[DataRequired()]) new_password = PasswordField( diff --git a/app/users/json_routes.py b/app/settings/json_routes.py similarity index 71% rename from app/users/json_routes.py rename to app/settings/json_routes.py index 0e421078..77b421d9 100644 --- a/app/users/json_routes.py +++ b/app/settings/json_routes.py @@ -1,4 +1,4 @@ -from flask import abort, current_app +from flask import abort, current_app, request from flask_login import current_user, login_required, logout_user from threading import Thread import os @@ -52,3 +52,22 @@ def delete_profile_avatar(user_id): 'message': f'Avatar marked for deletion' } return response_data, 202 + +@bp.route('//is_public', methods=['PUT']) +@login_required +@content_negotiation(consumes='application/json', produces='application/json') +def update_user_is_public(user_id): + is_public = request.json + if not isinstance(is_public, bool): + abort(400) + user = User.query.get_or_404(user_id) + user.is_public = is_public + db.session.commit() + response_data = { + 'message': ( + f'User "{user.username}" is now' + f' {"public" if is_public else "private"}' + ), + 'category': 'corpus' + } + return response_data, 200 diff --git a/app/settings/routes.py b/app/settings/routes.py new file mode 100644 index 00000000..bc36a9c8 --- /dev/null +++ b/app/settings/routes.py @@ -0,0 +1,108 @@ +from flask import ( + abort, + flash, + redirect, + render_template, + url_for +) +from flask_breadcrumbs import register_breadcrumb +from flask_login import current_user, login_required +import os +from app import db +from app.models import Avatar, Corpus, ProfilePrivacySettings, User +from . import bp +from .forms import ( + ChangePasswordForm, + EditNotificationSettingsForm, + EditProfileSettingsForm, + EditPublicProfileInformationForm +) +from .utils import user_endpoint_arguments_constructor as user_eac + + +@bp.route('//edit', methods=['GET', 'POST']) +@register_breadcrumb(bp, 'breadcrumbs.settings', 'settingsSettings', endpoint_arguments_constructor=user_eac) +@login_required +def edit_profile(user_id): + user = User.query.get_or_404(user_id) + if not (user == current_user or current_user.is_administrator()): + abort(403) + # region forms + edit_profile_settings_form = EditProfileSettingsForm( + current_user, + data=current_user.to_json_serializeable(), + prefix='edit-profile-settings-form' + ) + edit_public_profile_information_form = EditPublicProfileInformationForm( + data=current_user.to_json_serializeable(), + prefix='edit-public-profile-information-form' + ) + change_password_form = ChangePasswordForm( + current_user, + prefix='change-password-form' + ) + edit_notification_settings_form = EditNotificationSettingsForm( + data=current_user.to_json_serializeable(), + prefix='edit-notification-settings-form' + ) + # endregion forms + # region handle edit profile settings form + if edit_profile_settings_form.validate_on_submit(): + current_user.email = edit_profile_settings_form.email.data + current_user.username = edit_profile_settings_form.username.data + db.session.commit() + flash('Profile settings updated') + return redirect(url_for('users.user', user_id=user.id)) + # endregion handle edit profile settings forms + # region handle edit public profile information form + if edit_public_profile_information_form.submit.data and edit_public_profile_information_form.validate(): + print(edit_public_profile_information_form.show_email.data) + if edit_public_profile_information_form.show_email.data: + current_user.add_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL) + else: + current_user.remove_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL) + if edit_public_profile_information_form.show_last_seen.data: + current_user.add_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN) + else: + current_user.remove_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN) + if edit_public_profile_information_form.show_member_since.data: + current_user.add_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE) + else: + current_user.remove_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE) + if edit_public_profile_information_form.avatar.data: + try: + Avatar.create(edit_public_profile_information_form.avatar.data, user=current_user) + except (AttributeError, OSError): + abort(500) + current_user.about_me = edit_public_profile_information_form.about_me.data + current_user.location = edit_public_profile_information_form.location.data + current_user.organization = edit_public_profile_information_form.organization.data + current_user.website = edit_public_profile_information_form.website.data + current_user.full_name = edit_public_profile_information_form.full_name.data + db.session.commit() + flash('Profile settings updated') + return redirect(url_for('users.user', user_id=user.id)) + # endregion handle edit public profile information form + # region handle change_password_form POST + if change_password_form.submit.data and change_password_form.validate(): + current_user.password = change_password_form.new_password.data + db.session.commit() + flash('Your changes have been saved') + return redirect(url_for('.edit_profile', user_id=user.id)) + # endregion handle change_password_form POST + # region handle edit_notification_settings_form POST + if edit_notification_settings_form.submit and edit_notification_settings_form.validate(): + current_user.setting_job_status_mail_notification_level = edit_notification_settings_form.job_status_mail_notification_level.data + db.session.commit() + flash('Your changes have been saved') + return redirect(url_for('.edit_profile', user_id=user.id)) + # endregion handle edit_notification_settings_form POST + return render_template( + 'settings/edit_profile.html.j2', + edit_profile_settings_form=edit_profile_settings_form, + edit_public_profile_information_form=edit_public_profile_information_form, + change_password_form=change_password_form, + edit_notification_settings_form=edit_notification_settings_form, + user=user, + title='Settings' + ) diff --git a/app/settings/utils.py b/app/settings/utils.py new file mode 100644 index 00000000..53572740 --- /dev/null +++ b/app/settings/utils.py @@ -0,0 +1,6 @@ +from flask import request, url_for +from app.models import User + + +def user_endpoint_arguments_constructor(): + return {'user_id': request.view_args['user_id']} diff --git a/app/static/js/Requests/settings/settings.js b/app/static/js/Requests/settings/settings.js new file mode 100644 index 00000000..55ae94b2 --- /dev/null +++ b/app/static/js/Requests/settings/settings.js @@ -0,0 +1,35 @@ +/***************************************************************************** +* Users * +* Fetch requests for /users routes * +*****************************************************************************/ +Requests.settings = {}; + +Requests.settings.entity = {}; + +Requests.settings.entity.delete = (userId) => { + let input = `/settings/${userId}`; + let init = { + method: 'DELETE' + }; + return Requests.JSONfetch(input, init); +} + +Requests.settings.entity.deleteAvatar = (userId) => { + let input = `/settings/${userId}/avatar`; + let init = { + method: 'DELETE' + }; + return Requests.JSONfetch(input, init); +} + +Requests.settings.entity.isPublic = {}; + +Requests.settings.entity.isPublic.update = (userId, isPublic) => { + let input = `/settings/${userId}/is_public`; + let init = { + method: 'PUT', + body: JSON.stringify(isPublic) + }; + return Requests.JSONfetch(input, init); +}; + diff --git a/app/static/js/Requests/users/users.js b/app/static/js/Requests/users/users.js deleted file mode 100644 index 0ae1434e..00000000 --- a/app/static/js/Requests/users/users.js +++ /dev/null @@ -1,23 +0,0 @@ -/***************************************************************************** -* Users * -* Fetch requests for /users routes * -*****************************************************************************/ -Requests.users = {}; - -Requests.users.entity = {}; - -Requests.users.entity.delete = (userId) => { - let input = `/users/${userId}`; - let init = { - method: 'DELETE' - }; - return Requests.JSONfetch(input, init); -} - -Requests.users.entity.deleteAvatar = (userId) => { - let input = `/users/${userId}/avatar`; - let init = { - method: 'DELETE' - }; - return Requests.JSONfetch(input, init); -} diff --git a/app/templates/_navbar.html.j2 b/app/templates/_navbar.html.j2 index 467db7dd..8819d076 100644 --- a/app/templates/_navbar.html.j2 +++ b/app/templates/_navbar.html.j2 @@ -32,7 +32,7 @@