NOTE: This technique is valid only for Jive 6.0.1 and up.

 

Adding markup to the Jive application can be a challenge within the context of a plugin.  Well, I take that back.  It's easy to do, sure.  But doing it right?  A little harder than you might think.

 

Sure, you could just redefine a core Struts action in your plugin's struts.xml file and point it at your custom Struts action and/or custom Freemarker or Soy template.  But you know that doing so introduces risk.  Another plugin you install into your instance later on could override the same action, rendering one of them useless.  You may need to theme the Freemarker or Soy template that you modified, which would also wipe out the change made in your plugin.  You also are bound to that action and template's implementation for your version, which requires you to have to manually upgrade them each time you upgrade your Jive instance.

 

To get around that, you could always use Javascript, which you can include on every page in the system, that knows when to modify the DOM structure of the page, which is a pretty slick way to do it.  You can have your script detect which page you're currently on, and, when appropriate, invoke a Soy template, and append it to the DOM, thus modifying your view in a pretty upgrade-safe manner.  The problem with this approach becomes evident when you realize your custom page content requires server side data.  This means you need to make an AJAX call to a web service which you may or may not have to code up yourself, which totally works, but may not result in the best user experience, with the custom content being added to the DOM after a slight delay.

 

It would be great if we could somehow augment the data being returned by the Struts action in the first place, and have some sort of hook to add the markup that utilizes that data to the DOM structure.

 

Enter the PluginTemplateDefinition.

 

A PluginTemplateDefinition is a way to do just that.  Utilizing it requires the following:

  • Create PluginTemplateDefinition Java class implementation
  • Add your PluginTemplateDefinition class to Spring, and register it
  • Create a Soy Template

 

The Java Class

public class MyPluginTemplateDefinition implements PluginTemplateDefinition {

    @Override
    public boolean supports(Action action) {
        //only execute when looking at a place overview page
        return (action instanceof PlaceOverviewAction);
    }

    @Override
    public String getHeadTemplateName() {
        //no meta tags or css for this
        return null;
    }

    @Override
    public String getBodyTemplateName() {
        //template containing Javascript to modify the DOM
        return "jive.test.app.body";
    }

    @Override
    public Object getModel(ActionInvocation ai, Object actionModel) {
        //making available the data that I want
        Map<String, Object> model = new HashMap<String, Object>();

        Place place = ((PlaceViewBean) actionModel).getPlace();
        if(place.getObjectType() == JiveConstants.SOCIAL_GROUP) {
            List<String> owners = getSocialGroupOwners(place);
            model.add("owners", owners);
        }

        return model;
    }
}

 

The support() method simply checks whether this PluginTemplateDefinition should be executed for the current action.  In the case of this example, we only want it to execute when the PlaceOverviewAction is invoked.  If we wanted to execute on every page, we could just return true.

 

getHeadTemplateName() and getBodyTemplateName() will return fully-qualified soy template names.  The head template should only contain CSS includes and meta tag information.  In many cases, you won't need anything here, and can just return null.  The body template will contain the Javascript necessary to modify the DOM structure of the page being displayed.

 

In the getModel() method, you will create and return the data you want to be displayed on the page.  Here, you have access to the ActionInvocation itself, as well as the data returned by the action.  The actionModel parameter will be either a Soy model, for Soy-driven Actions, or the action itself, for legacy Struts actions.  You can access anything you want here, but should be wary of modifying any of the data in the model.

 

spring.xml

Registering your PluginTemplateDefinition is a matter of adding it to the list of plugin template definitions defined in Spring.  So, you would add the following to your plugin's spring.xml file:

<bean class="com.jivesoftware.community.util.spring.MergeableCollection" parent="pluginTemplateDefinitions">
    <property name="source">
        <util:list>
 <bean class="my.plugin.MyPluginTemplateDefinition" />
        </util:list>
    </property>
</bean>

 

Of course, if your PluginTemplateDefinition class has dependencies, you would define them here, as well.

 

Soy template

Your soy template will contain the Javascript needed to augment the DOM structure and display your data.

 

{namespace jive.test.app}

/**
* @param appParams
*
* @depends path=/resources/scripts/apps/my_amazing_js_app/main.js
*/
{template .body}
    {call jive.shared.soy.resourceInlineJs}
        {param id}plugins{/param}
        {param code}new jive.Amazing.App.Main({lb}buildJson({$appParams}{rb});{/param}
    {/call}
{/template}

 

This example assumes that you would be implementing some sort of complex, interactive visual layer on the screen.  If you're looking just for a simple inclusion of data on the screen, you could do something as simple as:

{namespace jive.socialgroupaccess}

/**
* @param owners
*/
{template .body}
    <script type="text/javascript">
        $j('#some-element').append('<p>{$owners}</p>');
    </script>
{/template}


 

Keeping in mind that you may also want to include another soy template here, rather than using HTML literals, just to keep things modularized.

 

Enjoy!