.. _hooks_chapter:
Using ZCML Hooks
================
ZCML "hooks" can be used to influence the behavior of the
:mod:`repoze.bfg` framework in various ways.
.. _changing_the_notfound_view:
Changing the Not Found View
---------------------------
When :mod:`repoze.bfg` can't map a URL to view code, it invokes a
notfound :term:`view`. The view it invokes can be customized by
placing something like the following ZCML in your ``configure.zcml``
file.
.. code-block:: xml
:linenos:
Replace ``helloworld.views.notfound_view`` with the Python dotted name
to the notfound view you want to use. Here's some sample code that
implements a minimal NotFound view:
.. code-block:: python
from webob.exc import HTTPNotFound
def notfound_view(context, request):
return HTTPNotFound()
.. note:: When a NotFound view is invoked, it is passed a request.
The ``environ`` attribute of the request is the WSGI environment.
Within the WSGI environ will be a key named ``repoze.bfg.message``
that has a value explaining why the not found error was raised.
This error will be different when the ``debug_notfound``
environment setting is true than it is when it is false.
Other available attributes of the ``notfound`` ZCML directive are as
follows:
attr
The attribute of the view callable to use if ``__call__`` is not
correct (has the same meaning as in the context of
:ref:`the_view_zcml_directive`; see the description of ``attr``
there).
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
renderer
This is either a single string term (e.g. ``json``) or a string
implying a path or :term:`resource specification`
(e.g. ``templates/views.pt``) used when the view returns a
non-:term:`response` object. This attribute has the same meaning as
it would in the context of :ref:`the_view_zcml_directive`; see the
description of ``renderer`` there).
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
wrapper
The :term:`view name` (*not* an object dotted name) of another view
declared elsewhere in ZCML (or via the ``@bfg_view`` decorator)
which will receive the response body of this view as the
``request.wrapped_body`` attribute of its own request, and the
response returned by this view as the ``request.wrapped_response``
attribute of its own request. This attribute has the same meaning
as it would in the context of :ref:`the_view_zcml_directive`; see
the description of ``wrapper`` there). Note that the wrapper view
*should not* be protected by any permission; behavior is undefined
if it does.
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
.. _changing_the_forbidden_view:
Changing the Forbidden View
---------------------------
When :mod:`repoze.bfg` can't authorize execution of a view based on
the authorization policy in use, it invokes a "forbidden view". The
default forbidden response has a 401 status code and is very plain,
but it can be overridden as necessary by placing something like the
following ZCML in your ``configure.zcml`` file.
.. code-block:: xml
:linenos:
Replace ``helloworld.views.forbidden_view`` with the Python
dotted name to the forbidden view you want to use. Like any other
view, the forbidden view must accept two parameters: ``context`` and
``request`` . The ``context`` is the context found by the router when
the view invocation was denied. The ``request`` is the current
:term:`request` representing the denied action. Here's some sample
code that implements a minimal forbidden view:
.. code-block:: python
from repoze.bfg.chameleon_zpt import render_template_to_response
def forbidden_view(context, request):
return render_template_to_response('templates/login_form.pt')
.. note:: When an forbidden view is invoked, it is passed
the request as the second argument. An attribute of the request is
``environ``, which is the WSGI environment. Within the WSGI
environ will be a key named ``repoze.bfg.message`` that has a value
explaining why the current view invocation was forbidden. This
error will be different when the ``debug_authorization``
environment setting is true than it is when it is false.
.. warning:: the default forbidden view sends a response with a ``401
Unauthorized`` status code for backwards compatibility reasons.
You can influence the status code of Forbidden responses by using
an alternate forbidden view. For example, it would make sense to
return a response with a ``403 Forbidden`` status code.
Other available attributes of the ``forbidden`` ZCML directive are as
follows:
attr
The attribute of the view callable to use if ``__call__`` is not
correct (has the same meaning as in the context of
:ref:`the_view_zcml_directive`; see the description of ``attr``
there).
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
renderer
This is either a single string term (e.g. ``json``) or a string
implying a path or :term:`resource specification`
(e.g. ``templates/views.pt``) used when the view returns a
non-:term:`response` object. This attribute has the same meaning as
it would in the context of :ref:`the_view_zcml_directive`; see the
description of ``renderer`` there).
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
wrapper
The :term:`view name` (*not* an object dotted name) of another view
declared elsewhere in ZCML (or via the ``@bfg_view`` decorator)
which will receive the response body of this view as the
``request.wrapped_body`` attribute of its own request, and the
response returned by this view as the ``request.wrapped_response``
attribute of its own request. This attribute has the same meaning
as it would in the context of :ref:`the_view_zcml_directive`; see
the description of ``wrapper`` there). Note that the wrapper view
*should not* be protected by any permission; behavior is undefined
if it does.
.. note:: This feature is new as of :mod:`repoze.bfg` 1.1.
Changing the response factory
-----------------------------
You may change the class used as the "response factory" from within
the :mod:`repoze.bfg` ``chameleon_zpt``, ``chameleon_genshi``,
``chameleon_text`` (the ``render_template_to_response`` function used
within each) and other various places where a Response object is
constructed by :mod:`repoze.bfg`. The default "response factory" is
the class ``webob.Response``. You may change it by placing the
following ZCML in your ``configure.zcml`` file.
.. code-block:: xml
:linenos:
Replace ``helloworld.factories.response_factory`` with the Python
dotted name to the response factory you want to use. Here's some
sample code that implements a minimal response factory:
.. code-block:: python
from webob import Response
class MyResponse(Response):
pass
def response_factory():
return MyResponse
Unlike a request factory, a response factory does not need to return
an object that implements any particular interface; it simply needs
have a ``status`` attribute, a ``headerlist`` attribute, and and
``app_iter`` attribute.