Monday, July 29, 2013

Allowing Customer Portal Users To Edit Their Own Contact Information

Customer Portal users have the ability to edit some information about themselves via the My Profile page.  That page allows them to edit their own User record, but not their own Contact or Account record.  However, the User record does have some fields in common with Contact and Account, and with a quick bit of Apex, you can make it such that when the portal user updates his information, that gets reflected in his Contact or Account record as well.

To allow portal users to see the My Profile link, go to Setup->Home->Home Page Layouts.  Make a separate home page layout for your Customer Portal (if youhaven't already done so) and add the Customer Portal Welcome componentto the top of it.  That will render a little area in the sidebar with a My Profile link (which will allow them to edit their user information) and a Logout link.

Now to synchronize that User information with the Contact information.  The example given here shows that synchronization just with the Contact, but it can easily be modified to apply to an Account or a Person Account.  While the code itself is simple, it's not as straightforward as it seems.  As this page of the Apex docs indicates, you can't just make a User trigger that updates a Contact or an Account, because it's forbidden to modify those "non-setup" objects from a "setup" object like User.  Fortunately there is a simple solution: the @future annotation .  The @future annotation allows you to create Apex that runs asynchronously at some point in the future (in my tests I've found that it runs immediately, or at least very soon after the trigger executes).  Methods that use @future are subject to different limits than normal trigger operations because @future methods don't hold up the trigger (and therefore the entire user experience) while they're working.  Therefore @future methods are often used to perform long-running web service callouts from triggers asynchronously.  However, they can also be handy for a case like ours, where we want to update an object that we're not normally eligible to update. Here I'll show some snippets of code that allow me to accomplish this synchronization.

You can download the full trigger and class including test methods at the Developing with Service Cloud page here.

Calling a method that has been marked as @future is just like calling any other static method.  Here's my trigger, short and sweet:

trigger UpdateContactFromPortalUser on User (after update) {
    //We only want to run on the single item that the user edited
    if (
        User u =_[0];
        //And only if it's a portal user
        if (u.ContactId!=null) {

My @future method is named UpdateContactFromPortalUser.updateContacts.  Let's see how that works. 

In the example given here, I just update a couple of fields from the user record: the name, the email address, and the title.

global class UpdateContactFromPortalUser {
    @future public static void updateContacts(String userId) {
        User u = [select ContactId,Email,FirstName,LastName,Title
                    from User
                    where Id=:userId];

        if (u!=null && u.ContactId!=null) {
            Contact c = new Contact (Id=u.ContactId,Email=u.Email,FirstName=u.FirstName,LastName=u.LastName,Title=u.Title);
            update c;

(note that the above is just a snippet -- download the whole class here)

By putting the @future above my method, I'm telling the system that when I call this method, it should be run asynchronously.  This allows me to bypass those pesky trigger restrictions and update my contact.  Note that the @future method does impose some constraints: the method has to be static, return void, and cannot take sObjects as parameters (which is why I passed the user ID in instead of the User sObject itself).

That's all there is to it!

No comments:

Post a Comment