Skip to main content

Let your Confluence Tweet!

The single biggest problem in communication is the illusion that it has taken place.
George Bernard Shaw


The recent Atlassian Codegeist competition afforded me an opportune moment to further explore new areas of Confluence plugin development. My current contracting assignment involves heavily customising both the theme and functionality of the Confluence platform - but Codegeist offered a chance to really experiment!

With the explosion of social networking applications and the rise of the flow, I decided to integrate the functionality of one of the most profilic flow tools in the netsphere, Twitter, with Confluence.

Twitter + wiki = Twikkir
Posing the simple question 'What are you doing now?", millions have taken to tweeting their response on Twitter to anybody who will listen. From a simple status update to a possible support circle to a tribe building ecosystem, Twitter has become another pillar of the fast-evolving web. In his blog, Rands notes:
I want to see how they see the world. This is why I follow people on Twitter. This is why they follow me.


Enterprise Flow
As I commented on the Workstreamer blog, I think enterprise companies have witnessed social networks flourish outside of their control and domain. They are now looking to replicate these networks in their own space in order to captialise on the perceived benefits, create structure/control mechanisms and expose the data for processing. Granted, some of these integrations may be exploratory - but the enterprise is definitely looking to keep apace with these developments - and a Twitter-like tool is a main contender.

Twikkir


The actual plugin development proffered some interesting issues to solve - persistence of posted messages, an update mechanism for diffusing new messages and the display of messages amongst others.

Persistence


The issue of persisting user posts was one of the first obstacles to tackle. Confluence offers various persistence mechanisms for storing data - in brief:

  • Hibernate - Confluence uses the Hibernate framework to persist its objects. However, it is generally not recommended to modify the Hibernate files to incorporate persistence of custom objects.

  • Content Properties - a mechanism to store a key value pair with an associated object (content, space, etc.) within Confluence

  • Bandana - this Atlassian framework uses XStream to convert arbitrary Java objects into XML for storage.

I selected the Bandana framework as the solution - allowing content-unrelated data (Twikkir posts and users) to be stored in the Confluence database. Extremely simple to use, Bandana allows the persistence and retrieval of objects through keys in a global or space related context. In order to avoid class loader issues, as noted by David Peterson, it is necessary for plugins to set the classloader reference in the XStream object correctly:

[java]
// Create xstream reference
if(xstream == null)
{
xstream = new XStream();
xstream.setClassLoader(getClass().getClassLoader());
}
...
// Persist data within the global Bandana context
String xml = getXStream().toXML(userSet);
bandanaManager.setValue(new ConfluenceBandanaContext(), Constants.BANDANA_CTX_KEY_TWIKKIR_USERS, xml);
...
// Retrieve
String xmlString = (String) bandanaManager.getValue(new ConfluenceBandanaContext(), Constants.BANDANA_CTX_KEY_TWIKKIR_USERS);
[/java]

AJAX


I was eager to add some AJAX functionality to this plugin and, in doing so, explore some of the JS libraries offering some 'Web 2.0' goodness. My goal was to enable the asynchronous ability to post and receive new twikkir posts and avoid a total page reload.

