+ Start a Discussion
Bryant Daniels 46Bryant Daniels 46 

Community Portal Self Registration Problem

Hello All,

I am currently having issues with allowing visitors to self-register in a lightning community portal. I have enabled "Person Accounts". Within in the site builder, I have the community configured to allow for self-registration and the profile is setup to the "Customer Community User Login" Profile. When I try to register I am receiving a error, "The error message is: portal account owner must have a role". I understand if I didn't have "Person Account" this would be fine because I can set that account with a user that has a role. The "Guest User" default for accounts is set to the "Person Account" record type, it is the only record type for that profile and it is the default RT. All of the files I am using are the ones that are created automatically when you create a custom community.  
Apex Controller:
global class LightningSelfRegisterController {

    public LightningSelfRegisterController() {

    }

    @TestVisible 
    private static boolean isValidPassword(String password, String confirmPassword) {
        return password == confirmPassword;
    }
    
    @TestVisible 
    private static boolean siteAsContainerEnabled(String communityUrl) {
        Auth.AuthConfiguration authConfig = new Auth.AuthConfiguration(communityUrl,'');
        return authConfig.isCommunityUsingSiteAsContainer();
    }
    
    @TestVisible 
    private static void validatePassword(User u, String password, String confirmPassword) {
        if(!Test.isRunningTest()) {
        Site.validatePassword(u, password, confirmPassword);
        }
        return;
    }
    
    @AuraEnabled
    public static String selfRegister(String firstname ,String lastname, String email, String password, String confirmPassword, String accountId, String regConfirmUrl, String extraFields, String startUrl, Boolean includePassword) {
        Savepoint sp = null;
        try {
            sp = Database.setSavepoint();
            
            if (lastname == null || String.isEmpty(lastname)) {
                return Label.Site.lastname_is_required;
            }
            
            if (email == null || String.isEmpty(email)) {
                return Label.Site.email_is_required;
            }
            
            User u = new User();
            u.Username = email;
            u.put('Email',email);
            
            u.FirstName = firstname;
            u.LastName = lastname;
            
            String networkId = Network.getNetworkId();

            // If using site to host the community the user should not hit s1 after logging in from mobile.
            if(networkId != null && siteAsContainerEnabled(Network.getLoginUrl(networkId))) {
                u.put('UserPreferencesHideS1BrowserUI',true);
            }
            
            String nickname = ((firstname != null && firstname.length() > 0) ? firstname.substring(0,1) : '' ) + lastname.substring(0,1);
            nickname += String.valueOf(Crypto.getRandomInteger()).substring(1,7);
            u.put('CommunityNickname', nickname);
                     
            if (extraFields != null) {
                List<Object> extraFieldsList = (List<Object>) JSON.deserializeUntyped(extraFields);        
                for (Object thisFieldObject : extraFieldsList) {
                    Map<String,Object> thisField = (Map<String,Object>) thisFieldObject;
                    Schema.SObjectField sof = Schema.SObjectType.User.fields.getMap().get((String) thisField.get('fieldPath'));
                    u.put(sof, thisField.get('value'));
                }
            }
                        
            if (includePassword) {    
                if (!isValidPassword(password, confirmPassword)) {
                    return Label.site.passwords_dont_match;
                }
             validatePassword(u, password, confirmPassword);
            }
            else {
                password = null;
            }
            
            // lastName is a required field on user, but if it isn't specified, we'll default it to the username
            String userId = Site.createExternalUser(u, accountId, password);
            // create a fake userId for test.
            if (Test.isRunningTest()) {
                userId = 'fakeUserId';           
            }
            if (userId != null) { 
                if (password != null && password.length() > 1) {
                    ApexPages.PageReference lgn = Site.login(email, password, startUrl);
                    if(!Test.isRunningTest()) {
                     aura.redirect(lgn);
                    }
                }
                else {
                    ApexPages.PageReference confirmRef = new PageReference(regConfirmUrl);
                    if(!Test.isRunningTest()) {
                    aura.redirect(confirmRef);
                   }

                }
            }
            return null;
        }
        catch (Exception ex) {
            Database.rollback(sp);
            return ex.getMessage();            
        }
    }
    
    @AuraEnabled
    public static List<Map<String,Object>> getExtraFields(String extraFieldsFieldSet) { 
        List<Map<String,Object>> extraFields = new List<Map<String,Object>>();
        Schema.FieldSet fieldSet = Schema.SObjectType.User.fieldSets.getMap().get(extraFieldsFieldSet);
        if(!Test.isRunningTest()) {
        if (fieldSet != null) {
            for (Schema.FieldSetMember f : fieldSet.getFields()) {
                Map<String, Object> fieldDetail = new Map<String, Object>();
                fieldDetail.put('dbRequired', f.getDBRequired());
                fieldDetail.put('fieldPath', f.getFieldPath());
                fieldDetail.put('label', f.getLabel());
                fieldDetail.put('required', f.getRequired());
                fieldDetail.put('type', f.getType());
                fieldDetail.put('value', '');   // client will populate
                extraFields.add(fieldDetail);
            }}}
        return extraFields;
    }
    
    @AuraEnabled
    global static String setExperienceId(String expId) {
        // Return null if there is no error, else it will return the error message 
        try {
            if (expId != null) {
                Site.setExperienceId(expId);   
            }
            return null; 
        } catch (Exception ex) {
            return ex.getMessage();            
        }        
    }  
}

Lighting Cmp:
<!-- add implements="forceCommunity:availableForAllPageTypes" to surface the component in community builder -->
<aura:component controller="LightningSelfRegisterController" implements="forceCommunity:availableForAllPageTypes" access="global">
    <aura:attribute name="accountId" type="String" required="false" description="accountId for creating the user. If not specified, it will create a PersonAccount if possible for B2C scenario. Or otherwise if it's in a community, the community's self-registration accountId will be used."/>
    <aura:attribute name="regConfirmUrl" type="String" required="true"/>
    <aura:attribute name="startUrl" type="String" required="false" description="The url you go to after a successful login" />
    <aura:attribute name="showError" type="Boolean" required="true" description="" default="false" access="private"/>
    <aura:attribute name="errorMessage" type="String" required="false" description="" access="private"/>
    <aura:attribute name="firstnameLabel" type="String" required="false" default="First Name"/>
    <aura:attribute name="lastnameLabel" type="String" required="false" default="Last Name"/>
    <aura:attribute name="emailLabel" type="String" required="false" default="Email"/>
    <aura:attribute name="passwordLabel" type="String" required="false" default="Create Password"/>
    <aura:attribute name="confirmPasswordLabel" type="String" required="false" default="Confirm Password"/>    
    <aura:attribute name="submitButtonLabel" type="String" required="false" default="Sign Up"/>
    <aura:attribute name="includePasswordField" type="Boolean" required="false" default="false" description="Whether to include password"/>    
    <aura:attribute name="extraFieldsFieldSet" type="String" required="false" description="A field set name whose fields are desired for user registration"/>
    <aura:attribute name="extraFields" type="list" required="false" description="A field set name whose fields are desired for user registration"/>
    <aura:handler name="init" value="{!this}" action="{!c.initialize}"/>
    <aura:attribute name="expid" type="String" required="false" description="The branding experience ID" />    
    
    <aura:registerevent name="sitePropagatedStartUrl" type="c:setStartUrl"/>
    <aura:handler name="init" value="{!this}" action="{!c.initialize}"/>
    <aura:dependency resource="c:setStartUrl" type="EVENT"/>
    
    <aura:dependency resource="siteforce:registerQueryEventMap" type="EVENT"/>
   
    <aura:handler event="c:setStartUrl" action="{!c.setStartUrl}"/> 
    <aura:handler event="c:setExpId" action="{!c.setExpId}"/>    
    <aura:dependency resource="c:setExpId" type="EVENT"/>   
    <ltng:require styles="{!$Resource.FontAwesomeWeb + '/fontawesome-web/css/all.css'}" />

   
    <div class="floating-container">
            <div class="register-container">
                <lightning:layoutItem padding="around-small" size="12" class="slds-align_absolute-center">
                    <div class="page-section page-header">
                        <img src="{!$Resource.starShadowOnly}" width="40" alt="city of chicago 311" />
                        <h1>Want to join us?</h1>
                        <p>Register a new account. Track all your requests </p>
                    </div>
                </lightning:layoutItem>
                <lightning:layoutItem size="12">
                <div id="sfdc_username_container" class="sfdc">
                    <!-- <span id="sfdc_user" class="login-icon" data-icon="a"></span> -->
                    <ui:inputText value="" aura:id="firstname" placeholder="{!v.firstnameLabel}" keyup="{!c.onKeyUp}" class="input sfdc_usernameinput sfdc"/>
                </div>
                </lightning:layoutItem>
                <lightning:layoutItem size="12">
                <div id="sfdc_nickname_container" class="sfdc">
                    <!-- <span id="sfdc_user" class="login-icon" data-icon="a"></span> -->
                    <ui:inputText value="" aura:id="lastname" placeholder="{!v.lastnameLabel}" keyup="{!c.onKeyUp}" class="input sfdc_usernameinput sfdc"/>
                </div>
                </lightning:layoutItem>
                <lightning:layoutItem size="12">
                <div id="sfdc_email_container" class="sfdc">
                    <!-- <span id="sfdc_user" class="login-icon" data-icon="k"></span> -->
                    <ui:inputText value="" aura:id="email" placeholder="{!v.emailLabel}" keyup="{!c.onKeyUp}" class="input sfdc_usernameinput sfdc"/>
                </div>
                </lightning:layoutItem>
                <aura:iteration aura:id="extraFields" items="{!v.extraFields}" var="curField" indexVar="index">
                    <div id="sfdc_extrafield_container" class="sfdc">
                        <!-- <span id="sfdc_user" class="login-icon" data-icon="a"></span> -->
                        <ui:inputText value="{!curField.value}" aura:id="{!curField.fieldPath}" placeholder="{!curField.label}" keyup="{!c.onKeyUp}" class="input sfdc_extrafieldinput sfdc"/>
                    </div>
                </aura:iteration>

                <aura:renderIf isTrue="{!v.includePasswordField}">
                    <div id="sfdc_password_container" class="sfdc">
                        <!-- <span id="sfdc_lock" class="login-icon sfdc" data-icon="c"></span> -->
                        <ui:inputSecret value="" aura:id="password" placeholder="{!v.passwordLabel}" keyup="{!c.onKeyUp}" class="input sfdc_passwordinput sfdc"/>
                    </div>
        
                    <div id="sfdc_confirm_password_container" class="sfdc">
                        <!-- <span id="sfdc_lock" class="login-icon sfdc" data-icon="c"></span> -->
                        <ui:inputSecret value="" aura:id="confirmPassword" placeholder="{!v.confirmPasswordLabel}" keyup="{!c.onKeyUp}" class="input sfdc_passwordinput sfdc"/>
                    </div>
                </aura:renderIf>
                <aura:renderIf isTrue="{!v.showError}">
                    <div id="error">
                        <ui:outputRichText value="{!v.errorMessage}"/>
                    </div>
                </aura:renderIf>

                <div class="sfdc">
                    <!-- <ui:button aura:id="submitButton" label="{!v.submitButtonLabel}" press="{!c.handleSelfRegister}" class="sfdc_button"/> -->
                    <lightning:button variant="brand" label="{!v.submitButtonLabel}" onclick="{!c.handleSelfRegister}" class="sfdc_button" />
                </div>
            </div>
    </div>
