Better front-end development
in Atlassian plugins
The road from back-end to front-end programming




Wojciech Seliga
JIRA Development Team Lead, Atlassian
Co-founder, Spartez
                                                  2
About me
• 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)
                                                         3
A bit of history




                   4
A bit of history
• 2002 - 2006 - Awesome UI, Web 1.0




                                      4
A bit of history
• 2002 - 2006 - Awesome UI, Web 1.0
    WebWork, XWork, JSP, Velocity, Freemarker




                                                4
A bit of history
• 2002 - 2006 - Awesome UI, Web 1.0
     WebWork, XWork, JSP, Velocity, Freemarker
• 2006 - 2009 - Features, features, features!




                                                 4
A bit of history
• 2002 - 2006 - Awesome UI, Web 1.0
     WebWork, XWork, JSP, Velocity, Freemarker
• 2006 - 2009 - Features, features, features!
     Mostly back-end technologies




                                                 4
A bit of history
• 2002 - 2006 - Awesome UI, Web 1.0
     WebWork, XWork, JSP, Velocity, Freemarker
• 2006 - 2009 - Features, features, features!
     Mostly back-end technologies
• 2009 - now - new Atlassian UI, Web 2.0+


                                                 4
Evolution Step 1 - AUI
• Confluence 3.0+, JIRA 3.13.5+
• Forms, Controls, Tabs, Inline Dialogs, ..., AJS




                                                    5
Evolution Step 1 - AUI
• Confluence 3.0+, JIRA 3.13.5+
• Forms, Controls, Tabs, Inline Dialogs, ..., AJS




                                                    5
Evolution Step 1 - AUI
• Confluence 3.0+, JIRA 3.13.5+
• Forms, Controls, Tabs, Inline Dialogs, ..., AJS




                                                    5
Evolution Step 1 - AUI
• Confluence 3.0+, JIRA 3.13.5+
• Forms, Controls, Tabs, Inline Dialogs, ..., AJS




                                                    5
Evolution Step 2 - REST
 • Plugin Framework v2 only (JIRA 4+, Confluence 3.1+)
 • Easier AJAX for plugin developers unleashed

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

 • World of Java annotations (Jersey)

                                                               6
@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();
    }
}

                                                                            7
injectable JS & CSS
+ REST = WIN




                      8
Evolution Step 3 (2010)
web resource contexts
• Confluence 2.10+, JIRA 4.2+
• Easy resource injection to popular destinations
• Easy to define own contexts
  <web-resource key="quick-edit-issue">
          <context>jira.view.issue</context>
          <context>jira.navigator.advanced</context>
          <context>jira.navigator.simple</context>
          <!-- ... -->
      </web-resource>
                                                       9
Evolution Step 4 (2010)
web-resource-transformer
<web-resource-transformer key="my-key"
      class="fqcn.must.implement.WebResourceTransformer"/>




                                                             10
Evolution Step 4 (2010)
web-resource-transformer
<web-resource-transformer key="my-key"
      class="fqcn.must.implement.WebResourceTransformer"/>

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



                                                             10
web-resource-transformers




                            11
web-resource-transformers
• I18n




                            11
web-resource-transformers
• I18n
• L&F




                            11
web-resource-transformers
• I18n
• L&F
• context path




                            11
web-resource-transformers
• I18n
• L&F
• context path
• SASS, LESS




                            11
web-resource-transformers
• I18n
• L&F
• context path
• SASS, LESS
• Soy


                            11
I18N




       12
I18N

   i18n resource file defined in atlassian-plugin.xml
          hello.world=Hello World
          from.atlascamp=from AtlasCamp {0}




                                                        12
I18N - web resource
transformation definition




                           13
I18N - web resource
 transformation definition
<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>


                                                             13
I18N transformation




                      14
I18N transformation

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




                                                        14
I18N transformation

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




                                                        14
I18N transformation

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




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


                                                        14
Times of Hacking




                   15
