From 0f562b3e6e07889715021b941122c12f70030ec0 Mon Sep 17 00:00:00 2001
From: Romain Courteaud <romain@nexedi.com>
Date: Tue, 24 Nov 2015 14:37:30 +0000
Subject: [PATCH] [erp5_hal_json_style] Add a new worklist mode.

This allow to provide worklist information to a javascript application.
allDocs query parameter are provided to get list of document to work on.
---
 .../extension.erp5.HalStyle.py                | 30 +++++++-
 .../ERP5Document_getHateoas.xml               | 47 +++++++++++++
 .../WorkflowTool_listActionParameterList.xml  | 28 ++++++++
 .../test.erp5.testHalJsonStyle.py             | 70 +++++++++++++++++++
 4 files changed, 172 insertions(+), 3 deletions(-)
 create mode 100644 bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/WorkflowTool_listActionParameterList.xml

diff --git a/bt5/erp5_hal_json_style/ExtensionTemplateItem/portal_components/extension.erp5.HalStyle.py b/bt5/erp5_hal_json_style/ExtensionTemplateItem/portal_components/extension.erp5.HalStyle.py
index a2a67c564c..2f6e3af06f 100644
--- a/bt5/erp5_hal_json_style/ExtensionTemplateItem/portal_components/extension.erp5.HalStyle.py
+++ b/bt5/erp5_hal_json_style/ExtensionTemplateItem/portal_components/extension.erp5.HalStyle.py
@@ -8,9 +8,33 @@ def Listbox_getListMethodName(self, field):
 
   return list_method_name
 
-def Field_getSubFieldKeyDict(self, field, id, key=None):
+def Field_getSubFieldKeyDict(self, field, field_id, key=None):
   """XXX"""
-  return field.generate_subfield_key(id, key=key)
+  return field.generate_subfield_key(field_id, key=key)
 
 def Field_getDefaultValue(self, field, key, value, REQUEST):
