XML notification templates

ELMA supports notification templates in the XML format. In ELMA, users can subscribe to objects. To learn how to create a custom object monitoring, read this article. This article describes creating a subscription to an object; if an object had been changed, subscribed users would receive a respective notification. System notification templates are stored in {ELMA Directory}\UserConfig\Notifications\. Each notification template can be adjusted to the user/customer needs.

Notification template example:

<?xml version="1.0" encoding="utf-8"?>
<Notifications description="Object notifications" version="3.7.3.13420">
   
  <Default>
    <Filter>
      <Event>IEntityActionHandler.ActionExecuted</Event>
      <Object>ObjectIconModule.Models.MyObject</Object>
    </Filter>
    <RecipientSet>
      <User>{$WatchList}</User>
    </RecipientSet>
    <URL>
      /ObjectIconModule/Home/ViewItem/{$New.Id}
    </URL>
    <ObjectId>{$New.Id}</ObjectId>
  </Default>
   
  <Notification description="Object renamed">
    <Filter>
      <Action>Rename</Action>
    </Filter>
    <Subject>
      ({SR(’Object renamed’)}) {$New.Name}
    </Subject>
    <ShortMessage>
      {SR(’{$New.ChangeAuthor} renamed the object from "{$Old.Name}" to "{$New.Name}"’)}
    </ShortMessage>
    <FullMessage>
      {TableStart()}
      {PropertyRowWithChanges({$Old.Name};{$New.Name})}
      {PropertyRow({$New.Name})}
      {PropertyRow({$New.ChangeAuthor})}
      {TableEnd()}
    </FullMessage>
  </Notification>
 
</Notifications>
 

To add your template to the system notification templates folder ({Configuration folder}\Notifications\) and make it work, do the following:

  1. Implement an empty class, inherited from NotificationTemplateEmbeddedDataSource. Place this class in the same folder, where you XML notification template is;
  2. Change the properties of the notification template file.

Example of implementation of an empty class, inherited from NotificationTemplateEmbeddedDataSource:

class MyObjectNotificationsDataSource : NotificationTemplateEmbeddedDataSource
{
}
 

Also, change the properties of your notification template file. Set Build Action to Embedded Resource:

Fig. 1. Changing the properties of the notification template file

To make the template display "old" values in notifications (e.g. {$Old.Name}), implement the following class:

[Component]
internal class MyObjectRenameActionsEventAggregator : BaseEntityUpdateEventAggregator
{
    protected override IEnumerable<Guid> ProcessedActions
    {
        get { yield return MyObjectActions.RenameGuid; }
    }
}
 

Consider an XML notification template from the article with the object subscription. There are two required tags in a notification template: Notifications and Notification.

<Notifications description="Object notifications" version="3.7.0.41532"></Notifications> - the main notification tags, that must contain all the other necessary tags.

<Notification Name="AddComment"></Notification> - tag with the notification. The same tags, as in the <Default> tag can be inside.

Example:

<Notification description="Object renamed">
  <Filter>
    <Action>Rename</Action> <!-- Action name -->
    <Object>ObjectIconModule.Models.MyObject</Object> <!-- Object to apply the action to -->
  </Filter>
  <Subject>
    ({SR(’Object renamed’)}) {$New.Name} <!-- Message subject -->
  </Subject>
  <ShortMessage>
    {SR({$New.ChangeAuthor} renamed the object from "{$Old.Name}" to "{$New.Name}")} <!-- Short message that contains the object author, old name and new name -->
  </ShortMessage>
  <FullMessage>
    {TableStart()}<!-- Table start -->
    {PropertyRow({$New.Name})} <!-- New object name -->
    {PropertyRow({$New.ChangeAuthor})} <!-- Changed by -->
    {TableEnd()}<!-- Table end -->
  </FullMessage>
</Notification>
 

There can be any number of <Notification> tags. Names may match, as well as <Action> actions and objects. It enables, for example, using the same action name in different objects or different actions in one object. Therefore you should pay attention when creating a notification. Names in Notification are also used in child notification templates to add to the parent notification.

If you plan to use your notification template as a parent one, you should assign Names in the Notification tag. Then in child templates, in the Notifications tag use the Default tag, in which specify the parent template, e.g. <Notifications Default="Projects.ProjectTaskBase/">; it means that the default template will be selected from the file Projects.ProjectTaskBase.xml. To use existing Notification, in your child template create a Notification with the Parent parameter, in which specify the parent Notification, e.g. <Notification Name="CreateMilestone" Parent="Projects.ProjectTaskBase/Create"> - in this case, the notification with the name Create from the template Projects.ProjectTaskBase.xml will be used.