Times of Hacking
  Javascript hacks
                                                            freestyle AJAX
                          JQuery                                                                    Javascript hacks
     JQuery                                JQuery                                        JQuery
                                                       JQuery
                         JQuery                                           JQuery
JQuery
               Javascript hacks                            JQuery     JQuery          JQuery            JQuery
                                       JQuery
   JQuery
                                           underscore.js        Javascript hacks
            JQuery                                                                             JQuery        JQuery
                          JQuery       JQuery                                      JQuery
  JQuery                      JQuery                        Prototype
                                                                               JQuery        JQuery       JQuery
         JQuery      JQuery                JQuery            JQuery
     Javascript hacks
                                  JQuery               Javascript hacks                 underscore.js
                                           JQuery                       JQuery                          JQuery
  JQuery             freestyle AJAX                                                        JQuery
                                              JQuery         JQuery          JQuery
         underscore.js                                                                                                 15
“
    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

                                                                         16
Evolution Step 5 (2011)


      Structure on the client side




                                     17
Evolution Step 5 (2011)


      Structure on the client side

 For speed, beauty and maintainability


                                         17
Evolution Step 5 (2011)


      Structure on the client side

 For speed, beauty and maintainability

                 FTW
                                         17
More structured approach in JS
• Backbone.js      • Ember.js
• SproutCore       • Angular.js
• Sammy.js         • Batman.js
• Spine.js         • Mustache
• Cappucino        • Handlebars
• Javascript MVC   • Soy (Google Closure Templates)
                                                      18
More structured approach in JS

         • Backbone.js
         • Soy (Google Closure Templates)




                                            19
More structured approach in JS

         • Backbone.js
         • Soy (Google Closure Templates)


                 MV C
                                            19
More structured approach in JS

        M Backbone.js
         •
         • Soy (Google Closure Templates)


                    VC
                                            19
More structured approach in JS

        M Backbone.js
         •
                 V
         • Soy (Google Closure Templates)


                        C
                                            19
More structured approach in JS

        M Backbone.js C
         •
                 V
         • Soy (Google Closure Templates)




                                            19
More structured approach in JS

         • Backbone.js
         • Soy (Google Closure Templates)




                                            19
More structured approach in JS


         • Soy (Google Closure Templates)




                                            19
More structured approach in JS




                                 19
Our Road to Soy




                  20
Our Road to Soy
• AJS.template




                  20
Our Road to Soy
• AJS.template
• Mustache




                  20
Our Road to Soy
• AJS.template
• Mustache
• Soy




                  20
Soy Features




               21
Soy Features
• Simplicity




               21
Soy Features
• Simplicity
• Logic and display separation




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




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




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


                                                 21
Soy Features
• Simplicity
• Logic and display separation
• Client and server side (Javascript and Java)
• Client-side speed
• Security (auto-escaping)
• Battle-tested by Google
                                                 21
Soy - Example
{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}
                                                                   22
Soy Syntax - Types
      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']

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

                              24
Soy - Commands




                 25
Soy - Commands
• {template}{/template}




                          25
Soy - Commands
• {template}{/template}
• {literal}{/literal}




                          25
Soy - Commands
• {template}{/template}
• {literal}{/literal}
• {print <expression>}




                          25
Soy - Commands
• {template}{/template}
• {literal}{/literal}
• {print <expression>}
• {<expression>}




                          25
Soy - Commands
• {template}{/template}
• {literal}{/literal}
• {print <expression>}
• {<expression>}
• {if <expression>},
  {elseif}, {else}, {/if}

                            25
Soy - Commands
• {template}{/template}     • {foreach}, {ifempty}, {/foreach}
• {literal}{/literal}
• {print <expression>}
• {<expression>}
• {if <expression>},
  {elseif}, {else}, {/if}

                                                                 25
Soy - Commands
• {template}{/template}     • {foreach}, {ifempty}, {/foreach}
• {literal}{/literal}       • {for}, {/for}
• {print <expression>}
• {<expression>}
• {if <expression>},
  {elseif}, {else}, {/if}

                                                                 25
