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
ctonectone 

Please help confirm odd results

I'm hoping that some one can spend a few minutes and verify the results that I am seeing here and hopefully, before I pull out what little hair I have left, tell me where I am going wrong.

I am attempting to swap a user's profile using Apex Code but I am receiving this error message:
Code:
System.DmlException: Update failed. First exception on row 0 with id 005500000011fHiAAI; first error: FIELD_INTEGRITY_EXCEPTION, 
This profile is used by a user who is a delegated admin and must have the view setup permission: Profile ID: [ProfileId]
 
I do not have any delegated admins defined AND the two profiles in question are unused (except by the user in this test case). 

Here is the Apex Class.  The test method runs w/o issue.
Code:
global class ProfileSwap {
    
    public static String origProfile = 'Sales-Test-A';
    public static String altProfile = 'Sales-Test-B';
    public static String targetUserEmail = 'someone@somewhere.com';
    
    public ProfileSwap(){}
    
    /* swaps current profile for a pre-determined alternate */
    public void swap(ID userID){
        
        //-- load current user profile
        User u = [select id, profileId from User where id =: userId];
        if( u == null ) throw new ValidationException('Unable to load user with id '+userId);
        System.debug('>>>>>> Loaded user '+u);
        
        Map<ID,ID> profileMap = getProfileMap();
        if( !profileMap.containsKey(u.profileId) ) throw new ValidationException('Unknow profile, cannot swap');
         
        //-- update w/ new profile
        u = new User(id=u.id, profileId = profileMap.get(u.profileId));
        System.debug('>>>>>> Updating user: '+u);
        update u;
    }
    
    /* returns mapping between current and target profiles */
    public static Map<ID, ID> getProfileMap(){
        //-- load target profiles
        ID origProfileId = [select Id from Profile where name =: origProfile].id;
        ID altProfileId = [select Id from Profile where name =: altProfile].id;
        
        Map<ID,ID> profileMap = new Map<ID,ID>();
        profileMap.put(origProfileId, altProfileId);
        profileMap.put(altProfileId, origProfileId);
        
        return profileMap;
    }

    public static testMethod void test(){
        
        User u = [select id, profileId, profile.name from User where email =: targetUserEmail];
        
        //-- mapping of profile to swap from -> to
        Map<ID,ID> profileMap = getProfileMap();
        
        //-- expected resulting id
        ID targetId = profileMap.get(u.profileId);
        String startProfile = u.profile.name;
        
        //-- swap profiles
        new ProfileSwap().swap(u.id);
        
        //-- verify
        u = [select id, profileId, profile.name from User where email =: targetUserEmail];
        System.debug('Orig Profile: '+startProfile+'; Swapped Profile: '+u.profile.name);
        System.assert(targetId == u.profileId, 'Expected target profile "'+targetId+'", got "'+u.profileId+'"');
    }
    
    webservice static void doIt(String userId){
        new ProfileSwap().swap(userId);
    }
}
 

The 'doIt' method is invoked by an OnClick javascript link on the user's home page.  Adding 'without sharing' to the ProfileSwap class does not affect anything.
Code:
{!requireScript("/soap/ajax/14.0/connection.js")}
{!requireScript("/soap/ajax/14.0/apex.js")}
  try{
        var userId = "{!$User.Id}";
 sforce.apex.execute("ProfileSwap", "doIt", {userId:userId});

  } catch(e){ alert(e); }

Thanks for your time.

--Chris
MikeGoelzerMikeGoelzer
Chris,

Confirmed -- I ran the code and got the same result.

I think the reason it fails may be that a user cannot change his or her own profile. There seems to be a restriction in the salesforce application itself that blocks this sort of thing. (Try changing your own profile manually right now.) Bug or feature, I don't know, but could you be running into the same issue?

Post more about what you're trying to do. If you modified your Javascript to get a fresh login sid for some designated admin user, the button should work for everybody else.

Mike
ctonectone
Mike-

Thanks for taking the time to look at this for me.  I think your assessment is correct and it's just a limitation/feature of Salesforce that a user cannot change his own profile.

Running the ApexCode as a separate user would seem to get around the limitation but would I have to log that user in using the sforce.connection.login(...) javascript API method? 

The reason I am trying to do this is because we use two profiles for our sales reps: A 'standard' profile and a 'restricted' profile.  The restricted profile is used for reps who have too many neglected Leads. It forces them to update those leads before allowing them back into the remainder of the system.  An external process runs nightly and assigns the reps to the restricted profile if needed.  However the swap back to the standard profile must happen right after they finish updating their Leads instead of waiting for the next nightly profile update.

Thanks for your help.

--Chris
MikeGoelzerMikeGoelzer
Chris, could the nightly job just be set to run every 5 minutes? That strikes me as the simplest solution here.

Otherwise, yes, use the login() call in the AJAX toolkit. You just have to be careful about how you deploy, because you'll need to hardcode a username/password into the Javascript and anyone with read permission on that s-control will be able to see those credentials.