+ Start a Discussion
Tim May 12Tim May 12 

Mixed DML Error Auth.ConfigurableSelfRegHandler Issue

Hello,

I have implemented a Configurable Self Registration Handler in a Community and used the Auth.ConfigurableSelfRegHandler to extend how the User / Contact is created.

I want the Contact that is made to have a particular RecordType. I updated the method generateContact() to either look for a Contact or create a new Contact. I started with the example code that is created when you enable a handler in the Community.

Here is the error I am getting:

Update failed. First exception on row 0 with id <ID>; first error: MIXED_DML_OPERATION, DML operation on setup object is not permitted after you have updated a non-setup object (or vice versa): User, original object: Contact: []

I know the User object is a setup object but I figured Site.createExternalUser() would be able to bypass that. 

Thanks for the assistance, code is below. 
 
global class AutocreatedConfigSelfReg implements Auth.ConfigurableSelfRegHandler {

    private final Long CURRENT_TIME = Datetime.now().getTime();
    private final String[] UPPERCASE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
    private final String[] LOWERCASE_CHARS = 'abcdefghijklmnopqrstuvwxyz'.split('');
    private final String[] NUMBER_CHARS = '1234567890'.split('');
    private final String[] SPECIAL_CHARS = '!#$%-_=+<>'.split('');

    // This method is called once after verification (if any was configured).
    // This method should create a user and insert it.
    // Password can be null.
    // Return null or throw an exception to fail creation.
    global Id createUser(Id accountId, Id profileId, Map<SObjectField, String> registrationAttributes, String password) {
        User u = new User();
        u.ProfileId = profileId;
        for (SObjectField field : registrationAttributes.keySet()) {
            String value = registrationAttributes.get(field);
            u.put(field, value);
        }

        u = handleUnsetRequiredFields(u);
        Contact cnct = generateContact(u, accountId);
        if (cnct.Id != null) {
            u.ContactId = cnct.Id;
        }
        if (String.isBlank(password)) {
            password = generateRandomPassword();
        }
        Site.validatePassword(u, password, password);
        if (u.contactId == null) {
            return Site.createExternalUser(u, accountId, password, True);
        }
        u.languagelocalekey = UserInfo.getLocale();
        u.localesidkey = UserInfo.getLocale();
        u.emailEncodingKey = 'UTF-8';
        u.timeZoneSidKey = UserInfo.getTimezone().getID();
        insert u;
        System.setPassword(u.Id, password);
        return u.id;
    }
    // Method to autogenerate a password if one isn't passed in.
    // By setting a password for a user, we won't send a 
    // welcome email to set the password.
    private String generateRandomPassword() {
        String[] characters = new List<String>(UPPERCASE_CHARS);
        characters.addAll(LOWERCASE_CHARS);
        characters.addAll(NUMBER_CHARS);
        characters.addAll(SPECIAL_CHARS);
        String newPassword = '';
        Boolean needsUpper = true, needsLower = true, needsNumber = true, needsSpecial = true;
        while (newPassword.length() < 50) {
            Integer randomInt = generateRandomInt(characters.size());
            String c = characters[randomInt];
            if (needsUpper && c.isAllUpperCase()) {
                needsUpper = false;
            } else if (needsLower && c.isAllLowerCase()) {
                needsLower = false;
            } else if (needsNumber && c.isNumeric()) {
                needsNumber = false;
            } else if (needsSpecial && !c.isAlphanumeric()) {
                needsSpecial = false;
            }
            newPassword += c; 
        }
        newPassword = addMissingPasswordRequirements(newPassword, needsLower, needsUpper, needsNumber, needsSpecial);
        return newPassword;
    }

    private String addMissingPasswordRequirements(String password, Boolean addLowerCase, Boolean addUpperCase, Boolean addNumber, Boolean addSpecial) {
        if (addLowerCase) {
            password += LOWERCASE_CHARS[generateRandomInt(LOWERCASE_CHARS.size())];
        }
        if (addUpperCase) {
            password += UPPERCASE_CHARS[generateRandomInt(UPPERCASE_CHARS.size())];
        }
        if (addNumber) {
            password += NUMBER_CHARS[generateRandomInt(NUMBER_CHARS.size())];
        }
        if (addSpecial) {
            password += SPECIAL_CHARS[generateRandomInt(SPECIAL_CHARS.size())];
        }
        return password;
    }
   // Generates a random number from 0 up to, but not including, max.
    private Integer generateRandomInt(Integer max) {
        return Math.mod(Math.abs(Crypto.getRandomInteger()), max);
    }

    // Loops over required fields that were not passed in to 
    // set to some default value.
    private User handleUnsetRequiredFields(User u) {
        if (String.isBlank(u.LastName)){
            u.LastName = generateLastName();
        }
        if (String.isBlank(u.Username)) {
            u.Username = generateUsername();
        }
        if (String.isBlank(u.Email)) {
            u.Email = generateEmail();
        }
        if (String.isBlank(u.Alias)) {
            u.Alias = generateAlias();
        }
        if (String.isBlank(u.CommunityNickname)) {
            u.CommunityNickname = generateCommunityNickname();
        }
        return u;
    }
    
    // Method to construct a contact for a user.
    private void generateContact(User u, Id accountId) {
    
        Contact cntct;
        
        // Contacts from the Community will have an additional contact record type
        List<RecordType> contactRecordTypes = [SELECT Id FROM RecordType
                                               WHERE SObjectType = 'Contact' and DeveloperName = 'Community_Contact'];

        // First step, look to see if there is a Contact already
        List<Contact> contactCheck = [SELECT Id, FirstName, LastName, AccountId
                                      FROM Contact WHERE Email = :u.Email];

        // There is one and only one Contact that we found, so return it.
        if (contactCheck.isEmpty() == false && contactCheck.size() == 1) {

            System.debug('Found an existing Contact based on Email Address');

            // Update the contact record with the information from the registration form
            // but make sure to not adjust the Account Id for security reasons.
            cntct = contactCheck.get(0);
            cntct.RecordTypeId = contactRecordTypes.get(0).Id;
            cntct.Active_Contact__c = true;
            cntct.LeadSource = 'Salesforce Community';
            cntct.FirstName = u.FirstName;
            cntct.LastName = u.LastName;
            update cntct;
        
        } else {
        
            // There was no Contact record found, so we need to make one
            // and put it in the right location.

            System.debug('Creating a new Contact');
            cntct.RecordTypeId = contactRecordTypes.get(0).Id;
            cntct.AccountId = accountId;
            cntct.Active_Contact__c = true;
            cntct.LeadSource = 'Salesforce Community';
            cntct.FirstName = u.FirstName;
            cntct.LastName = u.LastName;
            cntct.Email = u.Email;
            insert cntct;

        }

        System.debug('cntct: ' + cntct);
        return cntct;

    }
    // Default implementation to try to provide uniqueness.
    private String generateAlias() {
        String timeString = String.valueOf(CURRENT_TIME);
        return timeString.substring(timeString.length() - 8);
    }
    // Default implementation to try to provide uniqueness.
    private String generateLastName() {
        return 'ExternalUser' + CURRENT_TIME;
    }
    // Default implementation to try to provide uniqueness.
    private String generateUsername() {
        return 'externaluser' + CURRENT_TIME + '@company.com';
    }
    // Default implementation to try to provide uniqueness.
    private String generateEmail() {
        return 'externaluser' + CURRENT_TIME + '@company.com';
    }
    // Default implementation to try to provide uniqueness.
    private String generateCommunityNickname() {
        return 'ExternalUser' + CURRENT_TIME;
    }
}

 
Shweta Jha 20Shweta Jha 20
Hey,

Did you find any solution for this issue.

Thanks