mirror of
https://gitlab.ub.uni-bielefeld.de/sfb1288inf/nopaque.git
synced 2024-12-24 10:34:17 +00:00
Merge branch 'template-rework' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into template-rework
This commit is contained in:
commit
4f8426c0b0
@ -28,22 +28,23 @@ def create_app(config_name):
|
||||
socketio.init_app(
|
||||
app, message_queue=config[config_name].SOCKETIO_MESSAGE_QUEUE_URI)
|
||||
|
||||
from . import events
|
||||
from .admin import admin as admin_blueprint
|
||||
with app.app_context():
|
||||
from . import events
|
||||
from .admin import admin as admin_blueprint
|
||||
from .auth import auth as auth_blueprint
|
||||
from .corpora import corpora as corpora_blueprint
|
||||
from .errors import errors as errors_blueprint
|
||||
from .jobs import jobs as jobs_blueprint
|
||||
from .main import main as main_blueprint
|
||||
from .services import services as services_blueprint
|
||||
from .settings import settings as settings_blueprint
|
||||
app.register_blueprint(admin_blueprint, url_prefix='/admin')
|
||||
from .auth import auth as auth_blueprint
|
||||
app.register_blueprint(auth_blueprint, url_prefix='/auth')
|
||||
from .corpora import corpora as corpora_blueprint
|
||||
app.register_blueprint(corpora_blueprint, url_prefix='/corpora')
|
||||
from .errors import errors as errors_blueprint
|
||||
app.register_blueprint(errors_blueprint)
|
||||
from .jobs import jobs as jobs_blueprint
|
||||
app.register_blueprint(jobs_blueprint, url_prefix='/jobs')
|
||||
from .main import main as main_blueprint
|
||||
app.register_blueprint(main_blueprint)
|
||||
from .profile import profile as profile_blueprint
|
||||
app.register_blueprint(profile_blueprint, url_prefix='/profile')
|
||||
from .services import services as services_blueprint
|
||||
app.register_blueprint(services_blueprint, url_prefix='/services')
|
||||
app.register_blueprint(settings_blueprint, url_prefix='/settings')
|
||||
|
||||
return app
|
||||
|
@ -8,10 +8,10 @@ from ..models import Role, User
|
||||
from ..profile import tasks as profile_tasks
|
||||
|
||||
|
||||
@admin.route('/')
|
||||
@admin.route('/users')
|
||||
@login_required
|
||||
@admin_required
|
||||
def index():
|
||||
def users():
|
||||
users = User.query.all()
|
||||
users = [dict(username=u.username,
|
||||
email=u.email,
|
||||
@ -19,21 +19,18 @@ def index():
|
||||
confirmed=u.confirmed,
|
||||
id=u.id)
|
||||
for u in users]
|
||||
return render_template('admin/index.html.j2',
|
||||
title='Administration tools',
|
||||
users=users)
|
||||
return render_template('admin/users.html.j2', title='Users', users=users)
|
||||
|
||||
|
||||
@admin.route('/user/<int:user_id>')
|
||||
@admin.route('/users/<int:user_id>')
|
||||
@login_required
|
||||
@admin_required
|
||||
def user(user_id):
|
||||
user = User.query.get_or_404(user_id)
|
||||
return render_template('admin/user.html.j2', title='Administration: User',
|
||||
user=user)
|
||||
return render_template('admin/user.html.j2', title='Edit user', user=user)
|
||||
|
||||
|
||||
@admin.route('/user/<int:user_id>/delete')
|
||||
@admin.route('/users/<int:user_id>/delete')
|
||||
@login_required
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
@ -42,7 +39,7 @@ def delete_user(user_id):
|
||||
return redirect(url_for('admin.index'))
|
||||
|
||||
|
||||
@admin.route('/user/<int:user_id>/edit', methods=['GET', 'POST'])
|
||||
@admin.route('/users/<int:user_id>/edit', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
@admin_required
|
||||
def edit_user(user_id):
|
||||
@ -63,4 +60,5 @@ def edit_user(user_id):
|
||||
edit_user_form.role.data = user.role_id
|
||||
return render_template('admin/edit_user.html.j2',
|
||||
edit_user_form=edit_user_form,
|
||||
title='Administration: Edit user', user=user)
|
||||
title='Edit user',
|
||||
user=user)
|
||||
|
@ -1,3 +1,4 @@
|
||||
from flask import current_app
|
||||
from ..models import User
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (BooleanField, PasswordField, StringField, SubmitField,
|
||||
@ -17,9 +18,9 @@ class RegistrationForm(FlaskForm):
|
||||
username = StringField(
|
||||
'Username',
|
||||
validators=[DataRequired(), Length(1, 64),
|
||||
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0,
|
||||
'Usernames must have only letters, numbers, dots '
|
||||
'or underscores')]
|
||||
Regexp(current_app.config['ALLOWED_USERNAME_REGEX'],
|
||||
message='Usernames must have only letters, numbers,'
|
||||
' dots or underscores')]
|
||||
)
|
||||
password = PasswordField(
|
||||
'Password',
|
||||
|
@ -1,52 +0,0 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (BooleanField, PasswordField, SelectField, StringField,
|
||||
SubmitField, ValidationError)
|
||||
from wtforms.validators import DataRequired, Email, EqualTo
|
||||
|
||||
|
||||
class EditEmailForm(FlaskForm):
|
||||
email = StringField('New email', validators=[Email(), DataRequired()])
|
||||
save_email = SubmitField('Save email')
|
||||
|
||||
|
||||
class EditGeneralSettingsForm(FlaskForm):
|
||||
dark_mode = BooleanField('Dark mode')
|
||||
job_status_mail_notifications = SelectField(
|
||||
'Job status mail notifications',
|
||||
choices=[('', 'Choose your option'),
|
||||
('all', 'Notify on all status changes'),
|
||||
('end', 'Notify only when a job ended'),
|
||||
('none', 'No status update notifications')],
|
||||
validators=[DataRequired()])
|
||||
job_status_site_notifications = SelectField(
|
||||
'Job status site notifications',
|
||||
choices=[('', 'Choose your option'),
|
||||
('all', 'Notify on all status changes'),
|
||||
('end', 'Notify only when a job ended'),
|
||||
('none', 'No status update notifications')],
|
||||
validators=[DataRequired()])
|
||||
save_settings = SubmitField('Save settings')
|
||||
|
||||
|
||||
class EditPasswordForm(FlaskForm):
|
||||
current_password = PasswordField('Current password',
|
||||
validators=[DataRequired()])
|
||||
password = PasswordField(
|
||||
'New password',
|
||||
validators=[DataRequired(), EqualTo('password_confirmation',
|
||||
message='Passwords must match.')]
|
||||
)
|
||||
password_confirmation = PasswordField(
|
||||
'Password confirmation',
|
||||
validators=[DataRequired(),
|
||||
EqualTo('password', message='Passwords must match.')]
|
||||
)
|
||||
save_password = SubmitField('Save password')
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super(EditPasswordForm, self).__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
|
||||
def validate_current_password(self, field):
|
||||
if not self.user.verify_password(field.data):
|
||||
raise ValidationError('Invalid password.')
|
@ -1,13 +0,0 @@
|
||||
from .. import db
|
||||
from ..decorators import background
|
||||
from ..models import User
|
||||
|
||||
|
||||
@background
|
||||
def delete_user(user_id, *args, **kwargs):
|
||||
with kwargs['app'].app_context():
|
||||
user = User.query.get(user_id)
|
||||
if user is None:
|
||||
raise Exception('User {} not found'.format(user_id))
|
||||
user.delete()
|
||||
db.session.commit()
|
@ -1,69 +0,0 @@
|
||||
from flask import flash, redirect, render_template, url_for
|
||||
from flask_login import current_user, login_required, logout_user
|
||||
from . import profile
|
||||
from . import tasks
|
||||
from .forms import EditEmailForm, EditGeneralSettingsForm, EditPasswordForm
|
||||
from .. import db
|
||||
|
||||
|
||||
@profile.route('/settings', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def settings():
|
||||
edit_email_form = EditEmailForm(prefix='edit-email-form')
|
||||
edit_general_settings_form = EditGeneralSettingsForm(
|
||||
prefix='edit-general-settings-form')
|
||||
edit_password_form = EditPasswordForm(prefix='edit-password-form',
|
||||
user=current_user)
|
||||
# Check if edit_email_form is submitted and valid
|
||||
if (edit_email_form.save_email.data
|
||||
and edit_email_form.validate_on_submit()):
|
||||
db.session.add(current_user)
|
||||
db.session.commit()
|
||||
flash('Your email address has been updated.')
|
||||
return redirect(url_for('profile.settings'))
|
||||
# Check if edit_settings_form is submitted and valid
|
||||
if (edit_general_settings_form.save_settings.data
|
||||
and edit_general_settings_form.validate_on_submit()):
|
||||
current_user.setting_dark_mode = \
|
||||
edit_general_settings_form.dark_mode.data
|
||||
current_user.setting_job_status_mail_notifications = \
|
||||
edit_general_settings_form.job_status_mail_notifications.data
|
||||
current_user.setting_job_status_site_notifications = \
|
||||
edit_general_settings_form.job_status_site_notifications.data
|
||||
db.session.add(current_user)
|
||||
db.session.commit()
|
||||
flash('Your settings have been updated.')
|
||||
return redirect(url_for('profile.settings'))
|
||||
# Check if edit_password_form is submitted and valid
|
||||
if (edit_password_form.save_password.data
|
||||
and edit_password_form.validate_on_submit()):
|
||||
current_user.password = edit_password_form.password.data
|
||||
db.session.add(current_user)
|
||||
db.session.commit()
|
||||
flash('Your password has been updated.')
|
||||
return redirect(url_for('profile.settings'))
|
||||
# If no form is submitted or valid, fill out fields with current values
|
||||
edit_email_form.email.data = current_user.email
|
||||
edit_general_settings_form.dark_mode.data = current_user.setting_dark_mode
|
||||
edit_general_settings_form.job_status_site_notifications.data = \
|
||||
current_user.setting_job_status_site_notifications
|
||||
edit_general_settings_form.job_status_mail_notifications.data = \
|
||||
current_user.setting_job_status_mail_notifications
|
||||
return render_template(
|
||||
'profile/settings.html.j2',
|
||||
edit_email_form=edit_email_form,
|
||||
edit_password_form=edit_password_form,
|
||||
edit_general_settings_form=edit_general_settings_form,
|
||||
title='Settings')
|
||||
|
||||
|
||||
@profile.route('/delete', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def delete():
|
||||
"""
|
||||
View to delete yourslef and all associated data.
|
||||
"""
|
||||
tasks.delete_user(current_user.id)
|
||||
logout_user()
|
||||
flash('Your account has been deleted!')
|
||||
return redirect(url_for('main.index'))
|
@ -1,5 +1,5 @@
|
||||
from flask import Blueprint
|
||||
|
||||
|
||||
profile = Blueprint('profile', __name__)
|
||||
settings = Blueprint('settings', __name__)
|
||||
from . import views # noqa
|
86
web/app/settings/forms.py
Normal file
86
web/app/settings/forms.py
Normal file
@ -0,0 +1,86 @@
|
||||
from flask import current_app
|
||||
from flask_login import current_user
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (BooleanField, PasswordField, SelectField, StringField,
|
||||
SubmitField, ValidationError)
|
||||
from wtforms.validators import DataRequired, Email, EqualTo, Length, Regexp
|
||||
|
||||
|
||||
class ChangePasswordForm(FlaskForm):
|
||||
password = PasswordField('Old password', validators=[DataRequired()])
|
||||
new_password = PasswordField(
|
||||
'New password',
|
||||
validators=[DataRequired(), EqualTo('password_confirmation',
|
||||
message='Passwords must match.')]
|
||||
)
|
||||
new_password2 = PasswordField(
|
||||
'Confirm new password', validators=[DataRequired()])
|
||||
submit = SubmitField('Change password')
|
||||
|
||||
def __init__(self, user=current_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 EditGeneralSettingsForm(FlaskForm):
|
||||
dark_mode = BooleanField('Dark mode')
|
||||
email = StringField('E-Mail',
|
||||
validators=[DataRequired(), Length(1, 254), Email()])
|
||||
username = StringField(
|
||||
'Benutzername',
|
||||
validators=[DataRequired(),
|
||||
Length(1, 64),
|
||||
Regexp(current_app.config['ALLOWED_USERNAME_REGEX'],
|
||||
message='Usernames must have only letters, numbers,'
|
||||
' dots or underscores')]
|
||||
)
|
||||
submit = SubmitField('Submit')
|
||||
|
||||
def __init__(self, user=current_user, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
self.email.data = self.email.data or user.email
|
||||
self.dark_mode.data = self.dark_mode.data or user.setting_dark_mode
|
||||
self.username.data = self.username.data or user.username
|
||||
|
||||
def validate_email(self, field):
|
||||
if (field.data != self.user.email
|
||||
and User.query.filter_by(email=field.data).first()):
|
||||
raise ValidationError('Email already registered.')
|
||||
|
||||
def validate_username(self, field):
|
||||
if (field.data != self.user.username
|
||||
and User.query.filter_by(username=field.data).first()):
|
||||
raise ValidationError('Username already in use.')
|
||||
|
||||
|
||||
class EditNotificationSettingsForm(FlaskForm):
|
||||
job_status_mail_notifications = SelectField(
|
||||
'Job status mail notifications',
|
||||
choices=[('', 'Choose your option'),
|
||||
('all', 'Notify on all status changes'),
|
||||
('end', 'Notify only when a job ended'),
|
||||
('none', 'No status update notifications')],
|
||||
validators=[DataRequired()])
|
||||
job_status_site_notifications = SelectField(
|
||||
'Job status site notifications',
|
||||
choices=[('', 'Choose your option'),
|
||||
('all', 'Notify on all status changes'),
|
||||
('end', 'Notify only when a job ended'),
|
||||
('none', 'No status update notifications')],
|
||||
validators=[DataRequired()])
|
||||
submit = SubmitField('Save settings')
|
||||
|
||||
def __init__(self, user=current_user, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
self.job_status_mail_notifications.data = \
|
||||
self.job_status_mail_notifications.data \
|
||||
or user.setting_job_status_mail_notifications
|
||||
self.job_status_site_notifications.data = \
|
||||
self.job_status_site_notifications.data \
|
||||
or user.setting_job_status_site_notifications
|
73
web/app/settings/views.py
Normal file
73
web/app/settings/views.py
Normal file
@ -0,0 +1,73 @@
|
||||
from flask import current_app, flash, redirect, render_template, url_for
|
||||
from flask_login import current_user, login_required
|
||||
from . import settings
|
||||
from .forms import (ChangePasswordForm, EditGeneralSettingsForm,
|
||||
EditNotificationSettingsForm)
|
||||
from .. import db
|
||||
from ..decorators import admin_required
|
||||
from ..models import Role, User
|
||||
import os
|
||||
import uuid
|
||||
|
||||
|
||||
@settings.route('/')
|
||||
@login_required
|
||||
def index():
|
||||
return redirect(url_for('.edit_general_settings'))
|
||||
|
||||
|
||||
@settings.route('/change_password', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def change_password():
|
||||
form = ChangePasswordForm()
|
||||
if form.validate_on_submit():
|
||||
current_user.password = form.new_password.data
|
||||
db.session.commit()
|
||||
flash('Your password has been updated.')
|
||||
return redirect(url_for('.change_password'))
|
||||
return render_template('settings/change_password.html.j2',
|
||||
form=form,
|
||||
title='Change password')
|
||||
|
||||
|
||||
@settings.route('/edit_general_settings')
|
||||
@login_required
|
||||
def edit_general_settings():
|
||||
form = EditGeneralSettingsForm()
|
||||
if form.validate_on_submit():
|
||||
current_user.email = form.email.data
|
||||
current_user.setting_dark_mode = form.dark_mode.data
|
||||
current_user.username = form.username.data
|
||||
db.session.commit()
|
||||
flash('Your changes have been saved.')
|
||||
return render_template('settings/edit_general_settings.html.j2',
|
||||
form=form,
|
||||
title='General settings')
|
||||
|
||||
|
||||
@settings.route('/edit_notification_settings')
|
||||
@login_required
|
||||
def edit_notification_settings():
|
||||
form = EditNotificationSettingsForm()
|
||||
if form.validate_on_submit():
|
||||
current_user.setting_job_status_mail_notifications = \
|
||||
form.job_status_mail_notifications.data
|
||||
current_user.setting_job_status_site_notifications = \
|
||||
form.job_status_site_notifications.data
|
||||
db.session.commit()
|
||||
flash('Your changes have been saved.')
|
||||
return render_template('settings/edit_notification_settings.html.j2',
|
||||
form=form,
|
||||
title='Notification settings')
|
||||
|
||||
|
||||
@settings.route('/delete')
|
||||
@login_required
|
||||
def delete():
|
||||
"""
|
||||
View to delete current_user and all associated data.
|
||||
"""
|
||||
tasks.delete_user(current_user.id)
|
||||
logout_user()
|
||||
flash('Your account has been deleted!')
|
||||
return redirect(url_for('main.index'))
|
@ -131,11 +131,11 @@ RessourceList.dataMappers = {
|
||||
confirmed: user.confirmed,
|
||||
email: user.email,
|
||||
id: user.id,
|
||||
link: `user/${user.id}`,
|
||||
link: `users/${user.id}`,
|
||||
role_id: user.role_id,
|
||||
username: user.username,
|
||||
username2: user.username,
|
||||
"delete-link": `/admin/user/${user.id}/delete`,
|
||||
"delete-link": `/admin/users/${user.id}/delete`,
|
||||
"delete-modal": `delete-user-${user.id}-modal`,
|
||||
"delete-modal-trigger": `delete-user-${user.id}-modal`,
|
||||
}),
|
||||
|
@ -1,27 +1,35 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
{% import 'materialize/wtf.html.j2' as wtf %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12 m4">
|
||||
<h3 id="title">{{ user.username }}</h3>
|
||||
<p id="description">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,</p>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('admin.user', user_id=user.id) }}"><i class="material-icons left">arrow_back</i>Back to user administration</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">Edit user</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<form method="POST">
|
||||
<div class="card-content">
|
||||
{{ edit_user_form.hidden_tag() }}
|
||||
{{ M.render_field(edit_user_form.username, data_length='64', material_icon='account_circle') }}
|
||||
{{ M.render_field(edit_user_form.email, class_='validate', material_icon='email', type='email') }}
|
||||
{{ M.render_field(edit_user_form.role, material_icon='swap_vert') }}
|
||||
{{ M.render_field(edit_user_form.confirmed, material_icon='check') }}
|
||||
<div class="col s12 m4">
|
||||
<h2>{{ user.username }}</h2>
|
||||
<p>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,</p>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('.user', user_id=user.id) }}"><i class="material-icons left">arrow_back</i>Back to user administration</a>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<form method="POST">
|
||||
<div class="card-content">
|
||||
{{ edit_user_form.hidden_tag() }}
|
||||
{{ wtf.render_field(edit_user_form.username, data_length='64', material_icon='account_circle') }}
|
||||
{{ wtf.render_field(edit_user_form.email, class_='validate', material_icon='email', type='email') }}
|
||||
{{ wtf.render_field(edit_user_form.role, material_icon='swap_vert') }}
|
||||
{{ wtf.render_field(edit_user_form.confirmed, material_icon='check') }}
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ wtf.render_field(edit_user_form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ M.render_field(edit_user_form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,42 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% set full_width = True %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content" id="users">
|
||||
<span class="card-title">User list</span>
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-user" class="search" type="search"></input>
|
||||
<label for="search-user">Search user</label>
|
||||
</div>
|
||||
<ul class="pagination paginationTop"></ul>
|
||||
<table class="highlight responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort" data-sort="username">Username</th>
|
||||
<th class="sort" data-sort="email">Email</th>
|
||||
<th class="sort" data-sort="role_id">Role</th>
|
||||
<th class="sort" data-sort="confirmed">Confirmed Status</th>
|
||||
<th class="sort" data-sort="id">Id</th>
|
||||
<th>{# Actions #}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
</tbody>
|
||||
</table>
|
||||
<ul class="pagination paginationBottom"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import {RessourceList} from '../../static/js/nopaque.lists.js';
|
||||
let userList = new RessourceList('users', null, "User", RessourceList.options.extended);
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
userList._add({{ users|tojson|safe }});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,34 +1,40 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12 m4">
|
||||
<h3 id="title">{{ user.username }}</h3>
|
||||
<p id="description">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,</p>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('admin.index') }}"><i class="material-icons left">arrow_back</i>Back to admin board</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">User information</span>
|
||||
<ul>
|
||||
<li>Username: {{ user.username }}</li>
|
||||
<li>Email: {{ user.email }}</li>
|
||||
<li>ID: {{ user.id }}</li>
|
||||
<li>Member since: {{ user.member_since.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||
<li>Confirmed status: {{ user.confirmed }}</li>
|
||||
<li>Last seen: {{ user.last_seen.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||
<li>Role ID: {{ user.role_id }}</li>
|
||||
<li>Permissions as Int: {{ user.role.permissions }}</li>
|
||||
<li>Role name: {{ user.role.name }}</li>
|
||||
</ul>
|
||||
<div class="col s12 m4">
|
||||
<h2>{{ user.username }}</h2>
|
||||
<p id="description">Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,</p>
|
||||
<a class="waves-effect waves-light btn" href="{{ url_for('.users') }}"><i class="material-icons left">arrow_back</i>Back to Users</a>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a href="{{ url_for('admin.edit_user', user_id=user.id) }}" class="waves-effect waves-light btn"><i class="material-icons left">edit</i>Edit</a>
|
||||
<a data-target="delete-user-modal" class="waves-effect waves-light btn red modal-trigger"><i class="material-icons left">delete</i>Delete</a>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<span class="card-title">User information</span>
|
||||
<ul>
|
||||
<li>Username: {{ user.username }}</li>
|
||||
<li>Email: {{ user.email }}</li>
|
||||
<li>ID: {{ user.id }}</li>
|
||||
<li>Member since: {{ user.member_since.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||
<li>Confirmed status: {{ user.confirmed }}</li>
|
||||
<li>Last seen: {{ user.last_seen.strftime('%m/%d/%Y, %H:%M:%S %p') }}</li>
|
||||
<li>Role ID: {{ user.role_id }}</li>
|
||||
<li>Permissions as Int: {{ user.role.permissions }}</li>
|
||||
<li>Role name: {{ user.role.name }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a href="{{ url_for('.edit_user', user_id=user.id) }}" class="waves-effect waves-light btn"><i class="material-icons left">edit</i>Edit</a>
|
||||
<a data-target="delete-user-modal" class="waves-effect waves-light btn red modal-trigger"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col s12 l6">
|
||||
<h3>Corpora</h3>
|
||||
@ -92,21 +98,23 @@
|
||||
<!-- Modals -->
|
||||
<div id="delete-user-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>Confirm user deletion</h4>
|
||||
<h3>Delete user</h3>
|
||||
<p>Do you really want to delete the user {{ user.username }}? All associated data will be permanently deleted!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-light btn">Cancel</a>
|
||||
<a href="{{ url_for('admin.delete_user', user_id=user.id) }}" class="modal-close waves-effect waves-light btn red"><i class="material-icons left">delete</i>Delete</a>
|
||||
<a href="{{ url_for('.delete_user', user_id=user.id) }}" class="modal-close waves-effect waves-light btn red"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import {RessourceList} from '../../static/js/nopaque.lists.js';
|
||||
let corpusList = new RessourceList("corpora", nopaque.foreignCorporaSubscribers, "Corpus");
|
||||
let jobList = new RessourceList("jobs", nopaque.foreignJobsSubscribers, "Job");
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
nopaque.socket.emit("foreign_user_data_stream_init", {{ user.id }});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script type="module">
|
||||
import {RessourceList} from '{{ url_for('static', filename='js/nopaque.lists.js') }}';
|
||||
let corpusList = new RessourceList("corpora", nopaque.foreignCorporaSubscribers, "Corpus");
|
||||
let jobList = new RessourceList("jobs", nopaque.foreignJobsSubscribers, "Job");
|
||||
nopaque.socket.emit("foreign_user_data_stream_init", {{ user.id }});
|
||||
</script>
|
||||
{% endblock scripts %}
|
||||
|
48
web/app/templates/admin/users.html.j2
Normal file
48
web/app/templates/admin/users.html.j2
Normal file
@ -0,0 +1,48 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<div class="card">
|
||||
<div class="card-content" id="users">
|
||||
<div class="input-field">
|
||||
<i class="material-icons prefix">search</i>
|
||||
<input id="search-user" class="search" type="text"></input>
|
||||
<label for="search-user">Search user</label>
|
||||
</div>
|
||||
<ul class="pagination paginationTop"></ul>
|
||||
<table class="highlight responsive-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="sort" data-sort="username">Username</th>
|
||||
<th class="sort" data-sort="email">Email</th>
|
||||
<th class="sort" data-sort="role_id">Role</th>
|
||||
<th class="sort" data-sort="confirmed">Confirmed Status</th>
|
||||
<th class="sort" data-sort="id">Id</th>
|
||||
<th>{# Actions #}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="list">
|
||||
</tbody>
|
||||
</table>
|
||||
<ul class="pagination paginationBottom"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
{{ super() }}
|
||||
<script type="module">
|
||||
import {RessourceList} from '{{ url_for('static', filename='js/nopaque.lists.js') }}';
|
||||
let userList = new RessourceList('users', null, "User", RessourceList.options.extended);
|
||||
userList._add({{ users|tojson}});
|
||||
</script>
|
||||
{% endblock scripts %}
|
@ -18,7 +18,7 @@
|
||||
<div class="col s12 m4">
|
||||
<div class="card medium">
|
||||
<div class="card-content">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p>Want to boost your research and get going? nopaque is free and no download is needed. Register now!</p>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
|
@ -18,7 +18,7 @@
|
||||
<div class="col s12 m4">
|
||||
<div class="card medium">
|
||||
<div class="card-content">
|
||||
<h1>Register</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p>Simply enter a username and password to receive your registration email. After that you can start right away.</p>
|
||||
<p>It goes without saying that the <a href="{{ url_for('main.privacy_policy') }}">General Data Protection Regulation</a> applies, only necessary data is stored.</p>
|
||||
<p>Please also read our <a href="{{ url_for('main.terms_of_use') }}">terms of use</a> before signing up for nopaque!</p>
|
||||
|
@ -5,11 +5,11 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m4">
|
||||
<p>dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren,</p>
|
||||
<p>Enter a new password and confirm it! After that, the entered password is your new one!</p>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m4">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
@ -13,10 +13,10 @@
|
||||
<span class="card-title">Hello, {{ current_user.username }}!</span>
|
||||
<p><b>You have not confirmed your account yet.</b></p>
|
||||
<p>Before you can access this site you need to confirm your account. Check your inbox, you should have received an email with a confirmation link.</p>
|
||||
<p>Need another confirmation email? <a href="{{ url_for('.resend_confirmation') }}">Click here</a></p>
|
||||
<p>Need another confirmation email? Click the button below!</p>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
<a class="btn" href="{{ url_for('.register') }}"><i class="material-icons left">person_add</i>Register</a>
|
||||
<a class="btn" href="{{ url_for('.resend_confirmation') }}">Resend confirmation mail</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p class="light">{{ request.path }}</p>
|
||||
<p>Alternatively, you can visit the <a href="{{ url_for('main.index') }}">Main Page</a> or read <a class="modal-trigger" href="#more-information-modal">more information</a> about this type of error.</p>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="more-information-modal">
|
||||
<div class="modal-content">
|
||||
<h4>{{ title }}</h4>
|
||||
<h2>About the "{{ title }}" error</h2>
|
||||
<p>The request contained valid data and was understood by the server, but the server is refusing action. This may be due to the user not having the necessary permissions for a resource or needing an account of some sort, or attempting a prohibited action (e.g. creating a duplicate record where only one is allowed). This code is also typically used if the request provided authentication by answering the WWW-Authenticate header field challenge, but the server did not accept that authentication. The request should not be repeated.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p class="light">{{ request.path }}</p>
|
||||
<p>Alternatively, you can visit the <a href="{{ url_for('main.index') }}">Main Page</a> or read <a class="modal-trigger" href="#more-information-modal">more information</a> about this type of error.</p>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="more-information-modal">
|
||||
<div class="modal-content">
|
||||
<h4>{{ title }}</h4>
|
||||
<h2>About the "{{ title }}" error</h2>
|
||||
<p>The requested resource could not be found but may be available in the future. Subsequent requests by the client are permissible.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p class="light">{{ request.path }}</p>
|
||||
<p>Alternatively, you can visit the <a href="{{ url_for('main.index') }}">Main Page</a> or read <a class="modal-trigger" href="#more-information-modal">more information</a> about this type of error.</p>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="more-information-modal">
|
||||
<div class="modal-content">
|
||||
<h4>{{ title }}</h4>
|
||||
<h2>About the "{{ title }}" error</h2>
|
||||
<p>The request is larger than the server is willing or able to process. Previously called "Request Entity Too Large".</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -2,14 +2,14 @@
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p class="light">{{ request.path }}</p>
|
||||
<p>Alternatively, you can visit the <a href="{{ url_for('main.index') }}">Main Page</a> or read <a class="modal-trigger" href="#more-information-modal">more information</a> about this type of error.</p>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="more-information-modal">
|
||||
<div class="modal-content">
|
||||
<h4>{{ title }}</h4>
|
||||
<h2>About the "{{ title }}" error</h2>
|
||||
<p>A generic error message, given when an unexpected condition was encountered and no more specific message is suitable.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="section white">
|
||||
<div class="row container">
|
||||
<div class="col s12">
|
||||
<h2>nopaque</h2>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
<p>From text to data to analysis</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -4,8 +4,9 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
<div class="card" id="beta-launch">
|
||||
<div class="card-content">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1>{{ title }}</h1>
|
||||
<h1 id="title">{{ title }}</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12">
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!-- Analysis init modal. User feedback showing that the analysis session is
|
||||
loading. -->
|
||||
|
||||
<!-- Analysis init modal. User feedback showing that the analysis session is loading. -->
|
||||
<div class="modal no-autoinit" id="analysis-init-modal">
|
||||
<div class="modal-content">
|
||||
<h4>Initializing your corpus analysis session...</h4>
|
||||
@ -14,4 +12,4 @@ loading. -->
|
||||
</div>
|
||||
<p id="analysis-init-error" class="hide red-text"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<!-- Modal showing detailed context info for one match. -->
|
||||
|
||||
<div id="context-modal" class="modal">
|
||||
<div class="modal-content">
|
||||
<form>
|
||||
@ -73,4 +72,4 @@
|
||||
</a>
|
||||
<a href="#!" class="modal-close waves-effect waves-light red btn">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!-- Export query results modal. Allos the user to download the results in
|
||||
different file formats. WIP -->
|
||||
|
||||
<!-- Export query results modal. Allos the user to download the results in different file formats. WIP -->
|
||||
<div id="query-results-download-modal"
|
||||
class="modal modal-fixed-footer no-autoinit">
|
||||
<div class="modal-content">
|
||||
@ -51,4 +49,4 @@ different file formats. WIP -->
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-light red btn">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!-- Modal showing the corpus files for the current query results including
|
||||
title ant match count per corpus file. -->
|
||||
|
||||
<!-- Modal showing the corpus files for the current query results including title ant match count per corpus file. -->
|
||||
<div id="show-corpus-files-modal" class="modal bottom-sheet">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!-- Modal showing the meta data for the current query results or the imported
|
||||
results -->
|
||||
|
||||
<!-- Modal showing the meta data for the current query results or the imported results -->
|
||||
<div id="meta-data-modal" class="modal bottom-sheet">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
|
@ -1,6 +1,4 @@
|
||||
<!-- Modal to show all metadata of one text/corpus file. Used in conjunction
|
||||
with the show_meta_data.html.j2 template.-->
|
||||
|
||||
<!-- Modal to show all metadata of one text/corpus file. Used in conjunction with the show_meta_data.html.j2 template. -->
|
||||
<div id="modal-text-details" class="modal modal-fixed-footer">
|
||||
<div class="modal-content">
|
||||
<h4>Bibliographic data</h4>
|
||||
@ -9,4 +7,4 @@ with the show_meta_data.html.j2 template.-->
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="modal-close waves-effect waves-green red btn">Close</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -78,15 +78,14 @@
|
||||
{% endif %}
|
||||
<a href="{{ url_for('main.index') }}" class="brand-logo hide-on-med-and-down" style="height: 100%; overflow: hidden;"><img src="{{ url_for('static', filename='images/nopaque_-_logo_name_slogan.svg') }}" style="height: 128px; margin-top: -32px;"></a>
|
||||
<a href="{{ url_for('main.index') }}" class="brand-logo hide-on-large-only" style="height: 100%; overflow: hidden;"><img src="{{ url_for('static', filename='images/nopaque_-_logo.svg') }}" style="height: 128px; margin-top: -32px;"></a>
|
||||
<ul class="right hide-on-med-and-down">
|
||||
<li{% if request.path == url_for('main.news') %} class="active"{% endif %}><a href="{{ url_for('main.news') }}"><i class="material-icons left">notifications</i>News</a></li>
|
||||
<ul class="right">
|
||||
<li class="hide-on-med-and-down{% if request.path == url_for('main.news') %} active{% endif %}"><a href="{{ url_for('main.news') }}"><i class="material-icons left">notifications</i>News</a></li>
|
||||
{% if current_user.is_anonymous %}
|
||||
<li{% if request.path == url_for('auth.register') %} class="active"{% endif %}><a href="{{ url_for('auth.register') }}"><i class="material-icons left">assignment</i>Register</a></li>
|
||||
<li{% if request.path == url_for('auth.login') %} class="active"{% endif %}><a href="{{ url_for('auth.login') }}"><i class="material-icons left">login</i>Log in</a></li>
|
||||
{% else %}
|
||||
<li{% if request.path == url_for('main.dashboard', _anchor='corpora') %} class="active"{% endif %}><a href="{{ url_for('main.dashboard', _anchor='corpora') }}"><i class="material-icons left">book</i>My Corpora</a></li>
|
||||
<li{% if request.path == url_for('main.dashboard', _anchor='jobs') %} class="active"{% endif %}><a href="{{ url_for('main.dashboard', _anchor='jobs') }}"><i class="material-icons left">work</i>My Jobs</a></li>
|
||||
<li><a class="dropdown-trigger no-autoinit" data-target="nav-more-dropdown" href="#!" id="nav-more-dropdown-trigger"><i class="material-icons">more_vert</i></a></li>
|
||||
<li class="hide-on-med-and-down{% if request.path == url_for('main.dashboard') %} active{% endif %}"><a href="{{ url_for('main.dashboard') }}"><i class="material-icons left">dashboard</i>Dashboard</a></li>
|
||||
<li class="hide-on-med-and-down"><a class="dropdown-trigger no-autoinit" data-target="nav-more-dropdown" href="#!" id="nav-more-dropdown-trigger"><i class="material-icons">more_vert</i></a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
@ -98,7 +97,7 @@
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
<ul class="dropdown-content" id="nav-more-dropdown">
|
||||
<li><a href="{{ url_for('profile.settings') }}"><i class="material-icons left">settings</i>Settings</a></li>
|
||||
<li><a href="{{ url_for('settings.index') }}"><i class="material-icons left">settings</i>Settings</a></li>
|
||||
<li class="divider" tabindex="-1"></li>
|
||||
<li><a href="{{ url_for('auth.logout') }}">Log out</a></li>
|
||||
</ul>
|
||||
@ -126,17 +125,18 @@
|
||||
<li style="background-color: {{ colors.ocr }}; border-left: 10px solid {{ colors.ocr_darken }}; margin-top: 5px;"><a href="{{ url_for('services.service', service='ocr') }}"><i class="material-icons">find_in_page</i>OCR</a></li>
|
||||
<li style="background-color: {{ colors.nlp }}; border-left: 10px solid {{ colors.nlp_darken }}; margin-top: 5px;"><a href="{{ url_for('services.service', service='nlp') }}"><i class="material-icons">format_textdirection_l_to_r</i>NLP</a></li>
|
||||
<li style="background-color: {{ colors.corpus_analysis }}; border-left: 10px solid {{ colors.corpus_analysis_darken }}; margin-top: 5px;"><a href="{{ url_for('services.service', service='corpus_analysis') }}"><i class="material-icons">search</i>Corpus analysis</a></li>
|
||||
<li class="hide-on-large-only"><div class="divider"></div></li>
|
||||
<li class="hide-on-large-only"><a href="{{ url_for('profile.settings') }}"><i class="material-icons">settings</i>Settings</a></li>
|
||||
<li class="hide-on-large-only"><a href="{{ url_for('auth.logout') }}">Log out</a></li>
|
||||
<li><div class="divider"></div></li>
|
||||
<li><a class="subheader">Account</a></li>
|
||||
<li><a href="{{ url_for('settings.index') }}"><i class="material-icons">settings</i>Settings</a></li>
|
||||
<li><a href="{{ url_for('auth.logout') }}">Log out</a></li>
|
||||
{% else %}
|
||||
<li class="hide-on-large-only"><a href="{{ url_for('auth.register') }}"><i class="material-icons">assignment</i>Register</a></li>
|
||||
<li class="hide-on-large-only"><a href="{{ url_for('auth.login') }}"><i class="material-icons">login</i>Log in</a></li>
|
||||
<li><a href="{{ url_for('auth.register') }}"><i class="material-icons">assignment</i>Register</a></li>
|
||||
<li><a href="{{ url_for('auth.login') }}"><i class="material-icons">login</i>Log in</a></li>
|
||||
{% endif %}
|
||||
{% if current_user.is_administrator() %}
|
||||
<li><div class="divider"></div></li>
|
||||
<li><a class="subheader">Administration</a></li>
|
||||
<li><a href="{{ url_for('admin.index') }}"><i class="material-icons">build</i>Administration tools</a></li>
|
||||
<li><a href="{{ url_for('admin.users') }}"><i class="material-icons">build</i>Administration tools</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endblock sidenav %}
|
||||
|
@ -1,136 +0,0 @@
|
||||
{% extends "nopaque.html.j2" %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="col s12 m4">
|
||||
<h3>General settings</h3>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<br class="hide-on-small-only">
|
||||
<div class="card">
|
||||
<form method="POST">
|
||||
<div class="card-content">
|
||||
{{ edit_general_settings_form.hidden_tag() }}
|
||||
<div class="row">
|
||||
<div class="col s9">
|
||||
<p><i class="material-icons left">brightness_3</i>{{ edit_general_settings_form.dark_mode.label.text }}</p>
|
||||
<p class="light">Activate dark mode to ease your eyes.</p>
|
||||
</div>
|
||||
<div class="col s3 right-align">
|
||||
{{ M.render_field(edit_general_settings_form.dark_mode, label=False) }}
|
||||
</div>
|
||||
<div class="col s12"><p> </p></div>
|
||||
<div class="col s12 divider"></div>
|
||||
<div class="col s12"><p> </p></div>
|
||||
<div class="col s12 m8">
|
||||
<p><i class="material-icons left">notifications</i>Job status site notifications</p>
|
||||
<p class="light">Receive site notifications about job status changes.</p>
|
||||
</div>
|
||||
<div class="col s12 m4 right-align" style="margin-top: -1rem;">
|
||||
{{ M.render_field(edit_general_settings_form.job_status_site_notifications, label=False) }}
|
||||
</div>
|
||||
<div class="col s12"><p> </p></div>
|
||||
<div class="col s12 divider"></div>
|
||||
<div class="col s12"><p> </p></div>
|
||||
<div class="col s12 m8">
|
||||
<p><i class="material-icons left">notifications</i>Job status mail notifications</p>
|
||||
<p class="light">Receive mail notifications about job status changes.</p>
|
||||
</div>
|
||||
<div class="col s12 m4 right-align" style="margin-top: -1rem;">
|
||||
{{ M.render_field(edit_general_settings_form.job_status_mail_notifications, label=False) }}
|
||||
</div>
|
||||
<!--
|
||||
Seperate each setting with the following two elements
|
||||
<div class="col s12 divider"></div>
|
||||
<div class="col s12"><p> </p></div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ M.render_field(edit_general_settings_form.save_settings, material_icon='send') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col s12"></div>
|
||||
|
||||
|
||||
<div class="col s12 m4">
|
||||
<h3>Change password</h3>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<br class="hide-on-small-only">
|
||||
<div class="card">
|
||||
<form method="POST">
|
||||
<div class="card-content">
|
||||
{{ edit_password_form.hidden_tag() }}
|
||||
{{ M.render_field(edit_password_form.current_password, data_length='128', material_icon='vpn_key') }}
|
||||
{{ M.render_field(edit_password_form.password, data_length='128', material_icon='vpn_key') }}
|
||||
{{ M.render_field(edit_password_form.password_confirmation, data_length='128', material_icon='vpn_key') }}
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ M.render_field(edit_password_form.save_password, material_icon='send') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col s12"></div>
|
||||
|
||||
|
||||
<div class="col s12 m4">
|
||||
<h3>Change email</h3>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<br class="hide-on-small-only">
|
||||
<div class="card">
|
||||
<form method="POST">
|
||||
<div class="card-content">
|
||||
{{ edit_email_form.hidden_tag() }}
|
||||
{{ M.render_field(edit_email_form.email, class_='validate', material_icon='email', type='email') }}
|
||||
</div>
|
||||
<div class="card-action right-align">
|
||||
{{ M.render_field(edit_email_form.save_email, material_icon='send') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col s12"></div>
|
||||
|
||||
|
||||
<div class="col s12 m4">
|
||||
<h3>Delete account</h3>
|
||||
</div>
|
||||
<div class="col s12 m8">
|
||||
<br class="hide-on-small-only">
|
||||
<div class="card">
|
||||
<div class="card-content">
|
||||
<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 href="#delete-account-modal" class="btn modal-trigger red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modals -->
|
||||
<div class="modal" id="delete-account-modal">
|
||||
<div class="modal-content">
|
||||
<h4>Confirm deletion</h4>
|
||||
<p>Do you really want to delete your account and all associated data? All associated corpora, jobs and files will be permanently deleted!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||
<a href="{{ url_for('profile.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
5
web/app/templates/settings/_menu.html.j2
Normal file
5
web/app/templates/settings/_menu.html.j2
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="collection">
|
||||
<a href="{{ url_for('.edit_general_settings') }}" class="collection-item{%if request.path == url_for('.edit_general_settings') %} active{% endif %}">Edit general settings</a>
|
||||
<a href="{{ url_for('.change_password') }}" class="collection-item{%if request.path == url_for('.change_password') %} active{% endif %}">Change password</a>
|
||||
<a href="{{ url_for('.edit_notification_settings') }}" class="collection-item{%if request.path == url_for('.edit_notification_settings') %} active{% endif %}">Edit notification settings</a>
|
||||
</div>
|
35
web/app/templates/settings/change_password.html.j2
Normal file
35
web/app/templates/settings/change_password.html.j2
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends 'nopaque.html.j2' %}
|
||||
{% import 'materialize/wtf.html.j2' as wtf %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">Settings</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m4">
|
||||
{% include 'settings/_menu.html.j2' %}
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<form enctype="multipart/form-data" method="POST">
|
||||
<div class="card-content">
|
||||
<span class="card-title">{{ title }}</span>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ wtf.render_field(form.password, material_icon='vpn_key') }}
|
||||
{{ wtf.render_field(form.new_password, material_icon='vpn_key') }}
|
||||
{{ wtf.render_field(form.new_password2, material_icon='vpn_key') }}
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<div class="right-align">
|
||||
{{ wtf.render_field(form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
63
web/app/templates/settings/edit_general_settings.html.j2
Normal file
63
web/app/templates/settings/edit_general_settings.html.j2
Normal file
@ -0,0 +1,63 @@
|
||||
{% extends 'nopaque.html.j2' %}
|
||||
{% import 'materialize/wtf.html.j2' as wtf %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">Settings</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m4">
|
||||
{% include 'settings/_menu.html.j2' %}
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<form enctype="multipart/form-data" method="POST">
|
||||
<div class="card-content">
|
||||
<span class="card-title">{{ title }}</span>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ wtf.render_field(form.username, data_length='64', material_icon='person') }}
|
||||
{{ wtf.render_field(form.email, data_length='254', material_icon='email') }}
|
||||
{{ wtf.render_field(form.dark_mode, material_icon='brightness_3') }}
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<div class="right-align">
|
||||
|
||||
{{ wtf.render_field(form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<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 href="#delete-account-modal" class="btn modal-trigger red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Modals -->
|
||||
<div class="modal" id="delete-account-modal">
|
||||
<div class="modal-content">
|
||||
<h4>Confirm deletion</h4>
|
||||
<p>Do you really want to delete your account and all associated data? All associated corpora, jobs and files will be permanently deleted!</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#!" class="btn modal-close waves-effect waves-light">Cancel</a>
|
||||
<a href="{{ url_for('.delete') }}" class="btn red waves-effect waves-light"><i class="material-icons left">delete</i>Delete</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
@ -0,0 +1,34 @@
|
||||
{% extends 'nopaque.html.j2' %}
|
||||
{% import 'materialize/wtf.html.j2' as wtf %}
|
||||
|
||||
{% block page_content %}
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<h1 id="title">Settings</h1>
|
||||
</div>
|
||||
|
||||
<div class="col s12 m4">
|
||||
{% include 'settings/_menu.html.j2' %}
|
||||
</div>
|
||||
|
||||
<div class="col s12 m8">
|
||||
<div class="card">
|
||||
<form enctype="multipart/form-data" method="POST">
|
||||
<div class="card-content">
|
||||
<span class="card-title">{{ title }}</span>
|
||||
{{ form.hidden_tag() }}
|
||||
{{ wtf.render_field(form.job_status_mail_notifications, material_icon='notifications') }}
|
||||
{{ wtf.render_field(form.job_status_site_notifications, material_icon='feedback') }}
|
||||
</div>
|
||||
<div class="card-action">
|
||||
<div class="right-align">
|
||||
{{ wtf.render_field(form.submit, material_icon='send') }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock page_content %}
|
@ -31,6 +31,7 @@ class Config:
|
||||
|
||||
''' # General # '''
|
||||
ADMIN_EMAIL_ADRESS = os.environ.get('NOPAQUE_ADMIN_EMAIL_ADRESS')
|
||||
ALLOWED_USERNAME_REGEX = '^[A-Za-zÄÖÜäöüß0-9_.]*$'
|
||||
CONTACT_EMAIL_ADRESS = os.environ.get('NOPAQUE_CONTACT_EMAIL_ADRESS')
|
||||
DATA_DIR = os.environ.get('NOPAQUE_DATA_DIR', '/mnt/nopaque')
|
||||
SECRET_KEY = os.environ.get('NOPAQUE_SECRET_KEY', 'hard to guess string')
|
||||
|
Loading…
Reference in New Issue
Block a user