Tutorial: Simple Clearspace WidgetIn this tutorial you'll build a simple Hello World widget. Widgets are views for displaying data on the Overview tab of a Clearspace space or community. System and space administrators can "design" the overview layout by dragging widgets onto the overview design area, setting widget properties, and arranging widgets in a particular layout. The admin can save the design in an unpublished state for editing later, then publish the overview so that people using the space can take advantage of its customized space view. Widgets included with Clearspace provide views of content in Clearspace or on the web, or simply display a message. You can write your own widgets to give views of other content, or even act as enhanced versions of the included widgets. Here's what the Overview page design space looks like with a widget on the left (in preview mode) and the right (in edit mode).
Under the covers, a basic widget merely pulls together content and returns HTML for Clearspace to display when the widget is rendered. It can provide properties through which its user can guide what and how content is displayed. Along the way the widget might use an FTL file for presentation, properties files for internationalization, and so on.
This tutorial introduces you to the basics of building widget plugins. The simple widget you'll build with this tutorial displays a Hello World greeting.
Widget BasicsYour widget can be very basic — such as a single class. But the widget can also include other files to support your design goals. A basic widget could include:
In this tutorial, you'll create a widget that displays a "Hello World" message. Here are the pieces you'll work with:
What You'll Need to Get StartedThis tutorial is about the basics, so you'll want only a few things to build all the pieces it describes:
That's about it. Check out the next section for a few setup steps, then you can get started writing code. Setting UpThese setup steps are likely pretty common to other extension work you'll do - whether with macros or plugins or themes.
Set Up ClearspaceIf you haven't installed Clearspace and used its setup tool, use the following instructions. If you have, you can skip this part. Here are the steps:
Set Up Your ProjectIt's easier to develop plugins if you have your source and compiled artifacts in certain places. In particular, Clearspace expects your compiled Java classes to live in a classes directory under your plugin's root, your plugin.xml file will need to be at the root, and so on. Jive Software provides an Ant build.xml file you can use to create the project directory hierarchy, compile its sources, and deploy the result to your Clearspace instance for testing. (For the build file and samples, grab the latest developer kit at Jivespace). If You're Using Another DistributionThis tutorial (and the Ant build file that goes with it) assumes you're using the Clearspace standalone distribution. Of course, you can use this tutorial with another Clearspace distribution you've set up for development and testing (such as a Clearspace WAR file deployed to your application server). You can also set a jiveHome directory that's external to the distribution (this is the best practice for production). If you do either of these, you'll want to edit a couple of properties in the included Ant build file:
Create a Project Hierarchy
Writing Widget CodeCreate a plugin.xml FileEvery plugin has one. This file tells Clearspace what's in the plugin - in short, what Clearspace features you're extending - and where to find code and other supporting files your plugin needs (although not necessarily all of them, as you'll see in the next section).
Pretty simple, really. The <plugin> element's children include information about where the plugin is coming from (you), its version (in case you revise it for upgrade), and so on. The <minServerVersion> element specifies the minimum Clearspace version that the plugin will run on (Clearspace won't deploy it on earlier versions). The code here tells Clearspace that the plugin includes a widget, and which widget class to load. The plugin.xml can contain information about multiple bits of plugin functionality. For another kind of plugin, see Tutorial: Simple Clearspace Macro. Create a Java Class for the Widget's LogicHere, you'll create a Java class that provides logic for the widget. Your widget class will implement the com.jivesoftware.community.widget.Widget interface, usually by extending the com.jivesoftware.community.widget.BaseWidget class. As you implement the class, you will:
Required MethodsWhen you extend the BaseWidget class, you'll need to implement three methods from the Widget interface that aren't implemented in BaseWidget:
You'll see that each of these methods takes a WidgetContext instance as a parameter. The WidgetContext class includes methods through which you can get information about the context in which the widget instance is executing -- its containing community, its current user, request and response objects, and so on. In some cases, you'll simply be passing the instance to other Clearspace methods. Exposing Widget Design-Time PropertiesEach widget exposes properties that the page designer can use to customize how the widget displays. For the two default properties — title and description — you need only implement getters to return the values that Clearspace should display. For properties you add, you do the following:
In the following example, the title ("Hello World Widget") and description ("Displays a 'Hello World' message.") are properties implemented through the required accessors getTitle and getDescription. Another property, greetUser, is implemented in the widget class through its own annotation and accessors; the radio buttons here are automatically provided because the accessors get and set a boolean value. The greetUser property also provides title and description values through a properties file included in the widget. See the Java and property file examples in this topic.
Setting FreeMarker PropertiesIf your FTL file uses property variables, you can add values to the FreeMarker context from within your widget class. When Clearspace applies your template, the values you set will be used in generating the output. Note that Clearspace doesn't add the widget class itself to the FreeMarker context in the way it does with action classes. This means that a ${x} property in the FreeMarker template won't result automatically in a call to a getX() method in your widget class. Instead, you should override the BaseWidget.loadProperties(WidgetContext, ContainerSize) method to add in any FreeMarker variables you need. Your implementation should call the superclass's implementation to retrieve the properties Map, then set additional properties by putting them into the map. In other words, you'd add the x variable to the FreeMarker context using your loadProperties method implementation. See the widget class code below for an example. Helper MethodsBaseWidget provides helper methods you might find useful. Here are a few:
Writing the Java Code
package com.jivesoftware.clearspace.plugin.example.widget; import java.util.Map; import com.jivesoftware.community.annotations.PropertyNames; import com.jivesoftware.community.widget.BaseWidget; import com.jivesoftware.community.widget.WidgetContext; @PropertyNames("greetUser") public class HelloWorldWidget extends BaseWidget { // FreeMarker template for rendering preview and published widget. private static final String FREEMARKER_FILE = "/plugins/example/resources/hello-world.ftl"; // To hold the value of a custom property. private boolean greetUser = false; /** * Called by Clearspace to get the description that should be displayed for * the widget when the user hovers over it in the "customize" mode list of * widgets. * * @param widgetContext * Context in which the widget instance is executing. */ public String getDescription(WidgetContext widgetContext) { return "Displays a 'Hello World' message."; } /** * Called by Clearspace to get the widget's default title. The user will be * able to change this. If they do, their new title will be set with a call * to the final method BaseWidget.setCustomTitle. * * @param widgetContext * Context in which the widget instance is executing. */ public String getTitle(WidgetContext widgetContext) { return "Hello World Widget"; } /** * Called by Clearspace to get the value for the greetUser property. * * @return true if the user should be greeted; false to greet the world. */ public boolean getGreetUser() { return greetUser; } /** * Called by Clearspace to set the value for the greetUser property. * * @param greetUser * true to greet the user; false to greet the world. */ public void setGreetUser(boolean greetUser) { this.greetUser = greetUser; } /** * Called by Clearspace to get the HTML used to display the widget when it's * previewed or published. * * @param widgetContext * Context in which the widget instance is executing. * @param containerSize * An enum constant representing the size of the widget * instance's current container: LARGE or SMALL. */ public String render(WidgetContext widgetContext, ContainerSize containerSize) { // Process the included FTL file to render the HTML for display. String result = applyFreemarkerTemplate(widgetContext, containerSize, FREEMARKER_FILE); return result; } /** * Called by Clearspace to get properties for use in your FTL file. These * will be added to the FreeMarker context. * * @param widgetContext * Context in which the widget instance is executing. * @param containerSize * An enum constant representing the size of the widget * instance's current container: LARGE or SMALL. * @return A map of the properties and their values. */ protected Map<String, Object> loadProperties(WidgetContext widgetContext, ContainerSize containerSize) { // First load existing properties. Map<String, Object> properties = super.loadProperties(widgetContext, containerSize); // Get the name of the community this instance is in, then add it as a // property. String communityName = widgetContext.getCommunity().getName(); String userName = widgetContext.getUser().getName(); properties.put("communityName", communityName); properties.put("userName", userName); properties.put("greetUser", greetUser); return properties; } } Writing the Properties FileNext, you'll need to create a .properties file that provides the strings used in the design-time user interface. In the widget JAR, this file will be located at <jar_root>/classes/beans/HelloWorldWidget.properties.
# Resource bundle for Hello World Widget
# Author of widget
author=Me
# Version of the widget
version=1.0
# Properties - Display name and description
greetUser.displayName=Greet the User
greetUser.shortDescription=Greets the user by name; otherwise, greets the World.
Notice that the "greetUser" part of the keys matches the name of the property as given in the @PropertyNames annotation. Writing an FTL FileYou'll use a FreeMarker template (FTL) file to shape the data you're presenting in your published widget. The FTL file must be on the Clearspace classpath; that's easily accomplished by including it in your widget JAR file, then loading it at the relative location using the applyFreemarkerTemplate method (see the widget class code example in this topic). Handling Widget ResizingWhen someone clicks the "customize" link to begin designing a layout for a space's Overview tab, the design space includes a list of installed widgets and a three-column area for arranging widgets. Yet while it's a three-column area, a widget can only go on the left side (which is two columns wide) or the right side (which is one column wide). This gives a two-thirds/one-third look to the design. When someone drags your widget from the left side to the right side of the Overview tab design space, Clearspace resizes the widget from a large (two-column) size to a small (one-column) size. Here's a view with the same widget on both sides:
Each widget is responsible for its display characteristics, including adjusting within its frame when its size changes. In your FTL file, you can handle each of these cases by providing a large rendering and a small one. You do that by testing for the value of the enum com.jivesoftware.community.widget.Widget.ContainerSize; its two values are LARGE and SMALL.
<style type="text/css"> .jive-widget .jive-widget-body p.gobig { font: 16px verdana, helvetica, sans-serif; } .jive-widget .jive-widget-body p.getsmall { font: 10px verdana, helvetica, sans-serif; } </style> <#-- Use the widget's greetUser property to define the greeting style. --> <#if greetUser> <#assign greeting = "Hello " + userName + "!"> <#else> <#assign greeting = "Hello World!"> </#if> <#-- Render for display in a small area. --> <#if containerSize == enums['com.jivesoftware.community.widget.Widget$ContainerSize'].SMALL> <p class="getsmall">${greeting} Welcome to the ${communityName} community!</p> <#-- Render for display in a large area. --> <#else> <p class="gobig">${greeting} Welcome to the ${communityName} community!</p> </#if> Build, Deploy and Test the WidgetUse the build.plugins Ant target to compile and package the code into a JAR file, then use the deploy.plugins target to copy the plugin into the <jiveHome>/plugins directory.
To learn more about plugins, be sure to check out Jivespace, Jive's developer community site. |