[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