[Zope3-checkins] CVS: Zope3/src/zope/products/content - __init__.py:1.1.2.1 configure.zcml:1.1.2.1 file.py:1.1.2.1 fssync.py:1.1.2.1 i18nfile.py:1.1.2.1 i18nimage.py:1.1.2.1 image.py:1.1.2.1

Philipp von Weitershausen philikon at philikon.de
Sun Feb 8 09:03:45 EST 2004


Update of /cvs-repository/Zope3/src/zope/products/content
In directory cvs.zope.org:/tmp/cvs-serv10229/products/content

Added Files:
      Tag: philikon-movecontent-branch
	__init__.py configure.zcml file.py fssync.py i18nfile.py 
	i18nimage.py image.py 
Log Message:
Move zope.app.content,
     zope.app.interfaces.content,
 and zope.app.browser.content
to zope.products.content and zope.products.codecontent, respectively.


=== Added File Zope3/src/zope/products/content/__init__.py ===
#
# This file is necessary to make this directory a package.


=== Added File Zope3/src/zope/products/content/configure.zcml ===
<configure
    xmlns='http://namespaces.zope.org/zope'
    xmlns:browser='http://namespaces.zope.org/browser'
    xmlns:fssync='http://namespaces.zope.org/fssync'
    i18n_domain='zope'
    >

  <!-- Module aliases for backward compat -->

  <modulealias
      module="zope.app.folder"
      alias="zope.app.content.folder"
      />

  <modulealias
      module=".file"
      alias="zope.app.content.file"
      />


<!-- Simple Folder Directives -->

<interface 
    interface="zope.app.interfaces.folder.IFolder" 
    type="zope.app.interfaces.content.IContentType"
    /> 

<content class="zope.app.folder.Folder">

  <implements interface="zope.app.interfaces.container.IContentContainer" />

  <implements
     interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
     />

  <factory
      id="Folder"
      permission="zope.ManageContent"
      title="Folder"
      description="Minimal folder" />

  <allow
      attributes="getSiteManager"
      />

  <require
      permission="zope.ManageServices"
      attributes="setSiteManager"
      />

  <require
      permission="zope.View"
      interface="zope.app.interfaces.container.IReadContainer" 
      />

  <require
      permission="zope.ManageContent"
      interface="zope.app.interfaces.container.IWriteContainer"
      />

</content>

<adapter
   for="zope.app.interfaces.folder.IFolder"
   provides="zope.app.interfaces.file.IDirectoryFactory"
   factory="zope.app.container.directory.Cloner"
   permission="zope.ManageContent"
   />

<adapter
   for="zope.app.interfaces.folder.IFolder"
   provides="zope.app.interfaces.file.IFileFactory"
   factory=".image.FileFactory"
   permission="zope.ManageContent"
   />

<adapter
   for="zope.app.interfaces.folder.IFolder"
   provides="zope.app.interfaces.file.IReadDirectory"
   factory=".fssync.ReadDirectory"
   permission="zope.View"
   />


<!-- Image -->

<permission
    id="zope.AddImages"
    title="[add-images-permission] Add Images"
    />

<interface 
    interface=".interfaces.image.IImage" 
    type="zope.app.interfaces.content.IContentType"
    /> 

<content class=".image.Image">

  <factory
      id="Image"
      permission="zope.ManageContent"
      title="Image"
      description="An Image" />

  <require
      permission="zope.View"
      interface=".interfaces.file.IReadFile"
      attributes="getImageSize"
      />

  <require
      permission="zope.ManageContent"
      interface=".interfaces.file.IWriteFile"
      set_schema=".interfaces.file.IReadFile"
      />

  <implements
     interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
     />
</content>

<adapter
    factory=".image.ImageSized"
    provides="zope.app.interfaces.size.ISized"
    for=".interfaces.image.IImage"
    />


<!-- I18n Image -->

<interface 
    interface=".interfaces.i18nimage.II18nImage" 
    type="zope.app.interfaces.content.IContentType"
    />

<content class=".i18nimage.I18nImage">

  <factory
      id="I18nImage"
      permission="zope.ManageContent"
      title="I18n Image"
      description="An Internationalized Image" />
  <require
      permission="zope.View"
      interface=".interfaces.file.IReadFile"
      attributes="getImageSize"
      />
  <require
      permission="zope.ManageContent"
      interface=".interfaces.file.IWriteFile" />
  <require
      permission="zope.View"
      attributes="getDefaultLanguage getAvailableLanguages" />
  <require
      permission="zope.ManageContent"
      attributes="setDefaultLanguage removeLanguage" />

  <implements
     interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
     />

