Skip to content

Widgets

"Widget" is the main term we use for all placeholders, blocks and callbacks available in templates. They may be available globally ("global widgets") or in one (or more) specific application templates or within other widgets. Global widgets are always available everywhere (excluding non-parsed files like CSS or JS files) including other widgets.

This article describes global widgets. Non-global widgets are described in Template System.

How to create/register a global widget

Any component can register any number of global widgets. Their names need to be unique across all components. Global widgets are registered in postInit() hook and are handled by the Widget class of the Widget component.
Here's a simple widget registration example:

1
2
3
4
5
6
7
8
9
public function postInit(\Cx\Core\Core\Controller\Cx $cx) {
    $this->getComponent('Widget')->registerWidget(
        new \Cx\Core_Modules\Widget\Model\Entity\FinalStringWidget(
            $this,
            'MY_FIRST_WIDGET',
            'Hello world'
        )
    );
}

This registers a global widget that replaces the placeholder [[MY_SIMPLE_WIDGET]] by the string Hello world.

Global widget types

The same types as for non-global widgets are available. The widget type can be specified as an argument to the constructor of the widget class. Note that not all widget classes support all types. See Global widget Classes for more info about this.

Type

Description

Template-Format

Identifier

Placeholder

A widget of type placeholder is simply replaced by some content.

Placeholder widget names are written in uppercase. In the template it has the following format:

[[MY_PLACEHOLDER_WIDGET]]

\Cx\Core_Modules\Widget\Model\Entity\Widget::TYPE_PLACEHOLDER

Block

A widget of type block has content, which can be altered while parsing the widget. The block is then replaced with the parsed content.

Block widget names are written in lowercase. In the template it has the following format:

<!-- START my_block_widget -->
    any content
<!-- END my_block_widget -->

Everything between the start and the end tag is passed to the widget for parsing.

\Cx\Core_Modules\Widget\Model\Entity\Widget::TYPE_BLOCK

Callback

A widget of type callback is like a placeholder with arguments.

Callback widget names are written in lowercase. In the template it has the following format:

func_my_callback_widget('foo', 'bar')

'foo' and 'bar' are passed to the widget render method as arguments.

The widget name is passed in uppercase to the rendering method as it is internally parsed as a block widget.

\Cx\Core_Modules\Widget\Model\Entity\Widget::TYPE_CALLBACK

Global widget classes

Class Description Supported Types
FinalStringWidget Use for static content Placeholder
EsiWidget Use for dynamic content Placeholder / Block / Callback
RandomEsiWidget Use for randomized content Placeholder / Block / Callback

FinalStringWidget

FinalStringWidgets can only be used for things that have no dependency to anything other than things that flush the whole page and ESI cache. Examples for this would be:

  • [[CHARSET]]: This should never change on a live system.
  • [[PATH_OFFSET]]: This should never change on a live system.
  • [[TIME]], [[DATE_...]]: These are aliases to non-cached ESI functions like $strftime('%Y').
  • [[CONTACT_...]]: These are part of base config.

EsiWidget

In order to circumvent the cache limitations of a FinalStringWidget there are ESI widgets. ESI widgets are cached separately. Each ESI widget has its own cache lease time and its cache can be flushed individually (see Cache control).

This is achieved by replacing ESI widgets (resp. their template-notation) by ESI element tags. The ESI element tags are then (before the response is sent back to the client) being replaced (by a reverse proxy or Cloudrexx itself) by the response of an Exposed method.

You can register your own EsiWidget along with any other widget class in postInit() of your ComponentController. You'll then have to implement and register a new controller (see Add another controller) that inherits from \Cx\Core_Modules\Widget\Controller\EsiWidgetController. Your own EsiWidgetController must implement the method parseWidget() which will then be used to fetch the content of your ESI widget.

For more info about the ESI specification see https://www.w3.org/TR/esi-lang.

For more info about ESI widgets in Cloudrexx see ESI widgets.

