Passed
Push — 2.x ( b48eec...e19f97 )
by Jordi
06:55
created

UsersOverviewControlPanel.deleteMembers()   C

Complexity

Conditions 11

Size

Total Lines 52
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 27
dl 0
loc 52
rs 5.4
c 0
b 0
f 0
cc 11
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like senaite.core.browser.usergroup.usergroups_usersoverview.UsersOverviewControlPanel.deleteMembers() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# -*- coding: utf-8 -*-
2
#
3
# This file is part of SENAITE.CORE.
4
#
5
# SENAITE.CORE is free software: you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation, version 2.
8
#
9
# This program is distributed in the hope that it will be useful, but WITHOUT
10
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
# details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc., 51
16
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17
#
18
# Copyright 2018-2025 by it's authors.
19
# Some rights reserved, see README and LICENSE.
20
21
import six
22
from Acquisition import aq_inner
23
from bika.lims import api
24
from Products.CMFCore.utils import getToolByName
25
from Products.CMFPlone.controlpanel.browser.usergroups_usersoverview import \
26
    UsersOverviewControlPanel as BaseView
27
from senaite.core import logger
28
from senaite.core.catalog import CLIENT_CATALOG
29
from senaite.core.config.roles import HIDDEN_ROLES
30
from zExceptions import Forbidden
31
32
33
class UsersOverviewControlPanel(BaseView):
34
    """Custom userprefs controlpanel
35
    """
36
37
    @property
38
    def portal_roles(self):
39
        """Return only SENAITE Roles
40
        """
41
        pmemb = getToolByName(aq_inner(self.context), "portal_membership")
42
        roles = pmemb.getPortalRoles()
43
        return filter(lambda r: r not in HIDDEN_ROLES, roles)
44
45
    def get_clients(self):
46
        """Return all clients from the site
47
        """
48
        query = {"portal_type": "Client"}
49
        clients = api.search(query, CLIENT_CATALOG)
50
        return list(map(api.get_object, clients))
51
52
    def clear_user_groups(self, user):
53
        """Clear all assigned groups of the user
54
        """
55
        groups = user.getGroups()
56
        pgm = api.get_tool("portal_groups")
57
        user_id = user.getId()
58
        for group in groups:
59
            try:
60
                pgm.removePrincipalFromGroup(user_id, group)
61
                logger.info("Clearing group '%s' for user '%s'" % (
62
                    group, user_id))
63
            except KeyError:
64
                # happens e.g. for `AuthenticatedUsers`
65
                logger.warn("Could not clear group '%s' for user '%s'" % (
66
                    group, user_id))
67
                continue
68
69
    def clear_user_roles(self, user):
70
        """Clear all assigned roles of the user
71
        """
72
        roles = user.getRoles()
73
        acl = api.get_tool("acl_users")
74
        prm = acl.portal_role_manager
75
        user_id = user.getId()
76
        for role in roles:
77
            try:
78
                prm.removeRoleFromPrincipal(role, user_id)
79
                logger.info("Clearing role '%s' for user '%s'" % (
80
                    role, user_id))
81
            except KeyError:
82
                # happens e.g. for `AuthenticatedUsers`
83
                logger.warn("Could not clear role '%s' for user '%s'" % (
84
                    role, user_id))
85
                continue
86
87
    def deleteMembers(self, member_ids):
88
        # this method exists to bypass the 'Manage Users' permission check
89
        # in the CMF member tool's version
90
        context = aq_inner(self.context)
91
        mtool = api.get_tool("portal_membership")
92
93
        # Delete members in acl_users.
94
        acl_users = context.acl_users
95
        if isinstance(member_ids, six.string_types):
96
            member_ids = (member_ids,)
97
        member_ids = list(member_ids)
98
        for member_id in member_ids[:]:
99
            member = mtool.getMemberById(member_id)
100
            if member is None:
101
                member_ids.remove(member_id)
102
            else:
103
                if not member.canDelete():
104
                    raise Forbidden
105
                if "Manager" in member.getRoles() and not self.is_zope_manager:
106
                    raise Forbidden
107
                # clear all role/group assignments
108
                self.clear_user_groups(member)
109
                self.clear_user_roles(member)
110
111
        try:
112
            acl_users.userFolderDelUsers(member_ids)
113
        except (AttributeError, NotImplementedError):
114
            raise NotImplementedError('The underlying User Folder '
115
                                      'doesn\'t support deleting members.')
116
117
        # Delete member data in portal_memberdata.
118
        mdtool = api.get_tool("portal_memberdata")
119
        if mdtool is not None:
120
            for member_id in member_ids:
121
                mdtool.deleteMemberData(member_id)
122
123
        # NOTE: the original call below iterates over **all** objects
124
        # recursively to remove the local roles, which takes ages!
125
        # The only place we allow local roles to be assigned are clients.
126
        # Therefore, we want to make sure to remove them just from there
127
        #
128
        # Delete members' local roles.
129
        # mtool.deleteLocalRoles(
130
        #     getUtility(ISiteRoot),
131
        #     member_ids,
132
        #     reindex=1,
133
        #     recursive=1
134
        # )
135
        #
136
        # XXX: Maybe we could index local role assignments in the future?
137
        for client in self.get_clients():
138
            mtool.deleteLocalRoles(client, member_ids, reindex=0, recursive=0)
139