aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyaggr3g470r/templates/admin/create_user.html29
-rw-r--r--pyaggr3g470r/templates/admin/dashboard.html44
-rw-r--r--pyaggr3g470r/templates/admin/user.html49
-rw-r--r--pyaggr3g470r/templates/home.html2
-rw-r--r--pyaggr3g470r/templates/layout.html3
-rw-r--r--pyaggr3g470r/views.py120
6 files changed, 245 insertions, 2 deletions
diff --git a/pyaggr3g470r/templates/admin/create_user.html b/pyaggr3g470r/templates/admin/create_user.html
new file mode 100644
index 00000000..5a6838bd
--- /dev/null
+++ b/pyaggr3g470r/templates/admin/create_user.html
@@ -0,0 +1,29 @@
+{% extends "layout.html" %}
+{% block head%}
+{{super()}}
+{% endblock %}
+{% block content %}
+<div class="container">
+ <div class="jumbotron">
+ <h2>{{ message|safe }}</h2>
+ <form action="" method="post" name="saveprofileform" id="profileform">
+ {{ form.hidden_tag() }}
+
+ {{ form.firstname.label }}
+ {{ form.firstname(class_="form-control") }} {% for error in form.firstname.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
+
+ {{ form.lastname.label }}
+ {{ form.lastname(class_="form-control") }} {% for error in form.lastname.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
+
+ {{ form.email.label }}
+ {{ form.email(class_="form-control") }} {% for error in form.email.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
+
+ {{ form.password.label }}
+ {{ form.password(class_="form-control") }} {% for error in form.password.errors %} <span style="color: red;">{{ error }}<br /></span>{% endfor %}
+
+ <br />
+ {{ form.submit(class_="btn") }}
+ </form>
+ </div>
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/pyaggr3g470r/templates/admin/dashboard.html b/pyaggr3g470r/templates/admin/dashboard.html
new file mode 100644
index 00000000..2913d603
--- /dev/null
+++ b/pyaggr3g470r/templates/admin/dashboard.html
@@ -0,0 +1,44 @@
+{% extends "layout.html" %}
+{% block head%}
+{{super()}}
+{% endblock %}
+{% block content %}
+<div class="container">
+<h1>Registered users</h1>
+<table class="table table-striped">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Firstname</th>
+ <th>Lastname</th>
+ <th>Email</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for user in users|sort(attribute="firstname") %}
+ <tr>
+ <td>{{ loop.index }}</td>
+ <td><a href="/admin/user/{{ user.id }}/">{{ user.firstname }}</a></td>
+ <td>{{ user.lastname }}</td>
+ <td>{{ user.email }}</td>
+ <td>
+ <a href="/admin/user/{{ user.id }}/"><i class="glyphicon glyphicon-user" title="View this user"></i></a>
+ <a href="/admin/edit_user/{{ user.id }}/"><i class="glyphicon glyphicon-edit" title="Edit this user"></i></a>
+ {% if user.apikey == "" %}
+ <a href="/admin/enable_user/{{ user.id }}/"><i class="glyphicon glyphicon-ok-circle" title="Enable API key of this user"></i></a>
+ {% else %}
+ <a href="/admin/disable_user/{{ user.id }}/"><i class="glyphicon glyphicon-ban-circle" title="Disable API key of this user"></i></a>
+ {% endif %}
+ <a href="/admin/delete_user/{{ user.id }}/"><i class="glyphicon glyphicon-remove" title="Delete this user"></i></a>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+</table>
+<br />
+<a href="/admin/create_user/" class="btn btn-default">Add a new user</a>
+<br /><br />
+<p class="text-info">As an administrator you are not listed in this table.</p>
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/pyaggr3g470r/templates/admin/user.html b/pyaggr3g470r/templates/admin/user.html
new file mode 100644
index 00000000..767ff460
--- /dev/null
+++ b/pyaggr3g470r/templates/admin/user.html
@@ -0,0 +1,49 @@
+{% extends "layout.html" %}
+{% block head%}
+{{super()}}
+{% endblock %}
+{% block content %}
+<div class="container">
+ <div class="jumbotron">
+ <a href="/admin/edit_user/{{ user.id }}/" class="btn btn-default">Edit this user</a>
+ <h2>Membership</h2>
+ <p>Contributor since {{ user.date_created.strftime('%A, %d %B %Y') }}.</p>
+ <p>Last seen: {{ user.last_seen.strftime('%A, %d %B %Y at %H:%M:%S') }}.</p>
+ </div>
+ <div class="jumbotron">
+ {% if user.feeds.all()|count == 0 %}
+ <h1>This user is not subscribed to any feed.</h1>
+ {% else %}
+ <h1>Feeds</h1>
+ <table class="table table-striped">
+ <thead>
+ <tr>
+ <th>#</th>
+ <th>Name</th>
+ <th>Feed link</th>
+ <th>Site link</th>
+ <th>Number of articles</th>
+ <th>Actions</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for feed in user.feeds|sort(attribute="name") %}
+ <tr>
+ <td>{{ loop.index }}</td>
+ <td><a href="/feed/{{ feed.id }}">{{ feed.title }}</a></td>
+ <td>{{ feed.link }}</td>
+ <td>{{ feed.site_link }}</td>
+ <td>{{ feed.articles.all()|count }}</td>
+ <td>
+ <a href="/feed/{{ feed.id }}"><i class="glyphicon glyphicon-th-list" title="Measures"></i></a>
+ <a href="/edit_feed/{{ feed.id }}"><i class="glyphicon glyphicon-edit" title="Edit this feed"></i></a>
+ <a href="/delete_feed/{{ feed.id }}"><i class="glyphicon glyphicon-remove" title="Delete this feed"></i></a>
+ </td>
+ {% endfor %}
+ </tbody>
+ </table>
+ {% endif %}
+ <a href="/create_feed/" class="btn btn-default">Add a new feed</a>
+ </div>
+</div>
+{% endblock %} \ No newline at end of file
diff --git a/pyaggr3g470r/templates/home.html b/pyaggr3g470r/templates/home.html
index 223653c1..6363ee06 100644
--- a/pyaggr3g470r/templates/home.html
+++ b/pyaggr3g470r/templates/home.html
@@ -2,7 +2,7 @@
{% block content %}
<div class="container">
{% if user.feeds.all()|count == 0 %}
- <h1>You are not subscribed to any feed. <a href="/edit_feed/">Fix this</a>.</h1>
+ <h1>You are not subscribed to any feed. <a href="/create_feed/">Fix this</a>.</h1>
{% else %}
{% for feed in user.feeds|sort(attribute="title") %}
<div class="row">
diff --git a/pyaggr3g470r/templates/layout.html b/pyaggr3g470r/templates/layout.html
index e89706e9..71572181 100644
--- a/pyaggr3g470r/templates/layout.html
+++ b/pyaggr3g470r/templates/layout.html
@@ -56,6 +56,9 @@
</ul>
</li>
<li><a accesskey="m" href="/management/">Management</a></li>
+ {% if g.user.is_admin() %}
+ <li><a href="{{ url_for('dashboard') }}"><span class="glyphicon glyphicon-dashboard"></span> Dashboard</a></li>
+ {% endif %}
<li><a href="/about/">About</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
<li class="dropdown">
diff --git a/pyaggr3g470r/views.py b/pyaggr3g470r/views.py
index 1a5d45a9..7ca3ac02 100644
--- a/pyaggr3g470r/views.py
+++ b/pyaggr3g470r/views.py
@@ -31,6 +31,7 @@ import datetime
from flask import render_template, jsonify, request, flash, session, url_for, redirect, g, current_app, make_response
from flask.ext.login import LoginManager, login_user, logout_user, login_required, current_user, AnonymousUserMixin
from flask.ext.principal import Principal, Identity, AnonymousIdentity, identity_changed, identity_loaded, Permission, RoleNeed, UserNeed
+from werkzeug import generate_password_hash
import utils
import export
@@ -41,6 +42,10 @@ from forms import SigninForm, AddFeedForm, ProfileForm
from pyaggr3g470r import app, db
from pyaggr3g470r.models import User, Feed, Article, Role
+Principal(app)
+# Create a permission with a single Need, in this case a RoleNeed.
+admin_permission = Permission(RoleNeed('admin'))
+
login_manager = LoginManager()
login_manager.init_app(app)
@@ -156,7 +161,7 @@ def logout():
identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity())
flash("Logged out successfully.", 'success')
- return redirect(url_for('map_view'))
+ return redirect(url_for('login'))
@app.route('/')
@login_required
@@ -556,3 +561,116 @@ def profile():
if request.method == 'GET':
form = ProfileForm(obj=user)
return render_template('profile.html', user=user, form=form)
+
+
+
+#
+# Views dedicated to administration tasks.
+#
+@app.route('/admin/dashboard/', methods=['GET', 'POST'])
+@login_required
+@admin_permission.require()
+def dashboard():
+ """
+ Adminstrator's dashboard.
+ """
+ users = User.query.all()
+ users.remove(g.user)
+ return render_template('admin/dashboard.html', users=users)
+
+@app.route('/admin/create_user/', methods=['GET', 'POST'])
+@app.route('/admin/edit_user/<int:user_id>/', methods=['GET', 'POST'])
+@login_required
+@admin_permission.require()
+def create_user(user_id=None):
+ """
+ Create or edit a user.
+ """
+ form = ProfileForm()
+
+ if request.method == 'POST':
+ if form.validate():
+ if user_id != None:
+ # Edit a user
+ user = User.query.filter(User.id == user_id).first()
+ form.populate_obj(user)
+ if form.password.data != "":
+ user.set_password(form.password.data)
+ db.session.commit()
+ flash('User "' + user.firstname + '" successfully updated.', 'success')
+ else:
+ # Create a new user
+ role_user = Role.query.filter(Role.name == "user").first()
+ user = User(firstname=form.firstname.data,
+ lastname=form.lastname.data,
+ email=form.email.data,
+ pwdhash=generate_password_hash(form.password.data))
+ user.roles.extend([role_user])
+ db.session.add(user)
+ db.session.commit()
+ flash('User "' + user.firstname + '" successfully created.', 'success')
+ return redirect("/admin/edit_user/"+str(user.id)+"/")
+ else:
+ return render_template('profile.html', form=form)
+
+ if request.method == 'GET':
+ if user_id != None:
+ user = User.query.filter(User.id == user_id).first()
+ form = ProfileForm(obj=user)
+ message = "Edit the user <i>" + user.firstname + "</i>"
+ else:
+ form = ProfileForm()
+ message="Add a new user"
+ return render_template('/admin/create_user.html', form=form, message=message)
+
+@app.route('/admin/user/<int:user_id>/', methods=['GET'])
+@login_required
+@admin_permission.require()
+def user(user_id=None):
+ """
+ See information about a user (stations, etc.).
+ """
+ user = User.query.filter(User.id == user_id).first()
+ if user is not None:
+ return render_template('/admin/user.html', user=user)
+ else:
+ flash('This user does not exist.', 'danger')
+ return redirect(redirect_url())
+
+@app.route('/admin/delete_user/<int:user_id>/', methods=['GET'])
+@login_required
+@admin_permission.require()
+def delete_user(user_id=None):
+ """
+ Delete a user (with its stations and measures).
+ """
+ user = User.query.filter(User.id == user_id).first()
+ if user is not None:
+ db.session.delete(user)
+ db.session.commit()
+ flash('User "' + user.firstname + '" successfully deleted.', 'success')
+ else:
+ flash('This user does not exist.', 'danger')
+ return redirect(redirect_url())
+
+@app.route('/admin/enable_user/<int:user_id>/', methods=['GET'])
+@app.route('/admin/disable_user/<int:user_id>/', methods=['GET'])
+@login_required
+@admin_permission.require()
+def disable_user(user_id=None):
+ """
+ Enable or disable the API key of a user.
+ """
+ user = User.query.filter(User.id == user_id).first()
+ if user is not None:
+ if user.apikey != "":
+ user.pwdhash = ""
+ flash(user.firstname + '" successfully disabled.', 'success')
+ else:
+ #import random, base64, hashlib
+ user.pwdhash = "newpass"
+ flash(user.firstname + '" successfully enabled.', 'success')
+ db.session.commit()
+ else:
+ flash('This user does not exist.', 'danger')
+ return redirect(redirect_url()) \ No newline at end of file
bgstack15