Showing posts with label confluence. Show all posts
Showing posts with label confluence. Show all posts

Monday, October 14, 2019

Customizing Confluence: Last Modified Date

Introduction

At times of exporting content from Confluence, you may find yourself in a situation where a date stamp is required in the output files. Depending on your process for exporting content, by default, Confluence doesn't export time data (created or last updated) through its native means. If you have the budget, there are a number of plugins that can provide you with a macro to use in your documents but if you find your budget is tight, you can use the following code to create your own user macro to obtain a document's last updated date.

Required skills

You should be comfortable and/or knowledgable with the following:
  • HTML
  • jQuery
  • Creating user macros in Confluence
  • Modifying page layouts and space layouts in Confluence

Creating an user macro to display the last modified date

To create an user macro that display the last modified date, you can copy/paste the following code into your instance of Confluence:

Macro name: last-modified
Macro title: Last Modified
Description: Displays last modified date of the document.
Categories: Reporting
Macro Body Processing: No macro body
## @noparams
<span id="lastModifiedDate" style="font-size: 0.7em">Last Modified: $action.dateFormatter.formatDateTime($content.lastModificationDate)</span>


This simple little macro grabs the last modified date from Confluence and will display the value wherever you insert the user macro on your document. The font size is made to be purposely smaller so it won't be to noticeable on your document.

Modify layouts

If your process of exporting content includes the rendered HTML document, then instead of inserting a macro on every document can be replaced by updating the space layout with a little jQuery function to obtain the last updated date and insert it into the body of the wiki content element.

Note: Modifying the Content Layout of a space can be dangerous. If you remove a line from the Page Layout, you could corrupt the space and possibly make Confluence unstable. Use this method with care.

If you are using the Default Theme, insert the following code in Page Layout (Space Tools > Look and Feel > Layout > Create Custom under the Content Layouts in the Page Layout section) after the page setters:

<!-- last updated insertion -->
<script>
$(document).ready(function() {
$('div#main-content').append('<span style="font-size: 0.7em;">Last updated: ' + $('a.last-modified').text() + '</span>');
});
</script>
<!-- end last updated insertion -->

If you are using the Documentation Theme, insert the following code in the footer of the page layout:

<!-- last updated insertion -->
$(document).ready(function() {
$('div.wiki-content').append('<span style="font-size: 0.7em;">Last updated: ' + $('a.last-modified').text() + '</span>')
});
<!-- end last updated insertion -->


This method of inserting a last modified timestamp is, IMHO, the easiest and safest way to add a timestamp to every document in a space.

Saturday, September 14, 2019

Migrating Content From One Confluence Instance to Another

Introduction

From time to time, as a Confluence administrator, you'll be called upon to migrate a space to a new Confluence instance. This guide provides some tried and true steps to generate a list of spaces in Confluence, figure out how active a space is (so you can decide if it should be archived, migrated, or simply removed), how to find a space owner, add a warning message to a space, set a space to "read-only" mode, and delete a space.

Required skills

You should be comfortable and/or knowledgable with the following:
  • HTML
  • Managing Confluence space themes
  • Basic Confluence administration
  • Exporting and importing Confluence spaces

Migrating Confluence content

  1. Generate a list of all the spaces in your old Confluence (instance). This list will be used as a checklist for tracking all the spaces that have been migrated to the new instance.
    1. To get a list of spaces, go to Spaces > Space Directory. This page will show you a list of all the spaces in your instance.
  2. Start reviewing the spaces for level of activity. I believe it is safe to say that if a space hasn't had any visitors in 6+ months, then it should be marked as archived and prioritized for either removal or migration.
    1. To see the level of activity, go into the space and then Space Tools > Activity. In the Activity page, set the Period for months and review the previous six months of activity by clicking on the previous button for the month section.
    2. To archive a space, go to the space in Confluence and then Space Tools > Overview. In the Overview page, click on Edit space details button. From there, change the Status to Archived and click on the Save button.
  3. Identify and speak with space owner(s) and get their permission to archive and/or migrate a space. Also ask them if the space should have a higher or lower priority for migration.
    1. To see who the space owner is, open the space and go to Space Tools > Permissions. Under Individual Users, you should typically see someone who has all permissions to the space. Another way to see who created the space is to go to the space's home page. This home page will have information about who created this page (which should be the person who created the space (i.e. the owner of the space) unless of course the Confluence administrator is responsible with the task of creating spaces.
  4. Work with space owner(s) on active spaces so you don't interrupt their work and set a date for migration.
  5. Add a migration warning to the space that it will be migrated on a designated date.
    1. To add a warning label to a space and the target space is using the Documentation Theme, follow these steps:
      1. Navigate to the space's Themes page (space > Space Admin > Themes).
      2. In the Messages section under Header, add the following code:
        {html}
        <div style="background-color: red; color: white; padding: 5px;">This space will be migrated on <span style="color: yellow"><designated_migration_date></span></div>
        {html}
      3. This will add a message on top of every page in the space and the unstylish colors will definitely grab the attention of everyone viewing the page.
      4. Note: it's a good idea to give everyone at least a two weeks notice about the migration.
  6. For unvisited spaces or spaces about to be migrated, put the space into "read-only" mode.
    1. To make a space "read-only", go to Space Tools > Permissions.
    2. In the Permissions page, you should see a list of space admins and groups. It would be best to leave the permission scheme alone for the space admins but if you have a group where all users fall under, change it so it is only set to All View to checked and everything else is unchecked.
  7. Export spaces on designated dates. Start with higher priority spaces and work your way down to the low priority spaces. See Export and Import a Confluence Space for instructions on how to export a space.
  8. Import the exported space into the new instance. See Export and Import a Confluence Space for details on how to import a space.
  9. Go into the newly imported space and confirm that everything is in proper order (content and attachments are fine, macros are working as expected, permissions are good, and so on). Note: this step may take a bit of time. I recommend that you get help from the original space owner(s) to confirm that the migration went well.
  10. Change migration notice to migrated and marked for removal. Update warning of the exported space in the old instance that it has been migrated (with a link to the new space) and add a removal date.
    1. To add a removal warning, repeat the sub-steps listed in step 5.
      1. Navigate to the space's Themes page (space > Space Admin > Themes).
      2. In the Messages section under Header, add the following code:
        {html}
        <div style="background-color: red; color: white; padding: 5px;">This space has been migrated to <a style="color: white; text-decoration: underline;" href="url_of_new_confluence_instance/display/<spacekey>">url_of_new_confluence_instance/display/<spacekey></a> and will be removed from this wiki on <designated_removal_date></div>
        {html}

      3. This will add a message on top of every page in the space and the unstylish colors will definitely grab the attention of everyone viewing the page.
      4. Note: it's a good idea to give everyone at least a two weeks notice about the removal.
  11. On the designated removal date, delete the space old space in the old instance of Confluence. While this is a very dangerous step, keep in mind that you have the exported zip file and the newly created space in the new instance. If anything goes wrong, you can always re-import the space back into the original Confluence instance.
    • To delete a space, go to Space Tools > Overview. Click the Delete Space button. You may be prompted to enter your credentials so be ready to enter that information. Click the Ok button to start the deletion process. Depending on how big the space is, this could take a few seconds or several minutes. Check with the Time Remaining counter (I find it's mostly accurate 90% of the time but that will vary from server to server based on your server's configuration).
Tip: You may want to include a message at the top of the newly imported space in the new instance where one can find the old space in the old instance. Just use the sub-steps mentioned in either step 5 or 10 and add the necessary info to point back to the old space.

Wednesday, August 14, 2019

Modifying an Exported Space From Confluence

Introduction

