Projet

Général

Profil

0001-manager-use-multiple-selection-widget-to-manage-role.patch

Benjamin Dauvergne, 27 janvier 2022 19:01

Télécharger (12,2 ko)

Voir les différences:

Subject: [PATCH 1/2] manager: use multiple selection widget to manage role's
 members (#61188)

 src/authentic2/manager/forms.py               | 53 ++++++++++++-------
 src/authentic2/manager/role_views.py          | 20 +++----
 .../manager/role_members_table.html           |  2 +-
 tests/test_manager.py                         |  8 +--
 tests/test_role_manager.py                    | 14 ++---
 5 files changed, 55 insertions(+), 42 deletions(-)
src/authentic2/manager/forms.py
30 30
from django.utils.text import slugify
31 31
from django.utils.translation import pgettext, ugettext
32 32
from django.utils.translation import ugettext_lazy as _
33
from django_select2.forms import HeavySelect2Widget
33
from django_select2.forms import HeavySelect2MultipleWidget
34 34

  
35 35
from authentic2.a2_rbac.models import OrganizationalUnit, Permission, Role
36 36
from authentic2.a2_rbac.utils import generate_slug, get_default_ou
......
859 859
        self.add_error('import_file', error)
860 860

  
861 861

  
862
class HeavySelect2WidgetNoCache(HeavySelect2Widget):
862
class HeavySelect2MultipleWidgetNoCache(HeavySelect2MultipleWidget):
863 863
    class Media:
864 864
        js = ('authentic2/manager/js/select2_locale.js',)
865 865

  
......
867 867
        pass
868 868

  
869 869

  
870
class UserOrRoleField(forms.Field):
871
    widget = HeavySelect2MultipleWidgetNoCache(
872
        data_url='/',
873
        attrs={'data-placeholder': _('Select a user or a role'), 'data-minimum-input-length': 3},
874
    )
875

  
876

  
870 877
class ChooseUserOrRoleForm(FormWithRequest, forms.Form):
871
    user_or_role = forms.CharField(label=_('Add to role'))
878
    user_or_roles = UserOrRoleField(label=_('Add to role'))
872 879
    action = forms.CharField(initial='add', widget=forms.HiddenInput)
873 880

  
874 881
    def __init__(self, *args, **kwargs):
875 882
        self.role = kwargs.pop('role', None)
876 883
        super().__init__(*args, **kwargs)
877
        self.fields['user_or_role'].widget = HeavySelect2WidgetNoCache(
878
            data_url=reverse('user-or-role-select2-json', kwargs={'pk': self.role.pk}),
879
            attrs={'data-placeholder': _('Select a user or a role'), 'data-minimum-input-length': 3},
884
        self.fields['user_or_roles'].widget.data_url = reverse(
885
            'user-or-role-select2-json', kwargs={'pk': self.role.pk}
880 886
        )
881 887

  
882 888
    def clean(self):
883 889
        super().clean()
884
        try:
885
            object_type, pk = self.cleaned_data.get('user_or_role', '').split('-')
886
            pk = int(pk)
887
        except (ValueError, TypeError):
888
            return
889

  
890
        if object_type == 'user':
890
        self.cleaned_data['users'] = set()
891
        self.cleaned_data['roles'] = set()
892
        for user_or_role in self.cleaned_data.get('user_or_roles', []):
891 893
            try:
892
                self.cleaned_data['user'] = self.get_user_queryset(self.request.user, self.role).get(pk=pk)
893
            except User.DoesNotExist:
894
                return
895
        elif object_type == 'role':
896
            try:
897
                self.cleaned_data['role'] = self.get_role_queryset(self.request.user, self.role).get(pk=pk)
898
            except Role.DoesNotExist:
894
                object_type, pk = user_or_role.split('-')
895
                pk = int(pk)
896
            except (ValueError, TypeError):
899 897
                return
900 898

  
899
            if object_type == 'user':
900
                try:
901
                    self.cleaned_data['users'].add(
902
                        self.get_user_queryset(self.request.user, self.role).get(pk=pk)
903
                    )
904
                except User.DoesNotExist:
905
                    return
906
            elif object_type == 'role':
907
                try:
908
                    self.cleaned_data['roles'].add(
909
                        self.get_role_queryset(self.request.user, self.role).get(pk=pk)
910
                    )
911
                except Role.DoesNotExist:
912
                    return
913

  
901 914
    @staticmethod
902 915
    def get_role_queryset(user, role):
903 916
        qs = Role.objects.exclude(pk=role.pk)
src/authentic2/manager/role_views.py
223 223
        action = form.cleaned_data['action']
224 224
        if not self.can_manage_members:
225 225
            messages.warning(self.request, _('You are not authorized'))
226
        elif 'user' in form.cleaned_data:
227
            if action == 'add':
228
                self.add_user(form.cleaned_data['user'])
229
            elif action == 'remove':
230
                self.remove_user(form.cleaned_data['user'])
231
        elif 'role' in form.cleaned_data:
232
            if action == 'add':
233
                self.add_role(form.cleaned_data['role'])
234
            elif action == 'remove':
235
                self.remove_role(form.cleaned_data['role'])
226
        if action == 'add':
227
            user_action = self.add_user
228
            role_action = self.add_role
229
        elif action == 'remove':
230
            user_action = self.remove_user
231
            role_action = self.remove_role
232
        for user in form.cleaned_data.get('users', []):
233
            user_action(user)
234
        for role in form.cleaned_data.get('roles', []):
235
            role_action(role)
236 236
        return super().form_valid(form)
237 237

  
238 238
    def add_user(self, user):
src/authentic2/manager/templates/authentic2/manager/role_members_table.html
6 6
    <th></th>
7 7
  {% endblock %}
8 8
  {% block table.tbody.last.column %}
9
  <td class="remove-icon-column">{% if table.context.view.can_manage_members and row.record.direct %}<a class="js-remove-object" data-confirm="{% blocktrans with record=row.record role=table.context.object %}Do you really want to remove &quot;{{ record }}&quot; from role &quot;{{ role }}&quot;?{% endblocktrans %}" href="#" data-pk-arg="user_or_role"><span class="icon-remove-sign"></span></a>{% endif %}</td>
9
  <td class="remove-icon-column">{% if table.context.view.can_manage_members and row.record.direct %}<a class="js-remove-object" data-confirm="{% blocktrans with record=row.record role=table.context.object %}Do you really want to remove &quot;{{ record }}&quot; from role &quot;{{ role }}&quot;?{% endblocktrans %}" href="#" data-pk-arg="user_or_roles"><span class="icon-remove-sign"></span></a>{% endif %}</td>
10 10
  {% endblock %}
tests/test_manager.py
937 937
    # user can add members
938 938
    response = app.get('/manage/roles/%s/' % simple_role.pk)
939 939
    form = response.forms['add-member']
940
    form['user_or_role'].force_value('user-%s' % admin.pk)
940
    form['user_or_roles'].force_value('user-%s' % admin.pk)
941 941
    response = form.submit().follow()
942 942
    assert simple_role in admin.roles.all()
943 943

  
......
945 945
    q = response.pyquery.remove_namespaces()
946 946
    assert q('table tbody tr td .icon-remove-sign')
947 947
    token = str(response.context['csrf_token'])
948
    params = {'action': 'remove', 'user_or_role': 'user-%s' % admin.pk, 'csrfmiddlewaretoken': token}
948
    params = {'action': 'remove', 'user_or_roles': 'user-%s' % admin.pk, 'csrfmiddlewaretoken': token}
949 949
    app.post('/manage/roles/%s/' % simple_role.pk, params=params)
950 950
    assert simple_role not in admin.roles.all()
951 951

  
......
981 981
    # user can add role as a member through role members form
982 982
    response = app.get('/manage/roles/%s/' % simple_role.pk)
983 983
    form = response.forms['add-member']
984
    form['user_or_role'].force_value('role-%s' % role.pk)
984
    form['user_or_roles'].force_value('role-%s' % role.pk)
985 985
    response = form.submit().follow()
986 986
    assert role in simple_role.children()
987 987

  
......
989 989
    q = response.pyquery.remove_namespaces()
990 990
    assert q('table tbody tr td .icon-remove-sign')
991 991
    token = str(response.context['csrf_token'])
992
    params = {'action': 'remove', 'user_or_role': 'role-%s' % role.pk, 'csrfmiddlewaretoken': token}
992
    params = {'action': 'remove', 'user_or_roles': 'role-%s' % role.pk, 'csrfmiddlewaretoken': token}
993 993
    app.post('/manage/roles/%s/' % simple_role.pk, params=params)
994 994
    assert role not in simple_role.children()
995 995

  
tests/test_role_manager.py
563 563
    select2_json = request_select2(app, resp, term='Jôhn')
564 564
    assert len(select2_json['results']) == 1
565 565
    form = resp.forms['add-member']
566
    form['user_or_role'].force_value(select2_json['results'][0]['id'])
566
    form['user_or_roles'].force_value(select2_json['results'][0]['id'])
567 567
    resp = form.submit().follow()
568 568
    assert 'Jôhn Dôe' in resp.text
569 569

  
......
573 573
    data_pks = [row.attrib['data-pk'] for row in resp.pyquery('table tbody tr')]
574 574
    assert data_pks == ['user-%s' % simple_user.pk]
575 575
    data_pk_args = [row.attrib['data-pk-arg'] for row in resp.pyquery('table tbody tr td a.js-remove-object')]
576
    assert data_pk_args == ['user_or_role']
576
    assert data_pk_args == ['user_or_roles']
577 577

  
578 578
    select2_json = request_select2(app, resp, term='role_ou1')
579 579
    assert len(select2_json['results']) == 1
580 580
    form = resp.forms['add-member']
581
    form['user_or_role'].force_value(select2_json['results'][0]['id'])
581
    form['user_or_roles'].force_value(select2_json['results'][0]['id'])
582 582
    resp = form.submit().follow()
583 583
    assert 'role_ou1' in resp.text
584 584

  
......
588 588
    data_pks = [row.attrib['data-pk'] for row in resp.pyquery('table tbody tr')]
589 589
    assert data_pks == ['role-%s' % role_ou1.pk, 'user-%s' % simple_user.pk]
590 590
    data_pk_args = [row.attrib['data-pk-arg'] for row in resp.pyquery('table tbody tr td a.js-remove-object')]
591
    assert data_pk_args == ['user_or_role', 'user_or_role']
591
    assert data_pk_args == ['user_or_roles', 'user_or_roles']
592 592

  
593 593
    # simulate click on Jôhn Dôe delete icon
594 594
    token = str(resp.context['csrf_token'])
595
    params = {'action': 'remove', 'user_or_role': 'user-%s' % simple_user.pk, 'csrfmiddlewaretoken': token}
595
    params = {'action': 'remove', 'user_or_roles': 'user-%s' % simple_user.pk, 'csrfmiddlewaretoken': token}
596 596
    resp = app.post('/manage/roles/%s/' % simple_role.pk, params=params).follow()
597 597
    assert 'Jôhn Dôe' not in resp.text
598 598

  
599 599
    # simulate click on role_ou1 delete icon
600 600
    token = str(resp.context['csrf_token'])
601
    params = {'action': 'remove', 'user_or_role': 'role-%s' % role_ou1.pk, 'csrfmiddlewaretoken': token}
601
    params = {'action': 'remove', 'user_or_roles': 'role-%s' % role_ou1.pk, 'csrfmiddlewaretoken': token}
602 602
    resp = app.post('/manage/roles/%s/' % simple_role.pk, params=params).follow()
603 603
    assert 'role_ou1' not in resp.text
604 604

  
605 605
    # invalid choices are ignored
606 606
    for invalid_choice in ('', 'wrong-wrong', 'user-', 'user-xxx', 'role', 'user-99999'):
607 607
        form = resp.forms['add-member']
608
        form['user_or_role'].force_value(invalid_choice)
608
        form['user_or_roles'].force_value(invalid_choice)
609 609
        resp = form.submit().maybe_follow()
610
-