Last updated Oct 3, 2024

Writing Soy templates in your plugin

Applicable:

This tutorial applies to Confluence 4.3.

Level of experience:

This is an intermediate tutorial. You should have completed at least one beginner tutorial before working through this tutorial. See the list of developer tutorials.

StatusLEGACY This tutorial applies to Confluence versions that have reached end of life.

Related:

Overview

This tutorial shows you how to write a Soy template in your plugin to use in JavaScript. The plugin adds a table of metadata to the top of each Confluence page using Soy templates:

In order to do this, you will create a very simple Confluence plugin. Your plugin will consist of the following components:

  • Plugin descriptor - to enable the plugin module in Confluence.
  • A JavaScript resource - to call the Soy template
  • A Soy template.
  • A little internationalisation

All these components will be contained within a single JAR file. Each component is further discussed in the examples below.

This plugin is of little practical value, but it is designed to show you a way of templating UI elements in JavaScript for use on Confluence.

Source Code

The source code of the plugin used in this tutorial is available in bitbucket. You can browse the source code here: confluence-tutorial-soy-templates.

What are Soy templates?

Soy templates are a templating system for dynamically generating re-usable HTML and UI elements in both Java and JavaScript. Soy templates are also referred to as Closure templates.

For the client side, Soy templates are precompiled into efficient JavaScript. This tutorial is only concerned with client-side aspect of templating.

Step 1. Create the plugin project

Begin by creating a new Confluence plugin. You can use the Atlassian Plugin SDK to do this. (See Set up the Atlassian Plugin SDK and Build a Project for full instructions.)

In a command window, go to the directory where you want to create your plugin project, and type: atlas-create-confluence-plugin

You will be prompted to enter two values, the group ID and artifact ID. For this tutorial, we used the following values:

  • Group ID: com.appfusions.confluence.plugins
  • Artifact ID: confluence-tutorial-soy-templates

Accept the default values for version and package.

This will create a basic plugin with some Java code and tests. In the sample code, all Java code including tests have been removed as they are not needed for the plugin. 

Step 2. Create a Soy template

Create a new Soy template at src/main/resources/template.soy:

1
2
{namespace Confluence.Templates.SoyTutorial}
/**
 * Renders a table of selected AJS.params
 * @param pageId
 * @param pageTitle
 * @param parentPageId
 * @param spaceKey
 * @param spaceName
 */
{template .listSelectedAjsParams}
    <b>{getText('confluence.tutorial.soy.templates.intro')}</b>
    <table class="aui" id="basic">
        <thead>
            <tr>
                <th>{getText('confluence.tutorial.soy.templates.name')}</th>
                <th>{getText('confluence.tutorial.soy.templates.value')}</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>pageId</td>
                <td>{$pageId}</td>
            </tr>
            <tr>
                <td>pageTitle</td>
                <td>{$pageTitle}</td>
            </tr>
            <tr>
                <td>parentPageId</td>
                <td>{$parentPageId}</td>
            </tr>
            <tr>
                <td>spaceKey</td>
                <td>{$spaceKey}</td>
            </tr>
            <tr>
                <td>spaceName</td>
                <td>{$spaceName}</td>
            </tr>
        </tbody>
    </table>
{/template}

Each Soy file needs a namespace declaration at the top of the file. It must be declared before any templates are declared. 

In this case, we have created a new Confluence.Templates.SoyTutorial namespace for our templates and added a template which displays an AUI-style table containing a number of attributes that a Confluence page is likely to possess.

Each template must precede with a SoyDoc comment that explains the purpose of the template, in the same style as JavaDoc.

1
2
/**
 * Renders a table of selected AJS.params
 * @param pageId
 * @param pageTitle
 * @param parentPageId
 * @param spaceKey
 * @param spaceName
 */

Parameters use this syntax within the template:

1
2
{$pageId}

For internationalisation, use getText() - a JavaScript internationalisation transformer will take care of the i18n:

1
2
<b>{getText('confluence.tutorial.soy.templates.intro')}</b>

Step 3. Call the Soy template in a separate JavaScript file

Create a new JavaScript resource at src/main/resources/init.js:

1
2
AJS.toInit(function () {
    var $pageMetadata = AJS.$('#content.page.view .page-metadata:first');

    if ($pageMetadata.length > 0) {
        var selectedAjsParams = {
            pageId: AJS.params.pageId,
            pageTitle: AJS.params.pageTitle,
            parentPageId: AJS.params.parentPageId,
            spaceKey: AJS.params.spaceKey,
            spaceName: AJS.params.spaceName
        }   
        var template = Confluence.Templates.SoyTutorial.listSelectedAjsParams(selectedAjsParams);       
        $pageMetadata.after(template);
    }
});

