diff --git a/product/CMFCategory/CategoryTool.py b/product/CMFCategory/CategoryTool.py index d7d8ffaa585ed40a6631f3d08a5896ba9376262f..5e6b974000f7765a9f8a0096220cb486fd0b4677 100644 --- a/product/CMFCategory/CategoryTool.py +++ b/product/CMFCategory/CategoryTool.py @@ -222,6 +222,9 @@ class CategoryTool( UniqueObject, Folder, Base ): relative_url = str(relative_url) if base_category is not None: relative_url = '%s/%s' % (base_category, relative_url) + relative_url = \ + self._removeDuplicateBaseCategoryIdInCategoryPath(base_category, + relative_url) node = self.unrestrictedTraverse(relative_url) value = node except (TypeError, KeyError, NotFound): @@ -567,6 +570,7 @@ class CategoryTool( UniqueObject, Folder, Base ): else: category = my_category.getRelativeUrl() if my_base_category == category: + path = self._removeDuplicateBaseCategoryIdInCategoryPath(my_base_category, path) if spec_len == 0: if base: membership.append(path) @@ -825,6 +829,7 @@ class CategoryTool( UniqueObject, Folder, Base ): for category_url in self._getCategoryList(context): my_base_category = category_url.split('/', 1)[0] if my_base_category == base_category: + category_url = self._removeDuplicateBaseCategoryIdInCategoryPath(my_base_category, category_url) #LOG("getSingleCategoryMembershipList",0,"%s %s %s %s" % (context.getRelativeUrl(), # my_base_category, base_category, category_url)) if (checked_permission is None) or \ @@ -1739,6 +1744,24 @@ class CategoryTool( UniqueObject, Folder, Base ): getProperty = Base.getProperty hasProperty = Base.hasProperty + def _removeDuplicateBaseCategoryIdInCategoryPath(self, base_category_id, + path): + """Specific Handling to remove duplicated base_categories in path + values like in following example: 'region/region/europe/west'. + + If duplicated id is a real subobject of base_category, + then the path its keept as it is + """ + splitted_path = path.split('/', 2) + if len(splitted_path) >= 2 and base_category_id == splitted_path[1]: + # It needs to be checked. + base_category_value = self._getOb(base_category_id) + if base_category_value._getOb(splitted_path[1], None) is None: + # Duplicate found, strip len(base_category_id + '/') in path + path = path[len(base_category_id)+1:] + return path + + InitializeClass( CategoryTool ) # Psyco diff --git a/product/CMFCategory/tests/testCMFCategory.py b/product/CMFCategory/tests/testCMFCategory.py index 018fb22f7ae8b472a3ba2ee93281d77df191db2e..ce0159d0624ad0f73fc10b630644d15d00c4cc40 100644 --- a/product/CMFCategory/tests/testCMFCategory.py +++ b/product/CMFCategory/tests/testCMFCategory.py @@ -33,6 +33,8 @@ from Testing.ZopeTestCase.PortalTestCase import PortalTestCase from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import noSecurityManager +import transaction + try: from transaction import get as get_transaction except ImportError: @@ -88,7 +90,7 @@ class TestCMFCategory(ERP5TypeTestCase): # we also enable 'destination' category on organisations self._organisation_categories = organisation_ti.getTypeBaseCategoryList() organisation_ti._setTypeBaseCategoryList(self._organisation_categories - + ['destination']) + + ['destination', 'resource']) # Make persons. person_module = self.getPersonModule() @@ -151,11 +153,18 @@ class TestCMFCategory(ERP5TypeTestCase): portal_categories[bc].setAcquisitionCopyValue(0) portal_categories[bc].setAcquisitionAppendValue(0) portal_categories[bc].setFallbackBaseCategoryList(['subordination']) + for bc in ('resource', ): + if not hasattr(portal_categories, bc): + portal_categories.newContent(portal_type='Base Category',id=bc) + portal_categories[bc].setAcquisitionPortalTypeList("python: []") + portal_categories[bc].setAcquisitionMaskValue(0) + portal_categories[bc].setAcquisitionCopyValue(0) + portal_categories[bc].setAcquisitionAppendValue(0) def beforeTearDown(self): """Clean up.""" # categories - for bc in ('region', 'subordination', 'gender'): + for bc in ('region', 'subordination', 'gender', 'resource'): bc_obj = self.getPortal().portal_categories[bc] bc_obj.manage_delObjects() # type informations @@ -1010,6 +1019,67 @@ class TestCMFCategory(ERP5TypeTestCase): self.assertNotEquals([], list(category_tool.objectIds())) self.assertNotEquals([], list(category_tool.searchFolder())) + def test_duplicate_base_category_id_in_categories_list_properties(self): + """check that stored values like 'region/region/west' on categories property + are cleaned up by Getters to avoid accessing base_category through + implicit Acquisition. + + If region is a sub Category of Base Category region, + Relative Url must not be stripped + """ + portal_categories = self.getCategoriesTool() + organisation = self.getOrganisationModule().newContent( + portal_type='Organisation', + region='region/europe/west') + person = self.getPersonModule().newContent(portal_type='Person', + default_career_subordination_value=organisation) + self.assertTrue(organisation.hasRegion()) + self.assertEquals(organisation.getRegion(), 'europe/west') + self.assertEquals(organisation.getRegionList(), ['europe/west']) + old_west_region = organisation.getRegionValue() + self.assertEquals(old_west_region.getPortalType(), 'Category') + + # Check acquired categories + self.assertEquals(person.getRegion(), 'europe/west') + self.assertEquals(person.getRegionList(), ['europe/west']) + + region_base_category = portal_categories.region + new_region = region_base_category.newContent(portal_type='Category', + id='region') + new_region = new_region.newContent(portal_type='Category', id='europe') + new_region = new_region.newContent(portal_type='Category', id='west') + + self.assertEquals(organisation.getRegion(), 'region/europe/west') + self.assertEquals(organisation.getRegionList(), ['region/europe/west']) + self.assertEquals(organisation.getRegionValue().getPortalType(), 'Category') + self.assertNotEquals(organisation.getRegionValue(), old_west_region) + + self.assertEquals(person.getRegion(), 'region/europe/west') + self.assertEquals(person.getRegionList(), ['region/europe/west']) + + # Let's continue with resource because its ID conflict with + # "traversing namespaces" names + resource_value = portal_categories.resource.newContent(portal_type='Category', id='id1') + organisation.setResource('resource/id1') + self.assertEquals(organisation.getResource(), 'id1') + self.assertEquals(organisation.getResourceValue(), resource_value) + self.assertEquals(organisation.getResourceList(), ['id1']) + self.assertEquals(organisation.getResourceValue(portal_type='Category'), + resource_value) + self.assertEquals(organisation.getResourceValueList(portal_type='Category'), + [resource_value]) + + # Check other public methods of CategoryTool + self.assertEquals(portal_categories.getCategoryMembershipList(organisation, + 'resource', portal_type='Category'), + ['id1']) + self.assertEquals(portal_categories.getSingleCategoryMembershipList( + organisation, 'resource', + portal_type='Category'), + ['id1']) + # Check indexation + transaction.commit() + self.tic() def test_suite(): suite = unittest.TestSuite()