From 5c7d50d5a0ce0c2bc8024979de3365fdfc275095 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Tue, 9 Aug 2011 17:59:11 +0200
Subject: [PATCH] Fix webdav support when used to upload embedded files

Also cleanup and optimize PUT & ContributionTool

This reverts commit eaf12293bd10dd81d0a9bc120467207bc2b33ba1.
---
 .../interactions/Document_PUT.xml             | 94 -------------------
 .../Document_setFilenameAndContentType.xml    | 75 ---------------
 bt5/erp5_base/bt/revision                     |  2 +-
 product/ERP5/Tool/ContributionTool.py         | 57 ++++++-----
 product/ERP5Type/Core/Folder.py               |  5 +-
 product/ERP5Type/WebDAVSupport.py             | 78 ++++++++-------
 6 files changed, 76 insertions(+), 235 deletions(-)
 delete mode 100644 bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/interactions/Document_PUT.xml
 delete mode 100644 bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/scripts/Document_setFilenameAndContentType.xml

diff --git a/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/interactions/Document_PUT.xml b/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/interactions/Document_PUT.xml
deleted file mode 100644
index 697ae52b97..0000000000
--- a/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/interactions/Document_PUT.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="InteractionDefinition" module="Products.ERP5.Interaction"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>actbox_category</string> </key>
-            <value> <string>workflow</string> </value>
-        </item>
-        <item>
-            <key> <string>actbox_name</string> </key>
-            <value> <string></string> </value>
-        </item>
-        <item>
-            <key> <string>actbox_url</string> </key>
-            <value> <string></string> </value>
-        </item>
-        <item>
-            <key> <string>activate_script_name</string> </key>
-            <value>
-              <tuple/>
-            </value>
-        </item>
-        <item>
-            <key> <string>after_script_name</string> </key>
-            <value>
-              <list>
-                <string>Document_setFilenameAndContentType</string>
-              </list>
-            </value>
-        </item>
-        <item>
-            <key> <string>before_commit_script_name</string> </key>
-            <value>
-              <tuple/>
-            </value>
-        </item>
-        <item>
-            <key> <string>description</string> </key>
-            <value> <string></string> </value>
-        </item>
-        <item>
-            <key> <string>guard</string> </key>
-            <value>
-              <none/>
-            </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>Document_PUT</string> </value>
-        </item>
-        <item>
-            <key> <string>method_id</string> </key>
-            <value>
-              <list>
-                <string>PUT</string>
-              </list>
-            </value>
-        </item>
-        <item>
-            <key> <string>once_per_transaction</string> </key>
-            <value> <int>0</int> </value>
-        </item>
-        <item>
-            <key> <string>portal_type_filter</string> </key>
-            <value>
-              <none/>
-            </value>
-        </item>
-        <item>
-            <key> <string>script_name</string> </key>
-            <value>
-              <tuple/>
-            </value>
-        </item>
-        <item>
-            <key> <string>temporary_document_disallowed</string> </key>
-            <value> <int>0</int> </value>
-        </item>
-        <item>
-            <key> <string>title</string> </key>
-            <value> <string></string> </value>
-        </item>
-        <item>
-            <key> <string>trigger_type</string> </key>
-            <value> <int>2</int> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/scripts/Document_setFilenameAndContentType.xml b/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/scripts/Document_setFilenameAndContentType.xml
deleted file mode 100644
index 8013cb9348..0000000000
--- a/bt5/erp5_base/WorkflowTemplateItem/portal_workflow/document_conversion_interaction_workflow/scripts/Document_setFilenameAndContentType.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?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>document = state_change[\'object\']\n
-portal = document.getPortalObject()\n
-\n
-filename = document.getId()\n
-document.setFilename(filename)\n
-content_type = portal.mimetypes_registry.lookupExtension(filename)\n
-\n
-if content_type is not None:\n
-  document.setContentType(str(content_type))\n
-</string> </value>
-        </item>
-        <item>
-            <key> <string>_params</string> </key>
-            <value> <string>state_change</string> </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>Document_setFilenameAndContentType</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_base/bt/revision b/bt5/erp5_base/bt/revision
index 40b9b19206..b7e8805f4f 100644
--- a/bt5/erp5_base/bt/revision
+++ b/bt5/erp5_base/bt/revision
@@ -1 +1 @@
-990
\ No newline at end of file
+991
\ No newline at end of file
diff --git a/product/ERP5/Tool/ContributionTool.py b/product/ERP5/Tool/ContributionTool.py
index 464e243f2e..3279aaddfe 100644
--- a/product/ERP5/Tool/ContributionTool.py
+++ b/product/ERP5/Tool/ContributionTool.py
@@ -151,7 +151,6 @@ class ContributionTool(BaseTool):
         kw['filename'] = kw['file_name']
       del(kw['file_name'])
     filename = kw.get('filename', None)
-    portal_type = kw.get('portal_type')
     temp_object = kw.get('temp_object', False)
 
     document = None
@@ -161,42 +160,55 @@ class ContributionTool(BaseTool):
       # Container may disappear, be smoother by passing default value
       container = portal.restrictedTraverse(container_path, None)
     # Try to find the filename
-    content_type = None
     if not url:
       # check if file was provided
       file_object = kw.get('file')
       if file_object is not None:
         if not filename:
-          filename = file_object.filename
+          filename = getattr(file_object, 'filename', None)
       else:
         # some channels supply data and file-name separately
         # this is the case for example for email ingestion
         # in this case, we build a file wrapper for it
-        data = kw.get('data')
-        if data is not None and filename:
+        try:
+          data = kw.pop('data')
+        except KeyError:
+          raise ValueError('data must be provided')
+        if data is not None:
           file_object = cStringIO.StringIO()
           file_object.write(data)
           file_object.seek(0)
           kw['file'] = file_object
-          del kw['data']
-        else:
-          raise TypeError, 'data and filename must be provided'
+      content_type = kw.pop('content_type', None)
     else:
       file_object, filename, content_type = self._openURL(url)
-      if content_type:
-        kw['content_type'] = content_type
+      content_type = kw.pop('content_type', None) or content_type
       kw['file'] = file_object
 
+    if not filename:
+      raise ValueError('filename must be provided')
+
     if not content_type:
       # fallback to a default content_type according provided
       # filename
       content_type = self.guessMimeTypeFromFilename(filename)
+    if content_type:
+      kw['content_type'] = content_type
+
+    portal_type = kw.pop('portal_type', None)
+    if not portal_type:
+      if container is None or container.isModuleType():
+        # Guess it with help of portal_contribution_registry
+        portal_type = portal.portal_contribution_registry.findPortalTypeName(
+          filename=filename, content_type=content_type)
+      else:
+        portal_type = 'Embedded File'
 
-    if portal_type and container is not None:
+    if container is not None:
       # Simplify things here and return a document immediately
       # XXX Nicolas: This will break support of WebDAV
       # if _setObject is not called
-      document = container.newContent(id=document_id, **kw)
+      document = container.newContent(document_id, portal_type, **kw)
       if discover_metadata:
         document.activate(after_path_and_method_id=(document.getPath(),
             ('convertToBaseFormat', 'Document_tryToConvertToBaseFormat')))\
@@ -206,24 +218,14 @@ class ContributionTool(BaseTool):
       return document
 
     # If the portal_type was provided, we can go faster
-    if portal_type and container is None:
+    if portal_type:
       # We know the portal_type, let us find the default module
       # and use it as container
       try:
         container = portal.getDefaultModule(portal_type)
       except ValueError:
-        container = None
-
-    # From here, there is no hope unless a file was provided
-    if file_object is None:
-      raise ValueError, "No data provided"
-
+        pass
 
-    if portal_type is None:
-      # Guess it with help of portal_contribution_registry
-      registry = portal.portal_contribution_registry
-      portal_type = registry.findPortalTypeName(filename=filename,
-                                                content_type=content_type)
     #
     # Check if same file is already exists. if it exists, then update it.
     #
@@ -247,12 +249,9 @@ class ContributionTool(BaseTool):
     # Temp objects use the standard newContent from Folder
     if temp_object:
       # For temp_object creation, use the standard method
-      kw['portal_type'] = portal_type
-      return BaseTool.newContent(self, **kw)
+      return BaseTool.newContent(self, portal_type=portal_type, **kw)
 
     # Then put the file inside ourselves for a short while
-    if container_path is not None:
-      container = self.getPortalObject().restrictedTraverse(container_path)
     document = self._setObject(document_id, None, portal_type=portal_type,
                                user_login=user_login, container=container,
                                discover_metadata=discover_metadata,
@@ -270,7 +269,7 @@ class ContributionTool(BaseTool):
 
     # Allow reindexing, reindex it and return the document
     try:
-      delattr(document, 'isIndexable')
+      del document.isIndexable
     except AttributeError:
       # Document does not have such attribute
       pass
diff --git a/product/ERP5Type/Core/Folder.py b/product/ERP5Type/Core/Folder.py
index 87bbfb3322..be948ae124 100644
--- a/product/ERP5Type/Core/Folder.py
+++ b/product/ERP5Type/Core/Folder.py
@@ -44,7 +44,6 @@ from Products.ERP5Type.CopySupport import CopyContainer
 from Products.ERP5Type import PropertySheet
 from Products.ERP5Type.XMLExportImport import Folder_asXML
 from Products.ERP5Type.Utils import sortValueList
-from Products.ERP5Type.WebDAVSupport import Folder as WebDAVFolder
 from Products.ERP5Type import Permissions
 
 try:
@@ -454,7 +453,7 @@ BTREE_HANDLER = 1
 HBTREE_HANDLER = 2
 
 
-class Folder(CopyContainer, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn, WebDAVFolder):
+class Folder(CopyContainer, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn):
   """
   A Folder is a subclass of Base but not of XMLObject.
   Folders are not considered as documents and are therefore
@@ -518,7 +517,7 @@ class Folder(CopyContainer, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn,
   Title = Base.Title
   _setPropValue = Base._setPropValue
   _propertyMap = Base._propertyMap # are there any others XXX ?
-  PUT_factory = WebDAVFolder.PUT_factory
+  PUT_factory = None
   # XXX Prevent inheritance from PortalFolderBase
   description = None
 
diff --git a/product/ERP5Type/WebDAVSupport.py b/product/ERP5Type/WebDAVSupport.py
index c0f3701827..bbe983319b 100644
--- a/product/ERP5Type/WebDAVSupport.py
+++ b/product/ERP5Type/WebDAVSupport.py
@@ -200,36 +200,48 @@ class TextContent:
     """ Used for FTP and apparently the ZMI now too """
     return len(self.manage_FTPget())
 
-
-class Folder:
-  """
-  Taken from CMFCore.PortalFolder
-  """
-  def PUT_factory( self, name, typ, body ):
-    """ Factory for PUT requests to objects which do not yet exist.
-
-    Used by NullResource.PUT.
-
-    Returns -- Bare and empty object of the appropriate type (or None, if
-    we don't know what to do)
-    """
-    portal = self.getPortalObject()
-    registry = portal.portal_contribution_registry
-    portal_type = registry.findPortalTypeName(filename=name,
-                                              content_type=typ)
-    if portal_type is None:
-      return None
-
-    # The code bellow is inspired from ERP5Type.Core.Folder.newContent
-    pt = self._getTypesTool()
-    myType = pt.getTypeInfo(self)
-    if myType is not None and not myType.allowType( portal_type ) and \
-       'portal_contributions' not in self.getPhysicalPath():
-      raise ValueError('Disallowed subobject type: %s' % portal_type)
-    container = portal.getDefaultModule(portal_type)
-    pt.constructContent(type_name=portal_type,
-                        container=container,
-                        id=name)
-
-    document = container._getOb(name)
-    return document
+from webdav.common import Locked, PreconditionFailed
+from webdav.interfaces import IWriteLock
+from webdav.NullResource import NullResource
+from zope.contenttype import guess_content_type
+NullResource_PUT = NullResource.PUT
+
+def PUT(self, REQUEST, RESPONSE):
+        """Create a new non-collection resource.
+        """
+        if getattr(self.__parent__, 'PUT_factory', None) is not None: # BBB
+          return NullResource_PUT(self, REQUEST, RESPONSE)
+
+        self.dav__init(REQUEST, RESPONSE)
+
+        name = self.__name__
+        parent = self.__parent__
+
+        ifhdr = REQUEST.get_header('If', '')
+        if IWriteLock.providedBy(parent) and parent.wl_isLocked():
+            if ifhdr:
+                parent.dav__simpleifhandler(REQUEST, RESPONSE, col=1)
+            else:
+                # There was no If header at all, and our parent is locked,
+                # so we fail here
+                raise Locked
+        elif ifhdr:
+            # There was an If header, but the parent is not locked
+            raise PreconditionFailed
+
+        # <ERP5>
+        # XXX: Do we really want to force 'id'
+        #      when PUT is called on Contribution Tool ?
+        kw = {'id': name, 'data': None, 'filename': name}
+        contribution_tool = parent.getPortalObject().portal_contributions
+        if aq_base(contribution_tool) is not aq_base(parent):
+          kw.update(container=parent, discover_metadata=False)
+        ob = contribution_tool.newContent(**kw)
+        # </ERP5>
+
+        ob.PUT(REQUEST, RESPONSE)
+        RESPONSE.setStatus(201)
+        RESPONSE.setBody('')
+        return RESPONSE
+
+NullResource.PUT = PUT
-- 
2.30.9