Soy - Commands
• {template}{/template}     • {foreach}, {ifempty}, {/foreach}
• {literal}{/literal}       • {for}, {/for}
• {print <expression>}      • {call}, {/call}, {param}, {/param}
• {<expression>}
• {if <expression>},
  {elseif}, {else}, {/if}

                                                                   25
Soy - Commands
• {template}{/template}     • {foreach}, {ifempty}, {/foreach}
• {literal}{/literal}       • {for}, {/for}
• {print <expression>}      • {call}, {/call}, {param}, {/param}
• {<expression>}            • {sp}, {n}, {lb}, {rb}
• {if <expression>},
  {elseif}, {else}, {/if}

                                                                   25
Soy - defining variables




                          26
Soy - defining variables




                          26
Soy - defining variables




Not Supported!
   Keep business logic away from view!   26
Useful functions
 • {getText('i18n-key', ....)
 • {contextPath}
 • {$data|truncate:30}




                                27
Soy Javascript Compilation

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



                                        28
28
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();
};


                                                                 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();
};


                                                                 29
Auto-escaping
• 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

                                             30
Contextual Auto-escaping




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




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




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

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




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

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


<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E"
onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'">
&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>



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

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


<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E"
onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'">
&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>



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

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


<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E"
onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'">
&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>



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

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


<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E"
onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'">
&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>



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

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


<a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E"
onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'">
&gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a>



                                                                                  31
Soy @ Atlassian
• JIRA (gadgets, new project administration, mails, ...)
• GreenHopper
• PAC
• ...




                                                           32
Soy and Atlassian Plugins
<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>




                                                         33
Server-side Soy
• Atlassian Plugin Framework favours Velocity
• SoyTemplateRenderer/SoyTemplateRendererProvider
• DefaultVelocityContextProvider (jira-core)
• SoyData, SoyDataList, SoyDataMap,
  SoyData.createFromExistingData()



                                                    34
Soy - Coding Example




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




                               36
Backbone.js



              37
Backbone.js
• Event-driven
• Models
• Views (responsible for keeping markup in sync with
  model)




                                                       38
Our story with backbone.js




                             39
Our story with backbone.js
• Version/Component/People management in JIRA
  (Ignite) - 2011




                                                39
Our story with backbone.js
• Version/Component/People management in JIRA
  (Ignite) - 2011
• JIRA Importers Plugin, JIRA Mail Plugin - 2011




                                                   39
Our story with backbone.js
• Version/Component/People management in JIRA
  (Ignite) - 2011
• JIRA Importers Plugin, JIRA Mail Plugin - 2011
• RAB - 2011




                                                   39
Our story with backbone.js
• 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
Why Backbone




               40
Why Backbone

         backbone.js




                       40
Why Backbone

         backbone.js

      JIRA is a backbone



                           40
Why Backbone



      JIRA is a backbone
                backbone.js



                              40
Why Backbone.js




                  41
Why Backbone.js
• Small




                  41
Why Backbone.js
• Small
• Flexible




                  41
Why Backbone.js
• Small
• Flexible
• Does not impose any templating technologies




                                                41
Why Backbone.js
• Small
• Flexible
• Does not impose any templating technologies
• Well documented




                                                41
Why Backbone.js
• Small
• Flexible
• Does not impose any templating technologies
• Well documented
• Popular


                                                41
Why Backbone.js
• Small
• Flexible
• Does not impose any templating technologies
• Well documented
• Popular
• Liked by us
                                                41
42
DOM / Markup

               42
Javascript


DOM / Markup

                        42
Javascript


DOM / Markup

                        42
JQuery


           Javascript


DOM / Markup

                                 42
JQuery


           Javascript


DOM / Markup

                                 42
AUI                JQuery


            Javascript


DOM / Markup

                                  42
AUI                JQuery


            Javascript


DOM / Markup

                                  42
