function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
swathiswathi 

Trigger on FeedItem

Hi,

I am trying to write a chatter trigger to repost to an account if it meets a certain criteria ?  

 

For example: if i post something aboutn # canada in my personal feed or in a group feed, it should automatically reposted to the account feed which is having account name as canada.

 

I have trigger a trigger on this.

 

trigger chatterfeed on FeedItem (after insert)
{

set<string> fbody= new set<string>();
for(feedItem f:trigger.new)
{
fbody.add(f.body);
}
List<FeedItem> feedItems = new List<FeedItem>();
list<account> a=[select id,name,ownerId from account ];

for(FeedItem f:trigger.new)
{

for(account ac:a){
if(f.body.contains(ac.name))
{
string s= f.body;
FeedItem fitem = new FeedItem();
fitem.type = 'LinkPost';
fitem.ParentId = ac.id;
system.debug(fitem.parentId+'IIIIIIIIII');
fitem.linkurl='https://ap1.salesforce.com/_ui/core/userprofile/UserProfilePage?u=00590000000NI6s&fId='+f.id;
fitem.Title = 'view';
fitem.Body=s;
system.debug(fitem.body+'BBBBBBBBB');
feedItems.add(fitem);
system.debug(feedItems+'FFFFFFFFf');
}
}
}

if(feedItems.size() > 0) {
try{
Database.insert(feedItems,false);
system.debug(feedItems+'OOOO');}
catch(exception e){}
}
}

 

I am getting internal salesforce error when I am fetching feetitem body. If I fetch feed Item id it is working fine.

 

Can anyone pls help me out its very urgent.

 

Thanks in advance.

 

 

 

Best Answer chosen by Admin (Salesforce Developers) 
izayizay

The problem here is that the trigger continues firing everytime the post is posted to the account causing an infinite loop.

 

Example: The initial post is: "My post with a tag #AccountName". The trigger finds #AccountName and re-posts the same post to that account. Now, the trigger fires again because of the new post, it finds #AccountName again and continues repeating this over and over.

 

To solve this problem you can create a class with a static variable and prevent the triger from runing more than once.

 

Class with static variable:

global class stopTriggers{
    
    public static Boolean firstRun = true;
    
}

 

I also noticed that your trigger queries all the account in your org, which if the org has more than 50,000 account the query will fail. Also it will post to accounts where the name is in the post even if these wheren't tagged with #. I tweaked you trigger to look for all the tags # in the post body. If a tag # is found, it will get the string following the tag until the next delimeter, which could be any character or a space.

 

Example:

Post = "Post about #XYZ Company, and #123 Company LLC,"

My delimeter is set to be ",". You can set the delimeter to be whatever you want.

From this post the trigger will get

  • XYZ Company
  • 123 Company LLC

Then, it will query for the accounts with these names. If the query returns any accounts the trigger will copy the post there.

 

