diff --git a/web/app/__init__.py b/web/app/__init__.py
index 7a5d8b6850c2f3e48dc30227e017801f5f5254ba..a39a51a55576aa65c4bcf433a98696f8a66e76a6 100644
--- a/web/app/__init__.py
+++ b/web/app/__init__.py
@@ -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
diff --git a/web/app/admin/views.py b/web/app/admin/views.py
index e8d0d91691db6e86a4b283620c2780efe78df872..3046a63ccae9a776b47a9fe17f71c2f308f2b6f9 100644
--- a/web/app/admin/views.py
+++ b/web/app/admin/views.py
@@ -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)
diff --git a/web/app/auth/forms.py b/web/app/auth/forms.py
index e3f5c2766915db5f302aa634f8046d0fa29a1b4a..3344096b519da6a684170c2469f0a9bac09986d5 100644
--- a/web/app/auth/forms.py
+++ b/web/app/auth/forms.py
@@ -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',
diff --git a/web/app/profile/forms.py b/web/app/profile/forms.py
deleted file mode 100644
index 14517e293ed0d48ba89e1d75fe43eb1b9ac4d6de..0000000000000000000000000000000000000000
--- a/web/app/profile/forms.py
+++ /dev/null
@@ -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.')
diff --git a/web/app/profile/tasks.py b/web/app/profile/tasks.py
deleted file mode 100644
index 61f737c564770c608860b136c79d8639dbe4defc..0000000000000000000000000000000000000000
--- a/web/app/profile/tasks.py
+++ /dev/null
@@ -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()
diff --git a/web/app/profile/views.py b/web/app/profile/views.py
deleted file mode 100644
index f156715fbf824382c2d36a52a78482d12444f96c..0000000000000000000000000000000000000000
--- a/web/app/profile/views.py
+++ /dev/null
@@ -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'))
diff --git a/web/app/profile/__init__.py b/web/app/settings/__init__.py
similarity index 57%
rename from web/app/profile/__init__.py
rename to web/app/settings/__init__.py
index 85b9f61cdd705ef41af827f47c015aa5bd817917..2329d880513181a2642551fd3fd1076d8a727217 100644
--- a/web/app/profile/__init__.py
+++ b/web/app/settings/__init__.py
@@ -1,5 +1,5 @@
 from flask import Blueprint
 
 
-profile = Blueprint('profile', __name__)
+settings = Blueprint('settings', __name__)
 from . import views  # noqa