</content>


<!-- File -->

<interface 
    interface=".interfaces.file.IFile" 
    type="zope.app.interfaces.content.IContentType"
    /> 

<content class=".file.File">

  <factory
      id="File"
      permission="zope.ManageContent"
      title="File"
      description="A File" />

  <require
      permission="zope.View"
      interface=".interfaces.file.IReadFile" />

  <require
      permission="zope.ManageContent"
      interface=".interfaces.file.IWriteFile"
      set_schema=".interfaces.file.IReadFile"
      />

  <implements
     interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
     />

</content>

<adapter 
   for=".interfaces.file.IFile"
   provides="zope.app.interfaces.file.IReadFile"
   factory=".file.FileReadFile"
   permission="zope.View"
   />

<adapter 
   for=".interfaces.file.IFile"
   provides="zope.app.interfaces.file.IWriteFile"
   factory=".file.FileWriteFile"
   permission="zope.ManageContent"
   />


<!-- I18n File -->

<interface 
    interface=".i18nfile.II18nFile" 
    type="zope.app.interfaces.content.IContentType"
    />

<content class=".i18nfile.I18nFile">

  <factory
      id=".I18nFile"
      permission="zope.ManageContent"
      title="I18n File"
      description="An Internationalized File" />
  <require
      permission="zope.View"
      interface=".interfaces.file.IReadFile" />
  <require
      permission="zope.ManageContent"
      interface=".interfaces.file.IWriteFile" />
  <require
      permission="zope.View"
      attributes="getDefaultLanguage getAvailableLanguages" />
  <require
      permission="zope.ManageContent"
      attributes="setDefaultLanguage removeLanguage" />

  <implements
     interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
     />

</content>

  <adapter
      factory=".file.SearchableText"
      provides="zope.app.interfaces.index.text.ISearchableText"
      for=".interfaces.file.IReadFile"
      />

<!-- fssync:adapter directives -->

  <fssync:adapter
      factory="zope.fssync.server.entryadapter.DefaultFileAdpater"
      />

  <fssync:adapter
      class=".file.File"
      factory=".fssync.FileAdapter"
      />

  <fssync:adapter
      class=".image.Image"
      factory=".fssync.FileAdapter"
      />

  <fssync:adapter
      class="zope.app.folder.Folder"
      factory=".fssync.FolderAdapter"
      />


  <!-- include the browser package -->

  <include package=".browser" />

</configure>


=== Added File Zope3/src/zope/products/content/file.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""

$Id: file.py,v 1.1.2.1 2004/02/08 14:03:44 philikon Exp $
"""
from persistence import Persistent
from transaction import get_transaction
from zope.interface import implements

from zope.publisher.browser import FileUpload
from interfaces.file import IFile, IReadFile, IFileContent

# set the size of the chunks
MAXCHUNKSIZE = 1 << 16

class File(Persistent):
    implements(IFileContent, IFile)

    def __init__(self, data='', contentType=''):
        self.data = data
        self.contentType = contentType

    def __len__(self):
        return self.size

    def setContentType(self, contentType):
        '''See interface IFile'''
        self._contentType = contentType

    def getContentType(self):
        '''See interface IFile'''
        return self._contentType

    def edit(self, data, contentType=None):
        '''See interface IFile'''
        # XXX This seems broken to me, as setData can override the
        # content type explicitly passed in.

        if contentType is not None:
            self._contentType = contentType
        if isinstance(data, FileUpload) and not data.filename:
            data = None          # Ignore empty files
        if data is not None:
            self.data = data

    def getData(self):
        '''See interface IFile'''
        if isinstance(self._data, FileChunk):
            return str(self._data)
        else:
            return self._data

    def setData(self, data):
        '''See interface IFile'''
        # Handle case when data is a string
        if isinstance(data, unicode):
            data = data.encode('UTF-8')

        if isinstance(data, str):
            self._data, self._size = FileChunk(data), len(data)
            return

        # Handle case when data is None
        if data is None:
            raise TypeError('Cannot set None data on a file.')

        # Handle case when data is already a FileChunk
        if isinstance(data, FileChunk):
            size = len(data)
            self._data, self._size = data, size
            return

        # Handle case when data is a file object
        seek = data.seek
        read = data.read

        seek(0, 2)
        size = end = data.tell()

        if size <= 2*MAXCHUNKSIZE:
            seek(0)
            if size < MAXCHUNKSIZE:
                self._data, self._size = read(size), size
                return
            self._data, self._size = FileChunk(read(size)), size
            return

        # Make sure we have an _p_jar, even if we are a new object, by
        # doing a sub-transaction commit.
        get_transaction().savepoint()

        jar = self._p_jar

        if jar is None:
            # Ugh
            seek(0)
            self._data, self._size = FileChunk(read(size)), size
            return

        # Now we're going to build a linked list from back
        # to front to minimize the number of database updates
        # and to allow us to get things out of memory as soon as
        # possible.
        next = None
        while end > 0:
            pos = end - MAXCHUNKSIZE
            if pos < MAXCHUNKSIZE:
                pos = 0 # we always want at least MAXCHUNKSIZE bytes
            seek(pos)
            data = FileChunk(read(end - pos))

            # Woooop Woooop Woooop! This is a trick.
            # We stuff the data directly into our jar to reduce the
            # number of updates necessary.
            data._p_jar = jar

            # This is needed and has side benefit of getting
            # the thing registered:
            data.next = next

            # Now make it get saved in a sub-transaction!
            get_transaction().savepoint()

            # Now make it a ghost to free the memory.  We
            # don't need it anymore!
            data._p_changed = None

            next = data
            end = pos

        self._data, self._size = next, size
        return

    def getSize(self):
        '''See interface IFile'''
        return self._size

    data = property(getData, setData, None,
                    """Contains the data of the file.""")

    contentType = property(getContentType, setContentType, None,
                           """Specifies the content type of the data.""")

    size = property(getSize, None, None,
                    """Specifies the size of the file in bytes. Read only.""")


# Adapter for ISearchableText

from zope.app.interfaces.index.text import ISearchableText

class SearchableText:

    implements(ISearchableText)
    __used_for__ = IReadFile

    def __init__(self, file):
        self.file = file

    def getSearchableText(self):
        if self.file.contentType == "text/plain":
            return [unicode(self.file.data)]
        else:
            return None


class FileChunk(Persistent):
    # Wrapper for possibly large data

    next = None

    def __init__(self, data):
        self._data = data

    def __getslice__(self, i, j):
        return self._data[i:j]

    def __len__(self):
        data = str(self)
        return len(data)

    def __str__(self):
        next = self.next
        if next is None:
            return self._data

        result = [self._data]
        while next is not None:
            self = next
            result.append(self._data)
            next = self.next

        return ''.join(result)

# Adapters for file-system style access

class FileReadFile:

    def __init__(self, context):
        self.context = context

    def read(self):
        return self.context.getData()

    def size(self):
        return len(self.context.getData())

class FileWriteFile:

    def __init__(self, context):
        self.context = context

    def write(self, data):
        self.context.setData(data)


=== Added File Zope3/src/zope/products/content/fssync.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Filesystem synchronization support.

$Id: fssync.py,v 1.1.2.1 2004/02/08 14:03:44 philikon Exp $
"""

from zope.interface import implements
from zope.fssync.server.entryadapter import ObjectEntryAdapter, \
     DirectoryAdapter, AttrMapping
from zope.fssync.server.interfaces import IObjectFile, IContentDirectory

__metaclass__ = type

class RootDirectoryFactory:

    def __init__(self, context):
        pass

    def __call__(self, name):
        return Folder()

class ReadDirectory:
    """Adapter to provide a file-system rendition of folders
    """

    def __init__(self, context):
        self.context = context

    def keys(self):
        keys = self.context.keys()
        if ISite.isImplementedBy(self.context):
            return list(keys) + ['++etc++site']
        return keys

    def get(self, key, default=None):
        if key == '++etc++site' and ISite.isImplementedBy(self.context):
            return self.context.getSiteManager()

        return self.context.get(key, default)

    def __iter__(self):
        return iter(self.keys())

    def __getitem__(self, key):
        v = self.get(key, self)
        if v is self:
            raise KeyError, key
        return v

    def values(self):
        return map(self.get, self.keys())

    def __len__(self):
        l = len(self.context)
        if ISite.isImplementedBy(self.context):
            l += 1
        return l

    def items(self):
        get = self.get
        return [(key, get(key)) for key in self.keys()]

    def __contains__(self, key):
        return self.get(key) is not None