trigger PostOnTagAcct on FeedItem (after insert) {
    Map<Id, List<String>> acctNames = new Map<Id, List<String>>();
    Map<Id, List<Account>> accounts = new Map<Id, List<Account>>();
    Map<Id, FeedItem> feedMap = new Map<Id, FeedItem>();
    List<FeedItem> feedItems = new List<FeedItem>();
    
    //If this is the first time the trigger runs...
    if(stopTriggers.firstRun){
        stopTriggers.firstRun = false;//Set first run to false to prevent consecutive runs
        
        for(feedItem f :trigger.new){
            String body = f.Body;//Get the feed body
            //Get all the strings with a tag '#' in the feed body and add them to the acctNames Map
            acctNames.put(f.Id, getTaggedStrings(body, 0));//key: FeedItem.Id, value: List of tagged strings
            feedMap.put(f.Id, f);//Add the feed item to the feed map
        }
        //For each id in the acctNames map...
        for(Id id :acctNames.keySet()){
            //Query any account where the name is any of the tagged strings
            List<Account> temp = [SELECT Id, Name, OwnerId from Account WHERE Name IN:acctNames.get(id)];
            //If records are returned, add these to the accounts map
            if(temp.size() > 0)
                accounts.put(id,temp);//key: FeedItem.Id, value: List of returned accounts
        }
        //For each id in the acctNames map...
        for(Id id :acctNames.keySet()){
            //If the accounts map contains this key
            if(accounts.containsKey(id)){
                //For each account in the accounts map under this key
                for(Account a :accounts.get(id)){
                    //Create a copy of the post
                    FeedItem fitem = new FeedItem();
                    fitem.type = 'LinkPost';
                    fitem.ParentId = a.id;
                    system.debug(fitem.parentId+'IIIIIIIIII');
                    fitem.linkurl='https://ap1.salesforce.com/_ui/core/userprofile/UserProfilePage?u=00590000000NI6s&fId='+id;
                    fitem.Title = 'View';
                    fitem.Body = feedMap.get(id).Body;
                    system.debug(fitem.body+'BBBBBBBBB');
                    feedItems.add(fitem);
                    system.debug(feedItems+'FFFFFFFFf');
                }
            }
        }
        //If there are feedItems to insert...
        if(feedItems.size() > 0) {
            //Insert them
            try{
                Database.insert(feedItems,false);
                system.debug(feedItems+'OOOO');}
            catch(exception e){}
        }
    }
    
    //This method finds all the tagged strings in the post and returns a list with these strings
    //@param body :The body of the post
    //@param index :The index of the last characted for the last tagged string found.
    //              This tells the method where to begin searching for tags
    //@return :List of tagged strings
    public List<String> getTaggedStrings(String body, Integer index){
        List<String> taggedNames = new List<String>();//List to return
        //Get the index of the first tag in the body starting from the given index
        Integer tagStartIndex = body.indexOf('#', index);
        Integer tagStopIndex;
        //This is the delimeter (character that denotes the end of the tagged string). Change this if necessary
        String delimeter = ',';
        //If a tag '#' is found...
        if(tagStartIndex != -1){
            //Get the index of the delimeter
            tagStopIndex = body.indexOf(delimeter, tagStartIndex);//Get the index of the first ',' after the #
            //Get the string between the startIndex and stopIndex removing the # and any trailing spaces
            String tag = body.substring(tagStartIndex + 1, tagStopIndex).trim();//Get the tagged string
            taggedNames.add(tag);//Add the tagged string to the list
            taggedNames.addAll(getTaggedStrings(body, tagStopIndex));//Look for more tagged strings in this post
        }
        return taggedNames;//Return the final list
    }
}

 Hope this helps!

 


Izay Ramos-Irizarry

All Answers

izayizay

The problem here is that the trigger continues firing everytime the post is posted to the account causing an infinite loop.

 

Example: The initial post is: "My post with a tag #AccountName". The trigger finds #AccountName and re-posts the same post to that account. Now, the trigger fires again because of the new post, it finds #AccountName again and continues repeating this over and over.

 

To solve this problem you can create a class with a static variable and prevent the triger from runing more than once.

 

Class with static variable:

global class stopTriggers{
    
    public static Boolean firstRun = true;
    
}

 

I also noticed that your trigger queries all the account in your org, which if the org has more than 50,000 account the query will fail. Also it will post to accounts where the name is in the post even if these wheren't tagged with #. I tweaked you trigger to look for all the tags # in the post body. If a tag # is found, it will get the string following the tag until the next delimeter, which could be any character or a space.

 

Example:

Post = "Post about #XYZ Company, and #123 Company LLC,"

My delimeter is set to be ",". You can set the delimeter to be whatever you want.

From this post the trigger will get

  • XYZ Company
  • 123 Company LLC

Then, it will query for the accounts with these names. If the query returns any accounts the trigger will copy the post there.

 