Inside the Notifications tag you can use the optional tag <Partial Name="DefaultFullMessage"> </Partial>, which is a partial representation of the text, which later can be inserted to the required places, e.g. to <ShortMessage>/<FullMessage>. The Name parameter should be used in the method {Partial(’Partial name’)}. You can see an example of using the Partial method below in the <ShortMessage>/<FullMessage> tags. <Partial> tag example in CRM.ContractorLegal.xml:

<Partial Name="DefaultContractorFullMessage">
{PropertyRow({$New.CreationAuthor})} <!-- Author -->
{PropertyRow({$New.Name})} <!-- Contractor name -->
{PropertyRow({$New.Phone})} <!-- Phone -->
{PropertyRow({$New.Email})} <!-- Email -->
{PropertyRow({$New.Site})} <!-- Website -->
{PropertyFullRow({$Comment.Text};SR(’Comment’))} <!--Comment text-->
{PropertyFullRow({$Attachments})} <!-- Attachments -->
{ExtensionZone(’EleWise.ELMA.CRM.Models.ContractorLegal-DefaultFullMessage-AfterAttachments’)}
</Partial>
<Partial Name="DefaultContractorShortMessage">
{PropertyFullRow({$Comment.Text};’’)} <!-- Comment text -->
</Partial>
 

 <Default></Default> - Default values are inside this tag.

 1. Message sending channels:

<Channels>EMail</Channels>
 

If you add this tag and specify a message channel in it, then the notification will be sent only via the specified channel. If you do not specify this tag, then by default the notification will be sent via all the existing message channels, such as Email or via the channel you created. To learn more about creating a new message channel, read this article.

2. Object URL:

<URL>/ObjectIconModule/Home/ViewItem/{$New.Id}</URL>
 

3. Message subject:

<Subject>({SR(’Object renamed’)}) {$New.Name}</Subject>
 

4. Filter:

<Filter>
  <Event>IEntityActionHandler.ActionExecuted</Event>
  <Object>EleWise.ELMA.Projects.Models.ProjectTask</Object>
  <Condition>
    (IsProjectTemplate({$New}) = false) and ({$New.Project.Status} = ’Active’)
  </Condition>
</Filter>
In this case, upon the execution of any Event called via IEntityActionHandler a notification on the object ProjectTask will be sent, if the Condition if fulfilled (a filter will be triggered if the project status is Active and the project is not a template). The method IsProjectTemplate itself must be implemented in the class that implements the interface ITemplateGeneratorFunctionsContainer. For more information, read this article. IEntityActionHandler.ActionExecuted is a handler of an action with an entity after the action has been executed. Inside the filter in Notification, specify the name of the action with the object <Action>Rename</Action>, which is created on the Actions tab of the object page. Its logic is implemented in the manager of your object. For more information, read this article.

5. List of users who will receive the notification <RecipientSet><User> {$WatchList} </User></RecipientSet> (In this case, only the users subscribed ({$WatchList}) to the object will receive the notification, but you can pass other users as well, such as responsible users, executors, etc.). The user list has several additional parameters:

  • Clear, responsible for clearing the list of notification recipients. Using this parameter may be necessary if your notification template is a child template, therefore to prevent adding the users from the parent template you can use this parameter.
  • SendToAuthorOnly, responsible for sending a notification only to the author.
    Example:
<RecipientSet Clear="true">
  <User>{$New.Executor}</User> <!-- Executor -->
  <User>{$New.InformTo}</User> <!-- Informed -->
  <User>{$New.CreationAuthor}</User> <!-- Author -->
  <User>{$WatchList}</User> <!-- Subscribed users -->
</RecipientSet>

For the recipient you can define a condition using a condition parameter, for example,
<User condition="{$New.CreationAuthor}&lt;&gt;{$New.ControlUser}">{$New.CreationAuthor}</User>, which means that if the author is not controller, then the author will be added to the notification recipients.

The recipient list may contain various data. Data types used in ELMA:

Data type name

Description

Example

User

Notification for a user

<User>{$New.Executor}</User>
<!-- Executor User-->

UserId

Notification for a user by ID

<UserId>3</UserId>

ParticipantRecipients

Notification for a calendar event participants. if the user had confirmed participation in the event.

<ParticipantRecipients>
{$New}
</ParticipantRecipients>
<!-- Where {$New} is a CalendarEvent -->

RelationshipParticipantRecipients

Notification for a relationship participants.

<RelationshipParticipantRecipients>
{$New}
</RelationshipParticipantRecipients>
<!-- Where {$New} is a Relationship -->

InformToRecipients

Notification for calendar event participants. If the user is informed.

<InformToRecipients>{$New}</InformToRecipients>
<!-- Where {$New} is a CalendarEvent -->

ConfirmParticipationRecipient

List of users who must confirm participation in a calendar event. If a user is an event participant but has not yet confirmed/rejected participation.

<ConfirmParticipationRecipient>
{$New}
</ConfirmParticipationRecipient>
<!-- Where {$New} is a CalendarEvent -->

RelationshipInformToRecipients

Notification for a relationship participants. If the user is informed.

<RelationshipInformToRecipients>
{$New}
</RelationshipInformToRecipients>
<!-- Where {$New} is a Relationship -->

ProjectPlanApprovalTaskExpiredNotificationRecipients

Handler of notification recipients for project plan approval tasks, which are overdue.

<ProjectPlanApprovalTaskExpiredNotificationRecipients>
{$New}
</ProjectPlanApprovalTaskExpiredNotificationRecipients>
<!-- Where {$New} is a ProjectPlanApprovalTask -->

ProjectBudgetApprovalTaskExpiredNotificationRecipients

Handler of notification recipients for project budget approval tasks, which are overdue.

<ProjectBudgetApprovalTaskExpiredNotificationRecipients>
{$New}
</ProjectBudgetApprovalTaskExpiredNotificationRecipients>
<!-- Where {$New} is a ProjectBudgetApprovalTask -->

TargetNotificationRecipients

Handler of notification recipients for goals. If a user is responsible for a goal, activity, SMART task or child goal, he/she will receive a notification.

<TargetNotificationRecipients>
{$New}</TargetNotificationRecipients>
<!-- Where {$New} is a goal -->

MessageChannelRecipient

Handler of notification recipients for the Public Channel Message type. If a user is a channel subscriber or receipient, he/she will receive a notification.

<MessageChannelRecipient>
{$New}
</MessageChannelRecipient>
<!-- Where {$New} is a public channel message ChannelMessage -->

WorkflowTaskExpiredNotificationRecipients

Handler of notification recipients of overdue tasks. If a user is a business process initiator, responsible for an instance, process owner or process responsibility matrix owner, he/she will receive a notification.

<WorkflowTaskExpiredNotificationRecipients>
{$New}
</WorkflowTaskExpiredNotificationRecipients>
<!-- Where {$New} – is a business process task WorkflowTask -->

 

To learn about creating a custom data type in the list of notification recipients, read this article.

6. Object ID. Required for the messages section, where a notification is generated depending on the object type and a notification is shown by the specified ID, as well as for keeping history on the object with the specified ID (reassignments, rescheduling…):

<ObjectId>{$New.Id}</ObjectId>
 

7. Full message:

<FullMessage>
  {TableStart()}{Partial(’DefaultFullMessage’)}{TableEnd()}
</FullMessage>
 

8. Short message:

<ShortMessage>
  {TableStart()}{Partial(’DefaultShortMessage’)}{TableEnd()}
</ShortMessage>
 

The <ShortMessage> and <FullMessage> tags are responsible for displaying the short and the full notification. Note that <FullMessage> is displayed in an email, while <ShortMessage> is displayed in the Messages section. If <FullMessage> is not specified, then <ShortMessage> will be displayed by default.

9. Clearing the list of notification recipients. The difference from <RecipientSet Clear=true> is that Clear clears a specific set of notification recipients, while <RecipientSetClear> clears all the notification recipients:

<RecipientSetClear>true</RecipientSetClear>
 

10. Sending a message to the author. The author is a system user who performed an action, which led to sending a notification (e.g. created a task). By default, the system filters notifications and does not send notifications about actions, a user performed him/herself:

<SendToAuthor>true</SendToAuthor>
 

In the tags Notification, Notifications, Partial you can use parameters Name, description, Parent, responsible for the name, tag description and a link to a parent notification respectively.

 

Object dictionaries

In notification templates you can use object dictionaries - Dictionary<string, object>, its key is a name, used in a notification template, and its object may be any system object. Using object dictionaries may be useful to write objects more conveniently in case of a complex logic, e.g. separate the users who had already received a notification about the object from those, who had not received notifications. Example:

<RecipientSet>
  <User>{$Group}</User>
</RecipientSet>
 

In this example, Group is the dictionary key and the list of notification recipients is the object. You must implement an object dictionary in the same place, where the EntityActionEventArgs is initialized. Let's refer to the example from the article, in which subscription to notifications about changes in an object is implemented. The Rename method is defined in the object manager. Inside this method, EntityActionEventArgs is initialized. A simple use case of an object dictionary:

public IEntityActionHandler EntityActionHandler { get; set; }
 
[Transaction]
[ActionMethod(MyObjectActions.Rename)]
public virtual void Rename(IMyObject model, string newName)
{
    model.Name = newName;
    model.Save();
    var users = model.UserGroup.Users;
    var args = new EntityActionEventArgs(null, model, MyObjectActions.Rename);
    if (users.Count > 0)
    {
        args.ExtendedProperties.Add("Group", users);
    }
    EntityActionHandler.ActionExecuted(args);
}
 

As you can see, users from the UserGroup object group were added to the dictionary. Alternatively, the following structure can be used:

<RecipientSet>
  <User>{$New.UserGroup.Users}</User>
</RecipientSet>
 

However, {$Group} is shorter and can be used only inside a certain event, therefore you can implement a complex logic of compiling object dictionaries, for example, exclude certain users from the list of notified users.

Important
Note, that you can use this dictionary only in the event, to which this dictionary is bound, in this example, Rename is the event. If you use this dictionary to implement a notification about a different event, the value will be empty.

 

Functions in notification templates

You can use functions in notifications. The function name must be specified in braces, and then pass parameters in parenthesis. Parameters are separated with semicolumns. Two notification formats are generated: text and HTML; the sending channel decides which format to use. These functions are useful because they generate different markup for different formats.

Functions in notifications: 

Function name

Description

Example

{TableStart()}

Start of a two-column table for the notification

{TableStart()}
{PropertyRowWithChanges({$Old.Name};{$New.Name})}
{TableEnd()}

{TableEnd()}

End of a two-column table for the notification

{PropertyRow()}

Generates two columns - property name and property value.

Parameter 1 - Value.

Parameter 2 - Header (optional, the object property name is used by default).

Parameter 3 - Visibility (optional, by default true).

{PropertyRow({$New.Name};SR(’Назв.’))}

{PropertyFullRow()}

Generates two rows from two merged columns - property name and property value.

Parameter 1 - Value.

Parameter 2 - Header (optional, the object property name is used by default).

Parameter 3 - Visibility (optional, by default true).

{PropertyFullRow({$Comment.Text};SR(’Comment’))}

{EmptyRow()}

Empty space between rows with data. An empty row will be displayed only in an email and not in the web part.

{EmptyRow()}

{PropertyRowWithChanges()}

Generates two columns - property name and property value with the information about changes (if any).

Parameter 1 - Old value.

Parameter 2 - New value.

Parameter 3 - Header (optional, the object property name is used by default).

Parameter 4 - Visibility (optional, by default true).

{PropertyRowWithChanges({$Old.Name};{$New.Name})}

{PropertyFullRowWithChanges()}

Generates two rows from two merged columns - property name and property value with the information about changes (if any).

Parameter 1 - Old value.

Parameter 2 - New value.

Parameter 3 - Header (optional, the object property name is used by default).

Parameter 4 - Visibility (optional, by default true).

{PropertyFullRowWithChanges({$Old.Description};{$New.Description})}

{PropertyRowIfChanged()}

Generates two columns - property name and property value with the information about changes. If there are no changes, an empty row is generated.

Parameter 1 - Old value.

Parameter 2 - New value.

Parameter 3 - Header (optional, the object property name is used by default).

Parameter 4 - Visibility (optional, by default true).

{PropertyRowIfChanged({$Old.Executor};{$New.Executor})}

{PropertyFullRowIfChanged()}

Generates two rows from two merged columns - property name and property value with the information about changes. If there are no changes, an empty row is generated.

Parameter 1 - Old value.

Parameter 2 - New value.

Parameter 3 - Header (optional, the object property name is used by default).

Parameter 4 - Visibility (optional, by default true).

{PropertyFullRowIfChanged({$Old.Description};{$New.Description})}

 

Fig. 2. Example of {PropertyRowWithChanges({$Old.Name};{$New.Name})} without using {TableStart()} {TableEnd()}

 

Fig. 3. Example of {PropertyRowWithChanges({$Old.Name};{$New.Name})} with {TableStart()} {TableEnd()}

 

Using document template generation language in notification templates

You can use both conditions and cycles in notification templates. The syntax is same as the one used in reports and document templates. To learn more, read Help or this article.