Plugins on OnDemand with Remote Apps - Atlassian Summit 2012

Preview:

Citation preview

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

Recommended