Mustache templating

All new code must output content based on Mustache templates. The code is typically organized in 3 files:

  • The template

  • The presenter

  • The calling code (in a Controller for instance)

Example of template:

<h1>Hello</h1>

<p>Welcome to {{ my_title }}</p>
<!-- For readability, please note :                  -->
<!--   * the spaces between {{, variable name and }} -->
<!--   * the use of snake_case for variables         -->

Example of Presenter

declare(strict_types=1);

class Presenter
{
    public string $my_title;

    public function __construct()
    {
        $this->my_title = "My title";
    }
}

Example of calling code:

$renderer = TemplateRendererFactory::build()->getRenderer('/path/to/template/directory');

// Output content directly (to the browser for instance)
$renderer->renderToPage('template_name', new Presenter());

// Return the content for futur reuse
$string = $renderer->renderToString('template_name', new Presenter());

Note

For existing code, it’s acceptable to output content with “echo” to keep consistency.

Escaping

You should rely on Mustache {{ }} notation to benefit from automatic escaping.

If you need to put light formatting in you localised string, then you should escape beforehand and use {{{ }}} notation. As it produces a code that is less auditable (reviewer has to manually check if injections are not possible), the convention is to prefix the variable with purified_ and manually purify the variable in the presenter.

declare(strict_types=1);

class Presenter
{
    public string $purified_description;

    public function __construct()
    {
        $this->purified_description = Codendi_HTMLPurifier::instance()->purify(
            $GLOBALS['Language']->getText('key1', 'key2', 'https://example.com'),
            CODENDI_PURIFIER_LIGHT
        );
    }
}

// .tab file:
// key1    key2    This is the <b>description</b> you can put <a href="$1">light formatting</a>

// .mustache file:
// <p>{{{ purified_description }}}</p>

Secure forms against CSRF

All state-changing actions MUST be protected against CSRF vulnerabilities. In order to do that, a specific token must be added to your forms and verified before the execution of the action.

Example:

Controller.php:

declare(strict_types=1);

namespace Tuleap/CsrfExample;

use CSRFSynchronizerToken;
use TemplateRendererFactory;

class Controller
{
    public function display() : string
    {
        $csrf_token = CSRFSynchronizerToken(CSRF_EXAMPLE_BASE_URL . '/do_things');
        $presenter  = new Presenter($csrf_token);
        $renderer   = TemplateRendererFactory::build()->getRenderer(CSRF_EXAMPLE_TEMPLATE_DIR);

        $renderer->renderToPage('csrf-example', $presenter);
    }

    public function process() : void
    {
        $csrf_token = CSRFSynchronizerToken(CSRF_EXAMPLE_BASE_URL . '/do_things');
        $csrf_token->check();

        do_things();
    }
}

Presenter.php:

declare(strict_types=1);

namespace Tuleap/CsrfExample;

use CSRFSynchronizerToken;

class Presenter
{
     public CSRFSynchronizerToken $csrf_token;

    public function __construct(CSRFSynchronizerToken $csrf_token)
    {
        $this->csrf_token = $csrf_token;
    }
}

csrf-example.mustache:

<form method="post">
    {{# csrf_token }}
        {{> csrf_token_input }}
    {{/ csrf_token }}
    <input type="submit">
</form>

Note

For existing code rendering HTML without using templates, it can be acceptable to use the fetchHTMLInput method of the CSRFSynchronizerToken class.

Internationalization

See Internationalization for details.