Last Chatter Date
Anyone who happens to know about this code and perhaps use it...
Or anyone who can read it and gets what it does...
- IF a (TRIGGERS) Chatter Feed Item or Feed Comment is inserted
- AND (CLASS) the PARENT record of that Chatter has a "LastChatterDate__c" datetime field
- THEN (cLASS) stamp NOW() to that field
- ELSE do nothing
Do you know.. can you figure out.. can you advise on WHY it will MOSTLY WORK, but not 100% of the time?
I have tried multiple times to see "what" causes it, and it's just not consisten. It works great MOST of the time, then just occasionally does not update the field. Sometimes from an original Feed post, other times from a Feed comment. No consistency that I can find on my own.
Help!
ADDED INFO - WHEN EVENT DOES NOT OCCUR, ATTEMPT TO FORCE IT WITH SECOND CHATTER POST
Note that when this happens (the fields does not update after a chatter item is added), I will add another chatter item ("x"), and MOST of the time (again) it WILL stamp the datetime then - but not always. Sometimes it fails multiple times in a row - MOST of the time it works constantly. Adding screenshot of data where MOSTLY it's one attempt stamping it.
Actual code stored here from SF Dev Site:
Problem
The last Chatter date is not easy to get to when writing reports, formulas, or custom UIs. If only there was a field on the sObject that had the last Chatter date that automatically updates when users post on a feed or comment on a post.
Solution
Build a trigger that saves the last Chatter date on the parent sObject if it has a last chatter date field. Architecture: The ChatterDate class is a small class with one static method called updateChatterTimestamp. The static method is called by a Feed Item and Feed Comment trigger with one parameter, the trigger.newMap. The ChatterDate class is generic so all you need to do is add the LastChatterDate__c field to an sObject that is Chatter enabled.
Discussion
You will need the LastChatterDate__c field on the Account and Contact sObject for the unit test to work.
public class ChatterDate {
private static final String LAST_CHAT_DATE_FIELD_NAME = 'LastChatterDate__c';
public static void updateChatterTimestamp(map<Id, Sobject> sobjectMap) {
try {
// Get the global schema
map<String, Schema.Sobjecttype> globalDescribe = Schema.getGlobalDescribe();
// A list to put our sObjects in so we can bulk update them
List<SObject> sobjList = new List<SObject>();
// Iterate through the sObjectMap param
for(Sobject obj : sobjectMap.values()) {
String parentId = String.valueOf(obj.get('ParentId'));
// Iterate through the sObject types and find our parent sObject type by keyPrefix
for(schema.Sobjecttype objToken : globalDescribe.values()) {
DescribeSObjectResult objDef = objToken.getDescribe();
if(null != objDef.keyPrefix && parentId.startsWith(objDef.keyPrefix)) {
// We found our sObject type now check if it has the magic field
if(objDef.fields.getMap().containsKey(LAST_CHAT_DATE_FIELD_NAME)) {
// This sObject type has the magic field so create a new sObject with the
// parentId and set the last chatter date to the crated date of the
// the FeedItem or FeedComment)
Sobject targetObj = objToken.newSObject(parentId);
targetObj.put(LAST_CHAT_DATE_FIELD_NAME, (Datetime) obj.get('CreatedDate'));
sobjList.add(targetObj);
}
// We found our sObject so break out
break;
}
}
}
if(!sobjList.isEmpty()) {
// Call the update DML method, try to save what we can and deal with the exceptions
List<Database.SaveResult> saveResultList = database.update(sobjList, false);
// Iterate through the Save Results
for(Database.SaveResult sr:saveResultList) {
if(!sr.isSuccess()) {
// Validation rules and other unknown things can
// cause exceptions so deal with it as you see fit
}
}
}
} catch(system.Exception ex) {
// an unknown exception
}
}
}
The Triggers
trigger FeedItemLastChatterDate on FeedItem (after insert) {
ChatterDate.updateChatterTimestamp(trigger.newMap);
}
trigger FeedCommentLastChatterDate on FeedComment (after insert) {
ChatterDate.updateChatterTimestamp(trigger.newMap);
}
And no sample is complete without a nice unit test.
@isTest
private class ChatterDateTest {
static testMethod void singleUnitTest() {
Account testAccount = new Account(Name='TestAccount');
insert testAccount;
FeedItem post = new FeedItem();
post.parentId = testAccount.Id;
post.body = 'Test chatter post';
FeedComment comment = new FeedComment();
comment.CommentBody = 'Test chatter comment on test post';
test.startTest();
insert post;
comment.FeedItemId = post.Id;
insert comment;
test.stopTest();
Account resultAccount = [SELECT LastChatterDate__c FROM Account WHERE Id = :testAccount.Id];
AccountFeed resultFeed = [Select (Select Id, FeedItemId, ParentId, CreatedDate From FeedComments Where Id = :comment.Id) From AccountFeed Where Id = :post.Id];
system.assertEquals(resultAccount.LastChatterDate__c, resultFeed.FeedComments[0].CreatedDate);
}
static testMethod void multiUnitTest() {
Account testAccount = new Account(Name='TestAccount');
insert testAccount;
Contact testContact = new Contact(LastName='TestContact');
insert testContact;
List<FeedItem> feedItemList = new List<FeedItem>();
FeedItem accountPost = new FeedItem();
accountPost.parentId = testAccount.Id;
accountPost.body = 'Test chatter post';
feedItemList.add(accountPost);
FeedItem contactPost = new FeedItem();
contactPost.ParentId = testContact.Id;
contactPost.body = 'Test chatter post';
feedItemList.add(contactPost);
test.startTest();
insert feedItemList;
test.stopTest();
Account resultAccount = [SELECT LastChatterDate__c FROM Account WHERE Id = :testAccount.Id];
AccountFeed resultAccountFeed = [SELECT CreatedDate FROM AccountFeed WHERE Id = :accountPost.Id];
system.assertEquals(resultAccountFeed.CreatedDate, resultAccount.LastChatterDate__c);
Contact resultContact = [SELECT LastChatterDate__c FROM Contact WHERE Id = :testContact.Id];
ContactFeed resultContactFeed = [SELECT CreatedDate FROM ContactFeed WHERE Id = :contactPost.Id];
system.assertEquals(resultContactFeed.CreatedDate, resultContact.LastChatterDate__c);
}
}
UPDATE - DIFFERENCES between the raw code and my copy of it | Just added prefixes to the code labels. "PlascoID_"
MAIN CLASS
public class ChatterDate{ changed to public class PlascoID_ChatterDate {
(no other changes)
BOTH TRIGGERS
trigger FeedItemLastChatterDate on FeedItem (after insert) { changed to trigger PlascoID_FeedItemLastChatterDate on FeedItem (after insert) {
(same change to 'Comment' trigger on FeedComment)
BOTH TRIGGERS
ChatterDate.updateChatterTimestamp(trigger.newMap); changed to PlascoID_ChatterDate.updateChatterTimestamp(trigger.newMap);
That's it.