This Confluence user macro calls the Jira REST API to fetch comments, then recursively parses comment body nodes using a Velocity #macro to extract plain text. The result is rendered in an AUI table with sorting enabled and supplemented by AUI elements such as avatars and lozenges for authors and timestamps.
## Parameters:
## issueIdOrKey (required) - Jira issue ID or key to fetch comments for
## order (optional) - ASC / DESC (defaults to created ascending)
## maxResults (optional) - number of comments to request (Jira default is 100)
#set($issueIdOrKey = $parameters["issueIdOrKey"])
#if(!$issueIdOrKey)
## Display a message if issueIdOrKey is not provided
<div role="note" aria-labelledby="no-issue-key-title" class="aui-message aui-message-info" style="width:100%; height: 100%;">
<p id="no-issue-key-title" class="title">
Note: Issue key/id not provided
</p>
<p>
Please set the <code>issueIdOrKey</code> parameter for this macro.
</p>
</div>
#else
#set($rawOrder = $parameters["order"])
#set($orderBy = "created")
#if($rawOrder)
#set($order = $rawOrder.toUpperCase())
#if($order == "ASC")
#set($orderBy = "+created")
#elseif($order == "DESC")
#set($orderBy = "-created")
#end
#end
#set($maxResultsParam = "")
#set($maxResultsInput = $parameters["maxResults"])
#if($maxResultsInput)
#set($maxResultsParam = "&maxResults=" + $maxResultsInput)
#end
## Fetch comments
#set($requestUrl = "/rest/api/3/issue/" + $issueIdOrKey + "/comment?orderBy=" + $orderBy + $maxResultsParam)
#set($response = $JiraManager.get($requestUrl).comments)
#set($parsePattern = "yyyy-MM-dd'T'HH:mm:ss.SSSZ")
## Build a table if there are comments
#if($response && $response.size() > 0)
<table class="aui aui-table-sortable" style="width:100%;">
<thead>
<tr>
<th>Author</th>
<th>Comment</th>
<th class="aui-table-column-unsortable">Created 🟢 / Updated 🟣</th>
</tr>
</thead>
<tbody>
#foreach($c in $response)
#set($auth = $c.updateAuthor)
#if(!$auth)
#set($auth = $c.author)
#end
#set($displayName = $auth.displayName)
#set($avatar = $auth.avatarUrls.get("48x48"))
#if(!$avatar)
#set($avatar = "https://icon-library.com/images/avatar-icon-images/avatar-icon-images-4.jpg")
#end
## Format dates
#set($createdFormatted = "")
#if($c.created)
#set($createdFormatted = $DateUtils.parseDate($c.created, $parsePattern))
#end
#set($updatedFormatted = "")
#if($c.updated)
#set($updatedFormatted = $DateUtils.parseDate($c.updated, $parsePattern))
#end
## Render ADF to HTML
#set($html = "")
#set($stack = $c.body.content)
#foreach($lvl in [1..20])
#set($next = [])
#foreach($node in $stack)
## Paragraph
#if($node.type == "paragraph")
#set($p = "")
#if($node.content)
#foreach($n in $node.content)
#if($n.text)
#set($p = $p + $n.text)
#end
#end
#end
#set($html = $html + "<p>" + $p + "</p>")
#end
## Bullet List
#if($node.type == "bulletList")
#set($html = $html + "<ul>")
#foreach($item in $node.content)
#set($li = "")
#foreach($pnode in $item.content)
#foreach($t in $pnode.content)
#if($t.text)
#set($li = $li + $t.text)
#end
#end
#end
#set($html = $html + "<li>" + $li + "</li>")
#end
#set($html = $html + "</ul>")
#end
## Ordered List
#if($node.type == "orderedList")
#set($html = $html + "<ol>")
#foreach($item in $node.content)
#set($li = "")
#foreach($pnode in $item.content)
#foreach($t in $pnode.content)
#if($t.text)
#set($li = $li + $t.text)
#end
#end
#end
#set($html = $html + "<li>" + $li + "</li>")
#end
#set($html = $html + "</ol>")
#end
## Fallback recursion
#if($node.content)
#foreach($c2 in $node.content)
#set($discard = $next.add($c2))
#end
#end
#end
#if(!$next || $next.size() == 0)
#break
#end
#set($stack = $next)
#end
#if($html == "")
#set($html = "(no text)")
#end
<tr>
<td style="vertical-align:middle;">
<div style="display:flex; align-items:center;">
<div>
<aui-avatar size="medium" type="user" src="$avatar" title="$displayName" alt="avatar-$displayName"></aui-avatar>
</div>
<div style="margin-left:10px; line-height:1.1;">
$!displayName
</div>
</div>
</td>
<td style="word-break:break-word; vertical-align: middle;">
$html
</td>
<td style="white-space:nowrap;">
#if($createdFormatted)
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-success">$createdFormatted</span><br>
#end
#if($updatedFormatted)
<span class="aui-lozenge aui-lozenge-subtle aui-lozenge-new">$updatedFormatted</span>
#end
</td>
</tr>
#end
</tbody>
</table>
#else
## Display a message if no comments found
<div role="note" aria-labelledby="no-comments-title" class="aui-message aui-message-info" style="width:100%; height:100%;">
<p id="no-comments-title" class="title">
No comments found
</p>
<p>
Please check the <code>issueIdOrKey</code> or try another issue.
</p>
</div>
#end
#endShow worklogs from Jira issues
Display the top comments from a selected Confluence page, or from the current page by default
List Jira issues (work items) to display essential information in a readable and well-organized format
Show H1 headings for child topics to enable easy navigation
Filter Confluence groups based on a specified filter value and display the results in different formats
Retrieve and display labels from a specified space
Find images within page attachments. Handy for reusing files and updating them all at once.
Show filtered issues and their relations
Show specific content to users in a particular Confluence group
Create dropdown menus in Confluence Cloud with custom option sets, multiselect support, and permission-based access controls