Skip to content

January 2015

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