Projet

Général

Profil

0004-manager-implement-all-views.patch

Benjamin Dauvergne, 13 août 2014 15:28

Télécharger (49,3 ko)

Voir les différences:

Subject: [PATCH 4/5] manager: implement all views

 authentic2/manager/app_settings.py                 |   18 ++
 authentic2/manager/fields.py                       |   28 +++
 authentic2/manager/forms.py                        |   26 ++
 .../manager/css/icon-personnes-hover.png           |  Bin 0 -> 1588 bytes
 .../authentic2/manager/css/icon-personnes.png      |  Bin 0 -> 1609 bytes
 .../authentic2/manager/css/icon-user-hover.png     |  Bin 0 -> 1187 bytes
 .../static/authentic2/manager/css/icon-user.png    |  Bin 0 -> 489 bytes
 .../manager/static/authentic2/manager/css/info.png |  Bin 0 -> 1659 bytes
 .../static/authentic2/manager/css/style.css        |  167 +++++++++++++
 .../static/authentic2/manager/js/manager.js        |   76 ++++++
 authentic2/manager/tables.py                       |   19 ++
 .../manager/templates/authentic2/manager/base.html |   35 +++
 .../templates/authentic2/manager/delete.html       |   23 ++
 .../manager/templates/authentic2/manager/form.html |   42 ++++
 .../templates/authentic2/manager/homepage.html     |   24 ++
 .../manager/templates/authentic2/manager/role.html |   23 ++
 .../authentic2/manager/role_users_table.html       |   10 +
 .../templates/authentic2/manager/roles.html        |   64 +++++
 .../templates/authentic2/manager/table.html        |   58 +++++
 .../templates/authentic2/manager/users.html        |   26 ++
 authentic2/manager/urls.py                         |   23 ++
 authentic2/manager/utils.py                        |   58 +++++
 authentic2/manager/views.py                        |  256 ++++++++++++++++++++
 authentic2/settings.py                             |    3 +
 authentic2/urls.py                                 |    1 +
 requirements.txt                                   |    2 +
 setup.py                                           |    5 +-
 27 files changed, 986 insertions(+), 1 deletion(-)
 create mode 100644 authentic2/manager/__init__.py
 create mode 100644 authentic2/manager/app_settings.py
 create mode 100644 authentic2/manager/fields.py
 create mode 100644 authentic2/manager/forms.py
 create mode 100644 authentic2/manager/models.py
 create mode 100644 authentic2/manager/static/authentic2/manager/css/icon-personnes-hover.png
 create mode 100644 authentic2/manager/static/authentic2/manager/css/icon-personnes.png
 create mode 100644 authentic2/manager/static/authentic2/manager/css/icon-user-hover.png
 create mode 100644 authentic2/manager/static/authentic2/manager/css/icon-user.png
 create mode 100644 authentic2/manager/static/authentic2/manager/css/info.png
 create mode 100644 authentic2/manager/static/authentic2/manager/css/style.css
 create mode 100644 authentic2/manager/static/authentic2/manager/js/manager.js
 create mode 100644 authentic2/manager/tables.py
 create mode 100644 authentic2/manager/templates/authentic2/manager/base.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/delete.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/form.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/homepage.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/role.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/role_users_table.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/roles.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/table.html
 create mode 100644 authentic2/manager/templates/authentic2/manager/users.html
 create mode 100644 authentic2/manager/urls.py
 create mode 100644 authentic2/manager/utils.py
 create mode 100644 authentic2/manager/views.py
authentic2/manager/app_settings.py
1
import sys
2

  
3
class AppSettings(object):
4
    __PREFIX = 'A2_MANAGER_'
5
    __DEFAULTS = {
6
            'HOMEPAGE_URL': None,
7
            'LOGOUT_URL': None,
8
    }
9

  
10
    def __getattr__(self, name):
11
        from django.conf import settings
12
        if name not in self.__DEFAULTS:
13
            raise AttributeError
14
        return getattr(settings, self.__PREFIX + name, self.__DEFAULTS[name])
15

  
16
app_settings = AppSettings()
17
app_settings.__name__ = __name__
18
sys.modules[__name__] = app_settings
authentic2/manager/fields.py
1
from django_select2 import AutoSelect2Field, AutoModelSelect2MultipleField, NO_ERR_RESP
2

  
3
from django.contrib.auth.models import Group
4

  
5
from . import utils
6

  
7
class ChooseUserField(AutoSelect2Field):
8
    def security_check(self, request, *args, **kwargs):
9
        return True
10

  
11
    def get_results(self, request, term, page, context):
12
        return (NO_ERR_RESP, False, [(u.ref, u.name) for u in utils.search_user(term)])
13

  
14
    def get_val_txt(self, value):
15
        """
16
        The problem of issue #66 was here. I was not overriding this.
17
        When using AutoSelect2MultipleField you should implement get_val_txt in this case.
18
        I think that this is because there should be an unique correspondence between
19
        the referenced value and the shown value
20
        In this particular example, the referenced value and the shown value are the same
21
        """
22
        return unicode(value)
23

  
24
class GroupsField(AutoModelSelect2MultipleField):
25
    queryset = Group.objects
