Scan Confluence instance for macro usage...

Is there a way to look through all the content on our Confluence instance (4.2.4) for the presence of a certain set of macros?

We have a couple plug-ins licensed that we don't think are being used, and don't want to renew. Naturally, we don't want anything breaking when we reach the end of their maintenance period.

Thanks.

3 answers

1 accepted

Hi Dave,

There used to be a plugin called the Macro Usage Stats plugin, but it isn't compatible with anything above Confluence v3.5.x and therefore the only option at the moment is to run SQL queries directly on the database. However, the following query should work for you:

Select CONTENT.CONTENTID, Title, SpaceID, CREATOR, LASTMODDATE
from CONTENT, BODYCONTENT
WHERE
PREVVER IS NULL
AND (CONTENTTYPE = 'PAGE' OR CONTENTTYPE = 'BLOGPOST')
AND CONTENT.CONTENTID = BODYCONTENT.CONTENTID
AND CONTENT_STATUS = 'current'
AND (BODY like '%{macro%')

All the best,
John

Hi,

Does this run a report on all macros or individual macros? I'd like to run a report on all of them since searching for each macro individually would take a very long time and some might be missed.

Thanks!

7 votes
Davin Studer Community Champion Dec 18, 2014

Here is a user macro that I have created for this. Just put it on a page and it should give you a report of all of the macros and user macros that are being used in your instance. This requires at least Confluence 5.5 as the MacroBrowserManager.getMacroSummaries() method was added in 5.5.

## 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>

When I try the user macro you have created for this, I get this error: "ERROR GETTING COUNTS FOR MACRO:ACTIVITY STREAM". Do you have any idea why? I have Confluence version 5.6

Davin Studer Community Champion Mar 13, 2015

What's your base url for confluence? Is it like this htttp://mywikiserver/ or like this htttp://mywikiserver/confluence. Or maybe something else?

Davin Studer Community Champion Mar 13, 2015

One thing that could be an issue. I just updated line 60 to add "AJS.params.baseUrl + " to the searchURL. If your base url is not at the root of the server, then the script wouldn't work correctly.

That helped :-) Now it displays the correct table with a list of the macro use. But when I click on one of the "macro usage"-links I land on an error page with this message "The requested URL /dosearchsite.action was not found on this server." Why is that? And thank you very much for your help!

the base url for confluence looks a lot like this http://mywikiserver/confluence :-)

Arh I added "confluence" before /dosearchsite.action? and that fixed it (big grin)(big grin)

Davin Studer Community Champion Mar 17, 2015

I've updated the code above to fix the link issue. I also added a column called "Spaces Used In".

Devin, I m using your created macro but I am getting null results. I had replaced "baseUrl" with mine "http://localhost:8094/";. could you please help me out in this?

Is there any other Changes we need to do ...?

I using confluence version 5.7.1

Davin Studer Community Champion Jul 06, 2015

@Pratik Raj You should not need to do anything with the baseURL now. I edited the code above to fix the baseURL issue. The above code should work without any changes. You should be able to just put it into a user macro and then put the user macro on a page and have it render out.

David, I didn't find a change in the code above.Could you please check it.

also I tried without any change with base Url , I get this error: "ERROR GETTING COUNTS FOR MACRO:ACTIVITY STREAM"

Davin Studer Community Champion Jul 07, 2015

The change was made a while ago. It should just be plug and play. I have not tried it on 5.7.1 yet. I'll give it a go and see what happens.

Davin Studer Community Champion Jul 07, 2015

I think the issue might have to do with the AJAX call timing out. I am seeing it happen in our instance too. I have now edited the macro above to extend the AJAX timeout.

Davin Studer Community Champion Jul 07, 2015

I have also added the ability to limit the search to a particular space.

Thanks a lot @Davin Studer , its completely working now....I really appreciate your so concern, attitude and approach , its means a lot for me .

How can I use this script? 

Should do I put it on confluence page and uncomment the commented line with #?

It is shown as text content. sad

 

Davin Studer Community Champion Jul 14, 2016

You need to create a user macro of it. You can create user macros in Confluence Admin. After creating the user macro you will have that macro in the macro browsers when you are in edit mode. Once that is done just put the macro on a page and save.

Awesome macro! Thanks for sharing this smile

Excellent macro, thanks a lot ! Works on Confluence v5.9.12

EDIT: Also, by using getMacroMetadata() instead of getMacroSummaries() in the initialization, we can get more information (for instance, I wanted to know from which plugin each macro are coming)

Hallo Dave

You can use the "macroName:" keyword in the standard Confluence search box.

For example, let’s say that you want to search for all “include” macros. Type this into the Confluence search box:

macroName: include*

There's more detail in this post:

http://ffeathers.wordpress.com/2011/11/04/how-to-search-confluence-for-usage-of-a-macro/

I hope this helps!

Cheers, Sarah

Tha't exactly the type of thing I was looking for! Thanks, Sarah!

Suggest an answer

Log in or Sign up to answer
Atlassian Community Anniversary

Happy Anniversary, Atlassian Community!

This community is celebrating its one-year anniversary and Atlassian co-founder Mike Cannon-Brookes has all the feels.

Read more
Community showcase
Kesha Thillainayagam
Posted Apr 13, 2018 in Confluence

We want to hear how your non-technical teams are using Confluence!

Hi Community! Kesha (kay-sha) from the Confluence marketing team here! Can you share stories with us on how your non-technical (think Marketing, Sales, HR, legal, etc.) teams are using Confluen...

2,804 views 26 10
Join discussion

Atlassian User Groups

Connect with like-minded Atlassian users at free events near you!

Find a group

Connect with like-minded Atlassian users at free events near you!

Find my local user group

Unfortunately there are no AUG chapters near you at the moment.

Start an AUG

You're one step closer to meeting fellow Atlassian users at your local meet up. Learn more about AUGs

Groups near you