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
Eager-2-LearnEager-2-Learn 

Automatically assign a new user to a Chatter Group

Hi,

I am trying to automatically assign a new user to a Chatter Group called 'Everyone'.  The code below works as long as I do not select a role; however, when I select a role I get the following error.  Does anyone have a clue what this error means or if there is a code that can do what I am trying to do?

 

22:29:46.425
(425800000)|EXCEPTION_THROWN|[24]|System.DmlException: Insert failed.
First exception on row 0; first error: MIXED_DML_OPERATION, DML
operation on setup object is not permitted after you have updated a
non-setup object (or vice versa): CollaborationGroupMember, original
object: User: []
22:29:46.426
(426685000)|FATAL_ERROR|System.DmlException: Insert failed. First
exception on row 0; first error: MIXED_DML_OPERATION, DML operation on
setup object is not permitted after you have updated a non-setup object
(or vice versa): CollaborationGroupMember, original object: User: []

 

 

 

trigger User_trigger on User (after insert, before insert, after update, before update) {
    if ( Trigger.isInsert ) {    
        if ( Trigger.isAfter ) {
            List<CollaborationGroupMember> cgm = new List<CollaborationGroupMember>();              
            Id cgID = [ Select Id 
                      FROM CollaborationGroup 
                      WHERE Name = 'Everyone' LIMIT 1 ].ID;
System.debug('zzz: ' + cgID);    
            for ( user u: Trigger.new ) {
                cgm.add(new CollaborationGroupMember (CollaborationGroupId = cgID, MemberId = u.id));    
            }    
            insert cgm;          
        }
    }
}

 



Best Answer chosen by Admin (Salesforce Developers) 
grigri9grigri9

You need to bulkify your code so that it only calls the @future method once and only runs once and the cgm inserts once. Just put your for loop inside of the @future method and pass trigger.newmap.keyset() to that method. Something like this:

 

 

global class AsyncApex {
    @future
    public static void AddUserToGroup(list<ID> Users) {
        try {
            List<CollaborationGroupMember> cgm = new List<CollaborationGroupMember>();
            Id cgID = [ Select Id 
                        FROM CollaborationGroup 
                        WHERE Name = 'Everyone' LIMIT 1 ].ID;
            for(ID userID : Users){                          
               cgm.add(new CollaborationGroupMember (CollaborationGroupId = cgID, MemberId = userID));   
            } 
            insert cgm;
        }
        catch (QueryException qe) {
            System.debug('QueryException in AsyncApex.AddUserToGroup is :' + qe);  
        } 
        catch (Exception ex) {
            System.debug('Exception in AsyncApex.AddUserToGroup is :' + ex);
        }    
    }
}

 

As for #2 you just need to insert a user in a testmethod and then check that the @future has run after a test.StopTest. I.E.

 

  static testMethod void myTest() {
       User u = new User(....);
       Test.startTest();
       insert u;
       Test.stopTest();
list<collaborationgroupmember> cgm = [select id from CollaborationGroupMember where group.name='Everyone' and userid=:u.ID];
//this assert happens after test.stoptest() because asynchrnous methods run when they hit test.stoptest() System.assert(cgm.size(),1);
}

 

All Answers

Eager-2-LearnEager-2-Learn

1. Ok--this is what I came up with and it seems like it works; however, I would appreciate any community feedback about the code.  I want to make sure that by doing this it does not break anything within the SFDC ORG. 

 

2. In addition, I may need some help with a test script since I struggle with those and now this would be my first test script working with @future.


So if feedback is thumbs up on #1 above I would appreciate help with step 2.  Maybe others have a all user group and you get tired of adding them manually when you setup the user.  if this code doesnt inadvertantly cause additional issues I believe I will need to incorporate logic to prevent Customer Users from going into the Everyone Group.  I have not played with that yet????

 

trigger User_trigger on User (after insert, before insert, after update, before update) {
    if ( Trigger.isInsert ) {    
        if ( Trigger.isAfter ) { 
            for ( user u: Trigger.new ) { 
                id userId = u.id;
                asyncApex.addUserToGroup(userId);                  
            }              
        }
    }
}

 

 

global class AsyncApex {
    @future
    public static void AddUserToGroup(id userId ) {
        try {
            List<CollaborationGroupMember> cgm = new List<CollaborationGroupMember>();                      
            Id cgID = [ Select Id 
                        FROM CollaborationGroup 
                        WHERE Name = 'Everyone' LIMIT 1 ].ID;
           cgm.add(new CollaborationGroupMember (CollaborationGroupId = cgID, MemberId = userId));   
           insert cgm;
        } catch (QueryException qe) {
            System.debug('QueryException in AsyncApex.AddUserToGroup is :' + qe);  
        } catch (Exception ex) {
            System.debug('Exception in AsyncApex.AddUserToGroup is :' + ex);
        }    
    }
}

 

Again, I appreciate all feedback--thanks.

grigri9grigri9

You need to bulkify your code so that it only calls the @future method once and only runs once and the cgm inserts once. Just put your for loop inside of the @future method and pass trigger.newmap.keyset() to that method. Something like this:

 

 

global class AsyncApex {
    @future
    public static void AddUserToGroup(list<ID> Users) {
        try {
            List<CollaborationGroupMember> cgm = new List<CollaborationGroupMember>();
            Id cgID = [ Select Id 
                        FROM CollaborationGroup 
                        WHERE Name = 'Everyone' LIMIT 1 ].ID;
            for(ID userID : Users){                          
               cgm.add(new CollaborationGroupMember (CollaborationGroupId = cgID, MemberId = userID));   
            } 
            insert cgm;
        }
        catch (QueryException qe) {
            System.debug('QueryException in AsyncApex.AddUserToGroup is :' + qe);  
        } 
        catch (Exception ex) {
            System.debug('Exception in AsyncApex.AddUserToGroup is :' + ex);
        }    
    }
}

 

As for #2 you just need to insert a user in a testmethod and then check that the @future has run after a test.StopTest. I.E.

 

  static testMethod void myTest() {
       User u = new User(....);
       Test.startTest();
       insert u;
       Test.stopTest();
list<collaborationgroupmember> cgm = [select id from CollaborationGroupMember where group.name='Everyone' and userid=:u.ID];
//this assert happens after test.stoptest() because asynchrnous methods run when they hit test.stoptest() System.assert(cgm.size(),1);
}

 

This was selected as the best answer
Eager-2-LearnEager-2-Learn

Thanks for the heads up on Bulkify!  I have an error though at public static void AddUserToGroup(List<User> Users) {

 

I says: Unsupported parameter type LIST<User>

 

I even tried other objects such as Account and contact and I get the same error.

 

I am calling the method like this...

 

asyncApex.addUserToGroup(Trigger.new);

 

Any ideas?  I am still trying to figure it out but I wonder if @future allows these types of objects to be passed!!!!  I quickly tried removing the @future and it compiled so @future doesn't like these types being passed in!!!!

 

Editing hear again!!!

Just found this -- so I don't think I can do what you are suggesting!  What say you?

 

  • The parameters specified must be primitive dataypes, arrays of primitive datatypes, or collections of primitive datatypes.
  • Methods with thefutureannotation cannot take sObjects or objects as arguments.

But then again, talking out loud I can create all the IDS in a collection or array and pass that???

grigri9grigri9

Sorry, forgot about that. You can only pass a primitive, sobject, or list of primitives to @future functions. Just change your @future function to take a list of ids and pass it trigger.newmap.keyset().

Eager-2-LearnEager-2-Learn

That is too funny you just beat me posting pretty much the same thing!  Look at the previous post that I edited.

 

Thanks for your help I will work on that.  Another thing that i realized that I have to do.  I need to not process Chatter External (Customer Chatter) users.  Since this the Everyone Group is only for internal users.

 

If I run into any more issues I will be sure to reach out to you.  Thanks again for the Bulkify solution and the test script.  I haven't got to the test script yet! :)

Eager-2-LearnEager-2-Learn

Here is the final code that I unit tested and it works in case anyone else is looking for this solution.  Thanks again for your help on the Bulkify. I forget about that sometimes.  New concept for me along with coded test scripts too! :)

 

trigger User_trigger on User (after insert, before insert, after update, before update) {
    if ( Trigger.isInsert ) {    
        if ( Trigger.isAfter ) { 
            List<id> UserIds = new List<id>();
            for ( user u: Trigger.new ) {
                if ( u.Profile.UserLicense.Name <> 'Chatter External' ) {
                    UserIds.add(u.id);
                }                 
            } 
            if ( UserIds.size() > 0 ) {
                asyncApex.addUserToGroup(UserIds);  
            }           
        }
    }
}

 

global class AsyncApex {
    @future
    public static void AddUserToGroup(List<ID> UserIds) {
        try {
            List<CollaborationGroupMember> cgm = new List<CollaborationGroupMember>();                      
            Id cgID = [ Select Id 
                        FROM CollaborationGroup 
                        WHERE Name = 'Everyone' LIMIT 1 ].ID;
           for ( Id UserId : UserIds ) {
               cgm.add(new CollaborationGroupMember (CollaborationGroupId = cgID, MemberId = UserId));   
           }
           insert cgm;
        } catch (QueryException qe) {
            System.debug('QueryException in AsyncApex.AddUserToGroup is :' + qe);  
        } catch (Exception ex) {
            System.debug('Exception in AsyncApex.AddUserToGroup is :' + ex);
        }    
    }
}

 

 

Matt O'HaraMatt O'Hara
Thank you for this! I am just getting started with Apex myself and I think this will be one of my first projects. :)
Eduardo TamuraEduardo Tamura
Hi all,

do you know if it works for Community Groups too?

Thanks in advance!
CarbonCarbon
Nice work!