Category Archives: confluence

Confluence Nested Macro Highlighter

Have you ever clicked edit on a Confluence page only to be presented with a screen that more or less looks like a jail cell with nested macro upon nested macro in a maze of indistinguishable grey borders? Cold sweats ensue as you realize you need to pick the correct spot to add your new content and if you pick the wrong spot the fate of the whole company … possibly the world could be placed in peril. So you fall back to your training. You place your finger on your screen making sure to keep it steady Then you slowly scroll the page following the line down the page till you hit the bottom of the macro. Making sure not to move that finger off the screen you move the mouser pointer just below bottom border and click. Cursor placed. You sigh with relief. Finally, you pull out a wet napkin and clean the finger print smudge off your screen. You’ve just prevented another catastrophe and the world will continue to turn for another day.

Seriously! You’ve never experienced that? Huh!? Ok, well maybe I’m the only person that happens to. However, I can bet you’ve still seen a Confluence page with LOTS of macros and gotten confused as to which was which.

That is the inspiration for this browser bookmarklet. Simply drag the below bookmarklet link to your browser’s bookmarks bar. When you are in edit mode you can click the bookmarklet to change the borders of your macros from this …

to this …

Each macro now has a distinctive color making it easy to see which border goes with which macro. Problem solved! Unfortunately, I could not change the color of the macro header. It’s actually a dynamically generated image that is LONG with the grey color hard-coded into it. Also, I tried hard to make sure that each color is distinct from the colors next to it so that it is easy to see which is which. I even thought about color-deficient people with the color choices … as I myself am red/green color-deficient. 🙂 Hope it is helpful. Let me know if you use it and it has helped you.

Bookmarklet:
Nested Macro Highlighter <- drag this to your bookmarks toolbar

If you would like to change out the colors or add more nesting levels the below JavaScript is the code for the bookmarklet. Just change/add values to the colors array to fit your needs. Once you’ve done that you can use a site like Bookmarklet Maker to turn it into your own personalized bookmarklet.

// Add/Change the hex color values to add more levels of nesting or choose different colors.
var colors = ['#800000','#F58231','#3CB44B','#42D4F4','#000075','#FFE119','#808000','#469990','#911EB4','#FFD8B1','#E6BEFF','#9A6324','#FABEBE','#000000'];

// Don't edit past this line
var styles = '';
for (var i = 0; i < colors.length; i++) {
	styles += '.wiki-content table.wysiwyg-macro table.wysiwyg-macro';
	for (var x = 0; x < i; x++) {
		styles += ' table.wysiwyg-macro';
	}
	styles += ' {\n';
	styles += '    background-color: ' + colors[i] + ';\n';
	styles += '}\n';
}
AJS.$('iframe').contents().find('head').append($('<style type="text/css">' + styles + '</style>'));

FUME – A Better Confluence User Macro Editor

One of the best and most overlooked aspects of Confluence, both by Atlassian and Confluence administrators, is user macros. There are so many useful scenarios for user macros. Here are some:

  • Templated snippets
  • Overriding built in macros example with task list report
  • Quickly creating your own macros
  • Inserting arbitrary html/css/javascript into a page without having to enabled the html macro

However, there are some big usability issues with the user macro editor. First it’s super easy to accidentally delete one. The delete link is right next to the edit link and seriously, there is no confirmation on the delete link. It’s just gone. Ack!

Second, the link to create new user macros is at the bottom of the page. If you have more that what can fit on a screen you have to scroll down to get to the link to create a new one … this just gets worse over time as you create more.

Third, the template box in the editor is just a plain old text area … no line numbers, not syntax highlighting, it’s not even a mono-spaced font! Grr!

Fourth, the cancel button doesn’t ask you to confirm canceling the edit if you have made changes to the user macro and since it sits right next to the save button it’s easy to miss. Hope you can recreate your work quickly.

Finally, every time you save it kicks you back to the list page. So, if you want to make some changes and try it out on a page you have to click back into the editor every time you save and whoops you accidentally just clicked delete instead of edit! There goes all that work.

So, without further ado … FUME. Fantastic user macro editor. The fantastic part is really just because I needed a word that ended in “ume” and that was the only word I could think of. Really it’s not all that fantastic … maybe just great, but gume isn’t even a word. Then I thought “How about  great looking user macro editor”, but that would be glume and … well … yeah, that kinda defeats the purpose. So, FUME it is. All in all I think it is a much better editing experience than the default setup. Here are some of the features:

  • Copy that “Create a User Macro” link to the top of the list page … no more scrolling
  • Delete confirmation on the list page
  • Template box changed to a source code editor with (Ace editor):
    • monospaced font
    • line numbers
    • syntax highlighting
    • find and replace
    • code folding
    • column select
  • Confirmation on cancelling edits of the user macro if the template has been changed
  • Asynchronous user macro saves
  • It will do your dishes and laundry … ok, not quite yet

Update 4/9/2018:

Ignore the “How to Setup” section below. I’ll leave it there, however, for the sake of continuity. I decided to package this up as an add-on in the Atlassian Marketplace. I named it Enhanced User Macro Editor (EUME … pronounced you-me … it’s a stretch I know). It seemed a bit more humble of a name and is more descriptive of what it is. I hope it is as useful for you as it has been for me. Marketplace link below.

Enhanced User Macro Editor

How to Setup

  • Download these CSS and Javascript files. (right click the links and choose “Save link as”)
  • Place them on a web server where they will be web accessible to your user macro editors.
  • Add this to the end of Confluence Admin -> Custom HTML -> At end of the BODY
<!--
*****************************************
* Fantastic User Macro Editor           * *****************************************
-->
<link rel="stylesheet" type="text/css" href="http(s)://{your server}/path/to/fume.css">
<script src="http(s)://{your server}/path/to/fume.js" type="text/javascript"></script>
  • Enjoy editing your user macros.  🙂

Screenshots

User Macro List

User Macro List Page

User Macro Template Editor

User Macro Template Editor

Confluence Macro Usage Report

When it comes to upgrading Confluence it is handy to know which macros are being used throughout the site so that you can test the upgrade and check that the used macros still work correctly. The below user macro will generate a Confluence macro usage report that lists all of the macros and user macros that are used in your Confluence installation along with a link for each macro so that you can spot check the pages that use that macro … or you could check all the pages if you are that thorough. This requires at least Confluence 5.5 as the MacroBrowserManager.getMacroSummaries() method was added in 5.5.

Report Example:

Macro Usage Report

Macro Body Processing:
No macro body

Template:

## Developed by: Davin Studer
## Date created: 12/17/2014
## @param Space:title=Space|type=spacekey|desc=This will restrict the macro usage report to a specific space.

#set($containerManagerClass = $content.class.forName('com.atlassian.spring.container.ContainerManager'))
#set($getInstanceMethod = $containerManagerClass.getDeclaredMethod('getInstance',null))
#set($containerManager = $getInstanceMethod.invoke(null,null))
#set($containerContext = $containerManager.containerContext)
#set($macroBrowserManager = $containerContext.getComponent('macroBrowserManager'))
#set($allMacros = $macroBrowserManager.getMacroSummaries())

#########################################################################################
## This is used for getting around velocity issues when writing jQuery.                ##
#########################################################################################
#set( $d = '$' )

#########################################################################################
## Populate the macro information into a string that we will use as a JS object array. ##
#########################################################################################
#set ($i = 0)
#set($macroObjects = "")
#foreach($macro in $allMacros)
  #if($i != 0)
    #set($macroObjects = $macroObjects + ",")
  #end
  #set($macroObjects = $macroObjects + "{title:'" + $macro.getTitle().getKey().replace("'","\'") + "', name:'" + $macro.getMacroName().replace("'","\'") + "'}")
#set ($i = $i + 1)
#end

