[Zope3-checkins] CVS: Zope3/src/zope/products/file - __init__.py:1.1.2.1 browser.py:1.1.2.1 configure.zcml:1.1.2.1 file.py:1.1.2.1 file_icon.gif:1.1.2.1 file_upload.hlp:1.1.2.1 fssync.py:1.1.2.1 interfaces.py:1.1.2.1 tests.py:1.1.2.1

Philipp von Weitershausen philikon at philikon.de
Wed Feb 11 11:29:22 EST 2004


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

Added Files:
      Tag: philikon-movecontent-branch
	__init__.py browser.py configure.zcml file.py file_icon.gif 
	file_upload.hlp fssync.py interfaces.py tests.py 
Log Message:
Get rid of zope.products.content and zope.products.codecontent and move
content components in their own packages at zope.products.

See the package geddon proposal: http://dev.zope.org/Zope3/PackageGeddon


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


=== Added File Zope3/src/zope/products/file/browser.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.
#
##############################################################################
"""File views.

$Id: browser.py,v 1.1.2.1 2004/02/11 16:29:19 philikon Exp $
"""
from zope.app.browser.form.widget import BytesAreaWidget
from zope.app.form.widget import CustomWidgetFactory

__metaclass__ = type

class FileView:

    def show(self):
        """Call the File"""
        request = self.request
        if request is not None:
            request.response.setHeader('Content-Type',
                                       self.context.getContentType())
            request.response.setHeader('Content-Length',
                                       self.context.getSize())

        return self.context.getData()


class FileTextEdit:
    """File editing mix-in that uses a file-upload widget.
    """

    data_widget = CustomWidgetFactory(BytesAreaWidget)


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

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

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

  <interface 
      interface=".interfaces.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.IReadFile"
        />

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

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

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

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

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


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


  <!-- Browser stuff -->

  <browser:view
      name="_traverse"
      for=".interfaces.IFileContent"
      class="zope.app.publication.traversers.FileContentTraverser"
      permission="zope.Public"
      />
  
  <browser:editform
      name="edit.html"
      schema=".interfaces.IFile"
      label="Change a file"
      usage="objectview"
      permission="zope.ManageContent" 
      class=".browser.FileTextEdit" 
      />

  <browser:menuItem
      menu="zmi_views" title="Edit"
      for=".interfaces.IFile"
      action="edit.html"
      filter="python:context.contentType.startswith('text/')"
      permission="zope.ManageContent" />


  <browser:editform
      name="upload.html"
      menu="zmi_views" title="Upload"
      schema=".interfaces.IFile"
      label="Upload a file"
      permission="zope.ManageContent"
      />

  <!--browser:page
      for=".interfaces.IFile"
      name="preview.html"
      menu="zmi_views" title="Preview"
      template="preview.pt"
      permission="zope.ManageContent" /-->

  <browser:page
      for=".interfaces.IFile"
      name="index.html"
      permission="zope.View"
      class=".browser.FileView"
      attribute="show" />


  <browser:addMenuItem
      class=".file.File"
      title="File"
      permission="zope.ManageContent"
      view="zope.products.file.File"
      />

  <browser:addform
      schema=".interfaces.IFile"
      label="Add a File"
      content_factory=".file.File"
      name="zope.products.file.File"
      permission="zope.ManageContent"
      />

  <browser:icon
      name="zmi_icon"
      for=".interfaces.IFile"
      file="file_icon.gif"
      />

  <help:register
      id="file_upload"
      title="File Upload Screen"
      parent="ui"
      for=".interfaces.IFile"
      view="upload.html"
      doc_path="./file_upload.hlp"
      />

</configure>


=== Added File Zope3/src/zope/products/file/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.
#
##############################################################################
"""File content component

$Id: file.py,v 1.1.2.1 2004/02/11 16:29:19 philikon Exp $
"""
from persistence import Persistent
from transaction import get_transaction
from zope.interface import implements

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

__metaclass__ = type

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

