User Macro to Search for Old Pages
Asked by Brent Willems:

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 : )


Answers:
author: Stephen Deutsch
body: 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
```

comments: author: Raj_253
body: [@Stephen Deutsch](/t5/user/viewprofilepage/user-id/781490)

When we using above code, it fetching the information and dates correctly but table fromat showing in HTML.

**Example**

*** ** * ** ***

Found 1 Results :

\<tr\>\<td\>\<a href="/display/ABCD/john"\>john\</a\>\</td\>\<tom\</td\>\<td\>Jun 05,2022 13:13\</td\>\<td\>Rocky\</td\>\<td\>Jun 30,2022 10:40\</td\>\<tr\>

**Page \| Created By \| Created Date \| Last Modified By \| Last Modified Date \|**

*** ** * ** ***

names and dates in above results are fine but while it generates in table it shows the HTML code.

any suggestions.

Thanks.


author: Stephen Deutsch
body: 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?


author: Brent Willems
body: 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>
```


author: Brent Willems
body: 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.