AUI                JQuery


            Javascript


DOM / Markup

                                  42
AUI                JQuery


            Javascript


DOM / Markup

                                  42
Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Backbone.js




Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Backbone.js




Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Backbone.js




Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Backbone.js




Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
Backbone.js




Soy (client)         AUI                JQuery


                           Javascript


               DOM / Markup

                                                 42
REST API                       Backbone.js




           Soy (client)         AUI                JQuery


                                      Javascript


                          DOM / Markup

                                                            42
REST API                       Backbone.js




           Soy (client)         AUI                JQuery


                                      Javascript


                          DOM / Markup

                                                            42
REST API                       Backbone.js


Services
Managers   Soy (client)         AUI                JQuery


                                      Javascript


                          DOM / Markup

                                                            42
REST API                       Backbone.js


Services
Managers   Soy (client)         AUI                JQuery


                                      Javascript


                          DOM / Markup

                                                            42
Possible Future
• 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)


                                                             43
Looking back...




                  44
Looking back...


2.5 years ago...




                   44
Looking back...


2.5 years ago...
most of this stuff was not possible


                                      44
“   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




                                                                          45
TAKE-AWAYS




“   Atlassian is moving fast to client-side programming.
    Technology is there. Are you ready?
                                          ”
     #atlascamp


                                                           46
Thank you!

