From 89e254cf97430cf6460e9d961cecd29609adef43 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Mon, 28 Sep 2009 17:30:10 +0000
Subject: [PATCH] Optimimize and split _updateLocalRolesOnObject. Rename
 get{Action,Role}List.

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/portal_types@29250 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/BusinessTemplate.py     |   6 +-
 .../ERP5Security/tests/testERP5Security.py    |   2 +-
 product/ERP5Type/Core/ActionInformation.py    |   6 +-
 product/ERP5Type/ERP5Type.py                  | 186 +++++++++---------
 4 files changed, 95 insertions(+), 105 deletions(-)

diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py
index 0d011213dc1..eda8f3e265d 100644
--- a/product/ERP5/Document/BusinessTemplate.py
+++ b/product/ERP5/Document/BusinessTemplate.py
@@ -2463,7 +2463,7 @@ class ActionTemplateItem(ObjectTemplateItem):
       for path, action_dict in portal_type_dict.iteritems():
         container = p.unrestrictedTraverse(path)
         container.manage_delObjects([obj.id
-          for obj in container.getActionList()
+          for obj in container.getActionInformationList()
           if obj.reference in action_dict])
         for obj in action_dict.itervalues():
           container._importOldAction(obj)
@@ -2544,7 +2544,7 @@ class PortalTypeRolesTemplateItem(BaseTemplateItem):
       obj = p.unrestrictedTraverse("portal_types/%s" %
           relative_url.split('/', 1)[1])
       self._objects[relative_url] = type_role_list = []
-      for role in obj.getRoleList():
+      for role in obj.getRoleInformationList():
         type_role_dict = {}
         for k, v in aq_base(role).__getstate__().iteritems():
           if k == 'condition':
@@ -2647,7 +2647,7 @@ class PortalTypeRolesTemplateItem(BaseTemplateItem):
         obj = p.unrestrictedTraverse(path, None)
         if obj is not None:
           # reset roles before applying
-          obj.manage_delObjects([x.id for x in obj.getRoleList()])
+          obj.manage_delObjects([x.id for x in obj.getRoleInformationList()])
           type_roles_list = self._objects[roles_path] or []
           for role_property_dict in type_roles_list:
             obj._importRole(role_property_dict)
diff --git a/product/ERP5Security/tests/testERP5Security.py b/product/ERP5Security/tests/testERP5Security.py
index cce0d570248..73d752d76cd 100644
--- a/product/ERP5Security/tests/testERP5Security.py
+++ b/product/ERP5Security/tests/testERP5Security.py
@@ -378,7 +378,7 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
       base_cat.manage_delObjects(list(base_cat.objectIds()))
     # clear role definitions
     for ti in self.getTypesTool().objectValues():
-      ti.manage_delObjects([x.id for x in ti.getRoleList()])
+      ti.manage_delObjects([x.id for x in ti.getRoleInformationList()])
     # clear modules
     for module in self.portal.objectValues():
       if module.getId().endswith('_module'):
diff --git a/product/ERP5Type/Core/ActionInformation.py b/product/ERP5Type/Core/ActionInformation.py
index 8cefae8aa10..10300c48644 100644
--- a/product/ERP5Type/Core/ActionInformation.py
+++ b/product/ERP5Type/Core/ActionInformation.py
@@ -73,15 +73,15 @@ class ActionInformation(XMLObject):
 
   def _setActionExpression(self, value):
     if isinstance(value, basestring):
-      value = Expression(value)
+      value = value and Expression(value) or None
     self._baseSetActionExpression(value)
   def _setCondition(self, value):
     if isinstance(value, basestring):
-      value = Expression(value)
+      value = value and Expression(value) or None
     self._baseSetCondition(value)
   def _setIcon(self, value):
     if isinstance(value, basestring):
-      value = Expression(value)
+      value = value and Expression(value) or None
     self._baseSetIcon(value)
 
   def getCondition(self):
diff --git a/product/ERP5Type/ERP5Type.py b/product/ERP5Type/ERP5Type.py
index f8c831aaf93..43ad6a32c3e 100644
--- a/product/ERP5Type/ERP5Type.py
+++ b/product/ERP5Type/ERP5Type.py
@@ -245,7 +245,7 @@ class ERP5TypeInformation(XMLObject,
         # which acquire their security definition from their parent
         # The downside of this optimisation is that it is not possible to
         # set a local role definition if the local role list is empty
-        if self.getRoleList():
+        if self.getRoleInformationList():
           self.updateLocalRolesOnObject(ob)
 
         # notify workflow after generating local roles, in order to prevent
@@ -349,9 +349,46 @@ class ERP5TypeInformation(XMLObject,
                 'your setup. '\
                 'Please install it to benefit from group-based security'
 
+      group_id_role_dict = self.getGroupIdRoleDict(ob, user_name)
+
+      # Update role assignments to groups
+      if ERP5UserManager is not None: # Default implementation
+        # Clean old group roles
+        old_group_list = ob.get_local_roles()
+        ob.manage_delLocalRoles([x[0] for x in old_group_list])
+        # Save the owner
+        for group, role_list in old_group_list:
+          if 'Owner' in role_list:
+            group_id_role_dict.setdefault(group, set()).add('Owner')
+        # Assign new roles
+        for group, role_list in group_id_role_dict.iteritems():
+          if role_list:
+            ob.manage_addLocalRoles(group, role_list)
+      else: # NuxUserGroups implementation
+        # Clean old group roles
+        old_group_list = ob.get_local_group_roles()
+        # We duplicate role settings to mimic PAS
+        ob.manage_delLocalGroupRoles([x[0] for x in old_group_list])
+        ob.manage_delLocalRoles([x[0] for x in old_group_list])
+        # Save the owner
+        for group, role_list in old_group_list:
+          if 'Owner' in role_list:
+            group_id_role_dict.setdefault(group, set()).add('Owner')
+        # Assign new roles
+        for group, role_list in group_id_role_dict.iteritems():
+          # We duplicate role settings to mimic PAS
+          ob.manage_addLocalGroupRoles(group, role_list)
+          ob.manage_addLocalRoles(group, role_list)
+      # Make sure that the object is reindexed
+      if reindex:
+        ob.reindexObjectSecurity()
+
+    security.declareProtected(Permissions.AccessContentsInformation,
+                              "getGroupIdRoleDict")
+    def getGroupIdRoleDict(self, ob, user_name=None):
       # Create an empty local Role Definition dict
       role_category_list_dict = {}
-      role_group_list_dict = {}
+      group_id_role_dict = {}
 
       # Fill it with explicit local roles defined as subobjects of current
       # object
@@ -416,17 +453,20 @@ class ERP5TypeInformation(XMLObject,
           for c in role.getRoleCategoryList():
             bc, value = c.split('/', 1)
             category_definition_dict.setdefault(bc, []).append(value)
-          # Now create role dict for each roles
-          for role in role.getRoleNameList():
-            if isinstance(category_result, dict):
-              # category_result is a dict (which provide group IDs directly)
-              # which represents of mapping of roles, security group IDs
-              # XXX explain that this is for providing user IDs mostly
-              role_group_list = role_group_list_dict.setdefault(role, [])
-              for k, v in category_result.items():
-                if k == role:
-                  role_group_list.extend(v)
-            else:
+
+          role_list = role.getRoleNameList()
+
+          if isinstance(category_result, dict):
+            # category_result is a dict (which provide group IDs directly)
+            # which represents of mapping of roles, security group IDs
+            # XXX explain that this is for providing user IDs mostly
+            for role, group_id_list in category_result.iteritems():
+              if role in role_list:
+                for group_id in group_id_list:
+                  group_id_role_dict.setdefault(group_id, set()).add(role)
+          else:
+            # Now create role dict for each roles
+            for role in role_list:
               # category_result is a list of dicts that represents the resolved
               # categories we create a category_value_dict from each of these
               # dicts aggregated with category_order and statically defined
@@ -439,96 +479,45 @@ class ERP5TypeInformation(XMLObject,
                 role_category_list.append(category_value_dict)
 
       # Generate security group ids from category_value_dicts
-      role_group_id_dict = {}
-      parent = ob.aq_parent
-      group_id_generator = getattr( parent,
-                             ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT,
-                             None )
+      group_id_generator = getattr(aq_parent(ob),
+                             ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT)
       group_id_generator = group_id_generator.__of__(ob)
-      if group_id_generator is None:
-        raise RuntimeError, '%s script was not found' % \
-                              ERP5TYPE_SECURITY_GROUP_ID_GENERATION_SCRIPT
-      for role, group_list in role_group_list_dict.items():
-        role_group_id_dict.setdefault(role, []).extend(group_list)
-      for role, value_list in role_category_list_dict.items():
-        role_group_dict = {}
+      for role, value_list in role_category_list_dict.iteritems():
+        role_group_set = group_id_role_dict.setdefault(role, set())
         for category_dict in value_list:
-          group_id = group_id_generator(**category_dict) # category_order is passed in the dict
+          group_id_list = group_id_generator(**category_dict) # category_order is passed in the dict
           # If group_id is not defined, do not use it
-          if group_id not in (None, ''):
-            if isinstance(group_id, str):
+          if group_id_list:
+            if isinstance(group_id_list, str):
               # Single group is defined (this is usually for group membership)
               # DEPRECATED due to cartesian product requirement
-              role_group_dict[group_id] = 1
-            else:
-              # Multiple groups are defined (list of users
-              # or list of group IDs resulting from a cartesian product)
-              for user_id in group_id:
-                role_group_dict[user_id] = 1
-        role_group_id_dict.setdefault(role, []).extend(role_group_dict.keys())
+              group_id_list = group_id_list,
+            # Multiple groups are defined (list of users
+            # or list of group IDs resulting from a cartesian product)
+            for group_id in group_id_list:
+              group_id_role_dict.setdefault(group_id, set()).add(role)
 
-      # Switch index from role to group id
-      group_id_role_dict = {}
-      for role, group_list in role_group_id_dict.items():
-        for group_id in group_list:
-          group_id_role_dict.setdefault(group_id, []).append(role)
-
-      # Update role assignments to groups
-      if ERP5UserManager is not None: # Default implementation
-        # Clean old group roles
-        old_group_list = ob.get_local_roles()
-        ob.manage_delLocalRoles([x[0] for x in old_group_list])
-        # Save the owner
-        for group, role_list in old_group_list:
-          if 'Owner' in role_list:
-            if not group_id_role_dict.has_key(group):
-              group_id_role_dict[group] = ('Owner',)
-            else:
-              group_id_role_dict[group].append('Owner')
-        # Assign new roles
-        for group, role_list in group_id_role_dict.items():
-          ob.manage_addLocalRoles(group, role_list)
-      else: # NuxUserGroups implementation
-        # Clean old group roles
-        old_group_list = ob.get_local_group_roles()
-        # We duplicate role settings to mimic PAS
-        ob.manage_delLocalGroupRoles([x[0] for x in old_group_list])
-        ob.manage_delLocalRoles([x[0] for x in old_group_list])
-        # Save the owner
-        for group, role_list in old_group_list:
-          if 'Owner' in role_list:
-            if not group_id_role_dict.has_key(group):
-              group_id_role_dict[group] = ('Owner',)
-            else:
-              group_id_role_dict[group].append('Owner')
-        # Assign new roles
-        for group, role_list in group_id_role_dict.items():
-          # We duplicate role settings to mimic PAS
-          ob.manage_addLocalGroupRoles(group, role_list)
-          ob.manage_addLocalRoles(group, role_list)
-      # Make sure that the object is reindexed
-      if reindex:
-        ob.reindexObjectSecurity()
+      return group_id_role_dict
 
     security.declarePrivate('getFilteredRoleListFor')
     def getFilteredRoleListFor(self, ob=None):
-        """Return all roles applicable to the object against user."""
-        portal = self.portal_url.getPortalObject()
-        if ob is None:
-          folder = portal
-        else:
-          folder = aq_parent(ob)
-          # Search up the containment hierarchy until we find an
-          # object that claims it's a folder.
-          while folder is not None:
-            if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
-              # found it.
-              break
-            else:
-              folder = aq_parent(folder)
+      """Return all roles applicable to the object against user."""
+      portal = self.getPortalObject()
+      if ob is None:
+        folder = portal
+      else:
+        folder = aq_parent(ob)
+        # Search up the containment hierarchy until we find an
+        # object that claims it's a folder.
+        while folder is not None:
+          if getattr(aq_base(folder), 'isPrincipiaFolderish', 0):
+            break # found it.
+          else:
+            folder = aq_parent(folder)
 
-        ec = createExprContext(folder, portal, ob)
-        return (role for role in self.getRoleList() if role.testCondition(ec))
+      ec = createExprContext(folder, portal, ob)
+      return (role for role in self.getRoleInformationList()
+                   if role.testCondition(ec))
 
     security.declareProtected(Permissions.ManagePortal, 'updateRoleMapping')
     def updateRoleMapping(self, REQUEST=None, form_id=''):
@@ -599,13 +588,13 @@ class ERP5TypeInformation(XMLObject,
     # USE_BASE_TYPE
     #
 
-    security.declarePrivate('getRoleList')
-    def getRoleList(self):
+    security.declarePrivate('getRoleInformationList')
+    def getRoleInformationList(self):
       """Return all roles for this portal type"""
       return self.objectValues(portal_type='Role Information')
 
-    security.declarePrivate('getActionList')
-    def getActionList(self):
+    security.declarePrivate('getActionInformationList')
+    def getActionInformationList(self):
       """Return all actions for this portal type"""
       return self.objectValues(portal_type='Action Information')
 
@@ -672,7 +661,8 @@ class ERP5TypeInformation(XMLObject,
     security.declarePrivate('listActions')
     def listActions(self, info=None, object=None):
       """ List all the actions defined by a provider."""
-      return sorted(self.getActionList(), key=lambda x: x.getFloatIndex())
+      return sorted(self.getActionInformationList(),
+                    key=lambda x: x.getFloatIndex())
 
     def _importOldAction(self, old_action):
       from Products.ERP5Type.Document.ActionInformation import ActionInformation
-- 
2.30.9