AgileApps Support Wiki Pre Release

Difference between revisions of "HowTo:Send Notification Messages to Followers"

From AgileApps Support Wiki
imported>Aeric
imported>Aeric
Line 17: Line 17:
#: Select the objects that can be followed, or choose ''All Objects''.
#: Select the objects that can be followed, or choose ''All Objects''.
#: (The data in those fields has the format <tt>object_id:record_id</tt>. That's why you can follow any record in the system.)
#: (The data in those fields has the format <tt>object_id:record_id</tt>. That's why you can follow any record in the system.)
# Modify the Case form to create a new '''Related Information''' section that displays Follower records.
# Modify the Case form to create a new tab that displays Followers.
#* Select the''' Followers''' object
#* Go to '''[[File:GearIcon.png]] > Objects > Followers > Forms > Default Form'''
#* Select the '''Followers''' object
#* Link the Follower's '''Related To''' field to the object's '''ID''' field
#* Link the Follower's '''Related To''' field to the object's '''ID''' field
#* Select '''Email Address''' as the field to display
#* Select '''Email Address''' as the field to display
#* Sort by email address, in ascending order.
#* Sort by email address, in ascending order.
# Open a Case record and use that section to manually add yourself as a follower.
# Use the sample code below to create the <tt>Notifications</tt> class.
# Use the sample code below to create the <tt>Notifications</tt> class.
# Create a record-updated rule that invokes the <tt>notifyFollowers()</tt> method defined in the code.
# Create a record-updated rule that invokes the <tt>notifyFollowers()</tt> method defined in the code.
Line 29: Line 29:
#* '''Run this Rule:''' Unconditionally
#* '''Run this Rule:''' Unconditionally
#* '''Actions to Perform:''' Invoke Method (Notifications, notifyFollowers)
#* '''Actions to Perform:''' Invoke Method (Notifications, notifyFollowers)
# Open a case record, click the '''Followers''' tab, and manually add yourself as a follower.
# Update the case record and check your inbox for the notification message.
# Update the case record and check your inbox for the notification message.



Revision as of 03:31, 13 November 2014

This code sample sends an email to everyone who registered themselves as a "follower" of a Case (or any other object where the application designer allows it).

To follow the record, a "related record" with the user's email address is added to the Followers object. When the Case (or other record) is updated, an email notification is sent to everyone who is following it, using an API and an email template created for the purpose.

Learn more:

Setup and Testing

  1. Create the Followers object:
    • Launch the Object Construction Wizard
    • Define the fields: email_address, user_name, appID.
      (The appID field will be needed later, when constructing a link to put into the email. Define it now, so it's ready when you need it.)
  2. Create the linking field:
    Select the objects that can be followed, or choose All Objects.
    (The data in those fields has the format object_id:record_id. That's why you can follow any record in the system.)
  3. Modify the Case form to create a new tab that displays Followers.
    • Go to GearIcon.png > Objects > Followers > Forms > Default Form
    • Select the Followers object
    • Link the Follower's Related To field to the object's ID field
    • Select Email Address as the field to display
    • Sort by email address, in ascending order.
  4. Use the sample code below to create the Notifications class.
  5. Create a record-updated rule that invokes the notifyFollowers() method defined in the code.
    • Name: Send Update Notifications
    • Description: Tell anyone who is following the record that a change has occurred.
    • Run this Rule: Unconditionally
    • Actions to Perform: Invoke Method (Notifications, notifyFollowers)
  6. Open a case record, click the Followers tab, and manually add yourself as a follower.
  7. Update the case record and check your inbox for the notification message.

Next Steps

Here are some things you can do after you get the basic behavior working:

  1. Define a Follow macro for any object in which you want to add Followers:
    In that macro, invoke the addFollower() method defined in the code below.
    The macro will appear in the list of actions available for the record.
    When clicked, the macro will add a Follower record, with all fields filled in.
    Here's what you need for the basic macro:
    Name: Follow
    Description: Add the user who clicks the button to the list of "followers"--people who will receive an email when the record is updated.
    Show: Always
    Action: Invoke Method, Class: Notifications, Method: addFollower
  2. Send notifications for a subset of updates
    This is an extra credit assignment.
    To do it, you hard-code a list of fields you care about in the class. Or you could use the Functions.getFieldMetadata API to see send notifications only for Audited Fields.
  3. Allow for "unfollowing"
    Users can unfollow a record manually, by clicking their Follower record and choosing the Delete action. Alternatively, you could cannibalize the code below to add a method that searches for a Follower with a matching email_address and related_to field. (But then you have to decide which button to display on the form, which will require JavaScript that uses REST APIs to query the Followers object--all of which adds lag time before the form appears.)

Email Template

Template Name: Update Notification
Type: Case Templates (rather than an SLA template)
From Name: Support System
From Email Address: $custom.support_team_email_address
Subject: Case Record Updated
Template Variables:
Case Record Variables:
$cases.case_number, $cases.modified_id.full_name, $cases.description
Current Note Variable (case history): $__current_note__.description
Custom Variable: $custom.support_team_email_address

Here is a sample Update Notification template that uses those variables:

Case Record# $cases.case_number
was updated by $cases.modified_id.full_name
$__current_note__.description
Case Description:
$cases.description

The source code looks like this:

<p><b>Case Record#</b> <a href="https://{domain}/networking/servicedesk/index.jsp#_cases/$cases.case_number">$cases.case_number</a>&nbsp;<br />
was updated by $cases.modified_id.full_name</p>
<blockquote>
    $__current_note__.description
</blockquote>

<p><b>Case Description:</b></p>
<blockquote>
    $cases.description
</blockquote>

Code

package com.platform.acme.demo;

// Basic imports
import com.platform.api.*;
import java.util.*;

// Reference static functions without having to specify the Functions class.
import static com.platform.api.Functions.*;

import static com.platform.api.CONSTANTS.*;

/**
 * Prerequisites: 
 *   1. There is a Main object that contains records (e.g. cases) that people 
 *      want to "follow"--so they receive a notification when there is an update.
 *   2. There is an object that has related records, one for each person who is 
 *      following records in the Main object. This sample code assumes that the
 *      related object is called "Followers".
 *   3. A related record is added to that object when someone becomes a follower.
 *   4. That object has a Lookup field that points to the thing being followed,
 *      and an email address to send the notification to.
 *
 * Record Management:
 *   a. For initial testing, some "related records" can be created by hand.
 *   b. In a production setting, a "Follow" action will add a related record.
 *   c. To "unfollow", a user can easily delete the related record.
 *   d. Ideally, the Form will have OnLoad JavaScript that uses the REST APIs to
 *      see if the current user is already a follower, and sets the checkbox state 
 *      or state of the buttons accordingly.
 *
 * Usage:
 *   An update Rule on the main object should invoke the notifyFollowers()
 *   method defined in this class. The method finds all related records and
 *   sends the messages, using an email template defined for the notifications.
 */
public class Notifications
{

   public void log(String msg) throws Exception {
     // Put the message in the log.
     Logger.info(msg, "SendMailToFollowers");
   }
        
   public void debug(String msg) throws Exception {
     // Display message in a popup or at top of page, depending on context, and log it.
     Functions.showMessage(msg);
     log(msg);
   }

   public void notifyFollowers(Parameters p) throws Exception
   {
      try {
         // Get information from the current record
         String objectID = p.get("object_id");
         String recordID = p.get("id");  
            
         // Records that point to this one have this in their "related_to" field.
         String lookup_target_record = objectID +":"+ recordID;

         // Message contents. (To get the template ID, modify the templates view
         // to include the Record ID field.)
         String subject = "Record Update Notification";
         String ccList = "";
         String bodyTemplateId = "__ID of the notification template goes here__";
         String relatedObject = "Followers";
              // This is the name of the object with the related records
         
         // Find all related records whose Lookup field targets the current record
         // Format of the criteria string is: related_to = '{object}:{record}'
         Result result = Functions.searchRecords(relatedObject, 
            "email_address", "related_to = '" + lookup_target_record +"'");
         int resultCode = result.getCode(); 
         if (resultCode > 0)
         {
            // Process the records that were found.
            // (If getCode() has a positive number, it was the number found.)
            int count = 0;
            ParametersIterator iterator = result.getIterator();
            while(iterator.hasNext())
            {
               Parameters params = iterator.next();
               String toAddress = params.get("email_address");

               // Send message (nulls are parameters for attachments)
               Result sendEmail = Functions.sendEmailUsingTemplate(
                  objectID, recordID, toAddress, ccList, subject, bodyTemplateId, 
                  null, null);
               if (sendEmail.getCode() < 0)
               {  
                  String msg = "Error sending email: " + sendEmail.getMessage();
                  Functions.throwError(msg);      
               }
               count++;
            }
            debug(count + " update notifications sent"); // Count is one fo
         }
     } catch (Exception e) {
         // Catch surprises, display a popup, and put them in the log.
         String msg = "Unexpected exception";
         log(msg + ":\n" + e.getMessage() );
         Functions.throwError(msg + " - see debug log");      
      }
   }
   
   /*
    * Usage: This method should be called by Macro (i.e. a record Action)
    * on the record that the person wants to follow. (The form that displays
    * the record should also have a Related Information section, so the user
    * can remove their record.
    */
   public void addFollower(Parameters p) throws Exception
   {
     try {
         // Set up the lookup target
         String objectID = p.get("object_id");
         String recordID = p.get("id");  
         String lookup_target_record = objectID +":"+ recordID;
         
         // Get the current user's email address and name
         String email = Functions.getEnv(ENV.USER.EMAIL);
         String name  = Functions.getEnv(ENV.USER.FULL_NAME);
         
         // For extra credit, exit here if a record already exists with those values
         // Additional credit: Make the adjustments to display and store user's name

         Parameters params = Functions.getParametersInstance();
         params.add("email_address", email);
         params.add("related_to", lookup_target_record);
         params.add(PLATFORM.PARAMS.RECORD.DO_NOT_EXEC_RULES,"1");
         Functions.addRecord("Followers", params);
     } 
     catch (Exception e) {
         // Catch surprises, display a popup, and put them in the log.
         String msg = "Unexpected exception";
         log(msg + ":\n" + e.getMessage() );
         Functions.throwError(msg + " - see debug log");      
      }
   }
   
} // end class