Skip to content
Snippets Groups Projects
Commit 4f8426c0 authored by Patrick Jentsch's avatar Patrick Jentsch
Browse files

Merge branch 'template-rework' of...

Merge branch 'template-rework' of gitlab.ub.uni-bielefeld.de:sfb1288inf/nopaque into template-rework
parents 0fa78121 66d84892
No related branches found
No related tags found
No related merge requests found
Showing
with 314 additions and 215 deletions
...@@ -28,22 +28,23 @@ def create_app(config_name): ...@@ -28,22 +28,23 @@ def create_app(config_name):
socketio.init_app( socketio.init_app(
app, message_queue=config[config_name].SOCKETIO_MESSAGE_QUEUE_URI) app, message_queue=config[config_name].SOCKETIO_MESSAGE_QUEUE_URI)
from . import events with app.app_context():
from .admin import admin as admin_blueprint 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') app.register_blueprint(admin_blueprint, url_prefix='/admin')
from .auth import auth as auth_blueprint
app.register_blueprint(auth_blueprint, url_prefix='/auth') app.register_blueprint(auth_blueprint, url_prefix='/auth')
from .corpora import corpora as corpora_blueprint
app.register_blueprint(corpora_blueprint, url_prefix='/corpora') app.register_blueprint(corpora_blueprint, url_prefix='/corpora')
from .errors import errors as errors_blueprint
app.register_blueprint(errors_blueprint) app.register_blueprint(errors_blueprint)
from .jobs import jobs as jobs_blueprint
app.register_blueprint(jobs_blueprint, url_prefix='/jobs') app.register_blueprint(jobs_blueprint, url_prefix='/jobs')
from .main import main as main_blueprint
app.register_blueprint(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(services_blueprint, url_prefix='/services')
app.register_blueprint(settings_blueprint, url_prefix='/settings')
return app return app
...@@ -8,10 +8,10 @@ from ..models import Role, User ...@@ -8,10 +8,10 @@ from ..models import Role, User
from ..profile import tasks as profile_tasks from ..profile import tasks as profile_tasks
@admin.route('/') @admin.route('/users')
@login_required @login_required
@admin_required @admin_required
def index(): def users():
users = User.query.all() users = User.query.all()
users = [dict(username=u.username, users = [dict(username=u.username,
email=u.email, email=u.email,
...@@ -19,21 +19,18 @@ def index(): ...@@ -19,21 +19,18 @@ def index():
confirmed=u.confirmed, confirmed=u.confirmed,
id=u.id) id=u.id)
for u in users] for u in users]
return render_template('admin/index.html.j2', return render_template('admin/users.html.j2', title='Users', users=users)
title='Administration tools',
users=users)
@admin.route('/user/<int:user_id>') @admin.route('/users/<int:user_id>')
@login_required @login_required
@admin_required @admin_required
def user(user_id): def user(user_id):
user = User.query.get_or_404(user_id) user = User.query.get_or_404(user_id)
return render_template('admin/user.html.j2', title='Administration: User', return render_template('admin/user.html.j2', title='Edit user', user=user)
user=user)
@admin.route('/user/<int:user_id>/delete') @admin.route('/users/<int:user_id>/delete')
@login_required @login_required
@admin_required @admin_required
def delete_user(user_id): def delete_user(user_id):
...@@ -42,7 +39,7 @@ def delete_user(user_id): ...@@ -42,7 +39,7 @@ def delete_user(user_id):
return redirect(url_for('admin.index')) 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 @login_required
@admin_required @admin_required
def edit_user(user_id): def edit_user(user_id):
...@@ -63,4 +60,5 @@ def edit_user(user_id): ...@@ -63,4 +60,5 @@ def edit_user(user_id):
edit_user_form.role.data = user.role_id edit_user_form.role.data = user.role_id
return render_template('admin/edit_user.html.j2', return render_template('admin/edit_user.html.j2',
edit_user_form=edit_user_form, edit_user_form=edit_user_form,
title='Administration: Edit user', user=user) title='Edit user',
user=user)
from flask import current_app
from ..models import User from ..models import User
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import (BooleanField, PasswordField, StringField, SubmitField, from wtforms import (BooleanField, PasswordField, StringField, SubmitField,
...@@ -17,9 +18,9 @@ class RegistrationForm(FlaskForm): ...@@ -17,9 +18,9 @@ class RegistrationForm(FlaskForm):
username = StringField( username = StringField(
'Username', 'Username',
validators=[DataRequired(), Length(1, 64), validators=[DataRequired(), Length(1, 64),
Regexp('^[A-Za-z][A-Za-z0-9_.]*$', 0, Regexp(current_app.config['ALLOWED_USERNAME_REGEX'],
'Usernames must have only letters, numbers, dots ' message='Usernames must have only letters, numbers,'
'or underscores')] ' dots or underscores')]
) )
password = PasswordField( password = PasswordField(
'Password', 'Password',
......
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()
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'))
from flask import Blueprint from flask import Blueprint
profile = Blueprint('profile', __name__) settings = Blueprint('settings', __name__)
from . import views # noqa from . import views # noqa
from flask import current_app
from flask_login import current_user
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import (BooleanField, PasswordField, SelectField, StringField, from wtforms import (BooleanField, PasswordField, SelectField, StringField,
SubmitField, ValidationError) SubmitField, ValidationError)
from wtforms.validators import DataRequired, Email, EqualTo from wtforms.validators import DataRequired, Email, EqualTo, Length, Regexp
class EditEmailForm(FlaskForm): class ChangePasswordForm(FlaskForm):
email = StringField('New email', validators=[Email(), DataRequired()]) password = PasswordField('Old password', validators=[DataRequired()])
save_email = SubmitField('Save email') 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): class EditGeneralSettingsForm(FlaskForm):
dark_mode = BooleanField('Dark mode') 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 = SelectField(
'Job status mail notifications', 'Job status mail notifications',
choices=[('', 'Choose your option'), choices=[('', 'Choose your option'),
...@@ -25,28 +73,14 @@ class EditGeneralSettingsForm(FlaskForm): ...@@ -25,28 +73,14 @@ class EditGeneralSettingsForm(FlaskForm):
('end', 'Notify only when a job ended'), ('end', 'Notify only when a job ended'),
('none', 'No status update notifications')], ('none', 'No status update notifications')],
validators=[DataRequired()]) validators=[DataRequired()])
save_settings = SubmitField('Save settings') submit = SubmitField('Save settings')
class EditPasswordForm(FlaskForm): def __init__(self, user=current_user, *args, **kwargs):
current_password = PasswordField('Current password', super().__init__(*args, **kwargs)
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 self.user = user
self.job_status_mail_notifications.data = \
def validate_current_password(self, field): self.job_status_mail_notifications.data \
if not self.user.verify_password(field.data): or user.setting_job_status_mail_notifications
raise ValidationError('Invalid password.') self.job_status_site_notifications.data = \
self.job_status_site_notifications.data \
or user.setting_job_status_site_notifications
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 = { ...@@ -131,11 +131,11 @@ RessourceList.dataMappers = {
confirmed: user.confirmed, confirmed: user.confirmed,
email: user.email, email: user.email,
id: user.id, id: user.id,
link: `user/${user.id}`, link: `users/${user.id}`,
role_id: user.role_id, role_id: user.role_id,
username: user.username, username: user.username,
username2: 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": `delete-user-${user.id}-modal`,
"delete-modal-trigger": `delete-user-${user.id}-modal`, "delete-modal-trigger": `delete-user-${user.id}-modal`,
}), }),
......
{% extends "nopaque.html.j2" %} {% extends "nopaque.html.j2" %}
{% import 'materialize/wtf.html.j2' as wtf %}
{% block page_content %} {% block page_content %}
<div class="col s12 m4"> <div class="container">
<h3 id="title">{{ user.username }}</h3> <div class="row">
<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> <div class="col s12">
<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> <h1 id="title">Edit user</h1>
</div> </div>
<div class="col s12 m8"> <div class="col s12 m4">
<div class="card"> <h2>{{ user.username }}</h2>
<form method="POST"> <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>
<div class="card-content"> <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>
{{ edit_user_form.hidden_tag() }} </div>
{{ 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') }} <div class="col s12 m8">
{{ M.render_field(edit_user_form.role, material_icon='swap_vert') }} <div class="card">
{{ M.render_field(edit_user_form.confirmed, material_icon='check') }} <form method="POST">
</div> <div class="card-content">
<div class="card-action right-align"> {{ edit_user_form.hidden_tag() }}
{{ M.render_field(edit_user_form.submit, material_icon='send') }} {{ 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>
</form> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}
{% 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 %}
{% extends "nopaque.html.j2" %} {% extends "nopaque.html.j2" %}
{% block page_content %} {% block page_content %}
<div class="col s12 m4"> <div class="container">
<h3 id="title">{{ user.username }}</h3> <div class="row">
<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> <div class="col s12">
<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> <h1 id="title">{{ title }}</h1>
</div> </div>
<div class="col s12 m8"> <div class="col s12 m4">
<div class="card"> <h2>{{ user.username }}</h2>
<div class="card-content"> <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>
<span class="card-title">User information</span> <a class="waves-effect waves-light btn" href="{{ url_for('.users') }}"><i class="material-icons left">arrow_back</i>Back to Users</a>
<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>
<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> <div class="col s12 m8">
<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="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>
<div class="col s12 l6"> <div class="col s12 l6">
<h3>Corpora</h3> <h3>Corpora</h3>
...@@ -92,21 +98,23 @@ ...@@ -92,21 +98,23 @@
<!-- Modals --> <!-- Modals -->
<div id="delete-user-modal" class="modal"> <div id="delete-user-modal" class="modal">
<div class="modal-content"> <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> <p>Do you really want to delete the user {{ user.username }}? All associated data will be permanently deleted!</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<a href="#!" class="modal-close waves-effect waves-light btn">Cancel</a> <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>
</div> </div>
{% endblock %}
{% block scripts %}
{{ super() }}
<script type="module"> <script type="module">
import {RessourceList} from '../../static/js/nopaque.lists.js'; import {RessourceList} from '{{ url_for('static', filename='js/nopaque.lists.js') }}';
let corpusList = new RessourceList("corpora", nopaque.foreignCorporaSubscribers, "Corpus"); let corpusList = new RessourceList("corpora", nopaque.foreignCorporaSubscribers, "Corpus");
let jobList = new RessourceList("jobs", nopaque.foreignJobsSubscribers, "Job"); let jobList = new RessourceList("jobs", nopaque.foreignJobsSubscribers, "Job");
document.addEventListener("DOMContentLoaded", () => {
nopaque.socket.emit("foreign_user_data_stream_init", {{ user.id }}); nopaque.socket.emit("foreign_user_data_stream_init", {{ user.id }});
});
</script> </script>
{% endblock %} {% endblock scripts %}
{% 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 @@ ...@@ -18,7 +18,7 @@
<div class="col s12 m4"> <div class="col s12 m4">
<div class="card medium"> <div class="card medium">
<div class="card-content"> <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> <p>Want to boost your research and get going? nopaque is free and no download is needed. Register now!</p>
</div> </div>
<div class="card-action right-align"> <div class="card-action right-align">
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<div class="col s12 m4"> <div class="col s12 m4">
<div class="card medium"> <div class="card medium">
<div class="card-content"> <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>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>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> <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 @@ ...@@ -5,11 +5,11 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<h1>{{ title }}</h1> <h1 id="title">{{ title }}</h1>
</div> </div>
<div class="col s12 m4"> <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>
<div class="col s12 m8"> <div class="col s12 m8">
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<h1>{{ title }}</h1> <h1 id="title">{{ title }}</h1>
</div> </div>
<div class="col s12 m4"> <div class="col s12 m4">
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col s12"> <div class="col s12">
<h1>{{ title }}</h1> <h1 id="title">{{ title }}</h1>
</div> </div>
<div class="col s12"> <div class="col s12">
...@@ -13,10 +13,10 @@ ...@@ -13,10 +13,10 @@
<span class="card-title">Hello, {{ current_user.username }}!</span> <span class="card-title">Hello, {{ current_user.username }}!</span>
<p><b>You have not confirmed your account yet.</b></p> <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>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>
<div class="card-action right-align"> <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> </div>
</div> </div>
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
{% block page_content %} {% block page_content %}
<div class="container"> <div class="container">
<h1>{{ title }}</h1> <h1 id="title">{{ title }}</h1>
<p class="light">{{ request.path }}</p> <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> <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>
<div class="modal" id="more-information-modal"> <div class="modal" id="more-information-modal">
<div class="modal-content"> <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> <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>
<div class="modal-footer"> <div class="modal-footer">
......
...@@ -2,14 +2,14 @@ ...@@ -2,14 +2,14 @@
{% block page_content %} {% block page_content %}
<div class="container"> <div class="container">
<h1>{{ title }}</h1> <h1 id="title">{{ title }}</h1>
<p class="light">{{ request.path }}</p> <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> <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>
<div class="modal" id="more-information-modal"> <div class="modal" id="more-information-modal">
<div class="modal-content"> <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> <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>
<div class="modal-footer"> <div class="modal-footer">
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment