From decf8d395048c2ec819f028e3fb2e41deb11aadc Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Fri, 21 Aug 2020 12:47:57 +0900
Subject: [PATCH] ZODB Components: Migrate ContributionTool from filesystem.

---
 product/ERP5/__init__.py                      |   3 +-
 .../document.erp5.Document.py                 |   2 +-
 .../tool.erp5.ContributionTool.py}            |  66 +----------
 .../tool.erp5.ContributionTool.xml            | 110 ++++++++++++++++++
 .../bt/template_tool_component_id_list        |   1 +
 product/ERP5Type/ZopePatch.py                 |   1 +
 product/ERP5Type/patches/urllib_opener.py     |  89 ++++++++++++++
 7 files changed, 207 insertions(+), 65 deletions(-)
 rename product/ERP5/{Tool/ContributionTool.py => bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.py} (91%)
 create mode 100644 product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.xml
 create mode 100644 product/ERP5Type/patches/urllib_opener.py

diff --git a/product/ERP5/__init__.py b/product/ERP5/__init__.py
index 75f9ba7bc2..9d38d4288a 100644
--- a/product/ERP5/__init__.py
+++ b/product/ERP5/__init__.py
@@ -43,7 +43,7 @@ product_path = package_home( globals() )
 # Define object classes and tools
 from Tool import CategoryTool, IdTool, TemplateTool,\
                  AlarmTool,\
-                 TrashTool, ContributionTool,\
+                 TrashTool,\
                  SolverTool
 import ERP5Site
 from Document import PythonScript, SQLMethod
@@ -56,7 +56,6 @@ portal_tools = ( CategoryTool.CategoryTool,
                  TemplateTool.TemplateTool,
                  AlarmTool.AlarmTool,
                  TrashTool.TrashTool,
-                 ContributionTool.ContributionTool,
                  SolverTool.SolverTool,
                 )
 content_classes = ()
diff --git a/product/ERP5/bootstrap/erp5_core/DocumentTemplateItem/portal_components/document.erp5.Document.py b/product/ERP5/bootstrap/erp5_core/DocumentTemplateItem/portal_components/document.erp5.Document.py
index 8ed2ae6fb0..317074e2e7 100644
--- a/product/ERP5/bootstrap/erp5_core/DocumentTemplateItem/portal_components/document.erp5.Document.py
+++ b/product/ERP5/bootstrap/erp5_core/DocumentTemplateItem/portal_components/document.erp5.Document.py
@@ -37,7 +37,7 @@ from Products.ERP5Type import Permissions, PropertySheet
 from Products.ERP5Type.XMLObject import XMLObject
 from Products.ERP5Type.Utils import deprecated, guessEncodingFromText
 from Products.ERP5Type.TransactionalVariable import getTransactionalVariable
-from Products.ERP5.Tool.ContributionTool import MAX_REPEAT
+from erp5.component.tool.ContributionTool import MAX_REPEAT
 from Products.ZSQLCatalog.SQLCatalog import Query, NegatedQuery
 from AccessControl import Unauthorized
 import zope.interface
diff --git a/product/ERP5/Tool/ContributionTool.py b/product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.py
similarity index 91%
rename from product/ERP5/Tool/ContributionTool.py
rename to product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.py
index cdbdfdd282..65951067af 100644
--- a/product/ERP5/Tool/ContributionTool.py
+++ b/product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.py
@@ -29,7 +29,6 @@
 
 import cStringIO
 import re
-import socket
 import urllib2, urllib
 import urlparse
 from cgi import parse_header
@@ -48,67 +47,9 @@ from AccessControl import Unauthorized
 from DateTime import DateTime
 import warnings
 
-# Install openers
-import dircache
-import mimetypes, mimetools
-from email.utils import formatdate
-class DirectoryFileHandler(urllib2.FileHandler):
-    """
-    Extends the file handler to provide an HTML
-    representation of local directories.
-    """
-
-    # Use local file or FTP depending on form of URL
-    def file_open(self, req):
-        url = req.get_selector()
-        if url[:2] == '//' and url[2:3] != '/':
-            req.type = 'ftp'
-            return self.parent.open(req)
-        else:
-            return self.open_local_file(req)
-
-    # not entirely sure what the rules are here
-    def open_local_file(self, req):
-        host = req.get_host()
-        file = req.get_selector()
-        localfile = urllib2.url2pathname(file)
-        stats = os.stat(localfile)
-        size = stats.st_size
-        modified = formatdate(stats.st_mtime, usegmt=True)
-        mtype = mimetypes.guess_type(file)[0]
-        headers = mimetools.Message(cStringIO.StringIO(
-            'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
-            (mtype or 'text/plain', size, modified)))
-        if host:
-            host, port = urllib.splitport(host)
-        if not host or \
-           (not port and socket.gethostbyname(host) in self.get_names()):
-            try:
-              file_list = dircache.listdir(localfile)
-              s = cStringIO.StringIO()
-              s.write('<html><head><base href="%s"/></head><body>' % ('file:' + file))
-              s.write('<p>Directory Content:</p>')
-              for f in file_list:
-                s.write('<p><a href="%s">%s</a></p>\n' % (urllib.quote(f), f))
-              s.write('</body></html>')
-              s.seek(0)
-              headers = mimetools.Message(cStringIO.StringIO(
-                  'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
-                  ('text/html', size, modified)))
-              return urllib2.addinfourl(s, headers, 'file:' + file)
-            except OSError:
-              return urllib2.addinfourl(open(localfile, 'rb'),
-                                        headers, 'file:'+file)
-        raise urllib2.URLError('file not on local host')
-opener = urllib2.build_opener(DirectoryFileHandler)
-urllib2.install_opener(opener)
-
 # Global parameters
-TEMP_NEW_OBJECT_KEY = '_v_new_object'
 MAX_REPEAT = 10
 
