Commit 2e344ce2 authored by Jérome Perrin's avatar Jérome Perrin

Merge remote-tracking branch 'upstream/master' into zope4py3

parents d672329a 3f79e30e
......@@ -136,7 +136,6 @@ URL Crawler | view
Web Illustration | list
Web Illustration | local_permission
Web Illustration | related
Web Illustration | send_email
Web Illustration | view
Web Illustration | view_editor
Web Illustration | web_view
......@@ -145,7 +144,6 @@ Web Page | version_view
Web Table | list
Web Table | local_permission
Web Table | related
Web Table | send_email
Web Table | view
Web Table | view_editor
Web Table | web_view
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>send_email</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WebPage_viewSendByEmailDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Position Announcement Module | print_pdf
Position Announcement Module | view
Position Announcement | list
Position Announcement | send_email
Position Announcement | view
Position Announcement | view_editor
Position Module | print_pdf
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>send_email</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WebPage_viewSendByEmailDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -15,7 +15,6 @@ Test Page | launch
Test Page | list
Test Page | local_permission
Test Page | related
Test Page | send_email
Test Page | slideshow
Test Page | update_report
Test Page | verify_content
......
......@@ -26,7 +26,9 @@ def waitForActivities(self, delay=100, count=None):
if not x:
return 'Done.'
if all(x.processing_node == -2 for x in x):
break
raise RuntimeError(
'tic is looping forever: one failing activity (%s %s)' % (x[0].object_path, x[0].method_id)
)
activity_tool.process_timer(None, None)
if time.time() > deadline:
break
......
......@@ -6,10 +6,22 @@
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_recorded_property_dict</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>ERP5Zuite</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>extension.erp5.ERP5Zuite</string> </value>
......@@ -43,13 +55,28 @@
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
......@@ -62,7 +89,7 @@
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
</dictionary>
......@@ -71,7 +98,7 @@
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>send_email</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WebPage_viewSendByEmailDialog</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
for email in email_recipient_list:
context.MailHost.send("From: %s\nTo: %s\nContent-Type: text/plain;\n charset=\"utf-8\"\n\n\n%s" % (email_sender,email,email_text),
mto=email, mfrom=email_sender,
subject=email_title, encode='8bit')
return context.Base_redirect('view', keep_items={'portal_status_message': "Done"})
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</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>email_title, email_sender, email_text, email_recipient_list, **kw</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebPage_viewSendByEmail</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ERP5 Form" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary/>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>action</string> </key>
<value> <string>WebPage_viewSendByEmail</string> </value>
</item>
<item>
<key> <string>encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>enctype</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>group_list</string> </key>
<value>
<list>
<string>left</string>
<string>right</string>
<string>center</string>
<string>bottom</string>
<string>hidden</string>
</list>
</value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<dictionary>
<item>
<key> <string>bottom</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>center</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>hidden</string> </key>
<value>
<list/>
</value>
</item>
<item>
<key> <string>left</string> </key>
<value>
<list>
<string>your_email_title</string>
<string>your_email_sender</string>
<string>your_email_text</string>
<string>your_email_recipient_list</string>
</list>
</value>
</item>
<item>
<key> <string>right</string> </key>
<value>
<list/>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>WebPage_viewSendByEmailDialog</string> </value>
</item>
<item>
<key> <string>method</string> </key>
<value> <string>POST</string> </value>
</item>
<item>
<key> <string>name</string> </key>
<value> <string>WebPage_viewSendByEmailDialog</string> </value>
</item>
<item>
<key> <string>pt</string> </key>
<value> <string>form_dialog</string> </value>
</item>
<item>
<key> <string>row_length</string> </key>
<value> <int>4</int> </value>
</item>
<item>
<key> <string>stored_encoding</string> </key>
<value> <string>UTF-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
</item>
<item>
<key> <string>unicode_mode</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>update_action</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>title</string>
<string>height</string>
<string>width</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_email_recipient_list</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</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>field_id</string> </key>
<value> <string>my_lines_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>10</int> </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>Recipients</string> </value>
</item>
<item>
<key> <string>width</string> </key>
<value> <int>80</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>title</string>
<string>display_width</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_email_sender</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</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>display_width</string> </key>
<value> <int>40</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</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>Sender Email</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>default</string>
<string>height</string>
<string>title</string>
<string>whitespace_preserve</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_email_text</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</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>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<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>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_text_area_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</string> </value>
</item>
<item>
<key> <string>height</string> </key>
<value> <int>20</int> </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>Email Text</string> </value>
</item>
<item>
<key> <string>whitespace_preserve</string> </key>
<value> <int>0</int> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>python:here.asText()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?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>default</string>
<string>display_width</string>
<string>title</string>
</list>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>your_email_title</string> </value>
</item>
<item>
<key> <string>message_values</string> </key>
<value>
<dictionary>
<item>
<key> <string>external_validator_failed</string> </key>
<value> <string>The input failed the external validator.</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>default</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<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>default</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>display_width</string> </key>
<value> <int>80</int> </value>
</item>
<item>
<key> <string>field_id</string> </key>
<value> <string>my_string_field</string> </value>
</item>
<item>
<key> <string>form_id</string> </key>
<value> <string>Base_viewWebFieldLibrary</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>Email Title</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<tuple>
<global name="TALESMethod" module="Products.Formulator.TALESField"/>
<tuple/>
</tuple>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_text</string> </key>
<value> <string>here/getTitle</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -14,7 +14,6 @@ Static Web Site | view
Web Page Module | view
Web Page | list
Web Page | local_permission
Web Page | send_email
Web Page | view
Web Page | view_editor
Web Page | web_view
......
......@@ -167,7 +167,8 @@
function mergeGlobalActionWithRawActionList(jio_key, view, jump_view,
link_list, group_id_list,
command_mapping,
editable_mapping) {
editable_mapping,
filterMethod) {
var i, j, group,
action_type,
current_href,
......@@ -230,11 +231,20 @@
}
}
} else {
action_type = group;
group_mapping[group] = ensureArray(
link_list[group]
);
addRawUrlToGroupMapping(group, group + "_raw");
}
// Filter action to only keep action related to web site context
if (filterMethod !== undefined) {
for (j = group_mapping[action_type].length - 1; 0 <= j; j -= 1) {
if (!filterMethod(group_mapping[action_type][j])) {
group_mapping[action_type].splice(j, 1);
}
}
}
}
for (group in group_mapping) {
......
......@@ -236,7 +236,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>991.63236.62412.49203</string> </value>
<value> <string>1003.51428.45312.41796</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -256,7 +256,7 @@
</tuple>
<state>
<tuple>
<float>1620739824.54</float>
<float>1666341502.97</float>
<string>UTC</string>
</tuple>
</state>
......
......@@ -1631,6 +1631,9 @@ div.invisible_footer .document_table nav {
/**********************************************
* Radio field
**********************************************/
div[data-gadget-url$="gadget_erp5_field_radio.html"] {
display: inline-grid;
}
div[data-gadget-url$="gadget_erp5_field_radio.html"] label {
display: inline;
}
......
......@@ -248,7 +248,7 @@
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1009.18653.16090.52258</string> </value>
<value> <string>1013.58761.41247.2474</string> </value>
</item>
<item>
<key> <string>state</string> </key>
......@@ -268,7 +268,7 @@
</tuple>
<state>
<tuple>
<float>1687535887.84</float>
<float>1705411817.82</float>
<string>UTC</string>
</tuple>
</state>
......
div[data-gadget-url$="gadget_thread_reader.html"] {
max-width: 50em;
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol {
background-color: #FFFFFF;
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li {
padding-bottom: 12pt;
/*
&:nth-child(even) {
background-color: rgba(230, 230, 230, 0.65);
}
*/
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li > div.post_content {
display: inline-block;
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li > div.post_content > time {
color: hsl(0, 0%, 42%);
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li > div.post_content > div {
margin-top: 6pt;
max-width: 46em;
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li > div.post_avatar {
display: inline-block;
margin-right: 6pt;
width: 3em;
height: 3em;
line-height: 3em;
text-align: center;
border-radius: 50%;
background: #0E81C2;
color: #FFFFFF;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
vertical-align: top;
text-transform: uppercase;
}
div[data-gadget-url$="gadget_thread_reader.html"] > ol > li + li {
border-top: solid 1px #0E81C2;
padding-top: 6pt;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav {
display: flex;
padding-top: 6pt;
border-top: 2px solid rgba(0, 0, 0, 0.14902);
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav span {
opacity: 0.3;
flex: 2;
text-align: right;
float: right;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav a {
padding: 6pt;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: 0.325em;
background-color: #FFFFFF;
margin-right: 6pt;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav a::before {
margin-right: 6pt;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav a:hover,
div[data-gadget-url$="gadget_thread_reader.html"] > nav a:active {
background-color: #e0e0e0;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav a:last-of-type {
margin-right: 0;
}
div[data-gadget-url$="gadget_thread_reader.html"] > nav a:hover,
div[data-gadget-url$="gadget_thread_reader.html"] > nav a:active {
background-color: #e0e0e0;
}
@media not screen and (min-width: 45em) {
div[data-gadget-url$="gadget_thread_reader.html"] > nav a {
overflow: hidden;
text-indent: -9999px;
white-space: nowrap;
}
}
@media not screen and (min-width: 45em) {
div[data-gadget-url$="gadget_thread_reader.html"] > nav a::before {
float: left;
text-indent: 6pt;
}
}
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Style" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_thread_reader.css</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_thread_reader_css</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget Thread Reader CSS</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917544.17</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1011.24200.4035.15325</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1704208442.13</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917494.02</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<!DOCTYPE html>
<html>
<head>
<!--
data-i18n=No records
data-i18n=Records
data-i18n=Reset
data-i18n=Previous
data-i18n=Next
-->
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width" />
<title>Thread Reader</title>
<!-- renderjs -->
<link rel="stylesheet" href="gadget_thread_reader.css">
<script src="rsvp.js" type="text/javascript"></script>
<script src="renderjs.js" type="text/javascript"></script>
<script src="domsugar.js" type="text/javascript"></script>
<!--script src="jiodev.js" type="text/javascript"></script-->
<script src="gadget_global.js" type="text/javascript"></script>
<!-- custom script -->
<script src="gadget_thread_reader.js" type="text/javascript"></script>
</head>
<body>
<ol></ol>
<nav></nav>
</body>
</html>
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Page" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_thread_reader.html</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_thread_reader_html</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget Thread Reader</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917605.16</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1010.60920.5655.26692</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917613.92</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917561.24</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
/*jslint indent: 2, maxerr: 3, nomen: true */
/*global window, rJS, RSVP, console, domsugar, Intl, Query, SimpleQuery,
ComplexQuery*/
(function (window, rJS, RSVP, console, domsugar, Intl, Query, SimpleQuery,
ComplexQuery) {
"use strict";
function createMultipleSimpleOrQuery(key, value_list) {
var i,
query_list = [];
if (!Array.isArray(value_list)) {
value_list = [value_list];
}
for (i = 0; i < value_list.length; i += 1) {
query_list.push(new SimpleQuery({
key: key,
operator: "=",
type: "simple",
value: value_list[i]
}));
}
if (value_list.len === 1) {
return query_list[0];
}
return new ComplexQuery({
operator: "OR",
query_list: query_list,
type: "complex"
});
}
function getRelativeTimeString(language, current_date, date) {
var diff,
abs,
second = 1000,
minute = second * 60,
hour = minute * 60,
day = hour * 24,
week = day * 7,
time_format = new Intl.RelativeTimeFormat(language);
diff = date.getFullYear() - current_date.getFullYear();
if (diff !== 0) {
return time_format.format(diff, 'year');
}
diff = date - current_date;
abs = Math.abs(diff);
// "year", "quarter", "month", "week", "day", "hour", "minute", "second"
if (abs > (week * 2)) {
return time_format.format(Math.floor(diff / week), 'week');
}
if (abs > (day * 2)) {
return time_format.format(Math.floor(diff / day), 'day');
}
if (abs > (hour * 2)) {
return time_format.format(Math.floor(diff / hour), 'hour');
}
return time_format.format(Math.floor(diff / minute), 'minute');
}
function setPaginationElement(gadget, count, url_list) {
var disabled_suffix = ' ui-disabled',
span_dict,
first_dict = {
class: "ui-btn ui-icon-angle-double-left ui-btn-icon-left responsive ui-first-child",
text: "First"
},
previous_dict = {
class: "ui-btn ui-icon-angle-left ui-btn-icon-left responsive",
text: "Previous"
},
next_dict = {
class: "ui-btn ui-icon-angle-right ui-btn-icon-right responsive ui-last-child",
text: "Next"
};
if (url_list.length === 0) {
span_dict = {
class: "ui-icon-spinner ui-btn-icon-left"
};
} else {
span_dict = {
text: 'Page ' + Math.ceil((gadget.state.begin_from + count) / gadget.state.lines)
};
}
console.warn(gadget.state);
if (gadget.state.begin_from === 0) {
first_dict.class += disabled_suffix;
previous_dict.class += disabled_suffix;
} else {
first_dict.href = url_list[0];
previous_dict.href = url_list[1];
}
if (gadget.state.lines < count) {
next_dict.href = url_list[2];
} else {
next_dict.class += disabled_suffix;
}
// Set the pagination elements
domsugar(gadget.element.querySelector(':scope > nav'), [
domsugar('a', first_dict),
domsugar('a', previous_dict),
domsugar('a', next_dict),
domsugar('span', span_dict)
]);
}
rJS(window)
//////////////////////////////////////////////
// acquired method
//////////////////////////////////////////////
.declareAcquiredMethod("jio_allDocs", "jio_allDocs")
.declareAcquiredMethod("getUrlForList", "getUrlForList")
.declareAcquiredMethod("getUrlParameter", "getUrlParameter")
.declareAcquiredMethod("getTranslationList", "getTranslationList")
.declareAcquiredMethod('getSelectedLanguage', 'getSelectedLanguage')
//////////////////////////////////////////////
// initialize the gadget content
//////////////////////////////////////////////
.declareMethod('render', function render(options) {
var gadget = this;
// Cancel previous line rendering to not conflict with the asynchronous render for now
gadget.fetchLineContent(true);
return new RSVP.Queue(RSVP.hash({
language: gadget.getSelectedLanguage(),
begin_from: gadget.getUrlParameter(options.key + '_begin_from')
}))
.push(function (result_dict) {
return gadget.changeState({
key: options.key,
language: result_dict.language,
query_string: Query.objectToSearchText(
new ComplexQuery({
operator: "AND",
type: "complex",
query_list: Object.entries(options.query_dict)
.map(function (tuple) {
return createMultipleSimpleOrQuery(tuple[0], tuple[1]);
})
})
),
begin_from: parseInt(result_dict.begin_from || '0', 10) || 0,
lines: options.lines || 1,
date_column: options.date_column || 'modification_date',
source_column: options.source_column || 'source_title',
// Force line calculation in any case
render_timestamp: new Date().getTime(),
first_render: true,
allDocs_result: undefined
});
});
})
.onStateChange(function onStateChange(modification_dict) {
var gadget = this,
allDocs_result,
first_param,
prev_param,
next_param,
pagination_key;
if (!gadget.state.query_string) {
throw new Error('No "query_dict" defined for ' + gadget.state.key);
}
if (modification_dict.hasOwnProperty('first_render')) {
setPaginationElement(gadget, 0, []);
}
if (modification_dict.hasOwnProperty('render_timestamp')) {
domsugar(gadget.element.querySelector(':scope > nav > span'), {
class: "ui-icon-spinner ui-btn-icon-left",
text: ''
});
return gadget.fetchLineContent(false);
}
if (modification_dict.hasOwnProperty('allDocs_result')) {
allDocs_result = JSON.parse(gadget.state.allDocs_result);
pagination_key = gadget.state.key + '_begin_from';
first_param = {};
first_param[pagination_key] = undefined;
prev_param = {};
prev_param[pagination_key] = Math.max(0, gadget.state.begin_from - gadget.state.lines) || undefined;
next_param = {};
next_param[pagination_key] = gadget.state.begin_from + gadget.state.lines;
return new RSVP.Queue(RSVP.hash({
viewer_list: RSVP.all(allDocs_result.data.rows.map(function (entry, i) {
if (i === gadget.state.lines) {
return;
}
return gadget.declareGadget('gadget_html_viewer.html')
.push(function (viewer) {
return viewer.render({value: entry.value.asStrippedHTML})
.push(function () {
return viewer;
});
});
})),
url_list: gadget.getUrlForList([
{command: 'change', options: first_param},
{command: 'change', options: prev_param},
{command: 'change', options: next_param}
])
}))
.push(function (result_dict) {
var now = new Date();
domsugar(gadget.element.querySelector(':scope > ol'),
allDocs_result.data.rows.map(function (entry, i) {
if (i === gadget.state.lines) {
// Drop the last lines, in case we reached the +1 post value
// from allDocs, used to activate the pagination
return '';
}
var source_title = entry.value[gadget.state.source_column] || '',
attachment_list = entry.value
.DiscussionPost_getAttachmentList || [],
attachment_element_list = [],
j,
word_list = source_title.split(' '),
source_short_title;
if (word_list.length === 1) {
source_short_title = (word_list[0][0] || '?') + (word_list[0][1] || '');
} else {
source_short_title = word_list[0][0] + word_list[1][0];
}
for (j = 0; j < attachment_list.length; j += 1) {
attachment_element_list.push(
domsugar('li', [
domsugar('a', {
text: attachment_list[j].title,
href: attachment_list[j].url,
download: attachment_list[j].title
})
])
);
}
return domsugar('li', [
domsugar('div', {
class: 'post_avatar',
text: source_short_title
}),
domsugar('div', {
class: 'post_content'
}, [
domsugar('strong', {text: source_title}),
" ",
domsugar('time', {
datetime: entry.value[gadget.state.date_column],
title: entry.value[gadget.state.date_column],
text: getRelativeTimeString(
gadget.state.language,
now,
new Date(entry.value[gadget.state.date_column])
)
}),
domsugar('br'),
result_dict.viewer_list[i].element,
domsugar('br'),
domsugar('ul', attachment_element_list)
// domsugar('hr')
])
]);
}));
setPaginationElement(gadget, allDocs_result.data.total_rows,
result_dict.url_list);
});
}
})
.onLoop(function () {
// update relative time
var now = new Date(),
gadget = this;
this.element.querySelectorAll("div.post_content > time").forEach(
function (element) {
element.textContent = getRelativeTimeString(
gadget.state.language,
now,
new Date(element.getAttribute('datetime'))
);
}
);
// Loop every minute
}, 1000 * 60)
//////////////////////////////////////////////
// render the listbox in an asynchronous way
//////////////////////////////////////////////
.declareJob('fetchLineContent', function fetchLineContent(only_cancel) {
if (only_cancel) {
return;
}
var gadget = this,
limit_options = [];
if (gadget.state.lines === 0) {
limit_options = undefined;
} else {
limit_options = [gadget.state.begin_from, gadget.state.lines + 1];
}
return gadget.jio_allDocs({
query: gadget.state.query_string,
limit: limit_options,
select_list: ['asStrippedHTML', gadget.state.date_column,
gadget.state.source_column,
'DiscussionPost_getAttachmentList'],
sort_on: [[gadget.state.date_column, 'ASC'], ['uid', 'ASC']]
})
.push(function (result) {
return gadget.changeState({
allDocs_result: JSON.stringify(result)
});
});
})
.declareMethod("getContent", function getContent() {
return {};
})
.declareMethod("checkValidity", function checkValidity() {
return true;
});
}(window, rJS, RSVP, console, domsugar, Intl, Query, SimpleQuery,
ComplexQuery));
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Web Script" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_Access_contents_information_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Add_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Change_local_roles_Permission</string> </key>
<value>
<tuple>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_Modify_portal_content_Permission</string> </key>
<value>
<tuple>
<string>Assignee</string>
<string>Assignor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>_View_Permission</string> </key>
<value>
<tuple>
<string>Anonymous</string>
<string>Assignee</string>
<string>Assignor</string>
<string>Associate</string>
<string>Auditor</string>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>content_md5</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>default_reference</string> </key>
<value> <string>gadget_thread_reader.js</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>rjs_gadget_thread_reader_js</string> </value>
</item>
<item>
<key> <string>language</string> </key>
<value> <string>en</string> </value>
</item>
<item>
<key> <string>short_title</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Gadget Thread Reader JS</string> </value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>001</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>document_publication_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>edit_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
</value>
</item>
<item>
<key> <string>processing_status_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAU=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>publish_alive</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917441.67</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>published_alive</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="4" aka="AAAAAAAAAAQ=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>edit</string> </value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>1010.61000.1041.39372</string> </value>
</item>
<item>
<key> <string>state</string> </key>
<value> <string>current</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693928044.6</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="5" aka="AAAAAAAAAAU=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>actor</string> </key>
<value> <unicode>zope</unicode> </value>
</item>
<item>
<key> <string>comment</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>error_message</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>external_processing_state</string> </key>
<value> <string>empty</string> </value>
</item>
<item>
<key> <string>serial</string> </key>
<value> <string>0.0.0.0</string> </value>
</item>
<item>
<key> <string>time</string> </key>
<value>
<object>
<klass>
<global name="_reconstructor" module="copy_reg"/>
</klass>
<tuple>
<global name="DateTime" module="DateTime.DateTime"/>
<global name="object" module="__builtin__"/>
<none/>
</tuple>
<state>
<tuple>
<float>1693917390.89</float>
<string>UTC</string>
</tuple>
</state>
</object>
</value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -1911,6 +1911,7 @@ div[data-gadget-scope='erp5_searchfield'] {
* Radio field
**********************************************/
div[data-gadget-url$="gadget_erp5_field_radio.html"] {
display: inline-grid;
label {
// Ensure the label width do not use 100%
// in case of display block
......
@colorheaderbackground: #085078;
@colorsubheaderbackground: #0E81C2;
@foreground-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.20);
@colorlabel: hsl(0, 0%, 42%);
@margin-size: 6pt;
@double-margin-size: 12pt;
@border-size: 1px;
@border-type: solid;
@border-transparency: .3;
@border-color: rgba(0, 0, 0, @border-transparency);
@border: @border-size @border-type @border-color;
@border-radius: @radius;
@radius: .325em;
@white: #FFFFFF;
@colorbackground: @white;
@colorblocklinkbackground: #e0e0e0;
@smartphone: ~"not screen and (min-width: 45em)";
@tablet: ~"only screen and (min-width: 45em) and (max-width: 85em)";
@desktop: ~"not screen and (max-width: 85em)";
@listboxbordercolor: rgba(0, 0, 0, 0.14902);
.button() {
padding: @margin-size;
border: 1px solid rgba(0, 0, 0, 0.14);
border-radius: @border-radius;
background-color: @colorbackground;
&::before{
margin-right: @margin-size;
}
&:hover, &:active {
background-color: @colorblocklinkbackground;
}
}
.hide_text(@width: 3em) {
// https://css-tricks.com/forums/topic/hide-text-but-not-the-before-pseudo-class/
& when (@width > 0) {
width: @width;
}
overflow: hidden;
text-indent: -9999px;
white-space: nowrap;
}
@avatar_size: 3em;
div[data-gadget-url$="gadget_thread_reader.html"] {
// Limit the width to make reading more pleasant on large screen
max-width: 50em;
& > ol {
background-color: @colorbackground;
& > li {
padding-bottom: @double-margin-size;
/*
&:nth-child(even) {
background-color: rgba(230, 230, 230, 0.65);
}
*/
& > div.post_content {
display: inline-block;
max-width: 46em;
& > time {
color: @colorlabel;
}
& > div {
// Separate post info (user and time) from content
margin-top: @margin-size;
}
}
& > div.post_avatar {
display: inline-block;
margin-right: @margin-size;
width: @avatar_size;
height: @avatar_size;
line-height: @avatar_size;
text-align: center;
border-radius: 50%;
background: @colorsubheaderbackground;
color: @white;
text-shadow: @foreground-text-shadow;
vertical-align: top;
text-transform: uppercase;
}
& + li {
// Separate posts
border-top: solid 1px @colorsubheaderbackground;
padding-top: @margin-size;
}
}
}
& > nav {
// XXX Copied from listbox
display: flex;
padding-top: @margin-size;
border-top: 2px solid @listboxbordercolor;
span {
opacity: .3;
flex: 2;
text-align: right;
float: right;
}
a {
.button();
margin-right: @margin-size;
&:last-of-type {
margin-right: 0;
}
&:hover, &:active {
background-color: @colorblocklinkbackground;
}
@media @smartphone {
.hide_text(@width: initial);
}
&::before {
@media @smartphone {
float: left;
text-indent: @margin-size;
}
}
}
}
}
\ No newline at end of file
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="File" module="OFS.Image"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>__name__</string> </key>
<value> <string>gadget_thread_reader.less</string> </value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/plain</string> </value>
</item>
<item>
<key> <string>precondition</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -571,7 +571,7 @@
</tr>
<tr>
<td>click</td>
<td tal:content="python: '//div[contains(@data-gadget-scope, \'m\')]//a[text()=\'%s\' and contains(@href, \'#!\')]' % click_configuration['text']"></td>
<td tal:content="python: '//div[contains(@data-gadget-scope, \'m\')]//a[contains(text(), \'%s\') and contains(@href, \'#!\')]' % click_configuration['text']"></td>
<td></td>
</tr>
<tr>
......
##############################################################################
#
# Copyright (c) 2021 Nexedi SA and Contributors. All Rights Reserved.
# Vincent Pelletier <vincent@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
##############################################################################
import time
import urlparse
import ssl
import httplib
import json
from Products.ERP5Type.Timeout import getTimeLeft
from contextlib import contextmanager
from Products.ERP5Type.XMLObject import XMLObject
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Timeout import Deadline, TimeoutReachedError
from Products.ERP5Type.UnrestrictedMethod import super_user
from zLOG import LOG, ERROR
def isJson(header_dict):
return header_dict.get('content-type', '').split(';', 1)[0] == 'application/json'
class TimeTracker(object):
def __init__(self):
self.__stack = []
self.__history = []
@contextmanager
def __call__(self, reason):
stack = self.__stack
entry = [len(stack), reason, time.time(), None]
stack.append(entry)
self.__history.append(entry)
try:
yield
finally:
stack.pop()[3] = time.time()
def __str__(self):
return '\n'.join(
'%s%s: %.3fs' % (' ' * depth, reason, end - begin)
for depth, reason, begin, end in self.__history
if end is not None
)
class RESTAPIClientConnectorMixin(XMLObject):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
__EXPIRED_TOKEN = (0, None)
# Credential scheme:
# - primary credentials (client_id and client_secret) are persistent,
# set by admin on the connector instance
# - refresh token is persistent on the connector instance, and is
# expected to virtually never change once set
# - access token is volatile on connector instances
# so as to be per-ZODB.Connection. 2-tuple:
# - expiration timestamp
# - access token
_v_access_token = __EXPIRED_TOKEN
def _clearAccessToken(self):
"""
Forget current access token, so next _getAccessToken call retrieves a new one.
"""
self._v_access_token = self._EXPIRED_TOKEN
def _call(self, method, path, header_dict=(), body=None):
"""
body (None, string, json-serialisable objects)
If body is not None and not a string, it is serialised in json,
and the appropriate content-type is added to the headers.
Returns a 3-tuple:
- response header dict (header names lower-cased)
- response body
If response header content type is "application/json", the body
is json-decoded before being returned
- response status
"""
header_dict = dict(header_dict)
if body is not None and not isinstance(body, basestring):
header_dict['content-type'] = 'application/json'
body = json.dumps(body)
plain_url = self.getBaseUrl().rstrip('/') + '/' + path.lstrip('/')
parsed_url = urlparse.urlparse(plain_url)
ssl_context = ssl.create_default_context(
cadata=self.getCaCertificatePem(),
)
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.check_hostname = True
bind_address = self.getBindAddress()
if bind_address:
bind_address = (bind_address, 0)
time_left_before_timeout = getTimeLeft()
http_connection = httplib.HTTPSConnection(
host=parsed_url.hostname,
port=parsed_url.port,
strict=True,
timeout=time_left_before_timeout,
source_address=bind_address,
context=ssl_context,
)
request_start_time = time.time()
http_connection.request(
method=method,
url=path,
body=body,
headers=header_dict,
)
try:
http_response = http_connection.getresponse()
request_stop_time = time.time()
except ssl.SSLError as exc:
if 'The read operation timed out' == exc.message:
LOG(__name__, ERROR, "Call to %s %s raised Timeout (%ss)" %(
method, path, round(time_left_before_timeout, 6)
), error=True)
raise TimeoutReachedError
raise
except Exception:
LOG(__name__, ERROR, "Call to %s %s raised after %ss" %(
method, path, round(time_left_before_timeout, 6)
), error=True)
raise
response_body = http_response.read()
response_header_dict = {
name.lower(): value
for name, value in http_response.getheaders()
}
if isJson(response_header_dict):
response_body = json.loads(response_body)
return (
response_header_dict,
response_body,
http_response.status,
request_stop_time - request_start_time,
)
security.declarePrivate('call')
def call(
self,
archive_resource,
method,
path,
header_dict=(),
body=None,
archive_kw=None,
archive_document_relative_url=None,
archive_value_list=None,
timeout=None,
):
# default timeout should be kept very low
# to not block an instance with default zope configuration
timeout = timeout if timeout is not None else self.getTimeout(1)
original_header_dict = header_dict
header_dict = dict(header_dict)
time_tracker = TimeTracker()
try:
with time_tracker('call'), Deadline(timeout):
# Limit numbers of retries, in case the authentication API succeeds
# but the token is not usable.
for _ in xrange(2):
with time_tracker('token'):
access_token = self._getAccessToken()
if access_token is not None:
header_dict['Authorization'] = 'Bearer ' + self._getAccessToken()
with time_tracker('_call'):
(
response_header_dict,
response_body,
response_status,
response_time_duration,
) = self._call(
path=path,
method=method,
header_dict=header_dict,
body=body,
)
if response_status == 401:
self._clearAccessToken()
else:
# Success (or at least not an authentication failure), exit retry loop
break
except Exception:
LOG(__name__, ERROR, str(time_tracker), error=True)
raise
if archive_resource is not None:
archiveExchange = self._getTypeBasedMethod('archiveExchange')
if archiveExchange is not None:
with super_user():
archiveExchange(
resource_path=archive_resource,
raw_request=(
# XXX: how to avoid double request serialisation ?
path
if body is None else
json.dumps(body)
),
raw_response=(
# XXX: how to avoid deserialisation and then re-serialisation ?
response_body
if isinstance(response_body, basestring) else
json.dumps(response_body)
),
time_duration=response_time_duration,
archive_kw=archive_kw,
archive_document_relative_url=archive_document_relative_url,
archive_value_list=archive_value_list,
)
if response_status >= 300:
__traceback_info__ = { # pylint: disable=unused-variable
'request': {
'method': method,
'path': path,
# Do not put authentication headers in logs
'header_dict': original_header_dict,
'body': body,
},
'response': {
'header_dict': response_header_dict,
'body': response_body,
'status': response_status,
},
}
raise self.ClientConnectorError(
header_dict=response_header_dict,
body=response_body,
status=response_status,
)
return (
response_header_dict,
response_body,
response_status,
)
......@@ -2,78 +2,99 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
<global name="Mixin Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
<key> <string>default_reference</string> </key>
<value> <string>RESTAPIClientConnectorMixin</string> </value>
</item>
<item>
<key> <string>categories</string> </key>
<key> <string>description</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
<none/>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
<key> <string>id</string> </key>
<value> <string>mixin.erp5.RESTAPIClientConnectorMixin</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>text_content_error_message</string> </key>
<value>
<none/>
<tuple/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>send_email</string> </value>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>permissions</string> </key>
<key> <string>workflow_history</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
</item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WebPage_viewSendByEmailDialog</string> </value>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
......
# -*- coding: utf-8 -*-
# Copyright (c) 2002-2015 Nexedi SA and Contributors. All Rights Reserved.
from json import dumps
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
from httplib import HTTPSConnection
from erp5.component.mixin.RESTAPIClientConnectorMixin import RESTAPIClientConnectorMixin
from ssl import SSLError
from Products.ERP5Type.Timeout import TimeoutReachedError
import mock
expected_output_body_dict = {
u'output': 'output',
}
input_body_dict = {
'input': 'input',
}
class HTTPResponse_getresponse():
def __init__(self, status=200):
self.status = status
def getheaders(self):
return [
('content-type', 'application/json'),
]
def read(self):
return dumps(expected_output_body_dict)
class RESTAPIError(Exception):
__allow_access_to_unprotected_subobjects__ = {
'header_dict': 1,
'body': 1,
'status': 1,
}
def __init__(self, header_dict, body, status):
super(RESTAPIError, self).__init__()
self.header_dict = header_dict
self.body = body
self.status = status
class RESTAPIClientConnector(RESTAPIClientConnectorMixin):
meta_type = 'REST API Client Connector'
security = RESTAPIClientConnectorMixin.security
ClientConnectorError = RESTAPIError
def _getAccessToken(self):
return 'access_token'
def getTimeout(self, timeout):
return 5
def getBaseUrl(self):
return 'https://example.com/'
def getCaCertificatePem(self):
return 'ca_certificate_pem'
def getBindAddress(self):
return 'bind_address'
class TestRESTAPIClientConnector(ERP5TypeTestCase):
def afterSetUp(self):
self.rest_api_client_connection = RESTAPIClientConnector(id='rest_api_client_connection')
def test_api_call(self):
timeout = 1
with mock.patch(
'ssl.create_default_context',
) as mock_ssl_create_default_context, mock.patch(
'httplib.HTTPSConnection.request',
) as mock_https_connection_request, mock.patch(
'httplib.HTTPSConnection.getresponse',
return_value=HTTPResponse_getresponse()
), mock.patch('httplib.HTTPSConnection', return_value=HTTPSConnection) as mock_https_connection:
header_dict, body_dict, status = self.rest_api_client_connection.call(
archive_resource=None,
method='POST',
path='/path',
body=input_body_dict,
timeout=timeout,
)
# Check request
ssl_create_default_context_argument_dict = mock_ssl_create_default_context.call_args.kwargs
self.assertEqual(
ssl_create_default_context_argument_dict['cadata'],
'ca_certificate_pem'
)
https_connection_argument_dict = mock_https_connection.call_args.kwargs
self.assertTrue(
https_connection_argument_dict['timeout'] <= timeout
)
self.assertEqual(
https_connection_argument_dict['host'],
'example.com'
)
self.assertEqual(
https_connection_argument_dict['source_address'],
('bind_address', 0)
)
https_connection_request_argument_dict = mock_https_connection_request.call_args.kwargs
self.assertEqual(
https_connection_request_argument_dict['body'],
dumps(input_body_dict)
)
self.assertEqual(
https_connection_request_argument_dict['url'],
'/path'
)
self.assertEqual(
https_connection_request_argument_dict['headers']['Authorization'],
'Bearer access_token'
)
self.assertEqual(
https_connection_request_argument_dict['headers']['content-type'],
'application/json'
)
self.assertEqual(
https_connection_request_argument_dict['method'],
'POST'
)
# Check response
self.assertEqual(
header_dict['content-type'],
'application/json'
)
self.assertEqual(
body_dict,
expected_output_body_dict
)
self.assertEqual(
status,
200
)
def test_api_call_error(self):
with mock.patch(
'ssl.create_default_context',
), mock.patch(
'httplib.HTTPSConnection.request',
), mock.patch(
'httplib.HTTPSConnection.getresponse',
return_value=HTTPResponse_getresponse(498)
):
with self.assertRaises(RESTAPIError) as error:
self.rest_api_client_connection.call(
archive_resource=None,
method='POST',
path='/path',
body=input_body_dict
)
self.assertEqual(
error.status,
498
)
self.assertEqual(
error.header_dict['content-type'],
'application/json'
)
self.assertEqual(
error.body,
expected_output_body_dict
)
def test_api_call_timeout(self):
with mock.patch(
'ssl.create_default_context',
), mock.patch(
'httplib.HTTPSConnection.request',
), mock.patch(
'httplib.HTTPSConnection.getresponse',
) as mock_https_connection_getresponse:
mock_https_connection_getresponse.side_effect = SSLError('The read operation timed out')
self.assertRaises(
TimeoutReachedError,
self.rest_api_client_connection.call,
archive_resource=None,
method='POST',
path='/path',
body=input_body_dict
)
\ No newline at end of file
......@@ -2,78 +2,109 @@
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
<global name="Test Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>action</string> </key>
<key> <string>default_reference</string> </key>
<value> <string>testRESTAPIClientConnectorMixin</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
<none/>
</value>
</item>
<item>
<key> <string>categories</string> </key>
<key> <string>description</string> </key>
<value>
<tuple>
<string>action_type/object_jio_action</string>
</tuple>
<none/>
</value>
</item>
<item>
<key> <string>category</string> </key>
<value> <string>object_jio_action</string> </value>
<key> <string>id</string> </key>
<value> <string>test.erp5.testRESTAPIClientConnectorMixin</string> </value>
</item>
<item>
<key> <string>condition</string> </key>
<value> <string></string> </value>
<key> <string>portal_type</string> </key>
<value> <string>Test Component</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>send_email</string> </value>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>permissions</string> </key>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple>
<string>View</string>
</tuple>
<tuple/>
</value>
</item>
<item>
<key> <string>priority</string> </key>
<value> <float>10.0</float> </value>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Send by Email</string> </value>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>visible</string> </key>
<value> <int>1</int> </value>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>string:${object_url}/WebPage_viewSendByEmailDialog</string> </value>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
......
mixin.erp5.RESTAPIClientConnectorMixin
\ No newline at end of file
test.erp5.testFTPConnection
test.erp5.testRESTAPIClientConnectorMixin
test.erp5.testWebServiceTool
\ No newline at end of file
......@@ -46,6 +46,18 @@
<key> <string>portal_type</string> </key>
<value> <string>Base Type</string> </value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
<string>id</string>
</tuple>
</value>
</item>
<item>
<key> <string>type_class</string> </key>
<value> <string>InteractionWorkflow</string> </value>
......
......@@ -64,7 +64,10 @@
<value>
<tuple>
<string>id</string>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
</tuple>
</value>
</item>
......
......@@ -75,6 +75,18 @@
<none/>
</value>
</item>
<item>
<key> <string>searchable_text_property_id</string> </key>
<value>
<tuple>
<string>title</string>
<string>description</string>
<string>reference</string>
<string>short_title</string>
<string>id</string>
</tuple>
</value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
......
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