11.5. View Helpers

View helpers are PHP functions that assist in generating markup. Some view helpers are required to enable Perforce Chronicle to operate correctly, and others are provided (or can be created) to assist with repetitive markup tasks. View helpers are used in view scripts and layouts. Place view helpers in the helpers folder, within the theme's folder.

For detailed information about view helpers, refer to the Zend Framework documentation.

[Note] Class Prefix

If you are creating a view helper for your theme, the class prefix is Theme_Helper_. For a helper called Foo, the full classname would be Theme_Helper_Foo.

11.5.1. Escaping Values for Presentation and Security

Chronicle provides three view helpers that escape values included in view scripts, both to ensure that the values do not mess up the presentation, and to prevent cross-site scripting (XSS) attacks. PHP developers should already be familar with the htmlspecialchars command that converts selected characters to their equivalent HTML entities. Chronicle's view helpers do a more thorough job of entity conversion, and take into account the current character encoding.

Values that are to be displayed to users should be handled as follows:

<?= $this->escape($value); ?>

Values that need to be included within HTML attributes should be handled as follows:

<a href="<?= $this->escapeAttr($url); ?>">A link</a>
[Warning] URL Validation

When you use escapeAttr for URLs, be aware that only escaping is performed. To fully protect against XSS attacks, validation of the URL must also be performed. Please see The OWASP XSS (Cross Site Scripting) Prevention Cheat Sheet for more details.

Values that need to be included within javascript code should be handled as follows:

<script type="text/javascript">var x='<?= $this->escapeJs($data); ?>';</script>

11.5.2. Composing URLs

To compose a URL that points to an asset within the theme's folder, you can use: <?= $this->theme()->getBaseUrl(). Here are a few examples:

<a href="<?= $this->theme()->getBaseUrl() ?>/binary_files/example.document">Download the example</a>

<img src="<?= $this->theme()->getBaseUrl() ?>/images/photo.jpg" alt="a photo">

To compose a URL that points to a Chronicle content entry, you can use the url view helper. For example, to produce a URL that targets the content module's index controller's view action, with the parameter id=123, include the following PHP code in your view script or layout:

<?= $this->url(array(
    'module'     => 'content',
    'controller' => 'index',
    'action'     => 'view',
    'id'         => '123'
)) ?>

This approach ignores any custom URL that may have been assigned to a content entry. The alternative approach, when your view script has a content entry object available (or fetches a particular one), is to produce its possibly-custom URL as follows:

<?
    $entry = P4Cms_Content::fetch(123);
    echo $this->escape($entry->getUri());
?>

If the content entry is a binary file, instead of producing a link to the content entry's web page, you might prefer to make the link download the content:

<a href="<?= $this->url(array(
    'module'     => 'content',
    'controller' => 'index',
    'action'     => 'download',
    'id'         => '123'
)) ?>">Download item #123</a>

<!-- or from an object, possibly with a custom URL -->

<a href="<? $entry = P4Cms_Content::fetch(123); echo $entry->getUri('download'); ?>">
Download item #123</a>

For the display of Image content entries (or that include an image), the code would be:

<img src="<?= $this->url(array(
    'module'     => 'content',
    'controller' => 'index',
    'action'     => 'image',
    'id'         => '123'
)) ?>/>

<!-- or from an object, possibly with a custom URL -->

<img src="<? $entry = P4Cms_Content::fetch(123); echo $entry->getUri('image'); ?>"/>

Additionally, images can be scaled on the server by adding additional parameter when generating the URL:

<img src="<?= $this->url(array(
    'module'     => 'content',
    'controller' => 'index',
    'action'     => 'image',
    'id'         => '123',
    'width'      => 90,
    'height'     => 72
)) ?>/>

<!-- or from an object -->

<img src="<? $entry = P4Cms_Content::fetch(123);
    echo $entry->getUri('image', array('width' => 90, 'height' => 72)); ?>"/>
[Note] Image Aspect Ratio, Constraints, and Sharpening

To preserve an image's aspect ratio, you can include just one of the dimension parameters, either width or height, and the other dimension is computed.

For example, if the original image's size is 5184x3456 pixels, if you specify a width of 90 pixels, the height of the resized image is 67 pixels. Alternatively, if you specify a height of 72 pixels, the width of the resized image is 108 pixels.

Sometimes, the image you are resizing is extremely wide or tall. For a very wide image when you only specify the desired height, the danger is that after resizing the image is much too wide for your layout. Here we want to constrain the image even further. In this case, you should add the parameter 'maxWidth' => xxx (xxx is the maximum width in pixels). Alternatively, for a very tall image when you only specify the desired width, you should add the parameter 'maxHeight' => yyy (yyy is the maximum height in pixels).