During times of Confluence migrations (from one instance to another), you may find yourself in a situation where the new Confluence instance has a space that has the same spacekey as an old space that you are attempting to import. This month's post will show you how to get around that issue.

Required skills

This document assumes you know how to export a Confluence space already and are comfortable with editing XML files.

Exporting and editing

To start, you will need to expand the exported zip file (that comes from exporting a space). In this expanded directory, you will need to edit the exportDescriptor.properties file. In this file, modify the spaceKey property to the desired spacekey and save your change.

In entities.xml, search and replace the following items listed in this table (replacing NEWKEY with an unused and new spacekey you wish to use in the new Confluence instance):
Search for Replace with
[CDATA[OLDKEY] [CDATA[NEWKEY]
OLDKEY NEWKEY
spaceKey=OLDKEY spaceKey=NEWKEY
[OLDKEY: [NEWKEY:
key=OLDKEY] key=NEWKEY]
<spaceKey>OLDKEY</spaceKey> <spaceKey>NEWKEY</spaceKey>
ri:space-key="OLDKEY" ri:space-key="NEWKEY"
ri:space-key=OLDKEY ri:space-key=NEWKEY
<ac:parameter ac:name="spaces">OLDKEY</ac:parameter> <ac:parameter ac:name="spaces">NEWKEY</ac:parameter>
<ac:parameter ac:name="spaceKey">OLDKEY</ac:parameter> <ac:parameter ac:name="spaceKey">NEWKEY</ac:parameter>
<property name="lowerDestinationSpaceKey"><![CDATA[NEWKEY]]></property> <property name="lowerDestinationSpaceKey"><![CDATA[newkey]]></property>
<property name="lowerKey">![CDATA[NEWKEY]]></property> <property name="lowerKey"><![CDATA[newkey]]></property>
spaceKey=OLDKEY spaceKey=NEWKEY
spacekey=oldkey spacekey=newkey

With those two files updated, re-zip all content back together, rename it to the original zip file (you may need to remove the old zip file or just rename it just in case), and upload it to your new Confluence instance as you would normally to import a space.

Tuesday, May 14, 2019

Using Nightmare.js to Generate a Sitemap From Confluence

Introduction

In a recent project, I had a task to generate a list of documents in a particular Confluence space. I chose to explore my options using Nightmare.js. Using this Node.js (version 10.11.0) module, it allowed me to programmatically enter my credentials into Confluence, navigate to a specific document, and gather a list of documents (thanks to the target document using the Children Display macro that listed all the documents of the parent page of the target space). I also wanted this script to take arguments (flags) such as the username, password, spacekey, output file, and a delay value so that the process can be automated for a variety of reasons.

Required skills and npm packages

This tutorial requires a number of skills and/or npm modules to complete everything mentioned herein:
  • Confluence (5.x): You should be comfortable with creating pages that utilize the Children Display macro
  • Nightmare (3.0.1): have some familiarity with the basics of this module
  • Commander (2.19.0): have some familiarity with the basics of this module
  • Cheerio (1.0.0-rc.2): have some familiarity with the basics of this module
  • CSS: basic knowledge of how to select elements
  • JavaScript: fair knowledge of how to use JavaScript

Setting up requirements

First, we set off with requiring a number of modules:

const Nightmare = require("nightmare");
const cheerio = require('cheerio');
const program = require('commander');
const fs = require('fs');

....

Set up nightmare and flag options

The next two lines sets up nightmare to display it's process as it's going through the steps we'll program it to navigate and a selector to find the content we're looking for in our target document. The confluenceSelector is the CSS selector that will be used to find the desired content in the main body of the Confluence document.

....
const nightmare = Nightmare({
    show: true
});
const confluenceSelector = '#main-content';

....

Note: you don't want to see an Electron window pop up and nightmare to do it's stuff, set show to false.

Next, we set up the flags and their usage using commander's features:

...
program
  .version('0.0.1')
  .usage('-u <username> -p <password> -s <spacekey> -f <output.txt> -d <milliseconds>')
  .option('-u, --user', '*required* Username id')
  .option('-p, --password', '*required* User\'s password')
  .option('-s, --spacekey', '*required* Spacekey for the Confluence space')
  .option('-f --file', 'Text file to be used for tracking Confluence document names. Can be set to either true (defaults to the spacekey naming scheme) or a file name.')
  .option('-d, --delay', 'Delay (in milliseconds) to wait for server response')
  .parse(process.argv);

...

With the flags set, we now need to parse them into an object that we'll use throughout the rest of the script. We loop through the program.rawArgs value provided by the commander module. In this loop, we are looking for specific flags so we can associate the flag with the value associated with it.

...
var argument = {};

for (var i = 0; i < program.rawArgs.length; i++) {
  if (program.rawArgs[i] == '--user' || program.rawArgs[i] == '-u') {
    arguments.user = program.rawArgs[i + 1];
  }
  if (program.rawArgs[i] == '--password' || program.rawArgs[i] == '-p') {
    arguments.pass = program.rawArgs[i + 1];
  }
  if (program.rawArgs[i] == '--spacekey' || program.rawArgs[i] == '-s') {
    arguments.spacekey = program.rawArgs[i + 1];
  }
  if (program.rawArgs[i] == '--delay' || program.rawArgs[i] == '-d') {
    arguments.delay = parseInt(program.rawArgs[i + 1]);
  }
  if (program.rawArgs[i] == '--file' || program.rawArgs[i] == '-f') {
    arguments.file = program.rawArgs[i + 1];
  }
}

...


Since the delay flag is optional, we should set up a fallback if the user doesn't supply one. In this case, we're setting the delay to 10 seconds though you can adjust this delay value to a number you're comfortable with your Confluence server responding a login page request.

...
if (!arguments.delay) {
  arguments.delay = 10000;
  console.log('Server response delay not set. Assuming ' + arguments.delay + ' millisecond delay.');
}

...

Now we should set up the file path where we keep the site map information. If the user doesn't supply a file to output our data to, the script will use a fallback based on the submitted spacekey name.

...
if (arguments.file) {
  if (arguments.file.length > 5) {
    var confluenceSiteMap = arguments.file;
  } else {
    var confluenceSiteMap = arguments.spacekey + '-site_map.txt';
  }
} else {
  var confluenceSiteMap = confluenceSiteMap.txt;
}

...

The next thing our script will need is the Confluence URL to the site map document. Using the Children Display macro in your target Confluence space, we can gather all the document links in a single space by scraping this one document. Note: you should set up this Confluence document accordingly before executing this script and ensure it's named Site Map. Otherwise, you'll need to change the values in arguments.confluence.

...

if (arguments.spacekey) {
  arguments.confluence = <base Confluence URL> + '/display/' + arguments.spacekey + '/Site+Map';
}

...

With the arguments parsed, we should check that the user supplied the required flags. If any of these flags weren't submitted, then the script should gracefully exit.

...
if (!arguments.user || !arguments.pass || !arguments.spacekey) {
  if (!arguments.user) { // user id is required
    console.log('Username is required.');
  }
  if (!arguments.pass) { // password is required
    console.log('Password is required.')
  }

  if (!arguments.spacekey) {
    console.log('Spacekey is required.')
  }

  process.exit(1);

...

Pull content with nightmare

With the required flags set, we can now request a document from Confluence using your credentials. This chunk of code starts the nightmare.js process by navigating the Electron browser to the site map page in Confluence. The process belows assumes that a login is required when the target page is loaded, enters user supplied username and password in the appropriate fields (denoted by their element ids), click the login button (denoted by it's element id), wait for a period of time (hopefully long enough for the server to respond), grab the content from the predetermined CSS selector via the evaluate method, return the data for parsing later, and close the Electron browser.

...
} else {
  console.log('Getting document link list from ' + arguments.confluence);
  nightmare
    .goto(arguments.confluence)
    .type('#os_username', arguments.user)
    .type('#os_password', arguments.pass)
    .click('#loginButton')
    .wait(arguments.delay)
    .evaluate(confluenceSelector => {
      return {
        html: document.querySelector(confluenceSelector).innerHTML
      }
    }, confluenceSelector)
    .end()

...

Parse content with Cheerio

Now that nightmare.js has retrieved the document in question, we use the then method to load the HTML content into cheerio.js to generate a list of links. Generally speaking, the links listed in a Confluence document usually follow the li span a selector pattern inside the body of the document. Here, we use the output variable to hold the list of links found in the retrieve data.

...
.then(obj => {
  $ = cheerio.load(obj.html.toString());

  var output = '';

  $('li span a').each(function() {
    output += $(this).html() + '\n';
  });

...

Then, we write out the list of links we found in the Confluence document to our predetermined text file.

... 
  fs.writeFileSync(confluenceSiteMap, output, 'utf8');
})

...

Finally, we use the catch method to report back any errors.

...
  .catch(error => {
    console.error(error);
  });
}


Wrapping up

With the script complete, we should save it something like confluenceSitemap.js. From there, we can execute this command to generate our list of links text file: node confluenceSitemap.js -u <username> -p <password> -s <spacekey> -f <links.txt>

Sunday, April 14, 2019

Comparing Published and Unpublished Documents in Confluence

Introduction

I recently had a challenge to upload over a thousand HTML documents to Confluence. I won't go into the details of what scripts I created using various Node.js modules, but I did want to share with you how I maintained a list of documents that were or were not published to Confluence.

Requirements

You should be comfortable with a terminal interface, managing documents in Confluence, and Confluence CLI plugin.

Using the Confluence CLI

I wrote a script that generates a list of documents and media files from a specified directory (I'll share that script and it's processes another time). From there I used the Confluence CLI plugin to report back a list of files that have already been uploaded to Confluence. The command was pretty simple:

confluence --action getPageList --id "<parent page id>" --descendents > uploaded_docs.txt

Note: the Confluence command itself needs to be setup as an alias in your Bash profile. The instructions for setting up the Confluence CLI plugin mentions how do some of this. My Bash alias looks something like this:

alias confluence="<path to confluence script>./confluence.sh --server <base Confluence URL> --user <user> --password <pasword>"

With that alias setup and a little forward thinking about how the space was going to be structured under a single document, I saved myself some time by parenting all the documents under this one ultimate parent document. (I wrote a script that handles that task as well which I'll share another time.) Having a single parent document, the CLI command reported back all the documents I needed to work with in one single execution of this command. Otherwise, I would have had to identify each parent document, execute this command on parent document, and tally up all the uploaded documents.

From here, with the two lists in hand, it was now a simple matter of finding the differences. There are several options out there to accomplish this but in the end, I just used Excel and used the conditional formatting feature to highlight the duplicates and the ones that weren't highlighted were the ones that needed to uploaded.

Maybe in the future I'll write a script that does this automatically from the two lists and share that process as well.

Thursday, July 19, 2018

Export and Import a Confluence Space

This guide walks you through the process of exporting content from one version of Confluence (5.6.x) to another (6.6.x).

Exporting a space

  1. Navigate to the space you wish to export.
  2. Click on Space Tools > Content Tools.
  3. In Space Tools page, click on Export tab.
  4. In the Export Formats section, select XML and click on the Next >> button.
  5. In the Export XMLOptions section, select Full Export (should default to this option).
  6. Click the Export button to start the process.
  7. Wait. Depending on how many documents you have in your space, this can take a few seconds to several minutes. Confluence will switch to a "In Progress" report page and periodically update the status of the download. Don't get discouraged if the original Time Remaining estimate lists some ridiculous number (it's just an estimate).
  8. Once it completes the process, click the Download here link just below the completion bar. You should receive a zip file with all the contents and attachments.
Note: you can modify the contents of a space by modifying the entities.xml file found in the zip file. It would unwise to do this but if you need to programmatically change something throughout an entire space, you can do that.

Importing a space

  1. Log into your target wiki instance with Confluence administration permission.
  2. Under the cog, click on General configuration and then Backup & Restore. Even though this says it's a restore feature, it also acts as a space importer as well.
  3. Under the Upload and restore a site/space backup, disable the Build Index checkbox. With this checkbox disabled, the upload will be faster. Otherwise, if you wish to build the index, you should do so after office hours depending on your server attributes.
  4. Click on Choose File and navigate to where you downloaded your exported zip file and click on Open.
  5. Click on Upload and Restore.
  6. Confluence will take you to an import progress page that estimates the upload process. This may take a moment or two depending on the size of the space you are importing.
  7. Once completed, you should check the newly imported space to confirm it's contents.

Wednesday, February 14, 2018

Migrating Content to Another Confluence Server (and/or Upgrading Confluence)

Over the course of my career as a tech writer, I've also had the responsibility to maintain a Confluence server or two. In that time, I've performed several Confluence upgrades. Each upgrade has had unique challenges to migrate content from one server to another but they all have common steps that needs to be handled. Those steps include migrating plugins, user macros, and customizations, update and/or revise any automation scripts that depends on your Confluence server, content migration, and testing all the above.

In this month's post, I'm posting my list of common considerations for migrating Confluence to a new version. Below, you will find a checklist of steps that will ensure your migration goes smoothly. While there is no silver bullet in a complete migration process in the sense of which steps one should take first, in my experience, the order should look something like this:

  1. Build a test server with the new version of Confluence
  2. On a test server,
    1. Install necessary plugins
    2. Migrate user macros
    3. Migrated customizations
    4. Copy content
    5. Duplicate and customize any scripts that depends on your Confluence server
  3. Test everything!
    1. Do the plugins work? If not, why not?
    2. Do the user macros work?
    3. Does the customizations act and function as they did before?
    4. Does your scripts still work?
    5. Is all your content present and accounted for (page content, attachments, links, features, etc.)?
  4. Upgrade or update necessary plugins as needed
  5. Update broken or malfunctioning user macros
  6. Revise any customizations as needed
  7. Test again with fresh eyes: Have someone outside your team do a test run on a space that you have copied over for testing your migration settings
  8. Have a plan in place in case the switch over date encounters issues that prevents the migration from completing on time or failing all together
  9. With testing and fixing nearly done, announce a switch over date that includes any downtime, list of deprecated features as well as new ones, and set your user's expectations accordingly

Plugin migration

  1. Generate a list of plugins that needs to be installed in the new Confluence instance.
    • Confirm usage of plugins. There's multiple ways of accomplishing this. One way is to use Confluence's own search engine to find any and all used macros of a plugin. First, generate a list of all the enabled macros that is packaged with your plugins. Second, in the search field (with the filters set to search all spaces), enter this: macroName: <macro>*. This will return a list of pages that use the plugin macro in question.
    • Ensure deprecated plugins doesn't have any impact on our users. On your test server, you can check if an uninstalled plugin has any effect on your content. This can be accomplished in many ways such as brute forcing a review of every page that utilize your plugins or you can write scripts that checks the rendered HTML versions of your content for error messages.
    • Generate a handful of pages that utilizes each active plugin in the current server. Once these pages/spaces have been migrated to the new server, you'll want to check these pages to ensure that the plugins are working properly. I found have these "uber plugin" pages rather useful to one spot to view the behavior of the plugins that will get migrated. One should note though, some plugins won't be compatible with others and some may slow the server down. Depending on how many plugins you have installed, you may want to split up your uber plugin page into one page for every 15-20 plugins installed.
  2. Confirm that the current plugins are compatible with with the newer version of Confluence. This can be done by checking your admin section for any messages from the vendor about compatibility issues.
  3. Install required plugins on the new server.
  4. Report and track issues with the plugins.
  5. Configure the plugins with the same settings as they were defined in the older server. This will ensure that your users have the same experience with the plugins as before the migration took place.
  6. Clean up or archive pages that have uninstalled plugins. While this may be an optional step, if a page has become useless due to the unistalled plugin, there is a good chance that said page has outlived it's usefulness and can be archived.
  7. Inform team of the migrated plugins were successful (with/without any notes about broken or archived pages, plugins removed, and their impact).

User macro migration

  1. Generate a list of user macros:
    • Confirm usage of macros. This can be accomplished by searching for macroName: <user macro>* in the search field.
    • Ensure deprecated macros doesn't have any impact on our users. If they do, confirm that the page is still needed or not by your users. Chances are, they won't mind archiving this page.
    • Generate a page that utilizes each required user macro. As noted before, you may need to split the user macros up between multiple pages.
  2. Install required macros on test server.
  3. Test user macros for compatibility issues.
  4. Report, track, and fix issues for user macros.
  5. Backup up revised macros (e.g. Github repositories, local backups, text files with the code, etc.).
  6. Document changes to revised macros as necessary.
  7. Inform team of the migrated user macros were successful (with/without any notes about broken or archived pages, user macros removed, and their impact).

Customizations to Confluence

Migrating customization between different versions of Confluence can be tricky. Between major versions, the framework and page layout changes which will adversely affect any customization you made using HTML, CSS, JavaScript, jQuery, frontend/backend libraries, and so on. While it may be tempting to go back to a "vanilla instance" of Confluence, if your users come to rely and use these customizations, you'll have to justify not migrating them.

Out of all the migration steps, this one is the trickiness due to their intertwined nature of your custom code and Confluence's. My advice here is to map what your customizations use to hook into Confluence. For example, if a customization relies on the page layout to always have a body element with a nested div element with the class of "content", make sure the newer version of Confluence has that same page layout. If it doesn't, map it out and adjust your customization accordingly.

Again, in my experience between major versions, I had to make adjustments or keep an eye out for custom features that involves the following web features:
  • HTML: Atlassian tries to keep current with the latest HTML standards. You should too.
  • CSS: Classes, ids, and element selection in the CSS files change as well. Check your custom CSS files for any changes you need to make as well.
  • JavaScript: Keep your JavaScripts current and confirm that if the script hooks onto any element, that the element is still there as your script expects it to be.
  • jQuery: Atlassian keeps current with industry standards on which version of jQuery it utilizes. If you scripts use jQuery, confirm that the newer version doesn't adversely affect your customizations.
  • Libraries: Aside from jQuery, Confluence uses many other front-end libraries. You can include any library you like as well but make sure it still works with the new server.
  • Confluence features: If you use a combination of native and 3rd party plugins, user macros, and/or customizations to achieve a non-vanilla feature, you'll need to ensure that this feature still works.

Content migration

With your plugins, user macros, and customizations migrated, you should start migrating production content.
  1. Generate a list of spaces to migrate.  Confirm with our users that their team's spaces needs to be migrated. If not, archive the space using any method you deem worthy. Word of advice: it never fails that 1 in 20 users will ask you the whereabouts of a page that didn't get migrated 2-4 weeks afterward. I have been fortunate enough that we keep the old server around for just that purpose but if you find yourself in a different boat, I would recommend dumping an deprecated space to HTML documents, organization, and place them somewhere you can find them in the future. You may end up migrating the deprecated space anyway but mark it as "archived".
  2. Lock a space prior to migration. This will ensure that no new pages will appear on the old server while migrating this space's content to the new server.
  3. Migrate approved spaces. Track which spaces have been migrated.
  4. Test migrated spaces: refer to your list of pages that have plugins and user macros that need to be checked. Do a spot check of a dozen or so random pages from the migrated space to ensure the content was properly migrated without any errors. Note: this could be automated using a combination of scripts to extract the content, parse the HTML documents looking for error messages, broken links, missing images, and report on which page and what error message(s) was found.
  5. Report, track, and fix issues with migrated spaces.
  6. Inform teams that their spaces have been migrated.
  7. optional: Archive old spaces in the old or new server or in a non-Confluence repository.

Test scripts and external dependencies

  1. Ensure that any script you utilize with Confluence works as expected. For example, if you have a plugin that generates a REST API for you to generate HTML docs for publication (pretty specific), then you should confirm that the REST URL and related scripts still generate content in a consistent form.
  2. Review, test, and update your scripts accordingly.
  3. Document any changes to your scripts. If the changes affects the public consumption of your content, inform your audience.
  4. Back up your scripts. It is a good practice to have the old scripts archived.
  5. Announce to your users, if they rely on the output of said scripts, that everything works as it should or if you've made any improvements.
There you have it. Hopefully, this guide will help alleviate any future issues with your next Confluence upgrade.

Sunday, January 14, 2018

Sustainable Documentation in Confluence

Maintaining documentation in Confluence can be challenging at times (especially if you work for a company that produces hundreds or thousands of pages a month). This post provides some tips on how provide for sustainable documentation in Confluence.
  • Proper page names - this should go without saying. 
  • Leaving comments when editing a page (even minor edits like fixing typos). This will help with reviewing the page (history) when an issue arises in the future 
  • Linking is important (internally and to some extent externally). Confluence maintains links (intra and extra-space) automatically but doesn't valid or attempt to help the user when an external link is broken. If the external link is important, consider copying the content from the source and provide credit on the page. This wades into some grey areas that will need to be handled on a document-by-document basis (unless we have a company policy that provides guidelines as such). 
  • Labeling - helps with search results and potential documentation groupings. Use simple concise terms, avoid plurals and verbs, and try to limit to no more than three or four terms. 
  • Document hierarchies - Navigation my be king in documentation but it's not the "be-all end-all" solution. Keep the end user in mind. Topics must flow based on a simple logic or whatever logic your company deems necessary. 
  • Minimal use of macros. Plugins come and go all the time (based on popularity and yearly budget). 
  • Use page comments where necessary. If someone leaves a comment on a page that calls for action, do it, reply to the comment as such, and then remove the comments. It doesn't look good on us to leave years old comment on a page. Visitors will see the comment and may think the document is out of date. 
  • Pictures are worth a thousand words or minus a thousand words if they are sorely out of date. Try to use generic images where possible in your general documentation. Product version-specific imagery should be generally avoided unless it is tied to a release. The same is true of videos. Also, Confluence isn't a media hosting service and file attachments are limited to 25mb (default). Videos attachments should avoided categorically. I have several stories (fun, sad, and problematic) about this topic. 
  • On the topic if images, confirm that all images are properly attached to the target wiki page. While one can attach images from multiple sources (other wiki pages (complex topic), external sites, other servers, etc), you will run into publication issues if the images are not found within the Confluence server. 
  • Using the search engine to suss out outliers and commonly searched terms. Try to do this on a quarterly basis to see where your users are going and look for potential gaps in your documentation. This creeps into using and reviewing analytic data to draw a bigger and clearly picture. 
  • Try to find "Documentation Champions". DCs will be your ally in keeping the docs current and concise while notifying you of any weeds popping up their doc garden. Encourage them to contribute freely and review their work for minor issues (verbiage, typos, messaging, language issues, etc.). DCs will be your best resource to get other invested in the documentation when they feel welcomed, encouraged, and rewarded. 
  • If some people on your team have not started using Confluence for documentation (a.k.a. "squeaky wheels"), engaged them. Ask why they haven't adopted Confluence. The reasons can be numerous but most likely, at least from my experiences, it because they don't want to "learn another program", "they're too busy to ...", or "I like my current workflow to document". Look for the pain points and point out that we are using Confluence as the main source of documentation. Does the squeaky wheel really want to be left behind? 
  • Try to schedule regular doc reviews (annually would be nice). It's difficult to get the team engaged on this one but if one has the time to show the value of having fresh and current documentation, then the product manager should take this task up regularly. This will help prevent documentation overload. 
  • Don't be afraid to archive documents in a separate non-searchable space. This will have to be negationed with you and your team. Confluence has the option to make a space searchable or not. All spaces are searchable by default but when a space is marked as non-searchable, the documents found within it will not show up in a search result unless requested in the search options. There are tools out that can automate this process but you'll have to see if we have the budget for that plugin. 
  • Rely Confluence's version control for documents and attachments. When in doubt, check the page history and revert as necessary. 
  • Use restrictions lightly. If you need to draft something that isn't ready for prime time, consider restricting the single page or parent page if the page count is under a dozen or so. If you have a need for restricting an entire space for whatever reason, do so but don't openly mention the restricted space in meetings. This will provide an air of mystery when there is none (hopefully). 
  • Don't over-complicate permissions for individuals or groups. Keep it as simple as possible. You don't want to spend time managing permissions do you? 
  • Refuse to provide people with PDFs of a document. Printing a page is rather wasteful if they have access to the wiki. While Confluence natively supports the option to export a document to PDF, don't go out of your way to advertise this feature. 
  • If a page is out of date, look at who the original creator is and who last edited it. Contact those individuals and ask for clarity on the topic. Often time, these individuals (sometimes one in the same) will be happy to help you out.
  • Templates are golden. If your users are afraid to create a new blank page, give them a template and ask them to simply fill in the blanks. This helps new Confluences get started with Confluence, gain confidence with Confluence, and helps you maintain order (somewhat) in how your content is formed.

Tuesday, November 14, 2017

Build an user macro with parameters in Confluence

Overview

Continuing off of last month's post entitled "Build an user macro in Confluence", this document will show you how to write an user macro that accepts input from the user. We will be creating an feature that allows for page redirects in Confluence. The macro will accept two inputs from the user: 1- page title to redirect the current page to and 2- delay before current page is redirect (which should include a default of 10 seconds). From there, the macro will display a basic redirect message, derived in part from the user's input, and load the target page after the delay has passed.

For this tutorial, you should have completed either last month's tutorial and/or Guide to User Macro Templates, Velocity, and be comfortable with HTML and JavaScript.

Parameters

This user macro will take two user inputs: page title to redirect the current page to and a delay time. Let's take a look at these parameters and then break them down:

## @param URL:title=URL|type=string|required=true|desc=URL to redirect to.
## @param Time:title=Time|type=int|default=10|desc=Time to redirect (in seconds). Defaults to 10 seconds.


To declare a parameter, we use the ## @param <parameter name> definition. The parameter name can be anything as long as there are no spaces or starts with a number. It's a good habit to get into naming the parameter names by their purpose.

Next, we have the title of the parameter. The parameter is displayed in the macro's properties when entering the value for the parameter. Here, you can use any combination of characters and spaces as you see fit. The title value should be human readable.

To help the Confluence user macro determine what kind of value it is using and to make the user macro more user friendly, we define the type of input we are providing to the user macro. For the URL parameter, we declare the type to be a basic string (which means the value can be anything). For the Time parameter, we set the type to be int (integer (numbers only)).

For the URL parameter, we set a property called required to be true which tells Confluence that this parameter is mandatory. Not setting this parameter will result in an undefined value and thus disable the macro under it is defined.

For the Time parameter, we use the default property to provide the macro with a default value of 10 (seconds). This is a convenience for the user as they won't need to enter a value unless they want to use something other than the default value.

The last property in both of these parameters is the description (desc). This is the text that will be displayed in the user macro window when filling out the various parameters. You should strive to keep it short, simple, and direct to the point.

Macro Browser Information

As mentioned in the previous tutorial, you don't necessarily need to fill out every field in this section of the user macro but you are required to at least fill out the Macro Name and Macro Title. The others are up to you fill out as needed. But for this user macro I included the following values:
  • Macro Name: redirect
  • Visibility: Visible to all users in the Macro Browser
  • Macro Title: redirect
  • Description: Redirect current page to a new URL within a user specified time (seconds).
  • Categories: Navigation

Definition of User Macro

Since we won't be including any body information in this user macro, we'll set the Macro Body Processing to No macro body.

Template

For the template (code) of the user macro, we'll break it down into two sections: Setting Velocity variables and the rest of the code.

Setting Velocity variables

We set Velocity variable like this:
#set($variable=value)

For our macro's delay, we need to use the following to set up the input variable as milliseconds:

#set($timeOut= $paramTime + "000")

Here, we are setting the variable $timeOut to be the value of the Time parameter which the user supplies in the macro or the macro provides via it's default value.

Basic redirect message

Next, we need to set up the basic message that this page will redirect the current page to the target page.

<div id="redirectBox">This page will be redirected to <a href="$paramURL">$paramURL</a> in $paramTime seconds.</div>


Note the usage of the parameter variables and not the Velocity $timeOut variable so far. The code provides the wiki page with a few elements that displays a basic redirect notice and provides the user with a link in case the redirect function doesn't work automatically or the user wants to forego the delay and visit the target page sooner than later.

Redirect function

The last bit is the JavaScript function to redirect the page after the specified delay:

<script type="text/javascript">
  function Redirect() {
    window.location="$paramURL";
  }


  setTimeout('Redirect()', $timeOut);
</script>


Ideally, this JavaScript function would be included in a library that your Confluence server makes available globally but since it's a small and, hopefully, rarely used macro, it wouldn't hurt the page load time too much if we just include this function on every instance of the macro.

Complete macro code

## @param URL:title=URL|type=string|required=true|desc=URL to redirect to.
## @param Time:title=Time|type=int|default=10|desc=Time to redirect (in seconds). Defaults to 10 seconds.
#set($timeOut= $paramTime + "000")
<div id="redirectBox">This page will be redirected to <a href="$paramURL">$paramURL</a> in $paramTime seconds.</div>

<script type="text/javascript">
function Redirect() {
  window.location="$paramURL";
  }
setTimeout('Redirect()', $timeOut);
</script>



Resources

Saturday, October 14, 2017

Build an user macro in Confluence

As a tech writer, I often find myself using a good collection of Confluence macros in my documentation. So of the most commonly used macros are the table of contents, style, Jira issues, align, attachment, children display, html, and live search. There's actually dozens of macros that I utilize on a regular basis but I commonly use these in some in some combination with each other. For example, I use the table of contents macro nested within the align macro so I can avoid the dreaded long list of anchor links at the top of a document and have the table of contents listed in a floating (left) element so the content of the page can flow around it.

Wouldn't it be nice to combine some of these macros into one "uber macro"? In this tutorial, I'll walk you through the process to do just that: create a table of contents nested within a float left element.

User macro anatomy

Writing an user macro isn't for everyone. It requires some basic knowledge of how HTML and Confluence XML macros works. Plus any skills with CSS, JavaScript, jQuery, and Velocity would be to your benefit to create more advanced user macros. If you don't feel comfortable getting started with this tutorial, you can check out Guide to User Macro Templates by Atlassian for a good overview of what user macros templates are and how to write a basic user macro.

The typical anatomy of an user macro in Confluence looks something like this:
  • Macro settings
  • Macro definition
    • In-macro comments
    • Declaration of parameter usage (or not)
    • Template (macro code)
  • Detailed documentation (external to the macro)

Macro settings

Every macro must declare the following items:
  • Macro Name
  • Macro Title
These parameters of the user macro are required and the Macro Name should be all lowercase and unique. The Macro Title should reflect the Macro Name but you should be write it so it's human readable as this is the name of the macro that will appear in the macro browser.

The following macro settings are optional but highly encouraged to be filled out:
  • Visibility
    • You must decide if this macro will be available to everyone or to just the system admins
  • Description
    • This is the description of the macro that your users will see when selecting this macro from the macro browser
  • Categories
    • The macro browser can be sorted by different categories of macro. Choose yours accordingly. 
  • Icon URL
    • If an icon is not defined, Confluence will assign it a generic icon. For faster load times, the macro should be internal to your system. Historically, an icon can be hosted somewhere within the server as long as it is reachable by the standard user. Or, it could be hosted on a wiki page as an attachment.
  • Documentation URL
    • To say the least, this macro should be documented somewhere on the wiki

Macro definition

The Definition of User Macro is the section of the user macro that allows you to define the Macro Body Processing and it's Template.

Macro Body Processing options

The Macro Body Processing allows you to instruct Confluence how the macro should process the body before passing it your macro. The macro body is what gets display on the Confluence page, if the macro has a body that is.

Option for processing includes:
  • No macro body
    • Use this option if your macro does not utilize a body
  • Escaped
    • Use this option to render the contents of the body as HTML markup
  • Unrendered
    • HTML content will be processed before being rendered
  • Rendered
    • HTML content is rendered but doesn't guarantee that it will be rendered before the page finishes rendering. 
For our macro, we'll set this parameter to No macro body as we won't be utilizing any body elements.

Template

The Template is where you provide you macro with the code that it will utilize to render the desired results. You can use a combination of HTML and Confluence-specific XML elements, and Velocity.

In-macro comments

As an good code is well commented, user macros should utilize comments as well. To add a comment, you must use the #* comment *# format.

While it's not 100% necessary, it is helpful to include the two comments at the top of your macro: one sentence description (if the description is not filled out) and a link to it's documentation (if no link is provided for the Documentation URL). For the macro we will build in this tutorial, our comment should look something like this:

#* floatLeftToC is a left floating table of content *#
#* for more info, see <tiny url> *#

Parameters

Parameters allows the users to pass in options that the macro may use. Discussing the different options and features of macro parameters can be a whole tutorial in itself.

For the macro we are going to build, it won't take any options or parameters. Therefore, the parameter line in our code should look something like this:

## @noparams desc=Creates a left aligned floating table of contents

Code

For this macro, we want to wrap whatever content into a div element so we can float the element and it's content to the left of the page. To accomplish these, we can do the following:

<div style="float: left; margin: 0 10px 10px 0; padding: 5px 10px 5px 0; min-width: 50px; max-width: 250px; border: solid thin black; border-radius: 5px;">
  <ac:structured-macro ac:name="toc"/>
</div>

Ideally, the CSS would not be inline to the element we are using to wrap around the ToC. In my experience, the CSS should be a globally declared rule that Confluence makes available throughout your wiki.
Note: the border attributes in the style attribute is complete superficial. I like to use this when developing and/or debugging user macros that act as a container.

The nested structured-macro handles the table of contents thanks to a native Confluence macro.

Documentation

The documentation should include the following information:
  • Purpose
  • Usage
    • Intent
    • Parameters
  • Change history
  • Creator info
    • Name, email, etc.
  • Known issues and/or limitations
  • Links to related content
  • Attachment of the code
The purpose of the macro should be clearly defined with it's intent. One doesn't need to justify it's existence (as one assumes this macro was created out of a request or to fulfill an existing need).

The usage section should plainly define it's intended usage, and the parameters (if any) are used, and provide a few examples of the macro in use.

Depending on your work environment, you can include the change history of this macro (if you're not using Github or similar services). The same is true with the creator info and attachments of the code sections.

Depending on it's usage, you may want to include a section about known issues or limitations. While the macro should be designed to work with everything in the Confluence environment, it may not due to a multitude of reasons. Sometimes, the most innocent macro make work perfectly by itself but when combined with another, it may break the other itself or other macros. One recent example that comes to mind is a macro that wrapped a div element around an existing Confluence macro that changes the appearance of it via CSS. While this macro was written to only affect that one macro wrapped within it, it caused several issues with the non-wrapped macro on the same page.

Gotchas

It should be noted that if you have a typo anywhere in your macro's template, it will either fail to render the macro, display an error message, or at the worse, cause the page where the user macro is utilized to fail to render completely.
As a happy accident, while I was exploring which macro I should create, I failed to include the closing parentheses for some CSS rules. This caused the macro to not render and Confluence didn't inform me of anything wrong with the macro.

Friday, April 14, 2017

Automating Content Extraction From Confluence Using Exporter Plugins and Bash

Intro

As a technical writer, one of the many tasks I must work with on a regular basis is pulling content from multiple sources and compiling them into a source file for publication. Early on, the process I used was a very hands on process of manually generating a compressed file from Confluence and other sources, manipulate the contents of the extracted content, bundle everything up, and publish the refined package.

This tutorial's goal is to get novice users familiar with one method of automating content extraction by using a simple Bash script file that pulls content from Confluence using an exporter scheme URL.

The process contains two components: an export scheme URL and a Bash script. I have written two tutorials on how to extract content using two of Scroll's exporter plugins. Refer to Part 2 and/or Part 3 in the Export Content from Confluence series for details on how to generate the export URL using either Scroll's EclipseHelp or HTML Exporter plugins. You will need to complete at least one of these tutorials in order to complete this tutorial as you will need the export scheme URL. The Bash script we will create handles the manual part of extracting content from Confluence.

This tutorial is written for Mac users. The Bash features used in this document has not been tested on Linux but you know your way around wget, this tutorial should work just fine.

Prerequisites

Confluence Command Line Interface

Did you know that Confluence has a CLI? Check out and install Confluence Command Line Interface (CLI) as we may need it to gain access to Confluence via the command line to check against extracted content but I'll leave that up to you to decide if you want to use it or not.

For installation, please review Confluence CLI Installation and Use.

For getting started, reference, examples, and much more info, please review Confluence CLI User's Guide.

wget

Another component to automating the export process is using a command line network transfer tool such as wget. There are a few options out there for handling CLI transfers (such as curl) but I've found wget to be rather flexible, handles redirects well, and stable for my documentation needs. If you have arguments for or against, I'd love to hear them in the comments.

Setting up a "docbot" account for export

Prior to automating the export process from Confluence, you will want to create a non-human account that only has view and export permissions. If you don't plan on sharing or automating content extraction from Confluence, you can use your personal account but I've seen many tech writers get bitten by using their personal accounts.

In this case, I named this new account "docbot". If you don't have the proper credentials to create Atlassian accounts, please contact your Confluence administrator and request the account be created. Otherwise, create a new user account:
  1. Navigate to the Confluence Admin page.
  2. Click on Users under Users & Security section.
  3. Click Add Users.
  4. Enter docbot in Username field.
  5. Enter docbot in Full Name field.
  6. If you have a group email address that is shared with the tech writers, I recommend using that for the Email field. Otherwise, enter your email address.
With the docbot account created, we need to provide it with the proper permissions.
  1. Navigate to your target space's Space Admin page and click on Permissions.
  2. Under the Individual Users section, click the Edit Permission button.
  3. Locate the docbot account and enable only the following permissions:
    • All > View
    • Space > Export
  4. Once those two permissions have been set, click Save all.
Your docbot account should now have the proper permissions to export content from Confluence.

Bash script

Once you have ran through the process of creating and saving an export scheme from either one of the aforementioned plugins, you will need to apply the REST URL in our Bash script.

The Bash script will contain up to four lines: up to three variables and one command. You may wish to add a few additional lines before and after the export process to set up directories like adding a few commands for setting up an export archive and post processing the downloaded file (like renaming the .jar file to a .zip file, unzipping, it and so on).

...
USER='docbot'
PASS='<docbot's password>'
URL='<exporter scheme URL>'

wget --content-disposition "$URL&os_username=$USER&os_password=$PASS"
...


Breakdown of this script

The first three lines are just variables we will pass into the wget command. The fourth line is the backbone of the whole operation. Lets example each component of this command:
  • wget - network transfer command
  • --content-disposition - flag that will force the download to preserve the file name. Note: This flag is will experimental though I've never hand any problems with it.
  • "$URL&os_username=$USER&os_password=$PASS" - string that gets passed into the wget command. When pulling content from Confluence using the exporter scheme URL, you need to specify the exporter URL, provide the user requesting it (which it get checked for proper credentials, and the password). If everything checks out properly on Confluence's side, your command will pull down a compressed file based on the settings in your exporter scheme.
As mentioned earlier, one could use curl to pull content from Confluence but I found that command to be unreliable at times on my Mac. Maybe it was may flag options or some other setting but wget has worked very well for me for years using this configuration.

Note: if your Bash script will be shared with other users or in a hosted environment, you may want to localize the USER and PASS variables in your Bash profile.

From here, you can add any steps to the Bash script to include post processing, executing node scripts to modify extracted content, or whatever else you need to include in this automated process. Or, if you need to use this process on a regular basis, you can simply set a cron or Jenkins job.

Happy automating!

Tuesday, March 14, 2017

Export Content from Confluence - Part 3

Intro

Welcome to the final tutorial of this three part series on how to extract HTML content from Confluence.

In the previous two tutorials, we explored how to export content using Confluence's native solution Export Content from Confluence - Part 1 and Export Content from Confluence - Part 2. In this tutorial, we will run through the steps to generate HTML content into a zip file from an user selected space in Confluence using Scroll's HTML Exporter plugin. The biggest difference in using this plugin versus EclipseHelp Exporter, is that plugin offers the option to generate a search index of your extract content and the wiki's space logo will be a part of the final presentation.

Setup


This document assumes that you have the Scroll HTML Exporter plugin installed and properly enabled and that your user account has the proper credentials (at least view and export permissions) for exporting and plugin usage. If it's not, contact your Confluence administrator and request it to be set up.

This process was tested on Confluence 5.6.4 and Scroll HTML Exporter version 3.5.0. Results may very with other versions.

Exporting using HTML Exporter


  1. Navigate to the top most page in the space you wish to export from Confluence.
  2. Go to Tools > Export HTML.
  3. Click Customize Settings in lower left corner. For this tutorial, we are going to customize the content will wish to pull from Confluence.
  4. In the General step, you will need to make some choices depending on the needs of how and what you want to export from Confluence:
    1. In the Create drop down, you can choose between one HTML file for each Confluence page or a single large HTML file. For this tutorial, we'll be using the former option to export our content to the HTML format.
    2. Since this may be our first time using this plugin, the Template drop down will just list one option, Scroll WebHelp Template. This tutorial will not cover how to create HTML templates.
    3. With the Export option, we'll select This page and its children so we can collect every page starting with the current page and all of it's children.
  5. In the Central Processing step, you will have several options to output a few macros with your content, exporting images with original resolution, converting labels to index terms, and merging single, first heading and page titles. For this step, only enable the Export images with original resolution. If your pages in Confluence used the other macros previously listed and you need your exported content to use them, go ahead and enable those as well. Otherwise, leave those options disabled. I have found that exporting the content in as raw form as possible works best when attempting some post processing in my documentation publication scripts.
  6. In the File Naming step, we can leave the settings to their default values. These settings works very well with the vast majority of export settings and the content coming from Confluence.
  7. In the Search Index step, depending on your export needs, you may want to leave this option disabled. For this tutorial, leave it disable. When enabled, the plugin will generate a full text search index and add it to the exported content. Note: if you decide to export your content into one large HTML file, this feature won't be supported.
  8. Click Start Export and wait a moment or two while Confluence churns on the request. An Export in Progress window will appear informing you of the content export status (pages processed versus total number of pages in the request).
  9. Once the request has finished, your browser will download a zip file with the name of the page you selected to start the process from followed by the version number of the page, a date stamp of when it was exported, and a time stamp (set by the server clock). For example, your zip file name may look like this: Home-v12-20170104_1435.zip. The Export Result window will present with the size and number of pages in the export and how long it took. This window will also allow you to save the export scheme you just created, collect the ReST URL, and manage export schemes. If you are going to use this export process repeatedly, you should follow these steps:
    1. Click the Save Export Scheme ... button and select as new from the drop down. 
    2. In the Save new Export Scheme window, decide if this scheme will be used only in the current space or in all spaces (globally). For this tutorial, select in this space.
    3. Provide a good descriptive name and description in their respective fields.
    4. Click the Save button.
  10. Optional: back in the Export Result window, you can collect the ReST URL if you wish to automate the process in the future. Click the REST URL button and copy the URL listed in the middle of the REST URL window. Save this URL for your automation script.
  11. Optional: back in the Export Result window, you can manage the export schemes in either the current space or in all spaces (globally). A new browser window will open up and present you with options to modify any export schemes you may already have. That is, if you have the proper user permissions to see or edit features in the Space Admin page of the current space.
With the completion of this tutorial, you should have an HTML copy of your selected content and an export scheme that can be used repeatedly to pull content from your desired space in Confluence into an HTML zip file without having to go through this entire process again.

For future exports, all you need to do now is go to Tools > Export to HTML and select the export scheme you created in this tutorial.

Tuesday, February 14, 2017

Export Content from Confluence - Part 2

This tutorial is part two in the three series of how to export content from Confluence. In the previous tutorial, we covered how to export content from Confluence using it's native solution Export Content from Confluence - Part 1.

In this post, we'll explore the process of how to export content from our favorite wiki using Scroll's EclipseHelp plugin into an HTML zip file.

Setup

This document assumes that you have the Scroll EclipseHelp plugin installed and properly enabled and that your user account has the proper credentials (at least view and export permissions) for exporting and plugin usage. If it's not, contact your Confluence administrator and request it to be set up.

This process was tested on Confluence 5.6.4 and Scroll EclipseHelp plugin version 3.5.0. Results may very with other versions.

Exporting using EclipseHelp
  1. Navigate to the top most page in the space you wish to export from Confluence.
  2. To start the process, go to menu for Tools > Export to EclipseHelp.
  3. If this is your first time using this plugin, you may see a few global templates to choose from. This tutorial opts to create an export using custom settings so you can see what is available via this plugin. In the bottom left corner, click on the Customize Settings option.
  4. From the General step, select the Default EclipseHelp Template from the Template option (selected by default).
  5. From the Export option, you can select what you need to export (the current page and it's children, only the current page, or the current page and any children with an user specified label). For this tutorial, we'll use the option to export the current page and all it's children.
  6. Click the Content Processing step next. From here, you have several options on what you want to export with your content like exporting the table of contents (toc), children, section, and numbered-headings macros, images with original resolution, and merging single, first heading and page title. To keep it simple, let's not include any of these options except for the images with the original resolution. By selecting this option, the process will download the original size of any and all images that are included on the selected wiki pages.
  7. Skip down to EclipseHelp Features. In this step, you can add any additional features you would like to include in your export. The features you choose to include in your export will be up to you but if the content you are pulling from Confluence is meant to be a stand-alone doc set, I would at least include the following:
    1. From the Index option, select the Create Index and Convert Labels to Index Terms. Including an index will generate additional documents called index.xml, toc.xml, and a few others associated with all your pages that you included in this export.
    2. From the Compatibility option, Export to doc.zip. Once the export has finished, everything will be nicely wrapped up in a zip file called doc.zip.
  8. In the File Naming step, you can define how the exported content should be handled. I normally leave this section alone as the default values work well for most common needs.
  9. With you export options selected, click the Start Export button to start the export process. Depending on the size of the space or page selection you made, Confluence will churn for a moment or two. You should see an indicator window pop up telling you where you are in the export process. Once Confluence has finished with the export process, you will see your browser download a doc.zip file.
  10. Upon the completion of the export, EclipseHelp will tell you the size of the content, how many pages you exported, how long the process took, and present you with a few options can you can use in the future to make this process go a little faster: Saving the export scheme, capture the ReST URL (for automation reasons), and managing the export schemes. If you know you will be exporting this space with these settings repeatedly, I recommend saving the export scheme you just created. Click the Save Export Scheme ... drop down and select as new.
  11. Decide if this export scheme will be used globally throughout your Confluence instance or localized to just this particular space.
  12. Provide a good name and description and click the Save button.
  13. If you wish to automate the process by using the ReST URL, you will need to save the export scheme first (see steps 9 through 12 to save the scheme).
  14. With the export scheme saved, you can now click the REST URL button to receive the URL you can use for automating this export process. 
With the completion of this tutorial, you should have an HTML copy of your selected content and an export scheme that can be used repeatedly to pull content from your desired space in Confluence into an HTML zip file without having to go through this entire process again.

For future exports, all you need to do now is go to Tools > Export to EclipseHelp and select the export scheme you created in this tutorial.

Tuesday, January 10, 2017

Export Content from Confluence - Part 1

Intro

This post is the first of a three part series (part 2) of tutorials in which we'll explore the basics of exporting content from Atlassian's Confluence. This series will cover three different methods for exporting content from Confluence to a HTML format. In the first tutorial, we will cover how to use Confluence's native solution for exporting content to HTML and the pros and cons of this method. In later tutorials, we will cover how to use Scroll's EclipseHelp and HTML Export plugins to accomplish the same result but without the pitfalls that the native solution brings to the table.

Confluence has the capability of exporting the contents of a space to many formats such as PDF, Word, XML, and HTML. In this series, we will only focus on exporting to the HTML format. The reason why we will be focusing on the HTML format is that is a flexiable format that allows technical writers to apply post operations to the content prior to posting the content to it's final presentation form.

By the end of this tutorial, you should have a zip file with all the content you wish to export from Confluence in the HTML format.
  1. Navigate to the top most page in the space you wish to export from Confluence.
  2. Go to Space Tools  > Content Tools. The location of this link will vary depending on the theme you are using.
  3. Click on the Export tab.
  4. If you wish to export in HTML, select HTML from the Export Formats option and click Next. You can also choose from XML and PDF but those options will not be covered in this post.
  5. If you wish to export everything in your target space, select Normal Export. However, if you wish to export a particular set of pages, select Custom Export.
  6. By selecting Custom Export, you will be presented with every page in the space that can be exported (by default). 
  7. Click the Deselect All option and check off with pages you wish to export. If you select a page that has children pages, those pages will automatically be selected as well.
  8. If you wish to include comments with your select pages, leave the Include comments option enabled.
  9. With the pages selected, scroll to the bottom of the page and click Export. Confluence will churn for a moment and present you with a download link. Click the download link to receive your zipped file of HTML content.
This method is kind enough to generate an index.html file that lists, and links, the content you just generated along with space details at the top of the page, and document generation date in the footer of the page. This zip file will also include any and all images, and style sheets used in the pages you selected. If you were hoping that it would include any JavaScript code from features found on your Confluence pages, I'm afraid you're out of luck. While some JavaScript files may be exported, I didn't see any evidence of scripts that recreate the behaviors found in the macros typically used within Confluence.

Drawbacks

There are two drawbacks to this export method: 1: It isn't automatable. 2: No options to save export settings. I'm all about automating any documentation task. But, as far as I know, this method cannot be automated (please correct me if I'm wrong). Unlike the other export methods available to Confluence (see Scroll's plugin solution), this method doesn't offer any options to save the export settings or which pages to export.

Notes

If you applied any properties to the images found on your pages that you selected, they may not appear the same as before. At the time of writing this post, properties like floats and borders did not translate. The CSS rules and classes are in the code that is included in the export but the effects of the CSS didn't work out of the box.

This tutorial was tested on Confluence 5.6.4. Mileage may very on other versions.


Source

Exporting Confluence Pages and Spaces to HTML

Wednesday, December 14, 2016

Using Node.js for Text Processing

Intro

As a tech writer who is responsible for writing and publishing documentation in various formats, I've found a need to combine my hobby of toying around with JavaScript and document publication. In particular, I'm tasked with pulling information from an Atlassian's Confluence site down into a static HTML file set. However, the method I use (Export EclipseHelp with a custom template) doesn't reliably generate clean or consistent HTML documents. While the original intent of this tutorial was to update content extracted from Confluence, it can work on any HTML file.

I figure there are better ways of doing what I'm about to demonstrate, but my needs are rather particular (as in, this script needs to function as part of a bigger puzzle I employ for publication). If you have suggestions to improve it, I'd love to hear it.

This document doesn't cover how to export HTML from Confluence. What will be covered is a script I came up with that will complete a find and replace function on all HTML files in a particular directory.

Node.js requirements

This little script needs only three modules to read, write, gather a file list, and use jQuery-like features:

var fs = require('fs');
var cheerio = require('cheerio');
var shell = require('shelljs');

Documents, meet array

Using a shell module, I gather all the HTML files in a particular directory:

var fileNames = shell.ls('documents/*.html');

String it up

Read each document as a string if the document has the extension of .html:

for (i in fileNames) {
  if (fileNames[i].indexOf(".html") > -1) {
    $ = cheerio.load(fs.readFileSync(fileNames[i]).toString());
    ...
  }
}


While it may seem a bit redundant to look through the array matching the HTML file type, the array returned in fileNames can end with an empty element in the array and cause our script to throw an error at the end.

Here we use the cheerio module to add jQuery-like features to our script so we can do things like select elements and modify them in a number of ways.

Process the string

Check if an element with the class of footer. If it exists, remove it.

if ($('div.footer').length > 0) {
  console.log("Removing footer from ../" + fileNames[i]);
  $('div.footer').remove();
} else {
  console.log(fileNames[i] + " has no div.footer element.");
}


At this step in the script, we can have the actively selected HTML document be processed in a multitude of ways (e.g updating elements in the header, injecting Bootstrap grid system, swapping image locations, adding date stamps, and so on).

Update and save

Update the string (document) and save it out.

var removed = $.html();
fs.writeFileSync(fileNames[i],removed);

Full code:

var fs = require('fs');
var cheerio = require('cheerio');
var shell = require('shelljs');
var fileNames = shell.ls('documents/*.html');

for (i in fileNames) {
  if (fileNames[i].indexOf(".html") > -1) {
    $ = cheerio.load(fs.readFileSync(fileNames[i]).toString());
    if ($('div.footer').length > 0) {
      console.log("Removing footer from ../" + fileNames[i]);
      $('div.footer').remove();
    } else {
     console.log(fileNames[i] + " has no div.footer element.");
    }

    var removed = $.html();
    fs.writeFileSync(fileNames[i],removed); // save out HTML file
  }
}