User Macro to Search for Old Pages

Hi, I have been using JD's user macro: 

https://community.atlassian.com/t5/Confluence-questions/How-can-I-sort-Confluence-spaces-pages-by-date-modified/qaq-p/136793?posted=609162#U609162

to implement "wiki gardening" at my office. I have been trying to modify it so that, rather than "allPagesInSpace", it would only search the page on which I place it and all children (descendant) pages. We have some teams that share a space, and so would like to be able to create "targeted" lists of their "old pages" for them to review.

I can't seem to get the thing to work, though, always get "unable to render" or something.

Any tips?

Thx : )

4 answers

2 votes
Stephen Deutsch Community Champion Jul 13, 2017

Hi Brent,

If you make changes to the code, then any errors will show up in your Confluence log file (the error messages are usually pretty clear as to what you did wrong). I also have an additional user macro that will display the Confluence log in a page, so that makes debugging easier if you don't have access to the server itself.

If I were to rewrite the macro, I would probably hijack the "content by label" macro so I could use the CQL to search dependent pages earlier than a certain date ordered by last updated date. Are you interested?

2 votes

Here's something that I worked on today for displaying pages with CQL. It only really works for pages, so if you try to search for blogposts, comments, or attachments it displays strangely. You should be able to adapt it for your needs. You might use some CQL like this to get all pages in the space older than 6 months sorted with oldest pages first:

type = page and space=spaceKey() and created < now("-24w") order by lastModified asc

User macro:

## @param CQL:title=CQL Query|type=string|required=true|default=type = page and |desc=Enter CQL Query here, use pageId() to refer to current page ID and spaceKey() to refer to current space key.
#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 ( $pageManager = $containerContext.getComponent('pageManager') )
#set ( $contentEntityManager = $containerContext.getComponent('contentEntityManager') )
#set ( $long = $generalUtil.getSystemStartupTime() )
#set ( $cql = $paramCQL.replace("pageId()", $content.id.toString()).replace("spaceKey()", $space.key) )
#set ( $htmlString = $action.getHelper().renderConfluenceMacro("{contentbylabel:showLabels=false|showSpace=false|max=500|cql=$cql}") )
#set ( $tableListings = "" )
#set ( $displayMatch = $req.contextPath + '/display/' )
#set ( $pagesMatch = $req.contextPath + '/pages/' )
#set ( $results = 0)
#foreach ( $linkPart in $htmlString.split('href="') )
#if ( $linkPart.startsWith($displayMatch) || ($linkPart.startsWith($pagesMatch) && !$linkPart.contains('focusedCommentId=')) )
#set ( $results = $results + 1 )
#if ( $linkPart.startsWith($displayMatch) )
#foreach ( $link in $linkPart.split('"') )
#set ( $spaceAndPage = $link.replace($displayMatch, "" ) )
#foreach ( $part in $spaceAndPage.split("/") )
#set ( $spaceKey = $part )
#break
#end
#foreach ( $part in $spaceAndPage.split("/") )
#set ( $pageTitle = $generalUtil.urlDecode($part) )
#end
#set ( $page = $pageManager.getPage($spaceKey, $pageTitle) )
#break
#end
#end
#if ( $linkPart.startsWith($pagesMatch) )
#foreach ( $link in $linkPart.split('"') )
#foreach ( $section in $link.split('pageId=') )
#set ( $pageId = $section )
#end
#break
#end
#set ( $pageId = $long.parseLong($pageId) )
#set ( $page = $contentEntityManager.getById($pageId) )
#end
#set ( $tableListings = $tableListings + '<tr>' )
#set ( $tableListings = $tableListings + '<td><a href="' + $req.contextPath + $page.urlPath + '">' + $page.title + '</a></td>')
#set ( $tableListings = $tableListings + '<td>' + $page.creator.fullName + '</td>' )
#set ( $tableListings = $tableListings + '<td>' + $action.dateFormatter.formatDateTime($page.creationDate) + '</td>' )
#set ( $tableListings = $tableListings + '<td>' + $page.lastModifier.fullName + '</td>' )
#set ( $tableListings = $tableListings + '<td>' + $action.dateFormatter.formatDateTime($page.lastModificationDate) + '</td>' )
#set ( $tableListings = $tableListings + '</tr>' )
#set ( $page = "" )
#end
#end
#if ( $tableListings.contains("tr") )
<p> Found $results results: </p>
<table>
<tr>
<th>Page</th>
<th>Created</th>
<th>Created By</th>
<th>Last Modified</th>
<th>Last Modified By</th>
</tr>
$tableListings
</table>
#else
<p> No results found for CQL Query $cql </p>
#end

In case it was not clear, the original user macro works fine, it's just that whenever I make any ham-fisted changes to the code, it breaks, all on me.

Thank you, Stephen Deutsch! I reviewed your macro and then did some more researching and found another solution. I changed this line:

#set ( $allPagesInSpace = $pageManager.getPages($space, true) )

to this:

#set ( $allDescendants = $content.getDescendants() )

That now makes the user macro behave as I wish, it only searches the descendants of whatever page it is placed on. Hooray!

The next improvements I would like to make, but don't know how:

1) define a default sort for the results:

  1. created date, old>new
  2. modified date, old>new
  3. creating user, alphabetical
  4. modifying user, alphabetical

2) make #set include the parent page, i.e., the page on which the macro is placed, in the search, too.

Here is the updated user macro (updated lines are BOLDED):

## Macro title: Old Pages
## Macro has a body: Y or N (N)
## Body processing: Selected body processing option
## Output: Selected output option

## Macro to display pages that have not been updated for x days
## @param numdays:title=Number of Days|type=int|desc=Enter number of days to compare|required=true|multiple=false|default=7

#set ( $allDescendants = $content.getDescendants() )

## default value handler, set default to 7 days
#set ( $numdays = $paramnumdays )
#if(!$numdays)
#set ( $numdays = "7" )
#else
#set ( $numdays = $paramnumdays )
#end

## cast the number of days to an Integer
#set ( $Integer = 0 )
#set ( $intnumdays = $Integer.parseInt($numdays) )

## negative sign the number of days
#set ( $negativeDays = (-1 * $intnumdays) )

## get a calendar object so we can calculate the
## date we wish to compare with the page modification date
## e.g. $compareDate.add(6, -7) for pages modified over a week ago
#set ( $compareDate = $action.dateFormatter.getCalendar() )
$compareDate.setTime($content.getCurrentDate())
$compareDate.add(6, $negativeDays)

<p>Pages last updated before <strong>$action.dateFormatter.format($compareDate.getTime())</strong></p>

<table>
<tr>
<th>Page</th><th>Created</th><th>Created By</th><th>Last Updated</th><th>Last Updated By</th>
</tr>

## loop through all the pages in the current space
#foreach($page in $allDescendants)

## get a calendar object and for each page in space set it to last modification date
#set ($pagedate = $action.dateFormatter.getCalendar())
$pagedate.setTime($page.getLastModificationDate())

## only display pages last modified before the compare date
#if ($pagedate.before($compareDate))
<tr>
<td>#contentLink2($page true false)</td>
<td>$action.dateFormatter.format($page.getCreationDate())</td>
<td>#usernameLink($page.creatorName)</td>
<td>$action.dateFormatter.format($page.getLastModificationDate())</td>
<td>#usernameLink($page.lastModifierName)</td>
</tr>
#end
#end
## end looping through pages in the current space

</table>

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,821 views 27 11
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