Projet

Général

Profil

0003-manager-include-roles-along-with-users-in-role-membe.patch

Valentin Deniaud, 03 novembre 2021 14:15

Télécharger (10,3 ko)

Voir les différences:

Subject: [PATCH 3/4] manager: include roles along with users in role members
 table (#57955)

 src/authentic2/manager/forms.py               | 10 ++++
 src/authentic2/manager/role_views.py          | 34 +++++++++--
 src/authentic2/manager/tables.py              | 24 +++++++-
 .../authentic2/manager/role_members.html      |  2 +
 src/authentic2/manager/views.py               |  2 +-
 tests/test_role_manager.py                    | 58 ++++++++++++++++++-
 6 files changed, 122 insertions(+), 8 deletions(-)
src/authentic2/manager/forms.py
590 590
        return qs
591 591

  
592 592

  
593
class RoleMembersSearchForm(UserSearchForm):
594
    all_members = forms.BooleanField(initial=False, label=_('View all members'), required=False)
595

  
596
    def __init__(self, *args, **kwargs):
597
        disable_all_members = kwargs.pop('disable_all_members', False)
598
        super().__init__(*args, **kwargs)
599
        if disable_all_members:
600
            self.fields['all_members'].widget.attrs['disabled'] = True
601

  
602

  
593 603
class UserAddChooseOUForm(OUSearchForm):
594 604
    ou_permission = 'custom_user.add_user'
595 605

  
src/authentic2/manager/role_views.py
171 171

  
172 172
class RoleMembersView(views.HideOUColumnMixin, RoleViewMixin, views.BaseSubTableView):
173 173
    template_name = 'authentic2/manager/role_members.html'
174
    table_class = tables.RoleMembersTable
175 174
    form_class = forms.ChooseUserForm
176 175
    success_url = '.'
177
    search_form_class = forms.UserSearchForm
176
    search_form_class = forms.RoleMembersSearchForm
178 177
    permissions = ['a2_rbac.view_role']
179 178

  
179
    @property
180
    def table_class(self):
181
        if self.view_all_members:
182
            return tables.RoleMembersTable
183
        return tables.MixedUserRoleTable
184

  
180 185
    @property
181 186
    def title(self):
182 187
        return self.get_instance_name()
......
189 194
    def can_manage_members(self, value):
190 195
        self._can_manage_members = value
191 196

  
197
    def dispatch(self, request, *args, **kwargs):
198
        self.children = views.filter_view(self.request, self.get_object().children(include_self=False))
199
        return super().dispatch(request, *args, **kwargs)
200

  
201
    def get_table_data(self):
202
        if self.view_all_members:
203
            return super().get_table_data()
204
        members = views.filter_view(self.request, self.object.members.all())
205
        members = self.filter_by_search(members)
206
        return list(self.children) + list(members)
207

  
192 208
    def get_table_queryset(self):
193
        children = self.object.children(include_self=False)
194
        via_prefetch = Prefetch('roles', queryset=children, to_attr='via')
209
        via_prefetch = Prefetch('roles', queryset=self.children, to_attr='via')
195 210
        return self.object.all_members().prefetch_related(via_prefetch)
196 211

  
212
    @property
213
    def view_all_members(self):
214
        return self.search_form.is_valid() and self.search_form.cleaned_data.get('all_members')
215

  
197 216
    def form_valid(self, form):
198 217
        user = form.cleaned_data['user']
199 218
        action = form.cleaned_data['action']
......
239 258
            kwargs['ou'] = self.object.ou
240 259
        return kwargs
241 260

  
261
    def get_search_form_kwargs(self):
262
        kwargs = super().get_search_form_kwargs()
263
        if not self.children:
264
            kwargs['data']['search-all_members'] = 'on'
265
            kwargs['disable_all_members'] = True
266
        return kwargs
267

  
242 268
    def get_context_data(self, **kwargs):
243 269
        ctx = super().get_context_data(**kwargs)
244 270
        ctx['children'] = list(
src/authentic2/manager/tables.py
28 28
from django_rbac.utils import get_ou_model, get_permission_model, get_role_model
29 29

  
30 30
User = get_user_model()
31
Role = get_role_model()
31 32

  
32 33

  
33 34
class PermissionLinkColumn(tables.LinkColumn):
......
51 52

  
52 53
class UserLinkColumn(PermissionLinkColumn):
53 54
    def render(self, **kwargs):
54
        user = kwargs['record']
55
        record = kwargs['record']
55 56
        value = super().render(**kwargs)
56
        if not user.is_active:
57
        if isinstance(record, User) and not record.is_active:
57 58
            value = html.format_html(
58 59
                '<span class="disabled">{value} ({disabled})</span>', value=value, disabled=_('disabled')
59 60
            )
......
91 92
        pass
92 93

  
93 94

  
95
class UserOrRoleColumn(UserLinkColumn):
96
    def render(self, **kwargs):
97
        value = super().render(**kwargs)
98
        if isinstance(kwargs['record'], Role):
99
            value = html.format_html(_('Members of role {value}'), value=value)
100
        return value
101

  
102

  
103
class MixedUserRoleTable(tables.Table):
104
    name = UserOrRoleColumn(
105
        verbose_name=_('Members'),
106
        text=lambda x: x.get_full_name() if isinstance(x, User) else x.name,
107
        orderable=False,
108
    )
109

  
110
    class Meta:
111
        attrs = {'class': 'main', 'id': 'user-table'}
112

  
113

  
94 114
class RoleTable(tables.Table):
95 115
    name = tables.LinkColumn(
96 116
        viewname='a2-manager-role-members', kwargs={'pk': A('pk')}, accessor='name', verbose_name=_('label')
src/authentic2/manager/templates/authentic2/manager/role_members.html
62 62
   {% render_table table "authentic2/manager/role_members_table.html" %}
63 63
 {% endwith %}
64 64

  
65
 {% if search_form.cleaned_data.all_members %}
65 66
 {% include "authentic2/manager/export_include.html" with export_view_name="a2-manager-role-members-export" %}
67
 {% endif %}
66 68

  
67 69
 {% if view.can_manage_members %}
68 70
   <form method="post" class="manager-m2m-add-form" id="add-user">
src/authentic2/manager/views.py
164 164
        return self.search_form_class
165 165

  
166 166
    def get_search_form_kwargs(self):
167
        return {'request': self.request, 'data': self.request.GET}
167
        return {'request': self.request, 'data': self.request.GET.dict()}
168 168

  
169 169
    def get_search_form(self):
170 170
        form_class = self.get_search_form_class()
tests/test_role_manager.py
119 119
    user2.roles.add(role2)
120 120

  
121 121
    response = login(app, admin, '/manage/roles/%s/' % role1.id)
122
    response.forms['search-form']['search-all_members'] = True
123
    response = response.forms['search-form'].submit()
122 124
    rows = list(
123 125
        zip(
124 126
            [text_content(el) for el in response.pyquery('tr td.username')],
......
375 377
    role = Role.objects.create(name='Role a', ou=get_default_ou())
376 378
    getattr(simple_role, 'add_%s' % relation)(role)
377 379
    resp = app.get(url)
378
    assert 'Role a' not in resp.text
380
    assert not resp.pyquery('a.role-inheritance-%s:contains("Role a")' % relation)
379 381
    assert '(view all roles)' in resp.text
380 382

  
381 383
    resp = resp.click('(view all roles)', href=relation)
......
404 406
        'ou1 - Role 2',
405 407
        'ou1 - Role 3',
406 408
    ]
409

  
410

  
411
def test_role_members_user_role_mixed_table(app, superuser, settings, simple_role, simple_user):
412
    simple_user.roles.add(simple_role)
413

  
414
    url = reverse('a2-manager-role-members', kwargs={'pk': simple_role.pk})
415
    resp = login(app, superuser, url)
416

  
417
    # no children, directly display members details
418
    assert resp.forms['search-form']['search-all_members'].value == 'on'
419
    assert 'disabled' in resp.forms['search-form']['search-all_members'].attrs
420
    assert 'Download list as CSV' in resp.text
421

  
422
    column_names = [text_content(el) for el in resp.pyquery('table#user-table th') if text_content(el)]
423
    assert column_names == [
424
        'User',
425
        'Username',
426
        'Email Address',
427
        'First Name',
428
        'Last Name',
429
        'Organizational Unit',
430
        'Direct member',
431
        'Inherited from',
432
    ]
433

  
434
    rows = [text_content(el) for el in resp.pyquery('tr td.link')]
435
    assert rows == ['Jôhn Dôe']
436

  
437
    # add child
438
    role = Role.objects.create(name='Role a', ou=get_default_ou())
439
    user = User.objects.create(username='user1')
440
    user.roles.add(role)
441
    simple_role.add_child(role)
442

  
443
    resp = app.get(url)
444
    assert not resp.forms['search-form']['search-all_members'].value
445
    assert 'disabled' not in resp.forms['search-form']['search-all_members'].attrs
446
    assert 'Download list as CSV' not in resp.text
447

  
448
    column_names = [text_content(el) for el in resp.pyquery('table#user-table th') if text_content(el)]
449
    assert column_names == ['Members']
450

  
451
    rows = [text_content(el) for el in resp.pyquery('tr td.name')]
452
    assert rows == ['Members of role Role a', 'Jôhn Dôe']
453

  
454
    resp.forms['search-form']['search-all_members'] = True
455
    resp = resp.forms['search-form'].submit()
456

  
457
    assert resp.forms['search-form']['search-all_members'].value == 'on'
458
    assert 'disabled' not in resp.forms['search-form']['search-all_members'].attrs
459
    assert 'Download list as CSV' in resp.text
460

  
461
    rows = [text_content(el) for el in resp.pyquery('tr td.link')]
462
    assert rows == ['user1', 'Jôhn Dôe']
407
-