RandomEsiWidget

If one or more entries of a list should be shown, randomly changing for each request, then RandomEsiWidget is the way to go. It basically works the same as a normal ESI widget, with the following differences:

  • Use setUniqueRepetitionCount() to set the number of entries to show (default is 1)
  • Implement \Cx\Core_Modules\Widget\Controller\RandomEsiWidgetController instead of \Cx\Core_Modules\Widget\Controller\EsiWidgetController
  • Use RandomEsiWidgetController::getRandomEsiWidgetContentInfos($widgetName, $params, $template) to return the list of available entries

ESI widgets

ESI widgets are replaced by ESI tags. The reverse proxy then replaces them separately even when the request is answered from cache. Cloudrexx comes with an internal ESI parser that can replace the need for an external reverse proxy.

Parse Targets

Widgets are parsed in the scope of so-called Parse Targets. A Parse Target represents a website element that contains HTML-code as well as Widgets. Currently the following parse targets exist:

Parse Target Description
\Cx\Core\View\Model\Entity\Theme A functional HTML file of a webdesign template. Technically this means index.html of a theme as all other files are included using widgets.
\Cx\Core\ContentManager\Model\Entity\Page Any page managed by the Content Manager.
\Cx\Modules\Block\Model\Entity\Block Any content pane.

Implement your own Parse Target

To add your own parse target (i.e. an entity of your own component), your entity has to implement \Cx\Core_Module\Widget\Model\Entity\WidgetParseTarget.

Then you could parse any widgets within your own parse target as follows:

1
2
3
4
5
6
$this->cx->getComponent('Widget')->parseWidgets(
    $template,          // Sigma template to parse 
    $this->getName(),   // Name of the component your parse target belongs to
    '<NameOfEntity>',   // Name of your entity that is being used as parse target
    $id                 // The resolved identifier of your entity that has been requested
);

Cache control

If SSI/ESI-Caching is enabled or an external caching reverse proxy is used to parse ESI, then the content of ESI widgets and Random ESI widgets is being cached.

See Caching strategy as a guide on how to handle the cache of your widget.

Lease time

A cache lease time per ESI widget instructs the reverse proxy to only cache the widget for a certain amount of time.

The default lease time for ESI widget equals the page cache lease time. The default cache lease time can be overwritten for each widget in EsiWidgetController::parseWidget() by setting an expiration date on the response:

$response->setExpirationDate(new \DateTime('+1hour')); // sets a maximum lease time of one hour

Manually clear cache

The cache of one or more ESI widgets can be cleared manually by calling:

$this->cx->getEvents()->triggerEvent(
    'clearEsiCache',
    array(
        'Widget',
        array(
            'MY_FIRST_WIDGET',
            'MY_SECOND_WIDGET',
        ),
    )
);

Automatic cache management

If a widget depends on an element that is being changed through Cloudrexx, Cloudrexx drops the respective cache automatically.
Widgets always depend on their parse target. Additionally, you may specify other things your widget depends on. This is done using widget cache variables. All necessary variables should be selected to ensure all necessary data is available for parsing and the associated cache pages are dropped whenever necessary. The less variables are selected, the more efficient the widget can be cached. Therefore only choose the necessary variables.
The data corresponding to the selected cache variables is passed as $params[<key>] to the EsiWidgetController::parseWidget($name, $template, $response, $params) method. Only data of selected variables is passed.

