Commit 6bf9e694 authored by Julien Muchembled's avatar Julien Muchembled

Merge support for ZODB indexing of category related documents

parents a7780bc6 7865c58d
...@@ -30,7 +30,8 @@ ...@@ -30,7 +30,8 @@
"""\ """\
ERP portal_categories tool. ERP portal_categories tool.
""" """
from collections import deque
from BTrees.OOBTree import OOTreeSet
from OFS.Folder import Folder from OFS.Folder import Folder
from Products.CMFCore.utils import UniqueObject from Products.CMFCore.utils import UniqueObject
from Products.ERP5Type.Globals import InitializeClass, DTMLFile from Products.ERP5Type.Globals import InitializeClass, DTMLFile
...@@ -40,9 +41,11 @@ from Acquisition import aq_base, aq_inner ...@@ -40,9 +41,11 @@ from Acquisition import aq_base, aq_inner
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Base import Base from Products.ERP5Type.Base import Base
from Products.ERP5Type.Cache import getReadOnlyTransactionCache from Products.ERP5Type.Cache import getReadOnlyTransactionCache
from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
from Products.CMFCategory import _dtmldir from Products.CMFCategory import _dtmldir
from Products.CMFCore.PortalFolder import ContentFilter from Products.CMFCore.PortalFolder import ContentFilter
from Products.CMFCategory.Renderer import Renderer from Products.CMFCategory.Renderer import Renderer
from Products.CMFCategory.Category import Category, BaseCategory
from OFS.Traversable import NotFound from OFS.Traversable import NotFound
import types import types
...@@ -55,6 +58,34 @@ _marker = object() ...@@ -55,6 +58,34 @@ _marker = object()
class CategoryError( Exception ): class CategoryError( Exception ):
pass pass
class RelatedIndex(): # persistent.Persistent can be added
# without breaking compatibility
def __repr__(self):
try:
contents = ', '.join('%s=%r' % (k, list(v))
for (k, v) in self.__dict__.iteritems())
except Exception:
contents = '...'
return '<%s(%s) at 0x%x>' % (self.__class__.__name__, contents, id(self))
def __nonzero__(self):
return any(self.__dict__.itervalues())
def add(self, base, relative_url):
try:
getattr(self, base).add(relative_url)
except AttributeError:
setattr(self, base, OOTreeSet((relative_url,)))
def remove(self, base, relative_url):
try:
getattr(self, base).remove(relative_url)
except (AttributeError, KeyError):
pass
class CategoryTool( UniqueObject, Folder, Base ): class CategoryTool( UniqueObject, Folder, Base ):
""" """
The CategoryTool object is the placeholder for all methods The CategoryTool object is the placeholder for all methods
...@@ -308,38 +339,34 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -308,38 +339,34 @@ class CategoryTool( UniqueObject, Folder, Base ):
such as site/group/a/b/c/b1/c1 where b and b1 are both children such as site/group/a/b/c/b1/c1 where b and b1 are both children
categories of a. categories of a.
relative_url -- a single relative url of a list of relative_url -- a single relative url or a list of relative urls
relative urls
strict -- if set to 1, only return uids of parents, not strict -- if set to 1, only return uids of parents, not
relative_url relative_url
""" """
uid_dict = {} uid_set = set()
if isinstance(relative_url, str): if isinstance(relative_url, str):
relative_url = (relative_url,) relative_url = (relative_url,)
for path in relative_url: for path in relative_url:
try: try:
o = self.getCategoryValue(path, base_category=base_category) o = self.getCategoryValue(path, base_category=base_category)
if o is not None: if o is not None:
my_base_category = self.getBaseCategoryId(path) if base_category is None:
bo = self.get(my_base_category, None) my_base_category = self.getBaseCategoryId(path)
if bo is not None: else:
bo_uid = int(bo.getUid()) my_base_category = base_category
uid_dict[(int(o.uid), bo_uid, 1)] = 1 # Strict Membership bo_uid = self[my_base_category].getUid()
if o.meta_type == 'CMF Category' or o.meta_type == 'CMF Base Category': uid_set.add((o.getUid(), bo_uid, 1)) # Strict Membership
if not strict:
while o.portal_type == 'Category':
# This goes up in the category tree # This goes up in the category tree
# XXX we should also go up in some other cases.... # XXX we should also go up in some other cases....
# ie. when some documents act as categories # ie. when some documents act as categories
if not strict: o = o.aq_parent # We want acquisition here without aq_inner
while o.meta_type == 'CMF Category': uid_set.add((o.getUid(), bo_uid, 0)) # Non Strict Membership
o = o.aq_parent # We want acquisition here without aq_inner
uid_dict[(int(o.uid), bo_uid, 0)] = 1 # Non Strict Membership
except (KeyError, AttributeError): except (KeyError, AttributeError):
LOG('WARNING: CategoriesTool',0, 'Unable to find uid for %s' % path) LOG('WARNING: CategoriesTool',0, 'Unable to find uid for %s' % path)
return uid_dict.keys() return list(uid_set) # cast to list for <dtml-in>
security.declareProtected(Permissions.AccessContentsInformation, 'getUids')
getUids = getCategoryParentUidList
security.declareProtected(Permissions.AccessContentsInformation, 'getCategoryChildUidList') security.declareProtected(Permissions.AccessContentsInformation, 'getCategoryChildUidList')
def getCategoryChildUidList(self, relative_url, base_category = None, strict=0): def getCategoryChildUidList(self, relative_url, base_category = None, strict=0):
...@@ -631,90 +658,58 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -631,90 +658,58 @@ class CategoryTool( UniqueObject, Folder, Base ):
portal_type = (portal_type,) portal_type = (portal_type,)
spec = portal_type spec = portal_type
self._cleanupCategories(context)
if isinstance(category_list, str): if isinstance(category_list, str):
category_list = (category_list, ) category_list = (category_list, )
elif category_list is None: elif category_list is None:
category_list = () category_list = ()
elif isinstance(category_list, (list, tuple)):
pass
else:
__traceback_info__ = (base_category_list, category_list)
raise TypeError('Category must be of string, tuple of string '
'or list of string type.')
if isinstance(base_category_list, str): if isinstance(base_category_list, str):
base_category_list = (base_category_list, ) base_category_list = (base_category_list, )
# Build the ckecked_permission filter
if checked_permission is not None: if checked_permission is not None:
checkPermission = self.portal_membership.checkPermission checkPermission = self.portal_membership.checkPermission
def permissionFilter(obj):
if checkPermission(checked_permission, obj):
return 0
else:
return 1
new_category_list = [] new_category_list = deque()
default_dict = {} default_base_category_set = set()
spec_len = len(spec) default_category_set = set()
for path in self._getCategoryList(context): for path in self._getCategoryList(context):
my_base_id = self.getBaseCategoryId(path) my_base_id = self.getBaseCategoryId(path)
if my_base_id not in base_category_list: if my_base_id in base_category_list:
# Keep each membership which is not in the if spec or checked_permission is not None:
# specified list of base_category ids
new_category_list.append(path)
else:
keep_it = 0
if spec_len != 0 or (checked_permission is not None):
obj = self.unrestrictedTraverse(path, None) obj = self.unrestrictedTraverse(path, None)
if obj is not None: if obj is not None:
if spec_len != 0: # If spec is (), then we should keep nothing
# If spec is (), then we should keep nothing # Everything will be replaced
# Everything will be replaced # If spec is not (), Only keep this if not in our spec
# If spec is not (), Only keep this if not in our spec if (spec and obj.portal_type not in spec) or not (
my_type = obj.portal_type checked_permission is None or
keep_it = (my_type not in spec) checkPermission(checked_permission, obj)):
if (not keep_it) and (checked_permission is not None): new_category_list.append(path)
keep_it = permissionFilter(obj) continue
# We must remember the default value for each replaced category
if keep_it: if keep_default and my_base_id not in default_base_category_set:
new_category_list.append(path) default_base_category_set.add(my_base_id)
elif keep_default: default_category_set.add(path)
# We must remember the default value
# for each replaced category
if not default_dict.has_key(my_base_id):
default_dict[my_base_id] = path
# We now create a list of default category values
default_new_category_list = []
for path in default_dict.values():
if base or len(base_category_list) > 1:
if path in category_list:
default_new_category_list.append(path)
else: else:
if path[len(base_category_list[0])+1:] in category_list: # Keep each membership which is not in the
default_new_category_list.append(path) # specified list of base_category ids
new_category_list.append(path)
# Before we append new category values (except default values) # Before we append new category values (except default values)
# We must make sure however that multiple links are possible # We must make sure however that multiple links are possible
default_path_found = {} base = '' if base or len(base_category_list) > 1 \
else base_category_list[0] + '/'
for path in category_list: for path in category_list:
if not path in ('', None): if path not in ('', None):
if base or len(base_category_list) > 1: if base:
# Only keep path which are member of base_category_list path = base + path
if self.getBaseCategoryId(path) in base_category_list: elif self.getBaseCategoryId(path) not in base_category_list:
if path not in default_new_category_list or default_path_found.has_key(path): continue
default_path_found[path] = 1 if path in default_category_set:
new_category_list.append(path) default_category_set.remove(path)
new_category_list.appendleft(path)
else: else:
new_path = '%s/%s' % (base_category_list[0], path) new_category_list.append(path)
if new_path not in default_new_category_list: self._setCategoryList(context, new_category_list)
new_category_list.append(new_path)
# LOG("CategoryTool, setCategoryMembership", 0 ,
# 'new_category_list: %s' % str(new_category_list))
# LOG("CategoryTool, setCategoryMembership", 0 ,
# 'default_new_category_list: %s' % str(default_new_category_list))
self._setCategoryList(context, tuple(default_new_category_list + new_category_list))
security.declareProtected( Permissions.AccessContentsInformation, 'setDefaultCategoryMembership' ) security.declareProtected( Permissions.AccessContentsInformation, 'setDefaultCategoryMembership' )
...@@ -739,7 +734,6 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -739,7 +734,6 @@ class CategoryTool( UniqueObject, Folder, Base ):
to filter the object on to filter the object on
""" """
self._cleanupCategories(context)
if isinstance(default_category, (tuple, list)): if isinstance(default_category, (tuple, list)):
default_category = default_category[0] default_category = default_category[0]
category_list = self.getCategoryMembershipList(context, base_category, category_list = self.getCategoryMembershipList(context, base_category,
...@@ -1182,35 +1176,28 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1182,35 +1176,28 @@ class CategoryTool( UniqueObject, Folder, Base ):
""" """
if getattr(aq_base(context), 'isCategory', 0): if getattr(aq_base(context), 'isCategory', 0):
return context.isAcquiredMemberOf(category) return context.isAcquiredMemberOf(category)
for c in self._getAcquiredCategoryList(context): for c in self.getAcquiredCategoryList(context):
if c.find(category) >= 0: if c.find(category) >= 0:
return 1 return 1
return 0 return 0
security.declareProtected( Permissions.AccessContentsInformation, 'getCategoryList' ) security.declareProtected( Permissions.AccessContentsInformation, 'getCategoryList' )
def getCategoryList(self, context): def getCategoryList(self, context):
self._cleanupCategories(context) result = getattr(aq_base(context), 'categories', None)
return self._getCategoryList(context) if result is not None:
result = list(result)
security.declareProtected( Permissions.AccessContentsInformation, '_getCategoryList' )
def _getCategoryList(self, context):
if getattr(aq_base(context), 'categories', _marker) is not _marker:
if isinstance(context.categories, tuple):
result = list(context.categories)
elif isinstance(context.categories, list):
result = context.categories
else:
result = []
elif isinstance(context, dict): elif isinstance(context, dict):
result = list(context.get('categories', [])) return list(context.get('categories', ()))
else: else:
result = [] result = []
if getattr(context, 'isCategory', 0): if getattr(context, 'isCategory', 0):
category_url = context.getRelativeUrl() category_url = context.getRelativeUrl()
if category_url not in result: if category_url not in result:
result.append(context.getRelativeUrl()) # Pure category is member of itself result.append(category_url) # Pure category is member of itself
return result return result
_getCategoryList = getCategoryList
security.declareProtected( Permissions.ModifyPortalContent, 'setCategoryList' ) security.declareProtected( Permissions.ModifyPortalContent, 'setCategoryList' )
def setCategoryList(self, context, value): def setCategoryList(self, context, value):
self._setCategoryList(context, value) self._setCategoryList(context, value)
...@@ -1218,45 +1205,39 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1218,45 +1205,39 @@ class CategoryTool( UniqueObject, Folder, Base ):
security.declareProtected( Permissions.ModifyPortalContent, '_setCategoryList' ) security.declareProtected( Permissions.ModifyPortalContent, '_setCategoryList' )
def _setCategoryList(self, context, value): def _setCategoryList(self, context, value):
context.categories = tuple(value) old = set(getattr(aq_base(context), 'categories', ()))
context.categories = value = tuple(value)
if context.isTempDocument():
return
value = set(value)
relative_url = context.getRelativeUrl()
for edit, value in ("remove", old - value), ("add", value - old):
for path in value:
base = self.getBaseCategoryId(path)
try:
if self[base].isRelatedLocallyIndexed():
path = self._removeDuplicateBaseCategoryIdInCategoryPath(base, path)
ob = aq_base(self.unrestrictedTraverse(path))
try:
related = ob._related_index
except AttributeError:
if edit is "remove":
continue
related = ob._related_index = RelatedIndex()
getattr(related, edit)(base, relative_url)
except KeyError:
pass
security.declareProtected( Permissions.AccessContentsInformation, 'getAcquiredCategoryList' ) security.declareProtected( Permissions.AccessContentsInformation, 'getAcquiredCategoryList' )
def getAcquiredCategoryList(self, context): def getAcquiredCategoryList(self, context):
"""
Returns the list of acquired categories
"""
self._cleanupCategories(context)
return self._getAcquiredCategoryList(context)
security.declareProtected( Permissions.AccessContentsInformation, '_getAcquiredCategoryList' )
def _getAcquiredCategoryList(self, context):
result = self.getAcquiredCategoryMembershipList(context, result = self.getAcquiredCategoryMembershipList(context,
base_category = self.getBaseCategoryList(context=context)) base_category = self.getBaseCategoryList(context=context))
append = result.append for c in self._getCategoryList(context):
non_acquired = self._getCategoryList(context)
for c in non_acquired:
# Make sure all local categories are considered # Make sure all local categories are considered
if c not in result: if c not in result:
append(c) result.append(c)
if getattr(context, 'isCategory', 0):
append(context.getRelativeUrl()) # Pure category is member of itself
return result return result
security.declareProtected( Permissions.ModifyPortalContent, '_cleanupCategories' )
def _cleanupCategories(self, context):
# Make sure _cleanupCategories does not modify objects each time it is called
# or we get many conflicts
requires_update = 0
categories = []
append = categories.append
if getattr(context, 'categories', _marker) is not _marker:
for cat in self._getCategoryList(context):
if isinstance(cat, str):
append(cat)
else:
requires_update = 1
if requires_update: self.setCategoryList(context, tuple(categories))
# Catalog related methods # Catalog related methods
def updateRelatedCategory(self, category, previous_category_url, new_category_url): def updateRelatedCategory(self, category, previous_category_url, new_category_url):
new_category = re.sub('^%s$' % new_category = re.sub('^%s$' %
...@@ -1349,7 +1330,6 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1349,7 +1330,6 @@ class CategoryTool( UniqueObject, Folder, Base ):
security.declareProtected( Permissions.AccessContentsInformation, security.declareProtected( Permissions.AccessContentsInformation,
'getRelatedValueList' ) 'getRelatedValueList' )
def getRelatedValueList(self, context, base_category_list=None, def getRelatedValueList(self, context, base_category_list=None,
spec=(), filter=None, base=1,
checked_permission=None, **kw): checked_permission=None, **kw):
""" """
This methods returns the list of objects related to the context This methods returns the list of objects related to the context
...@@ -1359,65 +1339,158 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1359,65 +1339,158 @@ class CategoryTool( UniqueObject, Folder, Base ):
portal_type = kw.get('portal_type') portal_type = kw.get('portal_type')
if isinstance(portal_type, str): if isinstance(portal_type, str):
portal_type = [portal_type] portal_type = portal_type,
if spec is ():
# We do not want to care about spec
spec = None
# Base Category may not be related, besides sub categories # Base Category may not be related, besides sub categories
if context.getPortalType() == 'Base Category': relative_url = context.getRelativeUrl()
category_list = [context.getRelativeUrl()] local_index_dict = {}
if isinstance(context, BaseCategory):
category_list = relative_url,
else: else:
category_list = []
if isinstance(base_category_list, str): if isinstance(base_category_list, str):
base_category_list = [base_category_list] base_category_list = base_category_list,
elif base_category_list is () or base_category_list is None: elif base_category_list is () or base_category_list is None:
base_category_list = self.getBaseCategoryList() base_category_list = self.getBaseCategoryList()
category_list = []
for base_category in base_category_list: for base_category in base_category_list:
category_list.append("%s/%s" % (base_category, context.getRelativeUrl())) if self[base_category].isRelatedLocallyIndexed():
category = base_category + '/'
sql_kw = {} local_index_dict[base_category] = '' \
for sql_key in ('limit', 'order_by_expression'): # XXX-JPS it would be better to use Catalog API if relative_url.startswith(category) else category
if sql_key in kw: else:
sql_kw[sql_key] = kw[sql_key] category_list.append("%s/%s" % (base_category, relative_url))
brain_result = self.Base_zSearchRelatedObjectsByCategoryList( search = self.getPortalObject().Base_zSearchRelatedObjectsByCategoryList
category_list=category_list, if local_index_dict:
portal_type=portal_type, # For some base categories, lookup indexes in ZODB.
strict_membership=strict_membership, recurse = isinstance(context, Category) and not strict_membership
**sql_kw) result_dict = {}
def check_local():
r = set(getattr(related, base_category, ()))
r.difference_update(result_dict)
for r in r:
try:
ob = self.unrestrictedTraverse(r)
if category in aq_base(ob).categories:
result_dict[r] = ob
continue
# Do not add 'r' to result_dict, because 'ob' may be linked in
# another way.
except (AttributeError, KeyError):
result_dict[r] = None
related.remove(base_category, r)
tv = getTransactionalVariable().setdefault(
'CategoriesTool.getRelatedValueList', {})
try:
related = aq_base(context)._related_index
except AttributeError:
related = RelatedIndex()
include_self = False
for base_category, category in local_index_dict.iteritems():
if not category:
# Categories are member of themselves.
include_self = True
result_dict[relative_url] = context
category += relative_url
if tv.get(category, -1) < recurse:
# Update local index with results from catalog for backward
# compatibility. But no need to do it several times in the same
# transaction.
for r in search(category_list=category,
portal_type=None,
strict_membership=strict_membership):
r = r.relative_url
# relative_url is empty if object is deleted (but not yet
# unindexed). Nothing specific to do in such case because
# category tool won't match.
try:
ob = self.unrestrictedTraverse(r)
categories = aq_base(ob).categories
except (AttributeError, KeyError):
result_dict[r] = None
continue
if category in categories:
related.add(base_category, r)
result_dict[r] = ob
elif recurse:
for p in categories:
if p.startswith(category + '/'):
try:
o = self.unrestrictedTraverse(p)
p = aq_base(o)._related_index
except KeyError:
continue
except AttributeError:
p = o._related_index = RelatedIndex()
result_dict[r] = ob
p.add(base_category, r)
tv[category] = recurse
# Get and check all objects referenced by local index for the base
# category that is currently considered.
check_local()
# Modify context only if it's worth it.
if related and not hasattr(aq_base(context), '_related_index'):
context._related_index = related
# In case of non-strict membership search, include all objects that
# are linked to a subobject of context.
if recurse:
r = [context]
while r:
for ob in r.pop().objectValues():
r.append(ob)
relative_url = ob.getRelativeUrl()
if include_self:
result_dict[relative_url] = ob
try:
related = aq_base(ob)._related_index
except AttributeError:
continue
for base_category, category in local_index_dict.iteritems():
category += relative_url
check_local()
# Filter out objects that are not of requested portal type.
result = [ob for ob in result_dict.itervalues() if ob is not None and (
not portal_type or ob.getPortalType() in portal_type)]
# Finish with base categories that are only indexed in catalog,
# making sure we don't return duplicate values.
if category_list:
for r in search(category_list=category_list,
portal_type=portal_type,
strict_membership=strict_membership):
if r.relative_url not in result_dict:
try:
result.append(self.unrestrictedTraverse(r.path))
except KeyError:
pass
result = []
if checked_permission is None:
# No permission to check
for b in brain_result:
o = b.getObject()
if o is not None:
result.append(o)
else: else:
# Check permissions on object # Catalog-only search.
if isinstance(checked_permission, str): result = []
checked_permission = (checked_permission, ) for r in search(category_list=category_list,
checkPermission = self.portal_membership.checkPermission portal_type=portal_type,
for b in brain_result: strict_membership=strict_membership):
obj = b.getObject() try:
if obj is not None: result.append(self.unrestrictedTraverse(r.path))
for permission in checked_permission: except KeyError:
if not checkPermission(permission, obj): pass
break
result.append(obj)
return result if checked_permission is None:
# XXX missing filter and **kw stuff return result
#return self.search_category(category_list=category_list,
# portal_type=spec) # Check permissions on object
# future implementation with brains, much more efficient if isinstance(checked_permission, str):
checked_permission = checked_permission,
checkPermission = self.portal_membership.checkPermission
def check(ob):
for permission in checked_permission:
if checkPermission(permission, ob):
return True
return filter(check, result)
security.declareProtected( Permissions.AccessContentsInformation, security.declareProtected( Permissions.AccessContentsInformation,
'getRelatedPropertyList' ) 'getRelatedPropertyList' )
def getRelatedPropertyList(self, context, base_category_list=None, def getRelatedPropertyList(self, context, base_category_list=None,
property_name=None, spec=(), property_name=None,
filter=None, base=1,
checked_permission=None, **kw): checked_permission=None, **kw):
""" """
This methods returns the list of property_name on objects This methods returns the list of property_name on objects
...@@ -1426,8 +1499,7 @@ class CategoryTool( UniqueObject, Folder, Base ): ...@@ -1426,8 +1499,7 @@ class CategoryTool( UniqueObject, Folder, Base ):
result = [] result = []
for o in self.getRelatedValueList( for o in self.getRelatedValueList(
context=context, context=context,
base_category_list=base_category_list, spec=spec, base_category_list=base_category_list,
filter=filter, base=base,
checked_permission=checked_permission, **kw): checked_permission=checked_permission, **kw):
result.append(o.getProperty(property_name, None)) result.append(o.getProperty(property_name, None))
return result return result
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
# #
############################################################################## ##############################################################################
from collections import deque
import unittest import unittest
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
...@@ -42,6 +43,30 @@ class TestCMFCategory(ERP5TypeTestCase): ...@@ -42,6 +43,30 @@ class TestCMFCategory(ERP5TypeTestCase):
region2 = 'europe/west/germany' region2 = 'europe/west/germany'
region_list = [region1, region2] region_list = [region1, region2]
category_dict = dict(
region = dict(
acquisition_base_category_list=('subordination','parent'),
acquisition_portal_type_list="python: ['Address', 'Organisation', 'Person']",
acquisition_mask_value=1,
acquisition_object_id_list=['default_address'],
contents=('europe', ('west', ('france', 'germany'))),
),
subordination = dict(
acquisition_portal_type_list="python: ['Career', 'Organisation']",
acquisition_object_id_list=['default_career'],
),
gender = dict(
fallback_base_category_list=['subordination'],
),
resource = dict(
),
test0 = dict(
),
test1 = dict(
contents=('a', ('ab', 'ac', ('acd',))),
),
)
def getTitle(self): def getTitle(self):
return "CMFCategory" return "CMFCategory"
...@@ -74,16 +99,44 @@ class TestCMFCategory(ERP5TypeTestCase): ...@@ -74,16 +99,44 @@ class TestCMFCategory(ERP5TypeTestCase):
portal = self.portal portal = self.portal
self.validateRules() self.validateRules()
portal_categories = self.getCategoriesTool()
for name, kw in self.category_dict.iteritems():
try:
bc = portal_categories[name]
except KeyError:
bc = portal_categories.newContent(name)
edit_kw = dict(
acquisition_copy_value=0,
acquisition_append_value=0,
acquisition_mask_value=0,
acquisition_portal_type_list="python: []",
related_locally_indexed=0)
edit_kw.update(kw)
queue = deque(((bc, edit_kw.pop('contents', ())),))
bc.edit(**edit_kw)
while queue:
parent, contents = queue.popleft()
for x in contents:
if type(x) is str:
try:
category = parent[x]
except KeyError:
category = parent.newContent(x)
else:
queue.append((category, x))
# This test creates Person inside Person and Organisation inside # This test creates Person inside Person and Organisation inside
# Organisation, so we modifiy type informations to allow anything inside # Organisation, so we modify type informations to allow anything inside
# Person and Organisation (we'll cleanup on teardown) # Person and Organisation (we'll cleanup on teardown).
self.getTypesTool().getTypeInfo('Person').filter_content_types = 0 self._original_categories = {}
organisation_ti = self.getTypesTool().getTypeInfo('Organisation') for portal_type, categories in (
organisation_ti.filter_content_types = 0 ('Person', []),
# we also enable 'destination' category on organisations ('Organisation', ['destination', 'resource'])):
self._organisation_categories = organisation_ti.getTypeBaseCategoryList() ti = self.getTypesTool().getTypeInfo(portal_type)
organisation_ti._setTypeBaseCategoryList(self._organisation_categories ti.filter_content_types = 0
+ ['destination', 'resource']) self._original_categories[portal_type] = x = ti.getTypeBaseCategoryList()
x += 'test0', 'test1'
ti._setTypeBaseCategoryList(x + categories)
# Make persons. # Make persons.
person_module = self.getPersonModule() person_module = self.getPersonModule()
...@@ -109,62 +162,14 @@ class TestCMFCategory(ERP5TypeTestCase): ...@@ -109,62 +162,14 @@ class TestCMFCategory(ERP5TypeTestCase):
sale_packing_list_module = portal.sale_packing_list_module sale_packing_list_module = portal.sale_packing_list_module
if self.id1 not in sale_packing_list_module.objectIds(): if self.id1 not in sale_packing_list_module.objectIds():
sale_packing_list_module.newContent(id=self.id1) sale_packing_list_module.newContent(id=self.id1)
# This set the acquisition for region
for bc in ('region', ):
if not hasattr(portal_categories, bc):
portal_categories.newContent(portal_type='Base Category',id=bc)
portal_categories[bc].setAcquisitionBaseCategoryList(('subordination','parent'))
portal_categories[bc].setAcquisitionPortalTypeList("python: ['Address', 'Organisation', 'Person']")
portal_categories[bc].setAcquisitionMaskValue(1)
portal_categories[bc].setAcquisitionCopyValue(0)
portal_categories[bc].setAcquisitionAppendValue(0)
portal_categories[bc].setAcquisitionObjectIdList(['default_address'])
if not 'europe' in portal_categories[bc].objectIds():
portal_categories[bc].newContent(id='europe',portal_type='Category')
big_region = portal_categories[bc]['europe']
# Now we have to include by hand no categories
if not 'west' in big_region.objectIds():
big_region.newContent(id='west',portal_type='Category')
region = big_region['west']
if not 'france' in region.objectIds():
region.newContent(id='france',portal_type='Category')
if not 'germany' in region.objectIds():
region.newContent(id='germany',portal_type='Category')
for bc in ('subordination', ):
if not hasattr(portal_categories, bc):
portal_categories.newContent(portal_type='Base Category',id=bc)
portal_categories[bc].setAcquisitionPortalTypeList("python: ['Career', 'Organisation']")
portal_categories[bc].setAcquisitionMaskValue(0)
portal_categories[bc].setAcquisitionCopyValue(0)
portal_categories[bc].setAcquisitionAppendValue(0)
portal_categories[bc].setAcquisitionObjectIdList(['default_career'])
for bc in ('gender', ):
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)
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): def beforeTearDown(self):
"""Clean up.""" """Clean up."""
# categories
for bc in ('region', 'subordination', 'gender', 'resource'):
bc_obj = self.getPortal().portal_categories[bc]
bc_obj.manage_delObjects()
# type informations # type informations
self.getTypesTool().getTypeInfo('Person').filter_content_types = 1 for portal_type, categories in self._original_categories.iteritems():
organisation_ti = self.getTypesTool().getTypeInfo('Organisation') ti = self.getTypesTool().getTypeInfo(portal_type)
organisation_ti.filter_content_types = 1 ti.filter_content_types = 1
organisation_ti = self._organisation_categories ti._setTypeBaseCategoryList(categories)
def login(self): def login(self):
uf = self.portal.acl_users uf = self.portal.acl_users
...@@ -1075,6 +1080,76 @@ class TestCMFCategory(ERP5TypeTestCase): ...@@ -1075,6 +1080,76 @@ class TestCMFCategory(ERP5TypeTestCase):
# Check indexation # Check indexation
self.tic() self.tic()
def test_setCategoryMemberShip(self):
person = self.getPersonModule().newContent(portal_type='Person')
category_tool = self.getCategoriesTool()
bc = category_tool.newContent()
bc.newContent('a')
bc.newContent('b')
base = (bc.id + '/').__add__
def get(*args, **kw):
return category_tool.getCategoryMembershipList(person, *args, **kw)
def _set(*args, **kw):
return category_tool._setCategoryMembership(person, *args, **kw)
_set(bc.id, list('aa'))
self.assertEqual(get(bc.id), list('aa'))
_set(bc.id, list('baa'))
self.assertEqual(get(bc.id), list('aba'))
_set(bc.id, map(base, 'bb'), 1)
self.assertEqual(get(bc.id), list('bb'))
_set(bc.id, map(base, 'abb'), 1)
self.assertEqual(get(bc.id), list('bab'))
_set(bc.id, ())
def test_relatedIndex(self):
category_tool = self.getCategoriesTool()
newOrganisation = self.getOrganisationModule().newContent
organisation = newOrganisation()
other_organisation = newOrganisation(destination_value=organisation)
person = self.getPersonModule().newContent(test0_value=organisation,
test1='a/ac/acd')
self.tic()
get = organisation.getTest0RelatedValueList
a = category_tool.test1.a
def check():
self.assertEqual([person, other_organisation],
category_tool.getRelatedValueList(organisation))
self.assertEqual([person], get())
self.assertEqual([person], get(portal_type='Person'))
self.assertEqual([], get(portal_type='Organisation'))
self.assertEqual([person], a.getTest1RelatedValueList(
portal_type='Person'))
self.assertEqual([a], a.getTest1RelatedValueList(
strict_membership=True))
self.assertEqual([person], a.ac.acd.getTest1RelatedValueList(
portal_type='Person', strict_membership=True))
category_tool.test0._setRelatedLocallyIndexed(True)
category_tool.test1._setRelatedLocallyIndexed(True)
check()
related_list = sorted(a.getTest1RelatedList())
self.assertTrue(person.getRelativeUrl() in related_list)
self.assertEqual(related_list, sorted(x.getRelativeUrl()
for x in self.portal.portal_catalog(test1_uid=a.getUid())))
related = organisation._related_index
self.assertTrue(related)
self.assertEqual([person.getRelativeUrl()], list(related.test0))
person.unindexObject()
self.tic()
category_tool.test0._setRelatedLocallyIndexed(False)
self.assertEqual([], get())
category_tool.test0._setRelatedLocallyIndexed(True)
check()
person.categories = tuple(x for x in person.categories
if not x.startswith('test0/'))
self.assertEqual([], get())
self.assertFalse(related)
self.assertEqual([], list(related.test0))
related = a.ac.acd._related_index.test1
self.assertEqual(list(related), [person.getRelativeUrl()])
person._setTest1Value(a)
self.assertEqual(list(related), [])
def test_suite(): def test_suite():
suite = unittest.TestSuite() suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestCMFCategory)) suite.addTest(unittest.makeSuite(TestCMFCategory))
......
...@@ -544,7 +544,8 @@ class BaseTemplateItem(Implicit, Persistent): ...@@ -544,7 +544,8 @@ class BaseTemplateItem(Implicit, Persistent):
klass = obj.__class__ klass = obj.__class__
classname = klass.__name__ classname = klass.__name__
attr_set = set(('_dav_writelocks', '_filepath', '_owner', 'last_id', 'uid', attr_set = set(('_dav_writelocks', '_filepath', '_owner', '_related_index',
'last_id', 'uid',
'__ac_local_roles__', '__ac_local_roles_group_id_dict__')) '__ac_local_roles__', '__ac_local_roles_group_id_dict__'))
if export: if export:
if not keep_workflow_history: if not keep_workflow_history:
......
...@@ -40,6 +40,7 @@ from Products.ERP5Type.Core.Folder import OFS_HANDLER ...@@ -40,6 +40,7 @@ from Products.ERP5Type.Core.Folder import OFS_HANDLER
from Products.ERP5Type.CopySupport import CopyContainer from Products.ERP5Type.CopySupport import CopyContainer
from Products.CMFCore.utils import getToolByName from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.Cache import caching_instance_method from Products.ERP5Type.Cache import caching_instance_method
from Products.ERP5Type.dynamic import portal_type_class
from zLOG import LOG from zLOG import LOG
...@@ -84,54 +85,10 @@ class CategoryTool(CopyContainer, CMFCategoryTool, BaseTool): ...@@ -84,54 +85,10 @@ class CategoryTool(CopyContainer, CMFCategoryTool, BaseTool):
def hasContent(self,id): def hasContent(self,id):
return id in self.objectIds() return id in self.objectIds()
security.declareProtected(Permissions.AccessContentsInformation, 'getCategoryParentUidList') @caching_instance_method(
def getCategoryParentUidList(self, relative_url, base_category = None, strict=0): id='portal_categories.getBaseCategoryDict',
""" cache_factory='erp5_content_long',
Returns the uids of all categories provided in categorie. This cache_id_generator=lambda *a: portal_type_class.last_sync)
method can support relative_url such as site/group/a/b/c which
base category is site yet use categories defined in group.
It is also able to use acquisition to create complex categories
such as site/group/a/b/c/b1/c1 where b and b1 are both children
categories of a.
relative_url -- a single relative url of a list of
relative urls
strict -- if set to 1, only return uids of parents, not
relative_url
"""
uid_dict = {}
if type(relative_url) is type('a'): relative_url = (relative_url,)
for path in relative_url:
try:
o = self.getCategoryValue(path, base_category=base_category)
if o is not None:
if base_category is None:
my_base_category = self.getBaseCategoryId(path)
else:
my_base_category = base_category
bo = getattr(self, my_base_category, None)
if bo is not None:
bo_uid = bo.getUid()
uid_dict[(o.getUid(), bo_uid, 1)] = 1 # Strict membership
if o.meta_type == 'ERP5 Category' or o.meta_type == 'ERP5 Base Category' or \
o.meta_type == 'CMF Category' or o.meta_type == 'CMF Base Category':
# This goes up in the category tree
# XXX we should also go up in some other cases....
# ie. when some documents act as categories
if not strict:
while o.meta_type == 'ERP5 Category' or o.meta_type == 'CMF Category':
o = o.aq_parent # We want acquisition here without aq_inner
uid_dict[(o.getUid(), bo_uid, 0)] = 1 # Non strict
except (TypeError, KeyError):
LOG('WARNING: CategoriesTool',0, 'Unable to find uid for %s' % path)
return uid_dict.keys()
security.declareProtected(Permissions.AccessContentsInformation, 'getUids')
getUids = getCategoryParentUidList
@caching_instance_method(id='portal_categories.getBaseCategoryDict', cache_factory='erp5_content_long', cache_id_generator=lambda m, *a, **k:m)
def getBaseCategoryDict(self): def getBaseCategoryDict(self):
""" """
Cached method to which resturns a dict with category names as keys, and None as values. Cached method to which resturns a dict with category names as keys, and None as values.
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_body</string> </key>
<value> <string>return not (value and (\n
request.other[\'field_my_acquisition_object_id_list\'] or\n
request.other[\'field_my_acquisition_base_category_list\']))\n
</string> </value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>value, request</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>BaseCategory_validateRelatedLocallyIndexed</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -105,6 +105,7 @@ ...@@ -105,6 +105,7 @@
<key> <string>right</string> </key> <key> <string>right</string> </key>
<value> <value>
<list> <list>
<string>my_related_locally_indexed</string>
<string>my_acquisition_copy_value</string> <string>my_acquisition_copy_value</string>
<string>my_acquisition_mask_value</string> <string>my_acquisition_mask_value</string>
<string>my_acquisition_append_value</string> <string>my_acquisition_append_value</string>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ProxyField" module="Products.ERP5Form.ProxyField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>delegated_list</string> </key>
<value>
<list>
<string>external_validator</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>delegated_message_list</string> </key>
<value>
<list>
<string>external_validator_failed</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>my_related_locally_indexed</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>Local index is incompatible with category acquision.</string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>overrides</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>tales</string> </key>
<value>
<dictionary>
<item>
<key> <string>field_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_checkbox</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewFieldLibrary</string> </value>
</item>
<item>
<key> <string>target</string> </key>
<value> <string>Click to edit the target</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Index Related Documents Locally</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Method" module="Products.Formulator.MethodField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>method_name</string> </key>
<value> <string>BaseCategory_validateRelatedLocallyIndexed</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -58,15 +58,11 @@ order_by_expression</string> </value> ...@@ -58,15 +58,11 @@ order_by_expression</string> </value>
<key> <string>src</string> </key> <key> <string>src</string> </key>
<value> <string encoding="cdata"><![CDATA[ <value> <string encoding="cdata"><![CDATA[
SELECT DISTINCT catalog.uid, catalog.path, portal_type\n SELECT DISTINCT catalog.uid, path, relative_url, portal_type\n
FROM catalog, category\n FROM catalog, category\n
WHERE catalog.uid = category.uid\n WHERE catalog.uid = category.uid\n
<dtml-if portal_type>\n <dtml-if portal_type>\n
AND\n AND <dtml-sqltest portal_type type="string" multiple>\n
(<dtml-in portal_type>\n
<dtml-unless sequence-start> OR </dtml-unless>\n
catalog.portal_type=\'<dtml-var sequence-item>\'\n
</dtml-in>)\n
</dtml-if>\n </dtml-if>\n
AND (<dtml-var "portal_categories.buildSQLSelector(category_list)">)\n AND (<dtml-var "portal_categories.buildSQLSelector(category_list)">)\n
<dtml-if strict_membership>\n <dtml-if strict_membership>\n
......
41060 41061
\ No newline at end of file \ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Standard Property" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_local_properties</string> </key>
<value>
<tuple>
<dictionary>
<item>
<key> <string>id</string> </key>
<value> <string>mode</string> </value>
</item>
<item>
<key> <string>type</string> </key>
<value> <string>string</string> </value>
</item>
</dictionary>
</tuple>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>elementary_type/boolean</string>
</tuple>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Determines if related values should be indexed on target documents (i.e. in ZODB) in addition to catalog.\n
This is incompatible with category acquisition.</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>related_locally_indexed_property</string> </value>
</item>
<item>
<key> <string>mode</string> </key>
<value> <string>w</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Standard Property</string> </value>
</item>
<item>
<key> <string>property_default</string> </key>
<value> <string>python: 0</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
56 57
\ No newline at end of file \ No newline at end of file
...@@ -862,20 +862,19 @@ class TestResource(ERP5TypeTestCase): ...@@ -862,20 +862,19 @@ class TestResource(ERP5TypeTestCase):
self.tic() self.tic()
# Test the cases # Test the cases
for product, variation, node, base_price in test_case_list: for product, variation, node, base_price in test_case_list:
categories = []
if node is not None: if node is not None:
self.logMessage("Check product %s with destination section %s" % \ self.logMessage("Check product %s with destination section %s" % \
(product.getTitle(), node.getTitle()), (product.getTitle(), node.getTitle()),
tab=1) tab=1)
self.assertEquals(base_price, categories.append('destination_section/' + node.getRelativeUrl())
product.getPrice(
categories=['destination_section/%s' % node.getRelativeUrl(),
variation]))
else: else:
self.logMessage("Check product %s without destination section" % \ self.logMessage("Check product %s without destination section" % \
product.getTitle(), product.getTitle(),
tab=1) tab=1)
self.assertEquals(base_price, if variation:
product.getPrice(categories=[variation])) categories.append(variation)
self.assertEqual(base_price, product.getPrice(categories=categories))
# The following test tests Movement.getPrice, which is based on the movement # The following test tests Movement.getPrice, which is based on the movement
......
...@@ -48,8 +48,7 @@ class SetSetter(BaseSetter): ...@@ -48,8 +48,7 @@ class SetSetter(BaseSetter):
def __call__(self, instance, *args, **kw): def __call__(self, instance, *args, **kw):
if self._warning: if self._warning:
LOG("ERP5Type Deprecated Setter Id:",0, self._id) LOG("ERP5Type Deprecated Setter Id:",0, self._id)
value = set(args[0]) instance._setValue(self._key, set(args[0]),
instance._setValue(self._key, value,
spec=kw.get('spec',()), spec=kw.get('spec',()),
filter=kw.get('filter', None), filter=kw.get('filter', None),
portal_type=kw.get('portal_type',()), portal_type=kw.get('portal_type',()),
...@@ -451,7 +450,7 @@ class UidSetSetter(BaseSetter): ...@@ -451,7 +450,7 @@ class UidSetSetter(BaseSetter):
def __call__(self, instance, *args, **kw): def __call__(self, instance, *args, **kw):
if self._warning: if self._warning:
LOG("ERP5Type Deprecated Getter Id:",0, self._id) LOG("ERP5Type Deprecated Getter Id:",0, self._id)
instance._setValueUidList(self._key, args[0], instance._setValueUidList(self._key, set(args[0]),
spec=kw.get('spec',()), spec=kw.get('spec',()),
filter=kw.get('filter', None), filter=kw.get('filter', None),
portal_type=kw.get('portal_type',()), portal_type=kw.get('portal_type',()),
......
...@@ -2013,11 +2013,8 @@ class Base( CopyContainer, ...@@ -2013,11 +2013,8 @@ class Base( CopyContainer,
checked_permission=None): checked_permission=None):
# We must do an ordered list so we can not use the previous method # We must do an ordered list so we can not use the previous method
# self._setValue(id, self.portal_catalog.getObjectList(uids), spec=spec) # self._setValue(id, self.portal_catalog.getObjectList(uids), spec=spec)
references = [] references = map(self.getPortalObject().portal_catalog.getObject,
if type(uids) not in (type(()), type([])): (uids,) if isinstance(uids, (int, long)) else uids)
uids = [uids]
for uid in uids:
references.append(self.portal_catalog.getObject(uid))
self._setValue(id, references, spec=spec, filter=filter, portal_type=portal_type, self._setValue(id, references, spec=spec, filter=filter, portal_type=portal_type,
keep_default=keep_default, checked_permission=checked_permission) keep_default=keep_default, checked_permission=checked_permission)
...@@ -2178,9 +2175,6 @@ class Base( CopyContainer, ...@@ -2178,9 +2175,6 @@ class Base( CopyContainer,
""" """
return self._getCategoryTool().getAcquiredCategoryList(self) return self._getCategoryTool().getAcquiredCategoryList(self)
def _getAcquiredCategoryList(self):
return self._getCategoryTool()._getAcquiredCategoryList(self)
security.declareProtected( Permissions.ModifyPortalContent, 'setCategoryList' ) security.declareProtected( Permissions.ModifyPortalContent, 'setCategoryList' )
def setCategoryList(self, path_list): def setCategoryList(self, path_list):
self.portal_categories.setCategoryList(self, path_list) self.portal_categories.setCategoryList(self, path_list)
......
...@@ -722,27 +722,21 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor): ...@@ -722,27 +722,21 @@ class TestERP5Type(PropertySheetTestCase, LogInterceptor):
self.assertEquals(person.getDefaultRegion(), 'alpha') self.assertEquals(person.getDefaultRegion(), 'alpha')
person.setRegionUid(alpha.getUid()) person.setRegionUid(alpha.getUid())
self.assertEquals(person.getRegion(), 'alpha') self.assertEquals(person.getRegion(), 'alpha')
person.setRegionUidList([alpha.getUid(), alpha.getUid()]) person.setRegionUidList([beta.getUid(), beta.getUid()])
self.assertEquals(person.getRegionList(), ['alpha', 'alpha']) self.assertEquals(person.getRegionList(), ['beta', 'beta'])
person.setRegionUidSet([alpha.getUid(), alpha.getUid()]) person.setRegionUidSet([alpha.getUid(), alpha.getUid()])
self.assertEquals(person.getRegionSet(), ['alpha']) self.assertEquals(person.getRegionList(), ['alpha'])
person.setRegionUidList([alpha.getUid(), beta.getUid(), alpha.getUid()]) person.setRegionUidList([alpha.getUid(), beta.getUid(), alpha.getUid()])
self.assertEquals(person.getRegionList(), ['alpha', 'beta', 'alpha']) self.assertEquals(person.getRegionList(), ['alpha', 'beta', 'alpha'])
person.setRegionUidSet([alpha.getUid(), beta.getUid(), alpha.getUid()]) person.setRegionUidSet([alpha.getUid(), beta.getUid(), alpha.getUid()])
result = person.getRegionSet() self.assertEquals(sorted(person.getRegionSet()), ['alpha', 'beta'])
result.sort()
self.assertEquals(result, ['alpha', 'beta'])
person.setDefaultRegionUid(beta.getUid()) person.setDefaultRegionUid(beta.getUid())
self.assertEquals(person.getDefaultRegion(), 'beta') self.assertEquals(person.getDefaultRegion(), 'beta')
result = person.getRegionSet() self.assertEquals(sorted(person.getRegionSet()), ['alpha', 'beta'])
result.sort()
self.assertEquals(result, ['alpha', 'beta'])
self.assertEquals(person.getRegionList(), ['beta', 'alpha']) self.assertEquals(person.getRegionList(), ['beta', 'alpha'])
person.setDefaultRegionUid(alpha.getUid()) person.setDefaultRegionUid(alpha.getUid())
self.assertEquals(person.getDefaultRegion(), 'alpha') self.assertEquals(person.getDefaultRegion(), 'alpha')
result = person.getRegionSet() self.assertEquals(sorted(person.getRegionSet()), ['alpha', 'beta'])
result.sort()
self.assertEquals(result, ['alpha', 'beta'])
self.assertEquals(person.getRegionList(), ['alpha', 'beta']) self.assertEquals(person.getRegionList(), ['alpha', 'beta'])
# Test accessor on documents rather than on categories # Test accessor on documents rather than on categories
person.setDefaultRegionUid(person.getUid()) person.setDefaultRegionUid(person.getUid())
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment