Dynamic UI Extensions - BloomReach Experience - Open Source CMS

This article covers a Hippo CMS version 11. There's an updated version available that covers our most recent release.

04-07-2016

Dynamic UI Extensions

Hippo CMS provides a plugin architecture to facilitate a customizable UI. The visual rendering is based on Apache Wicket. All standard UI components are written as plugins. The Hippo CMS application offers a default configuration for these plugins. Customizing Hippo CMS comes down to editing this configuration, extending it with extra plugins provided by Hippo or writing your own.

Customization by configuration

Frontend plugins can extend the Wicket component model. Wicket HTML fragments can contain references to plugins that are not added explicitly in the Java code. Instead, these references are resolved by the configuration in the repository.

Example

Suppose we have an ordinary Wicket panel called RootPanel, which contains three child panels: a ListPanel, a TreePanel and a SimplePanel. The child panels would need to be added explicitly in code, for example in the constructor or RootPanel:

public class RootPanel extends Panel {
 
   public RootPanel {
       add("list", new ListPanel());
       add("tree", new TreePanel());
       add("custom", new SimplePanel());
   }
}

This means that replacing SimplePanel with e.g. AdvancedPanel implies recompilation of the code. The wiring of the Wicket component hierarchy is fixed in the Java code. Frontend plugins however need not be added explicitly in code; Instead they use the concept of 'extension points'. To illustrate this behavior, please see the following example in which we recreate the RootPanel from above, only this time implemented as a RenderPlugin. (note that we prefix the name of an extension point with ' extension.'. This is not required but does make the configuration easier to read, as we will see later on)

public class RootPlugin extends RenderPlugin {
 
   public RootPlugin(IPluginContext context, IPluginConfig config) {
       super(context, config);
 
       addExtensionPoint("extension.list");
       addExtensionPoint("extension.tree");
       addExtensionPoint("extension.custom");
   }
}

And the accompanying HTML file RootPlugin.html (note that a RenderPlugin is a Wicket Panel itself) :

<html xmlns:wicket="http://wicket.apache.org/">
  <wicket:panel>
    <h1>RootPlugin</h1>
    <div wicket:id="extension.list">A list</div>
    <div wicket:id="extension.tree">A tree</div>
    <span wicket:id="extension.custom">A custom component</span>
  </wicket:panel>
</html>

The RootPlugin is now able to wire the three extension points through configuration in the repository. At this point (we have not defined the end points for these extension points) the framework will simply render an EmptyPanel for all three extension points. So how do we wire an extension point to a plugin? It is just a matter of adding a property with the name of the extension point ( extension.list, extension.tree or extension.custom in this example) and as a value use the wicket.id of the plugin that should rendered at the extension point. In our case, the configuration could look like this:

+ rootPlugin
  - plugin.class: org.example.RootPlugin
  - wicket.id: service.root
  - extension.list: services.list
  - extension.tree: services.tree
  - extension.custom: services.custom
+ listPlugin
  - plugin.class: org.example.ListPlugin
  - wicket.id: services.list
+ treePlugin
  - plugin.class: org.example.TreePlugin
  - wicket.id: services.tree
+ customPlugin
  - plugin.class: org.example.SimplePlugin
  - wicket.id: services.custom

Changing the SimplePlugin to an AdvancedPlugin now only involves changing the configuration. No recompilation is needed. This configuration is stored in the repository in nodes of (primary) type frontend:plugin. The property plugin.class determines what Java class is instantiated.

We can enhance this even further by moving the definitnion of a plugin's extension points to configuration as well. This way we don't need to write any java code; simply add the extension points to the plugin's .HTML file and use property wicket.extensions (multiple) to define the extensions of the plugin. In our example the configuration would look like this:

+ rootPlugin
  - plugin.class: org.example.RootPlugin
  - wicket.id: service.root

  - wicket.extensions: [extension.list, extension.tree, extension.custom]

  - extension.list: services.list
  - extension.tree: services.tree
  - extension.custom: services.custom
+ listPlugin
  - plugin.class: org.example.ListPlugin
  - wicket.id: services.list
+ treePlugin
  - plugin.class: org.example.TreePlugin
  - wicket.id: services.tree
+ customPlugin
  - plugin.class: org.example.SimplePlugin
  - wicket.id: services.custom

and the implementation of RootPlugin is simplified to:

public class RootPlugin extends RenderPlugin {
 
   public RootPlugin(IPluginContext context, IPluginConfig config) {
       super(context, config);
   }
}
Did you find this page helpful?
How could this documentation serve you better?
On this page
    Did you find this page helpful?
    How could this documentation serve you better?