summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.txt11
-rw-r--r--docs/narr/urldispatch.rst50
-rw-r--r--docs/narr/views.rst72
-rw-r--r--repoze/bfg/tests/test_zcml.py620
-rw-r--r--repoze/bfg/view.py30
-rw-r--r--repoze/bfg/zcml.py91
6 files changed, 743 insertions, 131 deletions
diff --git a/CHANGES.txt b/CHANGES.txt
index d072cd79c..d045d261a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,12 +1,23 @@
Next release
============
+Features
+--------
+
+- Add ``xhr``, ``accept``, and ``header`` view configuration
+ predicates to ZCML view declaration, ZCML route declaration, and
+ ``bfg_view`` decorator. See the ``Views`` narrative documentation
+ chapter for more information about these predicates.
+
Documentation
-------------
- Virtual hosting narrative docs chapter updated with info about
``mod_wsgi``.
+- Point all index URLs at the literal 1.1 index (this alpha cycle may
+ go on a while).
+
1.1a5 (2009-10-10)
==================
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index a43d5e125..7a2a184f7 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -215,6 +215,56 @@ view_renderer
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+view_xhr
+
+ Thie value should be either ``True`` or ``False``. If this value is
+ specified and is ``True``, the :term:`request` must possess an
+ ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header for this
+ view to be found and called. This is useful for detecting AJAX
+ requests issued from jQuery, Prototype and other Javascript
+ libraries.
+
+ This attribute can also be spelled as ``xhr``.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+view_accept
+
+ The value of this attribute represents a match query for one or more
+ mimetypes in the ``Accept`` HTTP request header. If this value is
+ specified, it must be in one of the following forms: a mimetype
+ match token in the form ``text/plain``, a wildcard mimetype match
+ token in the form ``text/*`` or a match-all wildcard mimetype match
+ token in the form ``*/*``. If any of the forms matches the
+ ``Accept`` header of the request, this predicate will be true.
+
+ This attribute can also be spelled as ``accept``.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+view_header
+
+ The value of this attribute represents an HTTP header name or a
+ header name/value pair. If the value contains a ``:`` (colon), it
+ will be considered a name/value pair (e.g. ``User-Agent:Mozilla/.*``
+ or ``Host:localhost``). The *value* of an attribute that represent
+ a name/value pair should be a regular expression. If the value does
+ not contain a colon, the entire value will be considered to be the
+ header name (e.g. ``If-Modified-Since``). If the value evaluates to
+ a header name only without a value, the header specified by the name
+ must be present in the request for this predicate to be true. If
+ the value evaluates to a header name/value pair, the header
+ specified by the name must be present in the request *and* the
+ regular expression specified as the value must match the header
+ value. Whether or not the value represents a header name or a
+ header name/value pair, the case of the header name is not
+ significant.
+
+ This attribute can also be spelled as ``header``.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+
The Matchdict
-------------
diff --git a/docs/narr/views.rst b/docs/narr/views.rst
index c8c779380..3979ec8f6 100644
--- a/docs/narr/views.rst
+++ b/docs/narr/views.rst
@@ -472,6 +472,49 @@ request_type
``request_method`` attribute instead for maximum forward
compatibility.
+xhr
+
+ Thie value should be either ``True`` or ``False``. If this value is
+ specified and is ``True``, the :term:`request` must possess an
+ ``HTTP_X_REQUESTED_WITH`` (aka ``X-Requested-With``) header that has
+ the value ``XMLHttpRequest`` for this view to be found and called.
+ This is useful for detecting AJAX requests issued from jQuery,
+ Prototype and other Javascript libraries.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+accept
+
+ The value of this attribute represents a match query for one or more
+ mimetypes in the ``Accept`` HTTP request header. If this value is
+ specified, it must be in one of the following forms: a mimetype
+ match token in the form ``text/plain``, a wildcard mimetype match
+ token in the form ``text/*`` or a match-all wildcard mimetype match
+ token in the form ``*/*``. If any of the forms matches the
+ ``Accept`` header of the request, this predicate will be true.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
+header
+
+ The value of this attribute represents an HTTP header name or a
+ header name/value pair. If the value contains a ``:`` (colon), it
+ will be considered a name/value pair (e.g. ``User-Agent:Mozilla/.*``
+ or ``Host:localhost``). The *value* of an attribute that represent
+ a name/value pair should be a regular expression. If the value does
+ not contain a colon, the entire value will be considered to be the
+ header name (e.g. ``If-Modified-Since``). If the value evaluates to
+ a header name only without a value, the header specified by the name
+ must be present in the request for this predicate to be true. If
+ the value evaluates to a header name/value pair, the header
+ specified by the name must be present in the request *and* the
+ regular expression specified as the value must match the header
+ value. Whether or not the value represents a header name or a
+ header name/value pair, the case of the header name is not
+ significant.
+
+ .. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
+
.. _mapping_views_to_urls_using_a_decorator_section:
View Configuration Using the ``@bfg_view`` Decorator
@@ -484,10 +527,11 @@ functions with URLs instead of using :term:`ZCML` for the same
purpose. ``repoze.bfg.view.bfg_view`` can be used to associate
``for``, ``name``, ``permission`` and ``request_method``,
``containment``, ``request_param`` and ``request_type``, ``attr``,
-``renderer``, and ``wrapper`` information -- as done via the
-equivalent ZCML -- with a function that acts as a :mod:`repoze.bfg`
-view. All ZCML attributes (save for the ``view`` attribute) are
-available in decorator form and mean precisely the same thing.
+``renderer``, ``wrapper``, ``xhr``, ``accept``, and ``header``
+information -- as done via the equivalent ZCML -- with a function that
+acts as a :mod:`repoze.bfg` view. All ZCML attributes (save for the
+``view`` attribute) are available in decorator form and mean precisely
+the same thing.
To make :mod:`repoze.bfg` process your ``@bfg_view`` declarations, you
*must* insert the following boilerplate into your application's
@@ -582,6 +626,26 @@ If ``containment`` is supplied, the view will be invoked only if a
location parent supplies the interface or class implied by the
provided value.
+If ``xhr`` is specified, it must be a boolean value. If the value is
+``True``, the view will only be invoked if the request's
+``X-Requested-With`` header has the value ``XMLHttpRequest``.
+
+If ``accept`` is specified, it must be a mimetype value. If
+``accept`` is specified, the view will only be invoked if the
+``Accept`` HTTP header matches the value requested. See the
+description of ``accept`` in :ref:`the_view_zcml_directive` for
+information about the allowable composition and matching behavior of
+this value.
+
+If ``header`` is specified, it must be a header name or a
+``headername:headervalue`` pair. If ``header`` is specified, and
+possesses a value the view will only be invoked if an HTTP header
+matches the value requested. If ``header`` is specified without a
+value (a bare header name only), the view will only be invoked if the
+HTTP header exists with any value in the request. See the description
+of ``header`` in :ref:`the_view_zcml_directive` for information about
+the allowable composition and matching behavior of this value.
+
View lookup ordering for views registered with the ``bfg_view``
decorator is the same as for those registered via ZCML. See
:ref:`view_lookup_ordering` for more information.
diff --git a/repoze/bfg/tests/test_zcml.py b/repoze/bfg/tests/test_zcml.py
index 7cbadd1e2..1f0fc44d8 100644
--- a/repoze/bfg/tests/test_zcml.py
+++ b/repoze/bfg/tests/test_zcml.py
@@ -40,7 +40,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -72,7 +72,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -109,7 +109,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -141,7 +141,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -181,7 +181,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -220,7 +220,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -260,7 +260,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -299,7 +299,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -350,7 +350,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -386,7 +386,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -415,7 +415,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IDummy, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -445,7 +445,8 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
- discrim = ('view', IFoo, '', Dummy, IView, None, None, None, None, None)
+ discrim = ('view', IFoo, '', Dummy, IView, None, None, None, None,
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -472,7 +473,7 @@ class TestViewDirective(unittest.TestCase):
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, 'GET', None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -502,7 +503,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(actions[0]['discriminator'], discrim)
register = actions[0]['callable']
register()
@@ -536,7 +537,7 @@ class TestViewDirective(unittest.TestCase):
factory = sm.getUtility(IRouteRequest, 'foo')
request_type = implementedBy(factory)
discrim = ('view', IFoo, '', request_type, IView, None, None, None,
- 'foo', None)
+ 'foo', None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
the_view = sm.adapters.lookup((IFoo, request_type), IView, name='')
request = factory({})
@@ -559,7 +560,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, 'POST', None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -586,7 +587,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, 'POST', None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -612,7 +613,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, 'abc', None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -639,7 +640,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, 'abc', None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -665,7 +666,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, 'abc', None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -692,7 +693,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, 'abc', None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -701,6 +702,235 @@ class TestViewDirective(unittest.TestCase):
request.params = {'abc':'456'}
self.assertRaises(NotFound, wrapper, None, request)
+ def test_with_xhr_true(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, xhr=True)
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, True, None, None)
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.is_xhr = True
+ self.assertEqual(wrapper(None, request), '123')
+
+ def test_with_xhr_false(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.exceptions import NotFound
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, xhr=True)
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, True, None, None)
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.is_xhr = False
+ self.assertRaises(NotFound, wrapper, None, request)
+
+ def test_with_header_badregex(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from zope.configuration.exceptions import ConfigurationError
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self.assertRaises(ConfigurationError, self._callFUT,
+ context, None, IFoo,
+ view=view, header='Host:a\\')
+
+ def test_with_header_noval_match(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, header='Host')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, None, 'Host')
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.headers = {'Host':'whatever'}
+ self.assertEqual(wrapper(None, request), '123')
+
+ def test_with_header_noval_nomatch(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.exceptions import NotFound
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, header='Host')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, None, 'Host')
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.headers = {'NotHost':'whatever'}
+ self.assertRaises(NotFound, wrapper, None, request)
+
+ def test_with_header_val_match(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, header=r'Host:\d')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, None, r'Host:\d')
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.headers = {'Host':'1'}
+ self.assertEqual(wrapper(None, request), '123')
+
+ def test_with_header_val_nomatch(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.exceptions import NotFound
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, header=r'Host:\d')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, None, r'Host:\d')
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.headers = {'Host':'abc'}
+ self.assertRaises(NotFound, wrapper, None, request)
+
+ def test_with_accept_match(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, accept='text/xml')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, 'text/xml', None)
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.accept = ['text/xml']
+ self.assertEqual(wrapper(None, request), '123')
+
+ def test_with_accept_nomatch(self):
+ from zope.component import getSiteManager
+ from zope.interface import Interface
+ from repoze.bfg.interfaces import IRequest
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.exceptions import NotFound
+ class IFoo(Interface):
+ pass
+ context = DummyContext()
+ view = lambda *arg: None
+ sm = getSiteManager()
+ def view(context, request):
+ return '123'
+ self._callFUT(context, None, IFoo, view=view, accept='text/xml')
+ actions = context.actions
+ self.assertEqual(len(actions), 1)
+ action = actions[0]
+ discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
+ None, False, 'text/xml', None)
+ self.assertEqual(action['discriminator'], discrim)
+ register = action['callable']
+ register()
+ wrapper = sm.adapters.lookup((IFoo, IRequest), IView, name='')
+ request = DummyRequest()
+ request.accept = ['text/html']
+ self.assertRaises(NotFound, wrapper, None, request)
+
def test_with_containment_true(self):
from zope.component import getSiteManager
from zope.interface import directlyProvides
@@ -720,7 +950,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, IFoo, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -748,7 +978,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, IFoo, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -776,7 +1006,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -911,7 +1141,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', IFoo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -938,7 +1168,7 @@ class TestViewDirective(unittest.TestCase):
self.assertEqual(len(actions), 1)
action = actions[0]
discrim = ('view', Foo, '', IRequest, IView, None, None, None, None,
- None)
+ None, False, None, None)
self.assertEqual(action['discriminator'], discrim)
register = action['callable']
register()
@@ -1318,17 +1548,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], None)
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
register = view_action['callable']
register()
sm = getSiteManager()
@@ -1367,17 +1589,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], IDummy)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], None)
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', IDummy, '', request_type, IView, None, None, None,
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
request = DummyRequest()
self.assertEqual(wrapped(None, request), '123')
@@ -1431,17 +1645,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], 'GET')
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, None, None, 'GET',
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -1476,17 +1682,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], 'GET')
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, None, None, 'GET',
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -1522,17 +1720,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], 'GET')
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, None, None, 'GET',
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -1567,17 +1757,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], None)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], 'GET')
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, None, None, 'GET',
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -1612,17 +1794,9 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], True)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], None)
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, True, None, None,
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -1657,17 +1831,232 @@ class TestRouteDirective(unittest.TestCase):
request_factory = sm.getUtility(IRouteRequest, 'name')
request_type = implementedBy(request_factory)
view_discriminator = view_action['discriminator']
- self.assertEqual(len(view_discriminator), 10)
- self.assertEqual(view_discriminator[0], 'view')
- self.assertEqual(view_discriminator[1], None)
- self.assertEqual(view_discriminator[2],'')
- self.assertEqual(view_discriminator[3], request_type)
- self.assertEqual(view_discriminator[4], IView)
- self.assertEqual(view_discriminator[5], True)
- self.assertEqual(view_discriminator[6], None)
- self.assertEqual(view_discriminator[7], None)
- self.assertEqual(view_discriminator[8], 'name')
- self.assertEqual(view_discriminator[9], None)
+ discrim = ('view', None, '', request_type, IView, True, None, None,
+ 'name', None, False, None, None)
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_header(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view, view_header='Host')
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, False, None, 'Host')
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_header_alias(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view, header='Host')
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, False, None, 'Host')
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_xhr(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view, view_xhr=True)
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, True, None, None)
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_xhr_alias(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view, xhr=True)
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, True, None, None)
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_accept(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view,
+ view_accept='text/xml')
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, False, 'text/xml', None)
+ self.assertEqual(view_discriminator, discrim)
+ wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
+ self.failUnless(wrapped)
+
+ route_action = actions[1]
+ route_callable = route_action['callable']
+ route_discriminator = route_action['discriminator']
+ route_args = route_action['args']
+ self.assertEqual(route_callable, connect_route)
+ self.assertEqual(len(route_discriminator), 2)
+ self.assertEqual(route_discriminator[0], 'route')
+ self.assertEqual(route_discriminator[1], 'name')
+ self.assertEqual(route_args, ('path', 'name', None))
+
+ def test_with_view_xhr_alias(self):
+ from zope.component import getSiteManager
+ from zope.interface import implementedBy
+ from repoze.bfg.zcml import connect_route
+ from repoze.bfg.interfaces import IView
+ from repoze.bfg.interfaces import IRouteRequest
+
+ context = DummyContext()
+ def view(context, request):
+ """ """
+ self._callFUT(context, 'name', 'path', view=view, accept='text/xml')
+ actions = context.actions
+ self.assertEqual(len(actions), 2)
+
+ view_action = actions[0]
+ register = view_action['callable']
+ register()
+ sm = getSiteManager()
+ request_factory = sm.getUtility(IRouteRequest, 'name')
+ request_type = implementedBy(request_factory)
+ view_discriminator = view_action['discriminator']
+ discrim = ('view', None, '', request_type, IView, None, None, None,
+ 'name', None, False, 'text/xml', None)
+ self.assertEqual(view_discriminator, discrim)
wrapped = sm.adapters.lookup((IDummy, request_type), IView, name='')
self.failUnless(wrapped)
@@ -2001,7 +2390,8 @@ class TestBFGViewGrokker(unittest.TestCase):
request_type=IRequest, route_name=None,
request_method=None, request_param=None,
containment=None, attr=None, renderer=None,
- wrapper_viewname=None)
+ wrapper_viewname=None, xhr=False, header=None,
+ accept=None)
obj.__bfg_view_settings__ = settings
context = DummyContext()
result = grokker.grok('name', obj, context=context)
diff --git a/repoze/bfg/view.py b/repoze/bfg/view.py
index dba0f99b9..d6c47ec0f 100644
--- a/repoze/bfg/view.py
+++ b/repoze/bfg/view.py
@@ -240,7 +240,8 @@ class bfg_view(object):
The following arguments are supported: ``for_``, ``permission``,
``name``, ``request_type``, ``route_name``, ``request_method``,
- ``request_param``, and ``containment``.
+ ``request_param``, ``containment``, ``xhr``, ``accept``, and
+ ``header``.
If ``for_`` is not supplied, the interface
``zope.interface.Interface`` (matching any context) is used.
@@ -295,6 +296,27 @@ class bfg_view(object):
denoting that the view 'matches' the current request only if any graph
lineage node possesses this class or interface.
+ If ``xhr`` is specified, it must be a boolean value. If the value
+ is ``True``, the view will only be invoked if the request's
+ ``X-Requested-With`` header has the value ``XMLHttpRequest``.
+
+ If ``accept`` is specified, it must be a mimetype value. If
+ ``accept`` is specified, the view will only be invoked if the
+ ``Accept`` HTTP header matches the value requested. See the
+ description of ``accept`` in :ref:`the_view_zcml_directive` for
+ information about the allowable composition and matching behavior
+ of this value.
+
+ If ``header`` is specified, it must be a header name or a
+ ``headername:headervalue`` pair. If ``header`` is specified, and
+ possesses a value the view will only be invoked if an HTTP header
+ matches the value requested. If ``header`` is specified without a
+ value (a bare header name only), the view will only be invoked if
+ the HTTP header exists with any value in the request. See the
+ description of ``header`` in :ref:`the_view_zcml_directive` for
+ information about the allowable composition and matching behavior
+ of this value.
+
Any individual or all parameters can be omitted. The simplest
bfg_view declaration then becomes::
@@ -354,7 +376,8 @@ class bfg_view(object):
"""
def __init__(self, name='', request_type=None, for_=None, permission=None,
route_name=None, request_method=None, request_param=None,
- containment=None, attr=None, renderer=None, wrapper=None):
+ containment=None, attr=None, renderer=None, wrapper=None,
+ xhr=False, accept=None, header=None):
self.name = name
self.request_type = request_type
self.for_ = for_
@@ -366,6 +389,9 @@ class bfg_view(object):
self.attr = attr
self.renderer = renderer
self.wrapper_viewname = wrapper
+ self.xhr = xhr
+ self.accept = accept
+ self.header = header
def __call__(self, wrapped):
wrapped.__bfg_view_settings__ = self.__dict__.copy()
diff --git a/repoze/bfg/zcml.py b/repoze/bfg/zcml.py
index adb1f1f31..b0f5a2595 100644
--- a/repoze/bfg/zcml.py
+++ b/repoze/bfg/zcml.py
@@ -1,3 +1,4 @@
+import re
import sys
from zope.configuration import xmlconfig
@@ -121,6 +122,26 @@ class IViewDirective(Interface):
'parameter exists which matches this string.'),
required=False)
+ xhr = Bool(
+ title = (u'True if request has an X-Requested-With header with the '
+ 'value "XMLHttpRequest"'),
+ description=(u'Useful for detecting AJAX requests issued from '
+ 'jQuery, Protoype and other JavaScript libraries'),
+ required=False)
+
+ accept = TextLine(
+ title = (u'Mimetype(s) that must be present in "Accept" HTTP header '
+ 'for the view to match a request'),
+ description=(u'Accepts a mimetype match token in the form '
+ '"text/plain", a wildcard mimetype match token in the '
+ 'form "text/*" or a match-all wildcard mimetype match '
+ 'token in the form "*/*".'),
+ required = False)
+
+ header = TextLine(
+ title=u'Header name/value pair in the form "name=<regex>"',
+ description=u'Regular expression matching for header values',
+ required = False)
def view(
_context,
@@ -136,6 +157,9 @@ def view(
attr=None,
renderer=None,
wrapper=None,
+ xhr=False,
+ accept=None,
+ header=None,
cacheable=True, # not used, here for b/w compat < 0.8
):
@@ -167,9 +191,6 @@ def view(
if isinstance(request_type, basestring):
request_type = _context.resolve(request_type)
- predicates = []
- weight = sys.maxint
-
# Predicates are added to the predicate list in (presumed)
# computation expense order. All predicates associated with a
# view must evaluate true for the view to "match" a request.
@@ -196,10 +217,19 @@ def view(
# Views which do not have any predicates get a score of
# sys.maxint, meaning that they will be tried very last.
+ predicates = []
+ weight = sys.maxint
+
+ if xhr:
+ def xhr_predicate(context, request):
+ return request.is_xhr
+ weight = weight - 10
+ predicates.append(xhr_predicate)
+
if request_method is not None:
def request_method_predicate(context, request):
return request.method == request_method
- weight = weight - 10
+ weight = weight - 20
predicates.append(request_method_predicate)
if request_param is not None:
@@ -210,13 +240,35 @@ def view(
if request_param_val is None:
return request_param in request.params
return request.params.get(request_param) == request_param_val
- weight = weight - 20
+ weight = weight - 30
predicates.append(request_param_predicate)
+ if header is not None:
+ header_name = header
+ header_val = None
+ if ':' in header:
+ header_name, header_val = header.split(':', 1)
+ try:
+ header_val = re.compile(header_val)
+ except re.error, why:
+ raise ConfigurationError(why[0])
+ def header_predicate(context, request):
+ if header_val is None:
+ return header_name in request.headers
+ val = request.headers.get(header_name)
+ return header_val.match(val) is not None
+ predicates.append(header_predicate)
+
+ if accept is not None:
+ def accept_predicate(context, request):
+ return accept in request.accept
+ weight = weight - 40
+ predicates.append(accept_predicate)
+
if containment is not None:
def containment_predicate(context, request):
return find_interface(context, containment) is not None
- weight = weight - 30
+ weight = weight - 50
predicates.append(containment_predicate)
# this will be == sys.maxint if no predicates
@@ -272,7 +324,8 @@ def view(
name, _context.info)
_context.action(
discriminator = ('view', for_, name, request_type, IView, containment,
- request_param, request_method, route_name, attr),
+ request_param, request_method, route_name, attr,
+ xhr, accept, header),
callable = register,
args = (),
)
@@ -482,6 +535,9 @@ class IRouteDirective(Interface):
required=False)
view_attr = TextLine(title=u'view_attr', required=False)
view_renderer = TextLine(title=u'view_renderer', required=False)
+ view_header = TextLine(title=u'view_header', required=False)
+ view_accept = TextLine(title=u'view_accept', required=False)
+ view_xhr = Bool(title=u'view_xhr', required=False)
# alias for "view_for"
for_ = GlobalObject(title=u'for', required=False)
# alias for "view_permission"
@@ -500,6 +556,12 @@ class IRouteDirective(Interface):
attr = TextLine(title=u'attr', required=False)
# alias for "view_renderer"
renderer = TextLine(title=u'renderer', required=False)
+ # alias for "view_header"
+ header = TextLine(title=u'header', required=False)
+ # alias for "view_accept"
+ accept = TextLine(title=u'accept', required=False)
+ # alias for "view_xhr"
+ xhr = Bool(title=u'xhr', required=False)
class IRouteRequirementDirective(Interface):
""" The interface for the ``requirement`` route subdirective """
@@ -513,7 +575,8 @@ def route(_context, name, path, view=None, view_for=None,
request_method=None, view_request_method=None,
request_param=None, view_request_param=None, containment=None,
view_containment=None, attr=None, view_attr=None, renderer=None,
- view_renderer=None):
+ view_renderer=None, header=None, view_header=None, accept=None,
+ view_accept=None, xhr=False, view_xhr=False):
""" Handle ``route`` ZCML directives
"""
# the strange ordering of the request kw args above is for b/w
@@ -526,6 +589,9 @@ def route(_context, name, path, view=None, view_for=None,
containment = view_containment or containment
attr = view_attr or attr
renderer = view_renderer or renderer
+ header = view_header or header
+ accept = view_accept or accept
+ xhr = view_xhr or xhr
sm = getSiteManager()
@@ -545,7 +611,8 @@ def route(_context, name, path, view=None, view_for=None,
_view(_context, permission=permission, for_=for_, view=view, name='',
request_type=request_type, route_name=name,
request_method=request_method, request_param=request_param,
- containment=containment, attr=attr, renderer=renderer)
+ containment=containment, attr=attr, renderer=renderer,
+ header=header, accept=accept, xhr=xhr)
_context.action(
discriminator = ('route', name),
@@ -641,12 +708,16 @@ class BFGViewGrokker(martian.InstanceGrokker):
wrapper = settings['wrapper_viewname']
attr = settings['attr']
renderer = settings['renderer']
+ xhr = settings['xhr']
+ accept = settings['accept']
+ header = settings['header']
context = kw['context']
view(context, permission=permission, for_=for_,
view=obj, name=name, request_type=request_type,
route_name=route_name, request_method=request_method,
request_param=request_param, containment=containment,
- attr=attr, renderer=renderer, wrapper=wrapper)
+ attr=attr, renderer=renderer, wrapper=wrapper,
+ xhr=xhr, accept=accept, header=header)
return True
return False