[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