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.

Comments

  1. Nice work, buddy! Good to see you are still in touch with Atlassian products :-)

    ReplyDelete
  2. Thanks Dushan! Yes - I have been working on some very interesting customisations of Confluence with Headshift here in London over the last few months.

    ReplyDelete
  3. Great post Keith! I like your new blog, its design, etc. Keep it rocking bro!

    ReplyDelete
  4. The "Future" you described sounds very similar to workstreamer. If you haven't done so already please sign up for our beta.

    ReplyDelete

Post a Comment

Popular posts from this blog

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 ...

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 w...

Explore. Dream. Discover. Be Interesting.

Interesting 2008 Bunting and all ... Twenty years from now you will be more disappointed by the things that you didn’t do than by the ones you did do. So throw off the bowlines. Sail away from the safe harbor. Catch the trade winds in your sails. Explore. Dream. Discover. Mark Twain Over the weekend, my wife and I attended Interesting 2008. The concept of the event - to gather a number of speakers to talk about something they are interested in - was intriguing and all reports suggested it would be a Saturday well spent. Comparisons with the TED talks certainly helped to pique my interest and we were most definitely not disappointed. Russell Davies , the coordinator of the day, has this to say about being 'interesting': The way to be interesting is to be interested. You’ve got to find what’s interesting in everything, you’ve got to be good at noticing things, you’ve got to be good at listening. If you find people (and things) interesting, they’ll find you inter...