In this page

Customizing the notification emails

When you want to include custom content (like explanations) in the Better Content Archiving notification emails, or to remove some parts, or to translate the mails to your users' natural language, it is possible to customize the templates from which these emails are rendered.

The templates themselves are plain text documents, containing HTML markup augmented with Velocity directives (that implement if-then, iteration- and other type of logic).

Generally speaking, the templates are simple. HTML is easy to understand, plus the web is full of HTML tutorials, writing HTML will not be a problem. Velocity is also easy, and as it is the template language widely used in Confluence, you are probably already familiar with it. (If not, the Velocity User Guide is the best resource to learn more.)

Editing notification email templates

The web based email editor was introduced in Better Content Archiving 4.5.0. If you are using a prior version, read here.

Steps:

  1. Login to Confluence as administrator.
  2. Go to Administration → Archiving → Notification Emails (in the left-side bar under Archiving).
  3. You can edit the subject and the content of all notification email types via a simple web-based, syntax highlighting editor. (You can also reset the original templates easily.)

Velocity objects available for the notification emails

If you just want to add some custom instructions to email, or remove or rephrase some text, you can skip this part. If you want to make significant changes to the logic, numbers and lists, please read on.

The context (the set of objects available) was significantly changed in Better Content Archiving 6.0.0. If you are using a prior version, read here.

Data types

For performance and usability reasons, the email notification templates cannot access "large" amounts of data (10000 space, for example). Instead, they will receive size-limited views of the data as instances of the following collection types:

Limited collection types
Type name Description
LimitedList A size limited decorator for the Java List type.
Think about it as a list that maximizes the number of items it actually holds, but also knows the total number of items it would contain if it were non-limited. Example: when the list receives 135 items, 100 as max size, then it will return 100 items and 35 as remaining count.

It extends the java.util.List interface with:
  • $list.nonLimitedSize: the number of items that were added to the list even if those are not contained by this
  • $list.remainingCount: the number of items that this list actually contains
  • $list.hasRemaining(): if list.remainingCount is not zero
LimitedMap A size limited decorator for the Java Map type.
It has the semantics with map entries, as LimitedList has with the list items.

It extends the java.util.Map interface with:
  • $map.nonLimitedSize: the number of entries that were added to the map even if those are not contained by this
  • $map.remainingCount: the number of entries that this map actually contains
  • $map.hasRemaining(): if map.remainingCount is not zero
DTO types

The items in the above collections are lightweight DTO objects of the following types:

Type name Description
LightPage A lightweight replacement for a Confluence Page object.

Available fields:
  • id: numerical ID of the page
  • spaceKey: key of the space which contains this page
  • title: title of this page
  • creatorName: name of the Confluence user who created this page
  • lastModifierName: name of the Confluence user who last modified this page
  • creationDate: Date of page creation
LightSpace A lightweight replacement for a Confluence Space object.

Available fields:
  • key: space key
  • name: space name
  • creatorName: name of the Confluence user who created this space
LightPageArchiving Describes an archiving operation.

Available fields:
  • freshPage: the LightPage which represents the fresh page, or null if the archiving was not actually done (i.e. it is a "skip")
  • archivingAction: textual description of why and how this page was archived or skipped (e.g. "labeled with archive by john.doe")
  • archiverName: name of the Confluence user who is responsible (e.g. who added the archive label)
  • reason: the comment that was added to this page to explain the reason, or null for skipped pages (e.g. "This doc page is not relevant anymore.")
  • skipped: whether the fresh page is skipped (true) or actually archived (false)
ContentView Describes a page view event.

Available fields:
  • pageId: numerical ID of the page
  • viewerName: name of the Confluence user who last viewed the page
  • viewDate: Date of the last page view
ContentUpdate Describes a page update event.

Available fields:
  • pageId: numerical ID of the page
  • updaterName: name of the Confluence user who last updated the page
  • updateDate: Date of the last page update
  • type:
    • EXPLICITLY_EXPIRED: marked as expired (by a user adding a label)
    • EDITED: content of this page was modified
    • ATTACHMENT_ADDED: attachment was added to this page
    • CHILD_UPDATED: a descendant (typically a direct child) page was updated (and that change is propagated to this page)
  • updateAction: describes the last update for human reading (e.g. "edited by john.doe 366 days ago")
  • explicitlyExpired: true if type is EXPLICITLY_EXPIRED
Object instances

You can access the following objects in the Velocity context. These are built up from the aforementioned collection and DTO types, or from standard Java types:

Domain Object Description
$user User instance representing the user this email will be sent to.
(This also means that the template will be rendered separately for each addressee.)
$subject Subject of the notification email.
You can use the ${pages.size()} expression to refer to the length of the $pages collection.
$reason Identifies the reason why this notification is sent. It can also be interpreted as the "role" of the addressee.
Possible values (with obvious meaning):
  • AUTHOR
  • LAST_MODIFIER
  • SPACE_ADMIN
  • SPACE_CREATOR
  • SUPERVISOR
  • LABELER: addressee was the user who added the label (that triggered the event why the noficiation email was sent)
$spacesToPages Lists of pages grouped by their corresponding spaces. Contains only the pages relevant to the notification email.
Type: LimitedMap<LightSpace, LimitedList<LightPage>>
## traverse through the spaces
#foreach($space in $spacesToPages.keySet())
	## get the list of pages in the current space
	#set($pagesInSpace = $spacesToPages.get($space))

	## get the total number of pages in the current space
	#set($pageCount = $pagesInSpace.nonLimitedSize)

	... ## do whatever you want, e.g. traverse pages and render them
#end
$spacesToNotViewedPages For "Not-viewed pages" emails only.
Type: LimitedMap<LightSpace, LimitedMap<LightPage, ContentView>>
#set($lastView = $spacesToNotViewedPages.get($space).get($page))

$lastView.pageId ## page ID
$lastView.viewerName ## name of viewer
$lastView.viewDate ## date of view
$spacesToExpiredPages For "Expired pages" emails only.
Type: LimitedMap<LightSpace, LimitedMap<LightPage, ContentUpdate>>
#set($lastUpdate = $spacesToExpiredPages.get($space).get($page))

$lastUpdate.pageId ## page ID
$lastUpdate.updaterName ## name of updater
$lastUpdate.updateDate ## date of update
$lastUpdate.type ## type of update
$lastUpdate.updateAction ## human readable type of update
$pagesArchived For "Archived pages" emails only.
Type: LimitedMap<LightSpace, LimitedMap<LightPage, LightPageArchiving>>
#set($pageArchiving = $pagesArchived.get($space).get($page))

$pageArchiving.freshPage ## fresh page that was archived
$pageArchiving.archivingAction ## textual description
$pageArchiving.archiverName ## name of archiver
$pageArchiving.reason ## textual reason
$pagesSkipped For "Skipped pages" emails only.
Type: LimitedMap<LightSpace, LimitedMap<LightPage, LightPageArchiving>>
#set($pageArchiving = $pagesArchived.get($space).get($page))

$pageArchiving.archivingAction ## textual description
$pageArchiving.archiverName ## name of archiver
$pageStatsTool Velocity tool to work with page statistics.
It has various use cases depending on the email type:
## in "Not-viewed pages" emails
#set($config = $pageStatsTool.getArchivingConfiguration($space))
$config.pageViewAlertAge ## page view tracking alert age (in days)

#set($lastViewedPageAgeInDays = $pageStatsTool.getLastViewedPageAgeInDays($space, $spacesToNotViewedPages))
$lastViewedPageAgeInDays ## days passed since the last page view

## in "Expired pages" emails
#set($config = $pageStatsTool.getArchivingConfiguration($space))
$config.pageExpirationTrackingByAgeEnabled ## whether page expiration tracking by age is enabled
$config.pageExpirationAlertAge ## page expiration tracking alert age (in days)

#set($lastUpdatedPageAgeInDays = $pageStatsTool.getLastUpdatedPageAgeInDays($space, $spacesToExpiredPages))
$lastUpdatedPageAgeInDays ## days passed since the last page update

## in "Expired pages" emails
## get the number of days until the oldest page gets archived,
## or -1 if archiving is not enabled in that space,
## or -2 if there are only explicitly expired pages in the space:
#set($firstPageAutoArchivingInDays = $pageStatsTool.getFirstPageAutoArchivingInDays($space, $spacesToExpiredPages))

#if($firstPageAutoArchivingInDays == -1)
	Auto-archiving is disabled:
	these pages will not be auto-archived.
#elseif($firstPageAutoArchivingInDays == -2)
	All these pages are expired due to a label added:
	these pages will not be auto-archived.
#else
	## get the days until the oldest page gets archived
	## (i.e. the days until the first upcoming archival)
	$firstPageAutoArchivingInDays
#end

## in all email types
## get the archiving configuration for a space
## (you will unlikely need this object,
## but please contact Midori for details in case you do)
#set($config = $pageStatsTool.getArchivingConfiguration($space))
Data limits

As explained above, notification emails receive size-limited views on the data for performance reasons. If you are happy with the default limits, then you don't need to understand the details. Otherwise, please see the Performance Tuning page to learn more about how it works and how it can be configured.

Questions?

Ask us any time.