</aura:component>

Cmp Controller:
({
    initialize: function(component, event, helper) {
        $A.get("e.siteforce:registerQueryEventMap").setParams({"qsToEvent" : helper.qsToEventMap}).fire();
        $A.get("e.siteforce:registerQueryEventMap").setParams({"qsToEvent" : helper.qsToEventMap2}).fire();        
        component.set('v.extraFields', helper.getExtraFields(component, event, helper));
    },
    
    handleSelfRegister: function (component, event, helpler) {
        helpler.handleSelfRegister(component, event, helpler);
    },
    
    setStartUrl: function (component, event, helpler) {
        var startUrl = event.getParam('startURL');
        if(startUrl) {
            component.set("v.startUrl", startUrl);
        }
    },
    
    setExpId: function (component, event, helper) {
        var expId = event.getParam('expid');
        if (expId) {
            component.set("v.expid", expId);
        }
        helper.setBrandingCookie(component, event, helper);
    },
    
    onKeyUp: function(component, event, helpler){
        //checks for "enter" key
        if (event.getParam('keyCode')===13) {
            helpler.handleSelfRegister(component, event, helpler);
        }
    }   
})

Cmp Helper:
({
    qsToEventMap: {
        'startURL'  : 'e.c:setStartUrl'
    },
    
    qsToEventMap2: {
        'expid'  : 'e.c:setExpId'
    },
    
    handleSelfRegister: function (component, event, helpler) {
        var accountId = component.get("v.accountId");
        var regConfirmUrl = component.get("v.regConfirmUrl");
        var firstname = component.find("firstname").get("v.value");
        var lastname = component.find("lastname").get("v.value");
        var email = component.find("email").get("v.value");
        var includePassword = component.get("v.includePasswordField");
        var password = component.find("password").get("v.value");
        var confirmPassword = component.find("confirmPassword").get("v.value");
        var action = component.get("c.selfRegister");
        var extraFields = JSON.stringify(component.get("v.extraFields"));   // somehow apex controllers refuse to deal with list of maps
        var startUrl = component.get("v.startUrl");
        
        startUrl = decodeURIComponent(startUrl);
        
        action.setParams({firstname:firstname,lastname:lastname,email:email,
                password:password, confirmPassword:confirmPassword, accountId:accountId, regConfirmUrl:regConfirmUrl, extraFields:extraFields, startUrl:startUrl, includePassword:includePassword});
          action.setCallback(this, function(a){
          var rtnValue = a.getReturnValue();
          if (rtnValue !== null) {
             component.set("v.errorMessage",rtnValue);
             component.set("v.showError",true);
          }
       });
    $A.enqueueAction(action);
    },
    
    getExtraFields : function (component, event, helpler) {
        var action = component.get("c.getExtraFields");
        action.setParam("extraFieldsFieldSet", component.get("v.extraFieldsFieldSet"));
        action.setCallback(this, function(a){
        var rtnValue = a.getReturnValue();
            if (rtnValue !== null) {
                component.set('v.extraFields',rtnValue);
            }
        });
        $A.enqueueAction(action);
    },

    setBrandingCookie: function (component, event, helpler) {        
        var expId = component.get("v.expid");
        if (expId) {
            var action = component.get("c.setExperienceId");
            action.setParams({expId:expId});
            action.setCallback(this, function(a){ });
            $A.enqueueAction(action);
        }
    }    
})

 
Abdulla d 5Abdulla d 5
Hi Bryant,
can  you provide test class for this claass.
it will be  helpful for me.
Dhanith Kasarla 1Dhanith Kasarla 1
Hi Bryant,

Can you please provide test class for the above apex class ,Thanks in Advance

Regards,
Dhanith.