#########################################################################################
## Decide which space to search.                                                       ##
#########################################################################################
#if ($!paramSpace && $!paramSpace != "")
    #set ($searchSpace = $!paramSpace)
#else
    #set ($searchSpace = "conf_all")
#end

<script type="text/javascript">
var queue = {
    macros: [$macroObjects],
    current: 0,
    start: function () {
        this.macros.sort(compare);
        this.current = 0;
        this.next();
    },
    next: function () {
    	if (this.current >= this.macros.length) {
            AJS.$('#queryStatus span').removeClass('aui-lozenge-current');
            AJS.$('#queryStatus span').addClass('aui-lozenge-success');
            AJS.$('#queryStatus span').text('Report Complete');
            AJS.tablessortable.setTableSortable(AJS.$('#macroUsageReport table'));
            return null;
		}
		else {
			this.current += 1;
			lookupMacro(this.macros[this.current - 1]);
		}
    }
};

function lookupMacro(macro) {
    var html = '';
    var searchURL = '';
    
    AJS.$('#queryStatus span').text('Getting counts for macro: ' + macro.title);
    
    searchURL = AJS.params.baseUrl + '/rest/searchv3/1.0/search?where=${searchSpace}&spaceSearch=true&queryString=macroName:' + encodeURIComponent(macro.name);
    AJS.${d}.ajax(
        {
            type: 'GET',
            url: searchURL,
            dataType: "json",
            timeout:60000,
            success: function(data) {
                if(data.total > 0) {
                    var spaces = '';
                    for(var i = 0; i < data.results.length; i++)
                    {
                        if(spaces.indexOf(data.results[i].searchResultContainer.name) == -1)
                        {
                            spaces = spaces == '' ? '' : spaces + ', ';
                            spaces = spaces + data.results[i].searchResultContainer.name;
                        }
                    }
                    html = '<tr>';
                    html += '   <td class="confluenceTd">' + macro.title + '</td>';
                    html += '   <td class="confluenceTd">' + macro.name + '</td>';
                    html += '   <td class="confluenceTd">' + data.total + '</td>';
                    html += '   <td class="confluenceTd">' + spaces + '</td>';
                    html += '   <td class="confluenceTd"><a href="' + AJS.params.baseUrl + '/dosearchsite.action?where=conf_all&spaceSearch=true&queryString=macroName:' + encodeURIComponent(macro.name) + '" target="_blank">macro usage</a></td>';
                    html += '</tr>';
                }
                AJS.$('#macroUsageReport table tbody').append(html);
                
                queue.next();
            },
            error: function (x, y, z) {
                AJS.$('#queryStatus span').removeClass('aui-lozenge-current');
                AJS.$('#queryStatus span').addClass('aui-lozenge-error');
                AJS.$('#queryStatus span').text('Error getting counts for macro:' + macro.title);
            }
        }
    );
}

function compare(a, b) {
    if (a.title < b.title) {
        return -1;
    }
    if (a.title > b.title) {
        return 1;
    }
    return 0;
}

AJS.toInit(function(){
    queue.start();    
});
</script>
<div id="queryStatus">
    <span class="status-macro aui-lozenge aui-lozenge-current"></span>
</div>
<div id="macroUsageReport">
    <table class="confluenceTable">
        <thead>
            <tr>
                <th>Macro Title</th>
                <th>Macro Name</th>
                <th>Times Used</th>
                <th>Spaces Used In</th>
                <th>Pages Used On</th>
            </tr>
        </thead>
        <tbody>
        </tbody>
    </table>
</div>

Referenced on answers.atlassian.com:
https://answers.atlassian.com/questions/29262695/answers/29263237
https://answers.atlassian.com/questions/155280/answers/11465880
https://answers.atlassian.com/questions/13411280/answers/13411414
https://answers.atlassian.com/questions/11463431/answers/11464460
https://answers.atlassian.com/questions/11460827/answers/11464595
https://answers.atlassian.com/questions/11465513/answers/11465876
https://answers.atlassian.com/questions/11967411/answers/11967421