diff --git a/web/app/settings/forms.py b/web/app/settings/forms.py
new file mode 100644
index 0000000000000000000000000000000000000000..8cf9641501fc714c19416b62b027b4058bd0b69e
--- /dev/null
+++ b/web/app/settings/forms.py
@@ -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
diff --git a/web/app/settings/views.py b/web/app/settings/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..a90d8ab22293ce6798caf6904fd6fff9ff037a78
--- /dev/null
+++ b/web/app/settings/views.py
@@ -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'))
diff --git a/web/app/static/js/nopaque.lists.js b/web/app/static/js/nopaque.lists.js
index d91186dcc95b727e9ed58e6c1109a699674b46fa..603f81b108f4e80cf1ed2b91295d84fe7add706a 100644
--- a/web/app/static/js/nopaque.lists.js
+++ b/web/app/static/js/nopaque.lists.js
@@ -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`,
   }),
diff --git a/web/app/templates/admin/edit_user.html.j2 b/web/app/templates/admin/edit_user.html.j2
index 3a48adf8e16f8c20f87bb529a2b9c81e20e4a22b..6e47db5f88e672db02095ae5728efe77e00da702 100644
--- a/web/app/templates/admin/edit_user.html.j2
+++ b/web/app/templates/admin/edit_user.html.j2
@@ -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>
-      <div class="card-action right-align">
-        {{ M.render_field(edit_user_form.submit, material_icon='send') }}
+    <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>
-    </form>
+    </div>
   </div>
 </div>
-
 {% endblock %}
diff --git a/web/app/templates/admin/index.html.j2 b/web/app/templates/admin/index.html.j2
deleted file mode 100644
index 9fca5b583b8cf29374a0b0f43fae0096d680cc9b..0000000000000000000000000000000000000000
--- a/web/app/templates/admin/index.html.j2
+++ /dev/null
@@ -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 %}
diff --git a/web/app/templates/admin/user.html.j2 b/web/app/templates/admin/user.html.j2
index c1e53a182f9eb2a6b3a1c52c366afa0732b1b80f..81fc243a3aa6f4013ee3a913af9f9baeb9f69a60 100644
--- a/web/app/templates/admin/user.html.j2
+++ b/web/app/templates/admin/user.html.j2
@@ -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>
+{% endblock %}
 
+
+{% block scripts %}
+{{ super() }}
 <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", () => {
+  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 %}
+{% endblock scripts %}
diff --git a/web/app/templates/admin/users.html.j2 b/web/app/templates/admin/users.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..539ce0f13e2ea2e2c9a298a9ea4ac4081e1987a3
--- /dev/null
+++ b/web/app/templates/admin/users.html.j2
@@ -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 %}
diff --git a/web/app/templates/auth/login.html.j2 b/web/app/templates/auth/login.html.j2
index a477da4fcd6f01bfb040e483018630ff072d7bb5..657da25f602a21a6451a2998ba9832a70e9906a7 100644
--- a/web/app/templates/auth/login.html.j2
+++ b/web/app/templates/auth/login.html.j2
@@ -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">
diff --git a/web/app/templates/auth/register.html.j2 b/web/app/templates/auth/register.html.j2
index db4ef2c002ea682597ec4cec7ab072e1479d947c..9909cd608b1cb51d79ecb9026747d79d0adeb231 100644
--- a/web/app/templates/auth/register.html.j2
+++ b/web/app/templates/auth/register.html.j2
@@ -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>
diff --git a/web/app/templates/auth/reset_password.html.j2 b/web/app/templates/auth/reset_password.html.j2
index 414a2d1032a8b5df5b281b94c0627e2d808d3bb9..f3e4f26e71ec20db9f9da4b27ee5a44f33c7f265 100644
--- a/web/app/templates/auth/reset_password.html.j2
+++ b/web/app/templates/auth/reset_password.html.j2
@@ -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">
diff --git a/web/app/templates/auth/reset_password_request.html.j2 b/web/app/templates/auth/reset_password_request.html.j2
index 549af6cdc26b67161a9bd18f5f75f0ece4669985..403f1388230424cd657c47ce529c32edba90598f 100644
--- a/web/app/templates/auth/reset_password_request.html.j2
+++ b/web/app/templates/auth/reset_password_request.html.j2
@@ -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">
diff --git a/web/app/templates/auth/unconfirmed.html.j2 b/web/app/templates/auth/unconfirmed.html.j2
index 98ce4de474ecb816d4f7d961caf37530200a07c2..613c51a402ca22ef462a48c24ff711fd065fcf2f 100644
--- a/web/app/templates/auth/unconfirmed.html.j2
+++ b/web/app/templates/auth/unconfirmed.html.j2
@@ -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>
diff --git a/web/app/templates/errors/403.html.j2 b/web/app/templates/errors/403.html.j2
index 7a4eca4e1eeb2a0f4e6e853aa9a9533dbed5b0d9..0ebbea86e4219589a193790d325abdaef1914fda 100644
--- a/web/app/templates/errors/403.html.j2
+++ b/web/app/templates/errors/403.html.j2
@@ -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">
diff --git a/web/app/templates/errors/404.html.j2 b/web/app/templates/errors/404.html.j2
index a6bf74926ea95260d36800818de209457fe607b7..07692692b1420e8d1845fdfcf11b18f848e622ac 100644
--- a/web/app/templates/errors/404.html.j2
+++ b/web/app/templates/errors/404.html.j2
@@ -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">
diff --git a/web/app/templates/errors/413.html.j2 b/web/app/templates/errors/413.html.j2
index 1be86042bb4bc0cc901136d3024512fd9536769d..b711c42f0772f897b037550a7bc1b6d7b571cd3a 100644
--- a/web/app/templates/errors/413.html.j2
+++ b/web/app/templates/errors/413.html.j2
@@ -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">
diff --git a/web/app/templates/errors/500.html.j2 b/web/app/templates/errors/500.html.j2
index 1faaa42d8fa50771ce0d924bde2ba439b81ee6c9..2db380d9c98b8279671180825cb4a41cfcbeeb5a 100644
--- a/web/app/templates/errors/500.html.j2
+++ b/web/app/templates/errors/500.html.j2
@@ -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">
diff --git a/web/app/templates/main/about_and_faq.html.j2 b/web/app/templates/main/about_and_faq.html.j2
index b94a4d648c8f22f709945dd073d13e2345a1e1af..24e87d672c95719a8571ced7e6876bd7ec8ccbc9 100644
--- a/web/app/templates/main/about_and_faq.html.j2
+++ b/web/app/templates/main/about_and_faq.html.j2
@@ -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">
diff --git a/web/app/templates/main/dashboard.html.j2 b/web/app/templates/main/dashboard.html.j2
index 467aee301b303cda571fb213208065cf4dea244a..71d4f480182ed9d0917904ab1a0e1111aede3435 100644
--- a/web/app/templates/main/dashboard.html.j2
+++ b/web/app/templates/main/dashboard.html.j2
@@ -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">
diff --git a/web/app/templates/main/index.html.j2 b/web/app/templates/main/index.html.j2
index aec0ecb3403111816ba58dcc96f6555f7f8a7577..dbc32c3e95b510d8abd1ff775b9ed1d00e224644 100644
--- a/web/app/templates/main/index.html.j2
+++ b/web/app/templates/main/index.html.j2
@@ -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>
diff --git a/web/app/templates/main/news.html.j2 b/web/app/templates/main/news.html.j2
index cf3d96534b5c17568d0c15c2cbe4278cd7d7abeb..c7947530590bfa498f6cd5f755c49eef4a899fd0 100644
--- a/web/app/templates/main/news.html.j2
+++ b/web/app/templates/main/news.html.j2
@@ -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">
diff --git a/web/app/templates/main/privacy_policy.html.j2 b/web/app/templates/main/privacy_policy.html.j2
index db1561c84b363b77f5805eeaf72a1df18ddab2eb..71c469bf6cc8bc0f0de28d1ded1c964ec129b507 100644
--- a/web/app/templates/main/privacy_policy.html.j2
+++ b/web/app/templates/main/privacy_policy.html.j2
@@ -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">
diff --git a/web/app/templates/main/terms_of_use.html.j2 b/web/app/templates/main/terms_of_use.html.j2
index a763fbf03aaef3b454659966026a30046a5e51ae..dbe39dc93b3e014a2d3cceb5410188cde8d2f312 100644
--- a/web/app/templates/main/terms_of_use.html.j2
+++ b/web/app/templates/main/terms_of_use.html.j2
@@ -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">
diff --git a/web/app/templates/modals/analysis_init.html.j2 b/web/app/templates/modals/analysis_init.html.j2
index b7e540260202b4c17815d771c13d9e005130e52c..b4f189cf31270f567f5e8d1a68433192b0079054 100644
--- a/web/app/templates/modals/analysis_init.html.j2
+++ b/web/app/templates/modals/analysis_init.html.j2
@@ -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>
\ No newline at end of file
+</div>
diff --git a/web/app/templates/modals/context_modal.html.j2 b/web/app/templates/modals/context_modal.html.j2
index 636c907e42f0a9483d1ca5c304dbb7ff1345dd45..185a767a2b5758d4943520a9732ea7a9aa9ba41e 100644
--- a/web/app/templates/modals/context_modal.html.j2
+++ b/web/app/templates/modals/context_modal.html.j2
@@ -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>
\ No newline at end of file
+</div>
diff --git a/web/app/templates/modals/export_query_results.html.j2 b/web/app/templates/modals/export_query_results.html.j2
index e49edb93e9183b632bbb0b7e599a1220f5b56d2e..cf544d5e8ea77e85ac7b064ad43065bf1697263f 100644
--- a/web/app/templates/modals/export_query_results.html.j2
+++ b/web/app/templates/modals/export_query_results.html.j2
@@ -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>
\ No newline at end of file
+</div>
diff --git a/web/app/templates/modals/show_corpus_files.html.j2 b/web/app/templates/modals/show_corpus_files.html.j2
index 6b960fda430c6aadb86585eab3d8f04396fbfe10..942c8130126068d9e91a73a49b9beec45ccf5186 100644
--- a/web/app/templates/modals/show_corpus_files.html.j2
+++ b/web/app/templates/modals/show_corpus_files.html.j2
@@ -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">
diff --git a/web/app/templates/modals/show_metadata.html.j2 b/web/app/templates/modals/show_metadata.html.j2
index 863204b77357612c38d2f3acd8241b703e963810..e07d15fb579ff9572aa68ddb91bb4da2581cea1a 100644
--- a/web/app/templates/modals/show_metadata.html.j2
+++ b/web/app/templates/modals/show_metadata.html.j2
@@ -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">
diff --git a/web/app/templates/modals/show_text_details.html.j2 b/web/app/templates/modals/show_text_details.html.j2
index e3a7f18a0dfbacc5b2b51c1162ae264f511efe46..f246da538aca09eb09867f93823d11eeb272cb4c 100644
--- a/web/app/templates/modals/show_text_details.html.j2
+++ b/web/app/templates/modals/show_text_details.html.j2
@@ -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>
\ No newline at end of file
+</div>
diff --git a/web/app/templates/nopaque.html.j2 b/web/app/templates/nopaque.html.j2
index 0437c29ab24e26d4e9ce726407d3009b668dd087..7c01e1953fc7ec67e5b97aef536871783cdeabfd 100644
--- a/web/app/templates/nopaque.html.j2
+++ b/web/app/templates/nopaque.html.j2
@@ -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 %}
diff --git a/web/app/templates/profile/settings.html.j2 b/web/app/templates/profile/settings.html.j2
deleted file mode 100644
index 64e5152b3e729b7af10056fb4d567d809cfe430e..0000000000000000000000000000000000000000
--- a/web/app/templates/profile/settings.html.j2
+++ /dev/null
@@ -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>&nbsp;</p></div>
-          <div class="col s12 divider"></div>
-          <div class="col s12"><p>&nbsp;</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>&nbsp;</p></div>
-          <div class="col s12 divider"></div>
-          <div class="col s12"><p>&nbsp;</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>&nbsp;</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 %}
diff --git a/web/app/templates/settings/_menu.html.j2 b/web/app/templates/settings/_menu.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..554a3f67eb49b1bcaabffda54ada154afa959bad
--- /dev/null
+++ b/web/app/templates/settings/_menu.html.j2
@@ -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>
diff --git a/web/app/templates/settings/change_password.html.j2 b/web/app/templates/settings/change_password.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..771886bf7d8f17fd7d0b0e39ac3ccb42704e25fe
--- /dev/null
+++ b/web/app/templates/settings/change_password.html.j2
@@ -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 %}
diff --git a/web/app/templates/settings/edit_general_settings.html.j2 b/web/app/templates/settings/edit_general_settings.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..4126ee8eb4b289bcbcb2494d29855faed0386361
--- /dev/null
+++ b/web/app/templates/settings/edit_general_settings.html.j2
@@ -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 %}
diff --git a/web/app/templates/settings/edit_notification_settings.html.j2 b/web/app/templates/settings/edit_notification_settings.html.j2
new file mode 100644
index 0000000000000000000000000000000000000000..2a8e7b24f02bcedb87a784fe21e5a6f70cb70cfa
--- /dev/null
+++ b/web/app/templates/settings/edit_notification_settings.html.j2
@@ -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 %}
diff --git a/web/config.py b/web/config.py
index 0f78e6adf6d7642a1eec3a4561f20f5032a950cc..4ca3704fa524c804bc26e5f172945703fe8fbbe0 100644
--- a/web/config.py
+++ b/web/config.py
@@ -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')