Editor’s note: This blog post was originally published on the Atlassian Confluence blog and is republished below with permission from the author.
If you’ve ever worked inside Confluence, you probably know that it has a flexible plugin model that allows third parties to add new functionality and features. Traditionally, creating a Confluence plugin has involved the addition of new features in the plugin itself. However, as more companies are providing their software as a service (SaaS), it doesn’t always make sense to re-implement functionality in a plugin that already exists in a SaaS product. Instead, a plugin can be written to integrate the existing SaaS product with Confluence.
Integrating Confluence with an existing SaaS offering can provide a great experience for users. If you already have a SaaS product, you probably also have a substantial user base. Those users will want to access the data from their existing account within Confluence rather than being forced to manage one account for Confluence and another account for use outside of Confluence (either using your product stand-alone or with another service such as JIRA).
Furthermore, integrating Confluence with your existing SaaS product gives you flexibility to grow your SaaS offering. As you improve the offering, those improvements will be immediately available to your Confluence users without a need to re-write the plugin and duplicate code, and without a need for existing users to update their plugin.
This post describes the approach we took at Lucidchart to provide our users a great Confluence experience with our diagramming SaaS offering.
Authorization with OAuth
When creating a Confluence plugin for a SaaS product, one of the first challenges we encountered is determining how Confluence will gain access to the user’s data in the SaaS product. For the Lucidchart plugin, we turned to OAuth to solve this problem. OAuth is an industry standard for delegated authorization. A very brief description of an OAuth 3-legged authorization flow is given below. The details of the protocol are beyond the scope of this post, but can be found at oauth.net.
OAuth allows a consumer (in our case, the Confluence plugin) to request authorization from a service provider (Lucidchart, in this case) by redirecting the user to a page provided by the service provider where the user is asked whether she would like to allow the consumer to access her data. If the user grants access, she will be redirected back to the consumer and be given a token that allows data access from the service provider.
Adding the Menu Item
With a basic understanding of the OAuth authorization flow, let’s dive into how we created the plugin. We want users in Confluence to be able to quickly and easily insert a Lucidchart Diagram on a page. To do that, we’ll insert a menu item in the Confluence “Add” drop down menu labeled “Lucidchart Diagram”. Menu items are added by creating a Web UI module, which is done by adding an entry to atlassian-plugins.xml.
Each section of the web-item definition is described in detail in the Confluence Web UI module documentation. With this web-item definition, a menu item will appear in the “Add” drop down for spaces and pages with the text “Lucidchart Diagram” and the icon “lucid.png”. When the item is clicked, it will send the user to the relative URL below with $page.id replaced by the id of the Confluence page the user was on when he clicked the menu item:
Creating the XWork Module to Handle Authorization
Now we need to define what happens when the user arrives at the URL we provided in the Web UI module. An XWork module will do the trick. We define the XWork module in the atlassian-plugins.xml.
This snippet defines a WebWork action “insertDiagram” for the URL we provided to the web-item. The Java class InsertDiagram.java extends ConfluenceWebActionSupport and overrides the execute() method which will be called when the user arrives at the URL for this action.
When a user clicks on our web-item and arrives at our WebWork action, we need to first determine whether the user has already authorized Confluence to access her Lucidchart account and data (i.e. determine if an OAuth Access Token is already available). If the user has not yet authorized Confluence to access her data, we initiate the three-legged OAuth flow. This means that the user will be redirected to a page hosted at http://www.lucidchart.com where she can grant or deny Confluence access to her data.
Grant Confluence Access to Your Lucidchart Data
If the user grants access, she will be redirected to a callback URL provided previously as part of the OAuth flow. In our case, the callback will be the URL of the WebWork action we defined previously.
When the execute method is called this time, rather than redirecting to the Lucidchart authorization page, we will complete the OAuth flow to obtain and store an access token.
At this point, we have an OAuth access token that can be used to sign requests to Lucidchart, allowing us to make API calls from our plugin to Lucidchart to access the user’s Lucidchart data. Future requests made by the user to add a Lucidchart diagram will not need to go through this authorization process because the stored access token can be used until it is explicitly revoked by the user.
Displaying the User’s Lucidchart Document Manager
Now that the user has authorized Confluence to access his Lucidchart data, we display a screen that allows the user to make a new diagram or choose an existing diagram.
In our atlassian-plugins.xml XWork module definition, we included a result for “success”. When the execute() method of the InsertDiagram WebWork action completes the OAuth flow, it will return the string “success” which will cause the success result to be executed. In our case, we defined that as a velocity template called document-list.vm. The velocity template will render the Confluence header and footer, then include in an iframe the user’s “document list” as served from Lucidchart servers. The velocity template is:
The page allows the user to select an existing diagram from his account or create a new diagram.
The OAuth access token was used to make a signed oauth request in the iframe. Because we provided a signed OAuth request, the Lucidchart servers render a view of the Lucidchart document list for the specific user making the request. In the URL for the document list, we also included a callback for the Lucidchart servers to use when a user selects a diagram to add to Confluence or creates a new diagram. If we click “View source” on the page, we would see that the URL for the iframe looks something like:
Notice that the URL has the OAuth parameters as well as a callback parameter which when URL decoded is:
When a users clicks to insert a document or creates a new document, the user will be redirected to the callback with a documentId parameter added to the URL. For example, if the user clicks “Insert document” for document id 12345, he would be redirected to:
Attaching the Diagram to the Page
When the user has selected a diagram to attach, we saw that the user is redirected to a callback that was provided in the URL for the iframe. Now we need to define a second XWork module for that callback. The “attachDiagram” action is defined in the atlassian-plugins.xml as follows:
The execute() method of DiagramAttacher.java will pull the documentId from the URL and use it to request an image of the document from the Lucidchart servers (again, using an OAuth signed request to obtain authorization to the image). The DiagramAttacher will then attach each page of the diagram to the appropriate Confluence page with a unique name:
The diagram will now exist as an image attached to the Confluence page.
Display the Diagram in the Page
While having the diagram attached to the page is okay, we’d like to provide a nice viewer for the diagram that allows zooming, panning and moving through the pages of a diagram. To do this, we need to create a Confluence Macro Module. When a user chooses to add a Lucidchart diagram to a page, we will not only attach the images to the page as previously described, but also include wiki markup for a custom macro to view the diagram:
The Macro Module will parse this markup and insert the appropriate diagram in a viewer. As we did with the Web UI and XWork modules, we define the Macro module in the atlassian-plugins.xml:
The Java class LucidMacro.java extends com.atlassian.renderer.v2.macro.BaseMacro and overrides the execute() method. The execute() method’s signature is:
public String execute(Map params, String body, RenderContext renderContext)
We can use params to get the parameters of our macro (name, id, pages, width, height). Using those parameters, we can get a list of the URLs for the attached images that should be displayed:
The list of attachment URLs to display is put in the velocity context so it can be used in a velocity template to render the diagram viewer:
Mapcontext = MacroUtils.defaultVelocityContext();
return VelocityUtils.getRenderedTemplate(“templates/lucidchart/viewer.vm”, context);
The result is that the diagram selected by the user is displayed in the Confluence page with the ability to zoom, pan and move from page to page.
Lucidchart Viewer in Confluence
Creating a Confluence plugin for a SaaS offering is a simple and effective way to add value for your users. With the flexibility provided by the Confluence plugin model and industry standards such as OAuth, it’s a straightforward process for any SaaS offering to give users the ability to quickly use their data in Confluence.
Note: We were able to use the same approach to create a JIRA plugin for Lucidchart.