Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Frederic Thoma
erp5
Commits
6bf9e694
Commit
6bf9e694
authored
Jan 26, 2013
by
Julien Muchembled
Browse files
Options
Browse Files
Download
Plain Diff
Merge support for ZODB indexing of category related documents
parents
a7780bc6
7865c58d
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
662 additions
and
323 deletions
+662
-323
product/CMFCategory/CategoryTool.py
product/CMFCategory/CategoryTool.py
+248
-176
product/CMFCategory/tests/testCMFCategory.py
product/CMFCategory/tests/testCMFCategory.py
+136
-61
product/ERP5/Document/BusinessTemplate.py
product/ERP5/Document/BusinessTemplate.py
+2
-1
product/ERP5/Tool/CategoryTool.py
product/ERP5/Tool/CategoryTool.py
+5
-48
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_validateRelatedLocallyIndexed.xml
.../erp5_core/BaseCategory_validateRelatedLocallyIndexed.xml
+69
-0
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_view.xml
...TemplateItem/portal_skins/erp5_core/BaseCategory_view.xml
+1
-0
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_view/my_related_locally_indexed.xml
...rp5_core/BaseCategory_view/my_related_locally_indexed.xml
+124
-0
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_zSearchRelatedObjectsByCategoryList.xml
...ns/erp5_core/Base_zSearchRelatedObjectsByCategoryList.xml
+2
-6
product/ERP5/bootstrap/erp5_core/bt/revision
product/ERP5/bootstrap/erp5_core/bt/revision
+1
-1
product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/BaseCategory/related_locally_indexed_property.xml
..._sheets/BaseCategory/related_locally_indexed_property.xml
+58
-0
product/ERP5/bootstrap/erp5_property_sheets/bt/revision
product/ERP5/bootstrap/erp5_property_sheets/bt/revision
+1
-1
product/ERP5/tests/testResource.py
product/ERP5/tests/testResource.py
+5
-6
product/ERP5Type/Accessor/Value.py
product/ERP5Type/Accessor/Value.py
+2
-3
product/ERP5Type/Base.py
product/ERP5Type/Base.py
+2
-8
product/ERP5Type/tests/testERP5Type.py
product/ERP5Type/tests/testERP5Type.py
+6
-12
No files found.
product/CMFCategory/CategoryTool.py
View file @
6bf9e694
...
@@ -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
):
re
sult
=
list
(
context
.
get
(
'categories'
,
[]
))
re
turn
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
(
c
ontext
.
getRelativeUrl
()
)
# Pure category is member of itself
result
.
append
(
c
ategory_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
...
...
product/CMFCategory/tests/testCMFCategory.py
View file @
6bf9e694
...
@@ -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
))
...
...
product/ERP5/Document/BusinessTemplate.py
View file @
6bf9e694
...
@@ -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
:
...
...
product/ERP5/Tool/CategoryTool.py
View file @
6bf9e694
...
@@ -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.
...
...
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_validateRelatedLocallyIndexed.xml
0 → 100644
View file @
6bf9e694
<?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>
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_view.xml
View file @
6bf9e694
...
@@ -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>
...
...
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/BaseCategory_view/my_related_locally_indexed.xml
0 → 100644
View file @
6bf9e694
<?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>
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_zSearchRelatedObjectsByCategoryList.xml
View file @
6bf9e694
...
@@ -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
...
...
product/ERP5/bootstrap/erp5_core/bt/revision
View file @
6bf9e694
41060
41061
\ No newline at end of file
\ No newline at end of file
product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/BaseCategory/related_locally_indexed_property.xml
0 → 100644
View file @
6bf9e694
<?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>
product/ERP5/bootstrap/erp5_property_sheets/bt/revision
View file @
6bf9e694
56
57
\ No newline at end of file
\ No newline at end of file
product/ERP5/tests/testResource.py
View file @
6bf9e694
...
@@ -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
...
...
product/ERP5Type/Accessor/Value.py
View file @
6bf9e694
...
@@ -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'
,()),
...
...
product/ERP5Type/Base.py
View file @
6bf9e694
...
@@ -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
)
...
...
product/ERP5Type/tests/testERP5Type.py
View file @
6bf9e694
...
@@ -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
(),
alph
a
.
getUid
()])
person
.
setRegionUidList
([
beta
.
getUid
(),
bet
a
.
getUid
()])
self
.
assertEquals
(
person
.
getRegionList
(),
[
'
alpha'
,
'alph
a'
])
self
.
assertEquals
(
person
.
getRegionList
(),
[
'
beta'
,
'bet
a'
])
person
.
setRegionUidSet
([
alpha
.
getUid
(),
alpha
.
getUid
()])
person
.
setRegionUidSet
([
alpha
.
getUid
(),
alpha
.
getUid
()])
self
.
assertEquals
(
person
.
getRegion
Se
t
(),
[
'alpha'
])
self
.
assertEquals
(
person
.
getRegion
Lis
t
(),
[
'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
())
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment