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.
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);
}
}
Here is the Last Chatter Date field definition:
<fields>
<fullName>LastChatterDate__c</fullName>
<description>Used to record the date and time of the last chatter post or comment.</description>
<externalId>false</externalId>
<inlineHelpText>The date and time of the last Chatter post or comment.</inlineHelpText>
<label>Last Chatter Date</label>
<required>false</required>
<trackFeedHistory>false</trackFeedHistory>
<type>DateTime</type>
</fields>
Discussion
You will need the LastChatterDate__c field on the Account and Contact sObject for the unit test to work.
Recipe Activity - Please Log in to write a comment