-  return field._get_default(key, value, REQUEST)
\ No newline at end of file
+  return field._get_default(key, value, REQUEST)
+
+def WorkflowTool_listActionParameterList(self):
+  action_list = self.listActions()
+  info = self._getOAI(None)
+
+  workflow_dict = {}
+  result_list = []
+
+  for action in action_list:
+    if (action['workflow_id'] not in workflow_dict):
+      workflow = self.getWorkflowById(action['workflow_id'])
+      workflow_dict[action['workflow_id']] = workflow.getWorklistVariableMatchDict(info, check_guard=False)
+
+    query = workflow_dict[action['workflow_id']][action['worklist_id']]
+    query.pop('metadata')
+    result_list.append({
+      'count': action['count'],
+      'name': action['name'],
+      'local_roles': query.pop('local_roles'),
+      'query': query
+    })
+
+
+  return result_list
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
index 513b8bc2fa..037c59357d 100644
--- a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/ERP5Document_getHateoas.xml
@@ -76,6 +76,7 @@ url_template_dict = {\n
                        "{&relative_url,view}",\n
   "search_template": "%(root_url)s/%(script_id)s?mode=search" + \\\n
                      "{&query,select_list*,limit*,sort_on*,local_roles*}",\n
+  "worklist_template": "%(root_url)s/%(script_id)s?mode=worklist",\n
   "custom_search_template": "%(root_url)s/%(script_id)s?mode=search" + \\\n
                      "&relative_url=%(relative_url)s" \\\n
                      "&form_relative_url=%(form_relative_url)s" \\\n
@@ -866,6 +867,13 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
       if traversed_document_portal_type == "ERP5 Form":\n
         renderFormDefinition(traversed_document, result_dict)\n
         REQUEST.set("X-HATEOAS-CACHE", 1)\n
+      elif relative_url == \'portal_workflow\':\n
+        result_dict[\'_links\'][\'action_worklist\'] = {\n
+          "href": url_template_dict[\'worklist_template\'] % {\n
+            "root_url": site_root.absolute_url(),\n
+            "script_id": script.id\n
+          }\n
+        }\n
   \n
     # Define document action\n
     if action_dict:\n
@@ -1064,6 +1072,44 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
       return ""\n
     result_dict["result_list"] = [calculateHateoas(mode="traverse", **x) for x in json.loads(bulk_list)]\n
   \n
+  elif mode == \'worklist\':\n
+    #################################################\n
+    # Return all worklist jio urls\n
+    #################################################\n
+    if REQUEST.other[\'method\'] != "GET":\n
+      response.setStatus(405)\n
+      return ""\n
+    action_list = portal.portal_workflow.WorkflowTool_listActionParameterList()\n
+    work_list = []\n
+    for action in action_list:\n
+      query = sql_catalog.buildQuery(action[\'query\'])\\\n
+                         .asSearchTextExpression(sql_catalog)\n
+\n
+      if (action[\'local_roles\']):\n
+        # Hack to consider local_roles as a valid catalog parameter\n
+        role_query = sql_catalog.buildQuery({\'simulation_state\': action[\'local_roles\']})\\\n
+                                .asSearchTextExpression(sql_catalog)\n
+\n
+        query += \' AND %s\' % role_query.replace(\'simulation_state\', \'local_roles\')\n
+\n
+      portal_type = action[\'query\'].get(\'portal_type\', None)\n
+      if (portal_type):\n
+        if not same_type(portal_type, \'\'):\n
+          portal_type = portal_type[0]\n
+\n
+        work_list.append({\n
+          \'href\': url_template_dict["jio_search_template"] % {\n
+                    "query": make_query({"query": query})\n
+                  },\n
+          \'name\': action[\'name\'],\n
+          \'count\': action[\'count\'],\n
+          \'module\': default_document_uri_template % {\n
+                      "relative_url": portal.getDefaultModuleId(portal_type)\n
+                    }\n
+        })\n
+\n
+    result_dict["worklist"] = work_list\n
+\n
   else:\n
     raise NotImplementedError("Unsupported mode %s" % mode)\n
   \n
@@ -1072,6 +1118,7 @@ def calculateHateoas(is_portal=None, is_site_root=None, traversed_document=None,
 response.setHeader(\'Content-Type\', mime_type)\n
 hateoas = calculateHateoas(is_portal=temp_is_portal, is_site_root=temp_is_site_root,\n
                            traversed_document=temp_traversed_document,\n
+                           relative_url=relative_url,\n
                            REQUEST=REQUEST, response=response, view=view, mode=mode,\n
                            query=query, select_list=select_list, limit=limit, form=form,\n
                            restricted=restricted, list_method=list_method,\n
diff --git a/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/WorkflowTool_listActionParameterList.xml b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/WorkflowTool_listActionParameterList.xml
new file mode 100644
index 0000000000..fde1e66c24
--- /dev/null
+++ b/bt5/erp5_hal_json_style/SkinTemplateItem/portal_skins/erp5_hal_json_style/WorkflowTool_listActionParameterList.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_function</string> </key>
+            <value> <string>WorkflowTool_listActionParameterList</string> </value>
+        </item>
+        <item>
+            <key> <string>_module</string> </key>
+            <value> <string>HalStyle</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>WorkflowTool_listActionParameterList</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
index 442317ade4..8155f0398d 100644
--- a/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
+++ b/bt5/erp5_hal_json_style/TestTemplateItem/portal_components/test.erp5.testHalJsonStyle.py
@@ -47,6 +47,16 @@ def simulate(script_id, params_string, code_string):
     return decorated
   return upperWrap
 
+def createIndexedDocument():
+  def decorator(func):
+    def wrapped(self, *args, **kwargs):
+      self._makeDocument()
+      self.portal.portal_caches.clearAllCache()
+      self.tic()
+      return func(self, *args, **kwargs)
+    return wrapped
+  return decorator
+
 def do_fake_request(request_method, headers=None):
   __version__ = "0.1"
   if (headers is None):
@@ -436,6 +446,24 @@ class TestERP5Document_getHateoas_mode_traverse(ERP5HALJSONStyleSkinsMixin):
     self.assertEqual(result_dict['title'].encode("UTF-8"), document.getTitle())
     self.assertEqual(result_dict['_debug'], "traverse")
 
+  @simulate('Base_getRequestUrl', '*args, **kwargs',
+      'return "http://example.org/bar"')
+  @simulate('Base_getRequestHeader', '*args, **kwargs',
+            'return "application/hal+json"')
+  @changeSkin('Hal')
+  def test_getHateoasDocument_portal_workflow(self):
+    fake_request = do_fake_request("GET")
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(REQUEST=fake_request, mode="traverse", relative_url='portal_workflow')
+    self.assertEquals(fake_request.RESPONSE.status, 200)
+    self.assertEquals(fake_request.RESPONSE.getHeader('Content-Type'),
+      "application/hal+json"
+    )
+    result_dict = json.loads(result)
+    self.assertEqual(result_dict['_links']['self'], {"href": "http://example.org/bar"})
+
+    self.assertEqual(result_dict['_links']['action_worklist']['href'],
+                     "%s/web_site_module/hateoas/ERP5Document_getHateoas?mode=worklist" % self.portal.absolute_url())
+
   @simulate('Base_getRequestUrl', '*args, **kwargs',
       'return "http://example.org/bar"')
   @simulate('Base_getRequestHeader', '*args, **kwargs',
@@ -825,3 +853,45 @@ class TestERP5Document_getHateoas_mode_bulk(ERP5HALJSONStyleSkinsMixin):
                                                                                      self.portal.absolute_url(),
                                                                                      document.getRelativeUrl()))
     self.assertEqual(result_dict['result_list'][0]['_embedded']['_view']['_actions']['put']['method'], 'POST')
+
+class TestERP5Document_getHateoas_mode_worklist(ERP5HALJSONStyleSkinsMixin):
+
+  @simulate('Base_getRequestHeader', '*args, **kwargs',
+            'return "application/hal+json"')
+  @changeSkin('Hal')
+  def test_getHateoasWorklist_bad_method(self):
+    fake_request = do_fake_request("POST")
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(REQUEST=fake_request, mode="worklist")
+    self.assertEquals(fake_request.RESPONSE.status, 405)
+    self.assertEquals(result, "")
+
+
+  @simulate('Base_getRequestUrl', '*args, **kwargs',
+      'return "http://example.org/bar"')
+  @simulate('Base_getRequestHeader', '*args, **kwargs',
+            'return "application/hal+json"')
+  @createIndexedDocument()
+  @changeSkin('Hal')
+  def test_getHateoasWorklist_default_view(self):
+    # self._makeDocument()
+    fake_request = do_fake_request("GET")
+    result = self.portal.web_site_module.hateoas.ERP5Document_getHateoas(
+      REQUEST=fake_request,
+      mode="worklist"
+    )
+    self.assertEquals(fake_request.RESPONSE.status, 200)
+    self.assertEquals(fake_request.RESPONSE.getHeader('Content-Type'),
+      "application/hal+json"
+    )
+    result_dict = json.loads(result)
+    self.assertEqual(result_dict['_links']['self'], {"href": "http://example.org/bar"})
+
+    work_list = [x for x in result_dict['worklist'] if x['name'].startswith('Draft To Validate (')]
+    self.assertEqual(len(work_list), 1)
+    self.assertTrue(work_list[0]['count'] > 0)
+    self.assertEqual(work_list[0]['name'], 'Draft To Validate (%i)' % work_list[0]['count'])
+    self.assertEqual(work_list[0]['module'], 'urn:jio:get:bar_module')
+    self.assertEqual(work_list[0]['href'], 'urn:jio:allDocs?query=portal_type%3A%28%22Bar%22%20OR%20%22Foo%22%29%20AND%20simulation_state%3A%22draft%22')
+
+    self.assertEqual(result_dict['_debug'], "worklist")
+
-- 
2.30.9