Commit ad2cb155 authored by YSelf Tool's avatar YSelf Tool

Implemented user control

parent 6edacde7
form flask.ext.login import UserMixin
from flask.ext.login import UserMixin
from shared import db
......
from flask.ext.wtf import form
from flask.ext.wtf import Form
from wtforms import StringField, PasswordField, BooleanField, SelectMultipleField, SelectField, DateField, IntegerField, TextAreaField
from wtforms.validators import InputRequired, Length, EqualTo, Email, Optional, Length, NumberRange, AnyOf
from models.database import User
......@@ -9,6 +9,7 @@ import shared
class LoginForm(Form):
username = StringField("Username", validators=[InputRequired("Entering your username is required.")])
password = PasswordField("Password", validators=[InputRequired("Entering your password is required.")])
remember_me = BooleanField("Remember me?")
class NewUserForm(Form):
fullname = StringField("Full name", validators=[InputRequired("Entering your name is required.")])
......
from flask import Blueprint, render_template, redirect, url_for, request, flash, abort, send_file, Response
from flask.ext.login import login_required
from passlib.hash import pbkdf2_sha256
from models.database import User
from models.forms import AdminUserForm, NewUserForm
from shared import db, admin_permission
admin = Blueprint("admin", __name__)
@admin.route("/")
@login_required
@admin_permission.require()
def index():
users = User.query.limit(10).all()
return render_template("admin_index.html", users=users)
@admin.route("/user/")
@login_required
@admin_permission.require()
def user():
users = User.query.all()
return render_template("admin_user_index.html", users=users)
@admin.route("/user/edit", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def user_edit():
user_id = request.args.get("id", None)
if user_id is not None:
user = db.session.query(User).filter_by(id=user_id).first()
form = AdminUserForm(obj=user)
if form.validate_on_submit():
form.populate_obj(user)
db.session.commit()
return redirect(url_for(".index"))
else:
return render_template("admin_user_edit.html", form=form, id=user_id)
else:
return redirect(url_for(".index"))
@admin.route("/user/delete")
@login_required
@admin_permission.require()
def user_delete():
user_id = request.args.get("id", None)
if user_id is not None:
user = User.query.filter_by(id=user_id).first()
db.session.delete(user)
db.session.commit()
flash("User deleted.", "alert-success")
return redirect(url_for(".user"))
@admin.route("/user/new", methods=["GET", "POST"])
@login_required
@admin_permission.require()
def user_new():
form = NewUserForm()
if form.validate_on_submit():
password_hash = pbkdf2_sha256.encrypt(form.password.data, rounds=200000, salt_size=16)
user = User(form.fullname.data, form.username.data, password_hash)
db.session.add(user)
db.session.commit()
return redirect(url_for(".user"))
return render_template("admin_user_new.html", form=form)
#!/usr/bin/env python3
from flask import Flask, g, current_up, request, render_template, session, flash, redirect, url_for, abort
from flask import Flask, g, current_app, request, render_template, session, flash, redirect, url_for, abort
from flask.ext.login import login_user, logout_user, login_required, current_user
from flask.ext.principal import Principal, Identity, AnonymousIdentity, identity_changed, identity_loaded, UserNeed, RoleNeed
from passlib.hash import pbkdf2_sha256
......@@ -35,6 +35,46 @@ def index():
db.session.commit()
return render_template("index.html")
@app.route("/login", methods=["GET", "POST"])
def login():
form = LoginForm()
if form.validate_on_submit():
user = db.session.query(User).filter_by(username=form.username.data).first()
if (user is not None) and (pbkdf2_sha256.verify(form.password.data, user.password)):
login_user(user, remember=form.remember_me.data)
identity_changed.send(current_app._get_current_object(), identity=Identity(user.id))
flash("Welcome back, {}!".format(user.fullname), "alert-success")
return redirect(request.args.get("next") or url_for(".index"))
else:
flash("Invalid username or wrong password", "alert-error")
return render_template("login.html", form=form)
@app.route("/logout", methods=["GET", "POST"])
@login_required
def logout():
logout_user()
for key in ("identity.name", "identiy.auth_type"):
session.pop(key, None)
identity_changed.send(current_app._get_current_object(), identity=AnonymousIdentity())
flash("You have been logged out.", "alert-success")
return redirect(url_for(".index"))
@app.route("/register", methods=["GET", "POST"])
def register():
form = NewUserForm()
if form.validate_on_submit():
length = len(db.session.query(User).filter_by(username=form.username.data).all())
if length > 0:
flash("There already is a user with that name.")
return render_template("register.html", form=form)
password = pbkdf2_sha256.encrypt(form.password.data, rounds=200000, salt_size=16)
user = User(fullname, username, password, [])
db.session.add(user)
db.session.commit()
flash("Your account has been created, you may now log in with it.")
return redirect(url_for(".login"))
return render_template("register.html", form=form)
@identity_loaded.connect_via(app)
def on_identity_loaded(sender, identity):
......
......@@ -14,3 +14,86 @@
.flash-card.mdl-card.alert-error {
background-color: red;
}
.rede-avatar {
width: 48px;
height: 48px;
border-radius: 24px;
}
.rede-layout .rede-header .mdl-textfield {
padding-top: 27px;
}
.rede-layout .mdl-layout__header .mdl-layout__drawer-button {
color: rgba(0, 0, 0, 0.54);
}
.mdl-layout__drawer .avatar {
margin-bottom: 16px;
}
.rede-drawer {
border: none;
}
.rede-drawer .mdl-menu__container {
z-index: -1;
}
.rede-drawer .rede-navigation {
z-index: -2;
}
.rede-drawer .mdl-menu .mdl-menu__item {
display: flex
align-items: center;
}
.rede-drawer-header {
box-sizing: border-box;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 16px;
height: 151px;
}
.rede-avatar-dropdown {
display: flex;
position: relative;
flex-direction: row;
align-items: center;
width: 100%;
}
.rede-navigation {
flex-grow: 1;
}
.rede-layout .rede-navigation .mdl-navigation__link {
display: flex !important;
flex-direction: row;
align-items: center;
color: rgba(255, 255, 255, 0.56);
font-weight: 500;
}
.rede-layout .rede-navigation .mdl-navigation__link:hover {
background-color: #00BCD4;
color: #37474F;
}
.rede-navigation .mdl-navigation__link .material-icons {
font-size: 24px;
color: rgba(255, 255, 255, 0.56);
margin-right: 32px;
}
.rede-content {
max-width: 1080px;
}
.rede-separator {
height: 32px;
}
This diff is collapsed.
{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<table class="mdl-data-table mdl-js-table mdl-shadow--2dp sortable mdl-cell mdl-cell--3-col">
<thead>
<tr>
<th class="mdl-data-table_-cell--non-numeric">Full Name</th>
<th class="mdl-data-table_-cell--non-numeric">Username</th>
<th class="mdl-data-table_-cell--non-numeric">Roles</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td class="mdl-data-table__cell--non-numeric"><a href="{{ url_for(".user_edit", id=user.id) }}">{{ user.fullname }}</a></td>
<td class="mdl-data-table__cell--non-numeric">{{ user.username }}</td>
<td class="mdl-data-table__cell--non-numeric">{% if user.roles is not none %}{{ ", ".join(user.roles) }}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="mdl-card__actions">
<a class="mdl-button mdl-button--colored mdl-js-button" href="{{ url_for(".user") }}">All users</a>
</div>
{% endblock %}
{% extends "layout.html" %}
{% from "macros.html" import render_form %}
{% block title %}Edit User - Administration{% endblock %}
{% block content %}
{{ render_form(form, action_url=url_for(".user_edit", id=id), action_text="Apply", title="Edit User") }}
{% endblock %}
{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<div class="mdl-cell mdl-cell--3-col mdl-grid mdl-grid--no-spacing">
<table class="mdl-data-table mdl-js-table mdl-shadow--2dp mdl-cell mdl-cell--3-col">
<thead>
<tr>
<th class="mdl-data-table_-cell--non-numeric">Full Name</th>
<th class="mdl-data-table_-cell--non-numeric">Username</th>
<th class="mdl-data-table_-cell--non-numeric">Roles</th>
<th class="mdl-data-table_-cell--non-numeric">Delete</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td class="mdl-data-table__cell--non-numeric"><a href="{{ url_for(".user_edit", id=user.id) }}">{{ user.fullname }}</a></td>
<td class="mdl-data-table__cell--non-numeric">{{ user.username }}</td>
<td class="mdl-data-table__cell--non-numeric">{% if user.roles is not none %}{{ ", ".join(user.roles) }}{% endif %}</td>
<td class="mdl-data-table__cell--non-numeric">
<a href="{{ url_for('.user_delete', id=user.id) }}">
<i class="material-icons">delete</i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="rede-separator">
<div class="mdl-cell mdl-cell--1-col">
<a class="mdl-button mdl-button--colored mdl-js-button mdl-button--fab mdl-js-ripple-effect" href="{{ url_for('.user_new') }}">
<i class="material-icons">add</i>
</a>
</div>
</div>
</div>
{% endblock %}
{% extends "layout.html" %}
{% from "macros.html" import render_form %}
{% block title %}Add User - Administration{% endblock %}
{% block content %}
{{ render_form(form, action_url=url_for(".user_new", id=id), action_text="Add", title="Create User") }}
{% endblock %}
{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block content %}
<div class="rede-nocontent mdl-color--white mdl-shadow--2dp mdl-cell mdl-cell-12-col mdl-grid">
No content here
</div>
{% endblock %}
......@@ -5,23 +5,26 @@
<meta charset="utf-8" />
<link rel="stylesheet" href="https://storage.googleapis.com/code.getmdl.io/1.0.4/material.red-blue.min.css" />
<script src="https://storage.googleapis.com/code.getmdl.io/1.0.0/material.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Metrial+Icons">
<script src="{{ url_for('static', filename='js/sorttable.js') }}"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<meta name="description" content="moderation tool for handling speaking order">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" />
<title>{% block title %}Unknown Page{% endblock %} - Redeleitsystem</title>
{% endblock %}
</head>
<body>
<div class="rede-layout mdl-layout mdl-js-layout mdl-layout--fixed-draw mdl-layout--fixed-header">
<header class="rede-header hdml-layout__header mdl-color--white mdl-color--grey-100 mdl-color-text--grey-600">
<div class="rede-layout mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
<header class="rede-header mdl-layout__header mdl-color--white mdl-color--grey-100 mdl-color-text--grey-600">
<div class="mdl-layout__header-row">
<span class="mdl-layout-title">Redeleitsystem</span>
<a href="{{ url_for('index') }}"><span class="mdl-layout-title">Redeleitsystem</span></a>
<div class="mdl-layout-spacer"></div>
<button class="mdl-button mdl-js-button mdl-js-ripple-effet mdl-button--icon" id="hdrbtn">
<i class="material-icons">more_vert</i>
</button>
<ul class="mdl-menu mdl-js-menu mdl-js-ripple-effect mdl-menu--bottom-right" for="hdrbtn">
{% block toplinks %}
{% block topnav %}
<li class="mdl-menu__item">Impressum</li>
{% endblock %}
</ul>
......@@ -42,16 +45,21 @@
<i class="material-icons" role="presentation">arrow_drop_down</i>
<span class="visuallyhidden">Account</span>
</button>
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effet" for="accbtn">
<ul class="mdl-menu mdl-menu--bottom-left mdl-js-menu mdl-js-ripple-effet" for="accbtn">
{% if current_user.is_authenticated() %}
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for(".logout") }}">Logout</a></li>
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for("logout") }}">Logout</a></li>
{% else %}
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for(".login") }}">Login</a></li>
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for(".register") }}">Register</a></li>
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for("login") }}">Login</a></li>
<li class="mdl-menu__item"><a class="mdl-navigation__link" href="{{ url_for("register") }}">Register</a></li>
{% endif %}
</ul>
</div>
</header>
<nav class="rede-navigation mdl-navigation mdl-color--blue-grey-800">
{% if current_user.is_authenticated() and "admin" in current_user.roles %}
<a class="mdl-navigation__link" href="{{ url_for("admin.index") }}"><i class="mdl-color-text--blue-grey-400 material-icons" role="presentation">computer</i>Administration</a>
{% endif %}
</nav>
</div>
<main class="mdl-layout__content mdl-color--grey-100">
<div class="mdl-grid rede-content">
......@@ -60,5 +68,6 @@
{% endblock %}
</div>
</main>
<script src="http://www.getmdl.io/material.min.js"></script>
</body>
</html>
{% extends "layout.html" %}
{% from "macros.html" import render_form %}
{% block title %}Index{% endblock %}
{% block content %}
{{ render_form(form, action_url=url_for(".login"), action_text="Login", title="Login") }}
{% endblock %}
......@@ -9,8 +9,8 @@
{%- endmacro %}
{% macro render_stringfield(field) -%}
<div class="mdl-textfield mdl-js-textfield">
<input id="{{ field.id }}" name="{{ field.id }}" class="mdl-textfield__input" type="text" />
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input id="{{ field.id }}" name="{{ field.id }}" class="mdl-textfield__input" type="text" value="{{ field.data }}" />
<label class="mdl-textfield__label" for="{{ field.id }}">{{ field.label.text }}</label>
{% if field.errors %}
{% for e in errors %}
......@@ -23,7 +23,7 @@
{%- endmacro %}
{% macro render_passwordfield(field) -%}
<div class="mdl-textfield mdl-js-textfield">
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input id="{{ field.id }}" name="{{ field.id }}" class="mdl-textfield__input" type="password" />
<label class="mdl-textfield__label" for="{{ field.id }}">{{ field.label.text }}</label>
{% if field.errors %}
......@@ -41,12 +41,38 @@
<input type="checkbox" id="{{ field.id }}" name="{{ field.id }}" class="mdl-checkbox__input" />
<span class="mdl-checkbox__label">{{ field.label.text }}</span>
</label>
{% if field.errors %}
{% for e in errors %}
<div class="mdl-card__supporting-text">
{{ e }}
</div>
{% endfor %}
{% endif %}
{%- endmacro %}
{% macro render_csrftokenfield(field, kwargs) -%}
{{ field(title=field.description, **kwargs) }}
{%- endmacro %}
{% macro render_selectmultiplefield(field) -%}
<button id="{{ field.id }}-button" class="mdl-button mdl-js-button" type="button">
{{ field.label.text }}
</button>
<select id="{{ field.id }}" name="{{ field.id }}" multiple class="mdl-menu mdl-js-menu mdl-menu--top-left" for="{{ field.id }}-button">
{% for name, text in field.choices %}
<option value="{{ name }}" {% if field.data is not none and name in field.data %}selected{% endif %} class="mdl-menu__item" >{{ text }}</option>
{% endfor %}
</select>
{% if field.errors %}
{% for e in field.errors %}
<p class="help-block">{{ e }}</p>
{% endfor %}
{% endif %}
</label>
{%- endmacro %}
{% macro render_form(form, action_url="", title=None, action_text="Submit", class_="mdl-card mdl-shadow--2dp", title_class="mdl-card__title", title_next_class="mdl-card__title-text", content_class="mdl-card__supporting-text", action_class="mdl-card__actions", btn_class="mdl-button mdl-js-button mdl-button--raised mdl-button-colored") -%}
<div class="{{ class_ }}">
<form method="POST" action="{{ action_url }}">
......@@ -63,12 +89,13 @@
{{ render_booleanfield(f) }}
{% elif f.type == "CSRFTokenField" %}
{{ render_csrftokenfield(f, kwargs) }}
{% elif f.type == "SelectMultipleField" %}
{{ render_selectmultiplefield(f) }}
{% else %}
{{ f.type }}
{{ render_field(f) }}
{% endif %}
{% endfor %}
{% endfor %}
</div>
<div class="{{ action_class }}">
<button type="submit" class="{{ btn_class }}">{{ action_text }}</button>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment