diff --git a/app/__init__.py b/app/__init__.py index 78e208d1e29f0443cf944cdc2d027fd3ddd54bdd..960d4e90ff926e2f36f9765a79eae95646806667 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -77,6 +77,9 @@ def create_app(config: Config = Config) -> Flask: from .main import bp as main_blueprint app.register_blueprint(main_blueprint, url_prefix='/') + + from .profile import bp as profile_blueprint + app.register_blueprint(profile_blueprint, url_prefix='/profile') from .services import bp as services_blueprint app.register_blueprint(services_blueprint, url_prefix='/services') diff --git a/app/profile/__init__.py b/app/profile/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c8f843f41cbac2cd0e3d195d6dd8a42d5960edba --- /dev/null +++ b/app/profile/__init__.py @@ -0,0 +1,5 @@ +from flask import Blueprint + + +bp = Blueprint('profile', __name__) +from . import routes # noqa diff --git a/app/profile/routes.py b/app/profile/routes.py new file mode 100644 index 0000000000000000000000000000000000000000..5487d4173df7a0734d86bb3ea3e83f81827dd3ea --- /dev/null +++ b/app/profile/routes.py @@ -0,0 +1,24 @@ +from flask import render_template, url_for +from flask_login import current_user, login_required +from app import db +from app.models import User +from . import bp + + +@bp.route('') +@login_required +def profile(): + user_image = 'static/images/user_avatar.png' + user_name = current_user.username + last_seen = current_user.last_seen + member_since = current_user.member_since + email = current_user.email + role = current_user.role + return render_template('profile/profile_page.html.j2', + user_image=user_image, + user_name=user_name, + last_seen=last_seen, + member_since=member_since, + email=email, + role=role) + diff --git a/app/settings/forms.py b/app/settings/forms.py index 4e7eacf2b35f2149b820a64419ff8311980af6ec..1403b501d8cf52c75e80bcd9625f88f7c1a77095 100644 --- a/app/settings/forms.py +++ b/app/settings/forms.py @@ -1,10 +1,12 @@ from flask_wtf import FlaskForm from wtforms import ( BooleanField, + FileField, PasswordField, SelectField, StringField, SubmitField, + TextAreaField, ValidationError ) from wtforms.validators import ( @@ -47,6 +49,9 @@ class ChangePasswordForm(FlaskForm): class EditGeneralSettingsForm(FlaskForm): + user_avatar = FileField( + 'Image File' + ) email = StringField( 'E-Mail', validators=[InputRequired(), Length(max=254), Email()] @@ -65,8 +70,41 @@ class EditGeneralSettingsForm(FlaskForm): ) ] ) + full_name = StringField( + 'Full name', + validators=[Length(max=128)] + ) + bio = TextAreaField( + 'About me', + validators=[ + Length(max=254) + ] + ) + website = StringField( + 'Website', + validators=[ + Length(max=254) + ] + ) + organization = StringField( + 'Organization', + validators=[ + Length(max=128) + ] + ) + location = StringField( + 'Location', + validators=[ + Length(max=128) + ] + ) + submit = SubmitField() + def validate_image_file(self, field): + if not field.data.filename.lower().endswith('.jpg' or '.png' or '.jpeg'): + raise ValidationError('only .jpg, .png and .jpeg!') + def __init__(self, user, *args, **kwargs): super().__init__(*args, **kwargs) self.user = user @@ -96,3 +134,9 @@ class EditNotificationSettingsForm(FlaskForm): (x.name, x.name.capitalize()) for x in UserSettingJobStatusMailNotificationLevel ] + +class EditPrivacySettingsForm(FlaskForm): + public_profile = BooleanField( + 'Public profile' + ) + submit = SubmitField() diff --git a/app/settings/routes.py b/app/settings/routes.py index 26b7fdda6b0a561fd468422b0591b39feb7d0da8..424001366aa409f0af9a090270f7d1f27cdecd3e 100644 --- a/app/settings/routes.py +++ b/app/settings/routes.py @@ -6,7 +6,8 @@ from . import bp from .forms import ( ChangePasswordForm, EditGeneralSettingsForm, - EditNotificationSettingsForm + EditNotificationSettingsForm, + EditPrivacySettingsForm ) @@ -26,7 +27,10 @@ def settings(): data=current_user.to_json_serializeable(), prefix='edit-notification-settings-form' ) - + edit_privacy_settings_form = EditPrivacySettingsForm( + data=current_user.to_json_serializeable(), + prefix='edit-privacy-settings-form' + ) if change_password_form.submit.data and change_password_form.validate(): current_user.password = change_password_form.new_password.data db.session.commit() @@ -49,10 +53,13 @@ def settings(): db.session.commit() flash('Your changes have been saved') return redirect(url_for('.settings')) + user_image = 'static/images/user_avatar.png' return render_template( 'settings/settings.html.j2', change_password_form=change_password_form, edit_general_settings_form=edit_general_settings_form, edit_notification_settings_form=edit_notification_settings_form, - title='Settings' + edit_privacy_settings_form=edit_privacy_settings_form, + user_image=user_image, + title='Profile & Settings' ) diff --git a/app/static/images/user_avatar.png b/app/static/images/user_avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..09892098aa943af5fe95e7dd434a07fe03e7ccd1 Binary files /dev/null and b/app/static/images/user_avatar.png differ diff --git a/app/templates/profile/profile_page.html.j2 b/app/templates/profile/profile_page.html.j2 new file mode 100644 index 0000000000000000000000000000000000000000..cf724303800ca9cf8cb4ecde6a04f469923d8c7d --- /dev/null +++ b/app/templates/profile/profile_page.html.j2 @@ -0,0 +1,42 @@ +{% extends "base.html.j2" %} +{% import "materialize/wtf.html.j2" as wtf %} + +{% block page_content %} + <div class="container"> + <div class="row"> + <div class="col s12"> + <div class="card"> + <div class="card-content"> + <div class="row"> + <div class="col s1"></div> + <div class="col s3"> + <img src="{{ user_image }}" alt="user-image" class="circle responsive-img"> + </div> + <div class="col s1"></div> + <div class="col s7"> + <h3>{{ user_name }}</h3> + <div class="chip">Last seen: {{ last_seen }}</div> + <p><span class="material-icons" style="margin-right:20px; margin-top:20px;">location_on</span><i>Bielefeld</i></p> + <p></p> + <br> + <p>Inga Kirschnick</p> + <p>Bio</p> + </div> + </div> + <div class="row"> + <div class="col s1"></div> + <div class="col s6"> + <p>{{ email }}</p> + <p>Webseite</p> + <p>Organization</p> + <p>Member since: {{ member_since }}</p> + <p>Role: {{ role }}</p> + </div> + </div> + </div> + </div> + </div> + </div> + </div> + </div> +{% endblock page_content %} diff --git a/app/templates/settings/settings.html.j2 b/app/templates/settings/settings.html.j2 index e229a382656dd738cd3627ac72056355ec3395ad..a61e77ada1c7f57d5cf23bdc5e00cf2314a26574 100644 --- a/app/templates/settings/settings.html.j2 +++ b/app/templates/settings/settings.html.j2 @@ -14,63 +14,99 @@ {{ edit_general_settings_form.hidden_tag() }} <div class="card"> <div class="card-content"> - <span class="card-title">General settings</span> - {{ wtf.render_field(edit_general_settings_form.username, material_icon='person') }} - {{ wtf.render_field(edit_general_settings_form.email, material_icon='email') }} + <span class="card-title" style="margin-bottom: 40px;">Your profile</span> + <div class="row"> + <div class="col s1"></div> + <div class="col s3"> + <img src="{{ user_image }}" alt="user-image" class="circle responsive-img"> + {{wtf.render_field(edit_general_settings_form.user_avatar, accept='image/*', class='file-path validate')}} + </div> + <div class="col s1"></div> + <div class="col s7"> + {{ wtf.render_field(edit_general_settings_form.username, material_icon='person') }} + {{ wtf.render_field(edit_general_settings_form.email, material_icon='email') }} + {{ wtf.render_field(edit_general_settings_form.full_name, material_icon='badge') }} + {{ wtf.render_field(edit_general_settings_form.bio, material_icon='description') }} + </div> + </div> + <div class="row"> + <div class="col s6"> + {{ wtf.render_field(edit_general_settings_form.website, material_icon='laptop') }} + {{ wtf.render_field(edit_general_settings_form.organization, material_icon='business') }} + {{ wtf.render_field(edit_general_settings_form.location, material_icon='location_on') }} + </div> + </div> </div> <div class="card-action"> <div class="right-align"> {{ wtf.render_field(edit_general_settings_form.submit, material_icon='send') }} </div> </div> - </form> - </div> - </form> + </div> + </form> - <form method="POST"> - {{ edit_notification_settings_form.hidden_tag() }} <div class="card"> <div class="card-content"> - <span class="card-title">Notification settings</span> - {{ wtf.render_field(edit_notification_settings_form.job_status_mail_notification_level, material_icon='notifications') }} - </div> - <div class="card-action"> - <div class="right-align"> - {{ wtf.render_field(edit_notification_settings_form.submit, material_icon='send') }} + <span class="card-title" style="margin-bottom: 40px;">Settings</span> + <form method="POST"> + {{ edit_notification_settings_form.hidden_tag() }} + <div class="card"> + <div class="card-content"> + <span class="card-title">Notification settings</span> + {{ wtf.render_field(edit_notification_settings_form.job_status_mail_notification_level, material_icon='notifications') }} + </div> + <div class="card-action"> + <div class="right-align"> + {{ wtf.render_field(edit_notification_settings_form.submit, material_icon='send') }} + </div> + </div> + </div> + </form> + + <div class="card"> + <div class="card-content"> + <span class="card-title">Privacy settings</span> + {{ wtf.render_field(edit_privacy_settings_form.public_profile) }} + </div> + <div class="card-action"> + <div class="right-align"> + {{ wtf.render_field(edit_notification_settings_form.submit, material_icon='send') }} + </div> + </div> </div> - </div> - </div> - </form> - <form method="POST"> - {{ change_password_form.hidden_tag() }} - <div class="card"> - <div class="card-content"> - <span class="card-title">Change Password</span> - {{ wtf.render_field(change_password_form.password, material_icon='vpn_key') }} - {{ wtf.render_field(change_password_form.new_password, material_icon='vpn_key') }} - {{ wtf.render_field(change_password_form.new_password_2, material_icon='vpn_key') }} - </div> - <div class="card-action"> - <div class="right-align"> - {{ wtf.render_field(change_password_form.submit, material_icon='send') }} + <form method="POST"> + {{ change_password_form.hidden_tag() }} + <div class="card"> + <div class="card-content"> + <span class="card-title">Change Password</span> + {{ wtf.render_field(change_password_form.password, material_icon='vpn_key') }} + {{ wtf.render_field(change_password_form.new_password, material_icon='vpn_key') }} + {{ wtf.render_field(change_password_form.new_password_2, material_icon='vpn_key') }} + </div> + <div class="card-action"> + <div class="right-align"> + {{ wtf.render_field(change_password_form.submit, material_icon='send') }} + </div> + </div> + </div> + </form> + + <div class="card"> + <div class="card-content"> + <span class="card-title">Delete account</span> + <p>Deleting an account has the following effects:</p> + <ul> + <li>All data associated with your corpora and jobs will be permanently deleted.</li> + <li>All settings will be permanently deleted.</li> + </ul> + </div> + <div class="card-action right-align"> + <a class="btn red waves-effect waves-light" id="delete-user"><i class="material-icons left">delete</i>Delete</a> + </div> </div> </div> </div> - </form> - - <div class="card"> - <div class="card-content"> - <span class="card-title">Delete account</span> - <p>Deleting an account has the following effects:</p> - <ul> - <li>All data associated with your corpora and jobs will be permanently deleted.</li> - <li>All settings will be permanently deleted.</li> - </ul> - </div> - <div class="card-action right-align"> - <a class="btn red waves-effect waves-light" id="delete-user"><i class="material-icons left">delete</i>Delete</a> - </div> </div> </div> </div>