trigger PostOnTagAcct on FeedItem (after insert) {
    Map<Id, List<String>> acctNames = new Map<Id, List<String>>();
    Map<Id, List<Account>> accounts = new Map<Id, List<Account>>();
    Map<Id, FeedItem> feedMap = new Map<Id, FeedItem>();
    List<FeedItem> feedItems = new List<FeedItem>();
    
    //If this is the first time the trigger runs...
    if(stopTriggers.firstRun){
        stopTriggers.firstRun = false;//Set first run to false to prevent consecutive runs
        
        for(feedItem f :trigger.new){
            String body = f.Body;//Get the feed body
            //Get all the strings with a tag '#' in the feed body and add them to the acctNames Map
            acctNames.put(f.Id, getTaggedStrings(body, 0));//key: FeedItem.Id, value: List of tagged strings
            feedMap.put(f.Id, f);//Add the feed item to the feed map
        }
        //For each id in the acctNames map...
        for(Id id :acctNames.keySet()){
            //Query any account where the name is any of the tagged strings
            List<Account> temp = [SELECT Id, Name, OwnerId from Account WHERE Name IN:acctNames.get(id)];
            //If records are returned, add these to the accounts map
            if(temp.size() > 0)
                accounts.put(id,temp);//key: FeedItem.Id, value: List of returned accounts
        }
        //For each id in the acctNames map...
        for(Id id :acctNames.keySet()){
            //If the accounts map contains this key
            if(accounts.containsKey(id)){
                //For each account in the accounts map under this key
                for(Account a :accounts.get(id)){
                    //Create a copy of the post
                    FeedItem fitem = new FeedItem();
                    fitem.type = 'LinkPost';
                    fitem.ParentId = a.id;
                    system.debug(fitem.parentId+'IIIIIIIIII');
                    fitem.linkurl='https://ap1.salesforce.com/_ui/core/userprofile/UserProfilePage?u=00590000000NI6s&fId='+id;
                    fitem.Title = 'View';
                    fitem.Body = feedMap.get(id).Body;
                    system.debug(fitem.body+'BBBBBBBBB');
                    feedItems.add(fitem);
                    system.debug(feedItems+'FFFFFFFFf');
                }
            }
        }
        //If there are feedItems to insert...
        if(feedItems.size() > 0) {
            //Insert them
            try{
                Database.insert(feedItems,false);
                system.debug(feedItems+'OOOO');}
            catch(exception e){}
        }
    }
    
    //This method finds all the tagged strings in the post and returns a list with these strings
    //@param body :The body of the post
    //@param index :The index of the last characted for the last tagged string found.
    //              This tells the method where to begin searching for tags
    //@return :List of tagged strings
    public List<String> getTaggedStrings(String body, Integer index){
        List<String> taggedNames = new List<String>();//List to return
        //Get the index of the first tag in the body starting from the given index
        Integer tagStartIndex = body.indexOf('#', index);
        Integer tagStopIndex;
        //This is the delimeter (character that denotes the end of the tagged string). Change this if necessary
        String delimeter = ',';
        //If a tag '#' is found...
        if(tagStartIndex != -1){
            //Get the index of the delimeter
            tagStopIndex = body.indexOf(delimeter, tagStartIndex);//Get the index of the first ',' after the #
            //Get the string between the startIndex and stopIndex removing the # and any trailing spaces
            String tag = body.substring(tagStartIndex + 1, tagStopIndex).trim();//Get the tagged string
            taggedNames.add(tag);//Add the tagged string to the list
            taggedNames.addAll(getTaggedStrings(body, tagStopIndex));//Look for more tagged strings in this post
        }
        return taggedNames;//Return the final list
    }
}

 Hope this helps!

 


Izay Ramos-Irizarry

This was selected as the best answer
swathiswathi
Thank you so much for your quick reply. It is very useful
KymLeKymLe

Hi Izay,

 

Do you know if there's a way to extract the User Id from an @mentions within a trigger?

izayizay

Hi KymLe,

 

Yes, you can use the following code to retrieve the user id and user name from mentioned users in a chatter post:

 

trigger MentionsInPost on FeedItem (after insert) {
   
    List<FeedItem> atts = Trigger.new;//Get the FeedItem (post) being inserted
    String communityId = null;//The community id is null
    String feedItemId;//The id for the current feed item
    Map<Stirng, String> mentionedUsersMap = new Map<String, String>();//Map with user id as key, user name as value
    List<User> mentionedUsers = new List<User>();
    
    //For each FeedItem (post) being inserted...
    for(FeedItem fa:atts){
        feedItemId = fa.Id;//Get the feed item id
        ConnectApi.FeedItem feedItem = ConnectApi.ChatterFeeds.getFeedItem(communityId, feedItemId);//Get the feed item from ConnectApi
        List<ConnectApi.MessageSegment> messageSegments = feedItem.body.messageSegments;//Get the feed item message segments
        //For each segment in the feed item...
        for (ConnectApi.MessageSegment messageSegment : messageSegments) {
            //If the segment is a mention...
            if (messageSegment instanceof ConnectApi.MentionSegment) {
                //Get the data for the mention segment from the ConnectApi
                ConnectApi.MentionSegment mentionSegment = (ConnectApi.MentionSegment) messageSegment;
                //Add the mentioned user to the mentionedUsersMap(userid, username)
                mentionedUsersMap.put(mentionSegment.user.id, mentionSegment.name);
            }
        }
    }
    //Get the mentioned user records
    mentionedUsers = [SELECT Id, Name, Email FROM User WHERE Id IN :mentionedUsersMap.keySet()];
   //Do something here... }

Hope this helps!

KymLeKymLe

Awesome response Izay! 

 

One question:

 

Is there a way to throw and catch an error for just one User and process the rest?  I have an .addError but it stops the entire batch from going through.  I was under the assumption that the .addError would only apply to the single record.

 

Again, thanks!