Dashboard Widgets

Dashboard in Cantemo gives user a quick overview of their tasks, system status etc. Each user can customize their dashboard by adding dashboard widgets and configuring them. These widgets are implemented as plugins using the IDashboardWidget-interface.

When the dashboard loads, it loads the contents of each widget using AJAX. Widgets can request a refresh with JavaScript, which in practice mean that the dashboard code will reload the widget contents with a new AJAX request.

IDashboardWidget

class portal.generic.dashboard_interfaces.IDashboardWidget

An IDashboardWidget-plugin defines one type of widget that can be added one or more times in the dashboard, with separate settings for each instance.

Requires:
  • __init__ -method that defines: name for the plugin, configurable, and template_name

  • get_list_title() -static method

  • get_render_data() -static method

  • force_show_config() -static method, required only if self.configurable is True

  • get_config_form() -static method, required only if self.configurable is True

See below for detailed specifications of the methods.

__init__()
Must initialize:
  • self.name - Unique plugin name

  • self.configurable - Whether this widget can be configured (if True, get_config_form() and force_show_config() must be implemented)

  • self.template_name - Template for rendering this widget, can use default IDashboardWidget.default_template

default_template = 'dashboard/base_widget.html'

Default template for widget rendering

static force_show_config(settings, request)

Return True if the widget configuration form should be shown, even if normal rendering was requested. This method is not used if self.configurable is False.

See TextWidgetForcedConfig in example widgets for example usage.

Args:
  • settings - widget instance settings data from database

  • request - the current request

static get_config_form(settings, request)

Returns the configuration form of the widget. This method is not used if self.configurable is False.

Note that Django classes must be imported within this method, not during package initialisation (top of the Python file).

Example implementation value that shows a form with two fields, title and text:

@staticmethod
def get_config_form(settings, request):
    from django import forms

    class TextWidgetSettingsForm(forms.Form):
        title = forms.CharField(label='Widget title', max_length=100,
                                initial="Title")
        text = forms.CharField(label='Widget text', max_length=10000,
                               widget=forms.Textarea(attrs={'rows': 5}))
    return TextWidgetSettingsForm
Supported form field types:
  • django.forms.IntegerField

  • django.forms.CharField

  • django.forms.BooleanField

  • django.forms.ChoiceField

  • django.forms.EmailField

  • django.forms.FloatField

  • django.forms.NullBooleanField

  • django.forms.URLField

Args:
  • settings - widget instance settings data from database

  • request - the current request

static get_list_title()

Returns UI text for selecting this widget type.

static get_render_data(render_data, settings, request)

Returns a dictionary that is passed to the template to render this widget. This method is allowed to take a long time, the UI will show appropriate loading spinners.

For example:

  • Change render_data['title'] to set widget title based on settings or current request.

  • When using default template, set render_data['content'] to set text of the widget.

Args:
  • render_data is a dict with following default values
    • id – widget instance id

    • title – get_list_title()

  • settings - widget instance settings data from database

  • request - the current request

Returns:
  • A dictionary with template data

An example dashboard widget plugin that shows a text:

from portal.pluginbase.core import Plugin, implements
from portal.generic.dashboard_interfaces import IDashboardWidget

class HelloWidget(Plugin):
    implements(IDashboardWidget)

    def __init__(self):
        self.name = 'HelloWidget'
        self.plugin_guid = '2BFADB52-56B8-47B3-BA63-1383CD42F678'
        self.template_name = IDashboardWidget.default_template
        self.configurable = False
        # Note: Since configurable is False, this class does not need to implement
        # force_show_config() and get_config_form()

    @staticmethod
    def get_list_title():
        return "Hello widget"

    @staticmethod
    def get_render_data(render_data, settings, request):
        render_data['content'] = "Hello world!"
        return render_data

HelloWidget()

Dashboard Widget Templates and JavaScript

If a dashboard widget only wants to show textual content, it can just define render_data['content'] as in the example above. Custom widget layouts can be defined in templates, which must extent the template "dashboard/base_widget.html" and define their content in {% block widget_content %}.

Widgets can include JavaScript code in their templates directly inside a <script>-tag.

To request a refresh, a widget should call the cntmo.app.dashboard.reloadWidgetAfter()-function in JavaScript:

cntmo.app.dashboard.reloadWidgetAfter('{{ id }}', <time_in_milliseconds>);

There can be multiple instances of a single widget class visible at the same time. If animation or similar is user in JavaScript, all ids should use the widget instance id, available through {{ id }}.

Below is an example of a widget template that includes an animated <div> and refreshes at the given interval. Below python code references this from portal/plugins/corewidgets/templates/corewidgets/refresh_widget.html:

{% themeextends "dashboard/base_widget.html" %}
{% block widget_content %}
    {{ content }}
    <!-- Using an id here based on widgets id -- otherwise multiple
    refresh_widgets will mess with each other  -->
    <div id="timer-bar{{ id }}"
         style="background-color: #00b7ea; height: 1px; width: 100%"> </div>

    <!-- script below is loaded with the HTML and executed normally -->
    <script>
        // Ask to reload widget in given time, converted to milliseconds.
        cntmo.app.dashboard.reloadWidgetAfter('{{ id }}',
                {{ refresh_interval }}000);

        // Animate timer bar using jQuery UI. Called after a small timeout,
        // so that when adding this widget
        // for the first time the layout has finished before animation starts.
        setTimeout(function() {
            $("#timer-bar{{ id }}").hide("slide",
                    {direction: "left", easing: "linear"},
                    {{ refresh_interval }}000);
        }, 100);
    </script>
{% endblock widget_content %}

Below is the related Python code that should be put in a plugins.py-file:

from django.utils.translation import gettext as _

from portal.pluginbase.core import Plugin, implements
from portal.generic.dashboard_interfaces import IDashboardWidget

class RefreshWidget(Plugin):
    """
    Example of a widget that automatically refreshes every 10 seconds.

    Uses custom template which does JS reload using
    cntmo.app.dashboard.reloadWidgetAfter(id, time_ms)-function.
    """
    implements(IDashboardWidget)

    default_interval = 10

    def __init__(self):
        self.name = 'RefreshWidget'
        self.plugin_guid = '3622B952-E9C0-4429-BD00-65B6958322BF'
        self.template_name = 'corewidgets/refresh_widget.html'
        self.configurable = True

    @staticmethod
    def get_list_title():
        return _("Refresh widget")

    @staticmethod
    def get_render_data(render_data, settings, request):
        from datetime import datetime

        render_data['content'] = _("Time on server: {}").format(
            datetime.now().isoformat(' '))
        # Make sure refresh_interval is passed to template (use default
        # if user has not edited settings yet)
        if 'refresh_interval' in settings:
            render_data['refresh_interval'] = settings['refresh_interval']
        else:
            render_data['refresh_interval'] = RefreshWidget.default_interval
        return render_data

    @staticmethod
    def get_config_form(settings, request):
        # Django classes MUST be imported inside method, not when package
        # is initialized
        from django import forms

        class RefreshWidgetSettingsForm(forms.Form):
            refresh_interval = forms.IntegerField(
                label=_('Refresh interval (seconds):'),
                initial=RefreshWidget.default_interval, min_value=1)

        return RefreshWidgetSettingsForm

    @staticmethod
    def force_show_config(settings, request):
        return False

# Register widget plugin
RefreshWidget()

See the example dashboard widget plugins for more information and implementation examples.