diff --git a/CHANGES.erp5.util.txt b/CHANGES.erp5.util.txt
index 50fa0318afb6e51fabb8b3296c30778968684e01..1973a68881788b6f3f9391ef15eb3147874bd47b 100644
--- a/CHANGES.erp5.util.txt
+++ b/CHANGES.erp5.util.txt
@@ -1,6 +1,21 @@
 Changes
 =======
 
+0.4.5 (2012-07-04)
+------------------
+
+ * erp5.util.taskdistribution:
+
+  - xmlrpclib does not support named parameters, use positional ones
+    [Vincent Pelletier]
+
+0.4.4 (2012-07-04)
+------------------
+
+ * erp5.util.taskdistribution:
+
+  - New module [Vincent Pelletier]
+
 0.4.3 (2012-04-24)
 ----------------
 
diff --git a/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/DiscussionThread_redirectCreateNewDiscussionPost.xml b/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/DiscussionThread_redirectCreateNewDiscussionPost.xml
index 22fb2ad5ca4040a46315f6c7afc8a9174fca8d11..9416b222e1145dcedefeabe8e56b679f18989901 100644
--- a/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/DiscussionThread_redirectCreateNewDiscussionPost.xml
+++ b/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/DiscussionThread_redirectCreateNewDiscussionPost.xml
@@ -58,7 +58,11 @@ preferred_forum_quote_original_message= portal.ERP5Site_getUserPreferredForumSet
 if discussion_post_uid is not None:\n
   # set title & text_content\n
   discussion_post = getattr(context, discussion_post_uid)\n
