6. Widgets

As was mentioned in Extending WebApp, widgets appear in the Today context or the side panel. The user can add or remove them at will. A user can also add multiple instances of the same widget but with different parameters.

Having multiple instances of the same widget is made possible because each widget’s instance object has a unique identifier to keep multiple instances apart. As soon as you add a new widget to the Today context or the side panel, the widget receives a new identifier, a GUID, which will be used to store the widget’s state. This newly added instance of the widget can maintain his own settings; its settings are stored in the settings tree in the folder zarafa/v1/widgets/[GUID]. When a widget is removed from the Today context or the side panel, this folder is deleted.

6.1. Creating a widget

We will look at the widget’s architecture based on the Facebook Widget example. It shows the activity stream from a site that you can change in the widget’s configuration.

To create a custom widget, you need to do two simple steps:

  1. Create your own class that extends Zarafa.core.ui.widget.Widget;
  2. Register this widget class in the container.
Zarafa.widgets.FBWidget = Ext.extend(Zarafa.core.ui.widget.Widget, {
        // The widget's code goes here
});

Zarafa.onReady(function() {
        container.registerWidget(Zarafa.widgets.FBWidget,
        'fb',
        _('Facebook'),
        'plugins/facebookwidget/resources/_static/facebook.png');
});

6.2. Widget configuration

To allow the widget to do something, you need to add the custom functionality inside the class. In our example, we save the URL of the site which activity we want to monitor in the widget’s settings. To use them we need to initialize parameter hasConfig in constructor and set it to true. For example, here is the part of the constructor that tells this to the widget framework:

constructor : function(config) {
        config = config || {};
        Ext.applyIf(config, {
                name: 'fb',
                height: 600,
                hasConfig : true
        });
}

After this, the “gear” icon will appear in the right top corner of the widget (see e.g. “Widget settings”).

Widget settings

Widget settings

When you click on this icon, the config method of the widget will be called. In this method, we can setup the widget’s settings dialog and show it.

/**
 * Called when a user clicks the config button on the widget panel.
 * Shows the window with url field - where the user need to put
 * new value for Facebook Activity Site.
 */
config : function()
{
        var configWindow = new Ext.Window({
                title  : _('Configure widget'),
                layout : 'fit',
                width  : 350,
                height : 120,
                items : [{
                        xtype : 'form',
                        frame : true,
                        ref : 'formPanel',
                        labelWidth : 180,
                        items : [{
                                xtype: 'textfield',
                                anchor: '100%',
                                fieldLabel: _('Site name to track the activity'),
                                allowBlank : false,
                                vtype: 'url',
                                ref : '../siteUrlField',
                                name: 'site_url',
                                value: this.get('site_url')
                        }],
                buttons : [{
                        text : _('Save'),
                        scope : this,
                        ref : '../../savebutton',
                        handler : this.saveUserUrlToSettings
                        },
                        {
                        text: _('Cancel'),
                        scope : this,
                        ref : '../../cancelbutton',
                        handler : this.closeConfigWindow
                        }
                        ]
                }]
        });
        configWindow.show(this);
},

To save or receive the settings value we use this.set() and this.get() methods respectively. In our example, we get the default value of the “site url field” using this.get('site_url'). If it was not stored before with this.set('site_url'), it will return undefined. You will need to take care of this by yourself. That is why we use Ext.isEmpty check the value in the constructor:

var siteUrl = this.get('site_url');
if( Ext.isEmpty( siteUrl ) ) {
        siteUrl = this.defaultFbActivitySite;
        this.set('site_url', siteUrl);
}
this.setTitle( _('Facebook') + ' - ' + siteUrl);

Don’t forget to call parent constructor or the widget will not work; the parent class’ constructor puts it in the proper location and takes care of loading the settings, and so on.

Zarafa.widgets.FBWidget.superclass.constructor.call(this, config);

6.3. Events

As the Zarafa.core.ui.widget.Widget class extends the Ext.ux.Portlet and finally Ext.Panel, you can override some helpful methods to gain more consistency and readability inside your widget. One of these methods is initEvents, it will be called after the panel is rendered. It is a useful place in the code to initialize the widget’s events. But remember: each time you override the methods of the parent class, you need to call the superclass method explicitly.

initEvents : function()
{
        Zarafa.widgets.FBWidget.superclass.initEvents.apply(this, arguments);
        this.mon(this, 'bodyresize', this.reloadIframe, this);
},

Finally, another useful method is onRender, which is called after the component was rendered. You can use it to render your custom elements inside the widget. To check if the widget is visible in the current moment or not, you can use isWidgetVisible method. It will return true if the widget is visible and the widgetPanel, on which it resides, is not collapsed. When you click the “cross” (close) icon, then the widgetPanel, on which the widgets resides, will unregister the widget’s GUID and it will destroy the widget. It will then fire the destroy event. Therefore, you can define an onDestroy method to do some tasks after widget is destroyed.