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

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…

Deploying With Git

I have the misfortune to work on a number of PHP based web applications at work. Previously, the deployment process involved determining which files had changed since the last release and copying them across to the server. Needless to say, this was an error-prone and inefficient way of deploying updates.

Gitobots, Roll Out We use Git for our version control and, with Heroku's push to deploy in mind, I looked further into the possibilities of using Git for our deployment process. Abhijit Menon-Sen's article details the process very well. With a few slight variations, these are the steps I follow to deploy changes via Git.

Prime Remote On the remote server for your application (e.g. production, staging or test), create a new, bare Git repository for your codebase:

cd /cygdrive/c/repo mkdir project.git cd project.git git --bare init Hooks As this bare repository does not contain a working tree (the actual source), a Git hook is used to checkout the code to a specific location. Gi…

Explore. Dream. Discover. Be Interesting.

Interesting 2008

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 interesting.
Attended by designers, develop…