26
    search_fields = [
27
            'name__icontains',
28
    ]
authentic2/manager/forms.py
1
from django.utils.translation import ugettext_lazy as _
2
from django import forms
3

  
4
from authentic2.compat import get_user_model
5

  
6
from . import utils, fields
7

  
8
class RoleAddForm(forms.Form):
9
    name = forms.CharField(
10
            label=_('Role name'))
11

  
12
    def save(self):
13
        return utils.role_add(self.cleaned_data['name'])
14

  
15

  
16
class ChooseUserForm(forms.Form):
17
    ref = fields.ChooseUserField(label=_('user'))
18

  
19

  
20
class UserEditForm(forms.ModelForm):
21
    groups = fields.GroupsField(required=False)
22

  
23
    class Meta:
24
        model = get_user_model()
25
        fields = [ 'username', 'first_name', 'last_name', 'email', 'groups']
26

  
authentic2/manager/static/authentic2/manager/css/style.css
1

  
2
div#sidebar {
3
	width: 15%
4
	float: left;
5
	text-align: justify;
6
	padding-right: 10px;
7
}
8

  
9
div#sidebar {
10
	text-align: left;
11
}
12

  
13
div#sidebar input[type=text] {
14
       width: 95%;
15
}
16

  
17
div#sidebar button {
18
       width: 100%;
19
}
20

  
21

  
22
#sidebar ul.roles {
23
	padding: 0;
24
	text-align: left;
25
	list-style: none;
26
}
27

  
28
#sidebar ul.roles li {
29
	margin-bottom: 1ex;
30
}
31

  
32
#sidebar ul.roles li a {
33
	text-decoration: none;
34
}
35

  
36
#sidebar ul.roles a.active {
37
	font-weight: bold;
38
}
39

  
40
div.role-info h3 {
41
}
42

  
43
form#add-user-role {
44
	margin-top: 2em;
45
	margin-bottom: 2em;
46
}
47

  
48
form#add-user-role div.select2-container {
49
	width: 30%;
50
	display: inline-block;
51
	position: relative;
52
	top: 2px;
53
	margin-left: 1ex;
54
	margin-right: 1ex;
55
}
56

  
57
table.feeds td.url {
58
	text-align: left;
59
	padding-left: 1em;
60
}
61

  
62
form#errors label span,
63
form#smtp label span {
64
        width: 16em;
65
        display: inline-block;
66
}
67

  
68
form#errors button, form#smtp button {
69
	margin-left: 15.4em;
70
}
71

  
72
div#user-edit div.left {
73
        float: left;
74
        width: 400px;
75
}
76

  
77
div#user-edit div.left button {
78
        margin-top: 2em;
79
}
80

  
81
div#user-edit div.right {
82
        float: right;
83
        width: 150px;
84
}
85

  
86
div#user-edit div.right strong {
87
        display: block;
88
        margin-bottom: 1ex;
89
}
90

  
91
div#user-edit div.right button {
92
        width: 100%;
93
        text-align: left;
94
        padding: 0.5ex 1ex;
95
}
96

  
97
table.users tbody tr td:nth-child(2) {
98
	color: #FF7800;
99
}
100

  
101
table.users tbody tr td:nth-child(2):hover {
102
	text-decoration: underline;
103
	border-bottom: 1px solid transparent;
104
}
105

  
106
/* bdauvergne additions */
107

  
108
#delete-form {
109
  float: right;
110
}
111

  
112
.ui-dialog-content .helptext {
113
  display: none;
114
}
115

  
116
.form-inner-container {
117
  width: 500px;
118
}
119

  
120
.other_actions {
121
  float: right;
122
  position: relative;
123
  max-width: 35%;
124
}
125

  
126
.other_actions input[type=submit] {
127
  display: block;
128
  margin: 5px;
129
  width: 100%;
130
}
131

  
132
ul.messages {
133
	position: fixed;
134
	width: 30em;
135
	top: 10px;
136
	right: 10px;
137
	padding: 0;
138
	z-index: 2000;
139
	margin-top: 1em;
140
	list-style-type: none;
141
	display: table;
142
	margin: auto;
143
	background: rgba(40, 40, 40, 0.7);
144
	color: white;
145
	text-shadow: black 1px 1px 1px;
146
	border-radius: 10px;
147
}
148

  
149
ul.messages li {
150
	padding: 1ex;
151
	margin: 1ex;
152
	border-size: 0px;
153
	-moz-border-radius:7px;
154
	-webkit-border-radius:7px;
155
	border-radius:7px;
156
	border: none;
157
}
158

  
159
input[type=submit][name=password_reset] {
160
  white-space: normal;
161
}
162

  
163
li#roles a { background-image: url(icon-personnes.png); }
164
li#roles a:hover { background-image: url(icon-personnes-hover.png); }
165

  
166
li#users a { background-image: url(icon-user.png); }
167
li#users a:hover { background-image: url(icon-user-hover.png); }
authentic2/manager/static/authentic2/manager/js/manager.js
1
$(function() {
2
    /* search inputs behaviours */
3
    $('#search-input').change(function () {
4
      var params = $.url().param();
5
      if ($(this).val()) {
6
        params.search = $(this).val();
7
      } else {
8
        if ('search' in params) {
9
          delete params.search;
10
        }
11
      }
12
      var href = $.url().attr('path')
13
      if ($.param(params)) {
14
        href += '?' + $.param(params);
15
      }
16
      window.location = href;
17
    });
18

  
19
    /* role/user table refresh */
20
    function update_table(href, cb) {
21
      $.get(href, function (response_text) {
22
        var $response = $(response_text);
23
        var $content = $response.find('.table-container');
24
        if (! $content.length) {
25
            $content = $response.find('table');
26
        }
27
        var $container = $('.table-container');
28
        if (! $container.length) {
29
          $container = $('table');
30
        }
31
        $container.replaceWith($content);
32
        if (cb != undefined) {
33
          cb();
34
        }
35
      });
36
    }
37
    /* paginator ajax loading */
38
    $('.content').on('click', '.paginator a', function () {
39
      var href = $(this).attr('href');
40
      var title = $(this).text();
41
      update_table(href, function () {
42
        history.pushState(null, 'page ' + title, href);
43
      });
44
      return false;
45
    });
46
    /* dialog load handler */
47
    $(document).on('gadjo:dialog-loaded', function (e, form) {
48
      $('.messages', form).delay(3000*(1+$('.messages li', form).length)).fadeOut('slow');
49
      if ($('.table-container').length) {
50
        update_table(location.href);
51
      }
52
    });
53
    /* user deletion */
54
    $(document).on('click', '.js-remove-user', function (e) {
55
      var $anchor = $(e.target);
56
      if ($(e.target).data('confirm')) {
57
        if (! confirm($(e.target).data('confirm'))) {
58
          return false;
59
        }
60
      }
61
      var $tr = $anchor.parents('tr');
62
      var ref = $tr.data('ref');
63
      $.post('', {'csrfmiddlewaretoken': window.csrf_token, 'action': 'remove', 'ref': ref}, function () {
64
          update_table(window.location.href);
65
      });
66
      return false;
67
    });
68
    /* confirmation on submit buttons */
69
    $(document).on('click', 'input[type=submit]', function (e) {
70
      if ($(e.target).data('confirm')) {
71
        if (! confirm($(e.target).data('confirm'))) {
72
          return false;
73
        }
74
      }
75
    })
76
});
authentic2/manager/tables.py
1
from django.utils.translation import ugettext_lazy as _
2
from django.utils.safestring import mark_safe
3

  
4
import django_tables2 as tables
5

  
6
from authentic2.compat import get_user_model
7

  
8
class UserTable(tables.Table):
9
    username = tables.TemplateColumn(
10
        '<a rel="popup" href="{% url "a2-manager-user-edit" pk=record.pk %}">{{ record.username }}</a>',
11
        verbose_name=_('username'))
12
    email = tables.Column(verbose_name=mark_safe(_('Email')))
13

  
14
    class Meta:
15
        model = get_user_model()
16
        attrs = {'class': 'main', 'id': 'user-table'}
17
        fields = ('username', 'email', 'first_name', 'last_name',
18
                'is_active')
19
        empty_text = _('None')
authentic2/manager/templates/authentic2/manager/base.html
1
{% extends "gadjo/base.html" %}
2
{% load i18n staticfiles %}
3

  
4
{% block page-title %}{% trans "Management" %}{% endblock %}
5

  
6
{% block css %}
7
  <link rel="stylesheet" type="text/css" media="all" href="{% static "authentic2/manager/css/style.css" %}"/>
8
{% endblock %}
9

  
10
{% block extrascripts %}
11
  {% if debug %}
12
    <script src="{% static "jquery/js/jquery.form.js" %}"></script>
13
  {% else %}
14
    <script src="{% static "jquery/js/jquery.form.min.js" %}"></script>
15
  {% endif %}
16
  <script type="text/javascript" src="{% static "authentic2/js/purl.js" %}"></script>
17
  <script type="text/javascript" src="{% static "authentic2/manager/js/manager.js" %}"></script>
18
  <script type="text/javascript" src="/static/js/select2.js"></script>
19
  <script type="text/javascript" src="/static/js/heavy_data.js"></script>
20
  <script>
21
    window.csrf_token = '{{ csrf_token }}';
22
  </script>
23
{% endblock %}
24

  
25
{% block site-url %}{{ management_homepage_url }}{% endblock %}
26
{% block site-title %}{% trans "Management" %}{% endblock %}
27

  
28
{% block logout-url %}{{ management_logout_url }}{% endblock %}
29

  
30
{% block homepage-url %}{% url "a2-manager-homepage" %}{% endblock %}
31

  
32
{% block appbar %}
33
  <h2>{% block page_title %}{% endblock %}</h2>
34
{% endblock %}
35

  
authentic2/manager/templates/authentic2/manager/delete.html
1
{% extends "authentic2/manager/sidebar.html" %}
2
{% load i18n %}
3

  
4
{% block messages %}
5
{% endblock %}
6

  
7
{% block main %}
8
  {% if title %}
9
    <div id="appbar"><h2>{{ title }}</h2></div>
