From bccf6619577fa8a9cfc6d20e16b663b9c20b84af Mon Sep 17 00:00:00 2001
From: Stephan Porada <sporada@uni-bielefeld.de>
Date: Thu, 15 Aug 2019 15:56:53 +0200
Subject: [PATCH] Add new admin blueprint with new user delete feature.
 Reorganize templates etc.

---
 app/__init__.py                             |  3 +
 app/admin/__init__.py                       |  5 ++
 app/admin/views.py                          | 43 ++++++++++++
 app/main/views.py                           | 12 ----
 app/tables.py                               |  9 ++-
 app/templates/{main => admin}/admin.html.j2 |  0
 app/templates/admin/admin_user_page.html.j2 | 75 +++++++++++++++++++++
 app/templates/base.html.j2                  |  2 +-
 8 files changed, 134 insertions(+), 15 deletions(-)
 create mode 100644 app/admin/__init__.py
 create mode 100644 app/admin/views.py
 rename app/templates/{main => admin}/admin.html.j2 (100%)
 create mode 100644 app/templates/admin/admin_user_page.html.j2

diff --git a/app/__init__.py b/app/__init__.py
index 4432a4e8..87043486 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -33,6 +33,9 @@ def create_app(config_name):
     from .main import main as main_blueprint
     app.register_blueprint(main_blueprint)
 
+    from .admin import admin as admin_blueprint
+    app.register_blueprint(admin_blueprint, url_prefix='/admin')
+
     return app
 
 
diff --git a/app/admin/__init__.py b/app/admin/__init__.py
new file mode 100644
index 00000000..22877f95
--- /dev/null
+++ b/app/admin/__init__.py
@@ -0,0 +1,5 @@
+from flask import Blueprint
+
+admin = Blueprint('admin', __name__)
+
+from . import views
diff --git a/app/admin/views.py b/app/admin/views.py
new file mode 100644
index 00000000..04ca640d
--- /dev/null
+++ b/app/admin/views.py
@@ -0,0 +1,43 @@
+from flask import (abort, current_app, flash, redirect, request,
+                   render_template, url_for, send_from_directory)
+from flask_login import current_user, login_required
+from ..models import Corpus, User
+from ..tables import AdminUserTable, AdminUserItem
+from . import admin
+from ..decorators import admin_required
+from .. import db
+import os
+
+
+@admin.route('/overview', methods=['GET', 'POST'])
+@login_required
+@admin_required
+def for_admins_only():
+    users = User.query.order_by(User.username).all()
+    items = [AdminUserItem(u.username, u.email, u.role_id, u.confirmed, u.id) for u in users]
+    table = AdminUserTable(items).__html__()  # converts table object to html string
+    table = table.replace('tbody', 'tbody class="list"', 1)  # add class list to tbody element. Needed by list.js
+    return render_template('admin/admin.html.j2', title='Administration tools',
+                           table=table)
+
+
+@admin.route('/overview/admin_user_page/<int:user_id>', methods=['GET', 'POST'])
+@login_required
+@admin_required
+def admin_user_page(user_id):
+    selected_user = User.query.filter_by(id=user_id).first()
+    title = 'Administration of user {} with ID: {}'.format(selected_user.username,
+                                                           selected_user.id)
+    return render_template('admin/admin_user_page.html.j2',
+                           title=title, selected_user=selected_user)
+
+
+@admin.route('/overview/admin_user_page/delete/<int:user_id>', methods=['GET', 'POST'])
+@login_required
+@admin_required
+def admin_delete_user(user_id):
+    selected_user = User.query.filter_by(id=user_id).first()
+    db.session.delete(selected_user)
+    db.session.commit()
+    flash('User {} has been deleted!'.format(user_id))
+    return redirect(url_for('admin.for_admins_only'))
diff --git a/app/main/views.py b/app/main/views.py
index 024e74fb..9cf624a6 100644
--- a/app/main/views.py
+++ b/app/main/views.py
@@ -15,18 +15,6 @@ def index():
     return render_template('main/index.html.j2', title='Opaque')
 
 
-@main.route('/admin', methods=['GET', 'POST'])
-@login_required
-@admin_required
-def for_admins_only():
-    users = User.query.order_by(User.username).all()
-    items = [AdminUserItem(u.username, u.email, u.role_id, u.confirmed) for u in users]
-    table = AdminUserTable(items).__html__()  # converts table object to html string
-    table = table.replace('tbody', 'tbody class="list"', 1)  # add class list to tbody element. Needed by list.js
-    return render_template('main/admin.html.j2', title='Administration tools',
-                           table=table)
-
-
 @main.route('/corpora/<int:corpus_id>')
 @login_required
 def corpus(corpus_id):
diff --git a/app/tables.py b/app/tables.py
index ea967e3e..77f9a009 100644
--- a/app/tables.py
+++ b/app/tables.py
@@ -1,4 +1,4 @@
-from flask_table import Table, Col
+from flask_table import Table, Col, ButtonCol, LinkCol
 
 
 class AdminUserTable(Table):
