Commits

Joe Clark committed 6a10dfd

Committing tutorial code for Confluence Pipeline Transformer Demo

  • Participants

Comments (0)

Files changed (9)

+.idea/
+*.iml
+.DS_Store
+target/
+Copyright (c) 2012, Atlassian
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+You have successfully created a plugin using the Confluence plugin archetype!
+
+Here are the SDK commands you'll use immediately:
+
+* atlas-run   -- installs this plugin into Confluence and starts it on http://<machinename>:1990/confluence
+* atlas-debug -- same as atlas-run, but allows a debugger to attach at port 5005
+* atlas-cli   -- after atlas-run or atlas-debug, opens a Maven command line window:
+                 - 'pi' reinstalls the plugin into the running Confluence instance
+* atlas-help  -- prints description for all commands in the SDK
+
+Full documentation is always available at:
+
+https://developer.atlassian.com/display/DOCS/Developing+with+the+Atlassian+Plugin+SDK
+<?xml version="1.0" encoding="UTF-8"?>
+
+<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.confluence.transformer</groupId>
+    <artifactId>tutorial-confluence-transformer-demo</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <organization>
+        <name>Example Company</name>
+        <url>http://www.example.com/</url>
+    </organization>
+
+    <name>Confluence Pipeline Transformer Demo Plugin</name>
+    <description>A plugin that allows administrators to add custom banners to pages with a particular label</description>
+    <packaging>atlassian-plugin</packaging>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.atlassian.sal</groupId>
+            <artifactId>sal-api</artifactId>
+            <version>2.7.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.confluence</groupId>
+            <artifactId>confluence</artifactId>
+            <version>${confluence.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.atlassian.confluence.plugin</groupId>
+            <artifactId>func-test</artifactId>
+            <version>2.3</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.jwebunit</groupId>
+            <artifactId>jwebunit-htmlunit-plugin</artifactId>
+            <version>2.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>net.sourceforge.nekohtml</groupId>
+            <artifactId>nekohtml</artifactId>
+            <version>1.9.12</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.8.5</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>com.atlassian.maven.plugins</groupId>
+                <artifactId>maven-confluence-plugin</artifactId>
+                <version>${amps.version}</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <productVersion>${confluence.version}</productVersion>
+                    <productDataVersion>${confluence.data.version}</productDataVersion>
+                    <instructions/>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+        <confluence.version>4.1.6</confluence.version>
+        <confluence.data.version>3.5</confluence.data.version>
+        <amps.version>3.11</amps.version>
+    </properties>
+</project>

File src/main/java/com/example/confluence/transformer/storage/Banner.java

+package com.example.confluence.transformer.storage;
+
+/**
+ * Represents an individual Banner that has been configured and saved to the
+ * database.
+ * See {@link BannerStorage}
+ */
+public class Banner
+{
+    private long labelId;
+    private BannerType bannerType;
+    private String bannerTitle;
+    private String bannerText;
+
+
+    /**
+     * A no-args constructor. This is necessary in order for the serialisation
+     * of this class to and from the database to function correctly.
+     */
+    public Banner()
+    {
+        // No-Op.
+    }
+
+    /**
+     * A convenience constructor so that the {@Link BannerStorageImpl} class
+     * can easily create new, pre-configured instances of {@link Banner}
+     *
+     * @param labelId The unique ID of the label that this banner applies to.
+     * @param type    The kind of banner (eg. info/note/tip)
+     * @param title   The optional title of the banner
+     * @param text    The body text contained within the banner
+     */
+    Banner(long labelId, BannerType type, String title, String text)
+    {
+        this.labelId = labelId;
+        this.bannerText = text;
+        this.bannerTitle = title;
+        this.bannerType = type;
+    }
+
+    public void setLabelId(long labelId)
+    {
+        this.labelId = labelId;
+    }
+
+    public long getLabelId()
+    {
+        return labelId;
+    }
+
+    public void setBannerText(String bannerText)
+    {
+        this.bannerText = bannerText;
+    }
+
+
+    public String getBannerText()
+    {
+        return bannerText;
+    }
+
+    public void setBannerType(BannerType bannerType)
+    {
+        this.bannerType = bannerType;
+    }
+
+
+    public BannerType getBannerType()
+    {
+        return bannerType;
+    }
+
+    public void setBannerTitle(String bannerTitle)
+    {
+        this.bannerTitle = bannerTitle;
+    }
+
+
+    public String getBannerTitle()
+    {
+        return bannerTitle;
+    }
+}

File src/main/java/com/example/confluence/transformer/storage/BannerStorage.java

+package com.example.confluence.transformer.storage;
+
+import com.atlassian.confluence.labels.Label;
+
+import java.util.Collection;
+
+/**
+ * Defines the operations available on the bannerStorage component (see the
+ * atlassian-plugin.xml). Responsible for saving and loading instances of
+ * {@link Banner} to and from the Confluence database.
+ */
+public interface BannerStorage
+{
+    /**
+     * Creates a new instance of {@link Banner} to the database, using the
+     * provided information. The newly-created {@link Banner} can be
+     * subsequently retrieved using the
+     * {@link #getBannerForLabel(com.atlassian.confluence.labels.Label)} method.
+     * <p/>
+     * If a {@link Banner} already exists with the same {@link Label}, the
+     * existing {@link Banner} will be overwritten and replaced by this one.
+     *
+     * @param label The {@link Label} to which this banner should apply.
+     * @param type  The {@link BannerType} of the new banner.
+     * @param title The optional title for the banner. Specify null or ""
+     *              to create a banner with no title.
+     * @param text  The body text of the new banner.
+     */
+    public void addBanner(Label label, BannerType type, String title, String text);
+
+    /**
+     * Returns all currently configured instances of {@link Banner}. If there
+     * are currently no banners configured, an empty collection will be
+     * returned.
+     *
+     * @return An un-ordered collection of all configured Banner instances.
+     */
+    public Collection<Banner> getConfiguredBanners();
+
+    /**
+     * Deletes the {@link Banner} associated with the specified {@link Label},
+     * if one exists. If none exists, this method takes no action.
+     *
+     * @param label The corresponding {@link Label} object.
+     */
+    public void removeBanner(Label label);
+
+    /**
+     * Retrieves the {@link Banner} associated with the specified {@link Label},
+     * if one exists. If none exists, this method will return {@code null}.
+     *
+     * @param label The corresponding {@link Label} object.
+     * @return The {@link Banner} associared with the specified {@link Label},
+     *         or {@code null} if no banner exists.
+     */
+    public Banner getBannerForLabel(Label label);
+}

File src/main/java/com/example/confluence/transformer/storage/BannerStorageImpl.java

+package com.example.confluence.transformer.storage;
+
+import com.atlassian.confluence.labels.Label;
+import com.atlassian.sal.api.pluginsettings.PluginSettings;
+import com.atlassian.sal.api.pluginsettings.PluginSettingsFactory;
+import com.google.common.collect.Maps;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Provides a concrete implementation of the {@link BannerStorage} component
+ * interface.
+ */
+public class BannerStorageImpl implements BannerStorage
+{
+    private static final Logger log = LoggerFactory.getLogger(BannerStorageImpl.class);
+    private static final String BANNER_STORAGE_KEY = "banners";
+
+    private final PluginSettings pluginSettings;
+
+    /**
+     * Constructs a new instance of {@link BannerStorageImpl}. This constructor
+     * is called by Confluence when the plugin is initialised.
+     *
+     * @param pluginSettingsFactory The {@link PluginSettingsFactory} component
+     *                              provided by the Shared Access Layer ("SAL")
+     *                              plugin. Confluence will automatically find
+     *                              this component and set it as the
+     *                              constructor parameter for your component.
+     */
+    public BannerStorageImpl(PluginSettingsFactory pluginSettingsFactory)
+    {
+        // Use the SAL pluginSettingsFactory to create a new instance of
+        // the PluginSettings object. We can use this object to save simple
+        // Key/Value pairs in the Confluence database. The value objects must
+        // be serializable Java objects or primitive types.
+        this.pluginSettings = pluginSettingsFactory.createGlobalSettings();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addBanner(Label label, BannerType bannerType, String bannerTitle, String bannerText)
+    {
+        log.debug(String.format("Adding new Banner (%s) for Label (%s)", bannerText, label.getDisplayTitle()));
+        Map<Long, Banner> banners = getBanners();
+        banners.put(label.getId(), new Banner(label.getId(), bannerType, bannerTitle, bannerText));
+        saveBanners(banners);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Collection<Banner> getConfiguredBanners()
+    {
+        // Return a collection that cannot be modified - this prevents other code
+        // from sneakily doing things that bypass the public Add and Remove
+        // methods on the BannerStorage interface.
+        return Collections.unmodifiableCollection(getBanners().values());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void removeBanner(Label label)
+    {
+        log.debug(String.format("Removing the banner associated with Label %s", label.getDisplayTitle()));
+        Map<Long, Banner> banners = getBanners();
+        banners.remove(label.getId());
+        saveBanners(banners);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Banner getBannerForLabel(Label label)
+    {
+        return getBanners().get(label.getId());
+    }
+
+    /**
+     * A private method to load the banners from the database. Because we are
+     * storing the banners in a {@link Map} as a single Key/Value pair in the
+     * database, we must load the entire map into memory from the database in
+     * order to add or remove a single entry, and then save the entire map
+     * back to the database again.
+     *
+     * Note that this approach to database persistence works fine for small
+     * sets of data, but would not scale well to thousands or millions of
+     * entries.
+     */
+    @SuppressWarnings("unchecked") // This should never happen.
+    private Map<Long, Banner> getBanners()
+    {
+        Object value = pluginSettings.get(BANNER_STORAGE_KEY);
+        if (value == null)
+            // Initialise the Map if it doesn't yet exist in the database.
+            return Maps.newHashMap();
+
+        return (Map<Long, Banner>)value;
+    }
+
+    /**
+     * A private method to save the configured banners back to the database.
+     */
+    private void saveBanners(Map<Long, Banner> banners)
+    {
+        pluginSettings.put(BANNER_STORAGE_KEY, banners);
+    }
+}

File src/main/java/com/example/confluence/transformer/storage/BannerType.java

+package com.example.confluence.transformer.storage;
+
+/**
+ * This enumeration is used to persist the 'kind' of banner being saved to the database.
+ * See {@link BannerStorage}
+ */
+public enum BannerType
+{
+    /**
+     * Indicates that the banner should be created using the {info} macro.
+     */
+    INFO,
+
+    /**
+     * Indicates that the banner should be created using the {warning} macro.
+     */
+    WARNING,
+
+    /**
+     * Indicates that the banner should be created using the {tip} macro.
+     */
+    TIP,
+
+    /**
+     * Indicates that the banner should be created using the {note} macro.
+     */
+    NOTE
+}

File src/main/resources/atlassian-plugin.xml

+<?xml version="1.0" encoding="UTF-8"?>
+
+<atlassian-plugin key="${project.groupId}.${project.artifactId}" 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}"/>
+    </plugin-info>
+   
+    <component-import key="pluginSettingsFactory" interface="com.atlassian.sal.api.pluginsettings.PluginSettingsFactory"
+                      filter=""/>
+
+    <component key="bannerStorage" name="Configured Banner Storage"
+               class="com.example.confluence.transformer.storage.BannerStorageImpl">
+        <description>Stores configured banner information</description>
+        <interface>com.example.confluence.transformer.storage.BannerStorage</interface>
+    </component>
+</atlassian-plugin>