10
  {% endif %}
11
  <form method="post">
12
    {% csrf_token %}
13
    <div class="form-inner-container">
14
      {% block caption %}
15
      <p>{% blocktrans %}Do you want to delete role {{ object }} ?{% endblocktrans %}</p>
16
      {% endblock %}
17
      <div class="buttons">
18
        <button>{% trans "Delete" %}</button> 
19
        <a class="cancel" href="..">{% trans "Cancel" %}</a>
20
      </div>
21
    </div>
22
  </form>
23
{% endblock %}
authentic2/manager/templates/authentic2/manager/form.html
1
{% extends "authentic2/manager/sidebar.html" %}
2
{% load i18n %}
3

  
4
{% block messages %}
5
{% endblock %}
6

  
7
{% block main %}
8
  <div class="content">
9
    {% if title %}
10
      <div id="appbar"><h2>{{ title }}</h2></div>
11
    {% endif %}
12
    <form method="post">
13
      <div class="form-inner-container">
14
        {% if messages %}
15
          <ul class="messages">
16
              {% for message in messages %}
17
              <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>
18
                  {{ message }}
19
              </li>
20
              {% endfor %}
21
          </ul>
22
        {% endif %}
23
        {% if other_actions %}
24
          <div class="other_actions">
25
            <strong>{% trans "Actions" %}</strong>
26
            {% for action in other_actions %}
27
              <input type="submit" name="{{ action.name }}" value="{{ action.title }}"
28
                {% if action.confirm %}data-confirm="{{ action.confirm }}"{% endif %}
29
              />
30
            {% endfor %}
31
          </div>
32
        {% endif %}
33
        {% csrf_token %}
34
        {{ form.as_p }}
35
        <div class="buttons">
36
          <button>{% if action %}{{ action }}{% else %}{% trans "Create" %}{% endif %}</button> 
37
          <a class="cancel" href="..">{% trans "Cancel" %}</a>
38
        </div>
39
      </div>
40
    </form>
41
  </div>
42
{% endblock %}
authentic2/manager/templates/authentic2/manager/homepage.html
1
{% extends "authentic2/manager/base.html" %}
2
{% load i18n %}
3

  
4
{% block beforecontent %}
5
{% endblock %}
6

  
7
{% block appbar %}
8
   <h2>{% trans "Welcome" %}</h2>
9
{% endblock %}
10

  
11
{% block content %}
12
  <div id="content">
13
     <div id="user-info">
14
       {{ user.get_full_name }}
15
       (<a href="{% url "auth_password_change" %}">{% trans "Password change" %}</a>)
16
     </div>
17

  
18
      <ul class="apps">
19
        <li id="users"><a href="{% url "a2-manager-users" %}">{% trans "User management" %}</a></li>
20
        <li id="roles"><a href="{% url "a2-manager-roles" %}">{% trans "Role management" %}</a></li>
21
      </ul>
22
      <br style="clear: both;"/>
23
  </div>
24
{% endblock %}
authentic2/manager/templates/authentic2/manager/role.html
1
{% extends "authentic2/manager/roles.html" %}
2

  
3
{% load i18n staticfiles django_tables2 %}
4

  
5
{% block extra_scripts %}
6
  {{ block.super }}
7
  {{ choose_user_form.media }}
8
{% endblock %}
9

  
10
{% block main %}
11
   <div class="role-info">
12
     <h3 style="margin-top: 0;">{% trans "Users with role" %}: {{ active_role.name }}</h3>
13

  
14
     {% render_table users "authentic2/manager/role_users_table.html" %}
15

  
16
     <form method="post" id="add-user-role">
17
             {% trans "Add an user to this role:" %}
18
             {% csrf_token %}
19
             {{ choose_user_form.ref }}
20
             <button>{% trans "Add" %}</button>
21
     </form>
22
   </div>
23
{% endblock %}
authentic2/manager/templates/authentic2/manager/role_users_table.html
1
{% extends "authentic2/manager/table.html" %}
2

  
3
{% load i18n %}
4

  
5
{% block table.head.last.column %}
6
<th></th>
7
{% endblock %}
8
{% block table.tbody.last.column %}
9
<td><a class="icon-remove-sign js-remove-user" data-confirm="{% blocktrans with username=row.record.username %}Do you really want to delete user &quot;{{ username }}&quot; ?{% endblocktrans %}" href="#"></a></td>
10
{% endblock %}
authentic2/manager/templates/authentic2/manager/roles.html
1
{% extends "authentic2/manager/sidebar.html" %}
2
{% load i18n staticfiles %}
3

  
4
{% block title %}{{ block.super }} - {% trans "Roles management" %}{% endblock %}
5

  
6
{% block page_title %}{% trans "Roles management" %}
7
{% if active_role %} — {{ active_role.name }}{% endif %}
8
{% endblock %}
9

  
10
{% block appbar %}
11
  {{ block.super }}
12
  {% if active_role %}
13
  <a rel="popup" href="{% url "a2-manager-role-delete" role_ref=active_role.ref %}" id="add-user-btn">{% trans "Delete" %}</a>