class File(Persistent):
    """A persistent content component storing binary file data

    Let's test the constructor:

    >>> file = File()
    >>> file.getContentType()
    ''
    >>> file.getData()
    ''

    >>> file = File('Foobar')
    >>> file.getContentType()
    ''
    >>> file.getData()
    'Foobar'

    >>> file = File('Foobar', 'text/plain')
    >>> file.getContentType()
    'text/plain'
    >>> file.getData()
    'Foobar'

    >>> file = File(data='Foobar', contentType='text/plain')
    >>> file.getContentType()
    'text/plain'
    >>> file.getData()
    'Foobar'


    Let's test the mutators:

    >>> file = File()
    >>> file.setContentType('text/plain')
    >>> file.getContentType()
    'text/plain'

    >>> file.setData('Foobar')
    >>> file.getData()
    'Foobar'

    >>> file.edit('Blah', 'text/html')
    >>> file.getContentType()
    'text/html'
    >>> file.getData()
    'Blah'

    >>> file.setData(None)
    Traceback (most recent call last):
    ...
    TypeError: Cannot set None data on a file.


    Let's test large data input:

    >>> file = File()

    Insert as string:

    >>> file.setData('Foobar'*60000)
    >>> file.getSize()
    360000
    >>> file.getData() == 'Foobar'*60000
    True

    Insert data as FileChunk:

    >>> fc = FileChunk('Foobar'*4000)
    >>> file.setData(fc)
    >>> file.getSize()
    24000
    >>> file.getData() == 'Foobar'*4000
    True

    Insert data from file object:

    >>> import cStringIO
    >>> sio = cStringIO.StringIO()
    >>> sio.write('Foobar'*100000)
    >>> sio.seek(0)
    >>> file.setData(sio)
    >>> file.getSize()
    600000
    >>> file.getData() == 'Foobar'*100000
    True


    Last, but not least, verify the interface:

    >>> from zope.interface.verify import verifyClass
    >>> IFile.isImplementedByInstancesOf(File)
    True
    >>> verifyClass(IFile, File)
    True
    """
    
    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)

class FileReadFile:
    """Adapter for file-system style read access.

    >>> file = File()
    >>> content = "This is some file\\ncontent."
    >>> file.edit(content, 'text/plain')
    >>> FileReadFile(file).read() == content
    True
    >>> FileReadFile(file).size() == len(content)
    True
    """

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

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

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

class FileWriteFile:
    """Adapter for file-system style write access.

    >>> file = File()
    >>> content = "This is some file\\ncontent."
    >>> FileWriteFile(file).write(content)
    >>> file.getData() == content
    True
    """

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

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


=== Added File Zope3/src/zope/products/file/file_icon.gif ===
  <Binary-ish file>

=== Added File Zope3/src/zope/products/file/file_upload.hlp ===
This screen allows to upload new file data.

=== Added File Zope3/src/zope/products/file/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/11 16:29:19 philikon Exp $
"""
from zope.interface import implements
from zope.fssync.server.entryadapter import ObjectEntryAdapter
from zope.fssync.server.interfaces import IObjectFile

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/file/interfaces.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.
#
##############################################################################
"""Basic File interfaces.

$Id: interfaces.py,v 1.1.2.1 2004/02/11 16:29:19 philikon Exp $
"""
import zope.schema
from zope.interface import Interface
from zope.app.i18n import ZopeMessageIDFactory as _

class IReadFile(Interface):

    contentType = zope.schema.BytesLine(
        title = _(u'Content Type'),
        description=_(u'The content type identifies the type of data.'),
        default = 'text/plain',
        required=False
        )

    data = zope.schema.Bytes(
        title = _(u'Data'),
        description = _(u'The actual content of the object.'),
        default='',
        required=False,
        )

    def getData():
        """Return the contained data of the object."""

    def getContentType():
        """Returns the content type of the file using mime-types syntax."""

    def getSize():
        """Return the byte-size of the data of the object."""


class IWriteFile(Interface):

    def edit(data, contentType=None):
        """Sets the data and the content type for the object.

           Since some implementations will provide their content type
           through the data, it is good to leave the argument optional.
        """

    def setData(data):
        """Rewrite the 'file'."""

    def setContentType(contentType):
        """Sets the content type of the file."""


class IFile(IReadFile, IWriteFile):
    """The basic methods that are required to implement
       a file as a Zope Content object.

    """


class IFileContent(Interface):
    """Marker interface for content that can be managed as files.

    The default view for file content has effective URLs that don't end in
    /.  In particular, if the content included HTML, relative links in
    the HTML are relative to the container the content is in.
    """


=== Added File Zope3/src/zope/products/file/tests.py ===
##############################################################################
#
# Copyright (c) 2004 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: tests.py,v 1.1.2.1 2004/02/11 16:29:19 philikon Exp $
"""
import unittest
from zope.testing.doctestunit import DocTestSuite

def test_suite():
    return unittest.TestSuite((
        DocTestSuite('zope.products.file.file'),        
        ))

if __name__ == '__main__':
    unittest.main()




More information about the Zope3-Checkins mailing list