AgileApps Support Wiki Pre Release

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

From AgileApps Support Wiki
imported>Aeric
imported>Aeric
Line 27: Line 27:
#*:* '''Fields to Link: Followers Field:''' Related To, '''links to Cases Field:''' ID
#*:* '''Fields to Link: Followers Field:''' Related To, '''links to Cases Field:''' ID
#*:* '''Fields to Display:''' User Name, Email<br>(App ID will stay hidden, as it is basically unreadable.)
#*:* '''Fields to Display:''' User Name, Email<br>(App ID will stay hidden, as it is basically unreadable.)
#*:* '''Sort by email:''' email - Ascending
#*:* '''Sort by:''' Email, '''Order:''' Ascending
#*:* Click '''[Save]'''.
#*:* Click '''[Save]'''.
#:
#:
Line 36: Line 36:
#* '''Description:''' Tell anyone who is following the record that a change has occurred.  
#* '''Description:''' Tell anyone who is following the record that a change has occurred.  
#* '''Run this Rule:''' Unconditionally
#* '''Run this Rule:''' Unconditionally
#* '''Actions to Perform:''' Invoke Method - Notifications - notifyFollowers
#* '''Actions to Perform:''' Invoke Method, '''Class:''' Notifications, '''Method:''' notifyFollowers
#* Click '''[Save]'''.
#* Click '''[Save]'''.
#:
#:

Revision as of 02:47, 25 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: user_name, email, app_id.
      (The app ID field will be needed later, when constructing a link to put into the email. Define it now, so it's there when you're ready for it.)
    • Click [Save] and then [Create].
  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.)
    • Click [Save].
  3. Modify the Case form to create a new tab that displays Followers.
    • Go to GearIcon.png > Objects > Cases > Forms > Agent Form
    • Click [New Related Information]:
      • Object: Followers
      • Fields to Link: Followers Field: Related To, links to Cases Field: ID
      • Fields to Display: User Name, Email
        (App ID will stay hidden, as it is basically unreadable.)
      • Sort by: Email, Order: Ascending
      • Click [Save].
  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, Class: Notifications, Method: notifyFollowers
    • Click [Save].
  6. Open a case record, click the Followers tab, and manually add yourself as a follower. (Fill in the user and email fields only).
  7. Update the case record and check your inbox for the notification message.
  8. Manually delete the test record (it's missing the App ID need to make the email template work well.)

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 sample code.
    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