14
  <a rel="popup" href="{% url "a2-manager-role-edit" role_ref=active_role.ref %}" id="add-user-btn">{% trans "Edit" %}</a>
15
  {% else %}
16
   <p><a href="{% url "a2-manager-role-add" %}" rel="popup">{% trans "Add role" %}</a></p>
17
  {% endif %}
18
{% endblock %}
19

  
20

  
21
{% block sidebar %}
22
   <ul class="roles">
23
     {% for role in roles %}
24
       <li>
25
       <a href="{% url "a2-manager-role" role_ref=role.ref %}"
26
           {% if role.ref == active_role.ref %}class="active"{% endif %}>
27
            {{ role.name }}
28
         </a>
29
       </li>
30
     {% endfor %}
31
   </ul>
32

  
33
   {% if active_role %}
34
     <hr />
35
     <div id="search-form">
36
       <h3>{% trans "Search" %}</h3>
37
       <input id="search-input" type="search" value="{{ request.GET.search }}">
38
       <button>{% trans "Search" %}</button>
39
     </div>
40
   {% endif %}
41
{% endblock %}
42

  
43
{% block main %}
44
   <div class="big-msg-info">
45
     Utilisez les filtres sur sur la gauche de l'écran pour afficher
46
     les membres du rôle correspondant.
47
   </div>
48
{% endblock %}
49

  
50
{% block page-end %}
51
  <div id="role-edit" style="display: none;">
52
   <form>
53
    <label><span>Nom :</span> <input type="text" size="30" value="Foo"/></label></br>
54
   </form>
55
  </div>
56

  
57
  <div id="role-add" style="display: none;">
58
   <form id="role-add-form" method="post" action="{% url "a2-manager-role-add" %}">
59
    {% csrf_token %}
60
    {{ role_add_form }}
61
   </form>
62
  </div>
63

  
64
{% endblock %}
authentic2/manager/templates/authentic2/manager/table.html
1
{% extends "django_tables2/table.html" %}
2

  
3
{% load django_tables2 %}
4

  
5
{% block table.thead %}
6
<thead>
7
    <tr>
8
    {% for column in table.columns %}
9
        {% if column.orderable %}
10
        <th {{ column.attrs.th.as_html }}><a href="{% querystring table.prefixed_order_by_field=column.order_by_alias.next %}">{{ column.header }}</a></th>
11
        {% else %}
12
        <th {{ column.attrs.th.as_html }}>{{ column.header }}</th>
13
        {% endif %}
14
    {% endfor %}
15
    {% block table.head.last.column %}
16
    {% endblock %}
17
    </tr>