jQuery is an extremely powerful library and affords the developer some neat tricks with minimal fuss. In order to enable the jQuery library for use within the plugin JavaScript files, I found it necessary to define an alternate name for the jQuery variable through the noConflict() function in the main Velocity template.
[js]
var $jq = jQuery.noConflict();
$jq("document").ready(function(){ ...
[/js]

All subsequent calls to jQuery were made through the variable $jq. jQuery really impressed me - the animated tab panels were generated by one line of code! I look forward to exploring jQuery further.

jQuery can also simplify the construction and management of AJAX calls, but as I was interested in understanding the inner details of the application flow, I coded these manually in JavaScript. All calls followed this format - with the response callback using an anonymous function:

[js]
var request = createRequest();
var url = "/plugin/twikkir/postit.action";
request.open("POST", url, true);
request.onreadystatechange = function()
{
if(request.readyState == 4)
{
resp = request.responseXML;
document.getElementById("poststatus").innerHTML = getTagContent(resp, "poststatus");
postStatusImg.src = restImage.src;
document.getElementById("twikkirpost").value = null;
document.getElementById("charsleft").innerHTML = maxPostLen;
$jq("#poststatus").fadeIn(2000).fadeOut(3000);
}
};
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
request.send("twikkirPost="+escape(twikkir)+"&username="+escape(username));
[/js]

It should be noted that it is necessary to set the appropriate request type header in the request parameter (line 16 above). It is also necessary to set the appropriate response type in the associated action class - for example:

[java]
ServletActionContext.getResponse().setContentType("text/xml");
[/java]

Further, the altassian-plugin.xml should also reflect the return type of the action - in this instance "velocity-xml":

 <action name="postit" class="com.sidus.confluence.twikkir.action.PostItAction">
  <result name="success" type="velocity-xml">/templates/sidus/twikkir/posted.vm</result>
 </action>


Another experiment took shape in the version control system used - Github - where you can find the source code behind the plugin.

Future


Building the plugin proved to be an extremely useful learning exercise. The plugin certainly could be improved and there are many extension points:


  • Send/receive tweets from Twitter

  • Protect your twikkir feed

  • Enable Confluence entities (pages/ blogposts, etc.) to send tweets

  • Enable a command line interface to execute Confluence actions

  • Build twikkir clouds

  • Build twikkir connection data - inferring/suggesting potential relationships between users based on who is following who


The flexibility of the Confluence plugin system creates countless possibilities for extending the wiki platform with social networking integration and Twikkir is just one of these possibilities.

... and of course you can follow me @ keibro on Twitter.

Popular posts from this blog

Local Testing OAuth Social Signin

On some recent Grails projects, I have been looking at using the Twitter and Facebook OAuth signin process.

This process allows you to authenticate users based on their Twitter/Facebook logins, without the need for the user to expose their passwords to your site.

When you create your 'application' within Twitter or Facebook, it is necessary to define the URL where the application can be accessed. Twitter and Facebook will only redirect to this URL during the authentication process.

I have tested running some applications on Heroku or Appfog, with Twitter and Facebook happy to redirect to the appropriate URLs with successful authentication.

However, when testing locally, I follow these steps to work through the authentication process.

1. App Context
Ensure that the Grails app context is '/' - as the application is generally deployed this way on Heroku/Appfog:

Config.groovy grails.app.context = '/'
2. Port Binding:
While the local application will generally run o…

Brain Error: No space left on device

I'm not dumb. I just have a command of thoroughly useless information.
Calvin - It's a Magical World, Bill Waterson
Wired's June 2008 edition included an article entitled 'Quiet Please: how Man-made noises may be altering Earth's ecology'.

The article focused on the theory put forward by Bernie Krause, a field recording scientist, that nature's soundtrack (biophony) is being adversely affected by a louder human-made cacophony (anthrophony). Krause postulates that the animal kingdom divides the acoustic spectrum so it's inhabitants do not interfere with each other. However, human-made noise is increasingly disrupting this harmony and intrudes on a piece of the spectrum already in use - drowning out natures voice.

As an example, Krause summizes that the rapidly declining population of the Yosemite spadefoot toad is due to the noise generated from low-flying military aircraft, performing training exercises in the area. Coyotes and owls are able to home in on i…

If This, Then What?

Imagine that you could combine services (internet and otherwise) together like Lego blocks to create new, personalised services.

If This, Then That (ifttt) is a new service that allows you to do just that.

Through an intuitive interface ifttt puts the power of "Event Driven Programming" at your fingertips, letting you connect services with digital duct tape. Event driven programming can be simply broken into two stages - event selection/detection followed by event handling. ifttt terms these stages as triggers and tasks and provides a comprehensive list of services that you can target as a trigger or task, from Facebook and Twitter through to Google calendar events.

For example, it is possible to build a service that will send a text to your mobile phone (task) if the weather service has forecast rain for the following day (trigger). The mechanism for building such a service is so simple and straight-forward that no programming knowledge is required. Trigger and task construc…