erp5_crm: Add support for deeper "use" resource hierachy.
Remove depth limitations on domain generator. Make ListField item list generator present resource hierarchically (either through indentation or through relative title-based paths, depending on preference). Also, share a bit of code between both.
... | ... | @@ -50,66 +50,41 @@ |
</item> | ||
<item> | ||
<key> <string>_body</string> </key> | ||
<value> <string encoding="cdata"><![CDATA[ | ||
portal = context.getPortalObject()\n | ||
request = context.REQUEST\n | ||
<value> <string>portal = context.getPortalObject()\n | ||
if depth:\n | ||
category_relative_url = parent.getMembershipCriterionCategory()\n | ||
else:\n | ||
category_relative_url = portal.portal_preferences.getPreference(\n | ||
\'preferred_\' + context.REQUEST[\'here\'].getPortalType().replace(\' Module\', \'\').lower().replace(\' \', \'_\') + \'_use\',\n | ||
)\n | ||
|
||
if not category_relative_url:\n | ||
return ()\n | ||
child_list, resource_list = portal.portal_categories.use.restrictedTraverse(category_relative_url).Category_getUseCategoryListAndResourceList()\n | ||
domain_list = []\n | ||
\n | ||
here = request[\'here\']\n | ||
portal_type = here.getPortalType().replace(\' Module\', \'\')\n | ||
\n | ||
preference_id = \'preferred_%s_use\' % \'_\'.join(token.lower() for token in portal_type.split(\' \'))\n | ||
preferred_use = portal.portal_preferences.getPreference(preference_id)\n | ||
if not preferred_use:\n | ||
return ()\n | ||
\n | ||
use = portal.portal_categories.use.restrictedTraverse(preferred_use)\n | ||
sub_use_list = use.contentValues()\n | ||
\n | ||
max_depth = 0\n | ||
if sub_use_list:\n | ||
max_depth = 1\n | ||
if depth > max_depth:\n | ||
return ()\n | ||
\n | ||
if sub_use_list:\n | ||
if depth == 0:\n | ||
for sub_use in sub_use_list:\n | ||
domain = parent.generateTempDomain(id = \'new_%s\' % sub_use.getProperty(\'uid\'))\n | ||
domain.edit(title=sub_use.getTranslatedTitle(),\n | ||
membership_criterion_category=(sub_use.getRelativeUrl(),),\n | ||
domain_generator_method_id=script.id,\n | ||
uid=use.getUid())\n | ||
domain.setCriterionPropertyList([\'related_resource_from_use_category_uid\'])\n | ||
domain.setCriterion(\'related_resource_from_use_category_uid\', identity=sub_use.getUid())\n | ||
domain_list.append(domain)\n | ||
return domain_list\n | ||
else:\n | ||
use = portal.portal_categories.use.restrictedTraverse(parent.getMembershipCriterionCategory())\n | ||
\n | ||
sql_kw = { \'portal_type\': portal.getPortalResourceTypeList(),\n | ||
\'use_uid\': use.getUid(),\n | ||
\'validation_state\': \'validated\',\n | ||
\'sort_on\': \'title\'}\n | ||
\n | ||
for service in portal.portal_catalog(**sql_kw):\n | ||
domain = parent.generateTempDomain(id = \'new_%s\' % service.getProperty(\'uid\'))\n | ||
domain.edit(title=service.getTranslatedTitle(),\n | ||
membership_criterion_base_category=(\'resource\', ),\n | ||
membership_criterion_category=(\'resource/%s\' % service.getRelativeUrl(),),\n | ||
domain_generator_method_id=script.id,\n | ||
uid=service.getUid())\n | ||
for child in child_list:\n | ||
domain = parent.generateTempDomain(id=child.getId())\n | ||
domain.edit(\n | ||
title=child.getTranslatedTitle(),\n | ||
membership_criterion_category=(child.getRelativeUrl(), ),\n | ||
domain_generator_method_id=script.id,\n | ||
)\n | ||
domain.setCriterionPropertyList([\'related_resource_from_use_category_uid\'])\n | ||
domain.setCriterion(\'related_resource_from_use_category_uid\', identity=child.getUid())\n | ||
domain_list.append(domain)\n | ||
\n | ||
return domain_list\n | ||
]]></string> </value> | ||
for resource in resource_list:\n | ||
domain = parent.generateTempDomain(id=resource.getId())\n | ||
domain.edit(\n | ||
title=resource.getTranslatedTitle(),\n | ||
membership_criterion_base_category=(\'resource\', ),\n | ||
membership_criterion_category=(\'resource/\' + resource.getRelativeUrl(), ),\n | ||
)\n | ||
domain_list.append(domain)\n | ||
return sorted(domain_list, key=lambda x: x.getTitle())\n | ||
</string> </value> | ||
</item> | ||
<item> | ||
<key> <string>_params</string> </key> | ||
<value> <string>depth, parent, **kw</string> </value> | ||
<value> <string>depth, parent</string> </value> | ||
</item> | ||
<item> | ||
<key> <string>id</string> </key> | ||
... | ... |
... | ... | @@ -51,52 +51,132 @@ |
<item> | ||
<key> <string>_body</string> </key> | ||
<value> <string>"""\n | ||
This script returns the list of items based on the preferred\n | ||
resources for tickets. It is intended to be used\n | ||
by ListField instances.\n | ||
portal_type (None, string)\n | ||
Used to determine "use" category to start recursing from, using preference\n | ||
settings for this portal type.\n | ||
When None, context\'s portal_type is used.\n | ||
empty_item (bool)\n | ||
Controls presence of [\'\', \'\'] element in result.\n | ||
indent_category (bool)\n | ||
When true, category captions are indented.\n | ||
When false, categories captions are paths, relative to topmost category (not\n | ||
necessarily a Base Category !).\n | ||
indent_resource (bool)\n | ||
When true, resource captions are indented.\n | ||
When false, resource captions are not indented.\n | ||
compact (bool)\n | ||
When true, getCompactTranslatedTitle is used to generate captions.\n | ||
When false, getTranslatedTitle is used to generate captions.\n | ||
empty_category (bool)\n | ||
When true, categories with no resource children (at any depth) are present\n | ||
in result.\n | ||
When false, categories with no resource children (at any depth) are pruned\n | ||
from result.\n | ||
\n | ||
When indent_category, indent_resource and compact are simultaneously not\n | ||
provided (or None), a default is built from\n | ||
getPreferredCategoryChildItemListMethodId.\n | ||
"""\n | ||
from zExceptions import Unauthorized\n | ||
# Note: a possible improvement would be to merge consecutive disabled entries.\n | ||
# This is difficult though, because it requires splitting work a lot,\n | ||
# increasing complexity significantly for such little improvement:\n | ||
# - non-child categories must not be concatenated (empty /1/12/ must not be\n | ||
# merged with a following /2/)\n | ||
# - all resource child must be properly indented\n | ||
# It is much simpler if only "empty_category=False" case is handled.\n | ||
from Products.ERP5Type.Cache import CachingMethod\n | ||
portal = context.getPortalObject()\n | ||
portal_preferences = portal.portal_preferences\n | ||
category_id = portal_preferences.getPreference(\n | ||
\'preferred_\' + (portal_type or context.getPortalType()).lower().replace(\' \', \'_\') + \'_use\',\n | ||
)\n | ||
if indent_category == indent_resource == compact == None:\n | ||
indent_category, indent_resource, compact = {\n | ||
\'getCategoryChildTranslatedCompactLogicalPathItemList\': (False, False, True),\n | ||
\'getCategoryChildTranslatedLogicalPathItemList\': (False, True, False),\n | ||
\'getCategoryChildTranslatedIndentedCompactTitleItemList\': (True, False, True),\n | ||
\'getCategoryChildTranslatedIndentedTitleItemList\': (True, True, False),\n | ||
}.get(portal_preferences.getPreferredCategoryChildItemListMethodId(), (True, True, False))\n | ||
\n | ||
if not portal_type:\n | ||
portal_type = context.getPortalType()\n | ||
\n | ||
def getResourceItemList(portal_type):\n | ||
preference_id = \'preferred_%s_use\' % \'_\'.join(token.lower() for token in portal_type.split(\' \'))\n | ||
sql_kw = {\'portal_type\': portal.getPortalResourceTypeList(),\n | ||
\'use_uid\': portal.portal_categories.getCategoryUid(portal.portal_preferences.getPreference(preference_id), base_category=\'use\'),\n | ||
\'validation_state\': \'validated\',\n | ||
\'sort_on\': \'title\'}\n | ||
return [(\'\', \'\')] + [(result.getTranslatedTitle(), result.getRelativeUrl()) for result in portal.portal_catalog(**sql_kw)]\n | ||
\n | ||
getResourceItemList = CachingMethod(getResourceItemList, \n | ||
id=(script.id, portal_type, context.Localizer.get_selected_language()), \n | ||
cache_factory=\'erp5_ui_long\')\n | ||
\n | ||
result_list = getResourceItemList(portal_type)[:]\n | ||
accessor_id = \'getCompactTranslatedTitle\' if compact else \'getTranslatedTitle\'\n | ||
\n | ||
# BBB returns actual value in list field\n | ||
if include_context and context.getResource() and context.getResource() not in [result[1] for result in result_list]:\n | ||
try:\n | ||
resource_value = portal.portal_categories.getCategoryValue(context.getResource(), base_category=\'resource\')\n | ||
if resource_value is not None:\n | ||
if resource_value.getPortalType() == \'Category\':\n | ||
category_relative_url = resource_value.getCategoryRelativeUrl()\n | ||
category_title = resource_value.getTranslatedTitle()\n | ||
else:\n | ||
category_relative_url = resource_value.getRelativeUrl()\n | ||
category_title = resource_value.getTitle()\n | ||
result_list.append((category_title, category_relative_url))\n | ||
except Unauthorized:\n | ||
pass\n | ||
def getResourceItemList():\n | ||
INDENT = \'\\xc2\\xa0\' * 2 # UTF-8 Non-breaking space\n | ||
RESOURCE_INDENT = INDENT if indent_resource else \'\'\n | ||
getResourceTitle = lambda resource, category, depth: RESOURCE_INDENT * depth + getattr(resource, accessor_id)()\n | ||
if indent_category:\n | ||
def getCategoryTitle(category, depth):\n | ||
return INDENT * depth + getattr(category, accessor_id)()\n | ||
else:\n | ||
def getCategoryTitle_(category, depth):\n | ||
result = []\n | ||
append = result.append\n | ||
for _ in xrange(depth + 1):\n | ||
append(getattr(category, accessor_id)())\n | ||
category = category.getParentValue()\n | ||
return \'/\'.join(result[::-1])\n | ||
if indent_resource:\n | ||
getCategoryTitle = getCategoryTitle_\n | ||
else:\n | ||
getCategoryTitle = lambda category, depth: None\n | ||
def getResourceTitle(resource, category, depth):\n | ||
resource_title = getattr(resource, accessor_id)()\n | ||
# depth - 1 because we are at category\'s child level\n | ||
category_path = getCategoryTitle_(category, depth - 1)\n | ||
if category_path:\n | ||
return category_path + \'/\' + resource_title\n | ||
return resource_title\n | ||
def recurse(category, depth):\n | ||
child_list, resource_list = category.Category_getUseCategoryListAndResourceList()\n | ||
# Resources before child categories, to avoid ambiguity when resources are not indented\n | ||
result = sorted(\n | ||
[(getResourceTitle(x, category, depth), x.getRelativeUrl()) for x in resource_list],\n | ||
key=lambda x: x[0],\n | ||
)\n | ||
append = result.append\n | ||
extend = result.extend\n | ||
for _, caption, grand_child_list in sorted(\n | ||
[(x.getIntIndex(), getCategoryTitle(x, depth), recurse(x, depth + 1)) for x in child_list],\n | ||
|
||
key=lambda x: x[:2],\n | ||
):\n | ||
if grand_child_list or empty_category:\n | ||
if caption is not None:\n | ||
append((caption, None))\n | ||
extend(grand_child_list)\n | ||
return result\n | ||
category = portal.portal_categories.getCategoryValue(category_id, base_category=\'use\')\n | ||
if category is None:\n | ||
return []\n | ||
return recurse(category, 0)\n | ||
\n | ||
return result_list\n | ||
result = CachingMethod(\n | ||
getResourceItemList,\n | ||
id=(\n | ||
script.id,\n | ||
context.Localizer.get_selected_language(),\n | ||
bool(indent_resource),\n | ||
bool(indent_category),\n | ||
accessor_id,\n | ||
bool(empty_category),\n | ||
category_id,\n | ||
),\n | ||
cache_factory=\'erp5_ui_long\',\n | ||
)()\n | ||
if empty_item:\n | ||
prefix = [(\'\', \'\')]\n | ||
else:\n | ||
prefix = []\n | ||
if include_context:\n | ||
context_resource_value = context.getResourceValue()\n | ||
context_resource = context.getResource()\n | ||
if context_resource_value is not None and context_resource not in [x for _, x in result]:\n | ||
prefix.append((getattr(context_resource_value, accessor_id)(), context_resource))\n | ||
return prefix + result\n | ||
</string> </value> | ||
</item> | ||
<item> | ||
<key> <string>_params</string> </key> | ||
<value> <string>portal_type=None, include_context=True</string> </value> | ||
<value> <string>portal_type=None, include_context=True, empty_item=True, indent_category=None, indent_resource=None, compact=None, empty_category=False</string> </value> | ||
</item> | ||
<item> | ||
<key> <string>id</string> </key> | ||
... | ... |