-_marker = []  # Create a new marker object.
-
 class ContributionTool(BaseTool):
   """
     ContributionTool provides an abstraction layer to unify the contribution
@@ -254,7 +195,7 @@ class ContributionTool(BaseTool):
         filename=filename, content_type=content_type)
       if not (container is None or container.isModuleType() or
               container.getTypeInfo().allowType(portal_type)):
-          portal_type = 'Embedded File'
+        portal_type = 'Embedded File'
 
     if container is None:
       # If the portal_type was provided, we can go faster
@@ -410,6 +351,7 @@ class ContributionTool(BaseTool):
     return self.getPropertyDictFromFilename(filename)
 
   # WebDAV virtual folder support
+  # pylint: disable=arguments-differ,redefined-builtin
   def _setObject(self, id, ob, portal_type=None, user_login=None,
                  container=None, discover_metadata=True, filename=None,
                  input_parameter_dict=None):
@@ -607,7 +549,7 @@ class ContributionTool(BaseTool):
       try:
         url = content.asURL()
         file_object, filename, content_type = self._openURL(url)
-      except urllib2.URLError, error:
+      except urllib2.URLError:
         if repeat == 0 or not batch_mode:
           # XXX - Call the extendBadURLList method,--NOT Implemented--
           raise
@@ -656,7 +598,7 @@ class ContributionTool(BaseTool):
       elif document.getCrawlingDepth() > 0:
         # If this is an index document, stop crawling if crawling_depth is 0
         document.activate().crawlContent()
-    except urllib2.HTTPError, error:
+    except urllib2.HTTPError:
       if repeat == 0 or not batch_mode:
         # here we must call the extendBadURLList method,--NOT Implemented--
         # which had to add this url to bad URL list, so next time we avoid
diff --git a/product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.xml b/product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.xml
new file mode 100644
index 0000000000..41a8942035
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_core/ToolComponentTemplateItem/portal_components/tool.erp5.ContributionTool.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Tool Component" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>default_reference</string> </key>
+            <value> <string>ContributionTool</string> </value>
+        </item>
+        <item>
+            <key> <string>default_source_reference</string> </key>
+            <value> <string>Products.ERP5.Tool.ContributionTool</string> </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>tool.erp5.ContributionTool</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Tool Component</string> </value>
+        </item>
+        <item>
+            <key> <string>sid</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>text_content_error_message</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>text_content_warning_message</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>version</string> </key>
+            <value> <string>erp5</string> </value>
+        </item>
+        <item>
+            <key> <string>workflow_history</string> </key>
+            <value>
+              <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="PersistentMapping" module="Persistence.mapping"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>data</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>component_validation_workflow</string> </key>
+                    <value>
+                      <persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
+                    </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="3" aka="AAAAAAAAAAM=">
+    <pickle>
+      <global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_log</string> </key>
+            <value>
+              <list>
+                <dictionary>
+                  <item>
+                      <key> <string>action</string> </key>
+                      <value> <string>validate</string> </value>
+                  </item>
+                  <item>
+                      <key> <string>validation_state</string> </key>
+                      <value> <string>validated</string> </value>
+                  </item>
+                </dictionary>
+              </list>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/product/ERP5/bootstrap/erp5_core/bt/template_tool_component_id_list b/product/ERP5/bootstrap/erp5_core/bt/template_tool_component_id_list
index 07e43f184d..a0b0e927ef 100644
--- a/product/ERP5/bootstrap/erp5_core/bt/template_tool_component_id_list
+++ b/product/ERP5/bootstrap/erp5_core/bt/template_tool_component_id_list
@@ -2,6 +2,7 @@ tool.erp5.AcknowledgementTool
 tool.erp5.BuilderTool
 tool.erp5.CallableTool
 tool.erp5.ContributionRegistryTool
+tool.erp5.ContributionTool
 tool.erp5.DeliveryTool
 tool.erp5.DiffTool
 tool.erp5.DomainTool
diff --git a/product/ERP5Type/ZopePatch.py b/product/ERP5Type/ZopePatch.py
index 86f9e9ba4c..d1a1061cb8 100644
--- a/product/ERP5Type/ZopePatch.py
+++ b/product/ERP5Type/ZopePatch.py
@@ -91,6 +91,7 @@ from Products.ERP5Type.patches import MimetypesRegistry
 from Products.ERP5Type.patches import users
 from Products.ERP5Type.patches import Publish
 from Products.ERP5Type.patches import WSGITask
+from Products.ERP5Type.patches import urllib_opener
 
 # These symbols are required for backward compatibility
 from Products.ERP5Type.patches.PropertyManager import ERP5PropertyManager
diff --git a/product/ERP5Type/patches/urllib_opener.py b/product/ERP5Type/patches/urllib_opener.py
new file mode 100644
index 0000000000..24ebc02afc
--- /dev/null
+++ b/product/ERP5Type/patches/urllib_opener.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2007 Nexedi SARL and Contributors. All Rights Reserved.
+#                    Jean-Paul Smets <jp@nexedi.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.
+#
+##############################################################################
+
+# Install openers
+# -> testTemplateTool.TestTemplateTool.test_getBusinessTemplateUrl
+import urllib
+import urllib2
+import cStringIO
+import socket
+import os
+import dircache
+import mimetypes, mimetools
+from email.utils import formatdate
+class DirectoryFileHandler(urllib2.FileHandler):
+    """
+    Extends the file handler to provide an HTML
+    representation of local directories.
+    """
+
+    # Use local file or FTP depending on form of URL
+    def file_open(self, req):
+        url = req.get_selector()
+        if url[:2] == '//' and url[2:3] != '/':
+            req.type = 'ftp'
+            return self.parent.open(req)
+        else:
+            return self.open_local_file(req)
+
+    # not entirely sure what the rules are here
+    def open_local_file(self, req):
+        host = req.get_host()
+        file = req.get_selector()
+        localfile = urllib2.url2pathname(file)
+        stats = os.stat(localfile)
+        size = stats.st_size
+        modified = formatdate(stats.st_mtime, usegmt=True)
+        mtype = mimetypes.guess_type(file)[0]
+        headers = mimetools.Message(cStringIO.StringIO(
+            'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
+            (mtype or 'text/plain', size, modified)))
+        if host:
+            host, port = urllib.splitport(host)
+        if not host or \
+           (not port and socket.gethostbyname(host) in self.get_names()):
+            try:
+              file_list = dircache.listdir(localfile)
+              s = cStringIO.StringIO()
+              s.write('<html><head><base href="%s"/></head><body>' % ('file:' + file))
+              s.write('<p>Directory Content:</p>')
+              for f in file_list:
+                s.write('<p><a href="%s">%s</a></p>\n' % (urllib.quote(f), f))
+              s.write('</body></html>')
+              s.seek(0)
+              headers = mimetools.Message(cStringIO.StringIO(
+                  'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' %
+                  ('text/html', size, modified)))
+              return urllib2.addinfourl(s, headers, 'file:' + file)
+            except OSError:
+              return urllib2.addinfourl(open(localfile, 'rb'),
+                                        headers, 'file:'+file)
+        raise urllib2.URLError('file not on local host')
+opener = urllib2.build_opener(DirectoryFileHandler)
+urllib2.install_opener(opener)
-- 
2.30.9