Introduction to Atlassian Confluence Plugins

If you’re familiar with Atlassian’s suite of products, you know how powerful Confluence can be for team collaboration and knowledge management. However, sometimes the out-of-the-box features just aren’t enough. This is where plugins come into play, allowing you to customize and extend Confluence to meet your specific needs. In this article, we’ll dive into the world of developing plugins for Atlassian Confluence using Java.

Setting Up Your Development Environment

Before you start coding, you need to set up your development environment. Here are the steps to get you started:

Installing the Atlassian Plugin SDK

The Atlassian Plugin SDK is your best friend when it comes to developing plugins. Here’s how you can install it:

# Download and install the SDK
wget https://www.atlassian.com/software/sdk/downloads/binary/atlassian-plugin-sdk-8.0.2.tar.gz
tar -xzf atlassian-plugin-sdk-8.0.2.tar.gz
cd atlassian-plugin-sdk-8.0.2

# Set up the SDK
./bin/atlas-setup

# Verify the setup
./bin/atlas-version

Using Maven for Plugin Development

Maven is a crucial tool for managing dependencies and building your plugin. Here’s an example of how to set up your pom.xml file for a Confluence plugin:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example.plugins</groupId>
    <artifactId>my-confluence-plugin</artifactId>
    <version>1.0</version>
    <packaging>atlassian-plugin</packaging>

    <dependencies>
        <dependency>
            <groupId>com.atlassian.confluence</groupId>
            <artifactId>confluence</artifactId>
            <version>8.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>com.atlassian.maven.plugins</groupId>
                <artifactId>confluence-maven-plugin</artifactId>
                <version>8.0.2</version>
                <extensions>true</extensions>
                <configuration>
                    <productVersion>8.0.0</productVersion>
                    <productDataVersion>8.0.0</productDataVersion>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
</project>

Upgrading to Java 11

If you’re moving from an older version of Confluence to Confluence 8 or later, you’ll need to upgrade to Java 11. Here’s a tip from the community:

# Use a recent version of Maven
mvn --version

# Ensure your pom.xml is set up for Java 11
<properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
</properties>

Creating Your First Plugin

Plugin Descriptor

The heart of any Confluence plugin is the atlassian-plugin.xml file, which serves as the plugin descriptor. Here’s an example of what it might look like:

<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
    <plugin-info>
        <description>This is my first Confluence plugin.</description>
        <vendor-name>Maxim Zhirnov</vendor-name>
        <vendor-url>https://example.com</vendor-url>
    </plugin-info>

    <!-- Define your plugin modules here -->
    <module type="macro" key="my-macro">
        <name>My Macro</name>
        <class>com.example.plugins.MyMacro</class>
    </module>
</atlassian-plugin>

Writing a Simple Macro Plugin

Macros are a great place to start because they provide immediate visual feedback. Here’s an example of a simple macro:

import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.confluence.renderer.radeox.macros.MacroUtils;
import com.atlassian.confluence.util.velocity.VelocityUtils;

public class MyMacro implements Macro {
    @Override
    public String execute(Map<String, String> parameters, String body, ConversionContext context) throws MacroExecutionException {
        // Simple macro that returns a greeting
        return "Hello, World!";
    }

    @Override
    public BodyType getBodyType() {
        return BodyType.NONE;
    }

    @Override
    public OutputType getOutputType() {
        return OutputType.INLINE;
    }
}

Adding Resources and Configuration

Adding Plugin Resources

Resources are non-Java files that your plugin might need. Here’s how you can add a resource:

<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
    <!-- ... -->
    <resource type="download" name="my-resource" location="/path/to/my/resource.txt"/>
</atlassian-plugin>

Adding a Configuration UI

If your plugin requires configuration, you can add a configuration UI:

<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
    <!-- ... -->
    <module type="web" key="my-config-ui">
        <name>My Config UI</name>
        <url>/plugins/servlet/my-config-ui</url>
        <class>com.example.plugins.MyConfigUI</class>
    </module>
</atlassian-plugin>

Advanced Plugin Development

Using Standard Page Decorators

If you’re developing a plugin that needs to work across multiple Atlassian products, you can use standard page decorators:

<atlassian-plugin key="com.example.plugins.my-confluence-plugin" name="My Confluence Plugin">
    <!-- ... -->
    <module type="web-panel" key="my-web-panel">
        <name>My Web Panel</name>
        <url>/plugins/servlet/my-web-panel</url>
        <class>com.example.plugins.MyWebPanel</class>
        <location>system.header</location>
    </module>
</atlassian-plugin>

Accessing Confluence Components

To interact with Confluence components, you need to follow the Java package naming conventions to avoid conflicts:

import com.atlassian.confluence.core.ContentEntityObject;
import com.atlassian.confluence.core.ContentEntityManager;
import com.atlassian.confluence.user.ConfluenceUser;
import com.atlassian.confluence.user.UserAccessor;

public class MyPlugin {
    public void doSomething(ContentEntityManager contentEntityManager, UserAccessor userAccessor) {
        // Access Confluence components here
        ConfluenceUser user = userAccessor.getCurrentUser();
        // ...
    }
}

Example Workflow: Creating a Jira Issue from Confluence

Here’s an example of how you might create a plugin to generate Jira issues from Confluence, a common use case:

Step-by-Step Process

  1. Set Up the Plugin Skeleton: Use the Maven template to create a basic plugin skeleton.

  2. Define the Plugin Descriptor: Add the necessary modules to your atlassian-plugin.xml.

  3. Create the Macro or Web Item: Write the Java code for your macro or web item that will trigger the Jira issue creation.

  4. Use REST API or Webhooks: Use the Jira REST API or webhooks to create the issue.

Example Code

Here’s a simplified example using the Jira REST API:

import com.atlassian.confluence.content.render.xhtml.ConversionContext;
import com.atlassian.confluence.macro.Macro;
import com.atlassian.confluence.macro.MacroExecutionException;
import com.atlassian.httpclient.api.HttpClient;
import com.atlassian.httpclient.api.Request;
import com.atlassian.httpclient.api.Response;
import com.atlassian.httpclient.api.ResponseHandler;
import com.google.gson.Gson;

public class CreateJiraIssueMacro implements Macro {
    private final HttpClient httpClient;

    public CreateJiraIssueMacro(HttpClient httpClient) {
        this.httpClient = httpClient;
    }

    @Override
    public String execute(Map<String, String> parameters, String body, ConversionContext context) throws MacroExecutionException {
        String jiraUrl = parameters.get("jiraUrl");
        String projectKey = parameters.get("projectKey");
        String issueType = parameters.get("issueType");

        // Prepare the request
        Request request = Request.Builder(Request.Method.POST, jiraUrl + "/rest/api/2/issue")
                .setEntity(new Gson().toJson(new Issue(projectKey, issueType)))
                .build();

        // Execute the request
        Response response = httpClient.execute(request, new ResponseHandler<Response>() {
            @Override
            public Response handle(Response response) throws Exception {
                return response;
            }
        });

        if (response.getStatusCode() == 201) {
            return "Issue created successfully!";
        } else {
            return "Failed to create issue.";
        }
    }

    // Helper class for the issue JSON
    private static class Issue {
        private String project;
        private String issuetype;

        public Issue(String projectKey, String issueType) {
            this.project = new Project(projectKey);
            this.issuetype = new IssueType(issueType);
        }

        private static class Project {
            private String key;

            public Project(String key) {
                this.key = key;
            }
        }

        private static class IssueType {
            private String name;

            public IssueType(String name) {
                this.name = name;
            }
        }
    }
}

Sequence Diagram

Here is a sequence diagram showing the interaction between Confluence, the plugin, and Jira:

sequenceDiagram participant Confluence participant Plugin participant Jira Confluence->>Plugin: User clicks the "Create Issue" button Plugin->>Jira: Send POST request to create issue Jira->>Plugin: Return response (201 Created or error) Plugin->>Confluence: Display result to the user

Conclusion

Developing plugins for Atlassian Confluence is a powerful way to extend its functionality and tailor it to your team’s needs. From setting up your development environment to creating complex plugins that interact with other Atlassian products, the process involves several key steps. By following this guide, you’ll be well on your way to becoming a Confluence plugin master, ready to unleash the full potential of your team’s collaboration tools.

Remember, the world of plugin development is vast and full of possibilities. Don’t be afraid to experiment and push the boundaries of what you can achieve. Happy coding