27.2. Hello world as an extension

27.2.1. The extensions.xml file
27.2.2. Extending multiple extension points with a single extension

We will use the classical Hello world as the first simple example of an extension. This extension will add a new menu item in the menu which displays a popup with the text "Hello world!" when selected.

  1. Start by creating an empty directory in some suitable location on your hard disk. Inside this directory, create two more directories META-INF and resources.

  2. Copy the XML code below and save it to META-INF/extensions.xml in your newly created directory. It is important that you get the filename correct.

    
    <?xml version="1.0" encoding="UTF-8" ?>
    <extensions xmlns="http://base.thep.lu.se/extensions.xsd">
       <extension
          id="net.sf.basedb.clients.web.menu.extensions.helloworld"
          extends="net.sf.basedb.clients.web.menu.extensions"
          >
          <index>1</index>
          <about safe-scripts="1">
             <name>Hello world</name>
             <description>
                The very first extensions example. Adds a "Hello world"
                menu item that displays "Hello world" in a javascript
                popup when selected.
             </description>
          </about>
          <action-factory>
             <factory-class>	
                net.sf.basedb.clients.web.extensions.menu.FixedMenuItemFactory
             </factory-class>
             <parameters>
             	<id>hello-world</id>
                <title>Hello world!</title>
                <tooltip>This is to test the extensions system</tooltip>
                <icon>/images/info.png</icon>
                <script>~/hello.js</script>
             </parameters>
          </action-factory>
       </extension>
    </extensions>
    
    

    This file is the most important one when creating extensions since it is used to tell BASE about the extensions (and plug-ins) in the package. See below for more information.

  3. Copy the javascript code below and save it to resources/hello.js in your newly created directory. Once again, it is important that the filename is correct.

    
    var HelloWorld = function()  
    {  
       var hello = {};  
        
        /** 
           Executed once when the page is loaded. Typically 
           used to bind events to fixed control elements. 
        */  
        hello.initMenuItems = function()  
        {  
           // Bind event handlers the menu items.   
           // First parameter is the ID of the menu item  
           // Second parameter is the event to react to (=click)  
           // Last parameter is the function to execute  
           Events.addEventHandler('hello-world', 'click', hello.helloWorld);  
        }  
          
        // Show 'Hello world' message
        hello.helloWorld = function(event)  
        {  
           alert('Hello world');
        }  
         
        return hello;  
    }();  
          
    //Register the page initializer method with the BASE core  
    Doc.onLoad(HelloWorld.initMenuItems);  
    
    
  4. Create a JAR package containing the two directories and files you have created. It is important the the subdirectory structure inside the JAR file is rooted at the first empty directory you created.


    META-INF/
    META-INF/extensions.xml
    resources/
    resources/hello.js

    Copy the JAR file to the the plugins.dir directory for your BASE installation.

  5. Log in to BASE and install the extension by going through the installation wizard at AdministratePlug-ins & extensionsOverview. When the extension has been installed you should have a new menu item: ExtensionsHello world! which pops up a message in a Javascript window.

    [Note] Note

    You may have to logout and login again to see the new menu item.

27.2.1. The extensions.xml file

The <extensions> tag is the root tag and is needed to set up the namespace and schema validation.

The <extension> defines a new extension. It must have an id attribute that is unique among all installed extensions and an extends attribute which id the ID of the extension point. For the id attribute we recommend using the same naming conventions as for java packages. See Java naming conventions from Oracle.

The <about> tag is optional and can be used to provide meta information about the extension. We recommend that all extensions are given at least a <name>. Other supported subtags are:

  • <description>
  • <version>
  • <copyright>
  • <contact>
  • <email>
  • <url>

[Tip] Global about tag

<about> tag can also be specified as a first-level tag (eq. as a child to <extensions>). This can be useful when an XML file defines more than one extension and you don't want to repeat the same information for every extension. You can still override the information for specific extensions by including new values in the extension's <about> tag.

[Tip] Start out in disabled state

The <about> tag can be used to specify that an extension should start out in disabled state. This can be useful if there are multiple variants of an extension or if some extensions are considered "advanced mode" and only should be enabled after some serious thinking. To use this feature, simply set <about disabled="1"> in the XML file.

The <action-factory> tag is required and so is the <factory-class> subtag. It tells the extension system which factory to use for creating actions. The FixedMenuItemFactory is a very simple factory that is shipped with BASE. This factory always creates the same menu item, no matter what. Another factory for menu items is the PermissionMenuItemFactory which can create menu items that depends on the logged in user's permissions. It is for example, possible to hide or disable the menu item if the user doesn't have enough permissions. If none of the supplied factories suits you it is possible to supply your own implementation. More about this later.

The <parameters> subtag is used to provide initialisation parameters to the factory. Different factories supports different parameters and you will have to check the javadoc documentation for each factory to get information about which parameters that are supported.

[Tip] Tip

In case the factory is poorly documented you can always assume that public methods the start with set and take a single String as an argument can be used as a parameter. The parameter tag to use should be the same as the method name, minus the set prefix and with the first letter in lowercase. For example, the method setIcon(String icon) corresponds to the <icon> parameter.

[Note] Content security policy and inline javascript

The Hello World example requires quite a lot of code and it would have been tempting to skip the hello.js file and simply add <onclick>alert('Hello world')</onclick> to the <parameters> section in the extensions.xml file. However, BASE is by default configured to block all inline JavaScript since it opens up for cross-site scripting attacks. It is possible to relax this policy but it is nothing we recommend. See Section E.1, “Content security policy” for more information.

The real code examples uses a different design with pure HTML that is generated dynamically and a fixed javascript file that attaches event handlers when the page has been loaded and should be compatible with the stricter security policy.

27.2.2. Extending multiple extension points with a single extension

A single extension can extend multiple extension points as long as their action classes are compatible. This is for, for example, the case when you want to add a button to more than one toolbar. To do this use the <extends> tag with multiple <ref> tags. You can skip the extends attribute in the main tag.


<extension
   id="net.sf.basedb.clients.web.menu.extensions.history-edit"
   >
   <extends>
      <ref index="2">net.sf.basedb.clients.web.tabcontrol.edit.sample</ref>
      <ref index="2">net.sf.basedb.clients.web.tabcontrol.edit.extract</ref>
   </extends>
   ...
</extension>

This is a feature of the XML format only. Behind the scenes two extensions will be created (one for each extension point). The extensions will share the same action and renderer factory instances. Since the id for an extension must be unique a new id will be generated by combining the original id with the parts of the id's from the extension points.