Plugins on OnDemand with Remote Apps
Don BrownArchitect, Atlassian
Five years ago. . .
And now. . .• Over 12k active, 9k paid OnDemand accounts
• 5 out of 10 add Greenhopper
• 1.5 out of 10 add Bonfire
• 1 out of 10 add Team Calendars
. . . Instances
The Fine Print
But isn’t it just a copy?
Plugin Problems: Security
<% String eid = request.getParameter("eid"); %> ...Employee ID: <%= eid %>
Plugin Problems: Performance
java.lang.OutOfMemoryError: Java heap space at org.apache.xerces.dom.DeferredDocumentImpl.createChunk(Unknown Source) at org.apache.xerces.dom.DeferredDocumentImpl.ensureCapacity(Unknown Source) at org.apache.xerces.dom.DeferredDocumentImpl.createNode(Unknown Source) at org.apache.xerces.dom.DeferredDocumentImpl.createDeferredEntityReference(Unknown Source) at org.apache.xerces.parsers.AbstractDOMParser.startGeneralEntity(Unknown Source) at org.apache.xerces.impl.dtd.XMLDTDValidator.startGeneralEntity(Unknown Source) at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.startEntity(Unknown Source) at org.apache.xerces.impl.XMLDocumentScannerImpl.startEntity(Unknown Source) at org.apache.xerces.impl.XMLEntityManager.startEntity(Unknown Source) at org.apache.xerces.impl.XMLEntityManager.startEntity(Unknown Source
Plugin Problems: Upgrades
Everyone’s AngryQA
Plugin Dev
Admins
Product Dev
Introducing Remote Apps
This is a Remote App
• A single descriptor file
• Can be in XML, YAML, or JSON
• Contains
• App metadata
• Permissions
• Extension points
key: app1name: Remote App - app1version: 1display-url: http://example.comdescription: This appvendor: name: Atlassian url: http://atlassian.com
permissions: permission: - scope: browse_projects
general-page: - key: first name: First url: /first
Examples: General Page
Plugin Steps
1. Add web-item config
2. Add servlet config
3. Code servlet
4. Try to get output decorated
Remote App Steps
1. Add general-page config
2. Implement URL
Examples: React to Event
Plugin Steps
1. Add component config
2. Dig through code to find event to listen for
3. Register for events
4. Handle event
5. Make sure to unregister
Remote App Steps
1. Add web-hook config
2. Implement URL
Examples: Custom Macro Editor
Plugin Steps
1. Add web-resource and rest config
2. Find and call JavaScript API to override edit button
3. Create AUI dialog
4. Implement REST resources
Remote App Steps
1. Add custom-macro-editor config
2. Implement URL
Shipping….Now.
Apps: Dropbox by AppFusions
• Share your documents
• Written in Java, deployed at Contegix
• Provides a custom page macro, and macro editor with MS Office edit
Apps: TFS4JIRA by Spartez
• Integrate JIRA with your Team Foundation Server
• Written in Java, deployed at Heroku
• Uses project and issue tabs, and an admin page
Apps: Lucidchart by . . .
Steps to create Lucidchart app
• Establish Trust
• Register App
• Implement General Page
• Implement macro
Establish Trust
Steps to create Lucidchart app
• Establish Trust
• Register App
• Implement General Page
• Implement macro
Register the app
• Receive from OnDemand as Request params:
• OAuth consumer key
• OAuth RSA public key
• Base URL
Register the app
• Return to OnDemand description of the remote app
• Can be in XML, JSON or YAML
Provide OAuth consumer key and RSA public key<remote-app key=“xxxxxxx" name="Lucidchart" version="1" icon-url="http://www.lucidchart.com/favicon.ico" display-url="https://www.lucidchart.com/ondemand"> <vendor name="Lucidchart" url="http://www.lucidchart.com" /> <description>Lucidchart example</description> <oauth …> <public-key>yyyyyy</public-key> </oauth>...
Request permissions
...<permissions> <permission scope="read_users_and_groups" /> <permission scope="read_content" /> <permission scope="modify_attachments" /> <permission scope="modify_content" /></permissions>...
General Page...<general-page section="system.content.add/space" key="lucidAppGeneral" link-name="Lucidchart Diagram" name="Select or create a diagram" url="/doclist" width="1200" height="800" icon-url="/icon"> <description>Insert or Create a Lucidchart Diagram</description> <context-param name="page_id" /></general-page>...
General Page...<general-page section="system.content.add/space" key="lucidAppGeneral" link-name="Lucidchart Diagram" name="Select or create a diagram" url="/doclist" width="1200" height="800" icon-url="/icon"> <description>Insert or Create a Lucidchart Diagram</description> <context-param name="page_id" /></general-page>...
General Page...<general-page section="system.content.add/space" key="lucidAppGeneral" link-name="Lucidchart Diagram" name="Select or create a diagram" url="/doclist" width="1200" height="800" icon-url="/icon"> <description>Insert or Create a Lucidchart Diagram</description> <context-param name="page_id" /></general-page>...
General Page...<general-page section="system.content.add/space" key="lucidAppGeneral" link-name="Lucidchart Diagram" name="Select or create a diagram" url="/doclist" width="1200" height="800" icon-url="/icon"> <description>Insert or Create a Lucidchart Diagram</description> <context-param name="page_id" /></general-page>...
Macro...<macro key="lucidchart" url="/macro" output-type="block" body-type="none"> <description>Create a Lucidchart Diagram</description> <category name="development" /> <image-placeholder url="/imagePlaceHolder" width="140" height="140" apply-chrome="true"/> <parameters> <parameter name="width" title="Width" type="string" default=“700"/> <parameter name="height" title="Height (if blank, image ratio is preserved)" type="string" /> <parameter name="align" type="enum" default="Left"> <value name="Left"/> <value name="Right" /> </parameter> </parameters></macro>
Macro...<macro key="lucidchart" url="/macro" output-type="block" body-type="none"> <description>Create a Lucidchart Diagram</description> <category name="development" /> <image-placeholder url="/imagePlaceHolder" width="140" height="140" apply-chrome="true"/> <parameters> <parameter name="width" title="Width" type="string" default=“700"/> <parameter name="height" title="Height (if blank, image ratio is preserved)" type="string" /> <parameter name="align" type="enum" default="Left"> <value name="Left"/> <value name="Right" /> </parameter> </parameters></macro>
Macro...<macro key="lucidchart" url="/macro" output-type="block" body-type="none"> <description>Create a Lucidchart Diagram</description> <category name="development" /> <image-placeholder url="/imagePlaceHolder" width="140" height="140" apply-chrome="true"/> <parameters> <parameter name="width" title="Width" type="string" default=“700"/> <parameter name="height" title="Height (if blank, image ratio is preserved)" type="string" /> <parameter name="align" type="enum" default="Left"> <value name="Left"/> <value name="Right" /> </parameter> </parameters></macro>
Macro...<macro key="lucidchart" url="/macro" output-type="block" body-type="none"> <description>Create a Lucidchart Diagram</description> <category name="development" /> <image-placeholder url="/imagePlaceHolder" width="140" height="140"/> <parameters> <parameter name="width" title="Width" type="string" default=“700"/> <parameter name="height" title="Height" type="string" /> <parameter name="align" type="enum" default="Left"> <value name="Left"/> <value name="Right" /> </parameter> </parameters></macro>
Steps to create Lucidchart app
• Establish Trust
• Register App
• Implement General Page
• Implement macro
Any language can be used
• Lucidchart is using PHP
Add Diagram (general page)function doclist() { if (!$this->_hasValidOAuthSignature()) { $this->set("oauthValid", false); return; }
$userId = @$_REQUEST['user_id']; $pageId = @$_REQUEST['page_id'];
$redirectURL = "https://" . $_SERVER['SERVER_NAME'] . '/ondemand/attachDocument?page_id=' . $pageId . "&user_id=" . $userId;
$this->layout = ‘ondemand'; $this->set("baseUrl", $this->_getBaseURL()); $this->set("callback", urlencode($redirectURL));}
Add Diagram (general page)function doclist() { if (!$this->_hasValidOAuthSignature()) { $this->set("oauthValid", false); return; }
$userId = @$_REQUEST['user_id']; $pageId = @$_REQUEST['page_id'];
$redirectURL = "https://" . $_SERVER['SERVER_NAME'] . '/ondemand/attachDocument?page_id=' . $pageId . "&user_id=" . $userId;
$this->layout = ‘ondemand'; $this->set("baseUrl", $this->_getBaseURL()); $this->set("callback", urlencode($redirectURL));}
Add Diagram (general page)function doclist() { if (!$this->_hasValidOAuthSignature()) { $this->set("oauthValid", false); return; }
$userId = @$_REQUEST['user_id']; $pageId = @$_REQUEST['page_id'];
$redirectURL = "https://" . $_SERVER['SERVER_NAME'] . '/ondemand/attachDocument?page_id=' . $pageId . "&user_id=" . $userId;
$this->layout = ‘ondemand'; $this->set("baseUrl", $this->_getBaseURL()); $this->set("callback", urlencode($redirectURL));}
Add Diagram (general page)function doclist() { if (!$this->_hasValidOAuthSignature()) { $this->set("oauthValid", false); return; }
$userId = @$_REQUEST['user_id']; $pageId = @$_REQUEST['page_id'];
$redirectURL = "https://" . $_SERVER['SERVER_NAME'] . '/ondemand/attachDocument?page_id=' . $pageId . "&user_id=" . $userId;
$this->layout = ‘ondemand'; $this->set("baseUrl", $this->_getBaseURL()); $this->set("callback", urlencode($redirectURL));}
Make API calls to attach diagramfunction attachDocument() {...
$fullUrl = $baseURL . "/rpc/xmlrpc?user_id=" . $userId; $auth_header = $this->_getAuthHeader($fullUrl); xmlrpc_set_type($imageBytes, "base64"); $attachmentMetaData = array("fileName"=>$attachmentName, "contentType"=>"image/png", "comment"=>"Imported from Lucidchart (do not deleted)"); $addAttachmentBody= xmlrpc_encode_request( "confluence2.addAttachment", array("", $pageId, $attachmentMetaData ,$imageBytes)); $this->_sendXMLRPCRequest($fullUrl, array( "Content-Type: text/xml", $auth_header), $addAttachmentBody);...
}
Make API calls to attach diagramfunction attachDocument() {...
$fullUrl = $baseURL . "/rpc/xmlrpc?user_id=" . $userId; $auth_header = $this->_getAuthHeader($fullUrl); xmlrpc_set_type($imageBytes, "base64"); $attachmentMetaData = array("fileName"=>$attachmentName, "contentType"=>"image/png", "comment"=>"Imported from Lucidchart (do not deleted)"); $addAttachmentBody= xmlrpc_encode_request( "confluence2.addAttachment", array("", $pageId, $attachmentMetaData ,$imageBytes)); $this->_sendXMLRPCRequest($fullUrl, array( "Content-Type: text/xml", $auth_header), $addAttachmentBody);...
}
Make API calls to attach diagramfunction attachDocument() {...
$fullUrl = $baseURL . "/rpc/xmlrpc?user_id=" . $userId; $auth_header = $this->_getAuthHeader($fullUrl); xmlrpc_set_type($imageBytes, "base64"); $attachmentMetaData = array("fileName"=>$attachmentName, "contentType"=>"image/png", "comment"=>"Imported from Lucidchart (do not deleted)"); $addAttachmentBody= xmlrpc_encode_request( "confluence2.addAttachment", array("", $pageId, $attachmentMetaData ,$imageBytes)); $this->_sendXMLRPCRequest($fullUrl, array( "Content-Type: text/xml", $auth_header), $addAttachmentBody);...
}
Make API calls to attach diagramfunction attachDocument() {...
$fullUrl = $baseURL . "/rpc/xmlrpc?user_id=" . $userId; $auth_header = $this->_getAuthHeader($fullUrl); xmlrpc_set_type($imageBytes, "base64"); $attachmentMetaData = array("fileName"=>$attachmentName, "contentType"=>"image/png", "comment"=>"Imported from Lucidchart (do not deleted)"); $addAttachmentBody= xmlrpc_encode_request( "confluence2.addAttachment", array("", $pageId, $attachmentMetaData ,$imageBytes)); $this->_sendXMLRPCRequest($fullUrl, array( "Content-Type: text/xml", $auth_header), $addAttachmentBody);...
}
Steps to create Lucidchart app
• Establish Trust
• Register App
• Implement General Page
• Implement macro
Macro
• Receive macro params
• Return HTML
• Will be cached (caching header can be returned)
• No CSS, inline styles or JavaScript
Macro : Receive macro paramsfunction macro() {
$userId = $_REQUEST['user_id']; $name = $_REQUEST['name']; $pageId = $_REQUEST['pageId']; $docId = $_REQUEST['id']; $width= $_REQUEST['width']; $height = $_REQUEST['height']; $align = $_REQUEST['align']; $outputType = $_REQUEST['ctx_output_type'];...
Macro: Return HTML<div style="float: “ . $align . "; margin: 0 auto; width: " . (intVal($width) + 2) . "px;\"> <img src=“$pageAttachment['url']” width=“$width” height=“$height”/> <div style="text-align: center\"> <a href='sign://"$host/editDocument/$docId?attachment= $name'>Edit Diagram</a> | <a href=$baseUrl/plugins/servlet/remoteapps/lucidchart-app/lucidAppRemove?page_id=$pageId&doc_id=$docId&attachment=$name'>Remove Diagram</a> </div></div>
That’s it!
Straightforward approach to get your
app available for OnDemand
We’re taking Apps to the next level
What if you could . . .
• Tap into our 12k+ OnDemand customer base
• Register your app with one click
• Have full control over your app
If you want to integrate, call us!
• Currently targeted towards SaaS integrations
• Feature set in early stages
• Working on solution for “extensions”
Diagram here
#summit12
You can finally integrate your site into
OnDemand with Remote Apps
Thank you!
Getting started
https://remoteapps.jira.com