[odrs-web/production] Add a moderator class that can take over from me
- From: Richard Hughes <rhughes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [odrs-web/production] Add a moderator class that can take over from me
- Date: Fri, 7 Jul 2017 15:56:29 +0000 (UTC)
commit 17ec2f4cc3a4756281445231c69e3ac9f3a4c067
Author: Richard Hughes <richard hughsie com>
Date: Fri Jul 7 16:53:47 2017 +0100
Add a moderator class that can take over from me
README.md | 2 +
app/__init__.py | 2 +-
app/db.py | 148 +++++++++++++++++++++++---
app/models.py | 48 +++++---
app/templates/default.html | 5 +
app/templates/modadmin.html | 53 +++++++++
app/templates/mods.html | 52 +++++++++
app/views.py | 8 +-
app/views_admin.py | 248 ++++++++++++++++++++++++++++++++++++++-----
schema.sql | 18 +++-
10 files changed, 515 insertions(+), 69 deletions(-)
---
diff --git a/README.md b/README.md
index 7461fbb..a1c4032 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,8 @@ To set up the database tables do:
GRANT ALL ON odrs.* TO 'test'@'localhost';
SOURCE /path/to/schema.sql
+The default admin password is `Pa$$w0rd`
+
## How do I backup the data ##
You want to save the variable `ODRS_REVIEWS_SECRET` so that old review data
diff --git a/app/__init__.py b/app/__init__.py
index 861a48f..5eb1947 100644
--- a/app/__init__.py
+++ b/app/__init__.py
@@ -35,7 +35,7 @@ def teardown_request(exception):
@lm.user_loader
def load_user(user_id):
db = get_db()
- user = db.users.get_by_id(user_id)
+ user = db.moderators.get_by_id(user_id)
return user
@app.errorhandler(404)
diff --git a/app/db.py b/app/db.py
index a4a68ed..aef2df7 100644
--- a/app/db.py
+++ b/app/db.py
@@ -13,7 +13,7 @@ import hashlib
import pymysql as mdb
-from .models import User, Event, Review
+from .models import User, Event, Review, Moderator
class CursorError(Exception):
def __init__(self, cur, e):
@@ -64,6 +64,20 @@ def _create_user(e):
user.is_banned = int(e[4])
return user
+def _create_moderator(e):
+ """ Parse a user """
+ mod = Moderator()
+ mod.moderator_id = int(e[0])
+ mod.username = e[1]
+ mod.password = e[2]
+ mod.display_name = e[3]
+ mod.email = e[4]
+ mod.is_enabled = int(e[5])
+ mod.is_admin = int(e[6])
+ mod.user_hash = e[7]
+ mod.locales = e[8]
+ return mod
+
def _password_hash(value):
""" Generate a salted hash of the password string """
salt = 'odrs%%%'
@@ -92,6 +106,7 @@ class Database(object):
print("Error %d: %s" % (e.args[0], e.args[1]))
assert self._db
self.users = DatabaseUsers(self._db)
+ self.moderators = DatabaseModerators(self._db)
self.reviews = DatabaseReviews(self._db)
self.eventlog = DatabaseEventlog(self._db)
@@ -537,6 +552,121 @@ class DatabaseReviews(object):
data.append(en[0])
return data
+class DatabaseModerators(object):
+
+ def __init__(self, db):
+ """ Constructor for object """
+ self._db = db
+
+ def get_all(self):
+ """ Get all the users on the system """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT moderator_id, username, password, "
+ "display_name, email, is_enabled, is_admin, "
+ "user_hash, locales "
+ "FROM moderators ORDER BY moderator_id ASC;")
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchall()
+ if not res:
+ return []
+ users = []
+ for e in res:
+ users.append(_create_moderator(e))
+ return users
+
+ def get_by_id(self, moderator_id):
+ """ Get information about a specific user """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT moderator_id, username, password, "
+ "display_name, email, is_enabled, is_admin, "
+ "user_hash, locales "
+ "FROM moderators WHERE moderator_id=%s;",
+ (moderator_id,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ if not res:
+ return None
+ return _create_moderator(res)
+
+ def get_by_username(self, username):
+ """ Get information about a specific user """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT moderator_id, username, password, "
+ "display_name, email, is_enabled, is_admin, "
+ "user_hash, locales "
+ "FROM moderators WHERE username=%s;",
+ (username,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ if not res:
+ return None
+ return _create_moderator(res)
+
+ def get_by_username_password(self, username, password):
+ """ Get information about a specific login """
+ try:
+ cur = self._db.cursor()
+ cur.execute("SELECT moderator_id, username, password, "
+ "display_name, email, is_enabled, is_admin, "
+ "user_hash, locales "
+ "FROM moderators WHERE username=%s AND password=%s;",
+ (username,
+ _password_hash(password),))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ if not res:
+ return None
+ return _create_moderator(res)
+
+ def add(self, username, password, display_name, email):
+ """ Adds a moderator """
+ try:
+ cur = self._db.cursor()
+ cur.execute("INSERT INTO moderators (username, password, display_name, "
+ "email, is_enabled) "
+ "VALUES (%s, %s, %s, %s, %s);",
+ (username,
+ _password_hash(password),
+ display_name,
+ email,
+ True,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+ res = cur.fetchone()
+ if not res:
+ return None
+ return _create_moderator(res)
+
+ def delete(self, username):
+ """ Deletes a moderator """
+ try:
+ cur = self._db.cursor()
+ cur.execute("DELETE FROM moderators WHERE username = %s;",
+ (username,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+
+ def set_property(self, username, key, value):
+ """ Sets some properties on the moderator """
+ assert username
+ assert key
+
+ try:
+ query = "UPDATE moderators SET %s=%%s WHERE username=%%s;" % key
+ if key == 'password':
+ value = _password_hash(value)
+ cur = self._db.cursor()
+ cur.execute(query, (value, username,))
+ except mdb.Error as e:
+ raise CursorError(cur, e)
+
class DatabaseUsers(object):
def __init__(self, db):
@@ -569,22 +699,6 @@ class DatabaseUsers(object):
users.append(_create_user(e))
return users
- def get_with_login(self, username, password):
- """ Get information about a specific login """
- try:
- cur = self._db.cursor()
- cur.execute("SELECT user_id, date_created, "
- "user_hash, karma, is_banned "
- "FROM users WHERE user_hash=%s and password=%s;",
- (username,
- _password_hash(password),))
- except mdb.Error as e:
- raise CursorError(cur, e)
- res = cur.fetchone()
- if not res:
- return None
- return _create_user(res)
-
def get_by_id(self, user_hash):
""" Get information about a specific user """
try:
diff --git a/app/models.py b/app/models.py
index 67a6cb6..a687a82 100644
--- a/app/models.py
+++ b/app/models.py
@@ -14,24 +14,6 @@ class User(object):
self.user_hash = 0
self.is_banned = 0
- @property
- def is_authenticated(self):
- return True
-
- @property
- def is_active(self):
- return True
-
- @property
- def is_anonymous(self):
- return False
-
- def get_id(self):
- return str(self.id)
-
- def __repr__(self):
- return '<User %r>' % (self.id)
-
class Review(object):
def __init__(self):
self.review_id = 0
@@ -59,3 +41,33 @@ class Event(object):
self.message = None
self.app_id = None
self.important = False
+
+class Moderator(object):
+ def __init__(self):
+ self.moderator_id = 0
+ self.username = None
+ self.password = None
+ self.display_name = None
+ self.email = None
+ self.is_enabled = False
+ self.is_admin = False
+ self.user_hash = None
+ self.locales = None
+
+ @property
+ def is_authenticated(self):
+ return True
+
+ @property
+ def is_active(self):
+ return True
+
+ @property
+ def is_anonymous(self):
+ return False
+
+ def get_id(self):
+ return str(self.moderator_id)
+
+ def __repr__(self):
+ return '<Moderator %r>' % (self.moderator_id)
diff --git a/app/templates/default.html b/app/templates/default.html
index 9c8db80..6dd525a 100644
--- a/app/templates/default.html
+++ b/app/templates/default.html
@@ -39,10 +39,15 @@
<li><a href="/admin/show/all">All Reviews</a></li>
{% if current_user.is_authenticated %}
<li><a href="/admin/show/reported">Reported</a></li>
+{% endif %}
+{% if current_user.is_admin %}
<li><a href="/admin/stats">Statistics</a></li>
<li><a href="/admin/users/all">Users</a></li>
+ <li><a href="/admin/moderators/all">Moderators</a></li>
<li><a href="/admin/distros">Distributions</a></li>
<li><a href="/admin/graph_month">Usage</a></li>
+{% else %}
+ <li><a href="/admin/moderator/{{current_user.username}}/admin">Profile</a></li>
{% endif %}
</ul>
</div>
diff --git a/app/templates/modadmin.html b/app/templates/modadmin.html
new file mode 100644
index 0000000..a091215
--- /dev/null
+++ b/app/templates/modadmin.html
@@ -0,0 +1,53 @@
+{% extends "default.html" %}
+{% block title %}Edit Moderator{% endblock %}
+
+{% block content %}
+
+<h1>Details of user ‘{{u.username}}’</h1>
+
+<form method="post" action="/admin/moderator/{{u.username}}/modify_by_admin">
+ <table>
+ <tr>
+ <td>Display Name:</td>
+ <td><input type="text" class="form-control" name="display_name" value="{{u.display_name}}"
required></td>
+ </tr>
+ <tr>
+ <td>User Hash:</td>
+ <td><input type="text" class="form-control" name="user_hash" value="{{u.user_hash}}" required></td>
+ </tr>
+ <tr>
+ <td>Languages Spoken<br/>(e.g. <code>en,fr,pl</code>):</td>
+ <td><input type="text" class="form-control" name="locales" value="{{u.locales}}" required></td>
+ </tr>
+ <tr>
+ <td>Contact Email:</td>
+ <td><input type="text" class="form-control" name="email" value="{{u.email}}" required></td>
+ </tr>
+ <tr>
+ <td>New Password<br/>(optional):</td>
+ <td><input type="password" class="form-control" name="password"></td>
+ </tr>
+{% if not current_user.is_admin or u.username == 'admin' %}
+ <input type="hidden" name="is_enabled" value="1"/>
+{% else %}
+ <tr>
+ <td>Parameters:</td>
+ <td>
+{% if u.is_enabled %}
+ <input class="checkbox" type="checkbox" name="is_enabled" value="1" checked>Account enabled</input>
+{% else %}
+ <input class="checkbox" type="checkbox" name="is_enabled" value="1"/>Account enabled</input>
+{% endif %}
+ </td>
+ </tr>
+{% endif %}
+ </table>
+ <button type="submit" class="btn btn-primary btn-large" class="submit">Modify</button>
+{% if u.username != 'admin' and current_user.is_admin %}
+ <form method="get" action="/admin/moderator/{{u.username}}/delete">
+{% endif %}
+ <button class="btn btn-danger btn-large">Delete</button>
+ </form>
+</form>
+
+{% endblock %}
diff --git a/app/templates/mods.html b/app/templates/mods.html
new file mode 100644
index 0000000..d96adcc
--- /dev/null
+++ b/app/templates/mods.html
@@ -0,0 +1,52 @@
+{% extends "default.html" %}
+{% block title %}Moderators{% endblock %}
+
+{% block content %}
+
+<h2>Moderators</h2>
+<table>
+ <tr>
+ <th>ID</th>
+ <th>Username</th>
+ <th>Display Name</th>
+ <th>Email</th>
+ <th>Is Enabled</th>
+ <th>Is Admin</th>
+ <th>User Hashes</th>
+ </tr>
+{% for u in mods %}
+ <tr>
+ <td> {{ u.moderator_id }} </td>
+ <td><a href="/admin/moderator/{{u.username}}/admin">{{ u.username }}</a></td>
+ <td> {{ u.display_name }} </td>
+ <td> {{ u.email }} </td>
+ <td> {{ u.is_enabled }} </td>
+ <td> {{ u.is_admin }} </td>
+ <td> {{ u.user_hash }} </td>
+ </tr>
+{% endfor %}
+</table>
+
+<h2>Create new</h2>
+<form method="post" action="/admin/moderator/add" class="form">
+ <div class="form-control">
+ <label for="username_new">Username:</label>
+ <input type="text" class="form-control" name="username_new" required>
+ </div>
+ <div class="form-control">
+ <label for="password_new">Password:</label>
+ <input type="password" class="form-control" name="password_new" required>
+ </div>
+ <div class="form-control">
+ <label for="name">Display Name:</label>
+ <input type="text" class="form-control" name="display_name" required>
+ </div>
+ <div class="form-control">
+ <label for="email">Contact Email:</label>
+ <input type="text" class="form-control" name="email" required>
+ </div>
+ <input type="submit" value="Add">
+</form>
+
+
+{% endblock %}
diff --git a/app/views.py b/app/views.py
index 003e9f7..436d57d 100644
--- a/app/views.py
+++ b/app/views.py
@@ -17,7 +17,7 @@ from flask_login import login_user, logout_user
from app import app, get_db
from .db import CursorError
-from .models import Review
+from .models import Review, Moderator
def _get_user_key(user_hash, app_id):
salt = os.environ['ODRS_REVIEWS_SECRET']
@@ -123,8 +123,8 @@ def login():
password = request.form['password']
try:
db = get_db()
- user = db.users.get_with_login(request.form['username'],
- request.form['password'])
+ user = db.moderators.get_by_username_password(request.form['username'],
+ request.form['password'])
except CursorError as e:
flash(str(e))
return render_template('error.html'), 503
@@ -175,7 +175,7 @@ def json_error(msg=None, errcode=400):
mimetype="application/json")
@app.errorhandler(401)
-def error_permission_denied(msg=None):
+def _error_permission_denied(msg=None):
""" Error handler: Permission Denied """
return json_error(msg, 401)
diff --git a/app/views_admin.py b/app/views_admin.py
index 6f1cfd6..f9c6032 100644
--- a/app/views_admin.py
+++ b/app/views_admin.py
@@ -11,7 +11,7 @@ import calendar
from math import ceil
from flask import abort, request, flash, render_template, redirect, url_for
-from flask_login import login_required
+from flask_login import login_required, current_user
from app import app, get_db
from .db import CursorError
@@ -36,6 +36,29 @@ def _get_chart_labels_days():
labels.append("%02i-%02i-%02i" % (then.year, then.month, then.day))
return labels
+def _password_check(value):
+ """ Check the password for suitability """
+ success = True
+ if len(value) < 8:
+ success = False
+ flash('The password is too short, the minimum is 8 characters', 'warning')
+ if len(value) > 40:
+ success = False
+ flash('The password is too long, the maximum is 40 characters', 'warning')
+ if value.lower() == value:
+ success = False
+ flash('The password requires at least one uppercase character', 'warning')
+ if value.isalnum():
+ success = False
+ flash('The password requires at least one non-alphanumeric character', 'warning')
+ return success
+
+def _email_check(value):
+ """ Do a quick and dirty check on the email address """
+ if len(value) < 5 or value.find('@') == -1 or value.find('.') == -1:
+ return False
+ return True
+
class Pagination(object):
def __init__(self, page, per_page, total_count):
@@ -69,13 +92,13 @@ class Pagination(object):
last = num
@app.errorhandler(400)
-def error_internal(msg=None, errcode=400):
+def _error_internal(msg=None, errcode=400):
""" Error handler: Internal """
flash("Internal error: %s" % msg)
return render_template('error.html'), errcode
@app.errorhandler(401)
-def error_permission_denied(msg=None):
+def _error_permission_denied(msg=None):
""" Error handler: Permission Denied """
flash("Permission denied: %s" % msg)
return render_template('error.html'), 401
@@ -115,11 +138,14 @@ def show_stats():
"""
Return the statistics page as HTML.
"""
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to show stats as non-admin')
try:
db = get_db()
stats = db.get_stats()
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
# stats
results_stats = []
@@ -146,11 +172,14 @@ def distros():
"""
Return the statistics page as HTML.
"""
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to show distros as non-admin')
try:
db = get_db()
stats = db.get_stats_distro(8)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
labels = []
data = []
for s in stats:
@@ -202,9 +231,9 @@ def admin_show_review(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
return render_template('show.html', r=review)
@app.route('/admin/modify/<review_id>', methods=['POST'])
@@ -215,9 +244,9 @@ def admin_modify(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
review.distro = request.form['distro']
review.locale = request.form['locale']
if len(request.form['user_display']) == 0:
@@ -235,11 +264,14 @@ def admin_modify(review_id):
@login_required
def admin_user_ban(user_hash):
""" Change details about a review """
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to ban user as non-admin')
try:
db = get_db()
db.users.ban(user_hash)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return redirect(url_for('.admin_show_reported'))
@app.route('/admin/unreport/<review_id>')
@@ -250,9 +282,9 @@ def admin_unreport(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
review.reported = 0
db.reviews.modify(review)
return redirect(url_for('.admin_show_review', review_id=review_id))
@@ -265,9 +297,9 @@ def admin_unremove(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
review.date_deleted = 0
db.reviews.modify(review)
return redirect(url_for('.admin_show_review', review_id=review_id))
@@ -280,9 +312,9 @@ def admin_englishify(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
parts = review.locale.split('_')
if len(parts) == 1:
review.locale = 'en'
@@ -299,9 +331,9 @@ def admin_anonify(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
review.user_display = None
db.reviews.modify(review)
return redirect(url_for('.admin_show_review', review_id=review_id))
@@ -314,9 +346,9 @@ def admin_delete_force(review_id):
db = get_db()
review = db.reviews.get_for_id(review_id)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
if not review:
- return error_internal('no review with that ID')
+ return _error_internal('no review with that ID')
db.reviews.delete(review)
return redirect(url_for('.admin_show_all'))
@@ -332,13 +364,26 @@ def admin_show_all(page):
"""
Return all the reviews on the server as HTML.
"""
+
try:
db = get_db()
- reviews = db.reviews.get_all()
+ reviews_all = db.reviews.get_all()
except CursorError as e:
- return error_internal(str(e))
- if not reviews and page != 1:
+ return _error_internal(str(e))
+ if not reviews_all and page != 1:
abort(404)
+
+ # filter by the languages the moderator understands
+ reviews = []
+ if not current_user or current_user.locales == None:
+ reviews.extend(reviews_all)
+ else:
+ langs = current_user.locales.split(',')
+ for r in reviews_all:
+ lang = r.locale.split('_')[0]
+ if lang in langs:
+ reviews.append(r)
+
reviews_per_page = 20
pagination = Pagination(page, reviews_per_page, len(reviews))
# FIXME: do this database side...
@@ -348,6 +393,7 @@ def admin_show_all(page):
reviews=reviews)
@app.route('/admin/show/reported')
+@login_required
def admin_show_reported():
"""
Return all the reported reviews on the server as HTML.
@@ -360,7 +406,7 @@ def admin_show_reported():
if review.reported > 0:
reviews_filtered.append(review)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
@app.route('/admin/show/user/<user_hash>')
@@ -376,7 +422,7 @@ def admin_show_user(user_hash):
if review.user_hash == user_hash:
reviews_filtered.append(review)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
@app.route('/admin/show/app/<app_id>')
@@ -392,7 +438,7 @@ def admin_show_app(app_id):
if review.app_id == app_id:
reviews_filtered.append(review)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
@app.route('/admin/show/lang/<locale>')
@@ -408,7 +454,7 @@ def admin_show_lang(locale):
if review.locale == locale:
reviews_filtered.append(review)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return render_template('show-all.html', reviews=reviews_filtered)
@app.route('/admin/users/all')
@@ -422,5 +468,151 @@ def admin_users_all():
users_awesome = db.users.get_by_karma(best=True)
users_haters = db.users.get_by_karma(best=False)
except CursorError as e:
- return error_internal(str(e))
+ return _error_internal(str(e))
return render_template('users.html', users_awesome=users_awesome, users_haters=users_haters)
+
+@app.route('/admin/moderators/all')
+@login_required
+def admin_moderator_show_all():
+ """
+ Return all the moderators as HTML.
+ """
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to show all moderators')
+ try:
+ db = get_db()
+ mods = db.moderators.get_all()
+ except CursorError as e:
+ return _error_internal(str(e))
+ return render_template('mods.html', mods=mods)
+
+@app.route('/admin/moderator/add', methods=['GET', 'POST'])
+@login_required
+def admin_moderator_add():
+ """ Add a moderator [ADMIN ONLY] """
+
+ # only accept form data
+ if request.method != 'POST':
+ return redirect(url_for('.profile'))
+
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to add moderator as non-admin')
+
+ if not 'password_new' in request.form:
+ return _error_permission_denied('Unable to add user as no password_new')
+ if not 'username_new' in request.form:
+ return _error_permission_denied('Unable to add user as no username_new')
+ if not 'display_name' in request.form:
+ return _error_permission_denied('Unable to add user as no display name')
+ if not 'email' in request.form:
+ return _error_permission_denied('Unable to add user as no email')
+ try:
+ db = get_db()
+ auth = db.moderators.get_by_username(request.form['username_new'])
+ except CursorError as e:
+ return _error_internal(str(e))
+ if auth:
+ return _error_internal('Already a entry with that username', 422)
+
+ # verify password
+ password = request.form['password_new']
+ if not _password_check(password):
+ return redirect(url_for('.admin_moderator_show_all'), 422)
+
+ # verify email
+ email = request.form['email']
+ if not _email_check(email):
+ flash('Invalid email address', 'warning')
+ return redirect(url_for('.admin_moderator_show_all'), 422)
+
+ # verify name
+ display_name = request.form['display_name']
+ if len(display_name) < 3:
+ flash('Name invalid', 'warning')
+ return redirect(url_for('.admin_moderator_show_all'), 422)
+
+ # verify username
+ username_new = request.form['username_new']
+ if len(username_new) < 3:
+ flash('Username invalid', 'warning')
+ return redirect(url_for('.admin_moderator_show_all'), 422)
+ try:
+ db.moderators.add(username_new, password, display_name, email)
+ except CursorError as e:
+ return _error_internal(str(e))
+ flash('Added user')
+ return redirect(url_for('.admin_moderator_show_all'), 302)
+
+@app.route('/admin/moderator/<username>/admin')
+@login_required
+def admin_moderator_show(username):
+ """
+ Shows an admin panel for a moderator
+ """
+ if username != current_user.username and current_user.username != 'root':
+ return _error_permission_denied('Unable to show moderator information')
+ try:
+ db = get_db()
+ mod = db.moderators.get_by_username(username)
+ except CursorError as e:
+ return _error_internal(str(e))
+ return render_template('modadmin.html', u=mod)
+
+@app.route('/admin/moderator/<username>/delete')
+@login_required
+def admin_moderate_delete(username):
+ """ Delete a moderator """
+
+ # security check
+ if current_user.username != 'admin':
+ return _error_permission_denied('Unable to delete moderator as not admin')
+
+ # check whether exists in database
+ try:
+ db = get_db()
+ mod = db.moderators.get_by_username(username)
+ except CursorError as e:
+ return _error_internal(str(e))
+ if not mod:
+ flash("No entry with username %s" % username, 'error')
+ return redirect(url_for('.admin_moderator_show_all'), 422)
+ try:
+ db.moderators.remove(username)
+ except CursorError as e:
+ return _error_internal(str(e))
+ flash('Deleted user')
+ return redirect(url_for('.admin_moderator_show_all'), 302)
+
+@app.route('/admin/moderator/<username>/modify_by_admin', methods=['POST'])
+@login_required
+def user_modify_by_admin(username):
+ """ Change details about the any user """
+
+ # security check
+ if username != current_user.username and current_user.username != 'root':
+ return _error_permission_denied('Unable to modify user as non-admin')
+
+ # set each thing in turn
+ for key in ['display_name',
+ 'email',
+ 'password',
+ 'user_hash',
+ 'locales',
+ 'is_enabled']:
+ # unchecked checkbuttons are not included in the form data
+ if key in request.form:
+ tmp = request.form[key]
+ else:
+ tmp = '0'
+ try:
+ # don't set the optional password
+ if key == 'password' and len(tmp) == 0:
+ continue
+ db = get_db()
+ db.moderators.set_property(username, key, tmp)
+ except CursorError as e:
+ return _error_internal(str(e))
+ flash('Updated profile')
+ return redirect(url_for('.admin_moderator_show', username=username))
diff --git a/schema.sql b/schema.sql
index 5e7e6cc..80191bc 100644
--- a/schema.sql
+++ b/schema.sql
@@ -1,3 +1,6 @@
+ALTER TABLE analytics MODIFY COLUMN app_id VARCHAR(128) DEFAULT NULL;
+
+
DROP TABLE IF EXISTS reviews;
CREATE TABLE reviews (
review_id INT NOT NULL AUTO_INCREMENT,
@@ -34,7 +37,6 @@ CREATE TABLE users (
user_hash TEXT DEFAULT NULL,
karma INT DEFAULT 0,
is_banned INT DEFAULT 0,
- password TEXT DEFAULT NULL,
UNIQUE KEY id (user_id)
) CHARSET=utf8;
DROP TABLE IF EXISTS eventlog;
@@ -56,3 +58,17 @@ CREATE TABLE analytics (
UNIQUE (datestr,app_id)
) CHARSET=utf8;
CREATE INDEX date_created_idx ON eventlog (date_created);
+CREATE TABLE moderators (
+ moderator_id INT NOT NULL AUTO_INCREMENT,
+ username TEXT DEFAULT NULL,
+ password TEXT DEFAULT NULL,
+ display_name TEXT DEFAULT NULL,
+ email TEXT DEFAULT NULL,
+ is_enabled TINYINT DEFAULT 0,
+ is_admin TINYINT DEFAULT 0,
+ user_hash TEXT DEFAULT NULL,
+ locales TEXT DEFAULT NULL,
+ UNIQUE KEY id (moderator_id)
+) CHARSET=utf8;
+INSERT INTO moderators (username, password, display_name, email, is_enabled, is_admin)
+ VALUES ('admin', 'cc492636d799c5509bbd48b27b02986dcf25dfc8', 'Richard Hughes', 'richard hughsie com', 1,
1);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]