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 @@ | ... | @@ -50,66 +50,41 @@ |
</item> | </item> | ||
<item> | <item> | ||
<key> <string>_body</string> </key> | <key> <string>_body</string> </key> | ||
<value> <string encoding="cdata"><![CDATA[ | <value> <string>portal = context.getPortalObject()\n | ||
if depth:\n | |||
portal = context.getPortalObject()\n | category_relative_url = parent.getMembershipCriterionCategory()\n | ||
request = context.REQUEST\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 | domain_list = []\n | ||
\n | for child in child_list:\n | ||
here = request[\'here\']\n | domain = parent.generateTempDomain(id=child.getId())\n | ||
portal_type = here.getPortalType().replace(\' Module\', \'\')\n | domain.edit(\n | ||
\n | title=child.getTranslatedTitle(),\n | ||
preference_id = \'preferred_%s_use\' % \'_\'.join(token.lower() for token in portal_type.split(\' \'))\n | membership_criterion_category=(child.getRelativeUrl(), ),\n | ||
preferred_use = portal.portal_preferences.getPreference(preference_id)\n | domain_generator_method_id=script.id,\n | ||
if not preferred_use:\n | )\n | ||
return ()\n | domain.setCriterionPropertyList([\'related_resource_from_use_category_uid\'])\n | ||
\n | domain.setCriterion(\'related_resource_from_use_category_uid\', identity=child.getUid())\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 | |||
domain_list.append(domain)\n | domain_list.append(domain)\n | ||
\n | for resource in resource_list:\n | ||
return domain_list\n | domain = parent.generateTempDomain(id=resource.getId())\n | ||
domain.edit(\n | |||
title=resource.getTranslatedTitle(),\n | |||
]]></string> </value> | 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> | ||
<item> | <item> | ||
<key> <string>_params</string> </key> | <key> <string>_params</string> </key> | ||
<value> <string>depth, parent, **kw</string> </value> | <value> <string>depth, parent</string> </value> | ||
</item> | </item> | ||
<item> | <item> | ||
<key> <string>id</string> </key> | <key> <string>id</string> </key> | ||
... | ... |
... | @@ -51,52 +51,132 @@ | ... | @@ -51,52 +51,132 @@ |
<item> | <item> | ||
<key> <string>_body</string> </key> | <key> <string>_body</string> </key> | ||
<value> <string>"""\n | <value> <string>"""\n | ||
This script returns the list of items based on the preferred\n | portal_type (None, string)\n | ||
resources for tickets. It is intended to be used\n | Used to determine "use" category to start recursing from, using preference\n | ||
by ListField instances.\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 | """\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 | from Products.ERP5Type.Cache import CachingMethod\n | ||
portal = context.getPortalObject()\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 | \n | ||
if not portal_type:\n | accessor_id = \'getCompactTranslatedTitle\' if compact else \'getTranslatedTitle\'\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 | |||
\n | \n | ||
# BBB returns actual value in list field\n | def getResourceItemList():\n | ||
if include_context and context.getResource() and context.getResource() not in [result[1] for result in result_list]:\n | INDENT = \'\\xc2\\xa0\' * 2 # UTF-8 Non-breaking space\n | ||
try:\n | RESOURCE_INDENT = INDENT if indent_resource else \'\'\n | ||
resource_value = portal.portal_categories.getCategoryValue(context.getResource(), base_category=\'resource\')\n | getResourceTitle = lambda resource, category, depth: RESOURCE_INDENT * depth + getattr(resource, accessor_id)()\n | ||
if resource_value is not None:\n | if indent_category:\n | ||
if resource_value.getPortalType() == \'Category\':\n | def getCategoryTitle(category, depth):\n | ||
category_relative_url = resource_value.getCategoryRelativeUrl()\n | return INDENT * depth + getattr(category, accessor_id)()\n | ||
category_title = resource_value.getTranslatedTitle()\n | else:\n | ||
else:\n | def getCategoryTitle_(category, depth):\n | ||
category_relative_url = resource_value.getRelativeUrl()\n | result = []\n | ||
category_title = resource_value.getTitle()\n | append = result.append\n | ||
result_list.append((category_title, category_relative_url))\n | for _ in xrange(depth + 1):\n | ||
except Unauthorized:\n | append(getattr(category, accessor_id)())\n | ||
pass\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 | \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> | </string> </value> | ||
</item> | </item> | ||
<item> | <item> | ||
<key> <string>_params</string> </key> | <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> | ||
<item> | <item> | ||
<key> <string>id</string> </key> | <key> <string>id</string> </key> | ||
... | ... |