better front-end development in atlassian plugins

Post on 15-May-2015

3.233 Views

Category:

Technology

4 Downloads

Preview:

Click to see full reader

DESCRIPTION

Traditionally the UI of Atlassian plugins have been based on a typical old-school MVC frameworks (webwork, Struts) with little dynamic behaviour. But no more! JIRA Team Lead Wociech Seliga will show you have to develop sexier and more user-friendly plugins based on a modern stack using AUI, Soy templates, Backbone.js and friends.

TRANSCRIPT

Tuesday, April 3, 2012

The road from back-end to front-end programming

Wojciech SeligaJIRA Development Team Lead, AtlassianCo-founder, Spartez

Better front-end development in Atlassian plugins

2Tuesday, April 3, 2012

• 4+ years with Atlassian• 6+ years doing Atlassian plugin development:

• JIRA Importers Plugin

• JIRA Drag & Drop Attachments Plugin

• JIRA Mail Plugin

• ScreenSnipe for JIRA, ScreenSnipe for Confluence ...

• Veteran of old-school web development (Java)

About me

3Tuesday, April 3, 2012

A bit of history

4Tuesday, April 3, 2012

• 2002 - 2006 - Awesome UI, Web 1.0

A bit of history

4Tuesday, April 3, 2012

• 2002 - 2006 - Awesome UI, Web 1.0WebWork, XWork, JSP, Velocity, Freemarker

A bit of history

4Tuesday, April 3, 2012

• 2002 - 2006 - Awesome UI, Web 1.0WebWork, XWork, JSP, Velocity, Freemarker

• 2006 - 2009 - Features, features, features!

A bit of history

4Tuesday, April 3, 2012

• 2002 - 2006 - Awesome UI, Web 1.0WebWork, XWork, JSP, Velocity, Freemarker

• 2006 - 2009 - Features, features, features!Mostly back-end technologies

A bit of history

4Tuesday, April 3, 2012

• 2002 - 2006 - Awesome UI, Web 1.0WebWork, XWork, JSP, Velocity, Freemarker

• 2006 - 2009 - Features, features, features!Mostly back-end technologies

• 2009 - now - new Atlassian UI, Web 2.0+

A bit of history

4Tuesday, April 3, 2012

• Confluence 3.0+, JIRA 3.13.5+• Forms, Controls, Tabs, Inline Dialogs, ..., AJS

Evolution Step 1 - AUI

5Tuesday, April 3, 2012

• Confluence 3.0+, JIRA 3.13.5+• Forms, Controls, Tabs, Inline Dialogs, ..., AJS

Evolution Step 1 - AUI

5Tuesday, April 3, 2012

• Confluence 3.0+, JIRA 3.13.5+• Forms, Controls, Tabs, Inline Dialogs, ..., AJS

Evolution Step 1 - AUI

5Tuesday, April 3, 2012

• Confluence 3.0+, JIRA 3.13.5+• Forms, Controls, Tabs, Inline Dialogs, ..., AJS

Evolution Step 1 - AUI

5Tuesday, April 3, 2012

• Plugin Framework v2 only (JIRA 4+, Confluence 3.1+)• Easier AJAX for plugin developers unleashed

Evolution Step 2 - REST

6

<rest key="helloWorldRest" path="/helloworld" version="1.0"> <description>Hello world sample.</description></rest>

• World of Java annotations (Jersey)

Tuesday, April 3, 2012

7

@Path ("priority")@AnonymousAllowed@Consumes ({ MediaType.APPLICATION_JSON })@Produces ({ MediaType.APPLICATION_JSON })public class PriorityResource { // injected dependencies and the constructor here ... @GET @Path ("{id}") public Response getPriority(@PathParam ("id") final String id){ final Priority priority = constManager.getPriorityObject(id); if (priority == null) { throw new NotFoundWebException(ErrorCollection.of( i18n.getText("rest.priority.error.not.found", id))); }

return Response.ok(PriorityJsonBean.fullBean(priority, baseUrls)) .cacheControl(never()).build(); }}

Tuesday, April 3, 2012

injectable JS & CSS + REST = WIN

8Tuesday, April 3, 2012

• Confluence 2.10+, JIRA 4.2+• Easy resource injection to popular destinations• Easy to define own contexts

Evolution Step 3 (2010)web resource contexts

9

<web-resource key="quick-edit-issue"> <context>jira.view.issue</context> <context>jira.navigator.advanced</context> <context>jira.navigator.simple</context>

<!-- ... --> </web-resource>

Tuesday, April 3, 2012

Evolution Step 4 (2010)web-resource-transformer

10

<web-resource-transformer key="my-key" class="fqcn.must.implement.WebResourceTransformer"/>

Tuesday, April 3, 2012

Evolution Step 4 (2010)web-resource-transformer

10

