Last updated Mar 27, 2018

Adding a custom action to Confluence

Applicable:

This tutorial applies to Confluence 5.9.1 and higher.

Level of experience:

Beginner.

Time estimate:

It should take you less than 30 minutes to complete this tutorial.

Tutorial overview

This tutorial shows you how to create a plugin to add a custom action to Confluence. The custom action adds a "draft" label to a Confluence page accessible from the Tools drop-down menu. 

Finished custom action in Tools menu

Concepts covered in this tutorial:

  • Creating a plugin skeleton for Confluence.
  • Adding a Web Item and Struts module to your plugin.
  • The Confluence PageAware, Label, Labelable, and LabelManger interfaces.

About these instructions

You can use any supported combination of operating system and IDE to create this plugin. These instructions were written using Intellij IDEA 2017.2 on macOS Sierra. If you are using another operating system or IDE combination, you should use the equivalent operations for your specific environment.

This tutorial was last tested with Confluence 6.7.1 using Atlassian SDK 6.3.10. 

Before you begin

To complete this tutorial, you should:

  • understand basic Java classes, interfaces, methods, and know how to use the compiler.

Plugin Source

We encourage you to work through this tutorial. If you want to skip ahead or check your work when you are done, you can find the plugin source code on Atlassian Bitbucket. To clone the repository, run the following command:

1
2
git clone git@bitbucket.org:atlassian_tutorial/confluence-custom-action-tutorial.git

Alternatively, you can download the source as a ZIP archive.

Step 1. Create and prune the plugin skeleton

