Documentation

Tutorial: Manage your Confluence instance

In this tutorial, you'll learn about:

This tutorial shows you how to build a static Connect add-on that displays page hierarchy in a Confluence space. This add-on is handy to query, update, and delete Confluence pages. You'll also create a full-screen confirmation dialog displaying content from your add-on.

Your add-on will use the Confluence REST API. At completion, your add-on will look a lot like this:

Configuring your development environment

This step confirms your development environment is configured correctly. You'll need Git, Node.js, and your Atlassian add-on development environment configured.

Once you have all the prerequisites, you'll clone an existing repository to kick things off.

Show Git installation instructions [+]

Show Node.js instructions [+]

  1. Clone the Confluence Gardener repository.
    $ git clone https://bitbucket.org/atlassianlabs/confluence-gardener.git
  2. Change into your new confluence-gardener directory.
    $ cd confluence-gardener
  3. Get a cloud development environment setup by following the Development setup walk-through.

Host & install your add-on

Confluence Gardener is a static Connect add-on that can be served by a simple web server and cached with a CDN. We'll use a simple Node.js-powered static web server to host your add-on locally.

After you've spun up your server, you'll install your copy of Gardener to Confluence. You'll use a Bash script included in the repo you cloned to install the add-on.

  1. From the confluence-gardener directory, start your server on port 8000:

  2. In your browser, navigate to your descriptor file at http://localhost:8000/atlassian-connect.json

    Show atlassian-connect.json [+]

    
     {
         "key": "confluence-gardener",
         "name": "Confluence Gardener",
         "description": "Prune back your Confluence page graph.",
         "baseUrl": "https://addon-dev-url.ngrok.io",
         "vendor": {
             "name": "Atlassian Labs",
             "url": "https://www.atlassian.com"
         },
         "authentication": {
             "type": "none"
         },
         "version": "0.1",
         "modules": {
             "generalPages": [
                  {
                      "key": "gardener",
                      "url": "/index.html?spaceKey={space.key}",
                      "location": "system.content.action",
                      "name": {
                          "value": "Confluence Gardener"
                      }
                  }
              ]
         },
         "scopes": [
             "read",
             "write",
             "delete"
         ]
     }
     
  3. For the next step you will need to host your add-on so that it is accessible anywhere on the internet. Check out Developing Locally for a way to do this.
  4. Install Gardener using the Universal Plugin Manger (UPM).
    Expand UPM installation instructions [+]

    Install Gardener

    Gardener doesn't have functionality yet (you'll implement that in future steps), but feel free to try to load it anyway.

  5. Create a space in your development version of Confluence and navigate to the space.
  6. Click Tools at the top right, and choose Confluence Gardener.
    You should see a page like this:

Now you're ready to start developing functionality for your Gardener add-on.

Implement a Confluence REST API client

All the functions that request data from Confluence are in your js/data.js file. The functions are incomplete, so in this step you'll flesh these out.

You'll use the Confluence REST API Docs and the AP.request documentation to implement functions to get page and space hierarchy in Confluence, and add Gardener functionality to move and remove pages.

  1. Open the js/data.js file from your confluence-gardener source code. You should see the stub code below:

    
     define(function() {
         return {
             getPageContentHierarchy: function(pageId, callback) {
             },
    
             getSpaceHierarchy: function(spaceKey, callback) {
             },
    
             removePage: function(pageId, callback) {
             },
    
             movePage: function(pageId, newParentId, callback) {
             },
    
             movePageToTopLevel: function(pageId, spaceKey, callback) {
             }
         }
     });
     
  2. Implement getPageContentHierarchy to get the page hierachy in your Confluence instance:
    
     getPageContentHierarchy: function(pageId, callback) {
         AP.request({
             url: "/rest/prototype/1/content/" + pageId + ".json?expand=children",
             success: callback
         });
     },
     
  3. Next, implement getSpaceHierarchy to see the space hierarchy:
    
    getSpaceHierarchy: function(spaceKey, callback) {
        AP.request({
            url: "/rest/prototype/1/space/" + spaceKey + ".json?expand=rootpages",
            success: callback
        });
    },
    
  4. Implement removePage so your add-on can effectively delete Confluence pages:
    
     removePage: function(pageId, callback) {
         AP.request({
             url: "/rpc/json-rpc/confluenceservice-v2/removePage",
             contentType: "application/json",
             type: "POST",
             data: JSON.stringify([pageId]),
             success: callback
         });
     },
     
  5. Finally, try to implement movePage and movePageToTopLevel on your own. If you get stuck, expand the example below.

    Show working implementation [+]

    
     define(function() {
         return {
             getPageContentHierarchy: function(pageId, callback) {
                 AP.request({
                     url: "/rest/prototype/1/content/" + pageId + ".json?expand=children",
                     success: callback
                 });
             },
    
             getSpaceHierarchy: function(spaceKey, callback) {
                 AP.request({
                     url: "/rest/prototype/1/space/" + spaceKey + ".json?expand=rootpages",
                     success: callback
                 });
             },
    
             removePage: function(pageId, callback) {
                 AP.request({
                     url: "/rpc/json-rpc/confluenceservice-v2/removePage",
                     contentType: "application/json",
                     type: "POST",
                     data: JSON.stringify([pageId]),
                     success: callback
                 });
             },
    
             movePage: function (pageId, newParentId, callback) {
                 AP.request({
                     url: "/rpc/json-rpc/confluenceservice-v2/movePage",
                     contentType: "application/json",
                     type: "POST",
                     data: JSON.stringify([pageId, newParentId, "append"]),
                     success: callback
                 });
             },
    
             movePageToTopLevel: function(pageId, spaceKey, callback) {
                 AP.request({
                     url: "/rpc/json-rpc/confluenceservice-v2/movePageToTopLevel",
                     contentType: "application/json",
                     type: "POST",
                     data: JSON.stringify([pageId, spaceKey]),
                     success: callback
                 });
             }
         }
     });
     
  6. Now, refresh Gardener in your browser.
    You should see a page like this:

    Dark blue names indicate the space names, and light blue signifies pages with children.
  7. Click a light blue name, like the Welcome to the Confluence Demonstration Space page name.
    You should see the menu snap open to display the page children:

    Blue pages have children, whereas grey pages have no child pages underneath. You can also use your mouse to zoom in and out – just scroll your trackball up and down.

As you explore your Gardener add-on, you might notice that you're not able to actually remove pages. Let's fix that in the next step.

Display a full-screen dialog

When you attempt to remove a page, nothing happens. In this step, you'll add a a full-screen dialog to confirm the action, and make sure it actually works.

First, you'll declare the dialog in your atlassian-connect.json descriptor file. These dialogs are full-fledged AUI dialogs that exist in the parent frame (not in the same iframe Gardener uses). We'll provide the HTML source for the dialog, you'll register a new webItem that loads inside the full-screen dialog.

  1. Open atlassian-connect.json in your editor.
  2. Add the following snippet after the generalPages entry in the modules object:

    
     "webItems": [
         {
             "key": "gardener-remove-dialog",
             "url": "/remove-page-dialog.html",
             "location": "system.top.navigation.bar",
             "weight": 200,
             "context": "addon",
             "target": {
                 "type": "dialog",
                 "options": {
                     "width": "234px",
                     "height": "324px"
                 }
             },
             "name": {
                 "value": "dialog"
             }
         }
     ]
     

    Show atlassian-connect.json [+] with the dialog webItems entry

    
     {
         "key": "confluence-gardener",
         "name": "Confluence Gardener",
         "description": "Prune back your Confluence page graph.",
         "baseUrl": "https://add-on-dev-url.ngrok.io",
         "vendor": {
             "name": "Atlassian Labs",
             "url": "https://www.atlassian.com"
         },
         "authentication": {
             "type": "none"
         },
         "version": "0.1",
         "modules": {
         "generalPages": [
              {
                  "key": "gardener",
                  "url": "/index.html?spaceKey={space.key}",
                  "location": "system.content.action",
                  "name": {
                      "value": "Confluence Gardener"
                  }
              }
          ],
          "webItems": [
              {
                  "key": "gardener-remove-dialog",
                  "url": "/remove-page-dialog.html",
                  "location": "system.top.navigation.bar",
                  "weight": 200,
                  "context": "addon",
                  "target": {
                      "type": "dialog",
                      "options": {
                          "width": "234px",
                          "height": "324px"
                      }
                  },
                  "name": {
                      "value": "dialog"
                  }
              }
          ]
         },
         "scopes": [
             "read",
             "write",
             "delete"
         ]
     }
     
  3. Reinstall your add-on through the UPM Manage add-ons page. Your add-on won't show any changes – yet. In the next step, you'll implement a function to display the dialog.
  4. In your editor, open removePageDialog.js. You should see another empty function:
    
     define(function() {
         return function (deleteCallback) {
         }
     });
     
  5. Load the dialog and events modules using AP.require:
    
     return function(deleteCallback) {
         AP.require(["dialog", "events"], function (dialog, events) {
             dialog.create({
                 key: 'gardener-remove-dialog',
                 width: "400px",
                 height: "80px",
                 header: "Remove page?",
                 submitText: "Remove",
                 cancelText: "cancel",
                 chrome: true
             }); 
     
  6. After creating the dialog, unsubscribe from any previous confirmPageRemoval event bindings:
    events.offAll("confirmPageRemoval");
  7. Now, try subscribing to confirmPageRemoval event bindings using events.on to register the event and passing deleteCallback.

    Show removePageDialog.js with subscription to confirmPageRemoval [+]

    
     define(function() {
         return function(deleteCallback) {
             AP.require(["dialog", "events"], function (dialog, events) {
                 dialog.create({
                     key: 'gardener-remove-dialog',
                     width: "400px",
                     height: "80px",
                     header: "Remove page?",
                     submitText: "Remove",
                     cancelText: "cancel",
                     chrome: true
                 });
    
                 events.offAll("confirmPageRemoval");
                 events.on("confirmPageRemoval", deleteCallback);
             });
         }
     });
     
  8. In Confluence Gardener, click Remove on a page. You should see a dialog appear:
    If you don't see a dialog, try a hard refresh, or turning off the cache in your browser developer console.

What's next?

You've built an add-on that can help you prune pages from Confluence spaces. Now you're ready to explore our documentation. How about reading through: