diff --git a/app/static/main.css b/app/static/css/main.css similarity index 100% rename from app/static/main.css rename to app/static/css/main.css diff --git a/app/static/signin.css b/app/static/css/signin.css similarity index 100% rename from app/static/signin.css rename to app/static/css/signin.css diff --git a/app/static/js/search.js b/app/static/js/search.js new file mode 100644 index 0000000..d7f5d70 --- /dev/null +++ b/app/static/js/search.js @@ -0,0 +1,41 @@ +// Adapted from http://jsfiddle.net/ukW2C/3/ +$("#search").keyup(function () { + //split the current value of search + var data = this.value.split(" "); + + //create a jquery object of the rows + var jo = $("#users").find("tr"); + if (this.value == "") { + jo.show(); + $("#search-group").removeClass("has-error"); + $("#search-group").removeClass("has-success"); + return; + } + //hide all the rows + jo.hide(); + + //Recusively filter the jquery object to get results. + filtered = jo.filter(function (i, v) { + var $t = $(this); + for (var d = 0; d < data.length; ++d) { + if ($t.is(":contains('" + data[d] + "')")) { + return true; + } + } + return false; + }); + //show the rows that match. + filtered.show(); + + //update the search bar validation status + if (filtered.length) { + $("#search-group").addClass("has-success"); + $("#search-group").removeClass("has-error"); + } else { + $("#search-group").addClass("has-error"); + $("#search-group").removeClass("has-success"); + } +}).focus(function () { + this.value = ""; + $(this).unbind('focus'); +}); diff --git a/app/templates/base.html b/app/templates/base.html index fff8a7a..2b682c9 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -4,7 +4,7 @@ {% block styles %} {{super()}} + href="{{url_for('static', filename='css/main.css')}}"> {% endblock %} {% block navbar %} @@ -29,6 +29,7 @@ diff --git a/app/templates/confirm.html b/app/templates/confirm.html index c2c57b4..38b7b77 100644 --- a/app/templates/confirm.html +++ b/app/templates/confirm.html @@ -12,7 +12,7 @@

{% if data.prompt %}{{ data.prompt }}{% el {{ wtf.form_errors(form) }} {% if data.back %}
- + Back {{ form.submit(class='btn btn-lg btn-primary btn-danger', value=data.action) }} diff --git a/app/user/admin.py b/app/user/admin.py index 3b8db1e..d564831 100644 --- a/app/user/admin.py +++ b/app/user/admin.py @@ -6,7 +6,8 @@ from wtforms.fields.html5 import EmailField from app.views import confirm, is_safe_url from . import groups_required, user_blueprint -from .models import User +from .models import User, Group +from ldap3.core.exceptions import LDAPAttributeOrValueExistsResult class UserEditForm(FlaskForm): username = StringField('Username') @@ -26,10 +27,21 @@ class UserEditForm(FlaskForm): @groups_required('admin') def list_users(): users = User.query() - return render_template('admin/listUsers.html', + return render_template('admin/user/list.html', users = users ) +@user_blueprint.route('/admin/user/') +@login_required +@groups_required('admin') +def profile(username): + user = User.get(username) + if not user: + flash('Invalid user name!', 'error') + return redirect(url_for('user.list_users')) + all_groups = Group.query() + return render_template('admin/group/profile.html', user=user, groups=all_groups) + @user_blueprint.route('/admin/user//delete', methods=['GET', 'POST']) @login_required @confirm(title='Delete User?', @@ -85,7 +97,53 @@ def edit_user(username, back_url = None): form.surname.data = user.surname form.mail.data = user.mail - return render_template('admin/editUser.html', + return render_template('admin/user/edit.html', form = form, user = user ) + +@user_blueprint.route('/admin/user//join/') +@login_required +@groups_required('admin') +def join(username, group_name): + try: + group = Group.get(group_name) + if not group: + raise AttributeError("group does not exist") + group.join(User.get(username)) + group.save() + except LDAPAttributeOrValueExistsResult: + flash("{} is already a member of {}".format(username, group_name)) + except AttributeError: + abort(404) + return redirect(url_for('user.profile', username=username)) + +@user_blueprint.route('/admin/user//leave/', methods=['GET', 'POST']) +@login_required +@groups_required('admin') +@confirm(title='Leave group?', + prompt='Are you sure you want to remove this user from the group?', + action='Remove') +def leave(username, group_name): + group = Group.get(group_name) + if not group: + raise AttributeError("group does not exist") + group.leave(User.get(username)) + group.save() + return redirect(url_for('user.profile', username=username)) + +@user_blueprint.route('/admin/group/') +@login_required +@groups_required('admin') +def group_page(group_name): + group = Group.get(group_name) + if not group: + flash('Invalid group name!', 'error') + return redirect(url_for('user.list_groups')) + return render_template('admin/group/profile.html', group=group) + +@user_blueprint.route('/admin/groups') +@login_required +@groups_required('admin') +def list_groups(): + return render_template('admin/group/list.html', groups=Group.query()) diff --git a/app/user/models.py b/app/user/models.py index d883646..d1e2ba7 100644 --- a/app/user/models.py +++ b/app/user/models.py @@ -46,7 +46,7 @@ def create(username, givenName, surname, password, mail = None): def delete(self): """ - Remove a user. + Delete a group. """ for group in self.groups: group.leave(self) diff --git a/app/user/templates/admin/group/list.html b/app/user/templates/admin/group/list.html new file mode 100644 index 0000000..98a0ed7 --- /dev/null +++ b/app/user/templates/admin/group/list.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} + +{% block title %}Groups{% endblock %} + +{% block scripts %} +{{ super() }} + +{% endblock %} + +{% block content %} +
+

Groups

+ +
+ +
+ + +
+
+ + + + + + + + {% for group in groups %} + + + + + {% endfor %} + +
NameDescription
{{ group.group_name }}{{ group.description }}
+
+{% endblock %} diff --git a/app/user/templates/admin/group/profile.html b/app/user/templates/admin/group/profile.html new file mode 100644 index 0000000..c9963d3 --- /dev/null +++ b/app/user/templates/admin/group/profile.html @@ -0,0 +1,41 @@ +{% extends "base.html" %} + +{% block title %}{{ group.group_name }}{% endblock %} + +{% block content %} +
+ + +
+

General information

+
+
+ +

{{ group.dn }}

+
+
+
+ +
+
+

Members

+
+ +
+
+{% endblock %} diff --git a/app/user/templates/admin/listUsers.html b/app/user/templates/admin/listUsers.html deleted file mode 100644 index 2567855..0000000 --- a/app/user/templates/admin/listUsers.html +++ /dev/null @@ -1,92 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Users{% endblock %} - -{% block scripts %} -{{ super() }} - -{% endblock %} - -{% block content %} -
-

Users

- -
- -
- - -
-
- - - - - - - - - - {% for user in users %} - - - - - - - {% endfor %} - -
UsernameFull nameE-MailActions
{{ user.username }}{{ user.full_name }}{{ user.mail }} - -
-
-{% endblock %} diff --git a/app/user/templates/admin/editUser.html b/app/user/templates/admin/user/edit.html similarity index 100% rename from app/user/templates/admin/editUser.html rename to app/user/templates/admin/user/edit.html diff --git a/app/user/templates/admin/user/list.html b/app/user/templates/admin/user/list.html new file mode 100644 index 0000000..2a00d47 --- /dev/null +++ b/app/user/templates/admin/user/list.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} + +{% block title %}Users{% endblock %} + +{% block scripts %} +{{ super() }} + +{% endblock %} + +{% block content %} +
+

Users

+ +
+ +
+ + +
+
+ + + + + + + + + + {% for user in users %} + + + + + + + {% endfor %} + +
UsernameFull nameE-MailActions
{{ user.username }}{{ user.full_name }}{{ user.mail }} + +
+
+{% endblock %} diff --git a/app/user/templates/admin/user/profile.html b/app/user/templates/admin/user/profile.html new file mode 100644 index 0000000..979eb5f --- /dev/null +++ b/app/user/templates/admin/user/profile.html @@ -0,0 +1,65 @@ +{% extends "base.html" %} + +{% block title %}{{ user.name }}{% endblock %} + +{% block content %} +
+ + +
+

General information

+
+
+ +

{{ user.firstName }}

+
+
+ +

{{ user.surname }}

+
+
+ + +
+
+ +

{{ user.dn }}

+
+
+
+ +
+
+

+ Groups +
+ + +
+

+
+ +
+
+{% endblock %} diff --git a/app/user/templates/login.html b/app/user/templates/login.html index bf5f24f..be5de7d 100644 --- a/app/user/templates/login.html +++ b/app/user/templates/login.html @@ -4,7 +4,7 @@ {% block styles %} {{super()}} + href="{{url_for('static', filename='css/signin.css')}}"> {% endblock %} {% block title %}login{% endblock %} diff --git a/app/user/templates/signup.html b/app/user/templates/signup.html index 7c4bb19..2db60fa 100644 --- a/app/user/templates/signup.html +++ b/app/user/templates/signup.html @@ -4,7 +4,7 @@ {% block styles %} {{super()}} + href="{{url_for('static', filename='css/signin.css')}}"> {% endblock %} {% block title %}Sign Up{% endblock %} diff --git a/app/views.py b/app/views.py index 87a08b1..b88b04b 100644 --- a/app/views.py +++ b/app/views.py @@ -48,7 +48,7 @@ def f(*args, **kwargs): data={ 'title': title, 'action': action, - 'back': back, + 'back': url_for(back) if back else get_redirect_target(), 'prompt': prompt, 'text': text })