5. Insertion Points

The first thing plugin developers should really learn to start implementing new plugins are insertion points. An insertion point is a named location in the UI component hierarchy where plugins may add their own components. Insertion points are typically added to toolbars and context menus, and are intended to be easy hooks for adding new buttons, button groups or menuitems. The name of an insertion point is hierarchical, so most things specifically related to e-mail will start with context.mail and have a more precise indication of the location after that.

Note, that work with insertion points is implemented as a call to the container, which collects UI components from registered plugins and returns them. You can easily create your custom insertion points. See the listing below:

var toolbar = new Ext.Toolbar(
{
        items : [
                // Fixed items, always present
                {
                        iconCls: 'icon_new'
                },
                {
                        iconCls: 'icon_delete'
                },

                // Create insertion point 'example.toolbar'
                container.populateInsertionPoint('example.toolbar')
        ]
});

And now you have created a new insertion point, which can be used in future. By design, the names of the insertion points should reflect the structure of the application. Therefore, the proposed naming scheme follows a hierarchy separated by dots (.). More information and recommendations on the naming conventions are given in Appendix A: Naming Conventions.

In some cases, it’s useful to have insertion points that provide extra information to the plugin. An example is an insertion point in a context menu, where it’s useful to pass the menu object (Ext.menu.Menu) to the creation function. Yet another example is a toolbar in a read mail dialog, which might pass the entry ID of the item that is being shown to the plugin.

container.populateInsertionPoint('dialog.readmail.toolbar', mailEntryId);

A plugin is then able to register a function that uses these parameters:

createButton : function(insertionPoint, mailEntryId)
{
        return
        {
                xtype: 'button',
                text: 'Hello!',
                handler: function()
                {
                        alert('Message ID: ' + mailEntryId);
                }
        };
}

The returned component has an xtype field. This field is the unique identifier for that class, and is registered using Ext.reg() together with the corresponding constructor. However, in most cases, you need to use an already existing insertion point.

5.1. Enable insertion points

While we do not have a list of insertion points in the documentation, it is possible to get an overview of them by enabling insertion points. Doing so, you will be able to visually identify locations where WebApp can be extended: it highlights all existing insertion points, even those provided by third-party plugins.

Do this by navigating to Settings -> Advanced (Should be enabled in config.php -> Developer tools -> Show insertion points in WebApp Reload WebApp afterwards to view insertion points as buttons in WebApp.

5.2. Insertion points usage example

this.registerInsertionPoint('main.toolbar.actions', this.addCustomAction, this);

addCustomAction: function(insertionpoint) {
        return {
                xtype: 'button',
                tooltip: _('Custom Action'),
                iconCls: 'icon_customAction'
        }
}

In this case, the function addCustomAction is defined in your plug-in and returns an instance of Ext.Button, which is also suitable for inclusion in a tool bar like this. Other insertion points require different types of objects, please refer to the API documentation for the object type that the insertion point expects.

Thus, using the zdeveloper plug-in, you can quickly identify where you can add user interface elements to WebApp. Moreover, if you already know where to look, you can use it to get the exact name of the insertion point that you need. For all the rest, such as finding out the exact type of the object you should return when registering to an insertion point, you still need the API reference documentation.

5.3. Example: adding a button

The following code snippet shows an example with the Facebook events integration plugin:

Zarafa.plugins.facebook.FacebookEventsPlugin = Ext.extend(Zarafa.core.Plugin,
{
        /**
         * @constructor
         * @param {Object} config Configuration object
         *
         */
        constructor : function (config)
        {
                config = config || {};
                Zarafa.plugins.facebook.FacebookEventsPlugin.superclass.constructor.call(this, config);
                this.init();
        },

        /**
         * Called after constructor.
         * Registers insertion point for facebook button.
         * @private
         */
        init : function()
        {
                this.registerInsertionPoint('navigation.south', this.createFacebookButton, this);
        },

        /**
         * Creates the button by clicking on which the Facebook
         * will be imported to the Zarafa calendar.
         *
         * @return {Object} Configuration object for a {@link Ext.Button button}
         * @private
         */
        createFacebookButton:function()
        {
                var button=
                {
                        xtype             : 'button',
                        text              : _('Import Facebook events'),
                        iconCls           : 'icon_facebook_button',
                        navigationContext : container.getContextByName('calendar')
                }
                return button;
        });

        Zarafa.onReady(function()
        {
                if(container.getSettingsModel().get('zarafa/v1/plugins/facebook/enable') === true)
                {
                        container.registerPlugin(new Zarafa.plugins.facebook.
                        FacebookEventsPlugin());
                }
        });
});

So, let’s look closer on what is happening after the each function call. Just after calling the constructor we call init() function which registers insertion point in the navigation panel, south part.

init : function()
{
        this.registerInsertionPoint('navigation.south',this.createFacebookButton,
        this);
},

Here we initiate our plugin insertion point by registerInsertionPoint function. Which takes three parameters registerInsertionPoint(**match*, func, scope)*.

Where match is a string or regular expression naming the existing insertion point where new item will be added. Regular expression can be used like in the example below:

this.registerInsertionPoint(/context\..*?\.toolbar/, this.createButton, this);

func is a function that creates one or more Ext JS components at the specified insertion point and scope is an optional parameter of the scope in which the items will be created.

createFacebookButton:function()
{
        var button=
        {
                xtype             : 'button',
                text              : _('Import Facebook events'),
                iconCls           : 'icon_facebook_button',
                navigationContext : container.getContextByName('calendar')
        }
        return button;
}

Here comes the button (the returned component may be any other UI component (Ext.Component instances)) itself with specified xtype and text. We also specify the icon css style. And the last configuration property is navigationContext used here for our Facebook button to be displayed only when Calendar context is the active one.

After specifying all the necessary properties for items to insert we should register our plugin by calling the following function:

Zarafa.onReady(function() {
        if(container.getSettingsModel().get('zarafa/v1/plugins/facebook/enable') === true) {
                container.registerPlugin(new Zarafa.plugins.facebook.
                FacebookEventsPlugin());
        }
});

Zarafa.onReady function is called when all essentials have been loaded and Container is ready for work – so it is high time for plugins to start their work. container.getSettingsModel function is used to check in Settings if the plugin is enabled. More information about Settings can be found in the corresponding chapter Settings model. container.registerPlugin function registers the plugin itself for work.