[Checkins] SVN: z3c.bobopublisher/trunk/ import: development version
Fabio Tranchitella
kobold at kobold.it
Sat Aug 15 11:20:10 EDT 2009
Log message for revision 102811:
import: development version
Changed:
_U z3c.bobopublisher/trunk/
A z3c.bobopublisher/trunk/CHANGES.txt
A z3c.bobopublisher/trunk/TODO.txt
A z3c.bobopublisher/trunk/bootstrap.py
A z3c.bobopublisher/trunk/buildout.cfg
A z3c.bobopublisher/trunk/setup.py
A z3c.bobopublisher/trunk/src/
A z3c.bobopublisher/trunk/src/z3c/
A z3c.bobopublisher/trunk/src/z3c/__init__.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/README.txt
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/__init__.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/absoluteurl.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/application.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/browser.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/configure.zcml
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/interfaces.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/meta.zcml
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/metaconfigure.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/metadirectives.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/middleware.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/publication.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/resources.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/tests.py
A z3c.bobopublisher/trunk/src/z3c/bobopublisher/traversing.py
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/PKG-INFO
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/SOURCES.txt
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/dependency_links.txt
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/entry_points.txt
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/namespace_packages.txt
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/not-zip-safe
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/requires.txt
A z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/top_level.txt
-=-
Property changes on: z3c.bobopublisher/trunk
___________________________________________________________________
Added: svn:ignore
+ *.pyc
*.pyo
bin
parts
eggs
develop-eggs
.installed.cfg
Added: z3c.bobopublisher/trunk/CHANGES.txt
===================================================================
--- z3c.bobopublisher/trunk/CHANGES.txt (rev 0)
+++ z3c.bobopublisher/trunk/CHANGES.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,7 @@
+CHANGES
+=======
+
+0.0.1 (unreleased)
+------------------
+
+- First public release.
Added: z3c.bobopublisher/trunk/TODO.txt
===================================================================
--- z3c.bobopublisher/trunk/TODO.txt (rev 0)
+++ z3c.bobopublisher/trunk/TODO.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,7 @@
+Missing features
+================
+
+ - Views (with multiple pages) (bobo:view)
+ - Single resource (bobo:resource)
+ - Bobo subroutes (bobo:subroute)
+
Added: z3c.bobopublisher/trunk/bootstrap.py
===================================================================
--- z3c.bobopublisher/trunk/bootstrap.py (rev 0)
+++ z3c.bobopublisher/trunk/bootstrap.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id: bootstrap.py 73408 2007-03-21 05:53:10Z baijum $
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+ez = {}
+exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+import pkg_resources
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+if sys.platform == 'win32':
+ cmd = '"%s"' % cmd # work around spawn lamosity on windows
+
+ws = pkg_resources.working_set
+assert os.spawnle(
+ os.P_WAIT, sys.executable, sys.executable,
+ '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
Added: z3c.bobopublisher/trunk/buildout.cfg
===================================================================
--- z3c.bobopublisher/trunk/buildout.cfg (rev 0)
+++ z3c.bobopublisher/trunk/buildout.cfg 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,20 @@
+[buildout]
+develop = .
+parts = test app graph
+
+[test]
+recipe = zc.recipe.testrunner
+eggs = z3c.bobopublisher [test]
+
+[app]
+recipe = zc.recipe.egg
+eggs = z3c.bobopublisher
+ Paste
+ PasteScript
+ PasteDeploy
+interpreter = python
+
+[graph]
+recipe = z3c.recipe.depgraph
+eggs = z3c.bobopublisher
+variants = tred
Added: z3c.bobopublisher/trunk/setup.py
===================================================================
--- z3c.bobopublisher/trunk/setup.py (rev 0)
+++ z3c.bobopublisher/trunk/setup.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,49 @@
+from setuptools import setup, find_packages
+
+setup(
+ name='z3c.bobopublisher',
+ version='0.0.1dev',
+ url='http://pypi.python.org/pypi/z3c.bobopublisher',
+ license='ZPL 2.1',
+ author='Fabio Tranchitella',
+ author_email='fabio at tranchitella.it',
+ description="Minimal reimplementation of the Zope publisher using the bobo framework.",
+ long_description=(
+ open('src/z3c/bobopublisher/README.txt').read() + '\n\n' +
+ open('CHANGES.txt').read()
+ ),
+ packages=find_packages('src'),
+ package_dir={'': 'src'},
+ namespace_packages=['z3c'],
+ tests_require=[
+ 'webtest',
+ 'zope.testing',
+ ],
+ install_requires=[
+ 'setuptools',
+ 'bobo',
+ 'WebOb',
+ 'zope.browser',
+ 'zope.component [zcml]',
+ 'zope.configuration',
+ 'zope.dottedname',
+ 'zope.interface',
+ 'zope.location',
+ 'zope.schema',
+ 'zope.security',
+ ],
+ extras_require=dict(
+ test=[
+ 'webtest',
+ 'zope.testing',
+ ],
+ ),
+ entry_points="""
+ [paste.app_factory]
+ main = z3c.bobopublisher.application:Application
+ [paste.filter_app_factory]
+ proxy = z3c.bobopublisher.middleware:make_proxy_middleware
+ """,
+ include_package_data=True,
+ zip_safe=False,
+)
Added: z3c.bobopublisher/trunk/src/z3c/__init__.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/__init__.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/__init__.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1 @@
+__import__('pkg_resources').declare_namespace(__name__)
Property changes on: z3c.bobopublisher/trunk/src/z3c/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/README.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/README.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/README.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,279 @@
+z3c.bobopublisher
+=================
+
+This package provides a minimalistic reimplementation of the Zope publisher and
+publication machinery using the bobo framework. It can be used as a simple
+alternative for the following packages:
+
+ * zope.app.publication
+ * zope.app.publisher
+ * zope.publisher
+ * zope.traversing
+
+
+The application
+---------------
+
+Let's create a sample application we can use to test bobopublisher:
+
+ >>> from webtest import TestApp
+ >>> from z3c.bobopublisher.application import Application
+ >>> testapp = TestApp(Application())
+
+Each request is mapped by bobo to a callable which will provide the response
+for the client; if no specific bobo route matches the request's URL, the
+bobopublisher Application object will try to traverse the URL components
+starting from a root object.
+
+
+The root object
+---------------
+
+The root object must be registered as an utility which provides the interface
+zope.location.interfaces.IRoot:
+
+ >>> testapp.get('/')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass zope.location.interfaces.IRoot>, '')
+
+We register a dummy root object with a single hard-coded sub-item for our
+testing purposes:
+
+ >>> from zope.component import adapts, getGlobalSiteManager
+ >>> from zope.interface import implements, Interface
+ >>> from zope.interface.common.mapping import IReadMapping
+ >>> from zope.location.interfaces import IRoot
+ >>> class Root(object):
+ ... implements(IRoot, IReadMapping)
+ ... def get(self, name, default=None):
+ ... return name == u'subitem' and SubItem() or default
+ >>> class ISubItem(Interface):
+ ... pass
+ >>> class SubItem(object):
+ ... implements(ISubItem)
+ >>> getGlobalSiteManager().registerUtility(Root(), IRoot)
+
+
+PublishTraverse adapters
+------------------------
+
+Now the application can find the root object, but we did not register any
+PublishTraverse adapter, so every request will raise an exception:
+
+ >>> testapp.get('/')
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', <Root object at ...>, <InterfaceClass z3c...>)
+
+z3c.bobopublisher provides two generic PublishTraverse adapters:
+
+ >>> from z3c.bobopublisher.traversing import PublishTraverse, PublishTraverseMapping
+ >>> getGlobalSiteManager().registerAdapter(PublishTraverse)
+ >>> getGlobalSiteManager().registerAdapter(PublishTraverseMapping)
+
+It is now possible to perform a GET request on the test application:
+
+ >>> testapp.get('/', status=404).body
+ '...Not Found...'
+
+
+Browser pages
+-------------
+
+Browser pages are named multi-adapters which adapt the context object and the
+request; we register a browser page for the root object:
+
+ >>> from z3c.bobopublisher.interfaces import IRequest
+ >>> from zope.browser.interfaces import IBrowserView
+ >>> class BrowserPage(object):
+ ... adapts(IRoot, IRequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'ABC'
+ >>> getGlobalSiteManager().registerAdapter(BrowserPage, name='index.html')
+
+Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/index.html', status=200).body
+ 'ABC'
+
+It is also possible to register a page for a specific HTTP method:
+
+ >>> from z3c.bobopublisher.interfaces import IDELETERequest
+ >>> class BrowserPageDelete(object):
+ ... adapts(IRoot, IDELETERequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'DELETE'
+ >>> getGlobalSiteManager().registerAdapter(BrowserPageDelete, name='index.html')
+
+Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.delete('/index.html', status=200).body
+ 'DELETE'
+
+
+Traversing and locations
+------------------------
+
+After traversing an object, z3c.bobopublisher will locate the object setting
+the parent and the name; if the traversed object doesn't provide the interface
+ILocation it will be wrapped inside a location proxy.
+
+If we traverse to the sub-item, we don't have any page defined:
+
+ >>> testapp.get('/subitem/index.html', status=404).body
+ '...Not Found...'
+
+We register a browser page for the sub-item:
+
+ >>> from z3c.bobopublisher.interfaces import IRequest
+ >>> from zope.browser.interfaces import IBrowserView
+ >>> class SubItemBrowserPage(object):
+ ... adapts(ISubItem, IRequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'XYZ: %s' % repr(self.context.__parent__)
+ >>> getGlobalSiteManager().registerAdapter(SubItemBrowserPage, name='index.html')
+
+Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/subitem/index.html', status=200).body
+ 'XYZ: <Root object at ...>'
+
+
+Object proxies
+--------------
+
+z3c.bobopublisher is able to wrap published objects with a proxy; it provides a
+filter middleware to configure the proxy factory:
+
+ [filter-app:proxy]
+ use = egg:z3c.bobopublisher#proxy
+ proxy = zope.security.checker.ProxyFactory
+ next = application
+
+In this example, we configure a proxied application to use the ProxyFactory
+from zope.security:
+
+ >>> from z3c.bobopublisher.middleware import ProxyMiddleware
+ >>> proxyapp = TestApp(ProxyMiddleware(Application(),
+ ... 'zope.security.checker.ProxyFactory'))
+
+Using the test application, we are able to call the page and get its result:
+
+ >>> proxyapp.get('/subitem/index.html')
+ Traceback (most recent call last):
+ ....
+ ForbiddenAttribute: ('get', <Root object at ...>)
+
+
+Defining browser pages with ZCML
+--------------------------------
+
+z3c.bobopublisher provides ZCML directives that can be used to define browser
+pages and the name of the default page.
+
+ >>> from zope.configuration import config, xmlconfig
+ >>> context = config.ConfigurationMachine()
+ >>> xmlconfig.registerCommonDirectives(context)
+ >>> context = xmlconfig.string("""<?xml version="1.0"?>
+ ... <configure xmlns="http://namespaces.zope.org/bobo">
+ ... <include package="z3c.bobopublisher" file="meta.zcml" />
+ ... <page
+ ... name="something.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... class="z3c.bobopublisher.tests.TestBrowserPage"
+ ... />
+ ... <page
+ ... name="delete.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... class="z3c.bobopublisher.tests.TestBrowserPage"
+ ... methods="DELETE"
+ ... />
+ ... <defaultView
+ ... name="something.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... />
+ ... </configure>
+ ... """, context=context, execute=True)
+
+Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/something.html', status=200).body
+ 'TEST PAGE'
+
+As shown above, it is possible to register browser pages for one or more
+specific HTTP methods:
+
+ >>> testapp.delete('/delete.html', status=200).body
+ 'TEST PAGE'
+
+ >>> testapp.get('/delete.html', status=404).body
+ '...Not Found...'
+
+We also registered 'something.html' as the default view name for the root
+object:
+
+ >>> testapp.get('/', status=200).body
+ 'TEST PAGE'
+
+
+Resources
+---------
+
+z3c.bobopublisher provides a ZCML directive which can be used to publish static
+resources from a directory in the filesystem:
+
+ >>> import os, tempfile
+ >>> tempdir = tempfile.mktemp()
+ >>> os.mkdir(tempdir)
+ >>> open(os.path.join(tempdir, 'resource.txt'), 'w').write('RESOURCE')
+
+ >>> context = xmlconfig.string("""<?xml version="1.0"?>
+ ... <configure xmlns="http://namespaces.zope.org/bobo">
+ ... <resources
+ ... name="images"
+ ... directory="%s"
+ ... />
+ ... </configure>
+ ... """ % tempdir, context=context, execute=True)
+
+By the default resources are registered for the IRoot interface, as shown below:
+
+ >>> testapp.get('/images', status=302).body
+ 'See http://localhost/images/'
+
+ >>> testapp.get('/images/', status=200).body
+ '...resource.txt...'
+
+ >>> response = testapp.get('/images/resource.txt', status=200)
+ >>> response.content_type, response.charset, response.body
+ ('text/plain', 'UTF-8', 'RESOURCE')
+
+ >>> response.headers['Cache-Control']
+ 'public,max-age=86400'
+ >>> 'Expires' in response.headers
+ True
+ >>> 'Last-Modified' in response.headers
+ True
+
+It is not possible to quit from the path of the resource directory:
+
+ >>> testapp.get('/images/../images/resource.txt', status=404).body
+ '...Not Found...'
+
+We can remove now the temporary directory:
+
+ >>> os.unlink(os.path.join(tempdir, 'resource.txt'))
+ >>> os.rmdir(tempdir)
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/absoluteurl.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/absoluteurl.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/absoluteurl.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,129 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from urllib import quote, unquote
+
+from z3c.bobopublisher.interfaces import IAbsoluteURL, IRequest
+
+from zope.component import adapts, getMultiAdapter
+from zope.interface import implements, Interface
+from zope.location.interfaces import ILocation, ILocationInfo, IRoot
+
+
+class AbsoluteURL(object):
+ """AbsoluteURL adapter
+
+ Verify the class:
+
+ >>> from zope.interface.verify import verifyClass
+ >>> verifyClass(IAbsoluteURL, AbsoluteURL)
+ True
+
+ Create a testing environment for the adapter:
+
+ >>> from webob import Request
+ >>> request = Request.blank('http://localhost/test')
+
+ >>> from zope.interface import implements
+ >>> from zope.location.interfaces import IRoot, ILocation
+
+ >>> class Root(object):
+ ... implements(IRoot, ILocation)
+ ... __name__ = u'root'
+ ... __parent__ = None
+
+ >>> class SubItem(object):
+ ... implements(ILocation)
+ ... def __init__(self, name, parent):
+ ... self.__name__ = name
+ ... self.__parent__ = parent
+
+ >>> root = Root()
+ >>> a = SubItem(u'\xe1', root)
+ >>> b = SubItem(u'b', a)
+
+ >>> from zope.component import getGlobalSiteManager
+ >>> from zope.location.traversing import LocationPhysicallyLocatable
+ >>> getGlobalSiteManager().registerAdapter(LocationPhysicallyLocatable)
+
+ >>> from zope.interface import Interface
+ >>> getGlobalSiteManager().registerAdapter(AbsoluteURL, (Interface, Interface), IAbsoluteURL)
+
+ Verify the object:
+
+ >>> from zope.interface.verify import verifyObject
+ >>> obj = AbsoluteURL(b, request)
+ >>> verifyObject(IAbsoluteURL, obj)
+ True
+
+ Check the behaviour of the adapter:
+
+ >>> str(obj)
+ 'http://localhost/%C3%A1/b'
+
+ >>> obj()
+ 'http://localhost/%C3%A1/b'
+
+ >>> unicode(obj)
+ u'http://localhost/\\xe1/b'
+
+ >>> breadcrumbs = obj.breadcrumbs()
+ >>> breadcrumbs[0]
+ {'url': 'http://localhost', 'name': u'root'}
+ >>> breadcrumbs[1]
+ {'url': 'http://localhost/%C3%A1', 'name': u'\\xe1'}
+ >>> breadcrumbs[2]
+ {'url': 'http://localhost/%C3%A1/b', 'name': u'b'}
+
+ """
+
+ implements(IAbsoluteURL)
+ adapts(Interface, IRequest)
+
+ _safe = '/@+'
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __str__(self):
+ path = ILocationInfo(self.context).getPath()
+ if path == '/':
+ path = ''
+ return '%s%s' % (self.request.application_url,
+ quote(path.encode('utf-8'), self._safe))
+
+ def __unicode__(self):
+ return unquote(str(self)).decode('utf-8')
+
+ def __call__(self):
+ return str(self)
+
+ def breadcrumbs(self):
+ parts = []
+ object = self.context
+ while not IRoot.providedBy(object):
+ object = ILocation(object)
+ adapter = getMultiAdapter((object, self.request), IAbsoluteURL)
+ parts.append({'name': object.__name__, 'url': str(adapter)})
+ object = object.__parent__
+ if ILocation.providedBy(object):
+ adapter = getMultiAdapter((object, self.request), IAbsoluteURL)
+ parts.append({'name': object.__name__, 'url': str(adapter)})
+ parts.reverse()
+ return parts
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/absoluteurl.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/application.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/application.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/application.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,32 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 bobo
+
+from zope.component import getGlobalSiteManager
+
+
+class Application(bobo.Application):
+ """Create a WSGI application based on bobo"""
+
+ def __init__(self, DEFAULT=None, **config):
+ # add the z3c.bobopublisher publication subroute
+ config['bobo_resources'] = ('z3c.bobopublisher.publication' + \
+ '\n' + config.get('bobo_resources', '')).rstrip()
+ # call the original __init__ method
+ return bobo.Application.__init__(self, DEFAULT, **config)
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/application.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/browser.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/browser.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/browser.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,33 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from zope.browser.interfaces import IBrowserView
+from zope.interface import implements
+
+
+class BrowserPage(object):
+ """Simple browser page"""
+
+ implements(IBrowserView)
+
+ def __init__(self, context, request):
+ self.context = context
+ self.request = request
+
+ def __call__(self):
+ raise NotImplemented, '__call__ method not implemented'
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/browser.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/configure.zcml
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/configure.zcml (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/configure.zcml 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<configure xmlns="http://namespaces.zope.org/zope">
+
+ <adapter factory=".traversing.PublishTraverse" />
+ <adapter factory=".traversing.PublishTraverseMapping" />
+ <adapter factory=".absoluteurl.AbsoluteURL" />
+
+</configure>
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/interfaces.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/interfaces.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/interfaces.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,80 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from zope.interface import Interface
+
+
+class IPublishTraverse(Interface):
+ """Interface for publish traverse adapters"""
+
+ def publishTraverse(request, name):
+ """Lookup a name
+
+ The 'request' argument is the publisher request object. The
+ 'name' argument is the name that is to be looked up; it must
+ be an ASCII string or Unicode object.
+
+ If a lookup is not possible, raise a KeyError error.
+ """
+
+
+class IDefaultViewName(Interface):
+ """Marker interface for the bobo:defaultView directive"""
+
+
+class IRequest(Interface):
+ """Interface for webob.Request objects"""
+
+
+class IGETRequest(IRequest):
+ """Interface for webob.Request objects (GET method)"""
+
+
+class IPOSTRequest(IRequest):
+ """Interface for webob.Request objects (POST method)"""
+
+
+class IPUTRequest(IRequest):
+ """Interface for webob.Request objects (PUT method)"""
+
+
+class IDELETERequest(IRequest):
+ """Interface for webob.Request objects (DELETE method)"""
+
+
+class IAbsoluteURL(Interface):
+ """Absolute URL"""
+
+ def __unicode__():
+ """Returns the URL as a unicode string."""
+
+ def __str__():
+ """Returns an ASCII string with all unicode characters url quoted."""
+
+ def __repr__():
+ """Get a string representation"""
+
+ def __call__():
+ """Returns an ASCII string with all unicode characters url quoted."""
+
+ def breadcrumbs():
+ """Returns a tuple like ({'name':name, 'url':url}, ...)
+
+ Name is the name to display for that segment of the breadcrumbs.
+ URL is the link for that segment of the breadcrumbs.
+ """
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/interfaces.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/meta.zcml
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/meta.zcml (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/meta.zcml 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<configure xmlns="http://namespaces.zope.org/zope"
+ xmlns:meta="http://namespaces.zope.org/meta">
+
+ <meta:directive
+ namespace="http://namespaces.zope.org/bobo"
+ name="page"
+ schema=".metadirectives.IPageDirective"
+ handler=".metaconfigure.page"
+ />
+
+ <meta:directive
+ namespace="http://namespaces.zope.org/bobo"
+ name="resources"
+ schema=".metadirectives.IResourcesDirective"
+ handler=".metaconfigure.resources"
+ />
+
+ <meta:directive
+ namespace="http://namespaces.zope.org/bobo"
+ name="defaultView"
+ schema=".metadirectives.IDefaultViewDirective"
+ handler=".metaconfigure.defaultView"
+ />
+
+</configure>
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/metaconfigure.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/metaconfigure.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/metaconfigure.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,64 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 bobo
+
+from z3c.bobopublisher.interfaces import IDefaultViewName, IRequest, \
+ IGETRequest, IPOSTRequest, IPUTRequest, IDELETERequest
+from z3c.bobopublisher.resources import Directory
+
+from zope.component import getGlobalSiteManager
+from zope.component.zcml import adapter
+from zope.interface import Interface
+from zope.location.interfaces import IRoot
+
+
+def page(_context, name='index.html', for_=None, class_=None, permission=None,
+ methods=None):
+ requests = []
+ if methods == None:
+ requests.append(IRequest)
+ else:
+ for m in methods:
+ if m == 'GET':
+ requests.append(IGETRequest)
+ elif m == 'POST':
+ requests.append(IPOSTRequest)
+ elif m == 'PUT':
+ requests.append(IPUTRequest)
+ elif m == 'DELETE':
+ requests.append(IDELETERequest)
+ for request in requests:
+ adapter(
+ _context, (class_,), provides=Interface, for_=(for_, request),
+ name=name, permission=permission,
+ )
+
+def resources(_context, name, directory, for_=IRoot, permission=None):
+ def resourcesFactory(context, request):
+ return Directory(directory)
+ adapter(
+ _context, (resourcesFactory,), provides=Interface,
+ for_=(for_, IGETRequest), name=name, permission=permission,
+ )
+
+
+def defaultView(_context, name, for_=None):
+ adapter(
+ _context, (lambda x: name,), provides=IDefaultViewName, for_=(for_,),
+ )
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/metaconfigure.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/metadirectives.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/metadirectives.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/metadirectives.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,97 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from zope.configuration.fields import GlobalInterface, GlobalObject, Path, \
+ Tokens
+from zope.interface import Interface
+from zope.location.interfaces import IRoot
+from zope.schema import TextLine, BytesLine
+from zope.security.zcml import Permission
+
+
+class IPageDirective(Interface):
+ """bobo:page directive"""
+
+ name = TextLine(
+ title=u'Name',
+ required=True,
+ )
+
+ for_ = GlobalInterface(
+ title=u"The interface this page is registered for",
+ required=True,
+ )
+
+ class_ = GlobalObject(
+ title=u"The browser view",
+ required=True,
+ )
+
+ permission = Permission(
+ title=u"Permission",
+ required=False,
+ )
+
+ methods = Tokens(
+ title=u'Allowed HTTP methods',
+ value_type=BytesLine(
+ required=True,
+ constraint=lambda x: x in ('GET', 'POST', 'PUT', 'DELETE'),
+ ),
+ required=False,
+ default=None,
+ )
+
+
+class IResourcesDirective(Interface):
+ """bobo:resources directive"""
+
+ name = TextLine(
+ title=u'Name',
+ required=True,
+ )
+
+ for_ = GlobalInterface(
+ title=u"The interface this resource is registered for",
+ required=False,
+ default=IRoot,
+ )
+
+ directory = Path(
+ title=u"Resources path",
+ required=True,
+ )
+
+ permission = Permission(
+ title=u"Permission",
+ required=False,
+ )
+
+
+class IDefaultViewDirective(Interface):
+ """bobo:defaultView directive"""
+
+ name = TextLine(
+ title=u'Name',
+ required=True,
+ )
+
+ for_ = GlobalInterface(
+ title=u"The interface this page is for",
+ required=True,
+ )
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/metadirectives.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/middleware.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/middleware.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/middleware.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,38 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from zope.dottedname.resolve import resolve
+
+
+class ProxyMiddleware(object):
+ """WSGI application filter to add support for object proxy"""
+
+ def __init__(self, app, proxy):
+ self.app = app
+ if isinstance(proxy, str):
+ proxy = resolve(proxy)
+ self.proxy = proxy
+
+ def __call__(self, environ, start_response):
+ environ['bobopublisher.proxy'] = self.proxy
+ return self.app(environ, start_response)
+
+
+def make_proxy_middleware(app, global_conf, proxy):
+ """Configure a ProxyMiddleware middleware"""
+ return ProxyMiddleware(app, proxy)
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/middleware.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/publication.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/publication.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/publication.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,84 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 bobo
+
+from webob import Request
+
+from z3c.bobopublisher.interfaces import IPublishTraverse, IDefaultViewName, \
+ IRequest, IGETRequest, IPOSTRequest, IPUTRequest, IDELETERequest
+
+from zope.browser.interfaces import IBrowserView
+from zope.component import getUtility, queryUtility
+from zope.interface import directlyProvides
+from zope.location import LocationProxy, locate
+from zope.location.interfaces import ILocation, IRoot
+
+
+class Publication(object):
+ """Publication subroute"""
+
+ methods = ('GET', 'HEAD', 'POST', 'PUT', 'DELETE')
+
+ def __init__(self, request, context=None):
+ self.request = self._request(request)
+ if context is None:
+ context = getUtility(IRoot)
+ self.context = self.proxy(context)
+
+ def _request(self, request):
+ if request.method in (u'GET', u'HEAD'):
+ directlyProvides(request, IGETRequest)
+ elif request.method == u'POST':
+ directlyProvides(request, IPOSTRequest)
+ elif request.method == u'PUT':
+ directlyProvides(request, IPUTRequest)
+ elif request.method == u'DELETE':
+ directlyProvides(request, IDELETERequest)
+ else:
+ directlyProvides(request, IRequest)
+ return request
+
+ def proxy(self, object):
+ proxy = self.request.environ.get('bobopublisher.proxy')
+ if proxy is None:
+ return object
+ return proxy(object)
+
+ @bobo.resource('')
+ def base(self, request):
+ return bobo.redirect(request.url + '/')
+
+ @bobo.subroute('/:name')
+ def traverse(self, request, name):
+ traverser = IPublishTraverse(self.context)
+ name = name or IDefaultViewName(self.context, u'index.html')
+ try:
+ obj = traverser.publishTraverse(request, name)
+ except KeyError:
+ raise bobo.NotFound
+ if IBrowserView.providedBy(obj):
+ return bobo.query('', method=self.methods)(obj)
+ elif hasattr(obj, 'bobo_response'):
+ return obj
+ elif not ILocation.providedBy(obj):
+ obj = LocationProxy(obj)
+ locate(obj, self.context, name)
+ return Publication(request, obj)
+
+Publication = bobo.subroute('', scan=True)(Publication)
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/publication.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/resources.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/resources.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/resources.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,98 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 bobo
+import mimetypes
+import os
+import time
+import webob
+
+
+class Directory:
+ """Directory resource"""
+
+ def __init__(self, root, path=None):
+ self.root = os.path.abspath(root)+os.path.sep
+ self.path = path or root
+
+ @bobo.query('')
+ def base(self, bobo_request):
+ return bobo.redirect(bobo_request.url+'/')
+
+ @bobo.query('/')
+ def index(self):
+ links = []
+ for name in os.listdir(self.path):
+ if os.path.isdir(os.path.join(self.path, name)):
+ name += '/'
+ links.append('<a href="%s">%s</a>' % (name, name))
+ return """
+ <html>
+ <head><title>%s</title></head>
+ <body>
+ %s
+ </body>
+ </html>
+ """ % (self.path[len(self.root):], '<br>\n '.join(links))
+
+ @bobo.subroute('/:name')
+ def traverse(self, request, name):
+ path = os.path.abspath(os.path.join(self.path, name))
+ if not path.startswith(self.root):
+ raise bobo.NotFound
+ if os.path.isdir(path):
+ return Directory(self.root, path)
+ else:
+ return File(path)
+
+bobo.scan_class(Directory)
+
+
+def setCacheControl(response, seconds=86400):
+ t = time.time() + seconds
+ response.headers['Cache-Control'] = 'public,max-age=%s' % seconds
+ response.headers['Expires'] = time.strftime(
+ '%a, %d %b %Y %H:%M:%S GMT', time.gmtime(t))
+
+
+class File:
+ """File resource"""
+
+ def __init__(self, path):
+ self.path = path
+
+ @bobo.query('')
+ def base(self, bobo_request):
+ response = webob.Response()
+ content_type = mimetypes.guess_type(self.path)[0]
+ if content_type is not None:
+ response.content_type = content_type
+ if not content_type.startswith('text'):
+ response.charset = None
+ try:
+ response.body = open(self.path, 'rb').read()
+ except IOError:
+ raise bobo.NotFound
+ setCacheControl(response)
+ response.headers['Last-Modified'] = time.strftime(
+ '%a, %d %b %Y %H:%M:%S GMT', time.gmtime(
+ float(os.path.getmtime(self.path)) or time()))
+ return response
+
+bobo.scan_class(File)
+
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/resources.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/tests.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/tests.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/tests.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,48 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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 unittest
+
+from z3c.bobopublisher.browser import BrowserPage
+
+from zope.testing import doctest, doctestunit
+
+
+docfiles = [
+ 'README.txt',
+]
+
+doctests = [
+ 'z3c.bobopublisher.absoluteurl',
+]
+
+
+def test_suite():
+ tests = []
+ for d in doctests:
+ tests.append(doctestunit.DocTestSuite(d, optionflags=doctest.ELLIPSIS))
+ for d in docfiles:
+ tests.append(doctest.DocFileSuite(d, optionflags=doctest.ELLIPSIS))
+ return unittest.TestSuite(tests)
+
+
+class TestBrowserPage(BrowserPage):
+ """Test browser page"""
+
+ def __call__(self):
+ return u'TEST PAGE'
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/tests.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c/bobopublisher/traversing.py
===================================================================
--- z3c.bobopublisher/trunk/src/z3c/bobopublisher/traversing.py (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c/bobopublisher/traversing.py 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2009 Fabio Tranchitella <fabio at tranchitella.it>
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (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$
+"""
+
+from z3c.bobopublisher.interfaces import IPublishTraverse
+
+from zope.component import adapts, queryMultiAdapter
+from zope.interface import Interface, implements
+from zope.interface.common.mapping import IReadMapping
+
+
+class PublishTraverse(object):
+ """Generic PublishTraverse adapter"""
+
+ implements(IPublishTraverse)
+
+ adapts(Interface)
+
+ def __init__(self, context):
+ self.context = context
+
+ def publishTraverse(self, request, name):
+ view = queryMultiAdapter((self.context, request), name=name)
+ if view is not None:
+ return view
+ raise KeyError, name
+
+
+class PublishTraverseMapping(PublishTraverse):
+ """Generic PublishTraverse adapter for mappings"""
+
+ adapts(IReadMapping)
+
+ def publishTraverse(self, request, name):
+ obj = self.context.get(name, None)
+ if obj is not None:
+ return obj
+ return super(PublishTraverseMapping, self).publishTraverse(request, name)
Property changes on: z3c.bobopublisher/trunk/src/z3c/bobopublisher/traversing.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/PKG-INFO
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/PKG-INFO (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/PKG-INFO 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,298 @@
+Metadata-Version: 1.0
+Name: z3c.bobopublisher
+Version: 0.0.1dev
+Summary: Minimal reimplementation of the Zope publisher using the bobo framework.
+Home-page: http://pypi.python.org/pypi/z3c.bobopublisher
+Author: Fabio Tranchitella
+Author-email: fabio at tranchitella.it
+License: ZPL 2.1
+Description: z3c.bobopublisher
+ =================
+
+ This package provides a minimalistic reimplementation of the Zope publisher and
+ publication machinery using the bobo framework. It can be used as a simple
+ alternative for the following packages:
+
+ * zope.app.publication
+ * zope.app.publisher
+ * zope.publisher
+ * zope.traversing
+
+
+ The application
+ ---------------
+
+ Let's create a sample application we can use to test bobopublisher:
+
+ >>> from webtest import TestApp
+ >>> from z3c.bobopublisher.application import Application
+ >>> testapp = TestApp(Application())
+
+ Each request is mapped by bobo to a callable which will provide the response
+ for the client; if no specific bobo route matches the request's URL, the
+ bobopublisher Application object will try to traverse the URL components
+ starting from a root object.
+
+
+ The root object
+ ---------------
+
+ The root object must be registered as an utility which provides the interface
+ zope.location.interfaces.IRoot:
+
+ >>> testapp.get('/')
+ Traceback (most recent call last):
+ ...
+ ComponentLookupError: (<InterfaceClass zope.location.interfaces.IRoot>, '')
+
+ We register a dummy root object with a single hard-coded sub-item for our
+ testing purposes:
+
+ >>> from zope.component import adapts, getGlobalSiteManager
+ >>> from zope.interface import implements, Interface
+ >>> from zope.interface.common.mapping import IReadMapping
+ >>> from zope.location.interfaces import IRoot
+ >>> class Root(object):
+ ... implements(IRoot, IReadMapping)
+ ... def get(self, name, default=None):
+ ... return name == u'subitem' and SubItem() or default
+ >>> class ISubItem(Interface):
+ ... pass
+ >>> class SubItem(object):
+ ... implements(ISubItem)
+ >>> getGlobalSiteManager().registerUtility(Root(), IRoot)
+
+
+ PublishTraverse adapters
+ ------------------------
+
+ Now the application can find the root object, but we did not register any
+ PublishTraverse adapter, so every request will raise an exception:
+
+ >>> testapp.get('/')
+ Traceback (most recent call last):
+ ...
+ TypeError: ('Could not adapt', <Root object at ...>, <InterfaceClass z3c...>)
+
+ z3c.bobopublisher provides two generic PublishTraverse adapters:
+
+ >>> from z3c.bobopublisher.traversing import PublishTraverse, PublishTraverseMapping
+ >>> getGlobalSiteManager().registerAdapter(PublishTraverse)
+ >>> getGlobalSiteManager().registerAdapter(PublishTraverseMapping)
+
+ It is now possible to perform a GET request on the test application:
+
+ >>> testapp.get('/', status=404).body
+ '...Not Found...'
+
+
+ Browser pages
+ -------------
+
+ Browser pages are named multi-adapters which adapt the context object and the
+ request; we register a browser page for the root object:
+
+ >>> from z3c.bobopublisher.interfaces import IRequest
+ >>> from zope.browser.interfaces import IBrowserView
+ >>> class BrowserPage(object):
+ ... adapts(IRoot, IRequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'ABC'
+ >>> getGlobalSiteManager().registerAdapter(BrowserPage, name='index.html')
+
+ Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/index.html', status=200).body
+ 'ABC'
+
+ It is also possible to register a page for a specific HTTP method:
+
+ >>> from z3c.bobopublisher.interfaces import IDELETERequest
+ >>> class BrowserPageDelete(object):
+ ... adapts(IRoot, IDELETERequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'DELETE'
+ >>> getGlobalSiteManager().registerAdapter(BrowserPageDelete, name='index.html')
+
+ Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.delete('/index.html', status=200).body
+ 'DELETE'
+
+
+ Traversing and locations
+ ------------------------
+
+ After traversing an object, z3c.bobopublisher will locate the object setting
+ the parent and the name; if the traversed object doesn't provide the interface
+ ILocation it will be wrapped inside a location proxy.
+
+ If we traverse to the sub-item, we don't have any page defined:
+
+ >>> testapp.get('/subitem/index.html', status=404).body
+ '...Not Found...'
+
+ We register a browser page for the sub-item:
+
+ >>> from z3c.bobopublisher.interfaces import IRequest
+ >>> from zope.browser.interfaces import IBrowserView
+ >>> class SubItemBrowserPage(object):
+ ... adapts(ISubItem, IRequest)
+ ... implements(IBrowserView)
+ ... def __init__(self, context, request):
+ ... self.context = context
+ ... self.request = request
+ ... def __call__(self):
+ ... return u'XYZ: %s' % repr(self.context.__parent__)
+ >>> getGlobalSiteManager().registerAdapter(SubItemBrowserPage, name='index.html')
+
+ Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/subitem/index.html', status=200).body
+ 'XYZ: <Root object at ...>'
+
+
+ Object proxies
+ --------------
+
+ z3c.bobopublisher is able to wrap published objects with a proxy; it provides a
+ filter middleware to configure the proxy factory:
+
+ [filter-app:proxy]
+ use = egg:z3c.bobopublisher#proxy
+ proxy = zope.security.checker.ProxyFactory
+ next = application
+
+ In this example, we configure a proxied application to use the ProxyFactory
+ from zope.security:
+
+ >>> from z3c.bobopublisher.middleware import ProxyMiddleware
+ >>> proxyapp = TestApp(ProxyMiddleware(Application(),
+ ... 'zope.security.checker.ProxyFactory'))
+
+ Using the test application, we are able to call the page and get its result:
+
+ >>> proxyapp.get('/subitem/index.html')
+ Traceback (most recent call last):
+ ....
+ ForbiddenAttribute: ('get', <Root object at ...>)
+
+
+ Defining browser pages with ZCML
+ --------------------------------
+
+ z3c.bobopublisher provides ZCML directives that can be used to define browser
+ pages and the name of the default page.
+
+ >>> from zope.configuration import config, xmlconfig
+ >>> context = config.ConfigurationMachine()
+ >>> xmlconfig.registerCommonDirectives(context)
+ >>> context = xmlconfig.string("""<?xml version="1.0"?>
+ ... <configure xmlns="http://namespaces.zope.org/bobo">
+ ... <include package="z3c.bobopublisher" file="meta.zcml" />
+ ... <page
+ ... name="something.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... class="z3c.bobopublisher.tests.TestBrowserPage"
+ ... />
+ ... <page
+ ... name="delete.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... class="z3c.bobopublisher.tests.TestBrowserPage"
+ ... methods="DELETE"
+ ... />
+ ... <defaultView
+ ... name="something.html"
+ ... for="zope.location.interfaces.IRoot"
+ ... />
+ ... </configure>
+ ... """, context=context, execute=True)
+
+ Using the test application, we are able to call the page and get its result:
+
+ >>> testapp.get('/something.html', status=200).body
+ 'TEST PAGE'
+
+ As shown above, it is possible to register browser pages for one or more
+ specific HTTP methods:
+
+ >>> testapp.delete('/delete.html', status=200).body
+ 'TEST PAGE'
+
+ >>> testapp.get('/delete.html', status=404).body
+ '...Not Found...'
+
+ We also registered 'something.html' as the default view name for the root
+ object:
+
+ >>> testapp.get('/', status=200).body
+ 'TEST PAGE'
+
+
+ Resources
+ ---------
+
+ z3c.bobopublisher provides a ZCML directive which can be used to publish static
+ resources from a directory in the filesystem:
+
+ >>> import os, tempfile
+ >>> tempdir = tempfile.mktemp()
+ >>> os.mkdir(tempdir)
+ >>> open(os.path.join(tempdir, 'resource.txt'), 'w').write('RESOURCE')
+
+ >>> context = xmlconfig.string("""<?xml version="1.0"?>
+ ... <configure xmlns="http://namespaces.zope.org/bobo">
+ ... <resources
+ ... name="images"
+ ... directory="%s"
+ ... />
+ ... </configure>
+ ... """ % tempdir, context=context, execute=True)
+
+ By the default resources are registered for the IRoot interface, as shown below:
+
+ >>> testapp.get('/images', status=302).body
+ 'See http://localhost/images/'
+
+ >>> testapp.get('/images/', status=200).body
+ '...resource.txt...'
+
+ >>> response = testapp.get('/images/resource.txt', status=200)
+ >>> response.content_type, response.charset, response.body
+ ('text/plain', 'UTF-8', 'RESOURCE')
+
+ >>> response.headers['Cache-Control']
+ 'public,max-age=86400'
+ >>> 'Expires' in response.headers
+ True
+ >>> 'Last-Modified' in response.headers
+ True
+
+ It is not possible to quit from the path of the resource directory:
+
+ >>> testapp.get('/images/../images/resource.txt', status=404).body
+ '...Not Found...'
+
+ We can remove now the temporary directory:
+
+ >>> os.unlink(os.path.join(tempdir, 'resource.txt'))
+ >>> os.rmdir(tempdir)
+
+
+ CHANGES
+ =======
+
+ 0.0.1 (unreleased)
+ ------------------
+
+ - First public release.
+
+Platform: UNKNOWN
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/SOURCES.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/SOURCES.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/SOURCES.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,29 @@
+CHANGES.txt
+TODO.txt
+bootstrap.py
+buildout.cfg
+setup.py
+src/z3c/__init__.py
+src/z3c.bobopublisher.egg-info/PKG-INFO
+src/z3c.bobopublisher.egg-info/SOURCES.txt
+src/z3c.bobopublisher.egg-info/dependency_links.txt
+src/z3c.bobopublisher.egg-info/entry_points.txt
+src/z3c.bobopublisher.egg-info/namespace_packages.txt
+src/z3c.bobopublisher.egg-info/not-zip-safe
+src/z3c.bobopublisher.egg-info/requires.txt
+src/z3c.bobopublisher.egg-info/top_level.txt
+src/z3c/bobopublisher/README.txt
+src/z3c/bobopublisher/__init__.py
+src/z3c/bobopublisher/absoluteurl.py
+src/z3c/bobopublisher/application.py
+src/z3c/bobopublisher/browser.py
+src/z3c/bobopublisher/configure.zcml
+src/z3c/bobopublisher/interfaces.py
+src/z3c/bobopublisher/meta.zcml
+src/z3c/bobopublisher/metaconfigure.py
+src/z3c/bobopublisher/metadirectives.py
+src/z3c/bobopublisher/middleware.py
+src/z3c/bobopublisher/publication.py
+src/z3c/bobopublisher/resources.py
+src/z3c/bobopublisher/tests.py
+src/z3c/bobopublisher/traversing.py
\ No newline at end of file
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/dependency_links.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/dependency_links.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/dependency_links.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1 @@
+
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/entry_points.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/entry_points.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/entry_points.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,6 @@
+
+ [paste.app_factory]
+ main = z3c.bobopublisher.application:Application
+ [paste.filter_app_factory]
+ proxy = z3c.bobopublisher.middleware:make_proxy_middleware
+
\ No newline at end of file
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/namespace_packages.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/namespace_packages.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/namespace_packages.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1 @@
+z3c
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/not-zip-safe
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/not-zip-safe (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/not-zip-safe 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1 @@
+
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/requires.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/requires.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/requires.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1,15 @@
+setuptools
+bobo
+WebOb
+zope.browser
+zope.component [zcml]
+zope.configuration
+zope.dottedname
+zope.interface
+zope.location
+zope.schema
+zope.security
+
+[test]
+webtest
+zope.testing
\ No newline at end of file
Added: z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/top_level.txt
===================================================================
--- z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/top_level.txt (rev 0)
+++ z3c.bobopublisher/trunk/src/z3c.bobopublisher.egg-info/top_level.txt 2009-08-15 15:20:10 UTC (rev 102811)
@@ -0,0 +1 @@
+z3c
More information about the Checkins
mailing list