.. _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 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.
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 alterate forbidden view. For example, it would make sense to
return a response with a ``403 Forbidden`` status code.
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.
.. _overriding_resources_section:
Overriding Resources
--------------------
A ZCML directive exists named "resource". This ZCML directive allows
you to override Chameleon templates within a package (both directories
full of templates and individual template files) with other templates
in the same package or within another package. This allows you to
"fake out" a view's use of a template, causing it to retrieve a
different template than the one actually named by a relative path to a
call like ``render_template_to_response('templates/mytemplate.pt')``.
For example, you can override a template file by doing::
The string passed to "to_override" and "override_with" is named a
"specification". The colon separator in a specification separates the
package name from a package-relative directory name. The colon and
the following relative path are optional. If they are not specified,
the override attempts to resolve every lookup into a package from the
directory of another package. For example::
Individual subdirectories within a package can also be overridden::
If you wish to override a directory with another directory, you must
make sure to attach the slash to the end of both the ``to_override``
specification and the ``override_with`` specification. If you fail to
attach a slash to the end of a specification that points a directory,
you will get unexpected results. You cannot override a directory
specification with a file specification, and vice versa (a startup
error will occur if you try).
You cannot override a resource with itself (a startup error will
occur if you try).
Only individual *package* resources may be overridden. Overrides will
not traverse through subpackages within an overridden package. This
means that if you want to override resources for both
``some.package:templates``, and ``some.package.views:templates``, you
will need to register two overrides.
The package name in a specification may start with a dot, meaning that
the package is relative to the package in which the ZCML file resides.
For example::
Overrides for the same ``to_overrides`` specification can be named
multiple times within ZCML. Each ``override_with`` path will be
consulted in the order defined within ZCML, forming an override search
path.
Resource overrides can actually override resources other than
templates. Any software which uses the ``pkg_resources``
``get_resource_filename``, ``get_resource_stream`` or
``get_resource_string`` APIs will obtain an overridden file when an
override is used. However, the only built-in facility which uses the
``pkg_resources`` API within BFG is the templating stuff, so we only
call out template overrides here.