Here we are doing four things:

  1. Getting the first .page-metadata element if it exists.
  2. Creating an object containing only the AJS.params items that we are interested in.
  3. Passing these items - selectedAjsParams - into the template - Confluence.Templates.SoyTutorial.listSelectedAjsParams - to render the template into HTML.
  4. Appending the resulting HTML into the browser after the page metadata section.

You will notice the AUI function, AJS.params - this contains some useful information about the Confluence installation and the current page. In this case, we are using it for convenience. Take a look at AJS.params in your browser's JavaScript console:

Step 4. Edit the atlassian-plugin.xml file

You must now register the plugin module in your plugin descriptor, atlassian-plugin.xml.

Add the following code to your atlassian-plugin.xml file between the <atlassian-plugin> tags, but below the <plugin-info> tag group.

1
2
     <web-resource key="view-metadata" name="View metadata from AJS.params">


        <!-- transform calls to AJS.getText() inside JS files -->
        <transformation extension="js">
            <transformer key="jsI18n"/>
        </transformation>

        <!-- transform Soy templates into JS -->
        <transformation extension="soy">
            <transformer key="soyTransformer">
                <functions>com.atlassian.confluence.plugins.soy:soy-core-functions</functions>
            </transformer>
        </transformation>


        <!-- JavaScript resources -->
        <resource name="init.js" type="download" location="init.js"/>
        <resource name="template-soy.js" type="download" location="template.soy"/>
        <context>page</context>


    </web-resource>

Let's break down that XML code, by looking at the web resource module, transformers and resources in the code.

The web resource module

In the code example, the web-resource module (line) has 2 attributes:

  • key="view-metadata" sets an internal name for the new item.
  • name="View metadata from AJS.params" defines the default name of the item.
1
2
 <web-resource key="view-metadata" name="View metadata from AJS.params">

The transformers

In the code example, there are two transformation code blocks:

  • The first transformation module performs the internationalisation work.
  • The second transformation module transforms the .soy template into precompiled efficient JavaScript which can then be called by the JavaScript in init.js
1
2
        <!-- transform calls to AJS.getText() inside JS files -->
        <transformation extension="js">
            <transformer key="jsI18n"/>
        </transformation>

        <!-- transform Soy templates into JS -->
        <transformation extension="soy">
            <transformer key="soyTransformer">
                <functions>com.atlassian.confluence.plugins.soy:soy-core-functions</functions>
            </transformer>
        </transformation>

Resources

Lastly, there are Javascript resources for the web resources and the context in which they should be displayed. In this case, the Javascript is only added for Confluence pages, thus the 'page' context.

1
2
        <!-- JavaScript resources -->
        <resource name="init.js" type="download" location="init.js"/>
        <resource name="template-soy.js" type="download" location="template.soy"/>
        <context>page</context>

Step 5. Add new resource files and internationalisation

To add new resource files and internationalisation, add a new file in the resources directory of your plugin, called confluence-tutorial-soy-templates.properties, and include these lines of code:

1
2
confluence.tutorial.soy.templates.intro = Some metadata extracted from the AJS.params JavaScript object:
confluence.tutorial.soy.templates.name  = Name
confluence.tutorial.soy.templates.value = Value

The above keys are used in the Soy template. For example:

1
2
<b>{getText('confluence.tutorial.soy.templates.intro')}</b>

Additionally, add a reference the resource file in the atlassian-plugin.xml file - add this line of code above the <web-resource> code block:

1
2
<resource type="i18n" name="i18n" location="confluence-tutorial-soy-templates" />

This accesses your confluence-tutorial-soy-templates.properties file and retrieves the text for our button label.

If you would like to know more about internationalisation, see the Confluence documentation on the topic.

Step 6. Build, install and run the plugin

Go to the root directory in your plugin project - this is the directory that contains the pom.xml file. Type the atlas-run command, to compile the plugin project and then launch a local instance of Confluence.

Once Confluence has loaded:

  • Go to your web browser and access your local instance with this URL:

    1
    2
    http://localhost:1990/confluence/
    
  • Log in with username "admin" and password "admin".
  • Browse to a page in the Demonstration space 

Each page will now have a table above the page content, containing a selection of metadata - this has been rendered client-side using Soy templates.

Congratulations, you have completed this tutorial.

Rate this page: