Commit 8cf6c1d8 authored by Romain Courteaud's avatar Romain Courteaud

[erp5_core] Reactivate Change State of Multiple Documents action

Speed up all calculations.

Do not access the document to modify. Using their UID is enough.
Rely on the listbox which send the list of UIDs when submitting the dialog.

Calculate the list of possible transition by inspecting the portal_workflow configuration.

Do not revalidate the form box twice.

[erp5_core] Use _your validator prefix
parent 172d3457
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
</item> </item>
<item> <item>
<key> <string>visible</string> </key> <key> <string>visible</string> </key>
<value> <int>0</int> </value> <value> <int>1</int> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<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_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testMassWorkflowTransitionOnLimit</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Test RenderJS UI Module Action Change State (expected failure)</title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr><th rowspan="1" colspan="3">Test RenderJS UI Module Action Change State (expected failure)</th></tr>
</thead><tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<!-- Clean Up -->
<tr><td>open</td>
<td>${base_url}/foo_module/ListBoxZuite_reset</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Reset Successfully.</td><td></td></tr>
<!-- Shortcut for full renderjs url -->
<tr><td>store</td>
<td>${base_url}/web_site_module/renderjs_runner</td>
<td>renderjs_url</td></tr>
<tr><th colspan="3">Create Foo objects with distinguishible titles (Title 1%, Title 2%)</th></tr>
<tr><td>open</td>
<td>${base_url}/foo_module/FooModule_createObjects?start:int=1000&amp;num:int=40</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Created Successfully.</td><td></td></tr>
<tr><td>open</td>
<td>${base_url}/foo_module/FooModule_createObjects?start:int=2000&amp;num:int=40</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Created Successfully.</td><td></td></tr>
<tr><td>open</td>
<td>${base_url}/foo_module/FooModule_createObjects?start:int=3000&amp;num:int=40</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Created Successfully.</td><td></td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/wait_for_activities" />
<tr><td>open</td>
<td>${base_url}/FooModule_viewFooList/listbox/ListBox_setPropertyList?field_columns=id%7CID%0Atitle%7CTitle%0Asimulation_state%7CState&field_lines=3</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Set Successfully.</td><td></td></tr>
<tr><td>open</td>
<td>${renderjs_url}/#/foo_module</td><td></td></tr>
<tr><td>waitForElementPresent</td>
<td>//div[@data-role="header"]//a[@data-i18n="Actions"]</td><td></td></tr>
<tr><td>click</td>
<td>//div[@data-role="header"]//a[@data-i18n="Actions"]</td><td></td></tr>
<tr><td>waitForElementPresent</td>
<td>//ul[@data-role="listview"]//a[@data-i18n="Change State"]</td><td></td></tr>
<tr><td>click</td>
<td>//ul[@data-role="listview"]//a[@data-i18n="Change State"]</td><td></td></tr>
<tal:block tal:define="pagination_configuration python: {'header': '(1 - 3 / 120)', 'footer': 'Records 1 - 3 / 120'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/check_listbox_pagination_text" />
</tal:block>
<tr><td>waitForElementPresent</td>
<td>//select[@name="field_workflow_action"]</td><td></td></tr>
<tr><td>select</td>
<td>//select[@name="field_workflow_action"]</td>
<td>value=validate_action</td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'error', 'text': 'Too many documents selected! Submit again to proceed with the first 50 or Cancel and narrow down your search.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr><td>pause</td>
<td>3000</td><td></td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="notification_configuration python: {'class': 'success', 'text': 'Workflow modification in progress.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr><td>open</td>
<td>${base_url}/Zuite_waitForActivities</td><td></td></tr>
<tr><td>assertTextPresent</td>
<td>Done.</td><td></td></tr>
<tr><td>open</td>
<td>${renderjs_url}/#/foo_module</td><td></td></tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/triggle_filter" />
<tal:block tal:define="filter_section_configuration python: {'index': 0, 'key': 'COLUMN_simulation_state', 'value': 'draft'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/set_filter_section" />
</tal:block>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_filter" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block tal:define="pagination_configuration python: {'header': '(1 - 3 / 70)', 'footer': 'Records 1 - 3 / 70'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/check_listbox_pagination_text" />
</tal:block>
<!-- clear_query -->
</tbody></table>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<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_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testSelectAll</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode></unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -390,6 +390,43 @@ ...@@ -390,6 +390,43 @@
</tr> </tr>
</tal:block> </tal:block>
<tal:block metal:define-macro="go_to_history_view">
<tr>
<td colspan="3"><b>Go to history view</b></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[contains(@class, 'ui-header')]//a[@data-i18n='Views']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//div[contains(@class, 'ui-header')]//a[@data-i18n='Views']</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//ul[@class='document-listview']//a[@data-i18n='History']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//ul[@class='document-listview']//a[@data-i18n='History']</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_content_loaded" />
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_listbox_loaded" />
<tr>
<td>assertElementPresent</td>
<td>//div[@data-gadget-url='${base_url}/web_site_module/renderjs_runner/gadget_erp5_pt_report_view.html']</td>
<td></td>
</tr>
<tr>
<td colspan="3"><p></p></td>
</tr>
</tal:block>
<tal:block metal:define-macro="go_to_foo_relation_field_view"> <tal:block metal:define-macro="go_to_foo_relation_field_view">
<tr> <tr>
<td colspan="3"><b>Go to relation field view</b></td> <td colspan="3"><b>Go to relation field view</b></td>
......
"""Base_getFormIdForWorkflowAction returns form_id of a form (dialog) used by `workflow_action`
This is not an UI script - it should be used in TALES expressions or called internally.
Developer Notes:
Format of Action returned by getFilteredActions['workflow'] = [{
'available': True,
'visible': True,
'allowed': True,
'link_target': None,
'id': 'invalidate_action',
'category': 'workflow',
'name': 'Invalidate Action',
'title': 'Invalidate Action',
'url': 'https://softinst81338.host.vifib.net/erp5/web_site_module/renderjs_runner/foo_module/27/Base_viewWorkflowActionDialog?workflow_action=invalidate_action',
'transition': <TransitionDefinition at /erp5/portal_workflow/foo_workflow/id_form_dict/invalidate_action>,
'icon': ''}, ... ]
"""
action_tool = context.getPortalObject().portal_actions
id_form_dict = dict()
result_list = ()
if uids is not None:
result_list = context.getPortalObject().portal_catalog(uid=uids)
else:
result_list = context.Base_searchUsingFormIdAndQuery(form_id, query)
for result in result_list:
for action in action_tool.listFilteredActionsFor(result.getObject()).get('workflow', []):
action_form_id = action['url'].rsplit('/', 1)[1].split('?')[0]
id_form_dict[action['id']] = action_form_id
if workflow_action == action['id']:
return action_form_id # early return for performance reasons
if not workflow_action and len(id_form_dict) == 1:
# if we have only one possible workflow transition we suppose it is the default one
return id_form_dict.items()[0][1]
# if we have no idea what workflow form we should use - just use ~~the default one~~ nothing
return ""
"""Return a list of Documents specified by `uid` which provide `workflow_id` transition."""
action_tool = context.getPortalObject().portal_actions
result_list = context.Base_searchUsingListbox(
context.Base_getListbox(context.getPortalObject().REQUEST.get("form_id")), sort_on=sort_on, limit=limit, **kwargs)
if not workflow_action:
# if we have no filter (=workflow action) we return back all documents
return result_list
filtered_list = []
for document in (result.getObject() for result in result_list):
for action in action_tool.listFilteredActionsFor(document).get('workflow', []):
if action['id'] == workflow_action:
filtered_list.append(document)
break
return filtered_list
<?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>_params</string> </key>
<value> <string>workflow_action=\'\', sort_on=(), limit=None, **kwargs</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Folder_filterDocumentByWorkflow</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""Return items (ready to be used in listbox of listfield) of common workflows on documents defined by `form_id` and `query`.
:param form_id: {str} Form.ID of a form containing listbox to execute the query
:param query: {str} fulltext query
:param add_empty: {bool} First choice is empty
Developer Notes:
Format of Action returned by getFilteredActions['workflow'] = [{
'available': True,
'visible': True,
'allowed': True,
'link_target': None,
'id': 'invalidate_action',
'category': 'workflow',
'name': 'Invalidate Action',
'title': 'Invalidate Action',
'url': 'https://softinst81338.host.vifib.net/erp5/web_site_module/renderjs_runner/foo_module/27/Base_viewWorkflowActionDialog?workflow_action=invalidate_action',
'transition': <TransitionDefinition at /erp5/portal_workflow/foo_workflow/transition_dict/invalidate_action>,
'icon': ''}, ... ]
"""
def formatDict(d, padding=0):
if not isinstance(d, dict):
return " " * padding + str(d)
return "\n".join(" " * padding + key + ":" + formatDict(d[key], padding+4) for key in d)
action_tool = context.getPortalObject().portal_actions
translate = context.getPortalObject().Base_translateString
workflow_list = []
if add_empty:
workflow_list.append((translate('Choose desired action.'), ''))
transition_dict = {}
for result in context.Base_searchUsingFormIdAndQuery(form_id, query, limit=50):
for action in action_tool.listFilteredActionsFor(result.getObject()).get('workflow', []):
transition_dict[action['id']] = action['title']
if not transition_dict:
workflow_list.append((translate("No state change possible"), ""))
if len(transition_dict) == 1:
workflow_list = [] # if there is only one workflow possible - do not bother with an empty option
# transition_dict.items() is in form (id, title) but ERP5 requires (title, id) so we reverse
if transition_dict:
workflow_list.extend((title, id) for id, title in sorted(transition_dict.items(), key=lambda x: x[1]))
return workflow_list
"""Script to execute `workflow_action`on selected documents by `uids` with optional `comment`. """Script to execute `workflow_action`on selected documents by `listbox_uid` with optional `comment`.
This script is intended as a dialog target. This script is intended as a dialog target.
:param form_id, dialog_id: mandatory parameters of dialog form target script :param form_id, dialog_id: mandatory parameters of dialog form target script
:param uids: {list[int]} marks that this script takes objects from previous listbox as its input :param listbox_uid: {list[int]} marks that this script takes objects from previous listbox as its input
:param workflow_action: {str} the ID of (worflow) action to execute on each object :param mass_workflow_action: {str} the ID of (worflow) action to execute on each object
:param comment: {str} optional comment
""" """
from Products.CMFCore.WorkflowCore import WorkflowException
def stripMyYour(key):
if key.startswith("your_") or key.startswith("my_"):
return key.split("_", 1)[1]
MARKER = [] MARKER = []
portal = context.getPortalObject() portal = context.getPortalObject()
request = kwargs.get("REQUEST", None) or context.REQUEST request = kwargs.get("REQUEST", None) or context.REQUEST
translate = portal.Base_translateString translate = portal.Base_translateString
# Ensure the selected action is doable on received objects if not mass_workflow_action:
document_list = [result.getObject() for result in portal.portal_catalog.searchResults(uid=uids)]
workflowable_list = []
if not workflow_action:
return context.Base_redirect(form_id, return context.Base_redirect(form_id,
keep_items={ keep_items={
"portal_status_message": translate("No state change possible on selected documents."), "portal_status_message": translate("No state change possible on selected documents."),
"portal_status_level": "error"}) "portal_status_level": "error"})
# workflow_action_rendered is a control field to remember which workflow_action was rendered # previous_mass_workflow_action is a control field to remember which workflow_action was rendered
# and we diallow submit if different action is selected and different dialog embedded # and we disallow submit if different action is selected and different dialog embedded
request.form['workflow_action_rendered'] = workflow_action request.form['field_your_previous_mass_workflow_action'] = mass_workflow_action
# XXX This is hardcoded for the hidden field inside Base_viewWorkflowActionDialog
request.form['field_workflow_form_your_workflow_action'] = mass_workflow_action
if kwargs.get("update_method", ""): if update_method or (previous_mass_workflow_action != mass_workflow_action):
return context.Base_renderForm(dialog_id, return context.Base_renderForm(dialog_id,
message=translate("Form updated."), message=translate("Form updated."),
level="warning", level="warning",
REQUEST=request) REQUEST=request)
workflow_dialog = None
if workflow_action_rendered != workflow_action:
# if we get all fields for the workflow form - do not bother user and proceed
try:
workflow_dialog_id = context.Base_getFormIdForWorkflowAction(form_id, '', workflow_action, uids=uids)
workflow_dialog = getattr(context, workflow_dialog_id) # this can throw if form is not defined yet
for group in workflow_dialog.get_groups():
if group.lower() == 'hidden':
continue
for field in workflow_dialog.get_fields_in_group(group):
if request.form.get("field_workflow_dialog_" + field.id, MARKER) is MARKER:
raise AttributeError("field_workflow_dialog_" + field.id) # direct access request.form["key"] does not throw because publisher eats the exception
except AttributeError:
return context.Base_renderForm(dialog_id,
message=translate("Form updated."),
level="warning",
REQUEST=request)
for document in document_list:
try:
# Kato: Why does it throw an axception instead of just returning False?
portal.portal_workflow.canDoActionFor(document, workflow_action)
except WorkflowException as exception:
pass
else:
workflowable_list.append(document)
# generate a random tag for activity grouping # generate a random tag for activity grouping
tag = 'folder_workflow_action_{:d}'.format(random.randint(0, 1000)) # Kato: how come that random is accessible? tag = 'folder_workflow_action_{:d}'.format(random.randint(0, 1000)) # Kato: how come that random is accessible?
...@@ -73,22 +37,21 @@ tag = 'folder_workflow_action_{:d}'.format(random.randint(0, 1000)) # Kato: how ...@@ -73,22 +37,21 @@ tag = 'folder_workflow_action_{:d}'.format(random.randint(0, 1000)) # Kato: how
priority = 3 priority = 3
batch_size = 100 batch_size = 100
if workflow_dialog is None: workflow_action_kwargs = workflow_form.as_dict()
workflow_dialog_id = context.Base_getFormIdForWorkflowAction(form_id, '', workflow_action, uids=uids) workflow_action_kwargs['workflow_action'] = mass_workflow_action
workflow_dialog = getattr(context, workflow_dialog_id) workflow_action_kwargs['batch_mode'] = True
workflow_action_kwargs = workflow_dialog.validate_all(request, key_prefix='field_workflow_dialog')
workflow_action_kwargs = {stripMyYour(key): value for key, value in workflow_action_kwargs.items()}
workflow_action_kwargs['workflow_action'] = workflow_action
for i in xrange(0, len(workflowable_list), batch_size): portal.portal_catalog.searchAndActivate(
context.activate(activity='SQLQueue', priority=priority, tag=tag).callMethodOnObjectList( uid=listbox_uid,
[doc.getRelativeUrl() for doc in workflowable_list[i:i+batch_size]], method_id='Base_workflowStatusModify',
'Base_workflowStatusModify', activate_kw={'tag': tag, 'priority': priority},
batch_mode=True, **workflow_action_kwargs) packet_size=batch_size,
method_kw=workflow_action_kwargs
)
# activate something on the module after everything, so that user can know that # activate something on the module after everything, so that user can know that
# something is happening in the background # something is happening in the background
context.activate(after_tag=tag).getTitle() context.activate(after_tag=tag).getId()
return context.Base_redirect(form_id, return context.Base_redirect(form_id,
keep_items=dict(portal_status_message=translate("Workflow modification in progress."))) keep_items=dict(portal_status_message=translate("Workflow modification in progress.")))
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>form_id, dialog_id, uids, workflow_action, workflow_action_rendered, comment=\'\', **kwargs</string> </value> <value> <string>form_id, dialog_id, mass_workflow_action, previous_mass_workflow_action=None, listbox_uid=None, workflow_form=None, update_method=None, **kwargs</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -74,11 +74,9 @@ ...@@ -74,11 +74,9 @@
<key> <string>left</string> </key> <key> <string>left</string> </key>
<value> <value>
<list> <list>
<string>workflow_action</string> <string>your_mass_workflow_action</string>
<string>workflow_dialog</string> <string>workflow_form</string>
<string>workflow_dialog_my_workflow_action</string> <string>your_previous_mass_workflow_action</string>
<string>workflow_dialog_your_workflow_action</string>
<string>workflow_action_rendered</string>
</list> </list>
</value> </value>
</item> </item>
......
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
<key> <string>delegated_list</string> </key> <key> <string>delegated_list</string> </key>
<value> <value>
<list> <list>
<string>list_method</string> <string>count_method</string>
<string>default_params</string>
<string>enabled</string>
<string>lines</string>
<string>selection_name</string> <string>selection_name</string>
</list> </list>
</value> </value>
...@@ -50,17 +53,37 @@ ...@@ -50,17 +53,37 @@
<value> <value>
<dictionary> <dictionary>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>count_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default_params</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value> </value>
</item> </item>
<item> <item>
<key> <string>form_id</string> </key> <key> <string>enabled</string> </key>
<value> <value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent> <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value> </value>
</item> </item>
<item>
<key> <string>field_id</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>form_id</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
<item>
<key> <string>lines</string> </key>
<value> <string></string> </value>
</item>
<item> <item>
<key> <string>selection_name</string> </key> <key> <string>selection_name</string> </key>
<value> <string></string> </value> <value> <string></string> </value>
...@@ -72,6 +95,20 @@ ...@@ -72,6 +95,20 @@
<key> <string>values</string> </key> <key> <string>values</string> </key>
<value> <value>
<dictionary> <dictionary>
<item>
<key> <string>count_method</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>default_params</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>enabled</string> </key>
<value> <int>0</int> </value>
</item>
<item> <item>
<key> <string>field_id</string> </key> <key> <string>field_id</string> </key>
<value> <string>listbox</string> </value> <value> <string>listbox</string> </value>
...@@ -81,10 +118,8 @@ ...@@ -81,10 +118,8 @@
<value> <string>Folder_viewContentList</string> </value> <value> <string>Folder_viewContentList</string> </value>
</item> </item>
<item> <item>
<key> <string>list_method</string> </key> <key> <string>lines</string> </key>
<value> <value> <int>100</int> </value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item> </item>
<item> <item>
<key> <string>selection_name</string> </key> <key> <string>selection_name</string> </key>
...@@ -104,7 +139,7 @@ ...@@ -104,7 +139,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python: here.Base_getListbox(request.get("form_id")).getId()</string> </value> <value> <string>python: field.getTemplateField().get_value(\'default_params\') + here.Module_listWorkflowTransitionItemList()[\'listbox_parameter_dict\'][request[\'mass_workflow_action\']]</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
...@@ -117,20 +152,33 @@ ...@@ -117,20 +152,33 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>request/form_id</string> </value> <value> <string>python: request.get("mass_workflow_action")</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
</record> </record>
<record id="4" aka="AAAAAAAAAAQ="> <record id="4" aka="AAAAAAAAAAQ=">
<pickle> <pickle>
<global name="Method" module="Products.Formulator.MethodField"/> <global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python: here.Base_getListbox(request.get("form_id")).getId()</string> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
</pickle> </pickle>
<pickle> <pickle>
<dictionary> <dictionary>
<item> <item>
<key> <string>method_name</string> </key> <key> <string>_text</string> </key>
<value> <string>Folder_filterDocumentByWorkflow</string> </value> <value> <string>request/form_id</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>workflow_dialog</string> </value> <value> <string>workflow_form</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
...@@ -181,6 +181,10 @@ ...@@ -181,6 +181,10 @@
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>Workflow Dialog</string> </value> <value> <string>Workflow Dialog</string> </value>
</item> </item>
<item>
<key> <string>validator_form_field_prefix</string> </key>
<value> <string>your_</string> </value>
</item>
</dictionary> </dictionary>
</value> </value>
</item> </item>
...@@ -195,7 +199,7 @@ ...@@ -195,7 +199,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python: here.Base_getFormIdForWorkflowAction(request.get("form_id"), request.get("query"), request.get("workflow_action", ""))</string> </value> <value> <string>python: here.Module_getWorkflowTransitionDialogId(request)</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>workflow_action</string> </value> <value> <string>your_mass_workflow_action</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
...@@ -269,7 +269,7 @@ ...@@ -269,7 +269,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>_text</string> </key> <key> <string>_text</string> </key>
<value> <string>python:here.Folder_listWorkflow(request.get("form_id"), request.get("query"), add_empty=True)</string> </value> <value> <string>python:here.Module_listWorkflowTransitionItemList()[\'transition_item_list\']</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
<dictionary> <dictionary>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>workflow_dialog_my_workflow_action</string> </value> <value> <string>your_previous_mass_workflow_action</string> </value>
</item> </item>
<item> <item>
<key> <string>message_values</string> </key> <key> <string>message_values</string> </key>
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
</item> </item>
<item> <item>
<key> <string>title</string> </key> <key> <string>title</string> </key>
<value> <string>workflow_dialog_my_action</string> </value> <value> <string>Previous Action</string> </value>
</item> </item>
<item> <item>
<key> <string>truncate</string> </key> <key> <string>truncate</string> </key>
......
form_id_dict = context.Module_listWorkflowTransitionItemList()['form_id_dict']
# During rendering, this variable has been set into the request
# Render what user selected
action = request.get("mass_workflow_action", "")
if action:
return form_id_dict.get(action, '')
# Validate only if user didn't change the possible action
action = request.get("field_your_mass_workflow_action", "")
if (action and action == request.get("field_your_previous_mass_workflow_action", "")):
return form_id_dict.get(action, '')
return ''
...@@ -50,11 +50,11 @@ ...@@ -50,11 +50,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>form_id, query=\'\', workflow_action=\'\', uids=None</string> </value> <value> <string>request</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Base_getFormIdForWorkflowAction</string> </value> <value> <string>Module_getWorkflowTransitionDialogId</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
result = {
# items (ready to be used in a listfield items) of common workflow transition on a module's documents
'transition_item_list': [],
# form id for each transition
'form_id_dict': {},
# additional catalog parameter per transition
'listbox_parameter_dict': {}
}
TRIGGER_USER_ACTION = 1
portal = context.getPortalObject()
module = context
type_tool = portal.portal_types
workflow_tool = portal.portal_workflow
translate = portal.Base_translateString
module_portal_type = type_tool.getTypeInfo(module)
checked_workflow_id_dict = {}
for document_portal_type_id in module_portal_type.getTypeAllowedContentTypeList():
for workflow in workflow_tool.getWorkflowsFor(document_portal_type_id):
if workflow.id not in checked_workflow_id_dict:
# Do not check the same workflow twice
checked_workflow_id_dict[workflow.id] = None
state_variable = workflow.state_var
allowed_state_dict = {}
if getattr(workflow, 'states', None) is not None:
for state_id, state in workflow.states.items():
for possible_transition_id in state.transitions:
if possible_transition_id in allowed_state_dict:
allowed_state_dict[possible_transition_id].append(state_id)
else:
allowed_state_dict[possible_transition_id] = [state_id]
for transition_id in workflow.transitions:
transition = workflow.transitions[transition_id]
# Only display user action transition with a dialog to show to user
if (transition.trigger_type == TRIGGER_USER_ACTION) and (transition.actbox_url) and (transition.actbox_name):
action_form_id = transition.actbox_url.rsplit('/', 1)[1].split('?')[0]
result['transition_item_list'].append((translate(transition.actbox_name), transition_id))
result['form_id_dict'][transition_id] = action_form_id
# XXX portal_type parameter must also probably be added too
# This would required to detect identical transition id for different workflow
result['listbox_parameter_dict'][transition_id] = [(state_variable, allowed_state_dict[transition_id])]
result['transition_item_list'].sort()
result['transition_item_list'].insert(0, ('', ''))
return result
...@@ -56,11 +56,11 @@ ...@@ -56,11 +56,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>form_id, query, add_empty=False</string> </value> <value> <string></string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>Folder_listWorkflow</string> </value> <value> <string>Module_listWorkflowTransitionItemList</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
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