ESI cache variable EsiWidget constant Description Datatype Description Short name 1
page ESI_VAR_ID_PAGE Widget depends on current page \Cx\Core\ContentManager\Model\Entity\Page The resolved page the widget is located on p
locale ESI_VAR_ID_LOCALE Widget depends on current locale \Cx\Core\Locale\Model\Entity\Locale The locale of the resolved page l
path ESI_VAR_ID_PATH Widget depends on current path string The path of the requested resource pa
query ESI_VAR_ID_QUERY Widget depends on current URL query arguments array The query string arguments of the requested resource q
theme ESI_VAR_ID_THEME Widget depends on current theme \Cx\Core\View\Model\Entity\Theme The resolved theme t
channel ESI_VAR_ID_CHANNEL Widget depends on current channel string The resolved channel ch
user ESI_VAR_ID_USER Widget depends on session string 2 The session-ID of the request u
currency ESI_VAR_ID_CURRENCY Widget depends on current currency string 2 The currently set currency code (i.e. EUR) c
country ESI_VAR_ID_COUNTRY Widget depends on current country string 2 The country code the current request (/its IP) originates from g

ESI widget variables can be set on the EsiWidget instance by using setEsiVariables() or setEsiVariable(). \Cx\Core_Modules\Widget\Model\Entity\EsiWidget provides the appropriate constants which can be combined using the bitwise OR operation.

Example

// create new block widget
$widget = new \Cx\Core_Modules\Widget\Model\Entity\EsiWidget(
    $this,
    'MY_FANCY_BLOCK_WIDGET',
    \Cx\Core_Modules\Widget\Model\Entity\Widget::TYPE_BLOCK
);

// make widget locale-dependent
// note: $widget->setEsiVariable() does append an additional ESI-variable to the
// widget (in contrary to $widget->setEsiVariables(), which does overwrite
// existing variables).
$widget->setEsiVariable(
    \Cx\Core_Modules\Widget\Model\Entity\EsiWidget::ESI_VAR_ID_PATH
);

// register block widget
$this->getComponent('Widget')->registerWidget($widget);
When does an ESI variable need to be set

There are certain cases where it's not obvious which variables need to be set. All of the variables' associated information can be fetched from the Referer URL and the cookies without the need for any ESI variables. The variables are necessary anyway as the main reason for them is to know when to drop the cache for the widget. Therefore the main criterion when deciding whether to set an ESI variable or not is whether the Widget's cache should be dropped if the associated information changes.

Keep the following in mind:

  • For setting the ESI cache variables the parse target does not matter. In other words it does not matter where your widget is parsed in. Either it depends on some data or not. Set all cache variables that represent data your widget needs to parse itself.
  • If a widget depends on the current page it most likely also depends on locale as the cache gets invalid when the locale changes.

First of all this is not a FinalStringWidget because it depends on the page which is not static (/final). It has the following ESI cache variables set:

  • page: If the page changes the page's slug changes and the cache becomes invalid.
  • locale: If the locale changes the virtual language directory changes and the cache becomes invalid.
  • path: As components may resolve additional path parts the path could lead to a different canonical URL.
  • query: Components may display different content (i.e. paging) based on URL arguments which would lead to a different canonical URL.
  • user: Components may display different content based on the user. If a user has no access to the page the canonical URL may be empty.

It does however not depend on the following:

  • theme: The canonical URL does not change depending on the theme.
  • channel: The canonical URL does not change depending on the channel.
  • currency: The canonical URL does not change depending on the channel as in our current implementation the currency is never part of the URL.
  • country: The canonical URL does not change depending on the channel as the country is not part of the URL outside of the locale.

Caching strategy

  • Use automatic caching by setting the correct ESI caching variables.
  • If your widget depends on time (birthdays, scheduled publishing, ...) additionally set an appropriate lease time.
  • If your widget depends on one or more entity (that is not available as a ESI cache variable) manually drop the widget's cache in a model event listener.

ESI and SSI

This article only mentioned ESI so far. The same result can also be achieved using the SSI specification. If your reverse proxy supports SSI instead of ESI Cloudrexx can also output SSI- instead of ESI-tags. The usage of everything else stays the same: Use the EsiWidget, RandomEsiWidget and EsiWidgetController classes as described above. Cloudrexx will use the correct notation as set in the caching settings automatically.


  1. Used for cache file name URL 

  2. This will be updated to return the object representation instead of a string