18
</thead>
19
{% endblock table.thead %}
20

  
21
{% block table.tbody.row %}
22
        <tr data-ref="{{ row.record.id }}" class="{{ forloop.counter|divisibleby:2|yesno:"even,odd" }}"> {# avoid cycle for Django 1.2-1.6 compatibility #}
23
            {% for column, cell in row.items %}
24
                <td {{ column.attrs.td.as_html }}>{% if column.localize == None %}{{ cell }}{% else %}{% if column.localize %}{{ cell|localize }}{% else %}{{ cell|unlocalize }}{% endif %}{% endif %}</td>
25
            {% endfor %}
26
            {% block table.tbody.last.column %}
27
            {% endblock %}
28
        </tr>
29
{% endblock table.tbody.row %}
30

  
31
{% block pagination %}
32
  <p class="paginator">
33
      {% if table.page.number > 1 %}
34
        {% if table.page.previous_page_number != 1 %}
35
          <a href="{% querystring table.prefixed_page_field=1 %}">1</a>
36
          ...
37
        {% endif %}
38
      {% endif %}
39

  
40
      {% if table.page.has_previous %}
41
        <a href="{% querystring table.prefixed_page_field=table.page.previous_page_number %}">{{ table.page.previous_page_number }}</a>
42
      {% endif %}
43

  
44
      <span class="this-page">{{ table.page.number }}</span>
45

  
46
      {% if table.page.has_next %}
47
        <a href="{% querystring table.prefixed_page_field=table.page.next_page_number %}">{{ table.page.next_page_number }}</a>
48
      {% endif %}
49
      {% if  table.page.number != table.page.paginator.num_pages %}
50
        {% if table.page.paginator.num_pages > 1 %}
51
          {% if table.page.next_page_number != table.page.paginator.num_pages %}
52
            ...
53
            <a href="{% querystring table.prefixed_page_field=table.page.paginator.num_pages %}">{{ table.page.paginator.num_pages }}</a>
54
          {% endif %}
55
        {% endif %}
56
      {% endif %}
57
  </p>
58
{% endblock %}
authentic2/manager/templates/authentic2/manager/users.html
1
{% extends "authentic2/manager/sidebar.html" %}
2
{% load i18n staticfiles django_tables2 %}
3

  
4
{% block page-title %}{{ block.super }} - {% trans "Users management" %}{% endblock %}
5

  
6
{% block page_title %}{% trans "Users management" %}{% endblock %}
7

  
8
{% block appbar %}
9
  {{ block.super }}
10
  <a rel="popup" href="{% url "a2-manager-user-add" %}" id="add-user-btn">{% trans "Add user" %}</a>
11
{% endblock %}
12

  
13
{% block sidebar %}
14
  <div>
15
    <h3>{% trans "Search" %}</h3>
16
     <div id="search-form">
17
       <input id="search-input" type="search" value="{{ request.GET.search }}">
18
       <button>{% trans "Search" %}</button>
19
     </div>
20
  </div>
21
  <p>{{ users.count }} users</p>
22
{% endblock %}
23

  
24
{% block main %}
25
  {% render_table table "authentic2/manager/table.html" %}
26
{% endblock %}
authentic2/manager/urls.py
1
from django.conf.urls import patterns, url, include
2

  
3
from . import views
4

  
5
urlpatterns = patterns('authentic2.views', 
6
        url(r'^$', views.homepage, name='a2-manager-homepage'),
7
        url(r'^roles/$', views.roles, name='a2-manager-roles'),
8
        url(r'^roles/add/$', views.role_add,
9
            name='a2-manager-role-add'),
10
        url(r'^roles/(?P<role_ref>[^/]*)/$', views.role,
11
            name='a2-manager-role'),
12
        url(r'^roles/(?P<role_ref>[^/]*)/edit/$', views.role_edit,
13
            name='a2-manager-role-edit'),
14
        url(r'^roles/(?P<role_ref>[^/]*)/delete/$',
15
            views.role_delete, name='a2-manager-role-delete'),
16
        url(r'^users/$', views.users, name='a2-manager-users'),
17
        url(r'^users/add/$', views.user_add,
18
            name='a2-manager-user-add'),
19
        url(r'^users/(?P<pk>[^/]*)/$', views.user_edit,
20
            name='a2-manager-user-edit'),
21

  
22
        url(r'^', include('django_select2.urls')),
23
   )
authentic2/manager/utils.py
1
from django.contrib.auth.models import Group, User
2
from django.db.models.query import Q
3

  
4

  
5
class Role(object):
6
    def __init__(self, name, ref):
7
        self.name = name
8
        self.ref = ref
9

  
10
class RoleUser(Role):
11
    pass
12

  
13

  
14
def get_roles():
15
    return [Role(g.name, g.id) for g in Group.objects.order_by('name')]
16

  
17
def get_role(ref):
18
    g = Group.objects.get(id=ref)
19
    return Role(g.name, g.id)
20

  
21
def filter_user(qs, search):
22
    return qs.filter(Q(username__icontains=search)
23
            | Q(first_name__icontains=search)
24
            | Q(last_name__icontains=search)
25
            | Q(email__icontains=search))
26

  
27
def get_role_users(role, search=None):
28
    qs = User.objects.filter(groups__id=role.ref)
29
    if search:
30
        qs = filter_user(qs, search)
31
    return qs
32

  
33
def get_users(search=None):
34
    qs = User.objects.order_by('username')
35
    if search:
36
        qs = filter_user(qs, search)
37
    return qs
38

  
39
def role_add(name):
40
    g, created = Group.objects.get_or_create(name=name)
41
    return g.id
42

  
43
def search_user(term):
44
    return [RoleUser(u.get_full_name().strip() or u.username, u.id) for u in filter_user(User.objects.all(), term)[:10]]
45

  
46
def add_user_to_role(role, user):
47
    u = User.objects.get(id=user)
48
    if u.groups.filter(id=role.ref).exists():
49
        return False
50
    else:
51
        u.groups.add(Group.objects.get(id=role.ref))
52
        return True
53

  
54
def remove_user_from_role(role, user):
55
    User.objects.get(id=user).groups.remove(Group.objects.get(id=role.ref))
56

  
57
def delete_role(role):
58
    Group.objects.filter(id=role.ref).delete()
authentic2/manager/views.py
1
import json
2

  
3
from django.views.generic import (TemplateView, FormView, UpdateView,
4
        CreateView, DeleteView)
5
from django.http import HttpResponse, HttpResponseRedirect
6
from django.shortcuts import redirect
7
from django.utils.translation import ugettext_lazy as _
8
from django.forms import models as model_forms
9
from django.core.urlresolvers import reverse
10

  
11
from django.contrib.auth.models import Group
12
from django.contrib.auth.forms import PasswordResetForm
13
from django.contrib.auth.tokens import default_token_generator
14
from django.contrib.auth.decorators import (permission_required,
15
    login_required)
16

  
17
from django.contrib import messages
18

  
19
from django_tables2 import RequestConfig
20

  
21
from authentic2.compat import get_user_model
22

  
23
from . import app_settings, utils, tables, forms
24

  
25
class Action(object):
26
    def __init__(self, name, title, confirm=None):
27
        self.name = name
28
        self.title = title
29
        self.confirm = confirm
30

  
31
class ManagerMixin(object):
32
    def get_context_data(self, **kwargs):
33
        ctx = super(ManagerMixin, self).get_context_data(**kwargs)
34
        ctx['management_homepage_url'] = app_settings.HOMEPAGE_URL or reverse('auth_homepage')
35
        ctx['management_logout_url'] = app_settings.LOGOUT_URL or reverse('auth_logout')
36
        return ctx
37

  
38
class RolesMixin(ManagerMixin):
39
    def get_context_data(self, **kwargs):
40
        ctx = super(ManagerMixin, self).get_context_data(**kwargs)
41
        ctx['roles'] = utils.get_roles()
42
        ctx['role_add_form'] = forms.RoleAddForm()
43
        return ctx
44

  
45
class AjaxFormViewMixin(object):
46
    success_url = '.'
47

  
48
    def form_valid(self, form):
49
        if hasattr(form, 'save'):
50
            self.form_result = form.save()
51
        return super(AjaxFormViewMixin, self).form_valid(form)
52

  
53
    def dispatch(self, request, *args, **kwargs):
54
        response = super(AjaxFormViewMixin, self).dispatch(request, *args, **kwargs)
55
        if not request.is_ajax():
56
            return response
57
        data = {}
58
        if 'Location' in response:
59
            data['location'] = response['Location']
60
        if hasattr(response, 'render'):
61
            response.render()
62
            data['content'] = response.content
63
        return HttpResponse(json.dumps(data), content_type='application/json')
64

  
65
class RolesView(RolesMixin, TemplateView):
66
    template_name = 'authentic2/manager/roles.html'
67

  
68
class TitleMixin(object):
69
    title = None
70

  
71
    def get_context_data(self, **kwargs):
72
        ctx = super(TitleMixin, self).get_context_data(**kwargs)
73
        if self.title:
74
            ctx['title'] = self.title
75
        return ctx
76

  
77
class ActionMixin(object):
78
    action = None
79

  
80
    def get_context_data(self, **kwargs):
81
        ctx = super(ActionMixin, self).get_context_data(**kwargs)
82
        if self.action:
83
            ctx['action'] = self.action
84
        return ctx
85

  
86
class OtherActionsMixin(object):
87
    other_actions = None
88

  
89
    def get_context_data(self, **kwargs):
90
        ctx = super(OtherActionsMixin, self).get_context_data(**kwargs)
91
        ctx['other_actions'] = tuple(self.get_other_actions())
92
        return ctx
93

  
94
    def get_other_actions(self):
95
        return self.other_actions or ()
96

  
97
    def post(self, request, *args, **kwargs):
98
        self.object = self.get_object()
99
        for action in self.get_other_actions():
100
            if action.name in request.POST:
101
                method = getattr(self, 'action_' + action.name, None)
102
                if method:
103
                    response = method(request, *args, **kwargs)
104
                    if response:
105
                        return response
106
                self.request.method = 'GET'
107
                return self.get(request, *args, **kwargs)
108
        return super(OtherActionsMixin, self).post(request, *args, **kwargs)
109

  
110

  
111
class RoleAddView(TitleMixin, AjaxFormViewMixin, FormView):
112
    template_name = 'authentic2/manager/form.html'
113
    form_class = forms.RoleAddForm
114
    title = _('Add new role')
115

  
116
    def form_valid(self, form):
117
        super(RoleAddView, self).form_valid(form)
118
        return redirect('a2-manager-role', role_ref=self.form_result)
119

  
120
class RoleDeleteView(TitleMixin, AjaxFormViewMixin, DeleteView):
121
    template_name = 'authentic2/manager/delete.html'
122
    model = Group
123
    title = _('Delete role')
124
    pk_url_kwarg = 'role_ref'
125
    success_url = '..'
126

  
127

  
128
class RoleEditView(TitleMixin, AjaxFormViewMixin, UpdateView):
129
    template_name = 'authentic2/manager/form.html'
130
    title = _('Edit role')
131
    model = Group
132
    pk_url_kwarg = 'role_ref'
133
    fields = ['name']
134

  
135
    def get_form_class(self):
136
        return model_forms.modelform_factory(self.model, fields=self.fields)
137

  
138

  
139
class RoleView(RolesMixin, TemplateView):
140
    template_name = 'authentic2/manager/role.html'
141

  
142
    def get_role(self):
143
        return utils.get_role(self.kwargs['role_ref'])
144

  
145
    def get_context_data(self, **kwargs):
146
        ctx = super(RoleView, self).get_context_data(**kwargs)
147
        ctx['active_role'] = self.get_role()
148
        kwargs = {}
149
        if 'search' in self.request.GET:
150
            kwargs = {'search': self.request.GET['search']}
151
        users = utils.get_role_users(ctx['active_role'], **kwargs)
152
        table = tables.UserTable(users)
153
        RequestConfig(self.request).configure(table)
154
        ctx['users'] = table
155
        ctx['choose_user_form'] = forms.ChooseUserForm()
156
        return ctx
157

  
158
    def post(self, request, *args, **kwargs):
159
        role = self.get_role()
160
        ref = request.POST.get('ref')
161
        if ref:
162
            action = request.POST.get('action', 'add')
163
            if action == 'add':
164
                if not utils.add_user_to_role(role, ref):
165
                    messages.warning(request, _('User already in '
166
                        'this role'))
167
            elif action == 'remove':
168
                utils.remove_user_from_role(role, ref)
169
        if 'delete' in request.GET:
170
            utils.delete_role(role)
171
            return HttpResponseRedirect('..')
172
        return HttpResponseRedirect('')
173

  
174

  
175

  
176
roles = permission_required('group.add', raise_exception=True)(RolesView.as_view())
177
role_add = permission_required('group.add', raise_exception=True)(RoleAddView.as_view())
178
role_edit = permission_required('group.change', raise_exception=True)(RoleEditView.as_view())
179
role_delete = permission_required('group.delete', raise_exception=True)(RoleDeleteView.as_view())
180
role = permission_required('group.delete', raise_exception=True)(RoleView.as_view())
181

  
182
class UsersView(RolesMixin, TemplateView):
183
    template_name = 'authentic2/manager/users.html'
184

  
185
    def get_context_data(self, **kwargs):
186
        ctx = super(UsersView, self).get_context_data(**kwargs)
187
        if 'search' in self.request.GET:
188
            kwargs = {'search': self.request.GET['search']}
189
        users = utils.get_users(**kwargs)
190
        ctx['users'] = users
191
        table = tables.UserTable(users)
192
        RequestConfig(self.request).configure(table)
193
        ctx['table'] = table
194
        return ctx
195

  
196
class UserMixin(object):
197
    model = get_user_model()
198
    template_name = 'authentic2/manager/form.html'
199
    fields = ['username', 'first_name', 'last_name', 'email', 'is_active']
200
    form_class = forms.UserEditForm
201

  
202
class UserAddView(UserMixin, ActionMixin, TitleMixin,
203
        AjaxFormViewMixin, CreateView):
204
    title = _('Create user')
205
    action = _('Create')
206

  
207
class UserEditView(UserMixin, OtherActionsMixin, ActionMixin, TitleMixin,
208
        AjaxFormViewMixin, UpdateView):
209
    title = _('Edit user')
210
    action = _('Edit')
211
    fields = ['username', 'first_name', 'last_name', 'email']
212

  
213
    def get_other_actions(self):
214
        yield Action('password_reset', _('Reset password'))
215
        if self.object.is_active:
216
            yield Action('deactivate', _('Deactivate'))
217
        else:
218
            yield Action('activate', _('Activate'))
219
        yield Action('delete', 
220
                _('Delete'), 
221
                _('Do you really want to delete "%s" ?') % self.object.username)
222

  
223
    def action_activate(self, request, *args, **kwargs):
224
        self.object.is_active = True
225
        self.object.save()
226

  
227
    def action_deactivate(self, request, *args, **kwargs):
228
        self.object.is_active = False
229
        self.object.save()
230

  
231
    def action_delete(self, request, *args, **kwargs):
232
        self.object.delete()
233
        return HttpResponseRedirect('.')
234

  
235
    def action_password_reset(self, request, *args, **kwargs):
236
        # FIXME: a bit hacky, could break if PasswordResetForm implementation changes
237
        # copied from django.contrib.auth.views and django.contrib.auth.forms
238
        form = PasswordResetForm()
239
        form.users_cache = [self.object]
240
        opts = {
241
            'use_https': request.is_secure(),
242
            'token_generator': default_token_generator,
243
            'request': request,
244
        }
245
        form.save(**opts)
246
        messages.info(request, _('A mail was sent to %s') % self.object.email)
247

  
248

  
249
users = permission_required('user.delete', raise_exception=True)(UsersView.as_view())
250
user_add = permission_required('user.add', raise_exception=True)(UserAddView.as_view())
251
user_edit = permission_required('user.change', raise_exception=True)(UserEditView.as_view())
252

  
253
class HomepageView(ManagerMixin, TemplateView):
254
    template_name = 'authentic2/manager/homepage.html'
255

  
256
homepage = login_required(HomepageView.as_view())
authentic2/settings.py
175 175
    'admin_tools.dashboard',
176 176
    'django.contrib.admin',
177 177
    'registration',
178
    'django_select2',
179
    'django_tables2',
178 180
    'authentic2.nonce',
179 181
    'authentic2.saml',
180 182
    'authentic2.idp',
......
182 184
    'authentic2.auth2_auth',
183 185
    'authentic2.attribute_aggregator',
184 186
    'authentic2.disco_service',
187
    'authentic2.manager',
185 188
    'authentic2',
186 189
)
187 190

  
authentic2/urls.py
23 23
    url(r'^admin/', include(admin.site.urls)),
24 24
    url(r'^admin_tools/', include('admin_tools.urls')),
25 25
    url(r'^idp/', include('authentic2.idp.urls')),
26
    url(r'^manager/', include('authentic2.manager.urls')),
26 27
)
27 28

  
28 29
if getattr(settings, 'AUTH_OPENID', False):
requirements.txt
8 8
--allow-unverified django-admin-tools
9 9
django-admin-tools>=0.5.1
10 10
dnspython
11
django-select2
12
django-tables2
setup.py
120 120
        'django-registration>=1',
121 121
        'django-admin-tools>=0.5.1',
122 122
        'django-debug-toolbar<1.0.0',
123
        'dnspython',],
123
        'dnspython',
124
        'django-select2',
125
        'django-tables2',
126
      ],
124 127
      zip_safe=False,
125 128
      classifiers=[
126 129
          "Development Status :: 5 - Production/Stable",
127
-