Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
E
erp5_rtl_support
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Romain Courteaud
erp5_rtl_support
Commits
a270c222
Commit
a270c222
authored
Oct 30, 2013
by
Aurel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement support of deletion detection by ERP5SyncML & improve workflow usage on signatures
parent
badb7a60
Changes
58
Show whitespace changes
Inline
Side-by-side
Showing
58 changed files
with
1696 additions
and
1184 deletions
+1696
-1184
bt5/erp5_syncml/PropertySheetTemplateItem/portal_property_sheets/SyncMLPreference/preferred_check_delete_at_end_property.xml
...ncMLPreference/preferred_check_delete_at_end_property.xml
+40
-0
bt5/erp5_syncml/PropertySheetTemplateItem/portal_property_sheets/SyncMLPreference/preferred_split_indexation_property.xml
.../SyncMLPreference/preferred_split_indexation_property.xml
+40
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SQLCatalog_indexSyncMLDocumentList.xml
..._skins/erp5_syncml/SQLCatalog_indexSyncMLDocumentList.xml
+32
-23
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML.xml
.../portal_skins/erp5_syncml/SystemPreference_viewSyncML.xml
+2
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML/my_preferred_check_delete_at_end.xml
...reference_viewSyncML/my_preferred_check_delete_at_end.xml
+100
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML/my_preferred_split_indexation.xml
...emPreference_viewSyncML/my_preferred_split_indexation.xml
+100
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_catalog_syncml_document_list.xml
...rtal_skins/erp5_syncml/z_catalog_syncml_document_list.xml
+49
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_create_syncml.xml
...TemplateItem/portal_skins/erp5_syncml/z_create_syncml.xml
+45
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_delete_data_from_path.xml
...Item/portal_skins/erp5_syncml/z_delete_data_from_path.xml
+39
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_drop_syncml.xml
...inTemplateItem/portal_skins/erp5_syncml/z_drop_syncml.xml
+16
-17
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_deleted_gid_list.xml
...ortal_skins/erp5_syncml/z_get_syncml_deleted_gid_list.xml
+43
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_gid_list.xml
...teItem/portal_skins/erp5_syncml/z_get_syncml_gid_list.xml
+88
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_path_list.xml
...eItem/portal_skins/erp5_syncml/z_get_syncml_path_list.xml
+78
-0
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_unindex_syncml_data.xml
...teItem/portal_skins/erp5_syncml/z_unindex_syncml_data.xml
+39
-0
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow.xml
.../portal_workflow/syncml_signature_validation_workflow.xml
+1
-1
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/scripts/SyncMLSignature_afterDrift.xml
...alidation_workflow/scripts/SyncMLSignature_afterDrift.xml
+0
-71
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/scripts/SyncMLSignature_resetConflictList.xml
...on_workflow/scripts/SyncMLSignature_resetConflictList.xml
+0
-68
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict.xml
.../syncml_signature_validation_workflow/states/conflict.xml
+1
-2
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict_resolved_with_client_command_winning.xml
.../states/conflict_resolved_with_client_command_winning.xml
+1
-0
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict_resolved_with_merge.xml
...lidation_workflow/states/conflict_resolved_with_merge.xml
+1
-0
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/no_conflict.xml
...ncml_signature_validation_workflow/states/no_conflict.xml
+3
-4
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/no_conflict.xml
...signature_validation_workflow/transitions/no_conflict.xml
+4
-4
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/resolve_conflict_with_merge.xml
...tion_workflow/transitions/resolve_conflict_with_merge.xml
+5
-1
bt5/erp5_syncml/bt/revision
bt5/erp5_syncml/bt/revision
+1
-1
bt5/erp5_syncml_test_data/TestTemplateItem/portal_components/test.erp5.testSyncMLAsynchronousEngine.py
...rtal_components/test.erp5.testSyncMLAsynchronousEngine.py
+2
-0
bt5/erp5_syncml_test_data/bt/revision
bt5/erp5_syncml_test_data/bt/revision
+1
-1
bt5/erp5_tiosafe_core/PortalTypeBaseCategoryTemplateItem/base_category_list.xml
...PortalTypeBaseCategoryTemplateItem/base_category_list.xml
+1
-1
bt5/erp5_tiosafe_core/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
...rtalTypePropertySheetTemplateItem/property_sheet_list.xml
+1
-1
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/SynchronizationConfiguratorItem.xml
...ortal_property_sheets/SynchronizationConfiguratorItem.xml
+0
-4
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/WebServiceConnector.xml
...mplateItem/portal_property_sheets/WebServiceConnector.xml
+0
-4
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/WebServiceRequest.xml
...TemplateItem/portal_property_sheets/WebServiceRequest.xml
+0
-4
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/Account_getAccountValueList.xml
...al_skins/erp5_integration/Account_getAccountValueList.xml
+0
-61
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/Accounting_getAccountingValueList.xml
...ns/erp5_integration/Accounting_getAccountingValueList.xml
+0
-61
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_doTestCreate.xml
...skins/erp5_integration/IntegrationModule_doTestCreate.xml
+0
-63
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_getTestObjectList.xml
.../erp5_integration/IntegrationModule_getTestObjectList.xml
+0
-55
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_view.xml
.../portal_skins/erp5_integration/IntegrationModule_view.xml
+1
-0
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_view/my_title.xml
...kins/erp5_integration/IntegrationModule_view/my_title.xml
+260
-0
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/TransactionLine_getCategoryList.xml
...kins/erp5_integration/TransactionLine_getCategoryList.xml
+0
-55
bt5/erp5_tiosafe_core/ToolTemplateItem/portal_integrations.xml
...rp5_tiosafe_core/ToolTemplateItem/portal_integrations.xml
+0
-4
bt5/erp5_tiosafe_core/bt/template_extension_id_list
bt5/erp5_tiosafe_core/bt/template_extension_id_list
+1
-1
bt5/erp5_tiosafe_core/bt/template_portal_type_base_category_list
...5_tiosafe_core/bt/template_portal_type_base_category_list
+1
-1
bt5/erp5_tiosafe_document/SkinTemplateItem/portal_skins/erp5_tiosafe_document/DocumentConnector_readDocument.xml
.../erp5_tiosafe_document/DocumentConnector_readDocument.xml
+33
-30
bt5/erp5_tiosafe_document/bt/revision
bt5/erp5_tiosafe_document/bt/revision
+1
-1
product/ERP5/Document/BusinessTemplate.py
product/ERP5/Document/BusinessTemplate.py
+1
-1
product/ERP5SyncML/Document/SyncMLSignature.py
product/ERP5SyncML/Document/SyncMLSignature.py
+28
-36
product/ERP5SyncML/Document/SyncMLSubscription.py
product/ERP5SyncML/Document/SyncMLSubscription.py
+526
-369
product/ERP5SyncML/Engine/AsynchronousEngine.py
product/ERP5SyncML/Engine/AsynchronousEngine.py
+28
-30
product/ERP5SyncML/Engine/EngineMixin.py
product/ERP5SyncML/Engine/EngineMixin.py
+16
-17
product/ERP5SyncML/Engine/SynchronousEngine.py
product/ERP5SyncML/Engine/SynchronousEngine.py
+20
-18
product/ERP5SyncML/SyncMLConstant.py
product/ERP5SyncML/SyncMLConstant.py
+1
-51
product/ERP5SyncML/SyncMLMessage.py
product/ERP5SyncML/SyncMLMessage.py
+8
-3
product/ERP5SyncML/Tool/SynchronizationTool.py
product/ERP5SyncML/Tool/SynchronizationTool.py
+1
-90
product/ERP5SyncML/tests/testERP5DocumentSyncML.py
product/ERP5SyncML/tests/testERP5DocumentSyncML.py
+4
-9
product/ERP5SyncML/tests/testERP5SyncML.py
product/ERP5SyncML/tests/testERP5SyncML.py
+13
-10
product/ERP5SyncML/tests/testERP5SyncMLVCard.py
product/ERP5SyncML/tests/testERP5SyncMLVCard.py
+4
-2
product/ERP5TioSafe/Conduit/ERP5NodeConduit.py
product/ERP5TioSafe/Conduit/ERP5NodeConduit.py
+9
-6
product/ERP5TioSafe/Document/IntegrationSite.py
product/ERP5TioSafe/Document/IntegrationSite.py
+7
-1
product/ERP5Type/Utils.py
product/ERP5Type/Utils.py
+0
-2
No files found.
bt5/erp5_syncml/PropertySheetTemplateItem/portal_property_sheets/SyncMLPreference/preferred_check_delete_at_end_property.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"Standard Property"
module=
"erp5.portal_type"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
elementary_type/boolean
</string>
</tuple>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<string>
Indicate if it check deleted object at the end or with the splitted activities that check data changes
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
preferred_check_delete_at_end_property
</string>
</value>
</item>
<item>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Standard Property
</string>
</value>
</item>
<item>
<key>
<string>
preference
</string>
</key>
<value>
<int>
1
</int>
</value>
</item>
<item>
<key>
<string>
property_default
</string>
</key>
<value>
<string>
python: True
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/not_synchronized
.xml
→
bt5/erp5_syncml/
PropertySheetTemplateItem/portal_property_sheets/SyncMLPreference/preferred_split_indexation_property
.xml
View file @
a270c222
...
@@ -2,38 +2,37 @@
...
@@ -2,38 +2,37 @@
<ZopeData>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<pickle>
<global
name=
"Sta
teDefinition"
module=
"Products.DCWorkflow.States
"
/>
<global
name=
"Sta
ndard Property"
module=
"erp5.portal_type
"
/>
</pickle>
</pickle>
<pickle>
<pickle>
<dictionary>
<dictionary>
<item>
<key>
<string>
categories
</string>
</key>
<value>
<tuple>
<string>
elementary_type/boolean
</string>
</tuple>
</value>
</item>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
Define if indexation of source data will be splitted in activity or not
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
not_synchronized
</string>
</value>
<value>
<string>
preferred_split_indexation_property
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
titl
e
</string>
</key>
<key>
<string>
portal_typ
e
</string>
</key>
<value>
<string>
Not Synchronized
</string>
</value>
<value>
<string>
Standard Property
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
transitions
</string>
</key>
<key>
<string>
preference
</string>
</key>
<value>
<value>
<int>
1
</int>
</value>
<tuple>
<string>
change_to_conflict
</string>
<string>
change_to_partial
</string>
<string>
do_sync
</string>
<string>
synchronize
</string>
</tuple>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
type_list
</string>
</key>
<key>
<string>
property_default
</string>
</key>
<value>
<value>
<string>
python: False
</string>
</value>
<tuple/>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/scripts/SyncMLSignature_afterSynchronize
.xml
→
bt5/erp5_syncml/
SkinTemplateItem/portal_skins/erp5_syncml/SQLCatalog_indexSyncMLDocumentList
.xml
View file @
a270c222
...
@@ -50,39 +50,48 @@
...
@@ -50,39 +50,48 @@
</item>
</item>
<item>
<item>
<key>
<string>
_body
</string>
</key>
<key>
<string>
_body
</string>
</key>
<value>
<string>
signature = state_change[\'object\']\n
<value>
<string>
if not len(path_list):\n
return\n
restrictedTraverse = context.getPortalObject().restrictedTraverse\n
argument_getter_dict = {}\n
if subscription_path:\n
subscription = restrictedTraverse(subscription_path)\n
getId = subscription.getGidFromObject\n
getData = subscription.getDataFromDocument\n
else:\n
getId = getData = None\n
\n
\n
edit_kw = {}
\n
method = context.z_catalog_syncml_document_list
\n
\n
\n
temporary_data = signature.getTemporaryData()\n
parameter_append_list = []\n
if temporary_data is not None:\n
append = parameter_append_list.append\n
# This happens when we have sent the xml\n
parameter_dict = {}\n
# and we just get the confirmation\n
for property in method.arguments_src.split():\n
signature.setData(temporary_data)\n
parameter_dict[property] = parameter_value_list = []\n
edit_kw["temporary_data"] = None\n
if property == \'getData\':\n
getter = getData\n
elif property == \'getId\':\n
getter = getId\n
else:\n
getter = None\n
if getter is None:\n
getter = lambda obj, property=property: getattr(obj, property)()\n
append((parameter_value_list, getter))\n
\n
\n
if signature.isForce():\n
for path in path_list:\n
edit_kw["force"] = False\n
obj = restrictedTraverse(path)\n
if signature.hasPartialData():\n
for value_list, getter in parameter_append_list:\n
edit_kw["partial_data"] = None\n
value_list.append(getter(obj))\n
if signature.hasSubscriberXupdate():\n
method(**parameter_dict)\n
edit_kw["subscriber_xupdate"] = None\n
if signature.hasPublisherXupdate():\n
edit_kw["publisher_xupdate"] = None\n
\n
if len(edit_kw):\n
signature.edit(**edit_kw)\n
\n
context.SyncMLSignature_resetConflictList(state_change)\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
state_chang
e
</string>
</value>
<value>
<string>
path_list, subscription_path=None, activate_kw=Non
e
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
S
yncMLSignature_afterSynchronize
</string>
</value>
<value>
<string>
S
QLCatalog_indexSyncMLDocumentList
</string>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML.xml
View file @
a270c222
...
@@ -89,6 +89,8 @@
...
@@ -89,6 +89,8 @@
<value>
<value>
<list>
<list>
<string>
my_preferred_sync_action_per_activity_count
</string>
<string>
my_preferred_sync_action_per_activity_count
</string>
<string>
my_preferred_check_delete_at_end
</string>
<string>
my_preferred_split_indexation
</string>
</list>
</list>
</value>
</value>
</item>
</item>
...
...
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML/my_preferred_check_delete_at_end.xml
0 → 100644
View file @
a270c222
<?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>
</list>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
my_preferred_check_delete_at_end
</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>
20
</int>
</value>
</item>
<item>
<key>
<string>
field_id
</string>
</key>
<value>
<string>
my_checkbox
</string>
</value>
</item>
<item>
<key>
<string>
form_id
</string>
</key>
<value>
<string>
Base_viewFieldLibrary
</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>
Check Deletion at End
</string>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/SystemPreference_viewSyncML/my_preferred_split_indexation.xml
0 → 100644
View file @
a270c222
<?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>
</list>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
my_preferred_split_indexation
</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>
20
</int>
</value>
</item>
<item>
<key>
<string>
field_id
</string>
</key>
<value>
<string>
my_checkbox
</string>
</value>
</item>
<item>
<key>
<string>
form_id
</string>
</key>
<value>
<string>
Base_viewFieldLibrary
</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>
Split Source Indexation
</string>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_catalog_syncml_document_list.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"SQL"
module=
"Products.ZSQLMethods.SQL"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string>
getId\r\n
getPath\r\n
getData\r\n
</string>
</value>
</item>
<item>
<key>
<string>
connection_id
</string>
</key>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
z_catalog_syncml_document_list
</string>
</value>
</item>
<item>
<key>
<string>
src
</string>
</key>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
REPLACE INTO\n
syncml (`path`, `gid`, `data`)\n
VALUES\n
<dtml-in prefix="loop" expr="_.range(_.len(getPath))">
\n
(\n
<dtml-sqlvar
expr=
"getPath[loop_item]"
type=
"string"
>
,\n
<dtml-sqlvar
expr=
"getId[loop_item]"
type=
"string"
>
,\n
<dtml-sqlvar
expr=
"getData[loop_item]"
type=
"string"
>
\n
)
<dtml-unless
sequence-end
>
,
</dtml-unless>
\n
</dtml-in>
\n
]]>
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_create_syncml.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"SQL"
module=
"Products.ZSQLMethods.SQL"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
_col
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
connection_id
</string>
</key>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
z_create_syncml
</string>
</value>
</item>
<item>
<key>
<string>
src
</string>
</key>
<value>
<string>
CREATE TABLE `syncml` (\n
`path` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT \'\',\n
`gid` varchar(255) COLLATE utf8_unicode_ci DEFAULT \'\',\n
`data` LONGBLOB NULL,\n
PRIMARY KEY (`path`),\n
KEY `gid` (`gid`,`path`)\n
) ENGINE=InnoDB;\n
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/partial
.xml
→
bt5/erp5_syncml/
SkinTemplateItem/portal_skins/erp5_syncml/z_delete_data_from_path
.xml
View file @
a270c222
...
@@ -2,37 +2,36 @@
...
@@ -2,37 +2,36 @@
<ZopeData>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<pickle>
<global
name=
"S
tateDefinition"
module=
"Products.DCWorkflow.States
"
/>
<global
name=
"S
QL"
module=
"Products.ZSQLMethods.SQL
"
/>
</pickle>
</pickle>
<pickle>
<pickle>
<dictionary>
<dictionary>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
path
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
connection_
id
</string>
</key>
<value>
<string>
partial
</string>
</value>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
Partial
</string>
</value>
<value>
<string>
z_delete_data_from_path
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
transitions
</string>
</key>
<key>
<string>
src
</string>
</key>
<value>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
<tuple>
<string>
change_to_conflict
</string>
DELETE FROM syncml\n
<string>
do_sync
</string>
WHERE\n
<string>
synchronize
</string>
path = <dtml-sqlvar path type="string">
\n
</tuple>
</value>
]]>
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
type_list
</string>
</key>
<key>
<string>
title
</string>
</key>
<value>
<value>
<string></string>
</value>
<tuple/>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/synchronized
.xml
→
bt5/erp5_syncml/
SkinTemplateItem/portal_skins/erp5_syncml/z_drop_syncml
.xml
View file @
a270c222
...
@@ -2,36 +2,35 @@
...
@@ -2,36 +2,35 @@
<ZopeData>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<pickle>
<global
name=
"S
tateDefinition"
module=
"Products.DCWorkflow.States
"
/>
<global
name=
"S
QL"
module=
"Products.ZSQLMethods.SQL
"
/>
</pickle>
</pickle>
<pickle>
<pickle>
<dictionary>
<dictionary>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
_col
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
connection_
id
</string>
</key>
<value>
<string>
synchronized
</string>
</value>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
Synchronized
</string>
</value>
<value>
<string>
z_drop_syncml
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
transitions
</string>
</key>
<key>
<string>
src
</string>
</key>
<value>
<value>
<string>
DROP TABLE IF EXISTS syncml
</string>
</value>
<tuple>
<string>
change_to_conflict
</string>
<string>
drift
</string>
</tuple>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
type_list
</string>
</key>
<key>
<string>
title
</string>
</key>
<value>
<value>
<string></string>
</value>
<tuple/>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_deleted_gid_list.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"SQL"
module=
"Products.ZSQLMethods.SQL"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string>
signature_path\r\n
source_path
</string>
</value>
</item>
<item>
<key>
<string>
connection_id
</string>
</key>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
z_get_syncml_deleted_gid_list
</string>
</value>
</item>
<item>
<key>
<string>
src
</string>
</key>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
SELECT gid \n
FROM syncml\n
WHERE\n
path like <dtml-sqlvar signature_path type="string">
\n
AND\n
gid not in (SELECT gid FROM syncml WHERE\n
path like
<dtml-sqlvar
source_path
type=
"string"
>
)
]]>
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/synchronize
.xml
→
bt5/erp5_syncml/
SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_gid_list
.xml
View file @
a270c222
...
@@ -2,55 +2,85 @@
...
@@ -2,55 +2,85 @@
<ZopeData>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<pickle>
<global
name=
"
TransitionDefinition"
module=
"Products.DCWorkflow.Transitions
"
/>
<global
name=
"
SQL"
module=
"Products.ZSQLMethods.SQL
"
/>
</pickle>
</pickle>
<pickle>
<pickle>
<dictionary>
<dictionary>
<item>
<item>
<key>
<string>
actbox_category
</string>
</key>
<key>
<string>
allow_simple_one_argument_traversal
</string>
</key>
<value>
<string>
workflow
</string>
</value>
<value>
<none/>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
actbox_name
</string>
</key>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
strict_min_gid\r\n
min_gid\r\n
max_gid\r\n
path\r\n
limit
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
actbox_url
</string>
</key>
<key>
<string>
cache_time_
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
class_file_
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
after_script_name
</string>
</key>
<key>
<string>
class_name_
</string>
</key>
<value>
<string>
SyncMLSignature_afterSynchronize
</string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
connection_hook
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
guard
</string>
</key>
<key>
<string>
connection_id
</string>
</key>
<value>
<value>
<string>
erp5_sql_connection
</string>
</value>
<none/>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
synchronize
</string>
</value>
<value>
<string>
z_get_syncml_gid_list
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
new_state_id
</string>
</key>
<key>
<string>
max_cache_
</string>
</key>
<value>
<
string>
synchronized
</string
>
</value>
<value>
<
int>
100
</int
>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
script_name
</string>
</key>
<key>
<string>
max_rows_
</string>
</key>
<value>
<
string></string
>
</value>
<value>
<
int>
0
</int
>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
SELECT \n
gid\n
FROM syncml\n
WHERE\n
path like <dtml-sqlvar path type="string">
\n
<dtml-if
strict_min_gid
>
\n
AND gid >
<dtml-sqlvar
strict_min_gid
type=
"string"
>
\n
</dtml-if>
\n
<dtml-if
min_gid
>
\n
AND gid >=
<dtml-sqlvar
min_gid
type=
"string"
>
\n
</dtml-if>
\n
<dtml-if
max_gid
>
\n
AND gid
<
=
<dtml-sqlvar
max_gid
type=
"string"
>
\n
</dtml-if>
\n
ORDER BY gid\n
<dtml-if
limit
>
\n
LIMIT
<dtml-var
limit
>
\n
</dtml-if>
\n
]]>
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
t
rigger_typ
e
</string>
</key>
<key>
<string>
t
itl
e
</string>
</key>
<value>
<
int>
2
</int
>
</value>
<value>
<
string></string
>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/
WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/drif
t.xml
→
bt5/erp5_syncml/
SkinTemplateItem/portal_skins/erp5_syncml/z_get_syncml_path_lis
t.xml
View file @
a270c222
...
@@ -2,55 +2,75 @@
...
@@ -2,55 +2,75 @@
<ZopeData>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<pickle>
<global
name=
"
TransitionDefinition"
module=
"Products.DCWorkflow.Transitions
"
/>
<global
name=
"
SQL"
module=
"Products.ZSQLMethods.SQL
"
/>
</pickle>
</pickle>
<pickle>
<pickle>
<dictionary>
<dictionary>
<item>
<item>
<key>
<string>
actbox_category
</string>
</key>
<key>
<string>
allow_simple_one_argument_traversal
</string>
</key>
<value>
<string>
workflow
</string>
</value>
<value>
<none/>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
actbox_name
</string>
</key>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
min_gid\r\n
max_gid\r\n
path
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
actbox_url
</string>
</key>
<key>
<string>
cache_time_
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
class_file_
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
after_script_name
</string>
</key>
<key>
<string>
class_name_
</string>
</key>
<value>
<string>
SyncMLSignature_afterDrift
</string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
connection_hook
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
guard
</string>
</key>
<key>
<string>
connection_id
</string>
</key>
<value>
<value>
<string>
erp5_sql_connection
</string>
</value>
<none/>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
drif
t
</string>
</value>
<value>
<string>
z_get_syncml_path_lis
t
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
new_state_id
</string>
</key>
<key>
<string>
max_cache_
</string>
</key>
<value>
<
string>
not_synchronized
</string
>
</value>
<value>
<
int>
100
</int
>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
script_name
</string>
</key>
<key>
<string>
max_rows_
</string>
</key>
<value>
<
string></string
>
</value>
<value>
<
int>
0
</int
>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
src
</string>
</key>
<value>
<string></string>
</value>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
SELECT path, gid, data \n
FROM syncml\n
WHERE\n
path like <dtml-sqlvar path type="string">
\n
<dtml-if
min_gid
>
\n
AND gid >=
<dtml-sqlvar
min_gid
type=
"string"
>
\n
</dtml-if>
\n
<dtml-if
max_gid
>
\n
AND gid
<
=
<dtml-sqlvar
max_gid
type=
"string"
>
\n
</dtml-if>
\n
]]>
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
t
rigger_typ
e
</string>
</key>
<key>
<string>
t
itl
e
</string>
</key>
<value>
<
int>
2
</int
>
</value>
<value>
<
string></string
>
</value>
</item>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
...
...
bt5/erp5_syncml/SkinTemplateItem/portal_skins/erp5_syncml/z_unindex_syncml_data.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"SQL"
module=
"Products.ZSQLMethods.SQL"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
arguments_src
</string>
</key>
<value>
<string>
path
</string>
</value>
</item>
<item>
<key>
<string>
connection_id
</string>
</key>
<value>
<string>
erp5_sql_connection
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
z_unindex_syncml_data
</string>
</value>
</item>
<item>
<key>
<string>
src
</string>
</key>
<value>
<string
encoding=
"cdata"
>
<![CDATA[
DELETE FROM syncml\n
WHERE\n
path like <dtml-sqlvar path type="string">
\n
]]>
</string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow.xml
View file @
a270c222
...
@@ -34,7 +34,7 @@
...
@@ -34,7 +34,7 @@
</item>
</item>
<item>
<item>
<key>
<string>
initial_state
</string>
</key>
<key>
<string>
initial_state
</string>
</key>
<value>
<string>
no
t_synchronized
</string>
</value>
<value>
<string>
no
_conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
manager_bypass
</string>
</key>
<key>
<string>
manager_bypass
</string>
</key>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/scripts/SyncMLSignature_afterDrift.xml
deleted
100644 → 0
View file @
badb7a60
<?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>
_body
</string>
</key>
<value>
<string>
signature = state_change[\'object\']\n
if signature.hasPartialData():\n
signature.setPartialData(None)\n
if signature.hasTemporaryData():\n
signature.setTemporaryData(None)\n
</string>
</value>
</item>
<item>
<key>
<string>
_params
</string>
</key>
<value>
<string>
state_change
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
SyncMLSignature_afterDrift
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/scripts/SyncMLSignature_resetConflictList.xml
deleted
100644 → 0
View file @
badb7a60
<?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>
_body
</string>
</key>
<value>
<string>
signature = state_change[\'object\']\n
signature.resetConflictList()\n
</string>
</value>
</item>
<item>
<key>
<string>
_params
</string>
</key>
<value>
<string>
state_change
</string>
</value>
</item>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
SyncMLSignature_resetConflictList
</string>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict.xml
View file @
a270c222
...
@@ -22,10 +22,9 @@
...
@@ -22,10 +22,9 @@
<key>
<string>
transitions
</string>
</key>
<key>
<string>
transitions
</string>
</key>
<value>
<value>
<tuple>
<tuple>
<string>
drif
t
</string>
<string>
no_conflic
t
</string>
<string>
resolve_conflict_with_client_command_winning
</string>
<string>
resolve_conflict_with_client_command_winning
</string>
<string>
resolve_conflict_with_merge
</string>
<string>
resolve_conflict_with_merge
</string>
<string>
synchronize
</string>
</tuple>
</tuple>
</value>
</value>
</item>
</item>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict_resolved_with_client_command_winning.xml
View file @
a270c222
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
<tuple>
<tuple>
<string>
change_to_partial
</string>
<string>
change_to_partial
</string>
<string>
do_sync
</string>
<string>
do_sync
</string>
<string>
no_conflict
</string>
<string>
synchronize
</string>
<string>
synchronize
</string>
</tuple>
</tuple>
</value>
</value>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/conflict_resolved_with_merge.xml
View file @
a270c222
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
<tuple>
<tuple>
<string>
change_to_partial
</string>
<string>
change_to_partial
</string>
<string>
do_sync
</string>
<string>
do_sync
</string>
<string>
no_conflict
</string>
<string>
synchronize
</string>
<string>
synchronize
</string>
</tuple>
</tuple>
</value>
</value>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/
syncing
.xml
→
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/states/
no_conflict
.xml
View file @
a270c222
...
@@ -8,22 +8,21 @@
...
@@ -8,22 +8,21 @@
<dictionary>
<dictionary>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
Default state of signature
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
syncing
</string>
</value>
<value>
<string>
no_conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
title
</string>
</key>
<value>
<string>
Syncing
</string>
</value>
<value>
<string>
No Conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
transitions
</string>
</key>
<key>
<string>
transitions
</string>
</key>
<value>
<value>
<tuple>
<tuple>
<string>
change_to_conflict
</string>
<string>
change_to_conflict
</string>
<string>
synchronize
</string>
</tuple>
</tuple>
</value>
</value>
</item>
</item>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/
do_sync
.xml
→
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/
no_conflict
.xml
View file @
a270c222
...
@@ -28,7 +28,7 @@
...
@@ -28,7 +28,7 @@
</item>
</item>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
Reset to default state
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
guard
</string>
</key>
<key>
<string>
guard
</string>
</key>
...
@@ -38,11 +38,11 @@
...
@@ -38,11 +38,11 @@
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
do_sync
</string>
</value>
<value>
<string>
no_conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
new_state_id
</string>
</key>
<key>
<string>
new_state_id
</string>
</key>
<value>
<string>
syncing
</string>
</value>
<value>
<string>
no_conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
script_name
</string>
</key>
<key>
<string>
script_name
</string>
</key>
...
@@ -50,7 +50,7 @@
...
@@ -50,7 +50,7 @@
</item>
</item>
<item>
<item>
<key>
<string>
title
</string>
</key>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
<value>
<string>
No Conflict
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
trigger_type
</string>
</key>
<key>
<string>
trigger_type
</string>
</key>
...
...
bt5/erp5_syncml/WorkflowTemplateItem/portal_workflow/syncml_signature_validation_workflow/transitions/resolve_conflict_with_merge.xml
View file @
a270c222
...
@@ -10,6 +10,10 @@
...
@@ -10,6 +10,10 @@
<key>
<string>
actbox_category
</string>
</key>
<key>
<string>
actbox_category
</string>
</key>
<value>
<string>
workflow
</string>
</value>
<value>
<string>
workflow
</string>
</value>
</item>
</item>
<item>
<key>
<string>
actbox_icon
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<item>
<key>
<string>
actbox_name
</string>
</key>
<key>
<string>
actbox_name
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
...
@@ -20,7 +24,7 @@
...
@@ -20,7 +24,7 @@
</item>
</item>
<item>
<item>
<key>
<string>
after_script_name
</string>
</key>
<key>
<string>
after_script_name
</string>
</key>
<value>
<string>
SyncMLSignature_resetConflictList
</string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
description
</string>
</key>
<key>
<string>
description
</string>
</key>
...
...
bt5/erp5_syncml/bt/revision
View file @
a270c222
94
118
\ No newline at end of file
bt5/erp5_syncml_test_data/TestTemplateItem/portal_components/test.erp5.testSyncMLAsynchronousEngine.py
View file @
a270c222
...
@@ -42,6 +42,8 @@ class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
...
@@ -42,6 +42,8 @@ class testSyncMLAsynchronousEngine(TestERP5SyncMLMixin):
This is ran before anything, used to set the environment
This is ran before anything, used to set the environment
"""
"""
self
.
sync_tool
=
self
.
portal
.
portal_synchronizations
self
.
sync_tool
=
self
.
portal
.
portal_synchronizations
self
.
portal
.
z_drop_syncml
()
self
.
portal
.
z_create_syncml
()
# here, you can create the categories and objects your test will depend on
# here, you can create the categories and objects your test will depend on
def
_initSyncModule
(
self
):
def
_initSyncModule
(
self
):
...
...
bt5/erp5_syncml_test_data/bt/revision
View file @
a270c222
bt5/erp5_tiosafe_core/PortalTypeBaseCategoryTemplateItem/base_category_list.xml
View file @
a270c222
...
@@ -4,8 +4,8 @@
...
@@ -4,8 +4,8 @@
<item>
source_section
</item>
<item>
source_section
</item>
</portal_type>
</portal_type>
<portal_type
id=
"Integration Site"
>
<portal_type
id=
"Integration Site"
>
<item>
resource
</item>
<item>
destination_payment
</item>
<item>
destination_payment
</item>
<item>
resource
</item>
<item>
source_account
</item>
<item>
source_account
</item>
<item>
source_payment
</item>
<item>
source_payment
</item>
</portal_type>
</portal_type>
...
...
bt5/erp5_tiosafe_core/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
View file @
a270c222
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/SynchronizationConfiguratorItem.xml
View file @
a270c222
...
@@ -28,10 +28,6 @@
...
@@ -28,10 +28,6 @@
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
SynchronizationConfiguratorItem
</string>
</value>
<value>
<string>
SynchronizationConfiguratorItem
</string>
</value>
</item>
</item>
<item>
<key>
<string>
last_id
</string>
</key>
<value>
<string>
2
</string>
</value>
</item>
<item>
<item>
<key>
<string>
portal_type
</string>
</key>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Property Sheet
</string>
</value>
<value>
<string>
Property Sheet
</string>
</value>
...
...
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/WebServiceConnector.xml
View file @
a270c222
...
@@ -28,10 +28,6 @@
...
@@ -28,10 +28,6 @@
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
WebServiceConnector
</string>
</value>
<value>
<string>
WebServiceConnector
</string>
</value>
</item>
</item>
<item>
<key>
<string>
last_id
</string>
</key>
<value>
<string>
1
</string>
</value>
</item>
<item>
<item>
<key>
<string>
portal_type
</string>
</key>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Property Sheet
</string>
</value>
<value>
<string>
Property Sheet
</string>
</value>
...
...
bt5/erp5_tiosafe_core/PropertySheetTemplateItem/portal_property_sheets/WebServiceRequest.xml
View file @
a270c222
...
@@ -28,10 +28,6 @@
...
@@ -28,10 +28,6 @@
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
WebServiceRequest
</string>
</value>
<value>
<string>
WebServiceRequest
</string>
</value>
</item>
</item>
<item>
<key>
<string>
last_id
</string>
</key>
<value>
<string>
11
</string>
</value>
</item>
<item>
<item>
<key>
<string>
portal_type
</string>
</key>
<key>
<string>
portal_type
</string>
</key>
<value>
<string>
Property Sheet
</string>
</value>
<value>
<string>
Property Sheet
</string>
</value>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/Account_getAccountValueList.xml
View file @
a270c222
...
@@ -67,75 +67,14 @@ if account.getReference() and account.getValidationState() != \'deleted\':\n
...
@@ -67,75 +67,14 @@ if account.getReference() and account.getValidationState() != \'deleted\':\n
return account_list\n
return account_list\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<key>
<string>
_code
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
id="", gid=""
</string>
</value>
<value>
<string>
id="", gid=""
</string>
</value>
</item>
</item>
<item>
<key>
<string>
errors
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
func_code
</string>
</key>
<value>
<object>
<klass>
<global
name=
"FuncCode"
module=
"Shared.DC.Scripts.Signature"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
co_argcount
</string>
</key>
<value>
<int>
2
</int>
</value>
</item>
<item>
<key>
<string>
co_varnames
</string>
</key>
<value>
<tuple>
<string>
id
</string>
<string>
gid
</string>
<string>
account_list
</string>
<string>
_getiter_
</string>
<string>
_getattr_
</string>
<string>
context
</string>
<string>
account
</string>
<string>
getattr
</string>
</tuple>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
func_defaults
</string>
</key>
<value>
<tuple>
<string></string>
<string></string>
</tuple>
</value>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
Account_getAccountValueList
</string>
</value>
<value>
<string>
Account_getAccountValueList
</string>
</value>
</item>
</item>
<item>
<key>
<string>
warnings
</string>
</key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/Accounting_getAccountingValueList.xml
View file @
a270c222
...
@@ -67,75 +67,14 @@ if accounting.getReference() and accounting.getStartDate() and accounting.getSim
...
@@ -67,75 +67,14 @@ if accounting.getReference() and accounting.getStartDate() and accounting.getSim
return accounting_list\n
return accounting_list\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<key>
<string>
_code
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
id="", gid=""
</string>
</value>
<value>
<string>
id="", gid=""
</string>
</value>
</item>
</item>
<item>
<key>
<string>
errors
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
func_code
</string>
</key>
<value>
<object>
<klass>
<global
name=
"FuncCode"
module=
"Shared.DC.Scripts.Signature"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
co_argcount
</string>
</key>
<value>
<int>
2
</int>
</value>
</item>
<item>
<key>
<string>
co_varnames
</string>
</key>
<value>
<tuple>
<string>
id
</string>
<string>
gid
</string>
<string>
accounting_list
</string>
<string>
_getiter_
</string>
<string>
_getattr_
</string>
<string>
context
</string>
<string>
accounting
</string>
<string>
getattr
</string>
</tuple>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
func_defaults
</string>
</key>
<value>
<tuple>
<string></string>
<string></string>
</tuple>
</value>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
Accounting_getAccountingValueList
</string>
</value>
<value>
<string>
Accounting_getAccountingValueList
</string>
</value>
</item>
</item>
<item>
<key>
<string>
warnings
</string>
</key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_doTestCreate.xml
View file @
a270c222
...
@@ -74,77 +74,14 @@ return context.REQUEST.RESPONSE.redirect(\n
...
@@ -74,77 +74,14 @@ return context.REQUEST.RESPONSE.redirect(\n
url_quote(\'Request sent.\')))\n
url_quote(\'Request sent.\')))\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<key>
<string>
_code
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
*args, **kw
</string>
</value>
<value>
<string>
*args, **kw
</string>
</value>
</item>
</item>
<item>
<key>
<string>
errors
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
func_code
</string>
</key>
<value>
<object>
<klass>
<global
name=
"FuncCode"
module=
"Shared.DC.Scripts.Signature"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
co_argcount
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
co_varnames
</string>
</key>
<value>
<tuple>
<string>
args
</string>
<string>
kw
</string>
<string>
_getitem_
</string>
<string>
selection_name
</string>
<string>
Products.PythonScripts.standard
</string>
<string>
url_quote
</string>
<string>
_getattr_
</string>
<string>
context
</string>
<string>
uids
</string>
<string>
len
</string>
<string>
conduit_id
</string>
<string>
_getiter_
</string>
<string>
uid
</string>
</tuple>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
func_defaults
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
IntegrationModule_doTestCreate
</string>
</value>
<value>
<string>
IntegrationModule_doTestCreate
</string>
</value>
</item>
</item>
<item>
<key>
<string>
warnings
</string>
</key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_getTestObjectList.xml
View file @
a270c222
...
@@ -53,69 +53,14 @@
...
@@ -53,69 +53,14 @@
<value>
<string>
return context.getPortalObject()[context.getId()].contentValues()\n
<value>
<string>
return context.getPortalObject()[context.getId()].contentValues()\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<key>
<string>
_code
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
*args, **kw
</string>
</value>
<value>
<string>
*args, **kw
</string>
</value>
</item>
</item>
<item>
<key>
<string>
errors
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
func_code
</string>
</key>
<value>
<object>
<klass>
<global
name=
"FuncCode"
module=
"Shared.DC.Scripts.Signature"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
co_argcount
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
co_varnames
</string>
</key>
<value>
<tuple>
<string>
args
</string>
<string>
kw
</string>
<string>
_getattr_
</string>
<string>
_getitem_
</string>
<string>
context
</string>
</tuple>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
func_defaults
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
IntegrationModule_getTestObjectList
</string>
</value>
<value>
<string>
IntegrationModule_getTestObjectList
</string>
</value>
</item>
</item>
<item>
<key>
<string>
warnings
</string>
</key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_view.xml
View file @
a270c222
...
@@ -98,6 +98,7 @@
...
@@ -98,6 +98,7 @@
<value>
<value>
<list>
<list>
<string>
my_id
</string>
<string>
my_id
</string>
<string>
my_title
</string>
<string>
my_int_index
</string>
<string>
my_int_index
</string>
<string>
my_source_section_title
</string>
<string>
my_source_section_title
</string>
<string>
my_destination_section_title
</string>
<string>
my_destination_section_title
</string>
...
...
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/IntegrationModule_view/my_title.xml
0 → 100644
View file @
a270c222
<?xml version="1.0"?>
<ZopeData>
<record
id=
"1"
aka=
"AAAAAAAAAAE="
>
<pickle>
<global
name=
"StringField"
module=
"Products.Formulator.StandardFields"
/>
</pickle>
<pickle>
<dictionary>
<item>
<key>
<string>
id
</string>
</key>
<value>
<string>
my_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>
<item>
<key>
<string>
required_not_found
</string>
</key>
<value>
<string>
Input is required but no input given.
</string>
</value>
</item>
<item>
<key>
<string>
too_long
</string>
</key>
<value>
<string>
Too much input was given.
</string>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
overrides
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
alternate_name
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
css_class
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
default
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_maxwidth
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_width
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
editable
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
enabled
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
external_validator
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
extra
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
hidden
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
max_length
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
required
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
truncate
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
unicode
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
whitespace_preserve
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
tales
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
alternate_name
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
css_class
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
default
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_maxwidth
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_width
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
editable
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
enabled
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
external_validator
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
extra
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
hidden
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
max_length
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
required
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
truncate
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
unicode
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
whitespace_preserve
</string>
</key>
<value>
<string></string>
</value>
</item>
</dictionary>
</value>
</item>
<item>
<key>
<string>
values
</string>
</key>
<value>
<dictionary>
<item>
<key>
<string>
alternate_name
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
css_class
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
default
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
description
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_maxwidth
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
display_width
</string>
</key>
<value>
<int>
20
</int>
</value>
</item>
<item>
<key>
<string>
editable
</string>
</key>
<value>
<int>
1
</int>
</value>
</item>
<item>
<key>
<string>
enabled
</string>
</key>
<value>
<int>
1
</int>
</value>
</item>
<item>
<key>
<string>
external_validator
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
extra
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
hidden
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
max_length
</string>
</key>
<value>
<string></string>
</value>
</item>
<item>
<key>
<string>
required
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
title
</string>
</key>
<value>
<string>
Title
</string>
</value>
</item>
<item>
<key>
<string>
truncate
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
unicode
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
whitespace_preserve
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_tiosafe_core/SkinTemplateItem/portal_skins/erp5_integration/TransactionLine_getCategoryList.xml
View file @
a270c222
...
@@ -62,69 +62,14 @@ category_list.sort()\n
...
@@ -62,69 +62,14 @@ category_list.sort()\n
return category_list\n
return category_list\n
</string>
</value>
</string>
</value>
</item>
</item>
<item>
<key>
<string>
_code
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string></string>
</value>
<value>
<string></string>
</value>
</item>
</item>
<item>
<key>
<string>
errors
</string>
</key>
<value>
<tuple/>
</value>
</item>
<item>
<key>
<string>
func_code
</string>
</key>
<value>
<object>
<klass>
<global
name=
"FuncCode"
module=
"Shared.DC.Scripts.Signature"
/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key>
<string>
co_argcount
</string>
</key>
<value>
<int>
0
</int>
</value>
</item>
<item>
<key>
<string>
co_varnames
</string>
</key>
<value>
<tuple>
<string>
category_list
</string>
<string>
_getiter_
</string>
<string>
_getattr_
</string>
<string>
context
</string>
<string>
category
</string>
</tuple>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key>
<string>
func_defaults
</string>
</key>
<value>
<none/>
</value>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
TransactionLine_getCategoryList
</string>
</value>
<value>
<string>
TransactionLine_getCategoryList
</string>
</value>
</item>
</item>
<item>
<key>
<string>
warnings
</string>
</key>
<value>
<tuple/>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/ToolTemplateItem/portal_integrations.xml
View file @
a270c222
...
@@ -28,10 +28,6 @@
...
@@ -28,10 +28,6 @@
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
<value>
<string>
portal_integrations
</string>
</value>
<value>
<string>
portal_integrations
</string>
</value>
</item>
</item>
<item>
<key>
<string>
last_id
</string>
</key>
<value>
<string>
27
</string>
</value>
</item>
</dictionary>
</dictionary>
</pickle>
</pickle>
</record>
</record>
...
...
bt5/erp5_tiosafe_core/bt/template_extension_id_list
View file @
a270c222
bt5/erp5_tiosafe_core/bt/template_portal_type_base_category_list
View file @
a270c222
Integration Module | destination_section
Integration Module | destination_section
Integration Module | source_section
Integration Module | source_section
Integration Site | resource
Integration Site | destination_payment
Integration Site | destination_payment
Integration Site | resource
Integration Site | source_account
Integration Site | source_account
Integration Site | source_payment
Integration Site | source_payment
Synchronization Configurator Item | destination_section
Synchronization Configurator Item | destination_section
...
...
bt5/erp5_tiosafe_document/SkinTemplateItem/portal_skins/erp5_tiosafe_document/DocumentConnector_readDocument.xml
View file @
a270c222
...
@@ -53,14 +53,16 @@
...
@@ -53,14 +53,16 @@
<value>
<string
encoding=
"cdata"
>
<![CDATA[
<value>
<string
encoding=
"cdata"
>
<![CDATA[
# First retrieve the document\n
# First retrieve the document\n
doc_list = context.getPortalObject().document_module.searchFolder(reference=reference,\n
portal = context.getPortalObject()\n
sort_on = ((\'version\', \'DESC\'),),\n
document_list = portal.document_module.searchFolder(\n
limit=1)\n
reference=reference,\n
validation_state="shared",\n
sort_on=[(\'version\', \'DESC\')],\n
)\n
if len(document_list) != 1:\n
raise ValueError, "Impossible to find document with reference %s" %(reference)\n
document = document_list[0].getObject()\n
\n
\n
if not len(doc_list) == 1:\n
raise ValueError, "Impossible to find document with reference %r" %(reference)\n
\n
import_file = doc_list[0]\n
\n
\n
# Then parse it\n
# Then parse it\n
from Products.ERP5OOo.OOoUtils import OOoParser\n
from Products.ERP5OOo.OOoUtils import OOoParser\n
...
@@ -102,7 +104,7 @@ def getIDFromString(string=None):\n
...
@@ -102,7 +104,7 @@ def getIDFromString(string=None):\n
\n
\n
return clean_id\n
return clean_id\n
\n
\n
parser.openFromString(str(
import_file
.getData()))\n
parser.openFromString(str(
document
.getData()))\n
\n
\n
# Extract tables from the speadsheet file\n
# Extract tables from the speadsheet file\n
filename = parser.getFilename()\n
filename = parser.getFilename()\n
...
@@ -127,22 +129,23 @@ for table_name in spreadsheet_list.keys():\n
...
@@ -127,22 +129,23 @@ for table_name in spreadsheet_list.keys():\n
property_map[column_index] = column_id\n
property_map[column_index] = column_id\n
column_index += 1\n
column_index += 1\n
# This path_element_list help us to reconstruct the absolute path\n
# This path_element_list help us to reconstruct the absolute path\n
context.log("line_id = %r" %(line_id))\n
if line_id is not None:\n
if line_id is not None:\n
line_list = [sheet[int(line_id)-1],]\n
line_list = [sheet[int(line_id)-1],]\n
line_index = int(line_id)\n
line_index = int(line_id)\n
else:\n
else:\n
line_list = sheet[1:]\n
line_list = sheet[1:]\n
line_index = 2\n
line_index = 2\n
context.log(\'line_list = %s\' %(line_list))
\n
line_list = line_list[:limit]
\n
for line in line_list:\n
for line in line_list:\n
if id_list and str(line_index) not in id_list:\n
continue\n
# Exclude empty lines\n
# Exclude empty lines\n
context.log("\\tgot line", line)\n
if line.count(\'\') + line.count(None) == len(line):\n
if line.count(\'\') + line.count(None) == len(line):\n
continue\n
continue\n
\n
\n
# Prefetch line datas\n
# Prefetch line datas\n
line_data = {"id" : str(line_index)}\n
line_data = {"id" : str(line_index)}\n
if not id_only:\n
path_defined = []\n
path_defined = []\n
for cell_index, cell in enumerate(line):\n
for cell_index, cell in enumerate(line):\n
# Get the property corresponding to the cell data\n
# Get the property corresponding to the cell data\n
...
@@ -172,7 +175,7 @@ return spreadsheet_line_list\n
...
@@ -172,7 +175,7 @@ return spreadsheet_line_list\n
</item>
</item>
<item>
<item>
<key>
<string>
_params
</string>
</key>
<key>
<string>
_params
</string>
</key>
<value>
<string>
reference, table, li
ne_id
=None
</string>
</value>
<value>
<string>
reference, table, li
mit, id_only, line_id=None, id_list
=None
</string>
</value>
</item>
</item>
<item>
<item>
<key>
<string>
id
</string>
</key>
<key>
<string>
id
</string>
</key>
...
...
bt5/erp5_tiosafe_document/bt/revision
View file @
a270c222
1
4
\ No newline at end of file
\ No newline at end of file
product/ERP5/Document/BusinessTemplate.py
View file @
a270c222
...
@@ -5155,7 +5155,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
...
@@ -5155,7 +5155,7 @@ Business Template is a set of definitions, such as skins, portal types and categ
from
Products.ERP5VCS.WorkingCopy
import
NotAWorkingCopyError
from
Products.ERP5VCS.WorkingCopy
import
NotAWorkingCopyError
try
:
try
:
self
.
setRevision
(
self
.
getVcsTool
().
newRevision
())
self
.
setRevision
(
self
.
getVcsTool
().
newRevision
())
except
NotAWorkingCopyError
:
except
(
NotAWorkingCopyError
,
IOError
)
:
raise
ImportError
raise
ImportError
except
ImportError
:
except
ImportError
:
self
.
updateRevisionNumber
()
self
.
updateRevisionNumber
()
...
...
product/ERP5SyncML/Document/SyncMLSignature.py
View file @
a270c222
...
@@ -31,11 +31,12 @@ from hashlib import md5
...
@@ -31,11 +31,12 @@ from hashlib import md5
from
AccessControl
import
ClassSecurityInfo
from
AccessControl
import
ClassSecurityInfo
from
Products.ERP5Type.Accessor.Constant
import
PropertyGetter
as
ConstantGetter
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Type.XMLObject
import
XMLObject
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type
import
Permissions
from
Products.ERP5Type
import
PropertySheet
from
Products.ERP5Type
import
PropertySheet
from
Products.ERP5SyncML.Utils
import
PdataHelper
from
Products.ERP5SyncML.Utils
import
PdataHelper
from
Products.ERP5Type.Accessor.Constant
import
PropertyGetter
as
ConstantGetter
_MARKER
=
[]
_MARKER
=
[]
...
@@ -50,7 +51,6 @@ class SyncMLSignature(XMLObject):
...
@@ -50,7 +51,6 @@ class SyncMLSignature(XMLObject):
"""
"""
meta_type
=
'ERP5 Signature'
meta_type
=
'ERP5 Signature'
portal_type
=
'SyncML Signature'
portal_type
=
'SyncML Signature'
isIndexable
=
ConstantGetter
(
'isIndexable'
,
value
=
False
)
isIndexable
=
ConstantGetter
(
'isIndexable'
,
value
=
False
)
security
=
ClassSecurityInfo
()
security
=
ClassSecurityInfo
()
...
@@ -66,6 +66,32 @@ class SyncMLSignature(XMLObject):
...
@@ -66,6 +66,32 @@ class SyncMLSignature(XMLObject):
,
PropertySheet
.
Document
,
PropertySheet
.
Document
,
PropertySheet
.
SyncMLSignature
)
,
PropertySheet
.
SyncMLSignature
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'synchronize'
)
def
synchronize
(
self
):
"""
This is call when subscription get confirmation of the data synchronization
This copy & reset some properties if needed
"""
edit_kw
=
{}
temporary_data
=
self
.
getTemporaryData
()
if
temporary_data
is
not
None
:
# This happens when we have sent the xml
# and we just get the confirmation
self
.
setData
(
temporary_data
)
edit_kw
[
"temporary_data"
]
=
None
if
self
.
isForce
():
edit_kw
[
"force"
]
=
False
if
self
.
hasPartialData
():
edit_kw
[
"partial_data"
]
=
None
if
self
.
hasSubscriberXupdate
():
edit_kw
[
"subscriber_xupdate"
]
=
None
if
self
.
hasPublisherXupdate
():
edit_kw
[
"publisher_xupdate"
]
=
None
if
len
(
edit_kw
):
self
.
edit
(
**
edit_kw
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'setData'
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'setData'
)
def
setData
(
self
,
value
):
def
setData
(
self
,
value
):
"""
"""
...
@@ -249,24 +275,6 @@ class SyncMLSignature(XMLObject):
...
@@ -249,24 +275,6 @@ class SyncMLSignature(XMLObject):
else
:
else
:
return
self
.
_baseGetPublisherXupdate
(
default
)
return
self
.
_baseGetPublisherXupdate
(
default
)
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'reset'
)
def
reset
(
self
,
no_conflict
=
False
):
"""
Clear Signature and change validation_state to not_synchronized
no_conflict : prevent the reset of signature for which conflict
has not been marked resolved, this is usefull when
resetting all signature at the beginning of a sync process
XXX Use a better name and a positive value by default
"""
if
no_conflict
and
self
.
getValidationState
()
in
(
'conflict'
,
'conflict_resolved_with_merge'
,
'conflict_resolved_with_client_command_winning'
):
return
if
self
.
getValidationState
()
!=
'not_synchronized'
:
self
.
drift
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'getConflictList'
)
'getConflictList'
)
def
getConflictList
(
self
):
def
getConflictList
(
self
):
...
@@ -275,22 +283,6 @@ class SyncMLSignature(XMLObject):
...
@@ -275,22 +283,6 @@ class SyncMLSignature(XMLObject):
"""
"""
return
self
.
contentValues
()
return
self
.
contentValues
()
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'setConflictList'
)
def
setConflictList
(
self
,
conflict_list
):
"""
XXX is it still usefull ?
"""
return
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'resetConflictList'
)
def
resetConflictList
(
self
):
"""
XXX is it still usefull ?
"""
return
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
security
.
declareProtected
(
Permissions
.
ModifyPortalContent
,
'delConflict'
)
'delConflict'
)
def
delConflict
(
self
,
conflict
):
def
delConflict
(
self
,
conflict
):
...
...
product/ERP5SyncML/Document/SyncMLSubscription.py
View file @
a270c222
...
@@ -28,7 +28,6 @@
...
@@ -28,7 +28,6 @@
##############################################################################
##############################################################################
from
base64
import
b16encode
,
b16decode
from
base64
import
b16encode
,
b16decode
from
warnings
import
warn
from
logging
import
getLogger
from
logging
import
getLogger
from
urlparse
import
urlparse
from
urlparse
import
urlparse
from
lxml
import
etree
from
lxml
import
etree
...
@@ -66,6 +65,8 @@ syncml_logger = getLogger('ERP5SyncML')
...
@@ -66,6 +65,8 @@ syncml_logger = getLogger('ERP5SyncML')
MAX_OBJECT_PER_MESSAGE
=
300
MAX_OBJECT_PER_MESSAGE
=
300
RETRO_COMPATIBLE
=
True
_MARKER
=
[]
_MARKER
=
[]
class
SyncMLSubscription
(
XMLObject
):
class
SyncMLSubscription
(
XMLObject
):
"""
"""
...
@@ -99,81 +100,261 @@ class SyncMLSubscription(XMLObject):
...
@@ -99,81 +100,261 @@ class SyncMLSubscription(XMLObject):
self
.
logout
()
self
.
logout
()
self
.
_edit
(
authenticated_user
=
None
)
self
.
_edit
(
authenticated_user
=
None
)
security
.
declarePrivate
(
'getAnd
Activate
'
)
security
.
declarePrivate
(
'getAnd
Index
'
)
def
getAnd
Activate
(
self
,
callback
,
method_kw
,
activate_kw
,
**
kw
):
def
getAnd
Index
(
self
,
callback
,
method_kw
,
activate_kw
,
**
kw
):
"""
"""
This methods is called by the asynchronous engine to
split activity
This methods is called by the asynchronous engine to
index source
generation into activities.
data in sql table
callback : method to call in activity
callback : method to call in activity
method_kw : callback's parameters
method_kw : callback's parameters
activate_kw : activity parameters to pass to activate call
activate_kw : activity parameters to pass to activate call
kw : any parameter getAndActivate can required if it calls itself
kw : any parameter getAndActivate can required if it calls itself
Last activate must wait for all other activities to be processed in order
to set the Final tag in the message, this is required by SyncML DS
specification
"""
"""
# The following implementation is base on CatalogTool.searchAndActivate
if
kw
.
has_key
(
"packet_size"
):
# It might be possible to move a part of this code into the domain class
# so that it can be configurable as not all backend are optimised for
# this default implementation
search_kw
=
dict
(
kw
)
search_kw
=
dict
(
kw
)
packet_size
=
search_kw
.
pop
(
'packet_size'
,
30
)
packet_size
=
search_kw
.
pop
(
'packet_size'
,
30
)
limit
=
packet_size
*
search_kw
.
pop
(
'activity_count'
,
100
)
limit
=
packet_size
*
search_kw
.
pop
(
'activity_count'
,
100
)
else
:
# We index everything at once
limit
=
None
packet_size
=
None
search_kw
=
{}
try
:
try
:
r
=
self
.
getDocumentIdList
(
limit
=
limit
,
**
search_kw
)
# It is assumed that
r
=
self
.
getDocumentIdList
(
limit
=
limit
,
**
search_kw
)
# It is assumed that
# the result is sorted
# the result is sorted
except
TypeError
:
except
TypeError
:
if
not
RETRO_COMPATIBLE
:
raise
else
:
syncml_logger
.
warning
(
"Script %s does not accept paramaters limit=%s kw=%s"
%
syncml_logger
.
warning
(
"Script %s does not accept paramaters limit=%s kw=%s"
%
(
self
.
getListMethodId
(),
limit
,
search_kw
,))
(
self
.
getListMethodId
(),
limit
,
search_kw
,))
r
=
self
.
getDocumentList
()
# It is assumed that
r
=
self
.
getDocumentList
()
# It is assumed that
# the result is sorted
# the result is sorted
result_count
=
len
(
r
)
result_count
=
len
(
r
)
generated_other_activity
=
False
if
result_count
:
if
result_count
:
syncml_logger
.
debug
(
"getAndActivate : got %d result, limit = %d, packet %d"
%
r
=
[
str
(
x
.
path
)
for
x
in
r
]
(
result_count
,
limit
,
packet_size
))
if
not
limit
:
# We do not split in activity so call the callback right now
syncml_logger
.
info
(
"getAndIndex : got %d result and no limit, calling callback..."
%
(
result_count
,))
callback_method
=
getattr
(
self
,
callback
)
callback_method
(
path_list
=
r
[:],
activate_kw
=
activate_kw
,
**
method_kw
)
else
:
syncml_logger
.
info
(
"getAndIndex : got %d, %r result, limit = %r, packet %r"
%
(
result_count
,
r
,
limit
,
packet_size
))
generated_other_activity
=
False
if
result_count
==
limit
:
if
result_count
==
limit
:
# Recursive call to prevent too many activity generation
# Recursive call to prevent too many activity generation
next_kw
=
dict
(
activate_kw
,
priority
=
1
+
activate_kw
.
get
(
'priority'
,
1
))
next_kw
=
dict
(
activate_kw
,
priority
=
1
+
activate_kw
.
get
(
'priority'
,
1
))
kw
[
"min_id"
]
=
r
[
-
1
].
getId
()
kw
[
"min_id"
]
=
r
[
-
1
].
getId
()
syncml_logger
.
debug
(
"--> calling getAndActivate
in activity, min = %s"
%
syncml_logger
.
info
(
"--> calling getAndIndex
in activity, min = %s"
%
(
kw
[
"min_id"
],))
(
kw
[
"min_id"
],))
self
.
activate
(
**
next_kw
).
getAndIndex
(
callback
,
method_kw
,
activate_kw
,
**
kw
)
generated_other_activity
=
True
activate
=
self
.
activate
callback_method
=
getattr
(
activate
(
**
activate_kw
),
callback
)
if
generated_other_activity
:
for
i
in
xrange
(
0
,
result_count
,
packet_size
):
syncml_logger
.
info
(
"-- getAndIndex : recursive call, generating for %s"
%
(
r
[
i
:
i
+
packet_size
],))
callback_method
(
path_list
=
r
[
i
:
i
+
packet_size
],
activate_kw
=
activate_kw
,
**
method_kw
)
else
:
if
result_count
>
packet_size
and
limit
:
for
i
in
xrange
(
0
,
result_count
-
packet_size
,
packet_size
):
syncml_logger
.
info
(
"-- getAndIndex : i %s, call, generating for %s : %s"
%
(
i
,
r
[
i
:
i
+
packet_size
],
activate_kw
))
callback_method
(
path_list
=
r
[
i
:
i
+
packet_size
],
**
method_kw
)
final_min
=
i
+
packet_size
else
:
final_min
=
0
syncml_logger
.
info
(
"---- getAndIndex : final call for %s %s : %s"
\
%
(
final_min
,
r
[
final_min
:],
activate_kw
))
callback_method
(
path_list
=
r
[
final_min
:],
activate_kw
=
activate_kw
,
**
method_kw
)
return
result_count
security
.
declarePrivate
(
'generateBaseResponse'
)
def
generateBaseResponse
(
self
,
message_id
=
None
):
"""
Return a message containing default headers
"""
if
not
message_id
:
message_id
=
self
.
getNextMessageId
(),
syncml_response
=
SyncMLResponse
()
syncml_response
.
addHeader
(
session_id
=
self
.
getSessionId
(),
message_id
=
message_id
,
target
=
self
.
getUrlString
(),
source
=
self
.
getSubscriptionUrlString
())
syncml_response
.
addBody
()
return
syncml_response
security
.
declarePrivate
(
'getSearchableSourcePath'
)
def
getSearchableSourcePath
(
self
):
"""
Return the path of the subscription that will be used in sql table
_ char must be escaped because of the LIKE behaviour
"""
return
"%s%%"
%
(
self
.
getSourceValue
().
getPath
().
replace
(
"_"
,
"
\
_
"
),)
def sendSyncCommand(self, min_gid, max_gid, message_id, activate_kw):
"""
This methods is intented to be called by asynchronous engine in activity to
send sync commands for a subset of data
"""
# Build Message
syncml_response = SyncMLResponse()
syncml_response = self.generateBaseResponse(message_id)
self._getSyncMLData(
syncml_response=syncml_response,
min_gid=min_gid,
max_gid=max_gid,
)
# Send the message in activity to prevent recomputation of data in case of
# transport failure
# activate_kw["
group_method_id
"] = None
# activate_kw["
group_method_cost
"] = .05
self.activate(**activate_kw).sendMessage(xml=str(syncml_response))
security.declarePrivate('applySyncCommand')
def applySyncCommand(self, response_message_id, activate_kw, **kw):
"""
This methods is intented to be called by asynchronous engine in activity to
apply sync commands for a subset of data
"""
# Build Message
if response_message_id:
syncml_response = self.generateBaseResponse()
else:
syncml_response = None
self._applySyncCommand(syncml_response=syncml_response, **kw)
# Send the message in activity to prevent recomputing data in case of
# transport failure
if syncml_response:
syncml_logger("
----
%
s
sending
%
s
notifications
of
sync
"
% (self.getTitle(),
syncml_response.sync_confirmation_counter))
self.activate(activity="
SQLQueue
",
# group_method_id=None,
# group_method_cost=.05,
tag=activate_kw).sendMessage(xml=str(syncml_response))
security.declarePrivate('getAndActivate')
def getAndActivate(self, callback, activate_kw, **kw):
"""
This methods is called by the asynchronous engine to split activity
generation into activities.
callback : method to call in activity
activate_kw : activity parameters to pass to activate call
kw : any parameter getAndActivate can required if it calls itself
Last activate must wait for all other activities to be processed in order
to set the Final tag in the message, this is required by SyncML DS
specification
"""
# The following implementation is base on CatalogTool.searchAndActivate
# It might be possible to move a part of this code into the domain class
# so that it can be configurable as not all backend are optimised for
# this default implementation
search_kw = dict(kw)
packet_size = search_kw.pop('packet_size', 30)
limit = packet_size * search_kw.pop('activity_count', 100)
syncml_logger.debug("
-->
calling
getAndActivate
packet
size
=
%
s
,
limit
=
%
s
" %
(packet_size, limit))
# We must know if we have a lower limit or not to propagate
if not kw.has_key("
strict_min_gid
"):
first_call = True
else:
first_call = False
search_kw.update({"
stict_min_gid
" : None,
"
min_gid
" : None,
"
max_gid
" : None,
"
limit
" : limit,
"
path
" : self.getSearchableSourcePath()})
r = [x.gid for x in self.z_get_syncml_gid_list(**search_kw)]
result_count = len(r)
generated_other_activity = False
if result_count:
syncml_logger.info("
getAndActivate
:
got
%
d
result
" % (result_count,))
if result_count == limit:
# Recursive call to prevent too many activity generation
next_kw = dict(activate_kw, priority=1+activate_kw.get('priority', 1))
kw["
strict_min_gid
"] = r[-1]
syncml_logger.info("
-->
calling
getAndActivate
in
activity
,
min
=
%
s
" %
(kw.get("
strict_min_gid
", None),))
self.activate(**next_kw).getAndActivate(
self.activate(**next_kw).getAndActivate(
callback
,
method_kw
,
activate_kw
,
**
kw
)
callback, activate_kw, **kw)
generated_other_activity = True
generated_other_activity = True
r
=
[
x
.
getId
()
for
x
in
r
]
message_id_list = self.getNextMessageIdList(id_count=result_count)
message_id_list = self.getNextMessageIdList(id_count=result_count)
# XXX maybe (result_count / packet_size) + 1 instead of result_count
# XXX maybe (result_count / packet_size) + 1 instead of result_count
message_id_list.reverse() # We pop each id in the following loop
message_id_list.reverse() # We pop each id in the following loop
activate
=
self
.
getPortalObject
().
portal_synchronizations
.
activate
callback_method = getattr(self.activate(**activate_kw), callback)
callback_method
=
getattr
(
activate
(
**
activate_kw
),
callback
)
if generated_other_activity:
if generated_other_activity:
# XXX Can be factorized with following code
# upper_limit of xrange + some check ???
for i in xrange(0, result_count, packet_size):
for i in xrange(0, result_count, packet_size):
syncml_logger
.
debug
(
"-- getAndActivate : recursive call, generating for %s"
if first_call:
%
(
r
[
i
:
i
+
packet_size
],))
min_gid = None
callback_method
(
id_list
=
r
[
i
:
i
+
packet_size
],
first_call = False
else:
min_gid = r[i]
try:
max_gid = r[i+packet_size-1]
except IndexError:
# Last packet
max_gid = r[-1]
syncml_logger.info("
--
getAndActivate
:
recursive
call
i
=
%
s
,
min
=
%
s
,
max
=
%
s
"
\
% (i, min_gid, max_gid,))
callback_method(min_gid=min_gid,
max_gid=max_gid,
message_id=message_id_list.pop(),
message_id=message_id_list.pop(),
activate_kw
=
activate_kw
,
activate_kw=activate_kw)
**
method_kw
)
else:
else:
i = 0
i = 0
if result_count > packet_size:
for i in xrange(0, result_count-packet_size, packet_size):
for i in xrange(0, result_count-packet_size, packet_size):
syncml_logger
.
debug
(
"-- getAndActivate : call, generating for %s : %s"
%
if first_call:
(
r
[
i
:
i
+
packet_size
],
activate_kw
))
min_gid = None
callback_method
(
id_list
=
r
[
i
:
i
+
packet_size
],
first_call = False
else:
min_gid = r[i]
syncml_logger.info("
--
getAndActivate
:
call
min
=
%
s
,
max
=
%
s
"
\
% (min_gid, r[i+packet_size-1]))
callback_method(min_gid=min_gid,
max_gid=r[i+packet_size-1],
message_id=message_id_list.pop(),
message_id=message_id_list.pop(),
activate_kw
=
activate_kw
,
activate_kw=activate_kw)
**
method_kw
)
final_min = i + packet_size
# Final activity must be executed after all other
else:
syncml_logger
.
debug
(
"---- getAndActivate : final call for %s : %s"
%
(
r
[
i
+
packet_size
:],
activate_kw
))
final_min = 0
callback_method
(
id_list
=
r
[
i
+
packet_size
:],
# XXX Has to be unit tested
# Final activity must be tell there is no upper limit
# with mock object
# XXX maybe re-put here the final tag of message to avoid empty message
if first_call:
min_gid = None
else:
min_gid = r[final_min]
syncml_logger.info("
--
getAndActivate
:
final
call
min
=
%
s
,
max
=
None
"
\
% (min_gid,))
callback_method(min_gid=min_gid,
max_gid=None, # No limit when last call
message_id=message_id_list.pop(),
message_id=message_id_list.pop(),
activate_kw
=
activate_kw
,
activate_kw=activate_kw)
**
method_kw
)
return result_count
return result_count
security.declarePrivate('sendMessage')
security.declarePrivate('sendMessage')
...
@@ -207,7 +388,6 @@ class SyncMLSubscription(XMLObject):
...
@@ -207,7 +388,6 @@ class SyncMLSubscription(XMLObject):
sync_id=self.getDestinationReference(),
sync_id=self.getDestinationReference(),
content_type=self.getContentType())
content_type=self.getContentType())
def _loginUser(self, user_id=None):
def _loginUser(self, user_id=None):
"""
"""
Log in with the user provided or defined on self
Log in with the user provided or defined on self
...
@@ -217,7 +397,11 @@ class SyncMLSubscription(XMLObject):
...
@@ -217,7 +397,11 @@ class SyncMLSubscription(XMLObject):
if user_id:
if user_id:
# TODO: make it work for users existing anywhere
# TODO: make it work for users existing anywhere
user_folder = self.getPortalObject().acl_users
user_folder = self.getPortalObject().acl_users
try:
user = user_folder.getUserById(user_id).__of__(user_folder) # __of__ might got AttributeError
user = user_folder.getUserById(user_id).__of__(user_folder) # __of__ might got AttributeError
except AttributeError:
raise ValueError("
User
%
s
cannot
be
found
in
user
folder
,
\
synchronization
cannot
work
with
this
kind
of
user
" % (user_id,))
if user is None:
if user is None:
raise ValueError("
User
%
s
cannot
be
found
in
user
folder
,
\
raise ValueError("
User
%
s
cannot
be
found
in
user
folder
,
\
synchronization
cannot
work
with
this
kind
of
user
" % (user_id,))
synchronization
cannot
work
with
this
kind
of
user
" % (user_id,))
...
@@ -229,16 +413,20 @@ class SyncMLSubscription(XMLObject):
...
@@ -229,16 +413,20 @@ class SyncMLSubscription(XMLObject):
% (self.getRelativeUrl()))
% (self.getRelativeUrl()))
# XXX To be done later
security.declarePrivate('applyActionList')
def
_applyAddCommand
(
self
,
):
def
applyActionList(self, syncml_request, syncml_response, simulate=False
):
"""
"""
Apply the add command received, when document already exits, we
Browse the list of sync command received, apply them and generate answer
do a kind of "Replace" command instead
"""
"""
pass
for action in syncml_request.sync_command_list:
self._applySyncCommand(
action=action,
request_message_id=syncml_request.header["
message_id
"],
syncml_response=syncml_response,
simulate=simulate)
security.declarePrivate('applySyncCommand')
security.declarePrivate('applySyncCommand')
def
applySyncCommand
(
self
,
action
,
request_message_id
,
syncml_response
,
def
_
applySyncCommand(self, action, request_message_id, syncml_response,
simulate=False):
simulate=False):
"""
"""
Apply a sync command received
Apply a sync command received
...
@@ -255,6 +443,7 @@ class SyncMLSubscription(XMLObject):
...
@@ -255,6 +443,7 @@ class SyncMLSubscription(XMLObject):
# First retrieve the GID of the object we want to modify
# First retrieve the GID of the object we want to modify
gid = action["
source
"] or action["
target
"]
gid = action["
source
"] or action["
target
"]
# Retrieve the signature for the current GID
# Retrieve the signature for the current GID
path_list = []
signature = self.getSignatureFromGid(gid)
signature = self.getSignatureFromGid(gid)
if syncml_response is not None: # No response to send when no signature to create
if syncml_response is not None: # No response to send when no signature to create
document = self.getDocumentFromGid(gid)
document = self.getDocumentFromGid(gid)
...
@@ -265,15 +454,12 @@ class SyncMLSubscription(XMLObject):
...
@@ -265,15 +454,12 @@ class SyncMLSubscription(XMLObject):
portal_type='SyncML Signature',
portal_type='SyncML Signature',
id=gid,
id=gid,
)
)
syncml_logger
.
debug
(
"Created a signature for %s - document : %s"
syncml_logger.
info
("
Created
a
signature
for
%
s
-
document
:
%
s
"
% (signature.getPath(), document))
% (signature.getPath(), document))
if document is not None:
if document is not None:
signature.setReference(document.getPath())
signature.setReference(document.getPath())
elif
signature
.
getValidationState
()
==
'synchronized'
:
path_list.append(signature.getPath())
# Reset status of signature synchronization
signature
.
drift
()
force = signature.isForce() # XXX-must check the use of this later
force = signature.isForce() # XXX-must check the use of this later
else:
else:
force = True # Always erease data in this mode
force = True # Always erease data in this mode
...
@@ -291,7 +477,6 @@ class SyncMLSubscription(XMLObject):
...
@@ -291,7 +477,6 @@ class SyncMLSubscription(XMLObject):
if not action['more_data']:
if not action['more_data']:
# This is the last chunk of a partial xml
# This is the last chunk of a partial xml
# or this is just an entire data chunk
# or this is just an entire data chunk
if signature and signature.hasPartialData():
if signature and signature.hasPartialData():
# Build data with already stored data
# Build data with already stored data
signature.appendPartialData(incoming_data)
signature.appendPartialData(incoming_data)
...
@@ -304,7 +489,7 @@ class SyncMLSubscription(XMLObject):
...
@@ -304,7 +489,7 @@ class SyncMLSubscription(XMLObject):
if document is None:
if document is None:
# This is the default behaviour when getting an "
Add
" command
# This is the default behaviour when getting an "
Add
" command
# we create new document from the received data
# we create new document from the received data
syncml_logger
.
debug
(
"Calling addNode with no previous document found"
)
syncml_logger.
info
("
Calling
addNode
with
no
previous
document
found
")
add_data = conduit.addNode(xml=incoming_data,
add_data = conduit.addNode(xml=incoming_data,
object=destination,
object=destination,
signature=signature,
signature=signature,
...
@@ -390,23 +575,29 @@ class SyncMLSubscription(XMLObject):
...
@@ -390,23 +575,29 @@ class SyncMLSubscription(XMLObject):
elif action['command'] == 'Delete':
elif action['command'] == 'Delete':
status_code="
success
"
status_code="
success
"
document = self.getDocumentFromGid(signature.getId())
document = self.getDocumentFromGid(signature.getId())
syncml_logger.info("
Deleting
signature
%
s
&
doc
%
s
" %(signature.getPath(),
document.getPath()))
path_list.remove(signature.getPath())
if document is not None:
if document is not None:
# XXX Can't we get conflict ?
# XXX Can't we get conflict ?
# XXX Review the code to prevent retrieving document
conduit.deleteNode(xml=incoming_data,
conduit.deleteNode(xml=incoming_data,
object=destination,
object=destination,
object_id=document.getId())
object_id=document.getId())
# Delete signature
# Delete signature
self._delObject(gid)
self._delObject(gid)
else:
else:
syncml_logger
.
error
(
"Document with gid is already deleted"
syncml_logger.error("
Document
with
gid
%
s
is
already
deleted
"
% (gid,))
% (gid,))
self.z_delete_data_from_path(path="
%
s
" %(signature.getPath(),))
else:
else:
raise ValueError("
Unknown
command
%
s
" %(action['command'],))
raise ValueError("
Unknown
command
%
s
" %(action['command'],))
# Now update signature status regarding conflict list
# Now update signature status regarding conflict list
if action['command'] != "
Delete
" and signature:
if action['command'] != "
Delete
" and signature:
if len(conflict_list):
if len(conflict_list):
status_code
=
"conflict"
status_code
=
"
conflict
"
signature.changeToConflict()
signature.changeToConflict()
# Register the data received which generated the diff
# Register the data received which generated the diff
# XXX Why ?
# XXX Why ?
...
@@ -417,8 +608,8 @@ class SyncMLSubscription(XMLObject):
...
@@ -417,8 +608,8 @@ class SyncMLSubscription(XMLObject):
else:
else:
signature.setData(str(xml_document))
signature.setData(str(xml_document))
signature.synchronize()
signature.synchronize()
syncml_logger
.
debug
(
"change state of signature to
%s"
syncml_logger.
info("
change
state
of
signature
to
%
s
with
%
s
"
%
(
signature
.
getValidationState
(),))
% (signature.getValidationState(),
signature.getData()
))
if signature:
if signature:
# Generate status about the object synchronized
# Generate status about the object synchronized
...
@@ -432,9 +623,8 @@ class SyncMLSubscription(XMLObject):
...
@@ -432,9 +623,8 @@ class SyncMLSubscription(XMLObject):
message_ref=request_message_id)
message_ref=request_message_id)
else: # We want to retrieve more data
else: # We want to retrieve more data
syncml_logger
.
debug
(
"we need to retrieve more data for %s"
%
(
signature
,))
syncml_logger.info("
we
need
to
retrieve
more
data
for
%
s
"
if
signature
.
getValidationState
()
!=
'partial'
:
% (signature.getRelativeUrl(),))
signature
.
changeToPartial
()
signature.appendPartialData(incoming_data)
signature.appendPartialData(incoming_data)
# XXX Must check if size is present into the xml
# XXX Must check if size is present into the xml
# if not, client might ask it to server with a 411 alert
# if not, client might ask it to server with a 411 alert
...
@@ -455,294 +645,252 @@ class SyncMLSubscription(XMLObject):
...
@@ -455,294 +645,252 @@ class SyncMLSubscription(XMLObject):
source=self.getSourceReference(),
source=self.getSourceReference(),
last_anchor=self.getLastAnchor(),
last_anchor=self.getLastAnchor(),
next_anchor=self.getNextAnchor())
next_anchor=self.getNextAnchor())
# Index signature with their new value
if len(path_list):
self.SQLCatalog_indexSyncMLDocumentList(path_list)
def _sendFinalMessage(self):
security
.
declarePrivate
(
'applyActionList'
)
def
applyActionList
(
self
,
syncml_request
,
syncml_response
,
simulate
=
False
):
"""
"""
Browse the list of sync command received, apply them and generate answer
Send an empty message containing the final tag to notify the end of
the "
sending_modification
" stage of the synchronization
"""
"""
for
action
in
syncml_request
.
sync_command_list
:
syncml_response = self.generateBaseResponse()
self
.
applySyncCommand
(
syncml_response.addFinal()
action
=
action
,
request_message_id
=
syncml_request
.
header
[
"message_id"
],
syncml_response
=
syncml_response
,
simulate
=
simulate
)
def
_getDeletedData
(
self
,
syncml_response
=
None
):
final_activate_kw = {
"""
'after_method_id' : ("
processServerSynchronization
",
Add delete command to syncml resposne
"
processClientSynchronization
"),
"""
'priority' :ACTIVITY_PRIORITY + 1,
# XXX property must be renamed to activity_enabled
'tag' : "
%
s_delete
" %(self.getRelativeUrl(),)
if
self
.
getIsActivityEnabled
():
}
self
.
recurseCallMethod
(
syncml_logger.info("
Sending
final
message
for
modificationson
on
%
s
"
method_id
=
"getId"
,
min_depth
=
1
,
max_depth
=
1
,
activate_kw
=
{
'priority'
:
ACTIVITY_PRIORITY
,
'group_method_id'
:
"%s/checkAndSendDeleteMessage"
%
(
self
.
getRelativeUrl
()),
'tag'
:
"%s_delete"
%
self
.
getRelativeUrl
()})
self
.
activate
(
after_tag
=
"%s_delete"
%
(
self
.
getRelativeUrl
()),
priority
=
ACTIVITY_PRIORITY
+
1
,
).
_sendFinalMessage
()
else
:
# XXX not efficient at all but must not be used (former way)
syncml_logger
.
warning
(
"Using non-efficient way to retrieve delete object on %s"
% (self.getRelativeUrl(),))
% (self.getRelativeUrl(),))
id_list
=
[
x
.
getId
()
for
x
in
self
.
objectValues
()
if
\
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
x
.
getValidationState
()
==
"not_synchronized"
]
for
gid
in
id_list
:
syncml_response
.
addDeleteCommand
(
gid
=
gid
)
def
_sendFinalMessage
(
self
):
def
getDeletedSyncMLData(self, syncml_response=None
):
"""
"""
Send an empty message containing the final tag to notify the end of
Retrieve & generate the syncml message for messages that were deleted
the "sending_modification" stage of the synchronization
This message also contains the final tag to let know that the sending
of modification is over
"""
"""
syncml_response
=
SyncMLResponse
()
if not syncml_response:
syncml_response
.
addHeader
(
syncml_response = self.generateBaseResponse()
session_id
=
self
.
getSessionId
(),
message_id
=
self
.
getNextMessageId
(),
# Compare gid between signature & source to know which data were deleted
target
=
self
.
getUrlString
(),
deleted_signature_set = self.z_get_syncml_deleted_gid_list(
source
=
self
.
getSubscriptionUrlString
())
signature_path=self.getSearchablePath(),
syncml_response
.
addBody
()
source_path=self.getSearchableSourcePath())
syncml_logger.info("
\
t
--->
delete
signature
are
%
r" % (len(deleted_signature_set)))
for r in deleted_signature_set:
syncml_response.addDeleteCommand(gid=r.gid)
syncml_logger.info("
\
t
\
t
--->
%
r" % (r.gid))
syncml_response.addFinal()
syncml_response.addFinal()
# Now send the message
final_activate_kw = {
final_activate_kw = {
'after_method_id' : ("
processServerSynchronization
",
'after_method_id' : ("
processServerSynchronization
",
"
processClientSynchronization
"),
"
processClientSynchronization
"),
'priority' :ACTIVITY_PRIORITY + 1,
'priority' :ACTIVITY_PRIORITY + 1,
'tag' : "
%
s_delete
" %(self.getRelativeUrl(),)
'tag' : "
%
s_delete
" %(self.getRelativeUrl(),)
}
}
syncml_logger
.
warning
(
"Sending final message for modificationson on %s"
syncml_logger.
info
("
Sending
final
message
for
modificationson
on
%
s
"
% (self.getRelativeUrl(),))
% (self.getRelativeUrl(),))
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
self.activate(**final_activate_kw).sendMessage(xml=str(syncml_response))
def getSearchablePath(self):
return "
%
s
%%
" %(self.getPath().replace('_', '
\
_
'
),)
def
checkAndSendDeleteMessage
(
self
,
message_list
):
def _generateSyncCommand(self, action, signature, data_diff ,document_data, gid,
conduit, syncml_response):
"""
"""
This is a group method that will be invoked for a message list
Generate a sync command for a given data
It check signature synchronization state to know which one has
to be deleted and send the syncml message
"""
"""
syncml_logger
.
warning
(
"Checking deleted signature on %s"
more_data = False
%
(
self
.
getRelativeUrl
(),))
if signature:
to_delete_id_list
=
[]
if len(data_diff) > MAX_LEN and not self.getIsActivityEnabled():
for
m
in
message_list
:
# XXX-Aurel : I do not think splitting is working when running in activity
if
m
[
0
].
getValidationState
()
==
"not_synchronized"
:
syncml_logger.info("
data
for
%
s
too
big
,
splitting
...
" %(signature.getPath(),))
to_delete_id_list
.
append
(
m
[
0
].
getId
())
more_data = True
syncml_logger
.
warning
(
"
\
t
deleted object is %s"
data_diff, rest_string = cutXML(data_diff, MAX_LEN)
%
(
to_delete_id_list
,))
# Store the remaining data to send it later
if
len
(
to_delete_id_list
):
signature.setPartialData(rest_string)
syncml_response
=
SyncMLResponse
()
signature.setPartialAction(action)
syncml_response
.
addHeader
(
else:
session_id
=
self
.
getSessionId
(),
# The data will be copied in 'data' property once we get
message_id
=
self
.
getNextMessageId
(),
# confirmation that the document was well synchronized
target
=
self
.
getUrlString
(),
signature.setTemporaryData(document_data)
source
=
self
.
getSubscriptionUrlString
())
syncml_response
.
addBody
()
for
gid
in
to_delete_id_list
:
syncml_response
.
addDeleteCommand
(
gid
=
gid
)
syncml_logger
.
info
(
"%s sendDeleteCommand for %s"
# Generate the message
%
(
self
.
getRelativeUrl
(),
to_delete_id_list
))
syncml_logger.info("
adding
sync
command
%
s
for
%
s
" %(action, gid))
self
.
activate
(
activity
=
"SQLQueue"
,
syncml_response.addSyncCommand(
tag
=
"%s_delete"
%
(
self
.
getRelativeUrl
(),),
sync_command=action,
priority
=
ACTIVITY_PRIORITY
).
sendMessage
(
xml
=
str
(
syncml_response
))
gid=gid,
data=data_diff,
more_data=more_data,
media_type=conduit.getContentType())
return more_data
def
_getSyncMLData
(
self
,
syncml_response
,
id_list
=
None
):
def _getSyncMLData(self, syncml_response,
min_gid, max_gid
):
"""
"""
XXX Comment to be fixed
Compare data from source with data stored in signature from previous
synchronization. If there is any change, add command into the syncml
message
syncml_response : SyncML message to fill with command
min_gid = the lower limit for browsing data
max_gid = the upper limit for browsing data
"""
"""
if
not
id_list
:
syncml_logger.info("
getSyncMLData
,
min
%
s
-
max
%
r" % (min_gid, max_gid,))
syncml_logger
.
warning
(
"Non optimal call to _getSyncMLData, no id list provided : %r"
%
(
id_list
))
else
:
syncml_logger
.
info
(
"getSyncMLData, id list provided %s"
%
(
id_list
,))
conduit = self.getConduit()
conduit = self.getConduit()
finished
=
True
portal = self.getPortalObject()
traverse = portal.restrictedTraverse
if
isinstance
(
conduit
,
basestring
):
# Check deletion now ?
conduit
=
getConduitByName
(
conduit
)
if portal.portal_preferences.getPreferredCheckDeleteAtEnd() is False:
raise NotImplementedError
try
:
object_list = self.z_get_syncml_path_list(
object_list
=
self
.
getDocumentList
(
id_list
=
id_list
)
min_gid=min_gid,
except
TypeError
:
max_gid=max_gid,
# Old style script
path=self.getSearchableSourcePath())
warn
(
"Script %s does not accept id_list paramater"
%
(
self
.
getListMethodId
(),),
DeprecationWarning
)
syncml_logger.info("
getSyncMLData
,
object
list
is
%
s
" % ([x.path for x in object_list]))
object_list
=
self
.
getDocumentList
()
loop
=
0
traverse
=
self
.
getPortalObject
().
restrictedTraverse
alert_code = self.getSyncmlAlertCode()
alert_code = self.getSyncmlAlertCode()
sync_all = alert_code in ("
refresh_from_client_only
", "
slow_sync
")
sync_all = alert_code in ("
refresh_from_client_only
", "
slow_sync
")
# XXX Quick & dirty hack to prevent signature creation, this must be defined
# XXX Quick & dirty hack to prevent signature creation, this must be defined
# on pub/sub instead
# on pub/sub instead
create_signature = alert_code != "
refresh_from_client_only
"
create_signature = alert_code != "
refresh_from_client_only
"
if
not
len
(
object_list
)
and
id_list
:
if not len(object_list) and
(min_gid or max_gid)
:
syncml_logger
.
warning
(
"No object retrieved althoud id_list (
%s) is provided"
raise ValueError("
No
object
retrieved
althoud
min
/
max
gid
(
%
s
/
%
s
)
is
provided
"
%
(
id_list
))
% (
min_gid, max_gid
))
more_data = False
for result in object_list:
for result in object_list:
object_path
=
result
.
getPath
()
# XXX We need a way to stop the loop when we reach a given packet size
# if loop >= max_range:
document_path = result.path
# # For now, maximum object list is always none, so we will never come here !
gid = result.gid
# syncml_logger.warning("...Send too many objects, will split message...")
document_data = result.data
# finished = False
# break
# Get the GID
document
=
traverse
(
object_path
)
gid
=
self
.
getGidFromObject
(
document
)
if
not
gid
:
raise
ValueError
(
"Impossible to compute gid for %s"
%
(
object_path
))
if
True
:
# not loop: # or len(syncml_response) < MAX_LEN:
# XXX must find a better way to prevent sending
# no object due to a too small limit
signature = self.getSignatureFromGid(gid)
signature = self.getSignatureFromGid(gid)
more_data
=
False
if signature:
# For the case it was never synchronized, we have to send everything
syncml_logger.info("
signature
is
%
s
=
%
s
" %(signature.getRelativeUrl(),
if
not
signature
or
sync_all
:
signature.getValidationState()))
# First time we send this object or the synchronization more required
# to send every data as it was never synchronized before
document_data
=
conduit
.
getXMLFromObjectWithId
(
# XXX To be renamed (getDocumentData) independant from format
document
,
xml_mapping
=
self
.
getXmlBindingGeneratorMethodId
(),
context_document
=
self
.
getPath
())
if not document_data:
if not document_data:
continue
raise ValueError("
No
data
for
%
s
/
%
s
" %(gid, document_path))
# For the case it was never synchronized, we have to send everything
if not signature or sync_all:
# Either it is the first time we get this object
# either the synchronization process required
# to send every data again as if it was never done before
if create_signature:
if create_signature:
if not signature:
if not signature:
signature = self.newContent(portal_type='SyncML Signature',
signature = self.newContent(portal_type='SyncML Signature',
id=gid,
id=gid,
reference
=
document
.
getPath
()
,
reference=document_path
,
temporary_data=document_data)
temporary_data=document_data)
syncml_logger
.
debug
(
"Created a signature %s for gid = %s, path %s"
syncml_logger.info("
Created
a
signature
%
s
for
gid
=
%
s
,
path
%
s
"
%
(
signature
.
getPath
(),
gid
,
document
.
getPath
()))
% (signature.getPath(), gid, document_path))
if
len
(
document_data
)
>
MAX_LEN
:
more_data = self._generateSyncCommand(
syncml_logger
.
debug
(
"data too big, sending multiple message"
)
action=ADD_ACTION,
more_data
=
True
signature=signature,
finished
=
False
data_diff=document_data,
document_data
,
rest_string
=
cutXML
(
document_data
,
MAX_LEN
)
document_data=document_data,
# Store the remaining data to send it later
gid=gid,
signature
.
setPartialData
(
rest_string
)
conduit=conduit,
signature
.
setPartialAction
(
ADD_ACTION
)
syncml_response=syncml_response)
signature
.
changeToPartial
()
else
:
# The data will be copied in 'data' property once we get
# confirmation that the document was well synchronized
signature
.
setTemporaryData
(
document_data
)
signature
.
doSync
()
syncml_logger
.
debug
(
"signature %s is syncing"
%
(
signature
.
getRelativeUrl
(),))
# Generate the message
elif signature.hasPartialData():
# Case of partially sent data
# XXX Cutting must be managed by conduit
# Here it is too specific to XML data
xml_string = signature.getFirstPdataChunk(MAX_LEN)
if signature.hasPartialData():
more_data = True
# We need to convert XML to a CDATA type to prevent collision
# with syncml's XML
document_data = etree.CDATA(xml_string.decode('utf-8'))
syncml_logger.info("
adding
partial
sync
command
for
%
s
" %(gid,))
syncml_response.addSyncCommand(
syncml_response.addSyncCommand(
sync_command
=
ADD_ACTION
,
sync_command=signature.getPartialAction()
,
gid=gid,
gid=gid,
data=document_data,
data=document_data,
more_data=more_data,
more_data=more_data,
media_type=conduit.getContentType())
media_type=conduit.getContentType())
elif
signature
.
getValidationState
()
in
(
'not_synchronized'
,
if not more_data:
syncml_logger.info("
signature
%
s
is
syncing
from
partial
"
% (signature.getRelativeUrl(),))
elif signature.getValidationState() in ('no_conflict',
'conflict_resolved_with_merge'):
'conflict_resolved_with_merge'):
# We don't have synchronized this object yet but it has a signature
# We don't have synchronized this object yet but it has a signature
xml_object
=
conduit
.
getXMLFromObjectWithId
(
document
,
xml_mapping
=
self
.
getXmlBindingGeneratorMethodId
(),
context_document
=
self
.
getPath
())
if signature.getValidationState() == 'conflict_resolved_with_merge':
if signature.getValidationState() == 'conflict_resolved_with_merge':
# XXX Why putting confirmation message here
# XXX Why putting confirmation message here
# Server can get confirmation of sync although it has not yet
# Server can get confirmation of sync although it has not yet
# send its data modification to the client
# send its data modification to the client
# This must be checked against specifications
# This must be checked against specifications
# Right now, this message will tell the other side to apply the
# diff without checking conflicts
# We then send the modifications
syncml_response.addConfirmationMessage(
syncml_response.addConfirmationMessage(
source_ref
=
signature
.
getId
()
,
source_ref=gid
,
sync_code='conflict_resolved_with_merge',
sync_code='conflict_resolved_with_merge',
command='Replace')
command='Replace')
if
not
signature
.
checkMD5
(
xml_object
):
syncml_logger.info("
\
tMD5
is
%
s
for
%
s
" %((signature.checkMD5(document_data)),
signature.getReference()))
if not signature.checkMD5(document_data):
# MD5 checksum tell there is a modification of the object
# MD5 checksum tell there is a modification of the object
# XXX this diff generation must managed by the conduit
# we just need to have conduit.generateDocumentDiff(new_data, former_data)
if conduit.getContentType() != 'text/xml':
if conduit.getContentType() != 'text/xml':
# If there is no xml, we re-send the whole object
# If there is no xml, we re-send the whole object
# XXX this must be managed by conduit ?
data_diff = document_data
data_diff
=
xml_object
else:
else:
# Compute the diff
# Compute the diff
new_document
=
conduit
.
replaceIdFromXML
(
xml_object
,
'gid'
,
gid
)
new_document = conduit.replaceIdFromXML(document_data
, 'gid', gid)
previous_document = conduit.replaceIdFromXML(signature.getData(),
previous_document = conduit.replaceIdFromXML(signature.getData(),
'gid', gid)
'gid', gid)
data_diff = conduit.generateDiff(new_data=new_document,
data_diff = conduit.generateDiff(new_data=new_document,
former_data=previous_document)
former_data=previous_document)
if not data_diff:
if not data_diff:
# MD5 Checksum can detect changes like <lang/> != <lang></lang>
# MD5 Checksum can detect changes like <lang/> != <lang></lang>
# but Diff generator will return no diff for it
# but Diff generator will return no diff for it
# in this case, no need to send diff
# in this case, no need to send diff
signature
.
synchronize
()
syncml_logger.info("
\
tFake
diff
,
signature
%
s
is
synchronized
"
syncml_logger
.
debug
(
"signature %s is synchronized"
% (signature.getRelativeUrl(),))
% (signature.getRelativeUrl(),))
continue
continue
# Split data if necessary
# Reindex modified document
if
len
(
data_diff
)
>
MAX_LEN
:
syncml_logger.info("
\
tGot
a
diff
for
%
s
:
%
s
" %(gid, data_diff))
syncml_logger
.
debug
(
"data too big, sending multiple messages"
)
more_data = self._generateSyncCommand(
more_data
=
True
action=REPLACE_ACTION,
finished
=
False
signature=signature,
data_diff
,
rest_string
=
cutXML
(
data_diff
,
MAX_LEN
)
data_diff=data_diff,
signature
.
setPartialData
(
rest_string
)
document_data=document_data,
signature
.
setPartialAction
(
REPLACE_ACTION
)
if
signature
.
getValidationState
()
!=
'partial'
:
signature
.
changeToPartial
()
syncml_logger
.
debug
(
"signature %s is partial"
%
(
signature
.
getRelativeUrl
(),))
else
:
# Store the new representation of the document
# It will be copy to "data" property once synchronization
# is confirmed
signature
.
setTemporaryData
(
xml_object
)
signature
.
doSync
()
syncml_logger
.
debug
(
"signature %s is syncing"
%
(
signature
.
getRelativeUrl
(),))
# Generate the command
syncml_logger
.
debug
(
"will send Replace command with %s"
%
(
data_diff
,))
syncml_response
.
addSyncCommand
(
sync_command
=
REPLACE_ACTION
,
gid=gid,
gid=gid,
data
=
data_diff
,
conduit=conduit,
more_data
=
more_data
,
syncml_response=syncml_response)
media_type
=
conduit
.
getContentType
())
elif
signature
.
getValidationState
()
!=
'synchronized'
:
# We should not have this case when we are in CONFLICT_MERGE
syncml_logger
.
debug
(
"signature %s is synchronized"
%
(
signature
.
getRelativeUrl
(),))
signature
.
synchronize
()
elif signature.getValidationState() ==
\
elif signature.getValidationState() ==
\
'conflict_resolved_with_client_command_winning':
'conflict_resolved_with_client_command_winning':
# We have decided to apply the update
# We have decided to apply the update
# XXX previous_xml will be ge
XML instead of getTempXML because
# XXX previous_xml will be get
XML instead of getTempXML because
# some modification was already made and the update
# some modification was already made and the update
# may not apply correctly
# may not apply correctly
xml_update = signature.getPartialData()
xml_update = signature.getPartialData()
previous_xml_with_gid = conduit.replaceIdFromXML(signature.getData(),
previous_xml_with_gid = conduit.replaceIdFromXML(signature.getData(),
'gid', gid,
'gid', gid,
as_string=False)
as_string=False)
conduit
.
updateNode
(
xml
=
xml_update
,
object
=
document
,
conduit.updateNode(xml=xml_update, object=traverse(document_path)
,
previous_xml=previous_xml_with_gid, force=True,
previous_xml=previous_xml_with_gid, force=True,
gid=gid,
gid=gid,
signature=signature,
signature=signature,
...
@@ -754,46 +902,14 @@ class SyncMLSubscription(XMLObject):
...
@@ -754,46 +902,14 @@ class SyncMLSubscription(XMLObject):
signature.synchronize()
signature.synchronize()
syncml_logger.debug("
signature
%
s
is
synchronized
"
syncml_logger.debug("
signature
%
s
is
synchronized
"
% (signature.getRelativeUrl(),))
% (signature.getRelativeUrl(),))
elif
signature
.
getValidationState
()
==
'partial'
:
# Case of partially sent data
xml_string
=
signature
.
getPartialData
()
# XXX Cutting must be managed by conduit
# Here it is too specific to XML data
if
len
(
xml_string
)
>
MAX_LEN
:
syncml_logger
.
info
(
"Remaining data too big, splitting it..."
)
more_data
=
True
finished
=
False
xml_string
=
signature
.
getFirstPdataChunk
(
MAX_LEN
)
xml_string
=
etree
.
CDATA
(
xml_string
.
decode
(
'utf-8'
))
syncml_response
.
addSyncCommand
(
sync_command
=
signature
.
getPartialAction
(),
gid
=
gid
,
data
=
xml_string
,
more_data
=
more_data
,
media_type
=
self
.
getContentType
())
if
not
more_data
:
signature
.
doSync
()
syncml_logger
.
debug
(
"signature %s is syncing"
%
(
signature
.
getRelativeUrl
(),))
elif
signature
.
getValidationState
()
in
(
'syncing'
,
'synchronized'
):
raise
ValueError
(
"Must not get signature in %s state here, signature is %s"
%
(
signature
.
getValidationState
(),
signature
.
getPath
(),))
if
not
more_data
:
if more_data:
pass
else
:
syncml_logger.info("
Splitting
document
")
syncml_logger.info("
Splitting
document
")
break
break
else
:
syncml_logger
.
warning
(
"Package is going to be splitted"
)
syncml_logger.info("
_getSyncMLData
end
with
more_data
%
s
"
break
% (more_data,))
loop
+=
1
return not more_data
syncml_logger
.
debug
(
"_getSyncMLData end with finished %s"
%
(
finished
,))
return
finished
security.declareProtected(Permissions.AccessContentsInformation,
security.declareProtected(Permissions.AccessContentsInformation,
'getConduit')
'getConduit')
...
@@ -828,6 +944,18 @@ class SyncMLSubscription(XMLObject):
...
@@ -828,6 +944,18 @@ class SyncMLSubscription(XMLObject):
else:
else:
return self._baseGetXmlBindingGeneratorMethodId(default=default)
return self._baseGetXmlBindingGeneratorMethodId(default=default)
security.declareProtected(Permissions.AccessContentsInformation,
'getDataFromDocument')
def getDataFromDocument(self, document):
"""
Return the data (xml or other) for a given document
"""
return self.getConduit().getXMLFromObjectWithId(
document,
xml_mapping=self.getXmlBindingGeneratorMethodId(),
context_document=self.getPath())
security.declareProtected(Permissions.AccessContentsInformation,
security.declareProtected(Permissions.AccessContentsInformation,
'getGidFromObject')
'getGidFromObject')
def getGidFromObject(self, object, encoded=True):
def getGidFromObject(self, object, encoded=True):
...
@@ -900,6 +1028,9 @@ class SyncMLSubscription(XMLObject):
...
@@ -900,6 +1028,9 @@ class SyncMLSubscription(XMLObject):
try:
try:
result_list = query_method(context_document=self, **kw)
result_list = query_method(context_document=self, **kw)
except TypeError:
except TypeError:
if not RETRO_COMPATIBLE:
raise
else:
result_list = query_method(**kw)
result_list = query_method(**kw)
else:
else:
raise KeyError, 'This Subscriber %s provide no list method:%r'
\
raise KeyError, 'This Subscriber %s provide no list method:%r'
\
...
@@ -1029,21 +1160,47 @@ class SyncMLSubscription(XMLObject):
...
@@ -1029,21 +1160,47 @@ class SyncMLSubscription(XMLObject):
return conflict_list
return conflict_list
security.declareProtected(Permissions.ModifyPortalContent,
security.declareProtected(Permissions.ModifyPortalContent,
'initialiseSynchronization'
)
'indexSourceData')
def
initialiseSynchronization
(
self
):
def indexSourceData(self, client=False):
"""
"""
Set the status of every signature as not_synchronized
Index source data into mysql for ensemble comparison
"""
This depends on synchronization type
"""
# XXX Must check & index signature also (check lenght of BTree against
# lenght of data in sql
if (client and self.getSyncmlAlertCode() not in
\
("
one_way_from_server
", "
refresh_from_server_only
")) or
\
(not client and self.getSyncmlAlertCode() not in
\
("
one_way_from_client
", "
refresh_from_client_only
")):
portal = self.getPortalObject()
# First we must unindex everything
portal.z_unindex_syncml_data(path=self.getSearchableSourcePath())
if self.getIsActivityEnabled():
if self.getIsActivityEnabled():
self
.
getAndActivateResetSignature
()
activate_kw = {
'activity' : 'SQLQueue',
'tag' : self.getRelativeUrl(),
'priority' :ACTIVITY_PRIORITY
}
pref = portal.portal_preferences
if pref.getPreferredSplitIndexation():
kw = {'packet_size' : pref.getPreferredDocumentRetrievedPerActivityCount(),
'activity_count' : pref.getPreferredRetrievalActivityCount()}
else:
else:
for
signature
in
self
.
contentValues
(
portal_type
=
'SyncML Signature'
):
kw = {}
# Change the status only if we are not in a conflict mode
self.getAndIndex(
if
signature
.
getValidationState
()
not
in
(
callback="
SQLCatalog_indexSyncMLDocumentList
",
'conflict'
,
method_kw={'subscription_path' : self.getRelativeUrl()},
'conflict_resolved_with_merge'
,
activate_kw=activate_kw,
'conflict_resolved_with_client_command_winning'
):
**kw
signature
.
reset
()
)
else:
r = [x.getPath() for x in self.getDocumentList()]
syncml_logger.info("
indexing
data
from
%
s
:
%
r" %(self.getPath(), r))
portal.SQLCatalog_indexSyncMLDocumentList(
path_list=r[:],
subscription_path=self.getRelativeUrl())
security.declareProtected(Permissions.ModifyPortalContent,
security.declareProtected(Permissions.ModifyPortalContent,
'getAndActivateResetSignature')
'getAndActivateResetSignature')
...
...
product/ERP5SyncML/Engine/AsynchronousEngine.py
View file @
a270c222
...
@@ -65,7 +65,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -65,7 +65,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
subscription
.
sendModifications
()
# Worfklow action
subscription
.
sendModifications
()
# Worfklow action
syncml_response
=
None
syncml_response
=
None
tag
=
subscription
_path
=
subscription
.
getRelativeUrl
()
tag
=
subscription
.
getRelativeUrl
()
# Do action according to synchronization state
# Do action according to synchronization state
if
subscription
.
getSynchronizationState
()
==
"initializing"
:
if
subscription
.
getSynchronizationState
()
==
"initializing"
:
...
@@ -75,10 +75,13 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -75,10 +75,13 @@ class SyncMLAsynchronousEngine(EngineMixin):
if
subscription
.
getSyncmlAlertCode
()
in
(
"one_way_from_server"
,
if
subscription
.
getSyncmlAlertCode
()
in
(
"one_way_from_server"
,
"refresh_from_server_only"
):
"refresh_from_server_only"
):
# We only get data from server
# We only get data from server
syncml_response
=
s
elf
.
_generateBaseResponse
(
subscription
)
syncml_response
=
s
ubscription
.
generateBaseResponse
(
)
syncml_response
.
addFinal
()
syncml_response
.
addFinal
()
else
:
else
:
self
.
runGetAndActivate
(
subscription
=
subscription
,
tag
=
tag
)
# Make sure it is launched after indexation step
self
.
runGetAndActivate
(
subscription
=
subscription
,
tag
=
tag
,
after_method_id
=
(
"getAndIndex"
,
"SQLCatalog_indexSyncMLSignatureList"
))
syncml_logger
.
info
(
"X-> Client is sendind modification in activities"
)
syncml_logger
.
info
(
"X-> Client is sendind modification in activities"
)
# As we generated all activities to send data at once, process must not
# As we generated all activities to send data at once, process must not
# go back here, go into processing state thus status will be applied and
# go back here, go into processing state thus status will be applied and
...
@@ -99,7 +102,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -99,7 +102,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
%
(
len
(
syncml_request
.
sync_command_list
)))
%
(
len
(
syncml_request
.
sync_command_list
)))
if
syncml_request
.
isFinal
:
if
syncml_request
.
isFinal
:
if
not
syncml_response
:
if
not
syncml_response
:
syncml_response
=
s
elf
.
_generateBaseResponse
(
subscription
)
syncml_response
=
s
ubscription
.
generateBaseResponse
(
)
# We got and process all sync command from server
# We got and process all sync command from server
# notify it that all modifications were applied
# notify it that all modifications were applied
syncml_response
.
addFinal
()
syncml_response
.
addFinal
()
...
@@ -163,10 +166,10 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -163,10 +166,10 @@ class SyncMLAsynchronousEngine(EngineMixin):
# Apply command & send modifications
# Apply command & send modifications
# Apply status about object send & synchronized if any
# Apply status about object send & synchronized if any
s
ync_status_counter
=
s
elf
.
_readStatusList
(
syncml_request
,
subscriber
,
self
.
_readStatusList
(
syncml_request
,
subscriber
,
generate_alert
=
True
)
generate_alert
=
True
)
syncml_response
=
None
syncml_response
=
None
tag
=
subscri
ption_path
=
subscri
ber
.
getRelativeUrl
()
tag
=
subscriber
.
getRelativeUrl
()
after_method_id
=
None
after_method_id
=
None
if
subscriber
.
getSynchronizationState
()
==
"sending_modifications"
:
if
subscriber
.
getSynchronizationState
()
==
"sending_modifications"
:
if
syncml_request
.
isFinal
:
if
syncml_request
.
isFinal
:
...
@@ -202,15 +205,13 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -202,15 +205,13 @@ class SyncMLAsynchronousEngine(EngineMixin):
if
syncml_request
.
isFinal
:
if
syncml_request
.
isFinal
:
# Server then sends its modifications
# Server then sends its modifications
subscriber
.
sendModifications
()
subscriber
.
sendModifications
()
# Now that everything is ok, init sync information
# Run indexation only once client have sent its modifications
if
subscriber
.
getSyncmlAlertCode
()
not
in
(
"one_way_from_client"
,
subscriber
.
indexSourceData
()
"refresh_from_client_only"
):
# Reset signature only if we have to check modifications on server side
subscriber
.
initialiseSynchronization
()
# Start to send modification only once we have processed
# Start to send modification only once we have processed
# all message from client
# all message from client
after_method_id
=
'processServerSynchronization'
,
after_method_id
=
(
'processServerSynchronization'
,
'SQLCatalog_indexSyncMLDocumentList'
)
# XXX after tag might also be required to make sure all data are indexed
tag
=
(
tag
,
"%s_reset"
%
subscriber
.
getPath
(),)
tag
=
(
tag
,
"%s_reset"
%
subscriber
.
getPath
(),)
# Do not continue in elif, as sending modifications is done in the same
# Do not continue in elif, as sending modifications is done in the same
# package as sending notifications
# package as sending notifications
...
@@ -230,7 +231,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -230,7 +231,7 @@ class SyncMLAsynchronousEngine(EngineMixin):
# Server has no modification to send to client, return final message
# Server has no modification to send to client, return final message
syncml_logger
.
info
(
"X-> Server sending final message"
)
syncml_logger
.
info
(
"X-> Server sending final message"
)
if
not
syncml_response
:
if
not
syncml_response
:
syncml_response
=
s
elf
.
_generateBaseResponse
(
subscriber
)
syncml_response
=
s
ubscriber
.
generateBaseResponse
(
)
syncml_response
.
addFinal
()
syncml_response
.
addFinal
()
if
subscriber
.
getSynchronizationState
()
==
"finished"
:
if
subscriber
.
getSynchronizationState
()
==
"finished"
:
...
@@ -242,10 +243,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -242,10 +243,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
after_tag
=
tag
).
sendMessage
(
after_tag
=
tag
).
sendMessage
(
xml
=
str
(
syncml_response
))
xml
=
str
(
syncml_response
))
def
runGetAndActivate
(
self
,
subscription
,
tag
,
after_method_id
=
None
):
def
runGetAndActivate
(
self
,
subscription
,
tag
,
after_method_id
=
None
):
"""
"""
Generate tag and method parameter and call the getAndActivate method
Launch the browsing of GID that will call the generation of syncml commands
"""
"""
activate_kw
=
{
activate_kw
=
{
'activity'
:
'SQLQueue'
,
'activity'
:
'SQLQueue'
,
...
@@ -253,20 +253,20 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -253,20 +253,20 @@ class SyncMLAsynchronousEngine(EngineMixin):
'tag'
:
tag
,
'tag'
:
tag
,
'priority'
:
ACTIVITY_PRIORITY
'priority'
:
ACTIVITY_PRIORITY
}
}
method_kw
=
{
'subscription_path'
:
subscription
.
getRelativeUrl
(),
}
pref
=
getSite
().
portal_preferences
pref
=
getSite
().
portal_preferences
count
=
subscription
.
getAndActivate
(
subscription
.
getAndActivate
(
callback
=
"sendSyncCommand"
,
callback
=
"sendSyncCommand"
,
method_kw
=
method_kw
,
activate_kw
=
activate_kw
,
activate_kw
=
activate_kw
,
packet_size
=
pref
.
getPreferredDocumentRetrievedPerActivityCount
(),
packet_size
=
pref
.
getPreferredDocumentRetrievedPerActivityCount
(),
activity_count
=
pref
.
getPreferredRetrievalActivityCount
(),
activity_count
=
pref
.
getPreferredRetrievalActivityCount
(),
)
)
# Then get deleted document
# then send the final message of this sync part
# this will send also the final message of this sync part
if
pref
.
getPreferredCheckDeleteAtEnd
():
subscription
.
activate
(
after_tag
=
tag
).
_getDeletedData
()
subscription
.
activate
(
after_tag
=
tag
,
priority
=
ACTIVITY_PRIORITY
+
1
).
getDeletedSyncMLData
()
else
:
subscription
.
activate
(
after_tag
=
tag
,
priority
=
ACTIVITY_PRIORITY
+
1
).
_sendFinalMessage
()
return
True
return
True
...
@@ -284,9 +284,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -284,9 +284,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
response_id_list
=
[
None
for
x
in
response_id_list
=
[
None
for
x
in
xrange
(
len
(
syncml_request
.
sync_command_list
))]
xrange
(
len
(
syncml_request
.
sync_command_list
))]
split
=
getSite
().
portal_preferences
.
getPreferredSyncActionPerActivityCount
()
split
=
getSite
().
portal_preferences
.
getPreferredSyncActionPerActivityCount
()
if
not
split
:
if
not
split
:
# We do not use activities
if
send_response
:
if
send_response
:
syncml_response
=
s
elf
.
_generateBaseResponse
(
subscription
)
syncml_response
=
s
ubscription
.
generateBaseResponse
(
)
else
:
else
:
syncml_response
=
None
syncml_response
=
None
subscription
.
applyActionList
(
syncml_request
,
syncml_response
)
subscription
.
applyActionList
(
syncml_request
,
syncml_response
)
...
@@ -295,10 +295,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -295,10 +295,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
activity
=
"SQLQueue"
,
activity
=
"SQLQueue"
,
priority
=
ACTIVITY_PRIORITY
,
priority
=
ACTIVITY_PRIORITY
,
tag
=
subscription
.
getRelativeUrl
()).
sendMessage
(
xml
=
str
(
syncml_response
))
tag
=
subscription
.
getRelativeUrl
()).
sendMessage
(
xml
=
str
(
syncml_response
))
else
:
else
:
# XXX For now always split by one
# XXX For now always split by one
activate
=
subscription
.
getPortalObject
().
portal_synchronizations
.
activate
activate
=
subscription
.
activate
activate_kw
=
{
activate_kw
=
{
"activity"
:
"SQLQueue"
,
"activity"
:
"SQLQueue"
,
"priority"
:
ACTIVITY_PRIORITY
,
"priority"
:
ACTIVITY_PRIORITY
,
...
@@ -309,10 +308,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
...
@@ -309,10 +308,9 @@ class SyncMLAsynchronousEngine(EngineMixin):
for
action
in
syncml_request
.
sync_command_list
:
for
action
in
syncml_request
.
sync_command_list
:
syncml_logger
.
info
(
"---> launch action in activity %s"
%
(
action
,))
syncml_logger
.
info
(
"---> launch action in activity %s"
%
(
action
,))
activate
(
**
activate_kw
).
applySyncCommand
(
activate
(
**
activate_kw
).
applySyncCommand
(
subscription_path
=
subscription
.
getRelativeUrl
(),
response_message_id
=
response_id_list
.
pop
(),
response_message_id
=
response_id_list
.
pop
(),
activate_kw
=
activate_kw
,
activate_kw
=
activate_kw
,
action
=
action
,
action
=
action
,
request_message_id
=
syncml_request
.
header
[
"message_id"
],
request_message_id
=
syncml_request
.
header
[
"message_id"
],
simulate
=
False
)
simulate
=
False
)
#
XXX Response is not send here
#
Response is sent by the activity
product/ERP5SyncML/Engine/EngineMixin.py
View file @
a270c222
...
@@ -46,15 +46,6 @@ class EngineMixin(object):
...
@@ -46,15 +46,6 @@ class EngineMixin(object):
security
=
ClassSecurityInfo
()
security
=
ClassSecurityInfo
()
def
_generateBaseResponse
(
self
,
subscription
):
syncml_response
=
SyncMLResponse
()
syncml_response
.
addHeader
(
session_id
=
subscription
.
getSessionId
(),
message_id
=
subscription
.
getNextMessageId
(),
target
=
subscription
.
getUrlString
(),
source
=
subscription
.
getSubscriptionUrlString
())
syncml_response
.
addBody
()
return
syncml_response
security
.
declarePrivate
(
'_readStatusList'
)
security
.
declarePrivate
(
'_readStatusList'
)
def
_readStatusList
(
self
,
syncml_request
,
domain
,
syncml_response
=
None
,
def
_readStatusList
(
self
,
syncml_request
,
domain
,
syncml_response
=
None
,
...
@@ -63,6 +54,7 @@ class EngineMixin(object):
...
@@ -63,6 +54,7 @@ class EngineMixin(object):
Read status (answer to command) and act according to them
Read status (answer to command) and act according to them
"""
"""
sync_status_counter
=
0
sync_status_counter
=
0
path_list
=
[]
for
status
in
syncml_request
.
status_list
:
for
status
in
syncml_request
.
status_list
:
if
status
[
"command"
]
==
"SyncHdr"
:
# Check for authentication
if
status
[
"command"
]
==
"SyncHdr"
:
# Check for authentication
if
domain
.
getSynchronizationState
()
!=
"initializing"
:
if
domain
.
getSynchronizationState
()
!=
"initializing"
:
...
@@ -83,7 +75,7 @@ class EngineMixin(object):
...
@@ -83,7 +75,7 @@ class EngineMixin(object):
status
[
'authentication_type'
]))
status
[
'authentication_type'
]))
# XXX Not working To Review !
# XXX Not working To Review !
raise
NotImplementedError
(
"Adding credentials"
)
raise
NotImplementedError
(
"Adding credentials"
)
syncml_response
=
self
.
_generateBaseResponse
(
domain
)
syncml_response
=
domain
.
generateBaseResponse
(
)
syncml_response
.
addCredentialMessage
(
domain
)
syncml_response
.
addCredentialMessage
(
domain
)
return
syncml_response
return
syncml_response
elif
status
[
'status_code'
]
==
\
elif
status
[
'status_code'
]
==
\
...
@@ -124,7 +116,7 @@ class EngineMixin(object):
...
@@ -124,7 +116,7 @@ class EngineMixin(object):
'conflict_resolved_with_merge'
):
'conflict_resolved_with_merge'
):
# We will have to apply the update, and we should not care
# We will have to apply the update, and we should not care
# about conflicts, so we have to force the update
# about conflicts, so we have to force the update
signature
.
drif
t
()
signature
.
noConflic
t
()
signature
.
setForce
(
True
)
signature
.
setForce
(
True
)
syncml_logger
.
error
(
"
\
t
Object merged %s"
%
syncml_logger
.
error
(
"
\
t
Object merged %s"
%
(
status
[
'source'
]
or
status
[
'target'
]))
(
status
[
'source'
]
or
status
[
'target'
]))
...
@@ -134,20 +126,25 @@ class EngineMixin(object):
...
@@ -134,20 +126,25 @@ class EngineMixin(object):
'conflict_resolved_with_client_command_winning'
)):
'conflict_resolved_with_client_command_winning'
)):
syncml_logger
.
error
(
"
\
t
Object synchronized %s"
%
syncml_logger
.
error
(
"
\
t
Object synchronized %s"
%
(
status
[
'source'
]
or
status
[
'target'
],))
(
status
[
'source'
]
or
status
[
'target'
],))
if
signature
.
getValidationState
()
!=
"no_conflict"
:
signature
.
noConflict
()
signature
.
synchronize
()
signature
.
synchronize
()
elif
status
[
'status_code'
]
==
resolveSyncmlStatusCode
(
'chunk_accepted'
):
elif
status
[
'status_code'
]
==
resolveSyncmlStatusCode
(
'chunk_accepted'
):
syncml_logger
.
info
(
"Chunk was accepted for %s"
%
(
object_gid
,))
syncml_logger
.
info
(
"Chunk was accepted for %s"
%
(
object_gid
,))
else
:
else
:
raise
ValueError
(
"Unknown status code : %r"
%
(
status
[
'status_code'
],))
raise
ValueError
(
"Unknown status code : %r"
%
(
status
[
'status_code'
],))
# Index signature now to fill the data column
path_list
.
append
(
signature
.
getPath
())
elif
status
[
'command'
]
==
'Delete'
:
elif
status
[
'command'
]
==
'Delete'
:
sync_status_counter
+=
1
sync_status_counter
+=
1
object_gid
=
status
[
'source'
]
or
status
[
'target'
]
object_gid
=
status
[
'source'
]
or
status
[
'target'
]
signature
=
domain
.
getSignatureFromGid
(
object_gid
)
signature
=
domain
.
getSignatureFromGid
(
object_gid
)
if
status
[
'status_code'
]
==
resolveSyncmlStatusCode
(
'success'
):
if
status
[
'status_code'
]
==
resolveSyncmlStatusCode
(
'success'
):
if
signature
:
if
signature
:
domain
.
z_delete_data_from_path
(
path
=
signature
.
getPath
())
domain
.
_delObject
(
signature
.
getId
())
domain
.
_delObject
(
signature
.
getId
())
else
:
else
:
raise
ValueError
(
"Found no signature to delete
"
)
raise
ValueError
(
"Found no signature to delete
for gid %s"
%
(
object_gid
,)
)
else
:
else
:
raise
ValueError
(
"Unknown status code : %r"
%
(
status
[
'status_code'
],))
raise
ValueError
(
"Unknown status code : %r"
%
(
status
[
'status_code'
],))
syncml_logger
.
error
(
"
\
t
Object deleted %s"
%
syncml_logger
.
error
(
"
\
t
Object deleted %s"
%
...
@@ -155,6 +152,8 @@ class EngineMixin(object):
...
@@ -155,6 +152,8 @@ class EngineMixin(object):
else
:
else
:
raise
ValueError
(
"Unknown status command : %r"
%
(
status
[
'command'
],))
raise
ValueError
(
"Unknown status command : %r"
%
(
status
[
'command'
],))
if
len
(
path_list
):
domain
.
SQLCatalog_indexSyncMLDocumentList
(
path_list
)
return
sync_status_counter
return
sync_status_counter
#
#
...
@@ -191,10 +190,8 @@ class EngineMixin(object):
...
@@ -191,10 +190,8 @@ class EngineMixin(object):
if
subscription
.
getAuthenticationState
()
!=
'logged_in'
:
if
subscription
.
getAuthenticationState
()
!=
'logged_in'
:
# Workflow action
# Workflow action
subscription
.
login
()
subscription
.
login
()
if
subscription
.
getSyncmlAlertCode
()
not
in
(
"one_way_from_server"
,
"refresh_from_server_only"
):
subscription
.
indexSourceData
(
client
=
True
)
# Reset signature only if client send its modification to server
subscription
.
initialiseSynchronization
()
# Create the package 1
# Create the package 1
syncml_response
=
SyncMLResponse
()
syncml_response
=
SyncMLResponse
()
...
@@ -301,7 +298,9 @@ class EngineMixin(object):
...
@@ -301,7 +298,9 @@ class EngineMixin(object):
'one_way_from_server'
,
'one_way_from_server'
,
'refresh_from_client_only'
,
'refresh_from_client_only'
,
'one_way_from_client'
):
'one_way_from_client'
):
# XXX Why re-editing here ?
# Make sure we update configuration based on publication data
# so that manual edition is propagated
# XXX Must check all properties that must be setted
subscriber
.
setXmlBindingGeneratorMethodId
(
subscriber
.
setXmlBindingGeneratorMethodId
(
publication
.
getXmlBindingGeneratorMethodId
())
publication
.
getXmlBindingGeneratorMethodId
())
subscriber
.
setConduitModuleId
(
publication
.
getConduitModuleId
())
subscriber
.
setConduitModuleId
(
publication
.
getConduitModuleId
())
...
...
product/ERP5SyncML/Engine/SynchronousEngine.py
View file @
a270c222
...
@@ -54,7 +54,7 @@ class SyncMLSynchronousEngine(EngineMixin):
...
@@ -54,7 +54,7 @@ class SyncMLSynchronousEngine(EngineMixin):
# Must check what server tell about database synchronization
# Must check what server tell about database synchronization
# and update the mode if required
# and update the mode if required
syncml_response
=
s
elf
.
_generateBaseResponse
(
subscription
)
syncml_response
=
s
ubscription
.
generateBaseResponse
(
)
# Read & apply status about databases & synchronizations
# Read & apply status about databases & synchronizations
try
:
try
:
...
@@ -80,16 +80,16 @@ class SyncMLSynchronousEngine(EngineMixin):
...
@@ -80,16 +80,16 @@ class SyncMLSynchronousEngine(EngineMixin):
"refresh_from_server_only"
):
"refresh_from_server_only"
):
# We only get data from server
# We only get data from server
finished
=
True
finished
=
True
syncml_response
.
addFinal
()
else
:
else
:
finished
=
subscription
.
_getSyncMLData
(
finished
=
subscription
.
_getSyncMLData
(
syncml_response
=
syncml_response
,
syncml_response
=
syncml_response
,
min_gid
=
None
,
max_gid
=
None
)
)
if
finished
:
# Delete message will contain final tag
subscription
.
getDeletedSyncMLData
(
syncml_response
=
syncml_response
)
syncml_logger
.
info
(
"-> Client sendind modification, finished %s"
%
(
finished
,))
syncml_logger
.
info
(
"-> Client sendind modification, finished %s"
%
(
finished
,))
if
finished
:
if
finished
:
# Add deleted objets
subscription
.
_getDeletedData
(
syncml_response
=
syncml_response
)
# Notify that all modifications were sent
syncml_response
.
addFinal
()
# Will then start processing sync commands from server
# Will then start processing sync commands from server
subscription
.
processSyncRequest
()
subscription
.
processSyncRequest
()
...
@@ -149,7 +149,8 @@ class SyncMLSynchronousEngine(EngineMixin):
...
@@ -149,7 +149,8 @@ class SyncMLSynchronousEngine(EngineMixin):
raise
ValueError
(
"Authentication failed, impossible to sync data"
)
raise
ValueError
(
"Authentication failed, impossible to sync data"
)
# Apply command & send modifications
# Apply command & send modifications
syncml_response
=
self
.
_generateBaseResponse
(
subscriber
)
# XXX This can be called on subscription instead
syncml_response
=
subscriber
.
generateBaseResponse
()
# Apply status about object send & synchronized if any
# Apply status about object send & synchronized if any
self
.
_readStatusList
(
syncml_request
,
subscriber
,
syncml_response
,
True
)
self
.
_readStatusList
(
syncml_request
,
subscriber
,
syncml_response
,
True
)
...
@@ -191,10 +192,8 @@ class SyncMLSynchronousEngine(EngineMixin):
...
@@ -191,10 +192,8 @@ class SyncMLSynchronousEngine(EngineMixin):
if
syncml_request
.
isFinal
:
if
syncml_request
.
isFinal
:
# Server will now send its modifications
# Server will now send its modifications
subscriber
.
sendModifications
()
subscriber
.
sendModifications
()
if
subscriber
.
getSyncmlAlertCode
()
not
in
(
"one_way_from_client"
,
# Run indexation only once client has sent its modifications
"refresh_from_client_only"
):
subscriber
.
indexSourceData
()
# Reset signature only if we have to check modifications on server side
subscriber
.
initialiseSynchronization
()
# Do not continue in elif, as sending modifications is done in the same
# Do not continue in elif, as sending modifications is done in the same
# package as sending notifications
# package as sending notifications
...
@@ -204,13 +203,16 @@ class SyncMLSynchronousEngine(EngineMixin):
...
@@ -204,13 +203,16 @@ class SyncMLSynchronousEngine(EngineMixin):
"refresh_from_client_only"
):
"refresh_from_client_only"
):
# We only get data from client
# We only get data from client
finished
=
True
finished
=
True
syncml_response
.
addFinal
()
else
:
else
:
finished
=
subscriber
.
_getSyncMLData
(
finished
=
subscriber
.
_getSyncMLData
(
syncml_response
=
syncml_response
,
syncml_response
=
syncml_response
)
min_gid
=
None
,
max_gid
=
None
)
if
finished
:
# Delete message will contain final tag
subscriber
.
getDeletedSyncMLData
(
syncml_response
=
syncml_response
)
syncml_logger
.
info
(
"-> Server sendind data, finished %s"
%
(
finished
,))
syncml_logger
.
info
(
"-> Server sendind data, finished %s"
%
(
finished
,))
if
finished
:
if
finished
:
subscriber
.
_getDeletedData
(
syncml_response
=
syncml_response
)
syncml_response
.
addFinal
()
subscriber
.
waitNotifications
()
subscriber
.
waitNotifications
()
# Do not go into finished here as we must wait for
# Do not go into finished here as we must wait for
# notifications from client
# notifications from client
...
...
product/ERP5SyncML/SyncMLConstant.py
View file @
a270c222
...
@@ -28,51 +28,14 @@
...
@@ -28,51 +28,14 @@
##############################################################################
##############################################################################
# Namespaces.
SYNCML_NAMESPACE
=
'SYNCML:SYNCML1.2'
# In SyncML Representation Protocol OMA
# we use URN as format of namespace
# List namespaces supported
URN_LIST
=
(
'SYNCML:SYNCML1.1'
,
'SYNCML:SYNCML1.2'
)
SYNCML_NAMESPACE
=
'SYNCML:SYNCML1.2'
NSMAP
=
{
'syncml'
:
SYNCML_NAMESPACE
}
NSMAP
=
{
'syncml'
:
SYNCML_NAMESPACE
}
## SyncML Alert Codes
#TWO_WAY = 200
#SLOW_SYNC = 201 # This means we get the data from the publication
#ONE_WAY_FROM_SERVER = 204
#CODE_LIST = (TWO_WAY, ONE_WAY_FROM_SERVER,)
# SyncML Status Codes
#SUCCESS = 200
#ITEM_ADDED = 201
#WAITING_DATA = 214
#REFRESH_REQUIRED = 508
#CHUNK_OK = 214
#CONFLICT = 409 # A conflict is detected
#CONFLICT_MERGE = 207 # We have merged the two versions, sending
## whatever is needed to change(replace)
#CONFLICT_CLIENT_WIN = 208 # The client is the "winner", we keep
## the version of the client
#UNAUTHORIZED = 401
#AUTH_REQUIRED = 407
#AUTH_ACCEPTED = 212
NULL_ANCHOR
=
'00000000T000000Z'
NULL_ANCHOR
=
'00000000T000000Z'
# ERP5 Sync Codes for Signatures
SYNCHRONIZED
=
1
#SENT = 2
#NOT_SENT = 3
PARTIAL
=
4
NOT_SYNCHRONIZED
=
5
PUB_CONFLICT_MERGE
=
6
PUB_CONFLICT_CLIENT_WIN
=
8
#MAX_LINES = 5000
MAX_OBJECTS
=
300
MAX_OBJECTS
=
300
MAX_LEN
=
1
<<
16
MAX_LEN
=
1
<<
16
MAX_DOCUMENT_PER_MESSAGE
=
2
XUPDATE_INSERT_LIST
=
(
'xupdate:insert-after'
,
'xupdate:insert-before'
)
XUPDATE_INSERT_LIST
=
(
'xupdate:insert-after'
,
'xupdate:insert-before'
)
XUPDATE_ADD
=
'xupdate:append'
XUPDATE_ADD
=
'xupdate:append'
...
@@ -84,20 +47,7 @@ XUPDATE_INSERT_OR_ADD_LIST = XUPDATE_INSERT_LIST + (XUPDATE_ADD,)
...
@@ -84,20 +47,7 @@ XUPDATE_INSERT_OR_ADD_LIST = XUPDATE_INSERT_LIST + (XUPDATE_ADD,)
ADD_ACTION
=
'Add'
ADD_ACTION
=
'Add'
REPLACE_ACTION
=
'Replace'
REPLACE_ACTION
=
'Replace'
##media types :
#MEDIA_TYPE = {}
#MEDIA_TYPE['TEXT_XML'] = 'text/xml'
#MEDIA_TYPE['TEXT_VCARD'] = 'text/vcard'
#MEDIA_TYPE['TEXT_XVCARD'] = 'text/x-vcard'
##content types :
#CONTENT_TYPE = {}
#CONTENT_TYPE['SYNCML_XML'] = 'application/vnd.syncml+xml'
#CONTENT_TYPE['SYNCML_WBXML'] = 'application/vnd.syncml+wbxml'
#Activity priority
ACTIVITY_PRIORITY
=
5
ACTIVITY_PRIORITY
=
5
class
SynchronizationError
(
Exception
):
class
SynchronizationError
(
Exception
):
pass
pass
product/ERP5SyncML/SyncMLMessage.py
View file @
a270c222
...
@@ -579,9 +579,14 @@ class SyncMLRequest(object):
...
@@ -579,9 +579,14 @@ class SyncMLRequest(object):
sync_command_kw
[
"xml_data"
]
=
etree
.
tostring
(
xml_data
[
0
])
sync_command_kw
[
"xml_data"
]
=
etree
.
tostring
(
xml_data
[
0
])
else
:
else
:
# If not xml, return raw data
# If not xml, return raw data
# XXX this is unicode and can be a problem for activity
# XXX This must be CDATA type
sync_command_kw
[
"raw_data"
]
=
sync_command
.
xpath
(
data
=
sync_command
.
xpath
(
'string(.//syncml:Item/syncml:Data)'
,
'string(.//syncml:Item/syncml:Data)'
,
namespaces
=
self
.
data
.
nsmap
)
namespaces
=
self
.
data
.
nsmap
)
if
isinstance
(
data
,
etree
.
CDATA
):
parser
=
etree
.
XMLParser
(
strip_cdata
=
False
)
cdata
=
etree
.
XML
(
data
,
parser
)
data
=
cdata
.
text
# XXX this is unicode and can be a problem for activity
sync_command_kw
[
"raw_data"
]
=
data
append
(
sync_command_kw
)
append
(
sync_command_kw
)
product/ERP5SyncML/Tool/SynchronizationTool.py
View file @
a270c222
...
@@ -26,7 +26,6 @@
...
@@ -26,7 +26,6 @@
##############################################################################
##############################################################################
from
os
import
path
from
os
import
path
from
lxml
import
etree
from
logging
import
getLogger
,
Formatter
from
logging
import
getLogger
,
Formatter
from
AccessControl
import
ClassSecurityInfo
from
AccessControl
import
ClassSecurityInfo
...
@@ -36,26 +35,14 @@ from Products.ERP5Type import Permissions
...
@@ -36,26 +35,14 @@ from Products.ERP5Type import Permissions
from
Products.ERP5Type.Globals
import
InitializeClass
from
Products.ERP5Type.Globals
import
InitializeClass
from
Products.ERP5SyncML.SyncMLConstant
import
ACTIVITY_PRIORITY
,
\
from
Products.ERP5SyncML.SyncMLConstant
import
ACTIVITY_PRIORITY
,
\
SynchronizationError
SynchronizationError
from
Products.ERP5SyncML.SyncMLMessage
import
SyncMLRe
sponse
,
SyncMLRe
quest
from
Products.ERP5SyncML.SyncMLMessage
import
SyncMLRequest
from
Products.ERP5SyncML.Engine.SynchronousEngine
import
SyncMLSynchronousEngine
from
Products.ERP5SyncML.Engine.SynchronousEngine
import
SyncMLSynchronousEngine
from
Products.ERP5SyncML.Engine.AsynchronousEngine
import
SyncMLAsynchronousEngine
from
Products.ERP5SyncML.Engine.AsynchronousEngine
import
SyncMLAsynchronousEngine
from
Products.ERP5SyncML.Transport.HTTP
import
HTTPTransport
from
Products.ERP5SyncML.Transport.File
import
FileTransport
from
Products.ERP5SyncML.Transport.Mail
import
MailTransport
from
Products.ERP5.ERP5Site
import
getSite
from
Products.ERP5.ERP5Site
import
getSite
synchronous_engine
=
SyncMLSynchronousEngine
()
synchronous_engine
=
SyncMLSynchronousEngine
()
asynchronous_engine
=
SyncMLAsynchronousEngine
()
asynchronous_engine
=
SyncMLAsynchronousEngine
()
transport_scheme_dict
=
{
"http"
:
HTTPTransport
(),
"https"
:
HTTPTransport
(),
"file"
:
FileTransport
(),
"mail"
:
MailTransport
(),
}
parser
=
etree
.
XMLParser
(
remove_blank_text
=
True
)
# Logging channel definitions
# Logging channel definitions
# Main logging channel
# Main logging channel
syncml_logger
=
getLogger
(
'ERP5SyncML'
)
syncml_logger
=
getLogger
(
'ERP5SyncML'
)
...
@@ -390,7 +377,6 @@ class SynchronizationTool(BaseTool):
...
@@ -390,7 +377,6 @@ class SynchronizationTool(BaseTool):
return
engine
.
processClientSynchronization
(
syncml_request
,
subscription
)
return
engine
.
processClientSynchronization
(
syncml_request
,
subscription
)
# Send the message
# Send the message
# XXX This must depends on activity enables property, maybe use engine
if
subscription
.
getIsActivityEnabled
():
if
subscription
.
getIsActivityEnabled
():
subscription
.
activate
(
subscription
.
activate
(
after_tag
=
"%s_reset"
%
(
subscription
.
getPath
(),),
after_tag
=
"%s_reset"
%
(
subscription
.
getPath
(),),
...
@@ -402,79 +388,4 @@ class SynchronizationTool(BaseTool):
...
@@ -402,79 +388,4 @@ class SynchronizationTool(BaseTool):
return
str
(
syncml_response
)
return
str
(
syncml_response
)
def
applySyncCommand
(
self
,
subscription_path
,
response_message_id
,
activate_kw
,
**
kw
):
"""
This methods is intented to be called by asynchronous engine in activity to
apply sync commands for a subset of data
As engines are not zodb object, the tool acts as a placeholder for method
that need to be called in activities
"""
subscription
=
self
.
restrictedTraverse
(
subscription_path
)
assert
subscription
is
not
None
,
"Impossible to find subscription %s"
\
%
(
subscription_path
)
# Build Message
if
response_message_id
:
syncml_response
=
SyncMLResponse
()
syncml_response
.
addHeader
(
session_id
=
subscription
.
getSessionId
(),
message_id
=
response_message_id
,
target
=
subscription
.
getUrlString
(),
source
=
subscription
.
getSubscriptionUrlString
())
syncml_response
.
addBody
()
else
:
syncml_response
=
None
subscription
.
applySyncCommand
(
syncml_response
=
syncml_response
,
**
kw
)
# Send the message in activity to prevent recomputing data in case of
# transport failure
if
syncml_response
:
syncml_logger
(
"---- %s sending %s notifications of sync"
%
(
subscription
.
getTitle
(),
syncml_response
.
sync_confirmation_counter
))
subscription
.
activate
(
activity
=
"SQLQueue"
,
# group_method_id=None,
# group_method_cost=.05,
tag
=
activate_kw
).
sendMessage
(
xml
=
str
(
syncml_response
))
def
sendSyncCommand
(
self
,
id_list
,
message_id
,
subscription_path
,
activate_kw
,
is_final_message
=
False
):
"""
This methods is intented to be called by asynchronous engine in activity to
send sync commands for a subset of data
As engines are not zodb object, the tool acts as a placeholder for method
that need to be called in activities
"""
subscription
=
self
.
restrictedTraverse
(
subscription_path
)
assert
subscription
is
not
None
,
"Impossible to find subscription %s"
\
%
(
subscription_path
)
# Build Message
syncml_response
=
SyncMLResponse
()
syncml_response
.
addHeader
(
session_id
=
subscription
.
getSessionId
(),
message_id
=
message_id
,
target
=
subscription
.
getUrlString
(),
source
=
subscription
.
getSubscriptionUrlString
())
syncml_response
.
addBody
()
subscription
.
_getSyncMLData
(
syncml_response
=
syncml_response
,
id_list
=
id_list
,
)
if
is_final_message
:
# Notify that all modifications were sent
syncml_response
.
addFinal
()
# Send the message in activity to prevent recomputing data in case of
# transport failure
# activate_kw["group_method_id"] = None
# activate_kw["group_method_cost"] = .05
subscription
.
activate
(
**
activate_kw
).
sendMessage
(
xml
=
str
(
syncml_response
))
InitializeClass
(
SynchronizationTool
)
InitializeClass
(
SynchronizationTool
)
product/ERP5SyncML/tests/testERP5DocumentSyncML.py
View file @
a270c222
...
@@ -118,6 +118,8 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
...
@@ -118,6 +118,8 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
def
afterSetUp
(
self
):
def
afterSetUp
(
self
):
"""Setup."""
"""Setup."""
self
.
login
()
self
.
login
()
self
.
portal
.
z_drop_syncml
()
self
.
portal
.
z_create_syncml
()
self
.
addPublications
()
self
.
addPublications
()
self
.
addSubscriptions
()
self
.
addSubscriptions
()
self
.
portal
=
self
.
getPortal
()
self
.
portal
=
self
.
getPortal
()
...
@@ -316,12 +318,12 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
...
@@ -316,12 +318,12 @@ class TestERP5DocumentSyncMLMixin(TestERP5SyncMLMixin):
for
document
in
document_server
.
objectValues
():
for
document
in
document_server
.
objectValues
():
state_list
=
self
.
getSynchronizationState
(
document
)
state_list
=
self
.
getSynchronizationState
(
document
)
for
state
in
state_list
:
for
state
in
state_list
:
self
.
assertEqual
(
state
[
1
],
'
synchronized
'
)
self
.
assertEqual
(
state
[
1
],
'
no_conflict
'
)
document_client1
=
self
.
getDocumentClient1
()
document_client1
=
self
.
getDocumentClient1
()
for
document
in
document_client1
.
objectValues
():
for
document
in
document_client1
.
objectValues
():
state_list
=
self
.
getSynchronizationState
(
document
)
state_list
=
self
.
getSynchronizationState
(
document
)
for
state
in
state_list
:
for
state
in
state_list
:
self
.
assertEqual
(
state
[
1
],
'
synchronized
'
)
self
.
assertEqual
(
state
[
1
],
'
no_conflict
'
)
# Check for each signature that the tempXML is None
# Check for each signature that the tempXML is None
for
sub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
sub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
m
in
sub
.
contentValues
():
for
m
in
sub
.
contentValues
():
...
@@ -418,13 +420,7 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
...
@@ -418,13 +420,7 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
def
getTitle
(
self
):
def
getTitle
(
self
):
return
"ERP5 Document SyncML"
return
"ERP5 Document SyncML"
def
setupPublicationAndSubscriptionIdGenerator
(
self
):
portal_sync
=
self
.
getSynchronizationTool
()
sub1
=
portal_sync
[
self
.
sub_id1
]
pub
=
portal_sync
[
self
.
pub_id
]
def
checkSynchronizationStateIsConflict
(
self
,
portal_type
=
'Text'
):
def
checkSynchronizationStateIsConflict
(
self
,
portal_type
=
'Text'
):
portal_sync
=
self
.
getSynchronizationTool
()
document_server
=
self
.
getDocumentServer
()
document_server
=
self
.
getDocumentServer
()
for
document
in
document_server
.
objectValues
():
for
document
in
document_server
.
objectValues
():
if
document
.
getId
()
==
self
.
id1
:
if
document
.
getId
()
==
self
.
id1
:
...
@@ -641,7 +637,6 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
...
@@ -641,7 +637,6 @@ class TestERP5DocumentSyncML(TestERP5DocumentSyncMLMixin):
recognize objects (because by default, getGid==getId. Here, we will see
recognize objects (because by default, getGid==getId. Here, we will see
if it also works with a somewhat strange getGid
if it also works with a somewhat strange getGid
"""
"""
self
.
setupPublicationAndSubscriptionIdGenerator
()
nb_document
=
self
.
createDocumentServerList
()
nb_document
=
self
.
createDocumentServerList
()
# This will test adding object
# This will test adding object
self
.
synchronize
(
self
.
sub_id1
)
self
.
synchronize
(
self
.
sub_id1
)
...
...
product/ERP5SyncML/tests/testERP5SyncML.py
View file @
a270c222
...
@@ -98,6 +98,8 @@ class TestERP5SyncMLMixin(TestMixin):
...
@@ -98,6 +98,8 @@ class TestERP5SyncMLMixin(TestMixin):
def
afterSetUp
(
self
):
def
afterSetUp
(
self
):
"""Setup."""
"""Setup."""
self
.
login
()
self
.
login
()
self
.
portal
.
z_drop_syncml
()
self
.
portal
.
z_create_syncml
()
# This test creates Person inside Person, so we modifiy type information to
# This test creates Person inside Person, so we modifiy type information to
# allow anything inside Person (we'll cleanup on teardown)
# allow anything inside Person (we'll cleanup on teardown)
self
.
getTypesTool
().
getTypeInfo
(
'Person'
).
filter_content_types
=
0
self
.
getTypesTool
().
getTypeInfo
(
'Person'
).
filter_content_types
=
0
...
@@ -228,6 +230,7 @@ class TestERP5SyncMLMixin(TestMixin):
...
@@ -228,6 +230,7 @@ class TestERP5SyncMLMixin(TestMixin):
result
=
portal_sync
.
processClientSynchronization
(
subscription
.
getPath
())
result
=
portal_sync
.
processClientSynchronization
(
subscription
.
getPath
())
self
.
tic
()
self
.
tic
()
nb_message
+=
1
nb_message
+=
1
self
.
tic
()
return
nb_message
return
nb_message
def
synchronizeWithBrokenMessage
(
self
,
id
):
def
synchronizeWithBrokenMessage
(
self
,
id
):
...
@@ -329,33 +332,33 @@ class TestERP5SyncMLMixin(TestMixin):
...
@@ -329,33 +332,33 @@ class TestERP5SyncMLMixin(TestMixin):
for
person
in
person_server
.
objectValues
():
for
person
in
person_server
.
objectValues
():
state_list
=
self
.
getSynchronizationState
(
person
)
state_list
=
self
.
getSynchronizationState
(
person
)
for
state
in
state_list
:
for
state
in
state_list
:
self
.
assertEquals
(
state
[
1
],
'
synchronized
'
)
self
.
assertEquals
(
state
[
1
],
'
no_conflict
'
)
person_client1
=
self
.
getPersonClient1
()
person_client1
=
self
.
getPersonClient1
()
for
person
in
person_client1
.
objectValues
():
for
person
in
person_client1
.
objectValues
():
state_list
=
self
.
getSynchronizationState
(
person
)
state_list
=
self
.
getSynchronizationState
(
person
)
for
state
in
state_list
:
for
state
in
state_list
:
self
.
assertEquals
(
state
[
1
],
'
synchronized
'
)
self
.
assertEquals
(
state
[
1
],
'
no_conflict
'
)
person_client2
=
self
.
getPersonClient2
()
person_client2
=
self
.
getPersonClient2
()
for
person
in
person_client2
.
objectValues
():
for
person
in
person_client2
.
objectValues
():
state_list
=
self
.
getSynchronizationState
(
person
)
state_list
=
self
.
getSynchronizationState
(
person
)
for
state
in
state_list
:
for
state
in
state_list
:
self
.
assertEquals
(
state
[
1
],
'
synchronized
'
)
self
.
assertEquals
(
state
[
1
],
'
no_conflict
'
)
# Check for each signature that the tempXML is None
# Check for each signature that the tempXML is None
for
sub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
sub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
m
in
sub
.
contentValues
():
for
m
in
sub
.
contentValues
():
self
.
assertEquals
(
m
.
getTemporaryData
(),
None
)
self
.
assertEquals
(
m
.
getTemporaryData
(),
None
)
self
.
assertEquals
(
m
.
getPartialData
(),
None
)
self
.
assertEquals
(
m
.
getPartialData
(),
None
)
self
.
assertEquals
(
m
.
getValidationState
(),
"
synchronized
"
)
self
.
assertEquals
(
m
.
getValidationState
(),
"
no_conflict
"
)
for
pub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Publication'
):
for
pub
in
portal_sync
.
contentValues
(
portal_type
=
'SyncML Publication'
):
for
sub
in
pub
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
sub
in
pub
.
contentValues
(
portal_type
=
'SyncML Subscription'
):
for
m
in
sub
.
contentValues
():
for
m
in
sub
.
contentValues
():
self
.
assertEquals
(
m
.
getPartialData
(),
None
)
self
.
assertEquals
(
m
.
getPartialData
(),
None
)
self
.
assertEquals
(
m
.
getValidationState
(),
"
synchronized
"
)
self
.
assertEquals
(
m
.
getValidationState
(),
"
no_conflict
"
)
def
verifyFirstNameAndLastNameAreNotSynchronized
(
self
,
first_name
,
def
verifyFirstNameAndLastNameAreNotSynchronized
(
self
,
first_name
,
last_name
,
person_server
,
person_client
):
last_name
,
person_server
,
person_client
):
"""
"""
verify that the first and last name are NOT
synchronized
verify that the first and last name are NOT
no_conflict
"""
"""
self
.
assertNotEqual
(
person_server
.
getFirstName
(),
first_name
)
self
.
assertNotEqual
(
person_server
.
getFirstName
(),
first_name
)
self
.
assertNotEqual
(
person_server
.
getLastName
(),
last_name
)
self
.
assertNotEqual
(
person_server
.
getLastName
(),
last_name
)
...
@@ -481,7 +484,6 @@ class TestERP5SyncML(TestERP5SyncMLMixin):
...
@@ -481,7 +484,6 @@ class TestERP5SyncML(TestERP5SyncMLMixin):
pub
.
setConduitModuleId
(
'ERP5ConduitTitleGid'
)
pub
.
setConduitModuleId
(
'ERP5ConduitTitleGid'
)
def
checkSynchronizationStateIsConflict
(
self
):
def
checkSynchronizationStateIsConflict
(
self
):
portal_sync
=
self
.
getSynchronizationTool
()
person_server
=
self
.
getPersonServer
()
person_server
=
self
.
getPersonServer
()
for
person
in
person_server
.
objectValues
():
for
person
in
person_server
.
objectValues
():
if
person
.
getId
()
==
self
.
id1
:
if
person
.
getId
()
==
self
.
id1
:
...
@@ -751,7 +753,6 @@ return [context[%r]]
...
@@ -751,7 +753,6 @@ return [context[%r]]
# We will try to get the state of objects
# We will try to get the state of objects
# that are just synchronized
# that are just synchronized
self
.
test_08_FirstSynchronization
()
self
.
test_08_FirstSynchronization
()
portal_sync
=
self
.
getSynchronizationTool
()
person_server
=
self
.
getPersonServer
()
person_server
=
self
.
getPersonServer
()
person1_s
=
person_server
.
_getOb
(
self
.
id1
)
person1_s
=
person_server
.
_getOb
(
self
.
id1
)
state_list_s
=
self
.
getSynchronizationState
(
person1_s
)
state_list_s
=
self
.
getSynchronizationState
(
person1_s
)
...
@@ -782,6 +783,8 @@ return [context[%r]]
...
@@ -782,6 +783,8 @@ return [context[%r]]
kw
=
{
'first_name'
:
self
.
first_name1
,
'last_name'
:
self
.
last_name1
}
kw
=
{
'first_name'
:
self
.
first_name1
,
'last_name'
:
self
.
last_name1
}
person1_c
.
edit
(
**
kw
)
person1_c
.
edit
(
**
kw
)
#person1_c.setModificationDate(DateTime()+1)
#person1_c.setModificationDate(DateTime()+1)
# import ipdb
# ipdb.set_trace()
self
.
synchronize
(
self
.
sub_id1
)
self
.
synchronize
(
self
.
sub_id1
)
self
.
checkSynchronizationStateIsSynchronized
()
self
.
checkSynchronizationStateIsSynchronized
()
person1_s
=
person_server
.
_getOb
(
self
.
id1
)
person1_s
=
person_server
.
_getOb
(
self
.
id1
)
...
@@ -1543,7 +1546,7 @@ return [context[%r]]
...
@@ -1543,7 +1546,7 @@ return [context[%r]]
self
.
assertEquals
(
client_person
.
getLastName
(),
self
.
last_name1
)
self
.
assertEquals
(
client_person
.
getLastName
(),
self
.
last_name1
)
# reset for refresh sync
# reset for refresh sync
# after synchroniz
e, the client object retrieve value of
server
# after synchroniz
ation, the client retrieves value from
server
self
.
resetSignaturePublicationAndSubscription
()
self
.
resetSignaturePublicationAndSubscription
()
self
.
synchronize
(
self
.
sub_id1
)
self
.
synchronize
(
self
.
sub_id1
)
...
@@ -1596,7 +1599,7 @@ return [context[%r]]
...
@@ -1596,7 +1599,7 @@ return [context[%r]]
publication
=
self
.
addPublication
()
publication
=
self
.
addPublication
()
self
.
addRefreshFormClientOnlySubscription
()
self
.
addRefreshFormClientOnlySubscription
()
nb_person
=
self
.
populatePersonClient1
()
self
.
populatePersonClient1
()
portal_sync
=
self
.
getSynchronizationTool
()
portal_sync
=
self
.
getSynchronizationTool
()
subscription1
=
portal_sync
[
self
.
sub_id1
]
subscription1
=
portal_sync
[
self
.
sub_id1
]
self
.
assertEquals
(
subscription1
.
getSyncmlAlertCode
(),
self
.
assertEquals
(
subscription1
.
getSyncmlAlertCode
(),
...
...
product/ERP5SyncML/tests/testERP5SyncMLVCard.py
View file @
a270c222
...
@@ -28,9 +28,7 @@
...
@@ -28,9 +28,7 @@
#
#
##############################################################################
##############################################################################
from
testERP5SyncML
import
TestERP5SyncMLMixin
from
testERP5SyncML
import
TestERP5SyncMLMixin
from
zLOG
import
LOG
class
TestERP5SyncMLVCard
(
TestERP5SyncMLMixin
):
class
TestERP5SyncMLVCard
(
TestERP5SyncMLMixin
):
...
@@ -45,6 +43,10 @@ class TestERP5SyncMLVCard(TestERP5SyncMLMixin):
...
@@ -45,6 +43,10 @@ class TestERP5SyncMLVCard(TestERP5SyncMLMixin):
"""
"""
return
(
'erp5_base'
,
'erp5_syncml'
,)
return
(
'erp5_base'
,
'erp5_syncml'
,)
def
afterSetUp
(
self
):
self
.
portal
.
z_drop_syncml
()
self
.
portal
.
z_create_syncml
()
def
getTitle
(
self
):
def
getTitle
(
self
):
return
'testERP5SyncMLVCard'
return
'testERP5SyncMLVCard'
...
...
product/ERP5TioSafe/Conduit/ERP5NodeConduit.py
View file @
a270c222
...
@@ -69,15 +69,15 @@ class ERP5NodeConduit(TioSafeBaseConduit):
...
@@ -69,15 +69,15 @@ class ERP5NodeConduit(TioSafeBaseConduit):
we can filter person based on the plugin they came from
we can filter person based on the plugin they came from
"""
"""
site
=
self
.
getIntegrationSite
(
kw
[
'domain'
])
site
=
self
.
getIntegrationSite
(
kw
[
'domain'
])
default_stc
=
site
.
getSourceTrade
()
# try to find the corresponding STC
# try to find the corresponding STC
stc_list
=
object
.
getPortalObject
().
sale_trade_condition_module
.
searchFolder
(
title
=
"%s %s"
%
(
site
.
getReference
(),
object
.
getTitle
()),
stc_list
=
object
.
getPortalObject
().
sale_trade_condition_module
.
searchFolder
(
validation_state
=
"validated"
title
=
"%s %s"
%
(
site
.
getReference
(),
object
.
getTitle
()),
)
validation_state
=
"validated"
)
if
len
(
stc_list
)
==
0
:
if
len
(
stc_list
)
==
0
:
self
.
_createSaleTradeCondition
(
object
,
**
kw
)
self
.
_createSaleTradeCondition
(
object
,
**
kw
)
elif
len
(
stc_list
)
>
1
:
elif
len
(
stc_list
)
>
1
:
raise
ValueError
,
"Multiple trade condition (%s) retrieve for %s"
%
([
x
.
path
for
x
in
stc_list
],
object
.
getTitle
())
raise
ValueError
,
"Multiple trade condition (%s) retrieved for %s"
\
%
([
x
.
path
for
x
in
stc_list
],
object
.
getTitle
())
else
:
else
:
stc
=
stc_list
[
0
].
getObject
()
stc
=
stc_list
[
0
].
getObject
()
stc
.
edit
(
stc
.
edit
(
...
@@ -519,7 +519,10 @@ class ERP5NodeConduit(TioSafeBaseConduit):
...
@@ -519,7 +519,10 @@ class ERP5NodeConduit(TioSafeBaseConduit):
elif
tag
==
"email"
:
elif
tag
==
"email"
:
current_value
=
str
(
document
.
getDefaultEmailText
(
""
))
current_value
=
str
(
document
.
getDefaultEmailText
(
""
))
else
:
else
:
try
:
current_value
=
getattr
(
document
,
tag
)
current_value
=
getattr
(
document
,
tag
)
except
AttributeError
:
current_value
=
None
if
current_value
:
if
current_value
:
current_value
=
current_value
.
encode
(
'utf-8'
)
current_value
=
current_value
.
encode
(
'utf-8'
)
...
...
product/ERP5TioSafe/Document/IntegrationSite.py
View file @
a270c222
...
@@ -33,6 +33,7 @@ from Products.ERP5Type.Core.Folder import Folder
...
@@ -33,6 +33,7 @@ from Products.ERP5Type.Core.Folder import Folder
from
AccessControl
import
ClassSecurityInfo
from
AccessControl
import
ClassSecurityInfo
from
Products.ERP5Type
import
Permissions
,
PropertySheet
from
Products.ERP5Type
import
Permissions
,
PropertySheet
from
zLOG
import
LOG
,
INFO
,
ERROR
,
WARNING
from
zLOG
import
LOG
,
INFO
,
ERROR
,
WARNING
from
Products.ERP5Type.TransactionalVariable
import
getTransactionalVariable
class
IntegrationSite
(
Folder
):
class
IntegrationSite
(
Folder
):
...
@@ -187,7 +188,12 @@ class IntegrationSite(Folder):
...
@@ -187,7 +188,12 @@ class IntegrationSite(Folder):
base_mapping = the base property mapping
base_mapping = the base property mapping
property = string of the property we want the mapping
property = string of the property we want the mapping
"""
"""
mapping_line
=
base_mapping
.
searchFolder
(
portal_type
=
'Integration Property Mapping'
,
tv
=
getTransactionalVariable
()
key
=
"%s-%s"
%
(
base_mapping
.
getPath
(),
property_name
)
try
:
mapping_line
=
tv
[
key
]
except
KeyError
:
tv
[
key
]
=
mapping_line
=
base_mapping
.
searchFolder
(
portal_type
=
'Integration Property Mapping'
,
path
=
"%s%%"
%
(
base_mapping
.
getPath
()),
path
=
"%s%%"
%
(
base_mapping
.
getPath
()),
destination_reference
=
property_name
,
destination_reference
=
property_name
,
)
)
...
...
product/ERP5Type/Utils.py
View file @
a270c222
...
@@ -854,14 +854,12 @@ def setDefaultClassProperties(property_holder):
...
@@ -854,14 +854,12 @@ def setDefaultClassProperties(property_holder):
)
)
}
}
from
Globals
import
Persistent
,
PersistentMapping
def
importLocalDocument
(
class_id
,
path
=
None
,
class_path
=
None
):
def
importLocalDocument
(
class_id
,
path
=
None
,
class_path
=
None
):
"""Imports a document class and registers it in ERP5Type Document
"""Imports a document class and registers it in ERP5Type Document
repository ( Products.ERP5Type.Document )
repository ( Products.ERP5Type.Document )
"""
"""
import
Products.ERP5Type.Document
import
Products.ERP5Type.Document
import
Permissions
if
class_path
:
if
class_path
:
assert
path
is
None
assert
path
is
None
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment