logo

[ELMA3] Шаблоны сообщений XML

Система ELMA поддерживает шаблоны сообщений в формате XML. В системе существует возможность подписываться на объекты. О том, как создать собственное наблюдение за объектом, можно прочитать в следующей статье. В статье шла речь о создании подписки на объект, при изменении которого подписавшемуся пользователю приходило соответствующее сообщение. Шаблоны системных оповещений находятся в папке {Путь_до_ELMA}\UserConfig\Notifications\. Каждое сообщение можно изменить в зависимости от нужд пользователя/заказчика.

Пример шаблона оповещения:

<?xml version="1.0" encoding="utf-8"?>
<Notifications description="Оповещения по объекту" 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="Объект переименован">
    <Filter>
      <Action>Rename</Action>
    </Filter>
    <Subject>
      ({SR(’Объект переименован’)}) {$New.Name}
    </Subject>
    <ShortMessage>
      {SR(’{$New.ChangeAuthor} переименовал объект: из "{$Old.Name}" в "{$New.Name}"’)}
    </ShortMessage>
    <FullMessage>
      {TableStart()}
      {PropertyRowWithChanges({$Old.Name};{$New.Name})}
      {PropertyRow({$New.Name})}
      {PropertyRow({$New.ChangeAuthor})}
      {TableEnd()}
    </FullMessage>
  </Notification>

</Notifications>

Чтобы Ваш шаблон оповещений попал в папку с системными шаблонами оповещений ({Папка с конфигурацией}\Notifications\) и начал работать, необходимо сделать следующее:

  1. Реализовать пустой класс, наследуемый от NotificationTemplateEmbeddedDataSource. Данный класс необходимо поместить в туже папку, где находится Ваш шаблон оповещений XML.
  2. Изменить свойства файла с шаблоном Вашего оповещения.

Пример реализации пустого класса, наследуемого от NotificationTemplateEmbeddedDataSource:

class MyObjectNotificationsDataSource : NotificationTemplateEmbeddedDataSource
{
}

Также нужно изменить свойства файла с шаблоном Вашего оповещения. Установить Build Action в состояние Embedded Resource:

Рис. 1. Изменение свойств файла с шаблоном оповещения

Для того, чтобы у Вас отображались «старые» значения в оповещениях (например, {$Old.Name}), необходимо реализовать следующий класс:

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

Рассмотрим пример XML-шаблона сообщений из статьи с подпиской на объект. В шаблоне оповещений существует несколько обязательных тегов: Notifications и Notification.

<Notifications description="Оповещения по объекту" version="3.7.0.41532"></Notifications> - Основные теги оповещений, внутри этого тега должны располагаться все остальные необходимые теги.

<Notification Name="AddComment"></Notification> - Непосредственный тег с оповещением. Внутри него могут находится те же самые теги, что и в теге <Default>.

Пример:

<Notification description="Объект переименован">
  <Filter>
    <Action>Rename</Action> <!-- Название действия -->
    <Object>ObjectIconModule.Models.MyObject</Object> <!-- Объект, на который направлено действие -->
  </Filter>
  <Subject>
    ({SR(’Объект переименован’)}) {$New.Name} <!-- Тема сообщения -->
  </Subject>
  <ShortMessage>
    {SR(’{$New.ChangeAuthor} переименовал объект: из "{$Old.Name}" в "{$New.Name}"’)} <!-- Короткое сообщение, в котором используется автор создания, старое название и новое название объекта -->
  </ShortMessage>
  <FullMessage>
    {TableStart()}<!-- Начало таблицы -->
    {PropertyRow({$New.Name})} <!-- Новое название объекта -->
    {PropertyRow({$New.ChangeAuthor})} <!-- Автор изменения -->
    {TableEnd()}<!-- Окончание таблицы -->
  </FullMessage>
</Notification>

Тегов <Notification> может быть сколько угодно. Наименования Name могут совпадать, как и действие <Action> и объект, на который это действие направлено. Это сделано для того, чтобы была возможность, например, использовать одинаковое название действий в разных объектах или в одном объекте разных действий. Поэтому необходимо внимательно следить при формировании оповещения. Наименования в Notification также используются в дочерних шаблонах оповещения, чтобы дополнить родительское оповещение.

Если Вы планируете использовать свой шаблон сообщений в качестве родительского, для этого следует присваивать имена Name в теге Notification. Затем в дочерних шаблонах оповещений в теге Notifications использовать параметр Default, в котором указать ваш родительский шаблон, например, <Notifications Default="Projects.ProjectTaskBase/">, соответственно выберется шаблон по умолчанию из файла Projects.ProjectTaskBase.xml. Чтобы использовать уже готовые оповещения Notification, необходимо в своем дочернем шаблоне создать оповещение Notification с параметром Parent, в котором указать название Notification родителя, например, <Notification Name="CreateMilestone" Parent="Projects.ProjectTaskBase/Create"> - в данном случае из шаблона оповещений Projects.ProjectTaskBase.xml будет использовано оповещение с именем Create.

Внутри тега Notifications можно использовать необязательный тег <Partial Name="DefaultFullMessage"> </Partial>, который представляет собой частичное представление текста, который затем можно вставить в нужных местах, например, в <ShortMessage>/<FullMessage>. Параметр Name в дальнейшем необходимо использовать непосредственно в методе {Partial(’Имя вашего Partial’)}. Пример использования метода Partial можно увидеть ниже в тегах <ShortMessage>/<FullMessage>. Пример тега <Partial> в CRM.ContractorLegal.xml:

<Partial Name="DefaultContractorFullMessage">
{PropertyRow({$New.CreationAuthor})} <!-- Автор создания -->
{PropertyRow({$New.Name})} <!-- Наименование контрагента -->
{PropertyRow({$New.Phone})} <!-- Телефон -->
{PropertyRow({$New.Email})} <!-- Email -->
{PropertyRow({$New.Site})} <!-- Веб-сайт -->
{PropertyFullRow({$Comment.Text};SR(’Комментарий’))} <!--Текст комментария-->
{PropertyFullRow({$Attachments})} <!-- Вложения -->
{ExtensionZone(’EleWise.ELMA.CRM.Models.ContractorLegal-DefaultFullMessage-AfterAttachments’)}
</Partial>
<Partial Name="DefaultContractorShortMessage">
{PropertyFullRow({$Comment.Text};’’)} <!-- Текст комментария -->
</Partial>

<Default></Default> - Внутри данного тега находятся значения по умолчанию.

1. Каналы отправки сообщений:

<Channels>EMail</Channels>

Если указать данный тег, в который передать название канала отправки сообщений, то оповещение будет отправлено только в указанный канал отправки сообщений. Если не указывать данный тег, то по умолчанию оповещение отправится во все существующие каналы сообщений, такие как E-mail или в созданный вами канал отправки сообщений. Подробнее про создание своего канала отправки сообщений можно прочитать в следующей статье.

2. URL до объекта:

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

3. Тема сообщения:

<Subject>({SR(’Объект переименован’)}) {$New.Name}</Subject>

4. Фильтр:

<Filter>
  <Event>IEntityActionHandler.ActionExecuted</Event>
  <Object>EleWise.ELMA.Projects.Models.ProjectTask</Object>
  <Condition>
    (IsProjectTemplate({$New}) = false) and ({$New.Project.Status} = ’Active’)
  </Condition>
</Filter>
В данном случае при выполнении любого события (Event), вызванного через IEntityActionHandler будет отправлено оповещение по объекту (Object) ProjectTask, при условии (Condition) выполнения Condition (фильтр будет срабатывать, если статус проекта является Текущий и проект не является шаблоном). Сам метод IsProjectTemplate должен быть реализован в классе, реализующем интерфейс ITemplateGeneratorFunctionsContainer, подробнее можно узнать в следующей статье. IEntityActionHandler.ActionExecuted – это обработчик действий с сущностью, после выполнения действия. Внутри фильтра в notification нужно указать наименование действия над объектом <Action>Rename</Action>, которое создается во вкладке Действия в карточке объекта и логика которого реализована в менеджере Вашего объекта. Подробнее было описано в следующей статье.

5. Список пользователей, которые получат оповещение <RecipientSet><User> {$WatchList} </User></RecipientSet> (В данном случае оповещение получат те пользователи, которые подписаны ({$WatchList}) на объект, но можно передать и других пользователей, например, ответственных, исполнителей и т.д.). У списка пользователей есть несколько дополнительных параметров:

  • Clear, отвечающий за очистку списка получателей оповещения. Использование данного параметра бывает необходимо, если Ваш шаблон оповещения является дочерним, поэтому чтобы в список получателей не попали пользователи, указанные в родительском шаблоне можно использовать данный параметр;
  • SendToAuthorOnly, отвечающий за отправку оповещения только автору.
    Пример использования:
    <RecipientSet Clear="true">
      <User>{$New.Executor}</User> <!-- Исполнитель -->
      <User>{$New.InformTo}</User> <!-- Информируемый-->
      <User>{$New.CreationAuthor}</User> <!-- Автор создания -->
      <User>{$WatchList}</User> <!-- Подписавшиеся на рассылку оповещений -->
    </RecipientSet> 

Для получателя можно определить условие при помощи параметра condition, например,
<User condition="{$New.CreationAuthor}&lt;&gt;{$New.ControlUser}">{$New.CreationAuthor}</User>, что означает, если автор создания не равен контролёру, тогда добавить автора создания к получателям оповещения.

В списке получателей могут быть разные данные. Типы данных, используемые в ELMA:

Название типа данных

Описание

Пример

User

Оповещение для пользователя.

<User>{$New.Executor}</User>
<!-- Исполнитель User-->

UserId

Оповещение для пользователя по идентификатору.

<UserId>3</UserId>

ParticipantRecipients

Оповещение для участников события календаря, если пользователь подтвердил свое участие в событии.

<ParticipantRecipients>
{$New}
</ParticipantRecipients>
<!-- Где {$New} – событие CalendarEvent -->

RelationshipParticipantRecipients

Оповещение для участников взаимоотношения.

<RelationshipParticipantRecipients>
{$New}
</RelationshipParticipantRecipients>
<!-- Где {$New} – взаимоотношение Relationship -->

InformToRecipients

Оповещение для участников события календаря, если пользователь является информируемым.

<InformToRecipients>{$New}</InformToRecipients>
<!-- Где {$New} – событие CalendarEvent -->

ConfirmParticipationRecipient

Список участников для подтверждения участия в событии календаря. Если пользователь является участником события, но еще не подтвердил/опроверг свое участие.

<ConfirmParticipationRecipient>
{$New}
</ConfirmParticipationRecipient>
<!-- Где {$New} – событие CalendarEvent -->

RelationshipInformToRecipients

Оповещение для участников взаимоотношений, если пользователь является информируемым.

<RelationshipInformToRecipients>
{$New}
</RelationshipInformToRecipients>
<!-- Где {$New} – событие Relationship -->

ProjectPlanApprovalTaskExpiredNotificationRecipients

Обработчик получателей оповещения для задач утверждения плана проекта, начало работ по которым просрочено.

<ProjectPlanApprovalTaskExpiredNotificationRecipients>
{$New}
</ProjectPlanApprovalTaskExpiredNotificationRecipients>
<!-- Где {$New} – задача утверждения плана проекта ProjectPlanApprovalTask -->

ProjectBudgetApprovalTaskExpiredNotificationRecipients

Обработчик получателей оповещения для задач утверждения бюджета проекта, начало работ по которым просрочено.

<ProjectBudgetApprovalTaskExpiredNotificationRecipients>
{$New}
</ProjectBudgetApprovalTaskExpiredNotificationRecipients>
<!-- Где {$New} – задача утверждения бюджета проекта ProjectBudgetApprovalTask -->

TargetNotificationRecipients

Обработчик получателей оповещения для целей. Если пользователь является ответственным за цель, мероприятие, SMART-задачу или дочернюю цель – ему придет оповещение.

<TargetNotificationRecipients>
{$New}</TargetNotificationRecipients>
<!-- Где {$New} – Цель Target -->

MessageChannelRecipient

Обработчик получателей оповещения для типа Сообщение информационного канала. Если пользователь является получателем или подписчиком канала – он получит оповещение.

<MessageChannelRecipient>
{$New}
</MessageChannelRecipient>
<!-- Где {$New} –Сообщение инфоканала ChannelMessage -->

WorkflowTaskExpiredNotificationRecipients

Обработчик получателей оповещения для задач, начало работ по которым просрочено. Если пользователь является инициатором БП, Ответственным за экземпляр, владельцем процесса или владелецем матрицы ответственности БП – он получит оповещение.

<WorkflowTaskExpiredNotificationRecipients>
{$New}
</WorkflowTaskExpiredNotificationRecipients>
<!-- Где {$New} – задача БП WorkflowTask -->

О том, как сделать собственный тип данных в списке получателей оповещения можно прочитать в следующей статье.

6. Идентификатор объекта. Необходим для ленты сообщений, в ленте генерируется сообщение в зависимости от типа объекта и показывается оповещение по указанному идентификатору, а также для ведения истории по объекту с заданным идентификатором (переназначения, перенос сроков…):

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

7. Полное сообщение:

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

8. Короткое сообщение:

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

Теги <ShortMessage> и <FullMessage> отвечают за отображение сообщений короткого и полного формата соответственно. Стоит отметить, что <FullMessage> отображается во входящем e-mail сообщений, тогда как <ShortMessage> отображается в ленте сообщений. Если <FullMessage> не задано, то по умолчанию будет отображено <ShortMessage>.

9. Очистка списка пользователей, получающих оповещение. Отличие от <RecipientSet Clear=true> в том, что Clear очищает конкретный набор получателей оповещения, а <RecipientSetClear> очищает все наборы получателей оповещения:

<RecipientSetClear>true</RecipientSetClear>

10. Отправка оповещения автору. Автор – это пользователь системы, который произвел действие, которое привело к отправке сообщения (например, пользователь создал задачу). По умолчанию система фильтрует оповещения, и не отправляет этому пользователю сообщения о том, что он сам что-то сделал:

<SendToAuthor>true</SendToAuthor>

В тегах Notification, Notifications, Partial можно использовать параметры Name, description, Parent, отвечающие за название, описание тега и ссылку на родительское оповещение соответственно.

Словари объектов

В шаблонах оповещений можно использовать словари объектов – это словарь Dictionary<string, object>, ключом которого является название, используемое в шаблоне оповещения, а объектом может являться любой объект системы. Использование словарей объектов может быть полезно при более удобной записи объектов, при сложной логике, например, отделить пользователей, которые уже получали оповещение по объекту от тех, которые не получали оповещения по объекту. Пример использования:

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

В данном примере ключом словаря является название Group, а объектом является список пользователей, которые получат оповещение. Реализовывать словарь объектов необходимо в том же месте, где и происходит инициализация EntityActionEventArgs. Воспользуемся примером из статьи, в которой реализована подписка на рассылку оповещений об изменении объекта. В менеджере объекта определен метод Rename, внутри которого инициализирован EntityActionEventArgs. Реализуем простой пример использования словаря объектов:

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.GruppaPoljzovateley.Users;
    var args = new EntityActionEventArgs(null, model, MyObjectActions.Rename);
    if (users.Count > 0)
    {
        args.ExtendedProperties.Add("Group", users);
    }
    EntityActionHandler.ActionExecuted(args);
} 

Как Вы можете заметить, в словарь были добавлены пользователи из группы объекта GruppaPoljzovateley. Альтернативным вариантом данного примера может быть использование конструкции:

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

Но запись {$Group} короче и может использоваться только внутри определенного события, поэтому возможна реализация сложной логики составления словарей объекта, например, исключить определенных пользователей из списка оповещаемых.

Важное примечание
Стоит отметить, что использовать данный словарь можно только в том событии, к которому привязан данный словарь, в примере этим событием является Rename. Если использовать данный словарь в реализации оповещения по другому событию – значение будет пустым.

Функции в шаблонах оповещений

В оповещениях можно использовать функции. Название функции необходимо записывать в фигурных скобках, далее в круглых скобках передать параметры. Параметры отделяются друг от друга точкой с запятой. При генерации оповещения формируется 2 формата: обычный текстовый и HTML, и уже канал отправки решает какой из форматов использовать. Данные функции полезны при формировании сообщения, потому что они формируют для разных форматов разную разметку.

Функции в оповещениях:

Название функции

Описание

Пример

{TableStart()}

Начало таблицы из 2-х колонок для оповещения.

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

{TableEnd()}

Конец таблицы из 2-х колонок для оповещения.

{PropertyRow()}

Генерирует 2 столбца – имя свойства и значение свойства.

Параметр 1 – Значение.

Параметр 2 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 3 – Видимость (необязательный, по умолчанию true).

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

{PropertyFullRow()}

Генерирует 2 строки из слитых 2-х столбцов – имя свойства и значение свойства.

Параметр 1 – Значение.

Параметр 2 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 3 – Видимость (необязательный, по умолчанию true).

{PropertyFullRow({$Comment.Text};SR(’Комментарий’))}

{EmptyRow()}

Пустое пространство между строками с данными. Пустая строка будет отображаться только в E-mail сообщении, в веб-части пустой строки не будет.

{EmptyRow()}

{PropertyRowWithChanges()}

Генерирует 2 столбца – имя свойства и значение свойства с информацией об изменении (если оно было).

Параметр 1 – Старое значение.

Параметр 2 – Новое значение.

Параметр 3 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 4 – Видимость (необязательный, по умолчанию true).

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

{PropertyFullRowWithChanges()}

Генерирует 2 строки из слитых 2-х столбцов – имя свойства и значение свойства с информацией об изменении (если оно было).

Параметр 1 – Старое значение.

Параметр 2 – Новое значение.

Параметр 3 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 4 – Видимость (необязательный, по умолчанию true).

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

{PropertyRowIfChanged()}

Генерирует 2 столбца – имя свойства и значение свойства с информацией об изменении. Если изменения не было – пустая строка.

Параметр 1 – Старое значение.

Параметр 2 – Новое значение.

Параметр 3 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 4 – Видимость (необязательный, по умолчанию true).

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

{PropertyFullRowIfChanged()}

Генерирует 2 строки из слитых 2-х столбцов – имя свойства и значение свойства с информацией об изменении. Если изменения не было – пустая строка.

Параметр 1 – Старое значение.

Параметр 2 – Новое значение.

Параметр 3 – Заголовок (необязательный, по умолчанию устанавливается название свойства объекта).

Параметр 4 – Видимость (необязательный, по умолчанию true).

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

Рис. 2. Пример отображения {PropertyRowWithChanges({$Old.Name};{$New.Name})} без использования {TableStart()} {TableEnd()}

Рис. 3. Пример отображения {PropertyRowWithChanges({$Old.Name};{$New.Name})} с использованием {TableStart()} {TableEnd()}

Использование языка генерации шаблона документов в шаблонах оповещений

В шаблонах оповещений можно использовать как условия, так и циклы. Синтаксис совпадает с синтаксисом, используемым в отчетах или в шаблонах документов. Подробнее можно прочитать в справке или в статье.

Ссылки на элементы API

  • IWatchProvider (для версий 3.13, 3.15, 4.0);
  • ITemplateGeneratorFunctionsContainer (для версий 3.13, 3.15, 4.0);
  • INotificationFunctions (для версий 3.13, 3.15, 4.0);
  • INotificationRecipientTypeHandler(для версий 3.13, 3.15, 4.0).