From 646f735ab23653557f985b9d676ea8c6f382326a Mon Sep 17 00:00:00 2001 From: Inga Kirschnick Date: Mon, 13 Mar 2023 13:29:01 +0100 Subject: [PATCH 1/2] Reviewed User package and invite user optical fix --- app/corpora/routes.py | 3 + app/jobs/json_routes.py | 22 +-- app/static/css/materialize/fixes.css | 5 + app/static/js/Requests/users/users.js | 23 +++ app/static/js/Utils.js | 236 ----------------------- app/templates/_scripts.html.j2 | 3 +- app/templates/_sidenav.html.j2 | 2 +- app/templates/corpora/corpus.js.j2 | 13 +- app/templates/settings/settings.html.j2 | 20 +- app/templates/users/edit_profile.html.j2 | 55 ++---- app/templates/users/edit_profile.js.j2 | 36 ++++ app/templates/users/profile.html.j2 | 25 --- app/templates/users/profile.js.j2 | 19 ++ app/users/__init__.py | 3 +- app/users/json_routes.py | 54 ++++++ app/users/routes.py | 35 ---- 16 files changed, 190 insertions(+), 364 deletions(-) create mode 100644 app/static/js/Requests/users/users.js create mode 100644 app/templates/users/edit_profile.js.j2 create mode 100644 app/templates/users/profile.js.j2 create mode 100644 app/users/json_routes.py diff --git a/app/corpora/routes.py b/app/corpora/routes.py index 5484fb50..322c3657 100644 --- a/app/corpora/routes.py +++ b/app/corpora/routes.py @@ -13,6 +13,7 @@ from app.models import ( Corpus, CorpusFollowerAssociation, CorpusFollowerRole, + User ) from . import bp from .forms import CreateCorpusForm @@ -46,12 +47,14 @@ def create_corpus(): def corpus(corpus_id): corpus = Corpus.query.get_or_404(corpus_id) corpus_follower_roles = CorpusFollowerRole.query.all() + users = [u.to_json_serializeable() for u in User.query.filter(User.is_public == True, User.id != current_user.id).all()] # TODO: Add URL query option to toggle view if corpus.user == current_user or current_user.is_administrator(): return render_template( 'corpora/corpus.html.j2', corpus=corpus, corpus_follower_roles=corpus_follower_roles, + users = users, title='Corpus' ) if current_user.is_following_corpus(corpus) or corpus.is_public: diff --git a/app/jobs/json_routes.py b/app/jobs/json_routes.py index 50846bae..3562470f 100644 --- a/app/jobs/json_routes.py +++ b/app/jobs/json_routes.py @@ -1,4 +1,4 @@ -from flask import abort, current_app, jsonify +from flask import abort, current_app from flask_login import current_user, login_required from threading import Thread import os @@ -7,6 +7,7 @@ from app.decorators import admin_required, content_negotiation from app.models import Job, JobStatus from . import bp + @bp.route('/', methods=['DELETE']) @login_required @content_negotiation(produces='application/json') @@ -26,12 +27,10 @@ def delete_job(job_id): ) thread.start() response_data = { - 'message': \ - f'Job "{job.title}" marked for deletion' + 'message': f'Job "{job.title}" marked for deletion' } - response = jsonify(response_data) - response.status_code = 202 - return response + return response_data, 202 + @bp.route('//log') @login_required @@ -48,9 +47,7 @@ def job_log(job_id): 'message': '', 'jobLog': log } - response = jsonify(response_data) - response.status_code = 200 - return response + return response_data, 200 @bp.route('//restart', methods=['POST']) @@ -75,9 +72,6 @@ def restart_job(job_id): ) thread.start() response_data = { - 'message': \ - f'Job "{job.title}" marked for restarting' + 'message': f'Job "{job.title}" marked for restarting' } - response = jsonify(response_data) - response.status_code = 202 - return response + return response_data, 202 diff --git a/app/static/css/materialize/fixes.css b/app/static/css/materialize/fixes.css index b44af75f..75003a1f 100644 --- a/app/static/css/materialize/fixes.css +++ b/app/static/css/materialize/fixes.css @@ -1,3 +1,8 @@ .parallax-container .parallax { z-index: 0; } + +.autocomplete-content { + width: 100% !important; + left: 0 !important; +} diff --git a/app/static/js/Requests/users/users.js b/app/static/js/Requests/users/users.js new file mode 100644 index 00000000..0ae1434e --- /dev/null +++ b/app/static/js/Requests/users/users.js @@ -0,0 +1,23 @@ +/***************************************************************************** +* 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/static/js/Utils.js b/app/static/js/Utils.js index 822ab775..79879c75 100644 --- a/app/static/js/Utils.js +++ b/app/static/js/Utils.js @@ -69,240 +69,4 @@ class Utils { return Utils.mergeObjectsDeep(mergedObject, ...objects.slice(2)); } - static deleteProfileAvatarRequest(userId) { - return new Promise((resolve, reject) => { - let modalElement = Utils.HTMLToElement( - ` - - ` - ); - document.querySelector('#modals').appendChild(modalElement); - let modal = M.Modal.init( - modalElement, - { - dismissible: false, - onCloseEnd: () => { - modal.destroy(); - modalElement.remove(); - } - } - ); - - let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]'); - confirmElement.addEventListener('click', (event) => { - fetch(`/users/${userId}/avatar`, {method: 'DELETE', headers: {Accept: 'application/json'}}) - .then( - (response) => { - if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - app.flash(`Avatar marked for deletion`); - resolve(response); - }, - (response) => { - app.flash('Something went wrong', 'error'); - reject(response); - } - ); - }); - modal.open(); - }); - } - - // static deleteJobRequest(userId, jobId) { - // return new Promise((resolve, reject) => { - // let job; - // try { - // job = app.data.users[userId].jobs[jobId]; - // } catch (error) { - // job = {}; - // } - - // let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]'); - // confirmElement.addEventListener('click', (event) => { - // let jobTitle = job?.title; - // fetch(`/jobs/${jobId}`, {method: 'DELETE', headers: {Accept: 'application/json'}}) - // .then( - // (response) => { - // if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - // if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - // app.flash(`Job "${jobTitle}" marked for deletion`, 'job'); - // resolve(response); - // }, - // (response) => { - // app.flash('Something went wrong', 'error'); - // reject(response); - // } - // ); - // }); - // modal.open(); - // }); - // } - - // static getJobLogRequest(userId, jobId) { - // return new Promise((resolve, reject) => { - // fetch(`/jobs/${jobId}/log`, {method: 'GET', headers: {Accept: 'application/json, text/plain'}}) - // .then( - // (response) => { - // if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - // if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - // return response.text(); - // }, - // (response) => { - // app.flash('Something went wrong', 'error'); - // reject(response); - // } - // ) - // .then( - // (text) => { - // let modalElement = Utils.HTMLToElement( - // ` - // - // ` - // ); - // document.querySelector('#modals').appendChild(modalElement); - // let modal = M.Modal.init( - // modalElement, - // { - // onCloseEnd: () => { - // modal.destroy(); - // modalElement.remove(); - // } - // } - // ); - // modal.open(); - // resolve(text); - // } - // ); - // }); - // } - - // static restartJobRequest(userId, jobId) { - // return new Promise((resolve, reject) => { - // let job; - // try { - // job = app.data.users[userId].jobs[jobId]; - // } catch (error) { - // job = {}; - // } - - // let modalElement = Utils.HTMLToElement( - // ` - // - // ` - // ); - // document.querySelector('#modals').appendChild(modalElement); - // let modal = M.Modal.init( - // modalElement, - // { - // dismissible: false, - // onCloseEnd: () => { - // modal.destroy(); - // modalElement.remove(); - // } - // } - // ); - - // let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]'); - // confirmElement.addEventListener('click', (event) => { - // let jobTitle = job?.title; - // fetch(`/jobs/${jobId}/restart`, {method: 'POST', headers: {Accept: 'application/json'}}) - // .then( - // (response) => { - // if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - // if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - // if (response.status === 409) {app.flash('Conflict', 'error'); reject(response);} - // app.flash(`Job "${jobTitle}" restarted.`, 'job'); - // resolve(response); - // }, - // (response) => { - // app.flash('Something went wrong', 'error'); - // reject(response); - // } - // ); - // }); - // modal.open(); - // }); - // } - - static deleteUserRequest(userId) { - return new Promise((resolve, reject) => { - let user; - try { - user = app.data.users[userId]; - } catch (error) { - user = {}; - } - - let modalElement = Utils.HTMLToElement( - ` - - ` - ); - document.querySelector('#modals').appendChild(modalElement); - let modal = M.Modal.init( - modalElement, - { - dismissible: false, - onCloseEnd: () => { - modal.destroy(); - modalElement.remove(); - } - } - ); - - let confirmElement = modalElement.querySelector('.action-button[data-action="confirm"]'); - confirmElement.addEventListener('click', (event) => { - let userName = user?.username; - fetch(`/users/${userId}`, {method: 'DELETE', headers: {Accept: 'application/json'}}) - .then( - (response) => { - if (response.status === 403) {app.flash('Forbidden', 'error'); reject(response);} - if (response.status === 404) {app.flash('Not Found', 'error'); reject(response);} - app.flash(`User "${userName}" marked for deletion`); - resolve(response); - }, - (response) => { - app.flash('Something went wrong', 'error'); - reject(response); - } - ); - }); - modal.open(); - }); - } } diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 6dc253b4..11398cb9 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -64,7 +64,8 @@ 'js/Requests/corpora/corpora.js', 'js/Requests/corpora/files.js', 'js/Requests/corpora/followers.js', - 'js/Requests/jobs/jobs.js' + 'js/Requests/jobs/jobs.js', + 'js/Requests/users/users.js' %} {%- endassets %} diff --git a/app/templates/_sidenav.html.j2 b/app/templates/_sidenav.html.j2 index 0027772e..2d55999a 100644 --- a/app/templates/_sidenav.html.j2 +++ b/app/templates/_sidenav.html.j2 @@ -39,7 +39,7 @@
  • Account
  • settingsGeneral Settings
  • -
  • contact_pageProfile settings
  • +
  • contact_pageProfile Settings
  • Log out
  • {% if current_user.can('ADMINISTRATE') or current_user.can('USE_API') %}
  • diff --git a/app/templates/corpora/corpus.js.j2 b/app/templates/corpora/corpus.js.j2 index 7e815cf3..016b0b6a 100644 --- a/app/templates/corpora/corpus.js.j2 +++ b/app/templates/corpora/corpus.js.j2 @@ -26,16 +26,17 @@ deleteModalDeleteButtonElement.addEventListener('click', (event) => { let inviteUserModalElement = document.querySelector('#invite-user-modal'); let inviteUserModalSearchElement = document.querySelector('#invite-user-modal-search'); let inviteUserModalInviteButtonElement = document.querySelector('#invite-user-modal-invite-button'); +const users = {}; + +for (let user of {{ users|tojson }}) { + users[user.username] = user.avatar ? `/users/${user.id}/avatar` : '/static/images/user_avatar.png'; +} let inviteUserModalSearch = M.Chips.init( inviteUserModalSearchElement, { autocompleteOptions: { - data: { - 'nopaque': '/users/3V8Aqpg74JvxOd9o/avatar', - 'pjentsch': '/users/3V8Aqpg74JvxOd9o/avatar', - 'pjentsch2': '/users/3V8Aqpg74JvxOd9o/avatar' - } + data: users }, limit: 3, onChipAdd: (a, chipElement) => { @@ -43,7 +44,7 @@ let inviteUserModalSearch = M.Chips.init( chipElement.firstElementChild.click(); } }, - placeholder: 'Enter a username', + placeholder: 'Enter username', secondaryPlaceholder: 'Add more users' } ); diff --git a/app/templates/settings/settings.html.j2 b/app/templates/settings/settings.html.j2 index e9cfb97b..9210d36f 100644 --- a/app/templates/settings/settings.html.j2 +++ b/app/templates/settings/settings.html.j2 @@ -51,7 +51,7 @@ @@ -59,12 +59,26 @@ {% endblock page_content %} +{% block modals%} + +{% endblock modals %} + {% block scripts %} {{ super() }} {% endblock scripts %} diff --git a/app/templates/users/edit_profile.html.j2 b/app/templates/users/edit_profile.html.j2 index 11b4b4d2..1b2ef3f9 100644 --- a/app/templates/users/edit_profile.html.j2 +++ b/app/templates/users/edit_profile.html.j2 @@ -79,7 +79,7 @@
    @@ -111,44 +111,15 @@
    {% endblock page_content %} -{% block scripts %} -{{ super() }} - -{% endblock scripts %} +{% block modals %} + +{% endblock modals %} diff --git a/app/templates/users/edit_profile.js.j2 b/app/templates/users/edit_profile.js.j2 new file mode 100644 index 00000000..a5058d85 --- /dev/null +++ b/app/templates/users/edit_profile.js.j2 @@ -0,0 +1,36 @@ +let publicProfile = document.querySelector('#public-profile'); +let disableButtons = document.querySelectorAll('[data-action="disable"]'); +let deleteButton = document.querySelector('#delete-avatar'); +let avatar = document.querySelector('#avatar'); +let avatarUpload = document.querySelector('#avatar-upload'); + +for (let disableButton of disableButtons) { + disableButton.disabled = !publicProfile.checked; +} + +publicProfile.addEventListener('change', () => { + if (publicProfile.checked) { + for (let disableButton of disableButtons) { + disableButton.disabled = false; + } + } else { + for (let disableButton of disableButtons) { + disableButton.checked = false; + disableButton.disabled = true; + } + } +}); + +avatarUpload.addEventListener('change', function() { + let file = this.files[0]; + avatar.src = URL.createObjectURL(file); +}); + +deleteButton.addEventListener('click', () => { + Requests.users.entity.deleteAvatar({{ user.hashid|tojson }}) + .then( + (response) => { + avatar.src = "{{ url_for('static', filename='images/user_avatar.png') }}"; + } + ); +}); diff --git a/app/templates/users/profile.html.j2 b/app/templates/users/profile.html.j2 index 4a9a4c13..e73a3b7a 100644 --- a/app/templates/users/profile.html.j2 +++ b/app/templates/users/profile.html.j2 @@ -111,28 +111,3 @@
    {% endblock page_content %} -{% block scripts %} -{{ super() }} - -{% endblock scripts %} - diff --git a/app/templates/users/profile.js.j2 b/app/templates/users/profile.js.j2 new file mode 100644 index 00000000..5f729f18 --- /dev/null +++ b/app/templates/users/profile.js.j2 @@ -0,0 +1,19 @@ +let publicInformationBadge = document.querySelector('#public-information-badge'); +if ("{{ user.id }}" == "{{ current_user.hashid }}") { + if ("{{ user.is_public }}" == "True") { + publicInformationBadge.dataset.badgeCaption = 'Your profile is public'; + publicInformationBadge.classList.add('green'); + publicInformationBadge.classList.remove('red'); + } else { + publicInformationBadge.dataset.badgeCaption = 'Your profile is private'; + publicInformationBadge.classList.add('red'); + publicInformationBadge.classList.remove('green'); + } +} else { + publicInformationBadge.remove(); +} + +let followedCorpusList = new FollowedCorpusList(document.querySelector('.followed-corpus-list')); +followedCorpusList.add({{ followed_corpora|tojson }}); +let publicCorpusList = new PublicCorpusList(document.querySelector('.public-corpus-list')); +publicCorpusList.add({{ own_public_corpora|tojson }}); diff --git a/app/users/__init__.py b/app/users/__init__.py index 885cdbe2..705b3afa 100644 --- a/app/users/__init__.py +++ b/app/users/__init__.py @@ -2,4 +2,5 @@ from flask import Blueprint bp = Blueprint('users', __name__) -from . import events, routes +from . import events, routes, json_routes + diff --git a/app/users/json_routes.py b/app/users/json_routes.py new file mode 100644 index 00000000..0e421078 --- /dev/null +++ b/app/users/json_routes.py @@ -0,0 +1,54 @@ +from flask import abort, current_app +from flask_login import current_user, login_required, logout_user +from threading import Thread +import os +from app import db +from app.decorators import content_negotiation +from app.models import Avatar, User +from . import bp + +@bp.route('/', methods=['DELETE']) +@login_required +@content_negotiation(produces='application/json') +def delete_user(user_id): + def _delete_user(app, user_id): + with app.app_context(): + user = User.query.get(user_id) + user.delete() + db.session.commit() + + user = User.query.get_or_404(user_id) + if not (user == current_user or current_user.is_administrator()): + abort(403) + thread = Thread( + target=_delete_user, + args=(current_app._get_current_object(), user_id) + ) + if user == current_user: + logout_user() + thread.start() + response_data = { + 'message': f'User "{user.username}" marked for deletion' + } + return response_data, 202 + +@bp.route('//avatar', methods=['DELETE']) +@content_negotiation(produces='application/json') +def delete_profile_avatar(user_id): + def _delete_avatar(app, avatar_id): + with app.app_context(): + avatar = Avatar.query.get(avatar_id) + avatar.delete() + db.session.commit() + user = User.query.get_or_404(user_id) + if user.avatar is None: + abort(404) + thread = Thread( + target=_delete_avatar, + args=(current_app._get_current_object(), user.avatar.id) + ) + thread.start() + response_data = { + 'message': f'Avatar marked for deletion' + } + return response_data, 202 diff --git a/app/users/routes.py b/app/users/routes.py index d573da34..5d8ff266 100644 --- a/app/users/routes.py +++ b/app/users/routes.py @@ -51,24 +51,7 @@ def user(user_id): user_id=user_id ) -@bp.route('/', methods=['DELETE']) -@login_required -def delete_user(user_id): - def _delete_user(app, user_id): - with app.app_context(): - user = User.query.get(user_id) - user.delete() - db.session.commit() - user = User.query.get_or_404(user_id) - if not (user == current_user or current_user.is_administrator()): - abort(403) - thread = Thread( - target=_delete_user, - args=(current_app._get_current_object(), user_id) - ) - thread.start() - return {}, 202 @bp.route('//avatar') def profile_avatar(user_id): @@ -86,24 +69,6 @@ def profile_avatar(user_id): ) -@bp.route('//avatar', methods=['DELETE']) -def delete_profile_avatar(user_id): - def _delete_avatar(app, avatar_id): - with app.app_context(): - avatar = Avatar.query.get(avatar_id) - avatar.delete() - db.session.commit() - user = User.query.get_or_404(user_id) - if user.avatar is None: - abort(404) - thread = Thread( - target=_delete_avatar, - args=(current_app._get_current_object(), user.avatar.id) - ) - thread.start() - return {}, 202 - - @bp.route('//edit', methods=['GET', 'POST']) def edit_profile(user_id): user = User.query.get_or_404(user_id) From 4c05c6cc18b4973bb158f978517eccb179fa7f75 Mon Sep 17 00:00:00 2001 From: Inga Kirschnick Date: Mon, 13 Mar 2023 15:04:44 +0100 Subject: [PATCH 2/2] merge settings into users route --- app/__init__.py | 3 - app/admin/routes.py | 2 +- app/settings/__init__.py | 5 -- app/settings/forms.py | 43 ----------- app/settings/routes.py | 39 ---------- app/templates/_navbar.html.j2 | 3 +- app/templates/_sidenav.html.j2 | 3 +- app/templates/settings/_breadcrumbs.html.j2 | 6 -- app/templates/settings/settings.html.j2 | 84 --------------------- app/templates/users/edit_profile.html.j2 | 77 +++++++++++++++++-- app/templates/users/edit_profile.js.j2 | 5 ++ app/users/forms.py | 43 ++++++++++- app/users/routes.py | 36 ++++++++- 13 files changed, 157 insertions(+), 192 deletions(-) delete mode 100644 app/settings/__init__.py delete mode 100644 app/settings/forms.py delete mode 100644 app/settings/routes.py delete mode 100644 app/templates/settings/_breadcrumbs.html.j2 delete mode 100644 app/templates/settings/settings.html.j2 diff --git a/app/__init__.py b/app/__init__.py index dcb58e47..09e479a8 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -81,9 +81,6 @@ def create_app(config: Config = Config) -> Flask: from .services import bp as services_blueprint app.register_blueprint(services_blueprint, url_prefix='/services') - from .settings import bp as settings_blueprint - app.register_blueprint(settings_blueprint, url_prefix='/settings') - from .users import bp as users_blueprint app.register_blueprint(users_blueprint, url_prefix='/users') diff --git a/app/admin/routes.py b/app/admin/routes.py index 08f219ab..95ce7730 100644 --- a/app/admin/routes.py +++ b/app/admin/routes.py @@ -4,7 +4,7 @@ from threading import Thread from app import db, hashids from app.decorators import admin_required from app.models import Role, User, UserSettingJobStatusMailNotificationLevel -from app.settings.forms import ( +from app.users.forms import ( EditNotificationSettingsForm ) from app.users.forms import EditProfileSettingsForm diff --git a/app/settings/__init__.py b/app/settings/__init__.py deleted file mode 100644 index 1dab5142..00000000 --- a/app/settings/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from flask import Blueprint - - -bp = Blueprint('settings', __name__) -from . import routes # noqa diff --git a/app/settings/forms.py b/app/settings/forms.py deleted file mode 100644 index e90d9dda..00000000 --- a/app/settings/forms.py +++ /dev/null @@ -1,43 +0,0 @@ -from flask_wtf import FlaskForm -from wtforms import PasswordField, SelectField, SubmitField, ValidationError -from wtforms.validators import DataRequired, EqualTo -from app.models import UserSettingJobStatusMailNotificationLevel - - -class ChangePasswordForm(FlaskForm): - password = PasswordField('Old password', validators=[DataRequired()]) - new_password = PasswordField( - 'New password', - validators=[ - DataRequired(), - EqualTo('new_password_2', message='Passwords must match') - ] - ) - new_password_2 = PasswordField( - 'New password confirmation', - validators=[ - DataRequired(), - EqualTo('new_password', message='Passwords must match') - ] - ) - submit = SubmitField() - - def __init__(self, user, *args, **kwargs): - super().__init__(*args, **kwargs) - self.user = user - - def validate_current_password(self, field): - if not self.user.verify_password(field.data): - raise ValidationError('Invalid password') - - -class EditNotificationSettingsForm(FlaskForm): - job_status_mail_notification_level = SelectField( - 'Job status mail notification level', - choices=[ - (x.name, x.name.capitalize()) - for x in UserSettingJobStatusMailNotificationLevel - ], - validators=[DataRequired()] - ) - submit = SubmitField() diff --git a/app/settings/routes.py b/app/settings/routes.py deleted file mode 100644 index a07869b8..00000000 --- a/app/settings/routes.py +++ /dev/null @@ -1,39 +0,0 @@ -from flask import flash, redirect, render_template, url_for -from flask_login import current_user, login_required -from app import db -from app.models import UserSettingJobStatusMailNotificationLevel -from . import bp -from .forms import ChangePasswordForm, EditNotificationSettingsForm - - -@bp.route('', methods=['GET', 'POST']) -@login_required -def settings(): - 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' - ) - # 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('.settings')) - # 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('.settings')) - # endregion handle edit_notification_settings_form POST - return render_template( - 'settings/settings.html.j2', - change_password_form=change_password_form, - edit_notification_settings_form=edit_notification_settings_form, - title='Settings' - ) diff --git a/app/templates/_navbar.html.j2 b/app/templates/_navbar.html.j2 index d943c0e4..c4c4d3b7 100644 --- a/app/templates/_navbar.html.j2 +++ b/app/templates/_navbar.html.j2 @@ -29,8 +29,7 @@