Better Front-end Development in Atlassian Plugins

  • 2.
    Better front-end development inAtlassian plugins The road from back-end to front-end programming Wojciech Seliga JIRA Development Team Lead, Atlassian Co-founder, Spartez 2
  • 3.
    About me • 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) 3
  • 4.
    A bit ofhistory 4
  • 5.
    A bit ofhistory • 2002 - 2006 - Awesome UI, Web 1.0 4
  • 6.
    A bit ofhistory • 2002 - 2006 - Awesome UI, Web 1.0 WebWork, XWork, JSP, Velocity, Freemarker 4
  • 7.
    A bit ofhistory • 2002 - 2006 - Awesome UI, Web 1.0 WebWork, XWork, JSP, Velocity, Freemarker • 2006 - 2009 - Features, features, features! 4
  • 8.
    A bit ofhistory • 2002 - 2006 - Awesome UI, Web 1.0 WebWork, XWork, JSP, Velocity, Freemarker • 2006 - 2009 - Features, features, features! Mostly back-end technologies 4
  • 9.
    A bit ofhistory • 2002 - 2006 - Awesome UI, Web 1.0 WebWork, XWork, JSP, Velocity, Freemarker • 2006 - 2009 - Features, features, features! Mostly back-end technologies • 2009 - now - new Atlassian UI, Web 2.0+ 4
  • 10.
    Evolution Step 1- AUI • Confluence 3.0+, JIRA 3.13.5+ • Forms, Controls, Tabs, Inline Dialogs, ..., AJS 5
  • 11.
    Evolution Step 1- AUI • Confluence 3.0+, JIRA 3.13.5+ • Forms, Controls, Tabs, Inline Dialogs, ..., AJS 5
  • 12.
    Evolution Step 1- AUI • Confluence 3.0+, JIRA 3.13.5+ • Forms, Controls, Tabs, Inline Dialogs, ..., AJS 5
  • 13.
    Evolution Step 1- AUI • Confluence 3.0+, JIRA 3.13.5+ • Forms, Controls, Tabs, Inline Dialogs, ..., AJS 5
  • 14.
    Evolution Step 2- REST • Plugin Framework v2 only (JIRA 4+, Confluence 3.1+) • Easier AJAX for plugin developers unleashed <rest key="helloWorldRest" path="/helloworld" version="1.0"> <description>Hello world sample.</description> </rest> • World of Java annotations (Jersey) 6
  • 15.
    @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(); } } 7
  • 16.
    injectable JS &CSS + REST = WIN 8
  • 17.
    Evolution Step 3(2010) web resource contexts • Confluence 2.10+, JIRA 4.2+ • Easy resource injection to popular destinations • Easy to define own contexts <web-resource key="quick-edit-issue"> <context>jira.view.issue</context> <context>jira.navigator.advanced</context> <context>jira.navigator.simple</context> <!-- ... --> </web-resource> 9
  • 18.
    Evolution Step 4(2010) web-resource-transformer <web-resource-transformer key="my-key" class="fqcn.must.implement.WebResourceTransformer"/> 10
  • 19.
    Evolution Step 4(2010) web-resource-transformer <web-resource-transformer key="my-key" class="fqcn.must.implement.WebResourceTransformer"/> public interface WebResourceTransformer { DownloadableResource transform(Element configElement, ResourceLocation location, String filePath, DownloadableResource nextResource); } 10
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
    web-resource-transformers • I18n • L&F •context path • SASS, LESS 11
  • 25.
    web-resource-transformers • I18n • L&F •context path • SASS, LESS • Soy 11
  • 26.
  • 27.
    I18N i18n resource file defined in atlassian-plugin.xml hello.world=Hello World from.atlascamp=from AtlasCamp {0} 12
  • 28.
    I18N - webresource transformation definition 13
  • 29.
    I18N - webresource transformation definition <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> 13
  • 30.
  • 31.
    I18N transformation var helloText= AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012) 14
  • 32.
    I18N transformation var helloText= AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012) 14
  • 33.
    I18N transformation var helloText= AJS.I18n.getText("hello.world") + " " + AJS.I18n.getText("from.atlascamp", 2012) var helloText = "Hello World" + " " + AJS.format("from AtlasCamp {0}", 2012) 14
  • 34.
  • 35.
    Times of Hacking Javascript hacks freestyle AJAX JQuery Javascript hacks JQuery JQuery JQuery JQuery JQuery JQuery JQuery Javascript hacks JQuery JQuery JQuery JQuery JQuery JQuery underscore.js Javascript hacks JQuery JQuery JQuery JQuery JQuery JQuery JQuery JQuery Prototype JQuery JQuery JQuery JQuery JQuery JQuery JQuery Javascript hacks JQuery Javascript hacks underscore.js JQuery JQuery JQuery JQuery freestyle AJAX JQuery JQuery JQuery JQuery underscore.js 15
  • 36.
    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 16
  • 37.
    Evolution Step 5(2011) Structure on the client side 17
  • 38.
    Evolution Step 5(2011) Structure on the client side For speed, beauty and maintainability 17
  • 39.
    Evolution Step 5(2011) Structure on the client side For speed, beauty and maintainability FTW 17
  • 40.
    More structured approachin JS • Backbone.js • Ember.js • SproutCore • Angular.js • Sammy.js • Batman.js • Spine.js • Mustache • Cappucino • Handlebars • Javascript MVC • Soy (Google Closure Templates) 18
  • 41.
    More structured approachin JS • Backbone.js • Soy (Google Closure Templates) 19
  • 42.
    More structured approachin JS • Backbone.js • Soy (Google Closure Templates) MV C 19
  • 43.
    More structured approachin JS M Backbone.js • • Soy (Google Closure Templates) VC 19
  • 44.
    More structured approachin JS M Backbone.js • V • Soy (Google Closure Templates) C 19
  • 45.
    More structured approachin JS M Backbone.js C • V • Soy (Google Closure Templates) 19
  • 46.
    More structured approachin JS • Backbone.js • Soy (Google Closure Templates) 19
  • 47.
    More structured approachin JS • Soy (Google Closure Templates) 19
  • 48.
  • 49.
  • 50.
    Our Road toSoy • AJS.template 20
  • 51.
    Our Road toSoy • AJS.template • Mustache 20
  • 52.
    Our Road toSoy • AJS.template • Mustache • Soy 20
  • 53.
  • 54.
  • 55.
    Soy Features • Simplicity •Logic and display separation 21
  • 56.
    Soy Features • Simplicity •Logic and display separation • Client and server side (Javascript and Java) 21
  • 57.
    Soy Features • Simplicity •Logic and display separation • Client and server side (Javascript and Java) • Client-side speed 21
  • 58.
    Soy Features • Simplicity •Logic and display separation • Client and server side (Javascript and Java) • Client-side speed • Security (auto-escaping) 21
  • 59.
    Soy Features • Simplicity •Logic and display separation • Client and server side (Javascript and Java) • Client-side speed • Security (auto-escaping) • Battle-tested by Google 21
  • 60.
    Soy - Example {namespaceexamples.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} 22
  • 61.
    Soy Syntax -Types 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'] 23
  • 62.
    Soy Syntax -Operators • - (unary) not • * / % • + - (binary) • < > <= >= • == != • and • or • ?: (ternary) 24
  • 63.
  • 64.
    Soy - Commands •{template}{/template} 25
  • 65.
    Soy - Commands •{template}{/template} • {literal}{/literal} 25
  • 66.
    Soy - Commands •{template}{/template} • {literal}{/literal} • {print <expression>} 25
  • 67.
    Soy - Commands •{template}{/template} • {literal}{/literal} • {print <expression>} • {<expression>} 25
  • 68.
    Soy - Commands •{template}{/template} • {literal}{/literal} • {print <expression>} • {<expression>} • {if <expression>}, {elseif}, {else}, {/if} 25
  • 69.
    Soy - Commands •{template}{/template} • {foreach}, {ifempty}, {/foreach} • {literal}{/literal} • {print <expression>} • {<expression>} • {if <expression>}, {elseif}, {else}, {/if} 25
  • 70.
    Soy - Commands •{template}{/template} • {foreach}, {ifempty}, {/foreach} • {literal}{/literal} • {for}, {/for} • {print <expression>} • {<expression>} • {if <expression>}, {elseif}, {else}, {/if} 25
  • 71.
    Soy - Commands •{template}{/template} • {foreach}, {ifempty}, {/foreach} • {literal}{/literal} • {for}, {/for} • {print <expression>} • {call}, {/call}, {param}, {/param} • {<expression>} • {if <expression>}, {elseif}, {else}, {/if} 25
  • 72.
    Soy - Commands •{template}{/template} • {foreach}, {ifempty}, {/foreach} • {literal}{/literal} • {for}, {/for} • {print <expression>} • {call}, {/call}, {param}, {/param} • {<expression>} • {sp}, {n}, {lb}, {rb} • {if <expression>}, {elseif}, {else}, {/if} 25
  • 73.
    Soy - definingvariables 26
  • 74.
    Soy - definingvariables 26
  • 75.
    Soy - definingvariables Not Supported! Keep business logic away from view! 26
  • 76.
    Useful functions •{getText('i18n-key', ....) • {contextPath} • {$data|truncate:30} 27
  • 77.
    Soy Javascript Compilation {namespace JIRA.Templates.Demo} /** * Simplest Hello world demo * @param name */ {template .helloWorld} <div>Hello World, {$name}</div> {/template} 28
  • 78.
  • 79.
  • 80.
    // This filewas 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(); }; 29
  • 81.
    // This filewas 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(); }; 29
  • 82.
    Auto-escaping • implicit bydefault to HTML escaping • {namespace com.example autoescape="XXX"} XXX may be true, false, contextual • disable for a single case with {$templateData|noAutoescape} • sanitized data 30
  • 83.
  • 84.
    Contextual Auto-escaping /** *@param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} 31
  • 85.
    Contextual Auto-escaping /** *@param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} 31
  • 86.
    Contextual Auto-escaping /** *@param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} 31
  • 87.
    Contextual Auto-escaping /** * @param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} <a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'"> &gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a> 31
  • 88.
    Contextual Auto-escaping /** * @param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} <a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'"> &gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a> 31
  • 89.
    Contextual Auto-escaping /** * @param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} <a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'"> &gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a> 31
  • 90.
    Contextual Auto-escaping /** * @param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} <a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'"> &gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a> 31
  • 91.
    Contextual Auto-escaping /** * @param name */ {template .helloWorld autoescape="contextual"} <a href="/Demo?name={$name}" onclick="var x = {$name}">{$name}</a> {/template} {“name”: "><script>alert("x&xx")</script>"} <a href="/Demo?name=%3E%3Cscript%3Ealert%28%22x%26xx%22%29%3C%2Fscript%3E" onclick="var x = 'x3ex3cscriptx3ealert(x22xx26xxx22)x3c/scriptx3e'"> &gt;&lt;script&gt;alert(&quot;x&amp;xx&quot;)&lt;/script&gt;</a> 31
  • 92.
    Soy @ Atlassian •JIRA (gadgets, new project administration, mails, ...) • GreenHopper • PAC • ... 32
  • 93.
    Soy and AtlassianPlugins <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> 33
  • 94.
    Server-side Soy • AtlassianPlugin Framework favours Velocity • SoyTemplateRenderer/SoyTemplateRendererProvider • DefaultVelocityContextProvider (jira-core) • SoyData, SoyDataList, SoyDataMap, SoyData.createFromExistingData() 34
  • 95.
    Soy - CodingExample 35
  • 96.
    Soy - TemplateLibrary • {call} in Soy • web-resource <dependency> 36
  • 97.
  • 98.
    Backbone.js • Event-driven • Models •Views (responsible for keeping markup in sync with model) 38
  • 99.
    Our story withbackbone.js 39
  • 100.
    Our story withbackbone.js • Version/Component/People management in JIRA (Ignite) - 2011 39
  • 101.
    Our story withbackbone.js • Version/Component/People management in JIRA (Ignite) - 2011 • JIRA Importers Plugin, JIRA Mail Plugin - 2011 39
  • 102.
    Our story withbackbone.js • Version/Component/People management in JIRA (Ignite) - 2011 • JIRA Importers Plugin, JIRA Mail Plugin - 2011 • RAB - 2011 39
  • 103.
    Our story withbackbone.js • 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
  • 104.
  • 105.
    Why Backbone backbone.js 40
  • 106.
    Why Backbone backbone.js JIRA is a backbone 40
  • 107.
    Why Backbone JIRA is a backbone backbone.js 40
  • 108.
  • 109.
  • 110.
  • 111.
    Why Backbone.js • Small •Flexible • Does not impose any templating technologies 41
  • 112.
    Why Backbone.js • Small •Flexible • Does not impose any templating technologies • Well documented 41
  • 113.
    Why Backbone.js • Small •Flexible • Does not impose any templating technologies • Well documented • Popular 41
  • 114.
    Why Backbone.js • Small •Flexible • Does not impose any templating technologies • Well documented • Popular • Liked by us 41
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
    JQuery Javascript DOM / Markup 42
  • 120.
    JQuery Javascript DOM / Markup 42
  • 121.
    AUI JQuery Javascript DOM / Markup 42
  • 122.
    AUI JQuery Javascript DOM / Markup 42
  • 123.
    AUI JQuery Javascript DOM / Markup 42
  • 124.
    AUI JQuery Javascript DOM / Markup 42
  • 125.
    Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 126.
    Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 127.
    Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 128.
    Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 129.
    Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 130.
    Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 131.
    Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 132.
    Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 133.
    REST API Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 134.
    REST API Backbone.js Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 135.
    REST API Backbone.js Services Managers Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 136.
    REST API Backbone.js Services Managers Soy (client) AUI JQuery Javascript DOM / Markup 42
  • 137.
    Possible Future • 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) 43
  • 138.
  • 139.
  • 140.
    Looking back... 2.5 yearsago... most of this stuff was not possible 44
  • 141.
    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 45
  • 142.
    TAKE-AWAYS “ Atlassian is moving fast to client-side programming. Technology is there. Are you ready? ” #atlascamp 46
  • 143.

Editor's Notes