@@ -10,6 +10,10 @@ class AdminUserTable(Table):
     email = Col('Email', column_html_attrs={'class': 'email'})
     role_id = Col('Role', column_html_attrs={'class': 'role'})
     confirmed = Col('Confrimed Status', column_html_attrs={'class': 'confirmed'})
+    id = Col('User Id', column_html_attrs={'class': 'id'})
+    url = LinkCol('Profile', 'admin.admin_user_page',
+                    url_kwargs=dict(user_id='id'),
+                    anchor_attrs={'class': 'waves-effect waves-light btn-small'})
 
 
 class AdminUserItem(object):
@@ -17,11 +21,12 @@ class AdminUserItem(object):
     Describes one item like one row per table.
     """
 
-    def __init__(self, username, email, role_id, confirmed):
+    def __init__(self, username, email, role_id, confirmed, id):
         self.username = username
         self.email = email
         self.role_id = role_id
         self.confirmed = confirmed
+        self.id = id
 
         if self.role_id == 1:
             self.role_id = 'User'
diff --git a/app/templates/main/admin.html.j2 b/app/templates/admin/admin.html.j2
similarity index 100%
rename from app/templates/main/admin.html.j2
rename to app/templates/admin/admin.html.j2
diff --git a/app/templates/admin/admin_user_page.html.j2 b/app/templates/admin/admin_user_page.html.j2
new file mode 100644
index 00000000..49b1a35d
--- /dev/null
+++ b/app/templates/admin/admin_user_page.html.j2
@@ -0,0 +1,75 @@
+{% extends "base.html.j2" %}
+
+{% block page_content %}
+<div class="col s12 m6">
+  <div class="card large">
+    <div class="card-content">
+      <span class="card-title">User information</span>
+    </div>
+  </div>
+</div>
+<div class="col s12 m6">
+  <div class="card large">
+    <div class="card-content">
+      <span class="card-title">User Jobs</span>
+      <div id="users">
+        <div class="input-field">
+          <i class="material-icons prefix">search</i>
+          <input id="search-corpus" class="search" type="text"></input>
+          <label for="search-corpus">Search users</label>
+        </div>
+        <div class="collection list">
+          {% for job in selected_user.jobs.all() %}
+            {% if job.service == 'nlp' %}
+              {% set service_color = 'blue' %}
+              {% set service_icon = 'format_textdirection_l_to_r' %}
+            {% elif job.service =='ocr' %}
+              {% set service_color = 'green' %}
+              {% set service_icon = 'find_in_page' %}
+            {% else %}
+              {% set service_color = 'red' %}
+              {% set service_icon = 'help' %}
+            {% endif %}
+            {% if job.status == 'pending' %}
+              {% set badge_color = 'amber' %}
+            {% elif job.status =='running' %}
+              {% set badge_color = 'indigo' %}
+            {% elif job.status =='complete' %}
+              {% set badge_color = 'teal' %}
+            {% else %}
+              {% set badge_color = 'red' %}
+            {% endif %}
+            <a href="{{ url_for('main.job', job_id=job.id) }}" class="collection-item avatar">
+                <i class="material-icons circle {{ service_color }}">{{ service_icon }}</i>
+                <span class="new badge {{ badge_color }}" data-badge-caption="">{{ job.status }}</span>
+                <span class="title">{{ job.title }}</span>
+                <p>{{ job.description }}</p>
+            </a>
+          {% endfor %}
+        </div>
+        <ul class="pagination"></ul>
+      </div>
+    </div>
+  </div>
+</div>
+<div class="col s12">
+  <div class="card large">
+    <div class="card-content">
+      <span class="card-title">Administration actions</span>
+      <!-- Confirm deletion of selected user with modal dialogue
+    Modal Trigger-->
+      <a href="#modal-confirm-delete" class="waves-effect waves-light btn modal-trigger"><i class="material-icons left">delete</i>Delete User</a>
+      <!-- Modal Strucutre -->
+      <div id="modal-confirm-delete" class="modal">
+        <div class="modal-content">
+          <h4>Confirm deletion</h4>
+            <p>Do you really want to delete the current selected user ({{selected_user.username}})?</p>
+        </div>
+        <div class="modal-footer">
+          <a href="{{url_for('admin.admin_delete_user', user_id=selected_user.id)}}" class="modal-close waves-effect waves-green btn red"><i class="material-icons left">delete</i>Delete User</a></a>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+{% endblock %}
diff --git a/app/templates/base.html.j2 b/app/templates/base.html.j2
index bec894a5..94ca3988 100644
--- a/app/templates/base.html.j2
+++ b/app/templates/base.html.j2
@@ -95,7 +95,7 @@
         {% if current_user.is_administrator() %} <!-- Shows only for admins -->
           <li><div class="divider"></div></li>
           <li><a class="subheader">Administration</a></li>
-          <li><a href="{{ url_for('main.for_admins_only') }}"><i class="material-icons">build</i>Administration tools</a></li>
+          <li><a href="{{ url_for('admin.for_admins_only') }}"><i class="material-icons">build</i>Administration tools</a></li>
         {% endif %}
       </ul>
     </header>
-- 
GitLab