class FolderAdapter(DirectoryAdapter):
    """Adapter to provide an fssync interpretation of folders
    """

    def contents(self):
        result = super(FolderAdapter, self).contents()
        if ISite.isImplementedBy(self.context):
            sm = self.context.getSiteManager()
            result.append(('++etc++site', sm))
        return result

class FileAdapter(ObjectEntryAdapter):
    """ObjectFile adapter for file objects.
    """

    implements(IObjectFile)

    def getBody(self):
        return self.context.getData()

    def setBody(self, data):
        self.context.setData(data)

    def extra(self):
        return AttrMapping(self.context, ('contentType',))


=== Added File Zope3/src/zope/products/content/i18nfile.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: i18nfile.py,v 1.1.2.1 2004/02/08 14:03:44 philikon Exp $
"""

import persistence
from zope.interface import implements

from interfaces.i18nfile import II18nFile
from file import File

# XXX We shouldn't be dependent on Browser here! Aaaargh.
from zope.publisher.browser import FileUpload

class I18nFile(persistence.Persistent):
    """I18n aware file object.  It contains a number of File objects --
    one for each language.
    """

    implements(II18nFile)

    def __init__(self, data='', contentType=None, defaultLanguage='en'):
        """ """

        self._data = {}
        self.defaultLanguage = defaultLanguage
        self.setData(data, language=defaultLanguage)

        if contentType is None:
            self.setContentType('')
        else:
            self.setContentType(contentType)


    def __len__(self):
        return self.getSize()


    def _create(self, data):
        """Create a new subobject of the appropriate type.  Should be
        overriden in subclasses.
        """
        return File(data)


    def _get(self, language):
        """Helper function -- return a subobject for a given language,
        and if it does not exist, return a subobject for the default
        language.
        """
        file = self._data.get(language)
        if not file:
            file = self._data[self.defaultLanguage]
        return file


    def _get_or_add(self, language, data=''):
        """Helper function -- return a subobject for a given language,
        and if it does not exist, create and return a new subobject.
        """
        if language is None:
            language = self.defaultLanguage
        file = self._data.get(language)
        if not file:
            self._data[language] = file = self._create(data)
            self._p_changed = 1
        return file


    def setContentType(self, contentType):
        '''See interface IFile'''
        self._contentType = contentType


    def getContentType(self):
        '''See interface IFile'''
        return self._contentType


    contentType = property(getContentType, setContentType)

    def edit(self, data, contentType=None, language=None):
        '''See interface IFile'''

        # XXX This seems broken to me, as setData can override the
        # content type explicitly passed in.

        if contentType is not None:
            self.setContentType(contentType)
        if isinstance(data, FileUpload) and not data.filename:
            data = None          # Ignore empty files
        if data is not None:
            self.setData(data, language)


    def getData(self, language=None):
        '''See interface IFile'''
        return self._get(language).getData()


    def setData(self, data, language=None):
        '''See interface IFile'''
        self._get_or_add(language).setData(data)

    data = property(getData, setData)

    def getSize(self, language=None):
        '''See interface IFile'''
        return self._get(language).getSize()

    def getDefaultLanguage(self):
        'See II18nAware'
        return self.defaultLanguage

    def setDefaultLanguage(self, language):
        'See II18nAware'
        if not self._data.has_key(language):
            raise ValueError, \
                  'cannot set nonexistent language (%s) as default' % language
        self.defaultLanguage = language

    def getAvailableLanguages(self):
        'See II18nAware'
        return self._data.keys()

    def removeLanguage(self, language):
        '''See interface II18nFile'''

        if language == self.defaultLanguage:
            raise ValueError, 'cannot remove default language (%s)' % language
        if self._data.has_key(language):
            del self._data[language]
            self._p_changed = 1


=== Added File Zope3/src/zope/products/content/i18nimage.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
Revision Information:
$Id: i18nimage.py,v 1.1.2.1 2004/02/08 14:03:44 philikon Exp $
"""

from zope.interface import implements

from image import Image, getImageInfo
from i18nfile import I18nFile
from interfaces.i18nimage import II18nImage

