API:Writing Dashboard Widgets
Widgets are mini-applications that run within the dashboard in. Users can select and freely arrange widgets on their dashboard to create a custom system monitoring and management interface uniquely suited to their needs.
The functionality that can be encapsulated in a widget is only limited by space – they must fit within a single dashboard column (approximately 350 pixels wide). While widgets will typically provide simple, focused functionality they have full access to the CollectiveAccess database and programming APIs as well as their own configuration files and views and can interact with other CollectiveAccess plugins and external systems. See the Dashboard for a list of widgets included in the standard installation of CA.
Widgets are similar in structure to Application plugins. Each widget has a directory in app/widgets. The name of this directory must be the name of the widget (for example Clock). Within this directory must be a PHP class with your widget's name and the suffix Widget.php. For our clock example, the class is named ClockWidget.php'.
Your widget must be comprised of a widget class and at least one view (named, by convention, main_html.php). It can also have, if required, additional views, graphics and configuration files. By convention these are located in directories within the widget directory with the following layout (following our 'Clock' example):
app/widgets/Clock app/widgets/Clock/ClockWidget.php [widget class] app/widgets/Clock/conf [directory containing widget's configuration file(s)] app/widgets/Clock/views [directory containing views for the widget; you must define at least one view] app/widgets/Clock/graphics [directory containing graphic elements; only needed if the widget needs its own graphics]
The Widget Class
The widget class (ClockWidget.php in the clock example) must define two methods, two properties and a single data structure. The required methods are:
- checkStatus() - returns information about the widget and whether it is available for use or not. The return value for checkStatus() is an array with four keys:
- description : a description of the widget
- errors : an array of text error messages relating to the initialization of the widget. This should be an empty array if there are no errors. If the widget is not available the reason why should be expressed in the errors array.
- warnings : an array of text warning messages relating to the initialization of the widget. Should be a list of warnings about anything that will limit the functionality of the widget. Should be an empty array if there are no warnings.
- available : set to true if widget is loaded and available for use, false if it cannot load for some reason.
If initialization of your widget fails, or for some reason the widget should not be available in the current context (eg. some requirement for running is not met) then you must return false for the available value.
- renderWidget - is called when the widget needs to be displayed. This is where most of your widget code will live. The method is passed several parameters:
- $ps_widget_id - A 32 character MD5 hash widget unique identifying the widget instance
- $pa_settings - An array of widget settings in a key-value format corresponding to the settings structure defined in the BaseWidget::$s_widget_settings entry for the widget (see below for further discussion)
Your widget must also set the $title and $description properties, inherited from the base widget class. The $title property should be set to a single line description of the widget. The $description property should be set to a short narrative description of the plugin. Both are intended for display to end-users and thus should be written in plain and accessible language.
If your widget defines per-user settings then your widget class must also define an entry in the BaseWidget::$s_widget_settings array. The entry should have an array key equal to the widget name + 'Widget' (eg. 'ClockWidget') and an array value that defines each setting. The format of the array is described in the next section. Note that per-user settings are distinct from any configuration file settings the widget may have. The latter are intended for definition of system-wide widget behavior by system administrators while the former are meant for on-the-fly configuration by widget users. Since a user may place multiple instances of a single widget on their dashboard at the same time, per-user settings also provide a means to differentiate the copies.
If your widget needs to load its own configuration files or do other initialization, then you will need to include a constructor in your class. The constructor is passed an absolute file path to the widget's directory, which is needed to load widget-specific configuration files (or anything else in the widget directory for that matter).
You are free to create other methods as needed if you wish, but these will only be called by code you write. CA will only invoke the getDescription() and renderWidget() methods.
Rendering widget content
The dashboard will call the renderWidget() method of each widget that needs display. Your renderWidget() method should perform all of the work needed to generate content for display and then render that content using a view in the widget views directory. Your widget must have at least one view, and may have more if needed. Your rendered view will be automatically passed, in addition to whatever you pass into the view in your own code, the following values:
|widget_id||A unique 32 character MD5 hash for the widget instance.|
|settings||An array of user settings for this instance of the widget. The keys of the array are setting codes as defined in the BaseWidget::$s_widget_settings entry for the widget.|
You can access these variables from within the view using $this->getVar('<variable name>'); from within the controller you can access the view via the $opo_view property. For example, to pass a value of your own into the view from within the controller you would use code like this:
$this->opo_view->setVar('long_description', 'This is my long description...');
After you are through passing values to the view you must render it using the view render() method. The view specification is a path relative to the root of the widget view directory. This means that to render a view called main_html.php (by convention the main view for a widget is so titled) you would use this code:
Note that the specification for the view to be rendered is simply the name of the view, because the view in question resides in the root of the widget's views directory. If it was in a sub-directory then a root-relative path would be required.
The BaseWidget::$s_widget_settings Array
Your widget can define settings to be set by end-users. These settings may be set at any time using a web interface built into the dashboard itself and are attached to a specific instance of the widget. That is, if there is more than one copy of the same widget on a users' dashboard the settings are attached to the widget that was clicked upon, not all copies of the widget.
The settings form for your widget is created by the dashboard, so there is no need to define a view for your settings. All you need to do is specify what settings are required by the widget and what kind of values those settings should take in the BaseWidget::$s_widget_settings static array.
To specify your widget settings you must define an entry in BaseWidget::$s_widget_settings whose key is the name of the widget + 'Widget' (eg. 'ClockWidget') and whose value is an array listing each setting. The settings array for the clock example looks like this:
BaseWidget::$s_widget_settings['ClockWidget'] = array( 'display_mode' => array( 'formatType' => FT_TEXT, 'displayType' => DT_SELECT, 'width' => 40, 'height' => 1, 'takesLocale' => false, 'default' => 'standard', 'options' => array( _t('Analog') => 'retro', _t('Digital') => 'standard' ), 'label' => _t('Display mode'), 'description' => _t('Determines how to display information when it exceeds the maximum length.') ), 'display_format' => array( 'formatType' => FT_TEXT, 'displayType' => DT_SELECT, 'width' => 40, 'height' => 1, 'takesLocale' => false, 'default' => 'h:i a', 'options' => array( _t('Yes') => 'h:i:s a', _t('No') => 'h:i a' ), 'label' => _t('Show seconds?'), 'description' => _t('Determines how to display information when it exceeds the maximum length.') ) );
Note that each setting in the settings list has an alphanumeric code that unique identifies the setting within the context of the widget. The formatType and displayType values for the setting determine the type of data stored and the form the editing element will take for it in the settings form. The constants used for these two values are the same as those used in model definitions, as defined in app/lib/core/BaseModel.php. The dashboard settings form generator only supports a subset of the full list of format and displayType values, including: FT_TEXT for formatType (only text values are currently allowed) and DT_FIELD, DT_SELECT and DT_CHECKBOXES for displayType.
The takesLocale value should be set to true if the setting needs to be customized for each supported cataloguing language, otherwise false. Some values, such as options are only required when using specific form editing elements, such as DT_SELECT (and HTML <select> drop-down menu). The 'default' value should be chosen with care since it will be used when the user has not yet set a value. This means that the defaults you specify will help determine what the widget looks like when it is first added to the dashboard.