Skip to content

Commit

Permalink
Improve type guessing for the default WebDAV PUT factory (#1004)
Browse files Browse the repository at this point in the history
* - Improve type guessing for the default WebDAV PUT factory

* - defer to guess_content_type

* - cover case with no content-type header
  • Loading branch information
dataflake committed Jan 6, 2022
1 parent 870366c commit 0951a96
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ https://zope.readthedocs.io/en/2.13/CHANGES.html
4.6.4 (unreleased)
------------------

- Improve type guessing for the default WebDAV PUT factory
(`#997 <https://github.com/zopefoundation/Zope/issues/997>`_)

- Enable WebDAV PUT factories to change a newly created object's ID
(`#997 <https://github.com/zopefoundation/Zope/issues/997>`_)

Expand Down
2 changes: 1 addition & 1 deletion src/Products/PageTemplates/PageTemplateFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def guess_type(filename, body):
# detect text/xml if 'filename' won't end with .xml
# XXX: fix this in zope.contenttype

if body.startswith(b'<?xml'):
if body.startswith(b'<?xml') or filename.lower().endswith('.xml'):
return 'text/xml'

content_type, ignored_encoding = guess_content_type(filename, body)
Expand Down
2 changes: 1 addition & 1 deletion src/Products/PageTemplates/ZopePageTemplate.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def PUT(self, REQUEST, RESPONSE):
self.dav__init(REQUEST, RESPONSE)
self.dav__simpleifhandler(REQUEST, RESPONSE, refresh=1)
text = REQUEST.get('BODY', '')
content_type = guess_type('', text)
content_type = guess_type(self.getId(), text)
self.pt_edit(text, content_type)
RESPONSE.setStatus(204)
return RESPONSE
Expand Down
8 changes: 7 additions & 1 deletion src/webdav/NullResource.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,16 +100,22 @@ def HEAD(self, REQUEST, RESPONSE):
def _default_PUT_factory(self, name, typ, body):
# See if the name contains a file extension
shortname, ext = os.path.splitext(name)
ext = ext.lower()

# Make sure the body is bytes
if not isinstance(body, bytes):
body = body.encode('UTF-8')

# Guess the type of file if the passed content-type is
# just the generic application/octet-stream
if not typ or typ == 'application/octet-stream':
typ, encoding = guess_content_type(name, body)

if ext == '.dtml':
ob = DTMLDocument('', __name__=name)
elif typ in ('text/html', 'text/xml'):
ob = ZopePageTemplate(name, body, content_type=typ)
elif typ[:6] == 'image/':
elif typ.startswith('image/'):
ob = Image(name, '', body, content_type=typ)
else:
ob = File(name, '', body, content_type=typ)
Expand Down
77 changes: 77 additions & 0 deletions src/webdav/tests/testPUT_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,80 @@ def custom_put_factory(name, typ, body):
put = request.traverse('/folder/doc')
put(request, request.RESPONSE)
self.assertTrue('newname' in self.folder.objectIds())

def test_default_PUT_factory_type_guessing(self):
# Check how the default PUT factory guesses the type of object to
# create. It is based on either the content-type request header or the
# file name.
from OFS.DTMLDocument import DTMLDocument
from OFS.Image import File
from OFS.Image import Image
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
request = self.app.REQUEST

# DTML documents
put = request.traverse('/folder/doc.dtml')
put(request, request.RESPONSE)
self.assertIsInstance(self.folder['doc.dtml'], DTMLDocument)

# Page Templates
# PUT and content type guessing is messed up for ZopePageTemplates
request.environ['CONTENT_TYPE'] = 'text/html'
request['BODY'] = b'bar'
put = request.traverse('/folder/zpt1')
put(request, request.RESPONSE)
self.assertIsInstance(self.folder.zpt1, ZopePageTemplate)
self.assertEqual(self.folder.zpt1.content_type, 'text/html')

request['BODY'] = b'<?xml version="1.0" encoding="UTF-8"?>bar'
put = request.traverse('/folder/zpt2')
put(request, request.RESPONSE)
self.assertIsInstance(self.folder.zpt2, ZopePageTemplate)
self.assertEqual(self.folder.zpt2.content_type, 'text/xml')

for html_extension in ('.pt', '.zpt', '.html', '.htm'):
ob_id = 'zpt%s' % html_extension
request.environ['CONTENT_TYPE'] = 'application/octet-stream'
request['BODY'] = b'<html></html>'
put = request.traverse('/folder/%s' % ob_id)
put(request, request.RESPONSE)
zope_ob = getattr(self.folder, ob_id)
self.assertIsInstance(zope_ob, ZopePageTemplate)
self.assertEqual(zope_ob.content_type, 'text/html')

request.environ['CONTENT_TYPE'] = 'application/octet-stream'
put = request.traverse('/folder/zpt.xml')
put(request, request.RESPONSE)
zope_ob = self.folder['zpt.xml']
self.assertIsInstance(zope_ob, ZopePageTemplate)
self.assertEqual(zope_ob.content_type, 'text/xml')

# Images
for content_type in ('image/jpg', 'image/gif', 'image/png'):
request.environ['CONTENT_TYPE'] = content_type
ob_id = 'img_%s' % content_type.replace('/', '_')
put = request.traverse('/folder/%s' % ob_id)
put(request, request.RESPONSE)
zope_ob = getattr(self.folder, ob_id)
self.assertIsInstance(zope_ob, Image)
self.assertEqual(zope_ob.content_type, content_type)

for extension in ('.jpg', '.jpeg', '.gif', '.png', '.tiff'):
ob_id = 'img%s' % extension
request.environ['CONTENT_TYPE'] = 'application/octet-stream'
put = request.traverse('/folder/%s' % ob_id)
put(request, request.RESPONSE)
zope_ob = getattr(self.folder, ob_id)
self.assertIsInstance(zope_ob, Image)

# File, the last fallback
for content_type in ('text/plain', 'application/pdf',
'application/octet-stream'):
request.environ['CONTENT_TYPE'] = content_type
request['BODY'] = b'foobar'
ob_id = 'file_%s' % content_type.replace('/', '_')
put = request.traverse('/folder/%s' % ob_id)
put(request, request.RESPONSE)
zope_ob = getattr(self.folder, ob_id)
self.assertIsInstance(zope_ob, File)
self.assertEqual(zope_ob.content_type, content_type)

0 comments on commit 0951a96

Please sign in to comment.