1

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.

Screenshot of Last Chatter Date being used


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.

  • Agreed, my bad. Doing so now... – AMM Feb 7 '17 at 19:11
  • Okay, okay, so I Block quoted it instead of using code format. (grin) Thanks. – AMM Feb 7 '17 at 19:30
2

It's possible there is an exception on save of the Account (validation rule possibly?) which is preventing the save and thus not updating the Last Chatter field. If you implemented the code exactly how it is on that page then you are not handling any exceptions and the execution completes successfully since the database.update(sobjList, allOrNone) parameter is set to false.

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
    }
  }
}

If this is the case, you will need to handle the exception with the appropriate action.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.