Web Status
==========

.. _Jinja-Web-Templates:

Jinja Web Templates
~~~~~~~~~~~~~~~~~~~

Buildbot uses Jinja2 to render its web interface.  The authoritative source for
this templating engine is
`its own documentation <http://jinja.pocoo.org/2/documentation/>`_,
of course, but a few notes are in order for those who are
making only minor modifications.

Whitespace
++++++++++

Jinja directives are enclosed in ``{% .. %}``, and sometimes also have
dashes.  These dashes strip whitespace in the output.  For example:

.. code-block:: none

    {% for entry in entries %}
      <li>{{ entry }}</li>
    {% endfor %}

will produce output with too much whitespace:

.. code-block:: html

  <li>pigs</li>


  <li>cows</li>


But adding the dashes will collapse that whitespace completely:

.. code-block:: none

    {% for entry in entries -%}
      <li>{{ entry }}</li>
    {%- endfor %}

yields

.. code-block:: html

    <li>pigs</li><li>cows</li>

.. _Web-Authorization-Framework:
    
Web Authorization Framework
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Whenever any part of the web framework wants to perform some action on the
buildmaster, it should check the user's authorization first.

Always check authorization twice: once to decide whether to show the option to
the user (link, button, form, whatever); and once before actually performing
the action.

To check whether to display the option, you'll usually want to pass an authz
object to the Jinja template in your :class:`HtmlResource` subclass::

    def content(self, req, cxt):
        # ...
        cxt['authz'] = self.getAuthz(req)
        template = ...
        return template.render(**cxt)

and then determine whether to advertise the action in the template:

.. code-block:: none

    {{ if authz.advertiseAction('myNewTrick') }}
      <form action="{{ myNewTrick_url }}"> ...
    {{ endif }}

Actions can optionally require authentication, so use ``needAuthForm`` to
determine whether to require a 'username' and 'passwd' field in the generated
form.  These fields are usually generated by :meth:`authFormIfNeeded()`:

.. code-block:: none

    {{ authFormIfNeeded(authz, 'myNewTrick') }}

Once the POST request comes in, it's time to check authorization again.
This usually looks something like ::

    d = self.getAuthz(req).actionAllowed('myNewTrick', req, someExtraArg)
    wfd = defer.waitForDeferred(d)
    yield wfd
    res = wfd.getResult()
    if not res:
        yield Redirect(path_to_authfail(req))
        return

The ``someExtraArg`` is optional (it's handled with ``*args``, so you can
have several if you want), and is given to the user's authorization function.
For example, a build-related action should pass the build status, so that the
user's authorization function could ensure that devs can only operate on their
own builds.

Note that ``actionAllowed`` returns a ``Deferred`` instance, so you must wait
for the ``Deferred`` and yield the ``Redirect`` instead of returning it.

The available actions are described in :bb:status:`WebStatus`.