<web-resource-transformer key="my-key" class="fqcn.must.implement.WebResourceTransformer"/>

public interface WebResourceTransformer { DownloadableResource transform(Element configElement, ResourceLocation location, String filePath, DownloadableResource nextResource);}

Tuesday, April 3, 2012

web-resource-transformers

11Tuesday, April 3, 2012

• I18n

web-resource-transformers

11Tuesday, April 3, 2012

• I18n• L&F

web-resource-transformers

11Tuesday, April 3, 2012

• I18n• L&F• context path

web-resource-transformers

11Tuesday, April 3, 2012

• I18n• L&F• context path• SASS, LESS

web-resource-transformers

11Tuesday, April 3, 2012

• I18n• L&F• context path• SASS, LESS• Soy

web-resource-transformers

11Tuesday, April 3, 2012

I18N

12Tuesday, April 3, 2012

I18N

12

hello.world=Hello Worldfrom.atlascamp=from AtlasCamp {0}

i18n resource file defined in atlassian-plugin.xml

Tuesday, April 3, 2012

I18N - web resource transformation definition

13Tuesday, April 3, 2012

I18N - web resource transformation definition

13

<resource name="ourname" type="i18n" location="path/to/i18n/properties/file/no/ext"/>

<web-resource key="our-key"> <dependency>com.atlassian.auiplugin:ajs</dependency> <transformation extension="js"> <transformer key="jsI18n"/> </transformation> <resource type="download" name="filename.js" location="path/to/filename.js"/></web-resource>

Tuesday, April 3, 2012

I18N transformation

14Tuesday, April 3, 2012

I18N transformation

14

var helloText = AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012)

Tuesday, April 3, 2012

I18N transformation

14

var helloText = AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012)

Tuesday, April 3, 2012

I18N transformation

14

var helloText = "Hello World" + " " + AJS.format("from AtlasCamp {0}", 2012)

var helloText = AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012)

Tuesday, April 3, 2012

15

Times of Hacking

Tuesday, April 3, 2012

15

Times of Hacking

JQuery

Prototype

underscore.js Javascript hacks

JQuery JQuery

JQueryJQuery

Javascript hacks

Javascript hacks

JQuery

JQuery

underscore.js

freestyle AJAX

freestyle AJAX

JQuery JQuery

JQuery

JQuery

JQuery

Javascript hacks

JQuery

JQuery

JQuery

JQuery

JQueryJQuery

JQuery

JQuery

JQueryJQuery

JQueryJQuery

JQuery JQuery

Javascript hacksJQueryJQuery

JQuery

JQuery

JQuery

JQuery JQuery

JQuery

underscore.js

Javascript hacks

JQuery

JQuery

JQuery

JQuery

JQuery

Tuesday, April 3, 2012

It's all too easy to create JavaScript applications

that end up as tangled piles of jQuery selectors and

callbacks, all trying frantically to keep data in sync

between the HTML UI, your JavaScript logic, and

the database on your server. For rich client-side

applications, a more structured approach is often

helpful..

Introduction to Backbone.js”

16Tuesday, April 3, 2012

Evolution Step 5 (2011)

17

Structure on the client side

Tuesday, April 3, 2012

Evolution Step 5 (2011)

17

Structure on the client side

For speed, beauty and maintainability

Tuesday, April 3, 2012

Evolution Step 5 (2011)

17

Structure on the client side

For speed, beauty and maintainability

FTW

Tuesday, April 3, 2012

• SproutCore• Sammy.js• Spine.js• Cappucino• Javascript MVC

• Ember.js• Angular.js• Batman.js• Mustache• Handlebars

18

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.js

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.js

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.js

MVC

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.jsM

VC

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.jsMV

C

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.jsMV

C

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

• Backbone.js

Tuesday, April 3, 2012

19

More structured approach in JS

• Soy (Google Closure Templates)

Tuesday, April 3, 2012

19

More structured approach in JS

Tuesday, April 3, 2012

Our Road to Soy

20Tuesday, April 3, 2012

• AJS.template

Our Road to Soy

20Tuesday, April 3, 2012

• AJS.template• Mustache

Our Road to Soy

20Tuesday, April 3, 2012

• AJS.template• Mustache• Soy

Our Road to Soy

20Tuesday, April 3, 2012

Soy Features

21Tuesday, April 3, 2012

• Simplicity

Soy Features

21Tuesday, April 3, 2012

• Simplicity• Logic and display separation

Soy Features

21Tuesday, April 3, 2012

• Simplicity• Logic and display separation• Client and server side (Javascript and Java)

Soy Features

21Tuesday, April 3, 2012

• Simplicity• Logic and display separation• Client and server side (Javascript and Java)• Client-side speed

Soy Features

21Tuesday, April 3, 2012

• Simplicity• Logic and display separation• Client and server side (Javascript and Java)• Client-side speed• Security (auto-escaping)

Soy Features

21Tuesday, April 3, 2012

• Simplicity• Logic and display separation• Client and server side (Javascript and Java)• Client-side speed• Security (auto-escaping)• Battle-tested by Google

Soy Features

21Tuesday, April 3, 2012

Soy - Example

22

{namespace examples.simple}/** * Greets a person using "Hello" by default. * @param name The name of the person. * @param? greetingWord Optional greeting word to use instead of "Hello". */{template .helloName} {if not $greetingWord} Hello {$name}! {else} {$greetingWord} {$name}! {/if}{/template}

Tuesday, April 3, 2012

Soy Syntax - Types

23

Type Examples

null null

Boolean false, true

Integer 123, -857, 0x123

Float 0.5, 123.0, 10.1e4

String 'Atlassian', '', 'foo-bar'

List [], [1, 'two', 3, [4, 'five']]

Map [:], ['key': 'value', 'key2': 'value2']

Tuesday, April 3, 2012

Soy Syntax - Operators

24

• - (unary) not• * / %• + - (binary)• < > <= >=• == !=• and• or• ?: (ternary)

Tuesday, April 3, 2012

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}• {if <expression>},

{elseif}, {else}, {/if}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}• {if <expression>},

{elseif}, {else}, {/if}

• {foreach}, {ifempty}, {/foreach}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}• {if <expression>},

{elseif}, {else}, {/if}

• {foreach}, {ifempty}, {/foreach}• {for}, {/for}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}• {if <expression>},

{elseif}, {else}, {/if}

• {foreach}, {ifempty}, {/foreach}• {for}, {/for}• {call}, {/call}, {param}, {/param}

Soy - Commands

25Tuesday, April 3, 2012

• {template}{/template}• {literal}{/literal}• {print <expression>}• {<expression>}• {if <expression>},

{elseif}, {else}, {/if}

• {foreach}, {ifempty}, {/foreach}• {for}, {/for}• {call}, {/call}, {param}, {/param}• {sp}, {\n}, {lb}, {rb}

Soy - Commands

25Tuesday, April 3, 2012

Soy - defining variables

26Tuesday, April 3, 2012

Soy - defining variables

26Tuesday, April 3, 2012

Soy - defining variables

26Keep business logic away from view!

Not Supported!Tuesday, April 3, 2012

•{getText('i18n-key', ....)

•{contextPath}

•{$data|truncate:30}

Useful functions

27Tuesday, April 3, 2012

28

Soy Javascript Compilation

{namespace JIRA.Templates.Demo}/*** Simplest Hello world demo* @param name*/{template .helloWorld}<div>Hello World, {$name}</div>{/template}

Tuesday, April 3, 2012

28Tuesday, April 3, 2012

29Tuesday, April 3, 2012

29

// This file was automatically generated from demo.soy.// Please don't edit this file by hand.

if (typeof JIRA == 'undefined') { var JIRA = {}; }if (typeof JIRA.Templates == 'undefined') { JIRA.Templates = {}; }if (typeof JIRA.Templates.Demo == 'undefined') { JIRA.Templates.Demo = {}; }

JIRA.Templates.Demo.helloWorld = function(opt_data, opt_sb) { var output = opt_sb || new soy.StringBuilder(); output.append('<div>Hello World, ',

soy.$$escapeHtml(opt_data.name), '</div>'); return opt_sb ? '' : output.toString();};

Tuesday, April 3, 2012

29

// This file was automatically generated from demo.soy.// Please don't edit this file by hand.

if (typeof JIRA == 'undefined') { var JIRA = {}; }if (typeof JIRA.Templates == 'undefined') { JIRA.Templates = {}; }if (typeof JIRA.Templates.Demo == 'undefined') { JIRA.Templates.Demo = {}; }

JIRA.Templates.Demo.helloWorld = function(opt_data, opt_sb) { var output = opt_sb || new soy.StringBuilder(); output.append('<div>Hello World, ',

soy.$$escapeHtml(opt_data.name), '</div>'); return opt_sb ? '' : output.toString();};

Tuesday, April 3, 2012

• implicit by default to HTML escaping• {namespace com.example autoescape="XXX"}

XXX may be true, false, contextual• disable for a single case with {$templateData|noAutoescape}

• sanitized data

Auto-escaping

30Tuesday, April 3, 2012

Contextual Auto-escaping

31Tuesday, April 3, 2012

Contextual Auto-escaping

31

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = '\x3e\x3cscript\x3ealert(\x22x\x26xx\x22)\x3c\/script\x3e'">&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = '\x3e\x3cscript\x3ealert(\x22x\x26xx\x22)\x3c\/script\x3e'">&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = '\x3e\x3cscript\x3ealert(\x22x\x26xx\x22)\x3c\/script\x3e'">&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = '\x3e\x3cscript\x3ealert(\x22x\x26xx\x22)\x3c\/script\x3e'">&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

Contextual Auto-escaping

31

<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = '\x3e\x3cscript\x3ealert(\x22x\x26xx\x22)\x3c\/script\x3e'">&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>

/** * @param name */{template .helloWorld autoescape="contextual"}<a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a>{/template}

{“name”: "><script>alert(\"x&xx\")</script>"}

Tuesday, April 3, 2012

• JIRA (gadgets, new project administration, mails, ...)• GreenHopper• PAC• ...

Soy @ Atlassian

32Tuesday, April 3, 2012

Soy and Atlassian Plugins

33

<web-resource key="my-key">

<transformation extension="soy"> <transformer key="soyTransformer"/> </transformation>

<resource type="download" name="my-name.js" location="path/to/my/template.soy"/></web-resource>

Tuesday, April 3, 2012

• Atlassian Plugin Framework favours Velocity• SoyTemplateRenderer/SoyTemplateRendererProvider • DefaultVelocityContextProvider (jira-core)• SoyData, SoyDataList, SoyDataMap,

SoyData.createFromExistingData()

Server-side Soy

34Tuesday, April 3, 2012

Soy - Coding Example

35Tuesday, April 3, 2012

• {call} in Soy• web-resource <dependency>

Soy - Template Library

36Tuesday, April 3, 2012

Backbone.js

37Tuesday, April 3, 2012

• Event-driven• Models• Views (responsible for keeping markup in sync with

model)

Backbone.js

38Tuesday, April 3, 2012

39

Our story with backbone.js

Tuesday, April 3, 2012

• Version/Component/People management in JIRA (Ignite) - 2011

39

Our story with backbone.js

Tuesday, April 3, 2012

• Version/Component/People management in JIRA (Ignite) - 2011

• JIRA Importers Plugin, JIRA Mail Plugin - 2011

39

Our story with backbone.js

Tuesday, April 3, 2012

• Version/Component/People management in JIRA (Ignite) - 2011

• JIRA Importers Plugin, JIRA Mail Plugin - 2011• RAB - 2011

39

Our story with backbone.js

Tuesday, April 3, 2012

• Version/Component/People management in JIRA (Ignite) - 2011

• JIRA Importers Plugin, JIRA Mail Plugin - 2011• RAB - 2011• GH RapidBoard, New Issue Nav - 2011, 2012

39

Our story with backbone.js

Tuesday, April 3, 2012

Why Backbone

40Tuesday, April 3, 2012

Why Backbone

40

backbone.js

Tuesday, April 3, 2012

JIRA is a backbone

Why Backbone

40

backbone.js

Tuesday, April 3, 2012

JIRA is a backbone

Why Backbone

40

backbone.js

Tuesday, April 3, 2012

Why Backbone.js

41Tuesday, April 3, 2012

• Small

Why Backbone.js

41Tuesday, April 3, 2012

• Small• Flexible

Why Backbone.js

41Tuesday, April 3, 2012

• Small• Flexible• Does not impose any templating technologies

Why Backbone.js

41Tuesday, April 3, 2012

• Small• Flexible• Does not impose any templating technologies• Well documented

Why Backbone.js

41Tuesday, April 3, 2012

• Small• Flexible• Does not impose any templating technologies• Well documented• Popular

Why Backbone.js

41Tuesday, April 3, 2012

• Small• Flexible• Does not impose any templating technologies• Well documented• Popular• Liked by us

Why Backbone.js

41Tuesday, April 3, 2012

42Tuesday, April 3, 2012

42

DOM / Markup

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQuery

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQuery

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

REST API Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

REST API Backbone.js

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

REST API Backbone.js

ServicesManagers

Tuesday, April 3, 2012

42

DOM / Markup

Soy (client)

Javascript

JQueryAUI

REST API Backbone.js

ServicesManagers

Tuesday, April 3, 2012

• Easier, more powerful and efficient web resource transformations

• Better support for Soy on the server side (like Velocity or FreeMarker)

• (?) Dynamic injection of needed resources on-the-fly (inline dialogs)

Possible Future

43Tuesday, April 3, 2012

44

Looking back...

Tuesday, April 3, 2012

44

Looking back...

2.5 years ago...

Tuesday, April 3, 2012

44

Looking back...

most of this stuff was not possible2.5 years ago...

Tuesday, April 3, 2012

Don’t underestimate the power of the client-side

programming. Time to learn Javascript and

related frameworks, you old Java fellow

Master Joda, Javascript convert

”“

45Tuesday, April 3, 2012

#atlascamp

TAKE-AWAYS

Atlassian is moving fast to client-side programming.

Technology is there. Are you ready?“ ”

46Tuesday, April 3, 2012

Thank you!

Tuesday, April 3, 2012

top related