In this step, you'll create a plugin skeleton using atlas- commands. Since you won't need some of the files created in the skeleton, you'll also delete them in this step.

  1. Open Terminal and navigate to your workspace directory.

  2. To create a Confluence plugin skeleton, run the following command :

    1
    2
    atlas-create-confluence-plugin
    
  3. To identify your plugin, enter the following information:

    group-idcom.atlassian.plugins.tutorial
    artifact-idAddDraftLabelAction
    version1.0-SNAPSHOT
    packagecom.atlassian.plugins.tutorial
  4. When prompted, confirm your entries with Y or y.

    Your terminal notifies you of a successful build:

    1
    2
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESSFUL
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time: 1 minute 11 seconds
    [INFO] Finished at: Thu Jul 18 11:30:23 PDT 2013
    [INFO] Final Memory: 82M/217M
    [INFO] ------------------------------------------------------------------------
    
  5. Navigate to the project directory created in the previous step.

    1
    2
    cd AddDraftLabelAction/
    
  6. Delete the test directories.

    Setting up testing for your macro isn't part of this tutorial. Run the following commands to delete the generated test skeleton:

    1
    2
    rm -rf ./src/test/java
    rm -rf ./src/test/resources/
    
  7. Delete the unneeded Java class files.

    You'll build a single class for your custom action in further steps.

    1
    2
    rm -rf ./src/main/java/com/atlassian/plugins/tutorial/*
    
  8. Import project into your favorite IDE. 

Step 2. Create an AddDraftLabelAction stub class 

You've created the plugin skeleton. Here, you'll create a stub class in Java called AddDraftLabelAction. This class uses the existing class, ConfluenceActionSupport, to leverage existing methods for managing page labels like LabelManager. This class also implements PageAware interface methods to ensure your custom action only engages with current, valid pages. 

  1. Under src/main/java/com/atlassian/plugins/tutorial, create a new file AddDraftLabelAction.java.
1
2
package com.atlassian.plugins.tutorial;

import com.atlassian.confluence.core.ConfluenceActionSupport;
import com.atlassian.confluence.pages.AbstractPage;
import com.atlassian.confluence.pages.actions.PageAware;


public class AddDraftLabelAction extends ConfluenceActionSupport implements PageAware {

    @Override
    public AbstractPage getPage() {
        return null;
    }

    @Override
    public void setPage(AbstractPage page) {
    }

    @Override
    public boolean isPageRequired() {
        return false;
    }

    @Override
    public boolean isLatestVersionRequired() {
        return false;
    }

    @Override
    public boolean isViewPermissionRequired() {
        return false;
    }

    @Override
    public boolean isEditPermissionRequired() {
        return false;
    }
}

Step 3. Modify the stub code and add an execute method

You've built the foundation for your class, now you'll flesh out the stub code. Your class foundation is built from ConfluenceActionSupport and PageAware. It inherits methods that deal with validating or identifying a specific Confluence page. Here, you'll add additional interfaces—Label, Labelable, and LabelManager—so that your custom action can add a label to a page.

  1. Add the Label, Labelable, and LabelManager interfaces to your class.

    These interfaces provide access to methods and fields for managing and creating labels in Confluence. 

    1
    2
    import com.atlassian.confluence.labels.Label;
    import com.atlassian.confluence.labels.LabelManager;
    import com.atlassian.confluence.labels.Labelable;
    
  2. Add page and labelManager as instance variables.

    1
    2
    private AbstractPage page;
    private LabelManager labelManager;
    
  3. Complete the stub code for getPage() and setPage().

    getPage() obtains the current Confluence page and sets the return to page. With page defined, setPage() defines which page your custom action should interact with.  

    1
    2
        @Override
        public AbstractPage getPage() {
            return page;
        }
    
        public void setPage(AbstractPage page)
        {
            this.page = page;
        }
    
  4. Modify the stub code to return true for methods with a boolean return.

    These methods are all inherited from the PageAware interface. Now, your custom action will only execute if the Confluence page is within the scope of user permissions, is defined as a page, and is the most recent version.

    1
    2
        @Override
        public boolean isLatestVersionRequired() {
            return true;
        }
        
        @Override
        public boolean isPageRequired() {
            return true;
        }
        
        @Override
        public boolean isViewPermissionRequired() {
            return true;
        }
    
  5. Invoke the setLabelManager method.

    1
    2
        public void setLabelManager(LabelManager labelManager) {
            this.labelManager = labelManager;
        }
    
  6. Add the execute method to instantiate a new draft label.

    Your execute method returns the "success" message as a String, which your struts module in the atlassian-plugin.xml file uses to trigger a page refresh.

    1
    2
        public String execute() {
            Label label = new Label("draft");
            labelManager.addLabel((Labelable) page, label);
            return "success";
        }
    
  7. Save your changes.

    Here's what your finalized AddDraftLabelAction.java file should look like:

    1
    2
    package com.atlassian.plugins.tutorial;
    
    import com.atlassian.confluence.core.ConfluenceActionSupport;
    import com.atlassian.confluence.pages.AbstractPage;
    import com.atlassian.confluence.pages.actions.PageAware;
    import com.atlassian.confluence.labels.Label;
    import com.atlassian.confluence.labels.LabelManager;
    import com.atlassian.confluence.labels.Labelable;
    
    public class AddDraftLabelAction extends ConfluenceActionSupport implements
            PageAware {
    
        private AbstractPage page;
        private LabelManager labelManager;
     
        @Override
        public AbstractPage getPage()
        {
            return page;
        }
    
        public void setPage(AbstractPage page)
        {
            this.page = page;
        }
    
        @Override
        public boolean isLatestVersionRequired() {
            return true;
        }
    
        @Override
        public boolean isPageRequired() {
            return true;
        }
    
        @Override
        public boolean isViewPermissionRequired() {
            return true;
        }
    
        @Override
        public boolean isEditPermissionRequired() {
            return false;
        }
    
        public void setLabelManager(LabelManager labelManager) {
            this.labelManager = labelManager;       
        }
     
        public String execute() {
            Label label = new Label("draft");
            labelManager.addLabel((Labelable) page, label);
            return "success";
        }
    }
    

Step 4. Add a web-item  module to the  atlassian-plugin.xml plugin descriptor

You've built the skeleton for your plugin and finalized a Java class. To describe how Confluence should interact with your code, you'll modify the atlassian-plugin.xml file. This file bases these dependencies on your pom.xml. The atlassian-plugin.xml file was generated when you created your plugin skeleton. 

In this step you'll add a Web Item module to your plugin. This module type allows you to customize application menus in Confluence with links to your custom action. You can add modules to Confluence with the command atlas-create-confluence-module, but in this particular case it's more convenient to simply modify the atlassian-plugin.xml file directly. 

  1. In your IDE, open atlassian-plugin.xml.
    This file is located under src/main/resources.

  2. Find the closing </plugin-info> tag. 

  3. Directly after the closing </plugin-info> tag, add the following.

    This section defines the <label> users will see in the Tools menu, and when the custom action is accessible ( system.content.action/primary). There are four sections in the Tools menu: primary, marker, secondary, and modify. Designating your plugin's location as primary will place it at the top of the menu.

    1
    2
        <web-item name="add-draft-label-action-web-ui" key="add-draft-label-action-web-ui" section="system.content.action/primary" weight="10">
            <description key="item.add-draft-label-action-web-ui.link.desc">Adds the "Add 'draft' label" action into the Tools menu.</description>
            <label key="Add ''draft'' label"/>
            <link linkId="add-draft-label-action">/plugins/add-draft-label/add-label.action?pageId=$page.id</link>
        </web-item>
    
  4. Save your changes.

Step 5. Add a struts module to atlassian-plugin.xml

Now, you'll add a Struts module. Your Web Item component module defines the label and placement of the custom action. In this step, you'll define what the custom action should do within Confluence using an struts module. The struts module will rely on your Java class, AddDraftLabelAction. This module informs Confluence to reference your Java class to execute on the page. After the draft label is applied, AddDraftLabelAction.java returns a String literal value of "success". This module then uses that message to trigger a "redirect" to the current page. It is the same as a refresh.

  1. In atlassian-plugin.xml, find the closing tag for your newly-added </web-item>.

  2. Add the following on the line after </web-item>.

    1
    2
        <struts name="add-draft-label-action-struts" key="add-draft-label-action-struts">
            <description key="item.add-draft-label-action-struts.link.desc">Defines what the "Add 'draft' label" action does.</description>
            <package name="add-draft-label" extends="default" namespace="/plugins/add-draft-label">
                <default-interceptor-ref name="defaultStack"/>
                <action name="add-label" class="com.atlassian.plugins.tutorial.AddDraftLabelAction">
                    <result name="success" type="redirect">${page.urlPath}</result>
                </action>
            </package>
        </struts>
    
  3. Save your changes and close the file.

Here's an example of the complete atlassian-plugin.xml:

1
2
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
    <plugin-info>
        <description>${project.description}</description>
        <version>${project.version}</version>
        <vendor name="${project.organization.name}" url="${project.organization.url}" />
        <param name="plugin-icon">images/pluginIcon.png</param>
        <param name="plugin-logo">images/pluginLogo.png</param>
    </plugin-info>

    <web-item name="add-draft-label-action-web-ui" key="add-draft-label-action-web-ui" section="system.content.action/primary" weight="10">
        <description key="item.add-draft-label-action-web-ui.link.desc">Adds the "Add 'draft' label" action into the Tools menu.</description>
        <label key="Add ''draft'' label"/>
        <link linkId="add-draft-label-action">/plugins/add-draft-label/add-label.action?pageId=$page.id</link>
    </web-item>

     <struts name="add-draft-label-action-struts" key="add-draft-label-action-xwork">
        <description key="item.add-draft-label-action-struts.link.desc">Defines what the "Add 'draft' label" action does.</description>
        <package name="add-draft-label" extends="default" namespace="/plugins/add-draft-label">
            <default-interceptor-ref name="defaultStack"/>
            <action name="add-label" class="com.atlassian.plugins.tutorial.AddDraftLabelAction">
                <result name="success" type="redirect">${page.urlPath}</result>
            </action>
        </package>
    </struts>   

    <!-- add our i18n resource -->
    <resource type="i18n" name="i18n" location="AddDraftLabelAction"/>

    <!-- add our web resources -->
    <web-resource key="AddDraftLabelAction-resources" name="AddDraftLabelAction Web Resources">
        <dependency>com.atlassian.auiplugin:ajs</dependency>

        <resource type="download" name="AddDraftLabelAction.css" location="/css/AddDraftLabelAction.css"/>
        <resource type="download" name="AddDraftLabelAction.js" location="/js/AddDraftLabelAction.js"/>
        <resource type="download" name="images/" location="/images"/>
        <context>AddDraftLabelAction</context>
    </web-resource>
</atlassian-plugin>

Step 6. Build, install, and run your plugin

In this step, you'll install your plugin and run Confluence. Then, to test your custom action, you'll add a page and add a "draft" label to the page from the Tools menu.

  1. Open Terminal window and navigate to the plugin root folder. 

    1
    2
    cd AddDraftLabelAction
    
  2. To start up Confluence from your project root, run the atlas-run command.

    1
    2
    atlas-run
    

    This command builds your plugin code, starts a Confluence instance, and installs your plugin. This may take a few minutes.

  3. Find the URL for Confluence.
    Your terminal outputs the location of your local Confluence instance, defaulted to http://localhost:1990/confluence.

    1
    2
    [INFO] confluence started successfully in 71s at http://localhost:1990/confluence
    [INFO] Type CTRL-D to shutdown gracefully
    [INFO] Type CTRL-C to exit
    
  4. In your browser, open your local Confluence instance and log in with admin / admin.

  5. Click Create.

  6. In the dialog box, choose Blank Page, and then click Create.

  7. Add some content, and then click Save.

  8. Click ToolsAdd 'draft' label.

  9. Verify that the 'draft' label is applied.
    Confirm the label was applied

Rate this page: