Creating custom object monitoring

In this article, we show how to set up custom monitoring of IMyObject object. The example demonstrates how the Rename action is implemented in IMyObject (renaming an object item) and a respective notification is received.

Example of data display

Fig. 1. Object monitoring

Fig. 2. Notification about the name of the object having been modified

Extension (interface) methods

/// <summary>
/// Monitored types
/// </summary>
IEnumerable<Guid> TypeUid { get; }
 
/// <summary>
/// Whether to show a notification when you delete yourself from the list of observers 
/// </summary>
/// <param name="entityTypeUid">object type Uid</param>
/// <param name="entityId">Object ID (cannot be null)</param>
/// <returns><c>true</c> if the notification is needed</returns>
bool NeedConfirmFromDelete(Guid entityTypeUid, [NotNull]object entityId);
 
/// <summary>
/// Return the text of a warning when one delete’s himself or herself from the list of observers
/// </summary>
/// <param name="entityTypeUid">object type Uid</param>
/// <param name="entityId">Object id(cannot be null)</param>
/// <returns>Returns text of a warning when one delete’s himself or herself from the list of observers
</returns>
string TextConfirmFromDelete(Guid entityTypeUid, [NotNull]object entityId);
 
/// <summary>
/// Is the selected object supported
/// </summary>
bool IsAvailable(Guid entityTypeUid, object entityId);
 
/// <summary>
/// Monitored actions
/// </summary>
IEnumerable<Guid> ActionUids { get; }
 
/// <summary>
/// Description of monitoring by to object type
/// </summary>
string GlobalWatchDescription { get; }
 
/// <summary>
/// Description of monitoring of a specific object/// </summary>
string WatchDescription { get; }
 
/// <summary>
/// Parent object type id
/// </summary>
IEnumerable<Guid> ParentTypeUid { get; }
 
/// <summary>
/// Get entity’s parents
/// </summary>
/// <param name="entity">Entity</param>
/// <returns>List of entity parents</returns>
IEnumerable<Guid> GetParentTypeUid(IEntity entity);
 
/// <summary>
/// Get parent entity’s id
/// </summary>
/// <returns>Parent entity id</returns>
long? GetParentEntityId(IEntity entity);
 
/// <summary>
/// Whether to notify the selected user
/// </summary>
/// <param name="entity">Entity</param>
/// <param name="user">User</param>
/// <returns><c>true</c>, if the selected user has to be notified</returns>
bool CanSendToUser(IEntity entity, IUser user);

IWatchProvider extension point (interface) uses the following methods:

Example of extension point class

The IWatchProvider extension point uses the BaseWatchProvider base class, which implements the base logic of the extension point’s operation. To set custom object monitoring, you have to define several methods. Here is an example:

[Component]
public class WatchProviderExample : BaseWatchProvider
{
    public override IEnumerable<Guid> TypeUid
    {
        get { yield return InterfaceActivator.UID<IMyObject>(); } //Implementing monitoring of an IMyObject type object (simple object)
    }
 
    public override IEnumerable<Guid> ActionUids
    {
        get { yield return MyObjectActions.RenameGuid; } //Uid of the action in the object
    }
 
    public override string GlobalWatchDescription
    {
        get { return SR.T("You will receive notifications when an object item of this object type is renamed"); }
    }
 
    public override string WatchDescription
    {
        get { return SR.T("You will receive notifications when an object item is renamed"); }
    }
}
Note
To apply this extension point, you have to implement an action in the object. In this example, the action for IMyObject is Rename.

Fig. 3. Rename action in the object

Implement the logic of this action in the object’s manager. Here is an example of implementing the logic for renaming your object:

public IEntityActionHandler EntityActionHandler { get; set; }
 
[Transaction]
[ActionMethod(MyObjectActions.Rename)]
public virtual void Rename(IMyObject model, string newName)
{
    model.Name = newName;
    model.Save();
    var args = new EntityActionEventArgs(null, model, MyObjectActions.Rename);
    EntityActionHandler.ActionExecuted(args);     
}

EntityActionEventArgs – event parameters for entity action. Participate in creating the action’s history. Using this code is obligatory! Otherwise, notification will not work.

EntityActionHandler.ActionExecuted(args) – the entity action handler triggers once the action is complete.

In this example, we implemented the object item page shown in fig.1, which shows the object properties. Also, we implemented a sign-up button (to monitor the object) and edit button to edit the name of the object item. To add a sign-up button to the markup all you have to do is use Html.Header and pass the subject/name of the object and the object itself to it. Example of markup for displaying the object item’s page:

@model ObjectIconModule.Models.IMyObject
@using EleWise.ELMA.Model.Services
@using EleWise.ELMA;
@using EleWise.ELMA.BPM.Web.Tasks.Extensions
@using ObjectIconModule.Web
 
@(Html.Toolbar()
        .Group("tb-group1")
            .Button(b => b
                .Uid("toolbar-action-Back")
                .Text(SR.Back)
                .IconUrl("#x32/Prev.png")
                .Click("javascript:history.back(-1);")
            )
            .Button(b => b
                .Uid("toolbar-action-Edit")
                .Text(SR.T("Change name"))
                .IconUrl("#x32/Edit.png")
                .Click(Html.OpenPopup("RenamePopup", useReferrer: true).ToString())
            )
)
 
@{
    Html.Header(Model.Name, Model);
}
 
@using (Html.ElmaForm())
{
    @Html.TableFormStart()
    @Html.DisplayFor(m => m, "Object")
    @Html.TableFormEnd()
}
 
@(Html.PopupWindow(
    "RenamePopup",
SR.T("Change object name"),
          Url.Action("RenamePopup", "Home", new { area = RouteProvider.AreaName, Name = Model.Name, id = Model.Id })
, 600)
)

As you can see, when the toolbar button “Change name” is pressed, a pop-up window appears, featuring a single “Name” field. The pop-up window is called by the RenamePopup method in Home controller, which returns the view form of the pop-up. Here is an example of view form for the pop-up for changing object’s name:

@model IMyObject
@using EleWise.ELMA;
@using ObjectIconModule.Models
@using ObjectIconModule.Web
 
@{
    var id = Model.Id;
    var name = Model.Name; 
}
@Html.EditableProperty(m => m.Name)
<div class="popup-buttons">
    <input type="button" class="confirm" value="@(SR.T("Save"))"
           onclick="renameItem($(’#@ViewData.TemplateInfo.GetFullHtmlFieldId("Name")’).val())" />
    <input type="button" value="@(SR.T("Cancel"))" onclick="@(Html.ClosePopup())" />
</div>
 
<script language="javascript" type="text/javascript">
    function renameItem(name) {
        $.ajax({
            url: ’@(Url.Action("Rename", "Home", new { area = RouteProvider.AreaName, id }))’,
            type: "POST",
            dataType: ’json’,
            data: { "name": name },
            cache: false,
            success: function (data) {
                if (data && data.error) {
                    jAlert(data.error, ’@SR.T("Error")’);
                } else {
                    location.reload();
                }
            }
        });
    }
Note
As you can see from the code for the pop-up window for editing the object instance name, when the Save button is pressed, theJavaScript renameItem(name) function is called. It calls the Rename method of the Home. controller.

Example of code for the controller:

public ActionResult RenamePopup(long id)
{
    var model = MyObjectManager.Instance.LoadOrNull(id);
    return PartialView(model);
}

public ActionResult Rename(long id, string Name)
{
    var model = MyObjectManager.Instance.LoadOrNull(id);
    MyObjectManager.Instance.Rename(model, Name);
    var json = new JsonSerializer().Serialize(model);
    return new JsonResult{Data = json};
}

In the Rename method, the Rename method from your object manager is called. After it is executed, users who are subscribed to the object will receive a corresponding notification. In order for the notification to appear in the message feed or your personal message channel, create a template for your notification. You can find more information of notification templates here. Example of a notification template:

<?xml version="1.0" encoding="utf-8"?>
<Notifications description="Object notification" 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 has been renamed">
    <Filter>
      <Action>Rename</Action>
    </Filter>
    <Subject>
      ({SR(Object renamed)}) {$New.Name}
    </Subject>
    <ShortMessage>
      {SR(’{$New.ChangeAuthor} has changed the object’s name: from "{$Old.Name}" to "{$New.Name}"’)}
    </ShortMessage>
    <FullMessage>
      {TableStart()}
      {PropertyRowWithChanges({$Old.Name};{$New.Name})}
      {PropertyRow({$New.Name})}
      {PropertyRow({$New.ChangeAuthor})}
      {TableEnd()}
    </FullMessage>
  </Notification>
</Notifications>

If you want the notifications to feature the “old” values (for example, {$Old.Name}), implement the following class:

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

Links to API elements

IWatchProvider