-  title = \'Re: %s\' %discussion_post.getTitle()\n
+  \n
+  title = discussion_post.getTitle()\n
+  if not title.lower().startswith(\'re:\'):\n
+    # stop exploding "Re: Re: .." to one level\n
+    title = \'Re: %s\' %title\n
   context.REQUEST.set(\'discussion_post_title\', title)\n
   if preferred_forum_quote_original_message:\n
     author_dict = discussion_post.DiscussionPost_getAuthorDict()\n
diff --git a/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/WebSection_createNewDiscussionThread.xml b/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/WebSection_createNewDiscussionThread.xml
index 6dd1b43ac23076f8e5ca121442462a5a1a0664b2..0f434c246159150604d0e1aecbd1db104679bbca 100644
--- a/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/WebSection_createNewDiscussionThread.xml
+++ b/bt5/erp5_discussion/SkinTemplateItem/portal_skins/erp5_discussion/WebSection_createNewDiscussionThread.xml
@@ -50,7 +50,9 @@
         </item>
         <item>
             <key> <string>_body</string> </key>
-            <value> <string>"""\n
+            <value> <string encoding="cdata"><![CDATA[
+
+"""\n
  This script allows to create a new Discussion Thread.\n
 """\n
 MARKER = [\'\', None, []]\n
@@ -70,9 +72,9 @@ if site_list in MARKER:\n
 membership_criterion_category_list = context.getMembershipCriterionCategoryList()\n
 multimembership_criterion_base_category_list = context.getMultimembershipCriterionBaseCategoryList()\n
 \n
-reference = title.replace(\' \', \'-\')\n
-exisiting_document = context.getDocumentValue(reference)\n
-if exisiting_document is not None:\n
+reference = title.replace(\' \', \'-\').replace(\'?\', \'\').replace(\':\', \'\').replace(\'/\', \'\').replace(\'&\', \'\').replace(\'=\', \'\')\n
+existing_document = context.getDocumentValue(reference)\n
+if existing_document is not None:\n
   # if there are other document which reference duplicates just add some random part\n
   # so we can distinguish)\n
   reference = \'%s-%s\' %(context.Base_generateRandomString(), reference)\n
@@ -152,7 +154,9 @@ if send_notification_text not in (\'\', None):\n
 \n
 return context.Base_redirect(form_id,\n
          keep_items = dict(portal_status_message=context.Base_translateString(portal_status_message)))\n
-</string> </value>
+
+
+]]></string> </value>
         </item>
         <item>
             <key> <string>_params</string> </key>
diff --git a/bt5/erp5_discussion/bt/revision b/bt5/erp5_discussion/bt/revision
index 97e35041104a8d82c502ddb40b240e68f392c0d1..2702ba3d439a353c3f748180065944feb28b8b87 100644
--- a/bt5/erp5_discussion/bt/revision
+++ b/bt5/erp5_discussion/bt/revision
@@ -1 +1 @@
-110
\ No newline at end of file
+115
\ No newline at end of file
diff --git a/bt5/erp5_dms_conversion_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_preConvertDocumentList.xml b/bt5/erp5_dms_conversion_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_preConvertDocumentList.xml
index f0c60f264ed8e71d4046e5c1c92494c8be66f9b5..2b33ca5bbd1950c3b10b61f48f643bcb93dfe100 100644
--- a/bt5/erp5_dms_conversion_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_preConvertDocumentList.xml
+++ b/bt5/erp5_dms_conversion_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_preConvertDocumentList.xml
@@ -66,7 +66,7 @@ if address not in MARKER and port not in MARKER:\n
     # we need a way to do this by introspection\n
     if ((getattr(document, "getData", None) is not None and document.getData() not in MARKER) or \\\n
         (getattr(document, "getBaseData", None) is not None and document.getBaseData() not in MARKER)):\n
-      document.activate(tag="conversion").Base_callPreConvert()\n
+      document.activate(priority=4, tag="conversion").Base_callPreConvert()\n
 </string> </value>
         </item>
         <item>
diff --git a/bt5/erp5_dms_conversion_catalog/bt/revision b/bt5/erp5_dms_conversion_catalog/bt/revision
index 8e2afd342773582f9484b796cdc0b84736e8194e..25bf17fc5aaabd17402e77a2b16f95fbea7310d2 100644
--- a/bt5/erp5_dms_conversion_catalog/bt/revision
+++ b/bt5/erp5_dms_conversion_catalog/bt/revision
@@ -1 +1 @@
-17
\ No newline at end of file
+18
\ No newline at end of file
diff --git a/bt5/erp5_km/SkinTemplateItem/portal_skins/erp5_km_theme/km_css/km.css.xml b/bt5/erp5_km/SkinTemplateItem/portal_skins/erp5_km_theme/km_css/km.css.xml
index 08a34f0b960439a2242e806d54c05ebfe1e1c7fa..6e30ad108907a156a00750e9dbf98570e53be5a0 100644
--- a/bt5/erp5_km/SkinTemplateItem/portal_skins/erp5_km_theme/km_css/km.css.xml
+++ b/bt5/erp5_km/SkinTemplateItem/portal_skins/erp5_km_theme/km_css/km.css.xml
@@ -1340,6 +1340,12 @@ button.formbt > span{\n
 .full-width{\n
   width:100%;\n
 }\n
+\n
+/* discussions */\n
+.discussion-post-body-container a{\n
+  color:#686868;\n
+  text-decoration: underline;\n
+}\n
 </tal:block>
 
 ]]></unicode> </value>
diff --git a/bt5/erp5_km/bt/revision b/bt5/erp5_km/bt/revision
index e284175d816d24bcf6aa9b23b64fd45e4bfc2907..5c03cb53f437ecdbe6620b6065baaf3e698e3a3d 100644
--- a/bt5/erp5_km/bt/revision
+++ b/bt5/erp5_km/bt/revision
@@ -1 +1 @@
-1868
\ No newline at end of file
+1869
\ No newline at end of file
diff --git a/bt5/erp5_knowledge_pad/SkinTemplateItem/portal_skins/erp5_knowledge_pad/KnowledgePad_viewColumnWidget.xml b/bt5/erp5_knowledge_pad/SkinTemplateItem/portal_skins/erp5_knowledge_pad/KnowledgePad_viewColumnWidget.xml
index ecec29932907d6164e592a7034dffa50d77a64c3..9be11b3598ca080464c3776dd511705ac3e17ea0 100644
--- a/bt5/erp5_knowledge_pad/SkinTemplateItem/portal_skins/erp5_knowledge_pad/KnowledgePad_viewColumnWidget.xml
+++ b/bt5/erp5_knowledge_pad/SkinTemplateItem/portal_skins/erp5_knowledge_pad/KnowledgePad_viewColumnWidget.xml
@@ -56,6 +56,7 @@
         tal:define="box python: getattr(here, box_id);\n
                     box_relative_url box/getRelativeUrl;\n
                     box_dom_id python: \'%s\' %box_relative_url.replace(\'/\', \'_\');\n
+                    box_class python: (\'%s %s\' % (\'block\', box.getGroup() or \'\')).strip;\n
                     view_form_dom_id python: \'%s_content\' %box_dom_id;\n
                     edit_form_dom_id python: \'%s_edit_form\' %box_dom_id;\n
                     gadget_title_dom_id python: \'%s_gadget_title\' %box_dom_id;\n
@@ -71,13 +72,15 @@
 \n
     <!-- Render gadget as hidden one -->\n
     <div tal:condition="not: is_gadget_visible"\n
-         tal:attributes="id box_dom_id"\n
+         tal:attributes="id box_dom_id;\n
+                         class box_class"\n
          class="block invisible-gadget"></div>\n
 \n
     <!-- Show only public gadgets -->\n
     <div class="block" \n
          tal:condition="is_gadget_visible"\n
-         tal:attributes="id box_dom_id">\n
+         tal:attributes="id box_dom_id;\n
+                         class box_class">\n
 \n
       <h3 class="handle">\n
         <span class="handle">\n
diff --git a/bt5/erp5_knowledge_pad/bt/change_log b/bt5/erp5_knowledge_pad/bt/change_log
index 3d503308dc54e28429d81f649fe20deaa08b82d9..5d256a6afd9e12babd29941bdccfeffc385edc34 100644
--- a/bt5/erp5_knowledge_pad/bt/change_log
+++ b/bt5/erp5_knowledge_pad/bt/change_log
@@ -1,3 +1,6 @@
+2012-06-25 Kazuhiko
+* add knowledge pad's group in gadget title div class.
+
 2011-09-26 Kazuhiko
 * ERP5Site_getKnowledgePadListForUser should handle Unauthorized exception.
 
diff --git a/bt5/erp5_knowledge_pad/bt/revision b/bt5/erp5_knowledge_pad/bt/revision
index 5cd7ca2206dabce480b971122efe38f183187674..b02454449c8cae01aa9a1ce93c15cd4c90e8a117 100644
--- a/bt5/erp5_knowledge_pad/bt/revision
+++ b/bt5/erp5_knowledge_pad/bt/revision
@@ -1 +1 @@
-748
\ No newline at end of file
+749
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/ActionTemplateItem/portal_types/Paypal%20Service/view.xml b/bt5/erp5_paypal_secure_payment/ActionTemplateItem/portal_types/Paypal%20Service/view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..c3335c279f715d141935702903e7c26bbb427ecd
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/ActionTemplateItem/portal_types/Paypal%20Service/view.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="ActionInformation" module="Products.CMFCore.ActionInformation"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>action</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>categories</string> </key>
+            <value>
+              <tuple>
+                <string>action_type/object_view</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>category</string> </key>
+            <value> <string>object_view</string> </value>
+        </item>
+        <item>
+            <key> <string>condition</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>icon</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>view</string> </value>
+        </item>
+        <item>
+            <key> <string>permissions</string> </key>
+            <value>
+              <tuple>
+                <string>View</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Action Information</string> </value>
+        </item>
+        <item>
+            <key> <string>priority</string> </key>
+            <value> <float>10.0</float> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string>View</string> </value>
+        </item>
+        <item>
+            <key> <string>visible</string> </key>
+            <value> <int>1</int> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="Expression" module="Products.CMFCore.Expression"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>text</string> </key>
+            <value> <string>string:${object_url}/PaypalService_view</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/DocumentTemplateItem/PaypalService.py b/bt5/erp5_paypal_secure_payment/DocumentTemplateItem/PaypalService.py
new file mode 100644
index 0000000000000000000000000000000000000000..0069533bbffaca754b8eb407eda9079083024622
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/DocumentTemplateItem/PaypalService.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#                    François-Xavier Algrain <fxalgrain@tiolive.com>
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsability of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# garantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+import zope
+from urllib import urlencode
+from urllib2 import urlopen, Request
+from zLOG import LOG, DEBUG
+from AccessControl import ClassSecurityInfo
+from Products.ERP5Type import Permissions, PropertySheet, interfaces
+from Products.ERP5Type.XMLObject import XMLObject
+from Products.ERP5Type.Document import newTempDocument
+
+
+class PaypalService(XMLObject):
+  """Paypal Service for payment"""
+
+  meta_type = 'Paypal Service'
+  portal_type = 'Paypal Service'
+  security = ClassSecurityInfo()
+  zope.interface.implements(interfaces.IPaymentService)
+
+  # Declarative security
+  security = ClassSecurityInfo()
+  security.declareObjectProtected(Permissions.AccessContentsInformation)
+
+  # Declarative properties
+  property_sheets = (PropertySheet.Base,
+                     PropertySheet.XMLObject,
+                     PropertySheet.Reference
+                    )
+
+  def initialize(self, REQUEST=None, **kw):
+    """See Payment Service Interface Documentation"""
+
+  def _getFieldList(self, paypal_dict):
+    field_list = []
+    for k,v in paypal_dict.iteritems():
+      field_list.append((k, v))
+    return field_list
+
+  def navigate(self, REQUEST=None, **kw):
+    """See Payment Service Interface Documentation"""
+    self.Base_checkConsistency()
+    page_template = kw.pop("page_template")
+    paypal_dict = kw.get("paypal_dict", {})
+    temp_document = newTempDocument(self, 'id')
+    temp_document.edit(
+      link_url_string=self.getLinkUrlString(),
+      title=self.getTitle(),
+      field_list=self._getFieldList(paypal_dict),
+      # append the rest of transmitted parameters page template
+      **kw
+    )
+    return getattr(temp_document, page_template)()
+
+  def notifySuccess(self, redirect_path=None, REQUEST=None):
+    """See Payment Service Interface Documentation"""
+    return self._getTypeBasedMethod("acceptPayment")(redirect_path=redirect_path)
+
+  def notifyFail(self, redirect_path=None, REQUEST=None):
+    """See Payment Service Interface Documentation"""
+    return self._getTypeBasedMethod("failInPayment")(redirect_path=redirect_path)
+
+  def notifyCancel(self, redirect_path=None, REQUEST=None):
+    """See Payment Service Interface Documentation"""
+    return self._getTypeBasedMethod("abortPayment")(redirect_path=redirect_path)
+
+  def reportPaymentStatus(self, REQUEST=None):
+    """See Payment Service Interface Documentation"""
+    param_dict = REQUEST.form
+    LOG("PaypalService", DEBUG, param_dict)
+    param_dict["cmd"] = "_notify-validate"
+    if param_dict.has_key("service"):
+      param_dict.pop("service")
+    param_list = urlencode(param_dict)
+    paypal_url = self.getLinkUrlString()
+    request = Request(paypal_url, param_list)
+    request.add_header("Content-type", "application/x-www-form-urlencoded")
+    response = urlopen(request)
+    status = response.read()
+    method = self._getTypeBasedMethod("reportPaymentStatus")
+    LOG("PaypalService status", DEBUG, status)
+    if method and status == "VERIFIED":
+      method(REQUEST=REQUEST)
+    return True
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/PortalTypeAllowedContentTypeTemplateItem/allowed_content_types.xml b/bt5/erp5_paypal_secure_payment/PortalTypeAllowedContentTypeTemplateItem/allowed_content_types.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e6a48fba4a1fb648c36e115f914f88dcc08c0f42
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PortalTypeAllowedContentTypeTemplateItem/allowed_content_types.xml
@@ -0,0 +1,8 @@
+<allowed_content_type_list>
+ <portal_type id="Paypal Service">
+  <item>Link</item>
+ </portal_type>
+ <portal_type id="Secure Payment Tool">
+  <item>Paypal Service</item>
+ </portal_type>
+</allowed_content_type_list>
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/PortalTypePropertySheetTemplateItem/property_sheet_list.xml b/bt5/erp5_paypal_secure_payment/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
new file mode 100644
index 0000000000000000000000000000000000000000..394fcf35818a3a8d65cbf1fad4e55fa3969586fd
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PortalTypePropertySheetTemplateItem/property_sheet_list.xml
@@ -0,0 +1,6 @@
+<property_sheet_list>
+ <portal_type id="Paypal Service">
+  <item>PaymentService</item>
+  <item>PaypalService</item>
+ </portal_type>
+</property_sheet_list>
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/PortalTypeTemplateItem/portal_types/Paypal%20Service.xml b/bt5/erp5_paypal_secure_payment/PortalTypeTemplateItem/portal_types/Paypal%20Service.xml
new file mode 100644
index 0000000000000000000000000000000000000000..319123ea014e01d1798b1999d28099da9a025f05
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PortalTypeTemplateItem/portal_types/Paypal%20Service.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Base Type" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_property_domain_dict</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>short_title</string> </key>
+                    <value>
+                      <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>title</string> </key>
+                    <value>
+                      <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+                    </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>content_icon</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>Paypal Service</string> </value>
+        </item>
+        <item>
+            <key> <string>init_script</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>permission</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Base Type</string> </value>
+        </item>
+        <item>
+            <key> <string>type_class</string> </key>
+            <value> <string>PaypalService</string> </value>
+        </item>
+        <item>
+            <key> <string>type_mixin</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>domain_name</string> </key>
+            <value> <string>erp5_content</string> </value>
+        </item>
+        <item>
+            <key> <string>property_name</string> </key>
+            <value> <string>short_title</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="3" aka="AAAAAAAAAAM=">
+    <pickle>
+      <global name="TranslationInformation" module="Products.ERP5Type.TranslationProviderBase"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>domain_name</string> </key>
+            <value> <string>erp5_content</string> </value>
+        </item>
+        <item>
+            <key> <string>property_name</string> </key>
+            <value> <string>title</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService.xml b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService.xml
new file mode 100644
index 0000000000000000000000000000000000000000..60edf31b5cdbf8842c8c710bd9ab30f53f4c7057
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Property Sheet" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_count</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>_mt_index</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>_tree</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAQ=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Property Sheet</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="Length" module="BTrees.Length"/>
+    </pickle>
+    <pickle> <int>0</int> </pickle>
+  </record>
+  <record id="3" aka="AAAAAAAAAAM=">
+    <pickle>
+      <global name="OOBTree" module="BTrees.OOBTree"/>
+    </pickle>
+    <pickle>
+      <none/>
+    </pickle>
+  </record>
+  <record id="4" aka="AAAAAAAAAAQ=">
+    <pickle>
+      <global name="OOBTree" module="BTrees.OOBTree"/>
+    </pickle>
+    <pickle>
+      <none/>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/link_url_string_constraint.xml b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/link_url_string_constraint.xml
new file mode 100644
index 0000000000000000000000000000000000000000..b8b814be7ad9586e72400db6bfdc0a142c57d9c1
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/link_url_string_constraint.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="String Attribute Match Constraint" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_identity_criterion</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>_local_properties</string> </key>
+            <value>
+              <tuple>
+                <dictionary>
+                  <item>
+                      <key> <string>id</string> </key>
+                      <value> <string>message_property_not_set</string> </value>
+                  </item>
+                  <item>
+                      <key> <string>type</string> </key>
+                      <value> <string>string</string> </value>
+                  </item>
+                </dictionary>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>_range_criterion</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+            </value>
+        </item>
+        <item>
+            <key> <string>constraint_property</string> </key>
+            <value>
+              <tuple>
+                <string>link_url_string</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>link_url_string_constraint</string> </value>
+        </item>
+        <item>
+            <key> <string>message_attribute_match</string> </key>
+            <value> <string>Paypal URL have to be set</string> </value>
+        </item>
+        <item>
+            <key> <string>message_no_such_property</string> </key>
+            <value> <string>Paypal URL have to be set</string> </value>
+        </item>
+        <item>
+            <key> <string>message_property_not_set</string> </key>
+            <value> <string>Paypal URL have to be set</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>String Attribute Match Constraint</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="PersistentMapping" module="Persistence.mapping"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>data</string> </key>
+            <value>
+              <dictionary/>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="3" aka="AAAAAAAAAAM=">
+    <pickle>
+      <global name="PersistentMapping" module="Persistence.mapping"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>data</string> </key>
+            <value>
+              <dictionary/>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/service_signature_property.xml b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/service_signature_property.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f97dac1259077c366cfb9eb8b2d687ab37634331
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/PropertySheetTemplateItem/portal_property_sheets/PaypalService/service_signature_property.xml
@@ -0,0 +1,57 @@
+<?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>_local_properties</string> </key>
+            <value>
+              <tuple>
+                <dictionary>
+                  <item>
+                      <key> <string>id</string> </key>
+                      <value> <string>mode</string> </value>
+                  </item>
+                  <item>
+                      <key> <string>type</string> </key>
+                      <value> <string>string</string> </value>
+                  </item>
+                </dictionary>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>categories</string> </key>
+            <value>
+              <tuple>
+                <string>elementary_type/string</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value> <string>Signature of account</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>service_signature_property</string> </value>
+        </item>
+        <item>
+            <key> <string>mode</string> </key>
+            <value> <string>w</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Standard Property</string> </value>
+        </item>
+        <item>
+            <key> <string>property_default</string> </key>
+            <value> <string>python: \'\'</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..374d52ff70cd41c8e564b53633899c18fe9b54a5
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Folder" module="OFS.Folder"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_objects</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>erp5_paypal_secure_payment</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_abortPayment.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_abortPayment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..9360d3c940bf9a9c0b846cb5c403e4dbde5d54a6
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_abortPayment.xml
@@ -0,0 +1,72 @@
+<?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>"""Redirect user to the path\n
+Parameters:\n
+redirect_path -- Specify where redirect user, use \'paypal.payment.aborted\' as default"""\n
+\n
+context.REQUEST.RESPONSE.redirect("%s/%s" % (context.Base_getWebSiteSecureUrl(), \n
+                                             redirect_path))\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>redirect_path=\'paypal.payment.aborted\', **kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_abortPayment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_acceptPayment.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_acceptPayment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..83bdbcb4f30b303afcfb66232509a2659f39afa3
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_acceptPayment.xml
@@ -0,0 +1,72 @@
+<?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>"""Redirect user to the path\n
+Parameters:\n
+redirect_path -- Specify where redirect user, use \'paypal.payment.accepted\' as default"""\n
+\n
+context.REQUEST.RESPONSE.redirect("%s/%s" % (context.Base_getWebSiteSecureUrl(), \n
+                                             redirect_path))\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>redirect_path=\'paypal.payment.accepted\', **kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_acceptPayment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_failInPayment.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_failInPayment.xml
new file mode 100644
index 0000000000000000000000000000000000000000..e78c2f855a2165f4796b6b27a8bf0ec08b640258
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_failInPayment.xml
@@ -0,0 +1,72 @@
+<?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>"""Redirect user to the path\n
+Parameters:\n
+redirect_path -- Specify where redirect user, use \'paypal.payment.faild\' as default"""\n
+\n
+context.REQUEST.RESPONSE.redirect("%s/%s" % (context.Base_getWebSiteSecureUrl(), \n
+                                             redirect_path))\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>redirect_path=\'paypal.payment.failed\', **kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_failInPayment</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_getParameterDict.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_getParameterDict.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a4eabeac91757c8e28cf066de3da7c8fbcb7b39a
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_getParameterDict.xml
@@ -0,0 +1,67 @@
+<?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>return {}\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>**kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_getParameterDict</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_reportPaymentStatus.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_reportPaymentStatus.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ed9e67ffdc3f777d1023a82a9bb977b1d5c004f1
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_reportPaymentStatus.xml
@@ -0,0 +1,68 @@
+<?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>"""Call in server side after payment transaction"""\n
+# Do nothing by default\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>REQUEST, **kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_reportPaymentStatus</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8d58ce3a77e54afb6bdd7948283f52d5c681fdd3
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="ERP5Form" module="Products.ERP5Form.Form"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_bind_names</string> </key>
+            <value>
+              <object>
+                <klass>
+                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
+                </klass>
+                <tuple/>
+                <state>
+                  <dictionary>
+                    <item>
+                        <key> <string>_asgns</string> </key>
+                        <value>
+                          <dictionary/>
+                        </value>
+                    </item>
+                  </dictionary>
+                </state>
+              </object>
+            </value>
+        </item>
+        <item>
+            <key> <string>_objects</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>action</string> </key>
+            <value> <string>Base_edit</string> </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>edit_order</string> </key>
+            <value>
+              <list/>
+            </value>
+        </item>
+        <item>
+            <key> <string>encoding</string> </key>
+            <value> <string>UTF-8</string> </value>
+        </item>
+        <item>
+            <key> <string>enctype</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>group_list</string> </key>
+            <value>
+              <list>
+                <string>left</string>
+                <string>right</string>
+                <string>center</string>
+                <string>bottom</string>
+                <string>hidden</string>
+              </list>
+            </value>
+        </item>
+        <item>
+            <key> <string>groups</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>bottom</string> </key>
+                    <value>
+                      <list>
+                        <string>listbox</string>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>center</string> </key>
+                    <value>
+                      <list/>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>hidden</string> </key>
+                    <value>
+                      <list>
+                        <string>listbox_int_index</string>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>left</string> </key>
+                    <value>
+                      <list>
+                        <string>my_title</string>
+                        <string>my_reference</string>
+                        <string>my_link_url_string</string>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>right</string> </key>
+                    <value>
+                      <list>
+                        <string>my_service_username</string>
+                        <string>my_service_password</string>
+                        <string>my_service_signature</string>
+                      </list>
+                    </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>PaypalService_view</string> </value>
+        </item>
+        <item>
+            <key> <string>method</string> </key>
+            <value> <string>POST</string> </value>
+        </item>
+        <item>
+            <key> <string>name</string> </key>
+            <value> <string>PaypalService_view</string> </value>
+        </item>
+        <item>
+            <key> <string>pt</string> </key>
+            <value> <string>form_view</string> </value>
+        </item>
+        <item>
+            <key> <string>row_length</string> </key>
+            <value> <int>4</int> </value>
+        </item>
+        <item>
+            <key> <string>stored_encoding</string> </key>
+            <value> <string>UTF-8</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>unicode_mode</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>update_action</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>update_action_title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox.xml
new file mode 100644
index 0000000000000000000000000000000000000000..f625c78ad4ced74b06b393756d8a74032140ea6a
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox.xml
@@ -0,0 +1,186 @@
+<?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>anchor</string>
+                <string>columns</string>
+                <string>editable_columns</string>
+                <string>portal_types</string>
+                <string>selection_name</string>
+                <string>sort</string>
+                <string>title</string>
+                <string>url_columns</string>
+              </list>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>listbox</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>anchor</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+                <item>
+                    <key> <string>columns</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>int_index</string>
+                          <string>Index</string>
+                        </tuple>
+                        <tuple>
+                          <string>title</string>
+                          <string>Title</string>
+                        </tuple>
+                        <tuple>
+                          <string>translated_id</string>
+                          <string>Coordinate Function</string>
+                        </tuple>
+                        <tuple>
+                          <string>url_string</string>
+                          <string>URL</string>
+                        </tuple>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>editable_columns</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>int_index</string>
+                          <string>Index</string>
+                        </tuple>
+                        <tuple>
+                          <string>title</string>
+                          <string>Title</string>
+                        </tuple>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_view_mode_listbox</string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string>Base_viewFieldLibrary</string> </value>
+                </item>
+                <item>
+                    <key> <string>portal_types</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>Link</string>
+                          <string>Link</string>
+                        </tuple>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>selection_name</string> </key>
+                    <value> <string>paypal_service_list_selection</string> </value>
+                </item>
+                <item>
+                    <key> <string>sort</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>portal_type</string>
+                          <string>Type</string>
+                        </tuple>
+                        <tuple>
+                          <string>int_index</string>
+                          <string>Index</string>
+                        </tuple>
+                      </list>
+                    </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>Coordinates</string> </value>
+                </item>
+                <item>
+                    <key> <string>url_columns</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>url_string</string>
+                          <string>Coordinate_asURL</string>
+                        </tuple>
+                      </list>
+                    </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox_int_index.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox_int_index.xml
new file mode 100644
index 0000000000000000000000000000000000000000..008e8f0e77c76346ad472e480e6415016dd34e5c
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/listbox_int_index.xml
@@ -0,0 +1,115 @@
+<?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/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>listbox_int_index</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>items</string> </key>
+                    <value>
+                      <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_view_mode_int_index</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>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <tuple>
+        <tuple>
+          <string>Products.Formulator.TALESField</string>
+          <string>TALESMethod</string>
+        </tuple>
+        <none/>
+      </tuple>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_text</string> </key>
+            <value> <string>here/portal_categories/activity/getCategoryChildTranslatedLogicalPathItemList</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_link_url_string.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_link_url_string.xml
new file mode 100644
index 0000000000000000000000000000000000000000..355be7c48f5cebb1e4829a11572dadbe3e71c13e
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_link_url_string.xml
@@ -0,0 +1,96 @@
+<?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_link_url_string</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_link_field</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>Paypal URL</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_reference.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_reference.xml
new file mode 100644
index 0000000000000000000000000000000000000000..50e005aef6f04a485a8fef90feea6833ce5dbe2a
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_reference.xml
@@ -0,0 +1,90 @@
+<?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/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>my_reference</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_dialog_mode_reference</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>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_password.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_password.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7f8e8391046320be3b0de77d1d66d471bd56e7da
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_password.xml
@@ -0,0 +1,90 @@
+<?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/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>my_service_password</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_password</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>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_signature.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_signature.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ee5eca2f23bdacf712064996a69ef51e9acc61f1
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_signature.xml
@@ -0,0 +1,96 @@
+<?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_service_signature</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_string_field</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>Signature</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_username.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_username.xml
new file mode 100644
index 0000000000000000000000000000000000000000..03a3748b088b51e9929d2a41a57010c3a8cbc5fa
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_service_username.xml
@@ -0,0 +1,96 @@
+<?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_service_username</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_string_field</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>Username</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_title.xml b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_title.xml
new file mode 100644
index 0000000000000000000000000000000000000000..ef309226fd90d5ae28f312c8cc57561e420f08fb
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/SkinTemplateItem/portal_skins/erp5_paypal_secure_payment/PaypalService_view/my_title.xml
@@ -0,0 +1,90 @@
+<?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/>
+            </value>
+        </item>
+        <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>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>form_id</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>target</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>field_id</string> </key>
+                    <value> <string>my_view_mode_title</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>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_paypal_secure_payment/TestTemplateItem/testERP5PaypalSecurePayment.py b/bt5/erp5_paypal_secure_payment/TestTemplateItem/testERP5PaypalSecurePayment.py
new file mode 100644
index 0000000000000000000000000000000000000000..710d1da8888260d2456081f2562aa3dcb364039d
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/TestTemplateItem/testERP5PaypalSecurePayment.py
@@ -0,0 +1,101 @@
+##############################################################################
+#
+# Copyright (c) 2002-2012 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import random
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+
+
+def getMessageList(o):
+  return [str(q.getMessage()) for q in o.checkConsistency()]
+
+class TestERP5PaypalSecurePaymentMixin(ERP5TypeTestCase):
+  """
+  An ERP5 Paypal Secure Payment test case
+  """
+
+  def getTitle(self):
+    return "ERP5 Paypal Secure Payment"
+
+  def getBusinessTemplateList(self):
+    """
+    Tuple of Business Templates we need to install
+    """
+    return ('erp5_base',
+            'erp5_secure_payment',
+            'erp5_paypal_secure_payment')
+
+  def afterSetUp(self):
+    self.portal = self.getPortalObject()
+    if not self.portal.hasObject('portal_secure_payments'):
+      self.portal.manage_addProduct['ERP5SecurePayment'].manage_addTool(
+        'ERP5 Secure Payment Tool', None)
+      self.tic()
+    self.service = self.portal.portal_secure_payments.newContent(
+      portal_type='Paypal Service',
+      reference="default")
+    self.tic()
+
+
+class TestERP5PaypalSecurePaymenConstraint(TestERP5PaypalSecurePaymentMixin):
+
+  def _test(self, message, prop, value='12345'):
+    self.assertTrue(message in getMessageList(self.service))
+    self.service.edit(**{prop: value})
+    self.assertFalse(message in getMessageList(self.service))
+
+  def test_link_url_string(self):
+    self._test('Paypal URL have to be set', 'link_url_string')
+
+
+class TestERP5PaypalSecurePayment(TestERP5PaypalSecurePaymentMixin):
+
+  def test_navigate(self):
+    self.service.edit(
+      link_url_string='http://paypal/',
+      service_username="business@sample.com"
+    )
+    pt_id = str(random.random())
+    page_template_text = """<tal:block tal:repeat="value here/field_list">key=<tal:block tal:replace="python: value[0]"/> value=<tal:block tal:replace="python: value[1]"/>
+</tal:block>link=<tal:block tal:replace='here/link_url_string'/>
+business=<tal:block tal:replace='here/service_username'/>
+    """
+    self.portal.portal_skins.custom.manage_addProduct['PageTemplates']\
+                .manage_addPageTemplate(id=pt_id, text=page_template_text)
+    # flush skin cache
+    self.portal.changeSkin(None)
+    paypal_dict = {
+        "return" : "http://ipn/"
+    }
+    try:
+      result = self.service.navigate(page_template=pt_id, paypal_dict=paypal_dict)
+      self.assertEquals(result, """key=return value=http://ipn/
+link=http://paypal/
+business=business@sample.com""")
+    finally:
+      self.portal.portal_skins.custom.manage_delObjects([pt_id])
+      # flush skin cache
+      self.portal.changeSkin(None)
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/change_log b/bt5/erp5_paypal_secure_payment/bt/change_log
new file mode 100644
index 0000000000000000000000000000000000000000..2f0234e929398c8d2db682f836df9b6608745d51
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/change_log
@@ -0,0 +1,2 @@
+2012/07/03 Gabriel Monnerat
+* Initial version.
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/copyright_list b/bt5/erp5_paypal_secure_payment/bt/copyright_list
new file mode 100644
index 0000000000000000000000000000000000000000..dd86b8376aa272001a8ce936e0bc88919f447407
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/copyright_list
@@ -0,0 +1 @@
+Nexedi SA 2012
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/dependency_list b/bt5/erp5_paypal_secure_payment/bt/dependency_list
new file mode 100644
index 0000000000000000000000000000000000000000..eab34eef81a5d380b5042d123daff15274056e66
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/dependency_list
@@ -0,0 +1 @@
+erp5_secure_payment
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/description b/bt5/erp5_paypal_secure_payment/bt/description
new file mode 100644
index 0000000000000000000000000000000000000000..d54f0cec2258b748ea7624c20acb9e4c3fd50d1a
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/description
@@ -0,0 +1 @@
+Integrate Paypal payment service in ERP5
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/license b/bt5/erp5_paypal_secure_payment/bt/license
new file mode 100644
index 0000000000000000000000000000000000000000..3a3e12bcad97e4b3bdd6a8bb499fd23a4bcb0819
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/license
@@ -0,0 +1 @@
+GPL
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/maintainer_list b/bt5/erp5_paypal_secure_payment/bt/maintainer_list
new file mode 100644
index 0000000000000000000000000000000000000000..38363f73039fa6118b4a55f85965b90af9fc9384
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/maintainer_list
@@ -0,0 +1 @@
+gabriel
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/revision b/bt5/erp5_paypal_secure_payment/bt/revision
new file mode 100644
index 0000000000000000000000000000000000000000..d8263ee9860594d2806b0dfd1bfd17528b0ba2a4
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/revision
@@ -0,0 +1 @@
+2
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_action_path_list b/bt5/erp5_paypal_secure_payment/bt/template_action_path_list
new file mode 100644
index 0000000000000000000000000000000000000000..e20444be8b380fbc66b3ee1b3ca410a5512ee04d
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_action_path_list
@@ -0,0 +1 @@
+Paypal Service | view
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_document_id_list b/bt5/erp5_paypal_secure_payment/bt/template_document_id_list
new file mode 100644
index 0000000000000000000000000000000000000000..157fae487975ef103231fb335cac4930df6a5df2
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_document_id_list
@@ -0,0 +1 @@
+PaypalService
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_format_version b/bt5/erp5_paypal_secure_payment/bt/template_format_version
new file mode 100644
index 0000000000000000000000000000000000000000..56a6051ca2b02b04ef92d5150c9ef600403cb1de
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_format_version
@@ -0,0 +1 @@
+1
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_portal_type_allowed_content_type_list b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_allowed_content_type_list
new file mode 100644
index 0000000000000000000000000000000000000000..492a8f1ee459779ee8fd03aef34b3745ff694b24
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_allowed_content_type_list
@@ -0,0 +1,2 @@
+Paypal Service | Link
+Secure Payment Tool | Paypal Service
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_portal_type_id_list b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_id_list
new file mode 100644
index 0000000000000000000000000000000000000000..14966cc636e15e71e1af4ac137710159ebc57984
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_id_list
@@ -0,0 +1 @@
+Paypal Service
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_portal_type_property_sheet_list b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_property_sheet_list
new file mode 100644
index 0000000000000000000000000000000000000000..fb17e0c12bb3a3d1a1410521e6ca551d8950cfd7
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_portal_type_property_sheet_list
@@ -0,0 +1,2 @@
+Paypal Service | PaymentService
+Paypal Service | PaypalService
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_property_sheet_id_list b/bt5/erp5_paypal_secure_payment/bt/template_property_sheet_id_list
new file mode 100644
index 0000000000000000000000000000000000000000..157fae487975ef103231fb335cac4930df6a5df2
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_property_sheet_id_list
@@ -0,0 +1 @@
+PaypalService
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_skin_id_list b/bt5/erp5_paypal_secure_payment/bt/template_skin_id_list
new file mode 100644
index 0000000000000000000000000000000000000000..1e55ae79120594110423e11fc9eb277a0c39ba59
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_skin_id_list
@@ -0,0 +1 @@
+erp5_paypal_secure_payment
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/template_test_id_list b/bt5/erp5_paypal_secure_payment/bt/template_test_id_list
new file mode 100644
index 0000000000000000000000000000000000000000..8cd365f91b4590c34e54c02e2867c26167fb6d62
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/template_test_id_list
@@ -0,0 +1 @@
+testERP5PaypalSecurePayment
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/title b/bt5/erp5_paypal_secure_payment/bt/title
new file mode 100644
index 0000000000000000000000000000000000000000..1e55ae79120594110423e11fc9eb277a0c39ba59
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/title
@@ -0,0 +1 @@
+erp5_paypal_secure_payment
\ No newline at end of file
diff --git a/bt5/erp5_paypal_secure_payment/bt/version b/bt5/erp5_paypal_secure_payment/bt/version
new file mode 100644
index 0000000000000000000000000000000000000000..48360de846a2e022a0b981d250895f20d3480d34
--- /dev/null
+++ b/bt5/erp5_paypal_secure_payment/bt/version
@@ -0,0 +1 @@
+5.4.7
\ No newline at end of file
diff --git a/bt5/erp5_web/SkinTemplateItem/portal_skins/erp5_web_crm/WebSection_addWebMessage.xml b/bt5/erp5_web/SkinTemplateItem/portal_skins/erp5_web_crm/WebSection_addWebMessage.xml
index 4fbdb4794923500069bc0de682202db91d56d93f..ce7ddd7295b2a0a5825f1aef068613e218e84dc4 100644
--- a/bt5/erp5_web/SkinTemplateItem/portal_skins/erp5_web_crm/WebSection_addWebMessage.xml
+++ b/bt5/erp5_web/SkinTemplateItem/portal_skins/erp5_web_crm/WebSection_addWebMessage.xml
@@ -79,9 +79,10 @@ if connected_user is not None:\n
 # 1- Metadata discovery will be run by alarms with allowed user to access other documents.\n
 # 2- A proxy role can not wrap portal_contributions calls and disallow Anonymous user to create the document.\n
 tag = \'incoming_web_message\'\n
+edit_kw[\'activate_kw\'] = {\'tag\': tag}\n
 module.activate(tag=tag, activity=\'SQLQueue\').EventModule_addWebMessage(**edit_kw)\n
 \n
-# Trig explicitely alarm which will run discoverMetadata on created event, then \n
+# Trigger explicitly the alarm which will run discoverMetadata on created event, then \n
 # Fill in discoverable properties (sender, recipient, ...) and change workflow states.\n
 # XXX hardcoded id, must be picked up by reference and version API\n
 portal.portal_alarms.fetch_incoming_web_message_list.activate(after_tag=tag).activeSense()\n
diff --git a/bt5/erp5_web/bt/revision b/bt5/erp5_web/bt/revision
index 77bdd49b54fc4dab5d498705cf6d4df5982d6d55..1bb2f3ded3b22065bb443b414c536e0e8bea3d45 100644
--- a/bt5/erp5_web/bt/revision
+++ b/bt5/erp5_web/bt/revision
@@ -1 +1 @@
-1085
\ No newline at end of file
+1088
\ No newline at end of file
diff --git a/erp5/util/README.taskdistribution.txt b/erp5/util/README.taskdistribution.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4aa27463e7b7177cbb1a452716f08d91ca3acf47
--- /dev/null
+++ b/erp5/util/README.taskdistribution.txt
@@ -0,0 +1,5 @@
+erp5.util.taskdistribution
+--------------------------
+Module to access TaskDistributionTool, used to run test on several machines
+and aggregating results.
+Use pydoc to get module documentation and usage example.
diff --git a/erp5/util/taskdistribution/__init__.py b/erp5/util/taskdistribution/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..4752037e4f6d1f15cabb578698f813050b6408e2
--- /dev/null
+++ b/erp5/util/taskdistribution/__init__.py
@@ -0,0 +1,443 @@
+##############################################################################
+#
+# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 3
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+##############################################################################
+"""
+Client implementation for portal_task_distribution.
+
+Example use:
+  import erp5.util.taskdistribution
+  tool = erp5.util.taskdistribution.TaskDistributionTool(...)
+  test_result = tool.createTestResult(...)
+  test_result.addWatch('foo', open('foo'))
+  while True:
+      test_line = test_result.start()
+      if not test_line:
+          break
+      # Run the test_line.name test
+      test_line.stop()
+"""
+import logging
+import select
+import socket
+import threading
+import time
+import xmlrpclib
+
+__all__ = ['TaskDistributionTool', 'TestResultProxy', 'TestResultLineProxy', 'patchRPCParser']
+
+# Depending on used xmlrpc backend, different exceptions can be thrown.
+SAFE_RPC_EXCEPTION_LIST = [socket.error, xmlrpclib.ProtocolError,
+    xmlrpclib.Fault]
+parser, _ = xmlrpclib.getparser()
+if xmlrpclib.ExpatParser and isinstance(parser, xmlrpclib.ExpatParser):
+    SAFE_RPC_EXCEPTION_LIST.append(xmlrpclib.expat.ExpatError)
+else:
+    import sys
+    print >> sys.stderr, 'Warning: unhandled xmlrpclib parser %r, some ' \
+        'exceptions might get through safeRpcCall' % (parser, )
+    del sys
+SAFE_RPC_EXCEPTION_LIST = tuple(SAFE_RPC_EXCEPTION_LIST)
+del parser, _
+
+def null_callable(*args, **kw):
+    pass
+
+class NullLogger(object):
+    def __getattr__(self, name):
+        return null_callable
+null_logger = NullLogger()
+
+def patchRPCParser(error_handler):
+    """
+    Patch xmlrpcmlib's parser class, so it logs data content in case of errors,
+    to ease debugging.
+    Warning: this installs a monkey patch on a generic class, so it's last
+    comes wins. Must *not* be enabled by default.
+
+    error_handler (callable)
+      Receives the erroneous data as first parameter, and the exception
+      instance as second parameter.
+      If it returns a false value (ie, handler did not recover from the error),
+      exception is re-raised.
+    """
+    parser, _ = xmlrpclib.getparser()
+    parser_klass = parser.__class__
+    original_feed = parser_klass.feed
+    def verbose_feed(self, data):
+        try:
+            return original_feed(self, data)
+        except Exception, exc:
+            if not error_handler(data, exc):
+                raise
+    parser_klass.feed = verbose_feed
+
+class RPCRetry(object):
+    def __init__(self, proxy, retry_time, logger):
+        super(RPCRetry, self).__init__()
+        self._proxy = proxy
+        self._retry_time = retry_time
+        self._logger = logger
+        self.__rpc_lock = threading.Lock()
+
+    def _RPC(self, func_id, args=()):
+        with self.__rpc_lock:
+            return getattr(self._proxy, func_id)(*args)
+
+    def _retryRPC(self, func_id, args=()):
+        retry_time = self._retry_time
+        while True:
+            try:
+                return self._RPC(func_id, args)
+            except SAFE_RPC_EXCEPTION_LIST:
+                self._logger.warning('Got exception, retrying: %s%r '
+                    'in %is', func_id, tuple(args), retry_time, exc_info=1)
+                time.sleep(retry_time)
+                retry_time *= 1.5
+
+class TestResultLineProxy(RPCRetry):
+    """
+    Represents a single test in a suite.
+
+    Properties:
+    name (str) (ro)
+      Test name, as provided to TaskDistributionTool.createTestResult .
+    """
+    def __init__(self, proxy, retry_time, logger, test_result_line_path,
+            test_name):
+        super(TestResultLineProxy, self).__init__(proxy, retry_time, logger)
+        self._test_result_line_path = test_result_line_path
+        self._name = test_name
+
+    def __repr__(self):
+        return '<%s(%r, %r) at %x>' % (self.__class__.__name__,
+            self._test_result_line_path, self._name, id(self))
+
+    @property
+    def name(self):
+        return self._name
+
+    def stop(self, test_count=None, error_count=None, failure_count=None,
+            skip_count=None, duration=None, date=None, command=None,
+            stdout=None, stderr=None, html_test_result=None):
+        """
+        Notify server of test completion.
+
+        Without any parameter, notifies of a test failure which prevents any
+        precise reading (step count, how many succeeded, etc).
+        """
+        status_dict = {
+            'test_count': test_count,
+            'error_count': error_count,
+            'failure_count': failure_count,
+            'skip_count': skip_count,
+            'duration': duration,
+            'date': date,
+        }
+        if command is not None:
+            status_dict['command'] = command
+        if stdout is not None:
+            status_dict['stdout'] = stdout
+        if stderr is not None:
+            status_dict['stderr'] = stderr
+        if html_test_result is not None:
+            status_dict['html_test_result'] = html_test_result
+        self._retryRPC('stopUnitTest', (self._test_result_line_path,
+            status_dict))
+
+class TestResultProxy(RPCRetry):
+    """
+    Represents a test suite run.
+
+    Allows fetching work to do (eg a single test in an entire run), monitoring
+    log files, informing server of problems and monitoring server-side
+    cancellation.
+
+    Properties
+    watcher_period (float) (rw)
+      How long log watcher sleeps between successive uploading latest data
+      chunks.
+    revision (str) (ro)
+      Revision to test. Might be different from the revision requested, when a
+      test batch is running on an older revision.
+    """
+    _watcher_can_run = True
+    _watcher_thread = None
+
+    def __init__(self, proxy, retry_time, logger, test_result_path, node_title,
+            revision):
+        super(TestResultProxy, self).__init__(proxy, retry_time, logger)
+        self._test_result_path = test_result_path
+        self._node_title = node_title
+        self._revision = revision
+        self._watcher_period = 60
+        self._watcher_dict = {}
+        self._watcher_condition = threading.Condition()
+
+    def __repr__(self):
+        return '<%s(%r, %r, %r) at %x>' % (self.__class__.__name__,
+            self._test_result_path, self._node_title, self._revision, id(self))
+
+    @property
+    def revision(self):
+        return self._revision
+
+    def start(self, exclude_list=()):
+        """
+        Ask for a test to run, among the list of tests composing this test
+        result.
+        Return an TestResultLineProxy instance, or None if there is nothing to
+        do.
+        """
+        result = self._retryRPC('startUnitTest', (self._test_result_path,
+            exclude_list))
+        if result:
+            line_url, test_name = result
+            result = TestResultLineProxy(self._proxy, self._retry_time,
+                self._logger, line_url, test_name)
+        return result
+
+    def reportFailure(self, date=None, command=None, stdout=None, stderr=None):
+        """
+        Report a test-node-level problem, preventing the test from continuing
+        on this node.
+        """
+        self._stopWatching()
+        status_dict = {
+            'date': date,
+        }
+        if command is not None:
+            status_dict['command'] = command
+        if stdout is not None:
+            status_dict['stdout'] = stdout
+        if stderr is not None:
+            status_dict['stderr'] = stderr
+        self._retryRPC('reportTaskFailure', args=(self._test_result_path,
+            status_dict, self._node_title))
+
+    def reportStatus(self, command, stdout, stderr):
+        """
+        Report some progress.
+
+        Used internally by file monitoring, you shouldn't have to use this
+        directly.
+        """
+        try:
+            self._RPC('reportTaskStatus', (self._test_result_path, {
+                'command': command,
+                'stdout': stdout,
+                'stderr': stderr,
+            }, self._node_title))
+        except SAFE_RPC_EXCEPTION_LIST:
+            self._logger.warning('Got exception in reportTaskStatus, giving up',
+                exc_info=1)
+
+    def isAlive(self):
+        """
+        Tell if test is still alive on site.
+
+        Useful to probe for test cancellation by user, so a new test run can
+        be started without waiting for current one to finish.
+        """
+        try:
+            return self._RPC('isTaskAlive', (self._test_result_path, ))
+        except SAFE_RPC_EXCEPTION_LIST:
+            self._logger.warning('Got exception in isTaskAlive, assuming alive',
+                exc_info=1)
+            return 1
+
+    @property
+    def watcher_period(self):
+        return self._watcher_period
+
+    @watcher_period.setter
+    def watcher_period(self, period):
+        cond = self._watcher_condition
+        with cond:
+            self._watcher_period = period
+            cond.notify()
+
+    def addWatch(self, name, stream, max_history_bytes=None):
+        """
+        Monitor given file, sending a few latest lines to remote server.
+        name (any)
+          Arbitrary identifier for stream. Must be usable as a dict key.
+        stream (file object)
+          File to monitor from its current offset.
+        max_history_bytes (int, None)
+          How many bytes to send to remote server at most for each wakeup.
+          If None, send all lines.
+        """
+        watcher_dict = self._watcher_dict
+        if not watcher_dict:
+            self._startWatching()
+        elif name in watcher_dict:
+            raise ValueError('Name already known: %r' % (name, ))
+        watcher_dict[name] = (stream, max_history_bytes)
+
+    def removeWatch(self, name):
+        """
+        Stop monitoring given stream.
+        """
+        watcher_dict = self._watcher_dict
+        del watcher_dict[name]
+        if not watcher_dict:
+            self._stopWatching()
+
+    def _startWatching(self):
+        if self._watcher_thread is not None:
+            raise ValueError('Thread already started')
+        self._watcher_thread = thread = threading.Thread(target=self._watcher)
+        thread.daemon = True
+        thread.start()
+
+    def _watcher(self):
+        cond = self._watcher_condition
+        while self._watcher_can_run and self.isAlive():
+            working = time.time()
+            caption_list = []
+            append = caption_list.append
+            for name, (stream, max_history_bytes) in \
+                    self._watcher_dict.iteritems():
+                append('==> %s <==' % (name, ))
+                start = stream.tell()
+                stream.seek(0, 2)
+                end = stream.tell()
+                if start == end:
+                    caption = time.strftime(
+                        '(no new lines at %Y/%m/%d %H:%M:%S)', time.gmtime())
+                else:
+                    to_read = end - start
+                    if to_read < 0:
+                        # File got truncated, treat the whole content as new.
+                        to_read = end
+                    if max_history_bytes is not None:
+                        to_read = min(to_read, max_history_bytes)
+                    stream.seek(-to_read, 1)
+                    caption = stream.read(to_read)
+                append(caption)
+            self.reportStatus('', '\n'.join(caption_list), '')
+            with cond:
+                cond.wait(max(self._watcher_period - (working - time.time()),
+                    0))
+
+    def _stopWatching(self):
+        cond = self._watcher_condition
+        with cond:
+            self._watcher_can_run = False
+            cond.notify()
+        self._watcher_thread.join()
+
+class TaskDistributionTool(RPCRetry):
+    def __init__(self, portal_url, retry_time=64, logger=None):
+        """
+        portal_url (str, None)
+          Portal URL of ERP5 site to use as a task distributor.
+          If None, single node setup is assumed.
+        """
+        if logger is None:
+            logger = null_logger
+        if portal_url is None:
+            proxy = DummyTaskDistributionTool()
+        else:
+            proxy = xmlrpclib.ServerProxy(
+                portal_url,
+                allow_none=True,
+            ).portal_task_distribution
+        super(TaskDistributionTool, self).__init__(proxy, retry_time, logger)
+        protocol_revision = self._retryRPC('getProtocolRevision')
+        if protocol_revision != 1:
+            raise ValueError('Unsupported protocol revision: %r',
+                protocol_revision)
+
+    def createTestResult(self, revision, test_name_list, node_title,
+            allow_restart=False, test_title=None, project_title=None):
+        """
+        (maybe) create a new test run.
+        revision (str)
+          An opaque string describing code being tested.
+        test_name_list (list of str)
+          List of tests being part of this test run. May be empty.
+        node_title (str)
+          Human-readable test node identifier, so an adnmin can know which
+          node does what.
+        allow_restart (bool)
+          When true, a tet result is always created, even if a former finished
+          one is found for same name and revision pair.
+        test_title (str)
+          Human-readable title for test. Must be identical for successive runs.
+          Allows browsing its result history.
+        project_title (str)
+          Existing project title, so test result gets associated to it.
+
+        Returns None if no test run is needed (a test run for given name and
+        revision has already been completed).
+        Otherwise, returns a TestResultProxy instance.
+        """
+        result = self._retryRPC('createTestResult', ('', revision,
+            test_name_list, allow_restart, test_title, node_title,
+            project_title))
+        if result:
+            test_result_path, revision = result
+            result = TestResultProxy(self._proxy, self._retry_time,
+                self._logger, test_result_path, node_title, revision)
+        return result
+
+class DummyTaskDistributionTool(object):
+    """
+    Fake remote server.
+
+    Useful when willing to locally run all tests without reporting to any
+    server.
+
+    This class should remain internal to this module.
+    """
+    test_name_list = None
+
+    def __init__(self):
+        self._lock = threading.Lock()
+
+    def getProtocolRevision(self):
+        return 1
+
+    def createTestResult(self, name, revision, test_name_list, *args):
+        self.test_name_list = test_name_list[:]
+        return None, revision
+
+    def startUnitTest(self, test_result_path, exclude_list=()):
+        with self._lock:
+            for i, test in enumerate(self.test_name_list):
+                if test not in exclude_list:
+                    del self.test_name_list[i]
+                    return None, test
+
+    def stopUnitTest(self, *args):
+        pass
+
+    reportTaskFailure = reportTaskStatus = stopUnitTest
+
+    def isTaskAlive(self, *args):
+        return int(bool(self.test_name_list))
+
diff --git a/erp5/util/testbrowser/browser.py b/erp5/util/testbrowser/browser.py
index 346cac51733dcd75722ad18f92790dcc35d37f04..150cbb3afe3dfb24b264dac86c1684e0f15ad985 100644
--- a/erp5/util/testbrowser/browser.py
+++ b/erp5/util/testbrowser/browser.py
@@ -777,7 +777,8 @@ class MainForm(Form):
     @todo: Use information sent back as headers rather than looking
            into the page content?
     """
-    if 'Logged In as' in self.browser.contents:
+    check_logged_in_xpath = '//div[@id="logged_in_as"]/*'
+    if self.etree.xpath(check_logged_in_xpath):
       self._logger.debug("Already logged in")
       # TODO: Perhaps zope.testbrowser should be patched instead?
       self.browser.timer.start_time = self.browser.timer.end_time = 0
@@ -797,7 +798,7 @@ class MainForm(Form):
       self.browser.open('login_form')
       login(self.browser.mainForm)
 
-    if 'Logged In as' not in self.browser.contents:
+    if not self.etree.xpath(check_logged_in_xpath):
       raise LoginError("%s: Could not log in as '%s:%s'" % \
                          (self.browser._erp5_base_url,
                           self.browser._username,
diff --git a/product/CMFActivity/ActivityTool.py b/product/CMFActivity/ActivityTool.py
index 121f0d4e9009465ae753ed20d7cc530d13a15d74..0878eb1ab552028dde87b64aab7054e15ed126c0 100644
--- a/product/CMFActivity/ActivityTool.py
+++ b/product/CMFActivity/ActivityTool.py
@@ -54,6 +54,7 @@ from zExceptions import ExceptionFormatter
 from BTrees.OIBTree import OIBTree
 from Zope2 import app
 from Products.ERP5Type.UnrestrictedMethod import PrivilegedUser
+from zope.site.hooks import setSite
 
 try:
   from Products import iHotfix
@@ -306,6 +307,8 @@ class Message(BaseMessage):
             self.setExecutionState(MESSAGE_NOT_EXECUTABLE, exc_info,
                                    context=activity_tool)
           else:
+            # Store site info
+            setSite(activity_tool.getParentValue())
             if activity_tool.activity_timing_log:
               result = activity_timing_method(method, self.args, self.kw)
             else:
diff --git a/product/ERP5/Document/SimulationMovement.py b/product/ERP5/Document/SimulationMovement.py
index 4f0a35a4ec306ccba128824ab762485f82512d1a..3154b5781274f26264173a1da304321c39a5684c 100644
--- a/product/ERP5/Document/SimulationMovement.py
+++ b/product/ERP5/Document/SimulationMovement.py
@@ -716,7 +716,7 @@ class SimulationMovement(PropertyRecordableMixin, Movement, ExplainableMixin):
     catalog_simulation_movement_list = portal_catalog(
       portal_type='Simulation Movement',
       causality_uid=[p.getUid() for p in remaining_path_set],
-      path='%s/%%' % self.getPath())
+      path='%s/%%' % self.getPath().replace('_', r'\_'))
 
     for movement in catalog_simulation_movement_list:
       path = movement.getCausalityValue()
diff --git a/product/ERP5/ExplanationCache.py b/product/ERP5/ExplanationCache.py
index 2521fd867841697a94f0715858098275d6c80b2b..9df8bdd54d3b5daf11d241d9de81415ec1922851 100644
--- a/product/ERP5/ExplanationCache.py
+++ b/product/ERP5/ExplanationCache.py
@@ -137,7 +137,7 @@ class ExplanationCache:
         if not isinstance(value, dict):
           # We have a real root
           result.append('%s/%s' % (prefix, key))
-          result.append('%s/%s/%%' % (prefix, key))
+          result.append(('%s/%s/%%' % (prefix, key)).replace('_', r'\_'))
           # XXX-JPS here we must add all parent movements XXX-JPS
         else:
           browsePathDict('%s/%s' % (prefix, key), value) # Recursing with string append is slow XXX-JPS
@@ -262,7 +262,7 @@ class ExplanationCache:
         if simulation_path.startswith(path):
           # Only keep a path pattern which matches current simulation movement
           path_set.add(path)
-          path_set.add("%s/%%" % path)
+          path_set.add("%s/%%" % path.replace('_', r'\_'))
 
     # Lookup in cache based on path_tuple
     path_tuple = tuple(path_set) # XXX-JPS is the order guaranteed here ?
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_getRelatedDocumentList.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_getRelatedDocumentList.xml
index 85ed6d6674394aec30013059287966a580f46831..26f1c7646112012aaaa06e88591fb9d4a9f3caea 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_getRelatedDocumentList.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_getRelatedDocumentList.xml
@@ -58,7 +58,7 @@ follow_up_related_document_list = portal_catalog(\n
            portal_type=portal_type,\n
            follow_up_uid=context.getUid(), **kw)\n
 \n
-kw[\'query\'] = Query(relative_url=\'%s/%%\' % context.getRelativeUrl())\n
+kw[\'query\'] = Query(relative_url=\'%s/%%\' % context.getRelativeUrl().replace(\'_\', r\'\\_\'))\n
 if follow_up_related_document_list:\n
   kw[\'query\'] = ComplexQuery(\n
     kw[\'query\'],\n
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_reindexObjectSecurity.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_reindexObjectSecurity.xml
new file mode 100644
index 0000000000000000000000000000000000000000..8fdef14bd7ca8051028437b7c248c9761059d10e
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Base_reindexObjectSecurity.xml
@@ -0,0 +1,80 @@
+<?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># Do not re-index security recursively if contained objects don\'t acquire roles\n
+#\n
+# After all, recursively re-indexing a module in a production system\n
+# with lots of content could mean hours of non-usable overloaded system.\n
+type_tool = context.getPortalObject().portal_types \n
+for portal_type_name in context.getTypeInfo().getTypeAllowedContentTypeList():\n
+  portal_type = type_tool[portal_type_name]\n
+  if portal_type.getTypeAcquireLocalRole():\n
+    reindex = context.recursiveReindexObject\n
+    break\n
+else:\n
+  reindex = context.reindexObject\n
+\n
+return reindex(*args, **kw)\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>_params</string> </key>
+            <value> <string>*args, **kw</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>Base_reindexObjectSecurity</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/product/ERP5/bootstrap/erp5_core/bt/revision b/product/ERP5/bootstrap/erp5_core/bt/revision
index dcd2dffeb9dd155b31d877d2fe293985281bc9b3..e8f93305117031d99b25644f5d1313c5466729cb 100644
--- a/product/ERP5/bootstrap/erp5_core/bt/revision
+++ b/product/ERP5/bootstrap/erp5_core/bt/revision
@@ -1 +1 @@
-41040
\ No newline at end of file
+41042
\ No newline at end of file
diff --git a/product/ERP5/tests/testCRM.py b/product/ERP5/tests/testCRM.py
index bcf396ab47cceeb9dbf86469460f72db5d6e84d1..78cc5b2581bd4d9daf0a869e2e4c2b2d827713fd 100644
--- a/product/ERP5/tests/testCRM.py
+++ b/product/ERP5/tests/testCRM.py
@@ -1609,6 +1609,7 @@ class TestCRMMailSend(BaseTestCRM):
       self.assertEquals(event.getSourceSection(), user.getSubordination())
     finally:
       # clean up created roles on portal_types
+      self.login() # admin
       for portal_type in portal_type_list:
         portal_type_object = getattr(self.portal.portal_types, portal_type)
         portal_type_object._delObject('manager_role')
diff --git a/product/ERP5/tests/testDeliveryBuilderToSupportMultipleLines.py b/product/ERP5/tests/testDeliveryBuilderToSupportMultipleLines.py
index bc30d56227985704138f2296edf9170331349922..b33b201a05788f4704d375e36234e39540958b14 100644
--- a/product/ERP5/tests/testDeliveryBuilderToSupportMultipleLines.py
+++ b/product/ERP5/tests/testDeliveryBuilderToSupportMultipleLines.py
@@ -27,6 +27,7 @@
 ##############################################################################
 
 import unittest
+import transaction
 
 from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
 from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
@@ -34,6 +35,7 @@ from Products.ERP5Type.tests.Sequence import SequenceList
 from Products.ERP5Type.tests.utils import createZODBPythonScript
 from Products.ERP5.tests.testInvoice import TestSaleInvoiceMixin
 from Products.ERP5.tests.utils import newSimulationExpectedFailure
+from Products.ERP5Type.tests.backportUnittest import expectedFailure
 
 class TestNestedLineMixin(TestSaleInvoiceMixin):
 
@@ -323,7 +325,31 @@ class TestNestedLine(TestNestedLineMixin, ERP5TypeTestCase):
     self.assertEquals(self.new_invoice_quantity, document.getTotalQuantity())
     self.assertEquals(self.new_invoice_quantity, line_line.getQuantity())
 
+  def stepPrioritizeInvoiceUpdateCausalityStateTic(self, sequence):
+    invoice = sequence['invoice']
+    invoice_path = invoice.getPhysicalPath()
+    prioritize_uid_list = []
+    def stop_condition(message_list):
+      for message in message_list:
+        if (message.object_path == invoice_path and
+            message.method_id == 'updateCausalityState'):
+          prioritize_uid_list.append(message.uid)
+          return True
+        return False
+    self.tic(stop_condition=stop_condition)
+    update_causality_message_uid, = prioritize_uid_list
+    # make all other messages have less priority:
+    for table in 'message', 'message_queue':
+      self.portal.cmf_activity_sql_connection.manage_test("""
+        update %s
+          set priority=200
+        where uid <> %s
+      """ % (table, update_causality_message_uid))
+    transaction.commit()
+    self.stepTic(sequence)
+
   @newSimulationExpectedFailure
+  @expectedFailure
   def test_04_MergingMultipleSaleOrders(self, quiet=quiet):
     sequence_list = SequenceList()
     sequence = sequence_list.addSequenceString(self.DEFAULT_SEQUENCE + \
@@ -355,9 +381,10 @@ class TestNestedLine(TestNestedLineMixin, ERP5TypeTestCase):
 
       stepStartPackingList
       stepCheckInvoicingRule
-      stepTic
+      stepPrioritizeInvoiceUpdateCausalityStateTic
 
       stepCheckInvoiceIsDivergent
+      stepCheckInvoiceIsDiverged
       stepAdoptPrevisionInvoiceQuantity
       stepTic
     """
diff --git a/product/ERP5/tests/testERP5WebWithCRM.py b/product/ERP5/tests/testERP5WebWithCRM.py
index 2f989c66b229ee8aed8e80113b503d5674904f18..23bf750185d9fa68e43c4d77cf7f804f9c8bbbbb 100644
--- a/product/ERP5/tests/testERP5WebWithCRM.py
+++ b/product/ERP5/tests/testERP5WebWithCRM.py
@@ -28,6 +28,7 @@
 ##############################################################################
 
 import unittest
+import transaction
 
 from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase,\
      _getConversionServerDict
@@ -146,7 +147,7 @@ class TestERP5WebWithCRM(ERP5TypeTestCase):
                       form_kw['source_organisation_title'])
 
   def test_02_Contact_Us_with_Aunthenticated_user(self):
-    """Test creation of Web Message with Authenticted User
+    """Test creation of Web Message with Authenticated User
     """
     web_section = self.setupWebSection()
     self.logout()
@@ -159,6 +160,34 @@ class TestERP5WebWithCRM(ERP5TypeTestCase):
                'text_content': 'I want ERP5 for my company',
               }
     web_section.WebSection_addWebMessage(**form_kw)
+    transaction.commit()
+    # here we check a random bug caused by the ordering of activities
+    should_stop = [None]
+    event_module_path_prefix = self.portal.event_module.getPath() + '/'
+    deprioritize_message_list = []
+    # we'll stop whenever we find the message that reindex the newly created
+    # event object
+    def stop_condition(message_list):
+      for message in message_list:
+        object_path = '/'.join(message.object_path)
+        if (message.method_id == 'immediateReindexObject' and
+            object_path.startswith(event_module_path_prefix)):
+          deprioritize_message_list.append(message)
+          return True
+      return False
+    self.tic(stop_condition=stop_condition)
+    web_message_reindex_message, = deprioritize_message_list
+    web_message_path = web_message_reindex_message.object_path
+    self.assertTrue(
+      self.portal.unrestrictedTraverse(web_message_path).getPortalType(),
+      'Web Message',
+    )
+    # we'll deprioritize this message, so it executes last of all
+    self.portal.cmf_activity_sql_connection.manage_test("""
+      update message set priority=100
+      where uid=%s
+    """ % web_message_reindex_message.uid)
+    transaction.commit()
     self.tic()
     self.logout()
 
diff --git a/product/ERP5/tests/testOrder.py b/product/ERP5/tests/testOrder.py
index e52526919cfcf3dc818995d3c85facd7345d1f63..eb8c093973784fda2a4d4b62f27890bc74e87f2c 100644
--- a/product/ERP5/tests/testOrder.py
+++ b/product/ERP5/tests/testOrder.py
@@ -51,7 +51,8 @@ class TestOrderMixin(SubcontentReindexingWrapper):
   order_cell_portal_type = 'Sale Order Cell'
   applied_rule_portal_type = 'Applied Rule'
   simulation_movement_portal_type = 'Simulation Movement'
-  datetime = DateTime()
+  # see comment about self.datetime on afterSetUp() below
+  datetime = DateTime() - 2
   packing_list_portal_type = 'Sale Packing List'
   packing_list_line_portal_type = 'Sale Packing List Line'
   packing_list_cell_portal_type = 'Sale Packing List Cell'
@@ -94,15 +95,28 @@ class TestOrderMixin(SubcontentReindexingWrapper):
       preference.enable()
     self.tic()
 
-  def afterSetUp(self, quiet=1, run=1):
+  def afterSetUp(self):
+    # XXX-Leo: cannot call super here, because other classes call
+    # SuperClass.afterSetUp(self) directly... this needs to be cleaned
+    # up, including consolidating all conflicting definitions of
+    # .createCategories()
+    #super(TestOrderMixin, self).afterSetUp()
     self.login()
-    portal = self.getPortal()
     self.category_tool = self.getCategoryTool()
     portal_catalog = self.getCatalogTool()
     #portal_catalog.manage_catalogClear()
     self.createCategories()
     self.validateRules()
     self.setUpPreferences()
+    # pin datetime on the day before yesterday, to make sure that:
+    #
+    # 1. All calculations are done relative to the same time
+    # 2. We don't get random failures when tests run close to midnight
+    self.pinDateTime(self.datetime)
+
+  def beforeTearDown(self):
+    self.unpinDateTime()
+    super(TestOrderMixin, self).beforeTearDown()
 
   def createCurrency(self):
     currency_module = self.getPortal().currency_module
diff --git a/product/ERP5Security/tests/testERP5Security.py b/product/ERP5Security/tests/testERP5Security.py
index a64a09335dae7ac512e820d2df94aed10fb121ab..1b628dbaed063ba40ae26a0a2fb939de0b915fbb 100644
--- a/product/ERP5Security/tests/testERP5Security.py
+++ b/product/ERP5Security/tests/testERP5Security.py
@@ -32,6 +32,8 @@
 
 import unittest
 
+import transaction
+
 from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
 from Products.ERP5Type.tests.utils import createZODBPythonScript
 from AccessControl.SecurityManagement import newSecurityManager
@@ -476,16 +478,21 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
           ),)
         """)
     # configure group, site, function categories
+    category_tool = self.getCategoryTool()
     for bc in ['group', 'site', 'function']:
-      base_cat = self.getCategoryTool()[bc]
+      base_cat = category_tool[bc]
       code = bc[0].upper()
+      if base_cat.get('subcat', None) is not None:
+        continue
       base_cat.newContent(portal_type='Category',
                           id='subcat',
                           codification="%s1" % code)
     # add another function subcategory.
-    self.getCategoryTool()['function'].newContent(portal_type='Category',
-                                                  id='another_subcat',
-                                                  codification='F2')
+    function_category = category_tool['function']
+    if function_category.get('another_subcat', None) is None:
+      function_category.newContent(portal_type='Category',
+                                   id='another_subcat',
+                                   codification='F2')
     self.defined_category = "group/subcat\n"\
                             "site/subcat\n"\
                             "function/subcat"
@@ -503,11 +510,13 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
                                   site='subcat',
                                   function='subcat' )
     assignment.open()
+    self.person = pers
     self.tic()
 
   def beforeTearDown(self):
     """Called before teardown."""
     # clear base categories
+    self.person.getParentValue().manage_delObjects([self.person.getId()])
     for bc in ['group', 'site', 'function']:
       base_cat = self.getCategoryTool()[bc]
       base_cat.manage_delObjects(list(base_cat.objectIds()))
@@ -921,6 +930,39 @@ class TestLocalRoleManagement(ERP5TypeTestCase):
         (((cloning_owner_id), ('Owner',)),)
     )
 
+  def _checkMessageMethodIdList(self, expected_method_id_list):
+    actual_method_id_list = sorted([
+        message.method_id
+        for message in self.portal.portal_activities.getMessageList()
+    ])
+    self.assertEqual(expected_method_id_list, actual_method_id_list)
+
+  def test_reindexObjectSecurity_on_modules(self):
+    person_module = self.portal.person_module
+    portal_activities = self.portal.portal_activities
+    check = self._checkMessageMethodIdList
+
+    check([])
+    # We need at least one person for this test.
+    self.assertTrue(len(person_module.keys()))
+    # When we update security of a module...
+    person_module.reindexObjectSecurity()
+    transaction.commit()
+    # we don't want all underlying objects to be recursively
+    # reindexed. After all, its contents do not acquire local roles.
+    check(['immediateReindexObject'])
+    self.tic()
+    check([])
+    # But non-module objects, with subobjects that acquire local
+    # roles, should reindex their security recursively:
+    person, = [rec.getObject()
+               for rec in person_module.searchFolder(reference=self.username)]
+    self.assertTrue(len(person.objectIds()))
+    person.reindexObjectSecurity()
+    transaction.commit()
+    check(['recursiveImmediateReindexObject'])
+    self.tic()
+
 def test_suite():
   suite = unittest.TestSuite()
   suite.addTest(unittest.makeSuite(TestUserManagement))
diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py
index d49f39a1d05ca6add2046be098667c31ffdbd68b..416d21c59967a4690f8722e37d829eac5370f12a 100644
--- a/product/ERP5Type/Base.py
+++ b/product/ERP5Type/Base.py
@@ -86,6 +86,7 @@ from Products.ERP5Type.Accessor.TypeDefinition import asDate
 from Products.ERP5Type.Message import Message
 from Products.ERP5Type.ConsistencyMessage import ConsistencyMessage
 from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
+from Products.ERP5Type.dynamic.import_lock import ImportLock
 
 from zope.interface import classImplementsOnly, implementedBy
 
@@ -710,7 +711,7 @@ class Base( CopyContainer,
   isTempDocument = ConstantGetter('isTempDocument', value=False)
 
   # Dynamic method acquisition system (code generation)
-  aq_method_lock = threading.RLock()
+  aq_method_lock = ImportLock()
   aq_method_generated = set()
   aq_method_generating = []
   aq_portal_type = {}
diff --git a/product/ERP5Type/Core/Folder.py b/product/ERP5Type/Core/Folder.py
index 33c4a612ca68c5aa0f76396d699c94b32cbf8717..9551be36f4071fe9ec7c2aa616dae97c760ba572 100644
--- a/product/ERP5Type/Core/Folder.py
+++ b/product/ERP5Type/Core/Folder.py
@@ -1253,10 +1253,11 @@ class Folder(CopyContainer, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
   def reindexObjectSecurity(self, *args, **kw):
     """
         Reindex security-related indexes on the object
-        (and its descendants).
     """
-    # In ERP5, simply reindex all objects.
-    self.recursiveReindexObject(*args, **kw)
+    # In ERP5, simply reindex all objects, recursively by default.
+    reindex = self._getTypeBasedMethod('reindexObjectSecurity',
+                                       'recursiveReindexObject')
+    reindex(*args, **kw)
 
   security.declarePublic( 'recursiveReindexObject' )
   def recursiveReindexObject(self, activate_kw=None, **kw):
diff --git a/product/ERP5Type/ZopePatch.py b/product/ERP5Type/ZopePatch.py
index 6f6ef3bb68449d87f115b6bdcc2368789a02415b..10dab8686898c9aaf56c08e79463f5fb19881f25 100644
--- a/product/ERP5Type/ZopePatch.py
+++ b/product/ERP5Type/ZopePatch.py
@@ -71,6 +71,7 @@ from Products.ERP5Type.patches import ExternalMethod
 from Products.ERP5Type.patches import User
 from Products.ERP5Type.patches import zopecontenttype
 from Products.ERP5Type.patches import OFSImage
+from Products.ERP5Type.patches import default_zpublisher_encoding
 
 # These symbols are required for backward compatibility
 from Products.ERP5Type.patches.PropertyManager import ERP5PropertyManager
diff --git a/product/ERP5Type/dynamic/dynamic_module.py b/product/ERP5Type/dynamic/dynamic_module.py
index f5600aa597aa7cb27a102db271dd8d2d865a80f3..7998a704fb52cdc2b1b728dd398b289d8fae41ad 100644
--- a/product/ERP5Type/dynamic/dynamic_module.py
+++ b/product/ERP5Type/dynamic/dynamic_module.py
@@ -29,7 +29,7 @@
 
 from types import ModuleType
 import sys
-import threading
+from Products.ERP5Type.dynamic.import_lock import ImportLock
 
 class DynamicModule(ModuleType):
   """This module may generate new objects at runtime."""
@@ -41,7 +41,7 @@ class DynamicModule(ModuleType):
   def __init__(self, name, factory, doc=None):
     super(DynamicModule, self).__init__(name, doc=doc)
     self._factory = factory
-    self._lock = threading.RLock()
+    self._lock = ImportLock()
 
   def __getattr__(self, name):
     if name[:2] == '__':
diff --git a/product/ERP5Type/dynamic/import_lock.py b/product/ERP5Type/dynamic/import_lock.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e48479506e2603fedf002386e98d03bef1b5340
--- /dev/null
+++ b/product/ERP5Type/dynamic/import_lock.py
@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly advised to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import imp
+class ImportLock(object):
+  """
+  This class provides the interpreter's import lock.
+  It is intended to use in ERP5Type.dynamic to avoid possible dead lock.
+
+  It can be used in two ways :
+
+  1) 'with' statement
+
+  lock = ImportLock()
+  with lock:
+    ...
+
+  2) traditional 'try' and 'finally'
+
+  lock = ImportLock()
+  lock.acquire()
+  try:
+    ...
+  finally:
+    lock.release()
+
+  """
+  def __enter__(self):
+    imp.acquire_lock()
+
+  def __exit__(self, type, value, tb):
+    imp.release_lock()
+
+  def acquire(self):
+    imp.acquire_lock()
+
+  def release(self):
+    imp.release_lock()
diff --git a/product/ERP5Type/patches/default_zpublisher_encoding.py b/product/ERP5Type/patches/default_zpublisher_encoding.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c9c3a11a9d9323ac57874699a0e5361243ccd54
--- /dev/null
+++ b/product/ERP5Type/patches/default_zpublisher_encoding.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2010 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import ZPublisher.HTTPRequest
+
+# Force (do not depend on) the default-zpublisher-encoding setting of zope.conf
+ZPublisher.HTTPRequest.default_encoding = 'utf-8'
diff --git a/product/ERP5Type/tests/ERP5TypeTestCase.py b/product/ERP5Type/tests/ERP5TypeTestCase.py
index cafbfef7dc27c84832876c46ea8fa0ee24a2f457..8a391c7096a04cba17aa904f9d5a79713222e15a 100644
--- a/product/ERP5Type/tests/ERP5TypeTestCase.py
+++ b/product/ERP5Type/tests/ERP5TypeTestCase.py
@@ -26,6 +26,7 @@ from hashlib import md5
 from warnings import warn
 from ExtensionClass import pmc_init_of
 from ZTUtils import make_query
+from DateTime import DateTime
 
 # XXX make sure that get_request works.
 import Products.ERP5Type.Utils
@@ -275,6 +276,21 @@ def profile_if_environ(environment_var_name):
       # No profiling, return identity decorator
       return lambda self, method: method
 
+# Patch DateTime to allow pinning the notion of "now".
+assert getattr(DateTime, '_original_parse_args', None) is None
+DateTime._original_parse_args = DateTime._parse_args
+
+_pinned_date_time = None
+
+def _parse_args(self, *args, **kw):
+  if _pinned_date_time is not None and (not args or args[0] == None):
+    # simulate fixed "Now"
+    args = (_pinned_date_time,) + args[1:]
+  return self._original_parse_args(*args, **kw)
+
+_parse_args._original = DateTime._original_parse_args
+DateTime._parse_args = _parse_args
+
 class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
     """Mixin class for ERP5 based tests.
     """
@@ -354,6 +370,17 @@ class ERP5TypeTestCaseMixin(ProcessingNodeTestCase, PortalTestCase):
       cls.__bases__ = cls.__bases__[1:]
       pmc_init_of(cls)
 
+    def pinDateTime(self, date_time):
+      # pretend time has stopped at a certain date (i.e. the test runs
+      # infinitely fast), to avoid errors on tests that are started
+      # just before midnight.
+      global _pinned_date_time
+      assert date_time is None or isinstance(date_time, DateTime)
+      _pinned_date_time = date_time
+
+    def unpinDateTime(self):
+      self.pinDateTime(None)
+
     def getDefaultSitePreferenceId(self):
       """Default id, usefull method to override
       """
diff --git a/product/ERP5Type/tests/testLocalizer.py b/product/ERP5Type/tests/testLocalizer.py
new file mode 100644
index 0000000000000000000000000000000000000000..ada7eef091d0a061873d436977f175b1c433a91b
--- /dev/null
+++ b/product/ERP5Type/tests/testLocalizer.py
@@ -0,0 +1,133 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2012 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+import unittest
+
+from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
+from Products.ERP5Type.tests.utils import createZODBPythonScript
+from Persistence import PersistentMapping
+from zope.site.hooks import setSite
+
+class TestLocalizer(ERP5TypeTestCase):
+  def afterSetUp(self):
+    self.message_catalog = self.portal.Localizer.erp5_ui
+    if 'fr' not in self.message_catalog.get_available_languages():
+      self.message_catalog.add_language('fr')
+    self.message_catalog._messages.clear()
+
+  def beforeTearDown(self):
+    tmp_obj = getattr(self, 'tmp_obj', None)
+    if tmp_obj is not None:
+      tmp_obj.aq_parent.manage_delObjects(ids=[tmp_obj.getId(),])
+      self.tic()
+
+  def test_non_ascii_msgid(self):
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "This is 1€.")
+    # newly created key should be unicode.
+    self.assertFalse('This is 1€.' in self.message_catalog._messages)
+    self.assertTrue(u'This is 1€.' in self.message_catalog._messages)
+    self.message_catalog.message_edit(u'This is 1€.', 'fr', u"C'est 1€.", '')
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "C'est 1€.")
+    self.assertFalse('This is 1€.' in self.message_catalog._messages)
+    self.assertTrue(u'This is 1€.' in self.message_catalog._messages)
+
+  def test_migrated_non_ascii_msgid(self):
+    # register str key to simulate existing message that was already
+    # created by old Localizer.
+    self.message_catalog._messages['This is 1€.'] = PersistentMapping(
+      {'fr':"C'est 1€.", 'note':'',})
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "C'est 1€.")
+    # translate() should not create a unicode key if str key already exists.
+    self.assertTrue('This is 1€.' in self.message_catalog._messages)
+    self.assertFalse(u'This is 1€.' in self.message_catalog._messages)
+    self.message_catalog.message_edit(u'This is 1€.', 'fr', u"Ceci est 1€.", '')
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "Ceci est 1€.")
+    # message_edit() should not create a unicode key if str key already exists.
+    self.assertTrue('This is 1€.' in self.message_catalog._messages)
+    self.assertFalse(u'This is 1€.' in self.message_catalog._messages)
+
+  def test_non_ascii_mapping(self):
+    self.assertEqual(self.portal.Base_translateString('This is 1${currency}.', lang='fr',
+                                                      mapping={'currency':'€'}),
+                     "This is 1€.")
+    self.message_catalog.message_edit(u'This is 1${currency}.', 'fr', u"C'est 1${currency}.", '')
+    self.assertEqual(self.portal.Base_translateString('This is 1${currency}.', lang='fr',
+                                                      mapping={'currency':'€'}),
+                     "C'est 1€.")
+
+  def test_non_literal_mapping(self):
+    self.assertEqual(self.portal.Base_translateString('This is ${obj}.', lang='fr',
+                                                      mapping={'obj':[1,2]}),
+                     "This is [1, 2].")
+    self.message_catalog.message_edit(u'This is ${obj}.', 'fr', u"C'est ${obj}.", '')
+    self.assertEqual(self.portal.Base_translateString('This is ${obj}.', lang='fr',
+                                                      mapping={'obj':[1,2]}),
+                     "C'est [1, 2].")
+
+  def test_import_migrated_non_ascii_msgid(self):
+    # register str key to simulate existing message that was already
+    # created by old Localizer.
+    self.message_catalog._messages['This is 1€.'] = PersistentMapping(
+      {'fr':"C'est 1€.", 'note':'',})
+    self.message_catalog.po_import(
+      'fr',
+      'msgid "This is 1€."\nmsgstr "Ceci est 1€."')
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "Ceci est 1€.")
+    # po_import() converts existing str key to unicode key.
+    self.assertFalse('This is 1€.' in self.message_catalog._messages)
+    self.assertTrue(u'This is 1€.' in self.message_catalog._messages)
+
+  def test_localizer_transle_in_activity(self):
+    self.assertEqual(self.portal.Base_translateString('This is 1€.', lang='fr'),
+                     "This is 1€.")
+    self.message_catalog.message_edit(u'This is 1€.', 'fr', u"C'est 1€.", '')
+    skin = self.portal.portal_skins.custom
+    createZODBPythonScript(
+      skin, 'test_activity', '',
+      "context.setComment(context.Base_translateString('This is 1€.', lang='fr'))",
+      )
+    tmp_obj = self.portal.portal_templates.newContent()
+    self.tic()
+    tmp_obj.activate().test_activity()
+    # here we don't call self.tic() that calls self.getPortal() that
+    # reinvoke setSite(portal).
+    setSite()
+    self.commit()
+    while self.portal.portal_activities.getMessageList():
+      self.portal.portal_activities.process_timer(None, None)
+    self.assertEquals(tmp_obj.getComment(), "C'est 1€.")
+
+def test_suite():
+  suite = unittest.TestSuite()
+  suite.addTest(unittest.makeSuite(TestLocalizer))
+  return suite
diff --git a/product/Localizer/MessageCatalog.py b/product/Localizer/MessageCatalog.py
index 173fc070406d7ad60b1fcc16c142c16b1583b219..703ca8c10a4955e40ed872672b141218d4108648 100644
--- a/product/Localizer/MessageCatalog.py
+++ b/product/Localizer/MessageCatalog.py
@@ -63,13 +63,13 @@ def md5text(str):
     return md5(str.encode('utf-8')).hexdigest()
 
 
-def to_unicode(x, encoding=HTTPRequest.default_encoding):
+def to_unicode(x, encoding=None):
     """In Zope the ISO-8859-1 encoding has an special status, normal strings
     are considered to be in this encoding by default.
     """
-    if isinstance(x, unicode):
-        return x
-    return unicode(x, encoding)
+    if isinstance(x, str):
+        return unicode(x, encoding or HTTPRequest.default_encoding)
+    return unicode(x)
 
 
 def to_str(x):
@@ -193,6 +193,11 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
                   target_language=None, default=None):
         """ """
         msgstr = self.gettext(msgid, lang=target_language, default=default)
+        # BBB support str in mapping by converting to unicode for
+        # backward compatibility.
+        if mapping:
+            mapping = dict([to_unicode(k), to_unicode(v)]
+                            for k, v in mapping.iteritems())
         return interpolate(msgstr, mapping)
 
 
@@ -262,7 +267,7 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
         If a default is provided, use it instead of the message id
         as a translation for unknown messages.
         """
-        if not isinstance(message, (str, unicode)):
+        if not isinstance(message, basestring):
             raise TypeError, 'only strings can be translated.'
 
         message = message.strip()
@@ -633,9 +638,15 @@ class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
             msgid = to_unicode(entry.msgid, encoding=encoding)
             if msgid:
                 msgstr = to_unicode(entry.msgstr or '', encoding=encoding)
-                if not messages.has_key(msgid):
-                    messages[msgid] = PersistentMapping()
-                messages[msgid][lang] = msgstr
+                translation_map = messages.get(msgid)
+                if translation_map is None:
+                    # convert old non-unicode translations if they exist:
+                    translation_map = messages.pop(self.get_message_key(msgid),
+                                                   None)
+                    if translation_map is None:
+                        translation_map = PersistentMapping()
+                    messages[msgid] = translation_map
+                translation_map[lang] = msgstr
 
         # Set the encoding (the full header should be loaded XXX)
         self.update_po_header(lang, charset=encoding)
diff --git a/setup.py b/setup.py
index 6ad77d89a7f49c05e12dc10c866db2f24b3caf36..8fa33b8f925e2d1fef38875194720508f799d40a 100644
--- a/setup.py
+++ b/setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
 import glob
 import os
 
-version = '0.4.3'
+version = '0.4.5'
 name = 'erp5.util'
 long_description = open("README.erp5.util.txt").read() + "\n"