You are not logged in. Click here to log in.

codebeamer Application Lifecycle Management (ALM)

Search In Project

Search inClear

Developing Wiki Plugins for codeBeamer: The AbstractCodeBeamerWikiPlugin Class (Part 2)


Overview

In the first article of this series, a simple example was shown for developing and deploying a custom wiki plugin for CodeBeamer. In the second part, we will delve deeper into plugin development and explain some more advanced CodeBeamer-specific wiki plugin features.

Subclassing AbstractCodeBeamerWikiPlugin

What is AbstractCodeBeamerWikiPlugin?

In order to have convenient access to CodeBeamer-specific features, your plugin classes must extend the com.intland.codebeamer.wiki.plugins.AbstractCodeBeamerWikiPlugin class.

Doing this, you will have access to:

  • the user who initiated the wiki rendering
  • the wiki page that's being rendered
  • CodeBeamer's built-in Velocity template rendering

However the AbstractCodeBeamerWikiPlugin evolves in future versions, we will keep its API stable and 100% backward compatible, so your plugin development efforts will not break when upgrading to a new CodeBeamer version.

Accessing the User and the Wiki Page Objects

You can use the following getter methods provided by AbstractCodeBeamerWikiPlugin:

  • getUserFromContext returns the bean that represents the currently signed-in user
  • getPageFromContext returns the bean that represents the currently rendered wiki page

Note that transitively through the WikiPageDto, you can also get the project that owns the current wiki page:

ProjectDto project = getPageFromContext(wikiContext).getProject();

You can access the children of the current page, the administrator of the current project and other related information similarly through accessor ("getter") methods.

Rendering Templates

Why Use Templates?

CodeBeamer includes support for templating the wiki plugin output, i.e. isolating the HTML template from the actual Java code and rendering it at runtime using a set of variables.

CodeBeamer's templating mechanism is built on the top of Velocity, a full-blown open source templating engine developed at the Apache Software Foundation.
Its simple but effective template language supports most of the features that you will need out-of-the-box (like conditions, cycles and simple arithmetic operations), but you can also extend it based on your specific needs. The detailed description of the Velocity template language is out of the scope of this article, please refer to the The Apache Velocity Project for more details.

Templating in CodeBeamer's Wiki

Rendering a template in CodeBeamer is usually a three-step process:

  1. Obtain the default context with AbstractCodeBeamerWikiPlugin.getDefaultVelocityContextFromContext(WikiContext), which will be populated with two very important beans: user and wikiPage (names are case sensitive!) that are identical with the two objects returned by the getters listed above. However, we recommend using the default context of CodeBeamer; you can even start with an empty context if necessary.
  2. Add your own context variables.
  3. Render the template with this context using the AbstractCodeBeamerWikiPlugin.renderPluginTemplate(String, VelocityContext) method.

The following code snippet demonstrates this:

VelocityContext velocityContext = getDefaultVelocityContextFromContext(context); // 1

velocityContext.put("currentTime", new Date()); // 2
velocityContext.put("yearBorn", new Integer(1976)); // 2

return renderPluginTemplate("mytest-plugin.vm", velocityContext); // 3

Using CodeBeamer's Built-in Velocity Macros

CodeBeamer is shipped with an extensive library of reusable Velocity macros that can be found in $CODEBEAMER_HOME/tomcat/webapps/cb/config/templates/VM_global_library.vm. These can be used, for example, to generate links to tracker items or to construct HTML tables. Please take a look at that VM_global_library.vm file for details.

Why might you prefer these macros to your custom macro library?

  1. If URLs change in future version of CodeBeamer, these macros will not break!
  2. These macros are consistent with the CodeBeamer look-and-feel, so your plugins can seamlessly integrate with the built-in plugins.

Using CSFR tag in Velocity templates

The 'cbCsrfToken' has been added to the velocity context.


  • The CSFR tag can be used directly as well with the following properties:

    • token - the current token value.
    • parameterName - the name of the form value to submit the token with. Also known as the input tag name.
    • headerName - name of the header to submit the token value with for Ajax requests.


  • Add the Velocity macro 'generateCsrfHiddenField' to simplify generating hidden field with the current token.
    Example usage:
    <form id="velocityTest" method="post" action="/velocityTest.spr">
        #generateCsrfHiddenField()
        <input type="text" value="${testModel.value}" name="value">
        <input type="submit" value="Submit">
    </form>
By default, the page load event globally adds a token hidden field to all forms. This is the expected behavior.

Other Approaches for Templating

Using Velocity is recommended but not required. Alternative solutions might include:

  • Using FreeMarker, another advanced open source Java templating engine.
  • You can construct the output by using a StringBuffer object, but in this case the formatting is tightly coupled with the content generation, what is hard to read, hard to customize and violates the separation of concerns principle. (Just like view tier and action code in web application development.)

Compiling, Deploying and Testing

Compiling and deploying the Java code is done in the same way as with our first HelloWorldPlugin.

In this case, we have one more file, the Velocity template second-plugin.vm. You have to copy that to $CODEBEAMER_HOME/tomcat/webapps/cb/config/templates, this is where CodeBeamer will look for the templates. This directory stores not only the wiki plugin templates, but the email templates and wiki page templates, too.

Testing

Create a testpage and enter:

[{SecondDemoPlugin}]

You should see something like this:

Printing Basic Information
    * Signed in as: bond (Administrator, CodeBeamer)
    * Reading page: "My favourite trumpet dealers" (Mon Jun 26 15:06:45 CEST 2006)
    * Working in project: "Kind of Blue"
    * Now: Jun 26 17:52 2006
    * Am I the owner of this page? yes

Using Link Macros
    * View bond (Administrator, CodeBeamer)
    * View Home
    * View Kind of Blue

Please note that wiki pages are living entities, always reflecting the latest information available when rendering them: for example, rename your project and then reload the Wiki page and you'll see the changes immediately.


Appendix

SecondDemoPlugin.java

package com.intland.codebeamer.wiki.plugins;

import java.util.Date;
import java.util.Map;

import org.apache.velocity.VelocityContext;

import com.ecyrd.jspwiki.WikiContext;

import com.intland.codebeamer.persistence.dto.UserDto;
import com.intland.codebeamer.persistence.dto.WikiPageDto;

public class SecondDemoPlugin extends AbstractCodeBeamerWikiPlugin {
	public String execute(WikiContext context, Map params) {
		// get beans from wiki context
		UserDto user = getUserFromContext(context);
		WikiPageDto page = getPageFromContext(context);

		// get default Velocity context
		VelocityContext velocityContext = getDefaultVelocityContextFromContext(context);

		// put the current system time in the user's format to the context
		velocityContext.put("currentTime", user.getDateTimeFormat().format(new Date()));

		// put whether the current user is the owner of the current page to the context
		velocityContext.put("owner", page.getOwner().getId().equals(user.getId()) ? "yes" : "no");

		// render template
		return renderPluginTemplate("second-plugin.vm", velocityContext);
	}
}

Comments:

  1. CodeBeamer entities should always be compared using their identifiers, not using their Java references!
  2. The filename of the .vm file can be passed to renderPluginTemplate without the extension .vm.

seconddemo-plugin.vm

<h3>Printing Basic Information</h3>
<ul>
	<li>Signed in as: <i>${user.name} (${user.lastName}, ${user.firstName})</i></li>
	<li>Reading page: <i>"${wikiPage.name}" (${wikiPage.lastModifiedAt})</i></li>
	<li>Working in project: <i>"${wikiPage.project.name}"</i></li>
	<li>Now: <i>${currentTime}</i></li>
	<li>Am I the owner of this page? <i>${owner}</i></li>
</ul>

<h3>Using Link Macros</h3>
<ul>
	<li>View #linkUser($user)</li>
	<li>View #linkWikiPage($wikiPage)</li>
	<li>View #linkProject($wikiPage.project)</li>
</ul>

Comments: .vm files contain only HTML snippets, so there is no need for a HTML <head>, <body> and other structural elements.