[Zope3-checkins] CVS: Zope3/src/zope/app/content - __init__.py:1.2 configure.zcml:1.2 dtmlpage.py:1.2 file.py:1.2 folder.py:1.2 i18nfile.py:1.2 i18nimage.py:1.2 image.py:1.2 sql.py:1.2 zpt.py:1.2
Jim Fulton
jim@zope.com
Wed, 25 Dec 2002 09:13:50 -0500
Update of /cvs-repository/Zope3/src/zope/app/content
In directory cvs.zope.org:/tmp/cvs-serv15352/src/zope/app/content
Added Files:
__init__.py configure.zcml dtmlpage.py file.py folder.py
i18nfile.py i18nimage.py image.py sql.py zpt.py
Log Message:
Grand renaming:
- Renamed most files (especially python modules) to lower case.
- Moved views and interfaces into separate hierarchies within each
project, where each top-level directory under the zope package
is a separate project.
- Moved everything to src from lib/python.
lib/python will eventually go away. I need access to the cvs
repository to make this happen, however.
There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.
=== Zope3/src/zope/app/content/__init__.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/__init__.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,2 @@
+#
+# This file is necessary to make this directory a package.
=== Zope3/src/zope/app/content/configure.zcml 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/configure.zcml Wed Dec 25 09:12:48 2002
@@ -0,0 +1,278 @@
+<zopeConfigure
+ xmlns='http://namespaces.zope.org/zope'
+ xmlns:browser='http://namespaces.zope.org/browser'
+>
+
+<!-- Simple Folder Directives -->
+
+<content class="zope.app.content.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
+ interface="zope.app.interfaces.services.service.Read"
+ />
+
+ <require
+ permission="zope.ManageServices"
+ interface="zope.app.interfaces.services.service.Write"
+ />
+
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.container.IReadContainer"
+ />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.container.IWriteContainer"
+ />
+
+</content>
+
+<!-- XXX Do we really need RootFolder? -->
+
+<content class="zope.app.content.folder.RootFolder">
+
+ <implements interface="zope.app.interfaces.container.IContentContainer" />
+
+ <require like_class="zope.app.content.folder.Folder" />
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
+ />
+
+</content>
+
+ <permission id="zope.AddImages" title="Add Images" />
+
+ <content class="zope.app.content.image.Image">
+
+ <factory
+ id="Image"
+ permission="zope.ManageContent"
+ title="Image"
+ description="An Image" />
+
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.content.file.IReadFile"
+ attributes="getImageSize"
+ />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.content.file.IWriteFile"
+ set_schema="zope.app.interfaces.content.file.IReadFile"
+ />
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
+ />
+
+ </content>
+
+ <content class="zope.app.content.i18nimage.I18nImage">
+ <factory
+ id="I18nImage"
+ permission="zope.ManageContent"
+ title="I18n Image"
+ description="An Internationalized Image" />
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.content.file.IReadFile"
+ attributes="getImageSize"
+ />
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.content.file.IWriteFile" />
+ <require
+ permission="zope.View"
+ attributes="getDefaultLanguage getAvailableLanguages" />
+ <require
+ permission="zope.ManageContent"
+ attributes="setDefaultLanguage removeLanguage" />
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
+ />
+
+ </content>
+
+
+<content class="zope.app.content.zpt.ZPTPage">
+ <factory
+ id="ZPTPage"
+ permission="zope.ManageContent"
+ title="ZPT Page"
+ description="A simple, content-based Page Template" />
+
+ <factory
+ id=".pt"
+ permission="zope.ManageContent"
+ title="ZPT Page" />
+
+ <factory
+ id=".zpt"
+ permission="zope.ManageContent"
+ title="ZPT Page" />
+
+ <require
+ permission="zope.View"
+ attributes="__call__" />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.content.zpt.IZPTPage"
+ set_attributes="source" />
+
+ <require
+ permission="zope.View"
+ interface="zope.app.content.zpt.IRenderZPTPage" />
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
+
+ </content>
+
+ <adapter factory="zope.app.content.zpt.SearchableText"
+ provides="zope.app.interfaces.index.text.interfaces.ISearchableText"
+ for="zope.app.content.zpt.IZPTPage" />
+
+
+ <content class="zope.app.content.dtmlpage.DTMLPage">
+
+ <factory
+ id="DTMLPage"
+ permission="zope.ManageContent"
+ title="DTML Page"
+ description="A simple, content-based Page Template" />
+
+ <require permission="zope.View"
+ attributes="__call__" />
+
+ <require permission="zope.ManageContent"
+ interface="zope.app.content.dtmlpage.IDTMLPage" />
+
+ <require permission="zope.View"
+ interface="zope.app.content.dtmlpage.IRenderDTMLPage" />
+
+
+ <implements interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
+
+ </content>
+
+
+ <content class="zope.app.content.file.File">
+
+ <factory
+ id="File"
+ permission="zope.ManageContent"
+ title="File"
+ description="A File" />
+
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.content.file.IReadFile" />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.content.file.IWriteFile"
+ set_schema="zope.app.interfaces.content.file.IReadFile"
+ />
+
+ <implements
+ interface="zope.app.interfaces.annotation.IAttributeAnnotatable"
+ />
+
+ </content>
+
+ <content class="zope.app.content.i18nfile.I18nFile">
+
+ <factory
+ id="zope.app.content.I18nFile"
+ permission="zope.ManageContent"
+ title="I18n File"
+ description="An Internationalized File" />
+ <require
+ permission="zope.View"
+ interface="zope.app.interfaces.content.file.IReadFile" />
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.content.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="zope.app.content.file.SearchableText"
+ provides="zope.app.interfaces.index.text.interfaces.ISearchableText"
+ for="zope.app.interfaces.content.file.IReadFile" />
+
+ <!-- SQL Script Directives -->
+
+ <permission id="zope.AddSQLScripts" title="Add SQL Scripts" />
+
+ <content class="zope.app.content.sql.SQLScript">
+
+ <factory
+ id="SQLScript"
+ permission="zope.ManageContent"
+ title="SQL Script"
+ description="Dynamic SQL Script" />
+
+ <require
+ permission = "zope.ManageContent"
+ interface = "zope.app.interfaces.content.sql.ISQLScript."
+ set_schema = "zope.app.interfaces.content.sql.ISQLScript."
+ />
+
+ <require
+ permission="zope.ManageContent"
+ interface="zope.app.interfaces.content.file.IFileContent" />
+
+ <implements interface="zope.app.interfaces.annotation.IAttributeAnnotatable" />
+
+ </content>
+
+
+ <!-- Arguments Directives -->
+
+ <content class="zope.app.content.sql.Arguments">
+ <require
+ permission="zope.ManageContent"
+ interface="zope.interface.common.mapping.IEnumerableMapping" />
+ </content>
+
+
+ <!-- SQL DTML Directives -->
+
+ <content class="zope.app.content.sql.SQLDTML">
+ <require
+ permission="zope.ManageContent"
+ attributes="__call__" />
+ </content>
+
+
+ <!-- Further Directives -->
+
+
+</zopeConfigure>
=== Zope3/src/zope/app/content/dtmlpage.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/dtmlpage.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,96 @@
+##############################################################################
+#
+# 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$
+"""
+import zope.schema
+
+from persistence import Persistent
+
+from zope.interface import Interface, Attribute
+from zope.app.interfaces.annotation import IAnnotatable
+from zope.app.interfaces.content.file import IFileContent
+
+from zope.proxy.context import ContextMethod
+from zope.proxy.context import getWrapperContainer
+from zope.security.proxy import ProxyFactory
+
+from zope.documenttemplate.dt_html import HTML
+
+
+class IDTMLPage(Interface):
+ """DTML Pages are a persistent implementation of DTML."""
+
+ def setSource(text, content_type='text/html'):
+ """Save the source of the page template."""
+
+ def getSource():
+ """Get the source of the page template."""
+
+ source = zope.schema.Bytes(
+ title=u"Source",
+ description=u"""The source od the page template.""",
+ required=True)
+
+
+class IRenderDTMLPage(Interface):
+
+ content_type = Attribute('Content type of generated output')
+
+ def render(request, *args, **kw):
+ """Render the page template.
+
+ The first argument is bound to the top-level 'request'
+ variable. The positional arguments are bound to the 'args'
+ variable and the keyword arguments are bound to the 'options'
+ variable.
+ """
+
+
+class DTMLPage(Persistent):
+
+ # XXX Putting IFileContent at the end gives an error!
+ __implements__ = IFileContent, IDTMLPage, IRenderDTMLPage, IAnnotatable
+
+ def __init__(self, source=''):
+ self.setSource(source)
+
+ def getSource(self):
+ '''See interface IDTMLPage'''
+ return self.template.read()
+
+ def setSource(self, text, content_type='text/html'):
+ '''See interface IDTMLPage'''
+ self.template = HTML(text.encode('utf-8'))
+ self.content_type = content_type
+
+ def render(self, request, *args, **kw):
+ """See interface IDTMLRenderPage"""
+
+ instance = ProxyFactory(getWrapperContainer(self))
+ request = ProxyFactory(request)
+
+ for k in kw:
+ kw[k] = ProxyFactory(kw[k])
+ kw['REQUEST'] = request
+
+ return self.template(instance, request, **kw)
+
+
+ render = ContextMethod(render)
+
+ __call__ = render
+
+ source = property(getSource, setSource, None,
+ """Source of the DTML Page.""")
=== Zope3/src/zope/app/content/file.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/file.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,228 @@
+##############################################################################
+#
+# 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$
+"""
+import datetime
+zerotime = datetime.datetime.fromtimestamp(0)
+
+from persistence import Persistent
+from transaction import get_transaction
+
+from zope.component import getAdapter
+from zope.publisher.browser import FileUpload
+
+from zope.app.interfaces.dublincore import IZopeDublinCore
+from zope.app.interfaces.content.file import IFile, IReadFile
+
+
+# set the size of the chunks
+MAXCHUNKSIZE = 1 << 16
+
+class File(Persistent):
+ __implements__ = 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 Zope.App.OFS.Content.File.IFile.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 hasattr(data, '__class__') and data.__class__ is 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 hasattr(self._data, '__class__') and \
+ self._data.__class__ is 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):
+ size = len(data)
+ if size < MAXCHUNKSIZE:
+ self._data, self._size = FileChunk(data), size
+ return None
+ self._data, self._size = FileChunk(data), size
+ return None
+
+ # Handle case when data is None
+ if data is None:
+ self._data, self._size = None, 0
+ return None
+
+ # Handle case when data is already a FileChunk
+ if hasattr(data, '__class__') and data.__class__ is FileChunk:
+ size = len(data)
+ self._data, self._size = data, size
+ return None
+
+ # 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 None
+ self._data, self._size = FileChunk(read(size)), size
+ return None
+
+ # 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 None
+
+ # 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 None
+
+
+ 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.interfaces 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)
=== Zope3/src/zope/app/content/folder.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/folder.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,100 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+
+from persistence import Persistent
+from zodb.btrees.OOBTree import OOBTree
+from zope.app.interfaces.content.folder import IFolder, IRootFolder
+from zope.app.services.service import ServiceManagerContainer
+from zope.app.interfaces.services.service import IServiceManagerContainer
+from zope.exceptions import DuplicationError
+
+
+class Folder(Persistent, ServiceManagerContainer):
+ """The standard Zope Folder implementation."""
+
+ __implements__ = IFolder
+
+ def __init__(self):
+ self.data = OOBTree()
+
+ def keys(self):
+ """Return a sequence-like object containing the names
+ associated with the objects that appear in the folder
+ """
+ return self.data.keys()
+
+ def __iter__(self):
+ return iter(self.data.keys())
+
+ def values(self):
+ """Return a sequence-like object containing the objects that
+ appear in the folder.
+ """
+ return self.data.values()
+
+ def items(self):
+ """Return a sequence-like object containing tuples of the form
+ (name, object) for the objects that appear in the folder.
+ """
+ return self.data.items()
+
+ def __getitem__(self, name):
+ """Return the named object, or the value of the default
+ argument if given and the named object is not found.
+ If no default is given and the object is not found a
+ KeyError is raised.
+ """
+ return self.data[name]
+
+ def get(self, name, default=None):
+ """Return the named object, or the value of the default
+ argument if given and the named object is not found.
+ If no default is given and the object is not found a
+ KeyError is raised.
+ """
+ return self.data.get(name, default)
+
+ def __contains__(self, name):
+ """Return true if the named object appears in the folder."""
+ return self.data.has_key(name)
+
+ def __len__(self):
+ """Return the number of objects in the folder."""
+ return len(self.data)
+
+ def setObject(self, name, object):
+ """Add the given object to the folder under the given name."""
+
+ if not (isinstance(name, str) or isinstance(name, unicode)):
+ raise TypeError("Name must be a string rather than a %s" %
+ name.__class__.__name__)
+ if not name:
+ raise TypeError("Name must not be empty")
+
+ if name in self.data:
+ raise DuplicationError("name, %s, is already in use" % name)
+
+ self.data[name] = object
+ return name
+
+ def __delitem__(self, name):
+ """Delete the named object from the folder. Raises a KeyError
+ if the object is not found."""
+ del self.data[name]
+
+
+class RootFolder(Folder):
+ """The standard Zope root Folder implementation."""
+
+ __implements__ = Folder.__implements__, IRootFolder
=== Zope3/src/zope/app/content/i18nfile.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/i18nfile.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,145 @@
+##############################################################################
+#
+# 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$
+"""
+
+import persistence
+from zope.app.interfaces.content.i18nfile import II18nFile
+from zope.app.content.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 hasattr(data, '__class__') and data.__class__ is 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
=== Zope3/src/zope/app/content/i18nimage.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/i18nimage.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# 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$
+"""
+
+from zope.app.content.image import IImage, Image, getImageInfo
+from zope.app.content.i18nfile import I18nFile
+from zope.app.interfaces.content.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()
=== Zope3/src/zope/app/content/image.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/image.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,107 @@
+##############################################################################
+#
+# 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$
+"""
+import struct
+from zope.app.content.file import File
+from cStringIO import StringIO
+from zope.app.interfaces.content.image import IImage
+
+class Image(File):
+ __implements__ = IImage
+
+ def __init__(self, data=None):
+ '''See interface IFile'''
+ self.contentType, self._width, self._height = getImageInfo(data)
+ self.data = data
+
+
+ def setData(self, data):
+
+ super(Image, self).setData(data)
+
+ if data is not None:
+ contentType = None
+ 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.""")
+
+
+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
=== Zope3/src/zope/app/content/sql.py 1.1 => 1.2 === (801/901 lines abridged)
--- /dev/null Wed Dec 25 09:13:49 2002
+++ Zope3/src/zope/app/content/sql.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,898 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""\
+Inserting optional tests with 'sqlgroup'
+
+ It is sometimes useful to make inputs to an SQL statement
+ optinal. Doing so can be difficult, because not only must the
+ test be inserted conditionally, but SQL boolean operators may or
+ may not need to be inserted depending on whether other, possibly
+ optional, comparisons have been done. The 'sqlgroup' tag
+ automates the conditional insertion of boolean operators.
+
+ The 'sqlgroup' tag is a block tag that has no attributes. It can
+ have any number of 'and' and 'or' continuation tags.
+
+ Suppose we want to find all people with a given first or nick name
+ and optionally constrain the search by city and minimum and
+ maximum age. Suppose we want all inputs to be optional. We can
+ use DTML source like the following::
+
+ <dtml-sqlgroup>
+ <dtml-sqlgroup>
+ <dtml-sqltest name column=nick_name type=nb multiple optional>
+ <dtml-or>
+ <dtml-sqltest name column=first_name type=nb multiple optional>
+ </dtml-sqlgroup>
+ <dtml-and>
+ <dtml-sqltest home_town type=nb optional>
+ <dtml-and>
+ <dtml-if minimum_age>
+ age >= <dtml-sqlvar minimum_age type=int>
+ </dtml-if>
+ <dtml-and>
+ <dtml-if maximum_age>
+ age <= <dtml-sqlvar maximum_age type=int>
+ </dtml-if>
[-=- -=- -=- 801 lines omitted -=- -=- -=-]
+ except:
+ # Okay, the first try failed, so let's try to find the default
+ arg = self._arguments[name]
+ try:
+ arg_values[name] = arg['default']
+ except:
+ # Now the argument might be optional anyways; let's check
+ try:
+ if not arg['optional']:
+ missing.append(name)
+ except:
+ missing.append(name)
+
+ try:
+ connection = self.getConnection()
+ except AttributeError:
+ raise AttributeError, (
+ "The database connection **%s** cannot be found." % (
+ self.connectionName))
+
+ if connection is None:
+ raise 'Database Error', (
+ '%s is not connected to a database' %'foo')# self.id)
+
+ query = apply(self.template, (), arg_values)
+ cache = getCacheForObj(self)
+ location = getLocationForCache(self)
+ if cache and location:
+ _marker = []
+ result = cache.query(location, {'query': query}, default=_marker)
+ if result is not _marker:
+ return result
+ result = queryForResults(connection, query)
+ if cache and location:
+ cache.set(result, location, {'query': query})
+ return result
+
+ __call__ = ContextMethod(__call__)
+
+
+ # See ISQLScript
+ arguments = property(getArgumentsString, setArguments, None,
+ "Set the arguments that are used for the SQL Script.")
+ source = property(getSource, setSource, None,
+ "Set the SQL template source.")
+ connectionName = property(getConnectionName, setConnectionName, None,
+ "Connection Name for the SQL scripts.")
+
+
+valid_type = {'int':1, 'float':1, 'string':1, 'nb': 1}.has_key
=== Zope3/src/zope/app/content/zpt.py 1.1 => 1.2 ===
--- /dev/null Wed Dec 25 09:13:50 2002
+++ Zope3/src/zope/app/content/zpt.py Wed Dec 25 09:12:48 2002
@@ -0,0 +1,133 @@
+##############################################################################
+#
+# 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$
+"""
+
+import re
+
+from persistence import Persistent
+
+import zope.schema
+
+from zope.interface import Interface, Attribute
+from zope.proxy.context import ContextMethod
+from zope.proxy.context import getWrapperContainer
+from zope.security.proxy import ProxyFactory
+
+from zope.pagetemplate.pagetemplate import PageTemplate
+from zope.app.pagetemplate.engine import AppPT
+from zope.app.interfaces.index.text.interfaces import ISearchableText
+
+
+class IZPTPage(Interface):
+ """ZPT Pages are a persistent implementation of Page Templates.
+
+ Note: I introduced some new methods whose functionality is
+ actually already covered by some other methods but I
+ want to start enforcing a common coding standard.
+ """
+
+ def setSource(text, content_type='text/html'):
+ """Save the source of the page template.
+
+ 'text' must be Unicode.
+ """
+
+ def getSource():
+ """Get the source of the page template."""
+
+ source = zope.schema.Text(
+ title=u"Source",
+ description=u"""The source of the page template.""",
+ required=True)
+
+
+class IRenderZPTPage(Interface):
+
+ content_type = Attribute('Content type of generated output')
+
+ def render(request, *args, **kw):
+ """Render the page template.
+
+ The first argument is bound to the top-level 'request'
+ variable. The positional arguments are bound to the 'args'
+ variable and the keyword arguments are bound to the 'options'
+ variable.
+ """
+
+
+class ZPTPage(AppPT, PageTemplate, Persistent):
+
+ __implements__ = IZPTPage, IRenderZPTPage
+
+ def getSource(self):
+ '''See IZPTPage'''
+ return self.read()
+
+ def setSource(self, text, content_type='text/html'):
+ '''See IZPTPage'''
+ if not isinstance(text, unicode):
+ raise TypeError("source text must be Unicode")
+
+ self.pt_edit(text.encode('utf-8'), content_type)
+
+ def pt_getContext(self, instance, request, **_kw):
+ # instance is a View component
+ namespace = super(ZPTPage, self).pt_getContext(**_kw)
+ namespace['request'] = request
+ namespace['context'] = instance
+ return namespace
+
+ def render(self, request, *args, **keywords):
+ instance = getWrapperContainer(self)
+
+ request = ProxyFactory(request)
+ instance = ProxyFactory(instance)
+ if args: args = ProxyFactory(args)
+ kw = ProxyFactory(keywords)
+
+ namespace = self.pt_getContext(instance, request,
+ args=args, options=kw)
+
+ return self.pt_render(namespace)
+
+ render = ContextMethod(render)
+
+ source = property(getSource, setSource, None,
+ """Source of the Page Template.""")
+
+
+class SearchableText:
+
+ __used_for__ = IZPTPage
+ __implements__ = ISearchableText
+
+ def __init__(self, page):
+ self.page = page
+
+ def getSearchableText(self):
+ text = self.page.getSource()
+ if isinstance(text, str):
+ text = unicode(self.page.source, 'utf-8')
+ # else:
+ # text was already Unicode, which happens, but unclear how it
+ # gets converted to Unicode since the ZPTPage stores UTF-8 as
+ # an 8-bit string.
+
+ if self.page.content_type.startswith('text/html'):
+ tag = re.compile(r"<[^>]+>")
+ text = tag.sub('', text)
+
+ return [text]