class I18nImage(I18nFile):
    """An internationalized Image object.  Note that images of all
    languages share the same content type.
    """

    implements(II18nImage)

    def _create(self, data):
        return Image(data)

    def setData(self, data, language=None):
        '''See interface IFile'''
        super(I18nImage, self).setData(data, language)

        if language is None or language == self.getDefaultLanguage():
            # Uploading for the default language only overrides content
            # type.  Note: do not use the argument data here, it doesn't
            # work.
            contentType = getImageInfo(self.getData(language))[0]
            if contentType:
                self.setContentType(contentType)

    def getImageSize(self, language=None):
        '''See interface IImage'''
        return self._get(language).getImageSize()


=== Added File Zope3/src/zope/products/content/image.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id: image.py,v 1.1.2.1 2004/02/08 14:03:44 philikon Exp $
"""
import struct
from cStringIO import StringIO

from zope.interface import implements

from zope.app.interfaces.size import ISized
from zope.app.size import byteDisplay
from zope.app.content_types import guess_content_type
from zope.app.i18n import ZopeMessageIDFactory as _

from interfaces.image import IImage
from file import File

__metaclass__ = type

class Image(File):
    implements(IImage)

    def __init__(self, data=''):
        '''See interface IFile'''
        self.contentType, self._width, self._height = getImageInfo(data)
        self.data = data

    def setData(self, data):
        super(Image, self).setData(data)

        contentType, self._width, self._height = getImageInfo(self.data)
        if contentType:
            self.contentType = contentType

    def getImageSize(self):
        '''See interface IImage'''
        return (self._width, self._height)

    data = property(File.getData, setData, None,
                    """Contains the data of the file.""")


class ImageSized:

    implements(ISized)

    def __init__(self, image):
        self._image = image

    def sizeForSorting(self):
        'See ISized'
        return ('byte', self._image.getSize())

    def sizeForDisplay(self):
        'See ISized'
        w, h = self._image.getImageSize()
        if w < 0:
            w = '?'
        if h < 0:
            h = '?'
        bytes = self._image.getSize()
        byte_size = byteDisplay(bytes)
        mapping = byte_size.mapping
        size = _(byte_size + ' ${width}x${height}')
        mapping.update({'width': str(w), 'height': str(h)})
        size.mapping = mapping 
        return size


def getImageInfo(data):
    data = str(data)
    size = len(data)
    height = -1
    width = -1
    content_type = ''

    # handle GIFs
    if (size >= 10) and data[:6] in ('GIF87a', 'GIF89a'):
        # Check to see if content_type is correct
        content_type = 'image/gif'
        w, h = struct.unpack("<HH", data[6:10])
        width = int(w)
        height = int(h)

    # See PNG v1.2 spec (http://www.cdrom.com/pub/png/spec/)
    # Bytes 0-7 are below, 4-byte chunk length, then 'IHDR'
    # and finally the 4-byte width, height
    elif ((size >= 24) and data.startswith('\211PNG\r\n\032\n')
          and (data[12:16] == 'IHDR')):
        content_type = 'image/png'
        w, h = struct.unpack(">LL", data[16:24])
        width = int(w)
        height = int(h)

    # Maybe this is for an older PNG version.
    elif (size >= 16) and data.startswith('\211PNG\r\n\032\n'):
        # Check to see if we have the right content type
        content_type = 'image/png'
        w, h = struct.unpack(">LL", data[8:16])
        width = int(w)
        height = int(h)

    # handle JPEGs
    elif (size >= 2) and data.startswith('\377\330'):
        content_type = 'image/jpeg'
        jpeg = StringIO(data)
        jpeg.read(2)
        b = jpeg.read(1)
        try:
            while (b and ord(b) != 0xDA):
                while (ord(b) != 0xFF): b = jpeg.read(1)
                while (ord(b) == 0xFF): b = jpeg.read(1)
                if (ord(b) >= 0xC0 and ord(b) <= 0xC3):
                    jpeg.read(3)
                    h, w = struct.unpack(">HH", jpeg.read(4))
                    break
                else:
                    jpeg.read(int(struct.unpack(">H", jpeg.read(2))[0])-2)
                b = jpeg.read(1)
            width = int(w)
            height = int(h)
        except struct.error:
            pass
        except ValueError:
            pass

    return content_type, width, height


class FileFactory:

    def __init__(self, context):
        self.context = context

    def __call__(self, name, content_type, data):
        if not content_type and data:
            content_type, width, height = getImageInfo(data)
        if not content_type:
            content_type, encoding = guess_content_type(name, data, '')

        if content_type.startswith('image/'):
            return Image(data)

        return File(data, content_type)




More information about the Zope3-Checkins mailing list