Finally, some large images can look blurry when resized to thumbnail size. In these cases, you should add the parameter 'sharpen' => 1. This applies a predetermined amount of sharpening to the thumbnail that can reduce the blurring.

11.5.3. Managing Regions

Regions specify rectangular areas on a web page where widgets can be displayed. Regions can be used in a layout or a view script. Wherever a region's markup is generated, the widgets configured to appear in that region also have their markup generated and included in the region's markup. To define a region, include the following PHP code in your layout:

<?= $this->region('region_name') ?>

The region_name is a unique name that you assign to distinguish between regions. For example, you might have regions named "header," "footer," and "sidebar," to represent the appropriate areas of the pages you are designing for your theme.

If widgets are configured for a region and you switch your site to use a different theme that contains a region with the same name, the widgets are displayed. If region names are unique to the theme, the only widgets initially displayed are default widgets that have been specified in the second theme's theme.ini file.

11.5.4. Navigation

Navigation in Chronicle uses an enhanced implementation of Zend_Navigation. For background information about terminology and navigation, refer to the Zend_Navigation documentation.

11.5.4.1. Rendering A Breadcrumb

A Breadcrumb is a navigational aid that displays the active selection within navigation. The following example shows how to activate the display of the breadcrumb. The code checks whether the "manage-toolbar" navigation exists and if so: fetches it, expands any dynamic sections and attempts to locate a page called Manage Content. If that page exists, the page is made active. Finally, the navigation is rendered as a breadcrumb.

<?php
if (P4Cms_Menu::exists('manage-toolbar')) {
    $menu = P4Cms_Menu::fetch('manage-toolbar');
    $expanded = $menu->getExpandedContainer();
    $page = $expanded->findBy('label', 'Manage Content');
    if ($page) {
        $page->setActive(true);
    }
    echo $this->breadcrumbs($expanded);
}
?>

The following example links the last item in the breadcrumb:

<?= $this->breadcrumbs($expanded)->setLinkLast(true) ?>

The following example displays an arrow as the separator between breadcrumbs:

<?= $this->breadcrumbs($expanded)->setSeparator(' -> ') ?>

Options can be combined; for example:

<?= $this->breadcrumbs($expanded)->setLinkLast(true)->setSeparator('--') ?>

For more information about breadcrumb rendering, see the Zend Framework documentation.

11.5.5. Content List

A list of content in Chronicle can be displayed using the Content List view helper to query for content and provide information regarding display options. By default, the view helper shows the title of each content entry, wrapped in a link to the content page. For example, to show the latest 10 pieces of content sorted so the newest is first:

<?= $this->contentList(
    P4Cms_Record_Query::create()
            ->setMaxRows(10)
            ->setSortBy(P4Cms_Record_Query::SORT_DATE)
            ->setReverseOrder(true)
);
>

An additional array of options can be passed to the view helper, allowing for more granular control of the presentation of the content list. This array contains two parts: a list of fields, with filters and decorators to control their display, and an optional view script used to render the list. For more information on filters and decorators, refer to the Zend filter and decorator documentation. This example provides links to the 5 last uploaded files:

<?= $this->contentList(
        $this->view->query = P4Cms_Record_Query::create()
            ->setMaxRows($this->getOption('count'))
            ->setSortBy(P4Cms_Record_Query::SORT_DATE)
            ->setReverseOrder(true)
            ->setFilter(P4Cms_Record_Filter::create()->add('contentType', 'file')),
        array(
            'fields'    => array(
                'file'         => array(
                    'decorators'    => array(
                        'displayFileLink'
                    )
                )
            )
        )
    );
?>

This example uses an optional template to apply custom html. All fields are available to the template for use, and additional filters or decorators can be applied.

<?= $this->contentList(
        $this->view->query = P4Cms_Record_Query::create()
            ->setMaxRows($this->getOption('count'))
            ->setSortBy(P4Cms_Record_Query::SORT_DATE)
            ->setReverseOrder(true),
    array(
        'template' => 'foo.phtml'
    )
);
?>

The template file:

<? foreach ($this->entries as $entry) { ?>
    <div class = "myClass"><?= $entry->title ?></div>
<? } ?>

Options can be added to the P4Cms_Record_Filter object to allow other modules to influence the query. So far, only lucene search and categories are supported.

<?= $this->contentList(
        $this->view->query = P4Cms_Record_Query::create()
            ->setMaxRows($this->getOption('count'))
            ->setSortBy(P4Cms_Record_Query::SORT_DATE)
            ->setReverseOrder(true)
            ->setFilter(
                P4Cms_Record_Filter::create(
                    'lucene'     => 'search keywords',
                    'categories' => array('category1', 'category2')
                )
            )
        )
    );
?>
Perforce Chronicle - Release: 2012.2/486814