Service and Support Blog - January 2009
-
Allowing Customer Portal Users To Edit Their Own Contact Information
Marco Casalaina Jan 29, 2009Customer 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 you haven't already done so) and add the Customer Portal Welcome component to 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 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 (Trigger.new.size()==1) {
User u = Trigger.new[0];
//And only if it's a portal user
if (u.ContactId!=null) {
UpdateContactFromPortalUser.updateContacts(u.Id);
}
}
}
My @future method is namedUpdateContactFromPortalUser.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);
c.Email = u.Email;
c.FirstName=u.FirstName;
c.LastName=u.LastName;
c.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!
-
The Service Cloud: Join the Conversation
Ken Osborn Jan 27, 2009When you’ve got a customer service challenge...where’s the first place you turn to get help? This blog? Google? Maybe you turn to your network on Facebook? It’s not a surprise...but every day more and more customers are using these alternative “cloud-based” channels for customer service. So how do you keep your company in the loop and a part of these online conversations?
Last week we announced an exciting new vision that helps address this new and growing world of customer service & support at an event here in San Francisco. It’s called the Service Cloud. The whole event is up on video, and there’s also a very cool demo of some of our new technology. You can check it out here:
http://www.salesforce.com/servicecloud
Take a look...let us know what you think!
-
Showing A "New Object" Button In The CTI Adapter
Marco Casalaina Jan 20, 2009Here's another one for you CTI adapter writers out there. Often customers want the option to autocreate certain objects, like a Contact, Lead or Account, pushing the phone number and perhaps some attached data directly into the edit screen.
In today's example we'll autocreate the Account object. This is pretty common in B2C scenarios where call center agents are creating Person Accounts.
First, a little background. Every top-level object in Salesforce.com has a 3-character "entity ID." You can use this entity ID to make a new edit page -- indeed, you'll notice that whenever you navigate to any edit page, it looks like /XXX/e. That XXX is the entity ID -- for Account, for example, it's 001.
1. Define a constant like this in your user interface .h file:
#define BUTTON_NEW_ACCOUNT 21
2. Add the following to your Initialize override after you've called the base class:
CCTIButton pAccountButton;
pAccountButton.SetLabel(L"New Account");
pAccountButton.SetLongStyle(true);
pAccountButton.SetColor(COLOR_BEIGE);
pAccountButton.SetLinkURL(L"/001/e");
UIAddButtonToAllLines(BUTTON_NEW_ACCOUNT,&pAccountButton);3. Add this to CTILine.h:
CCTIButton* GetButton(int nId);
and this to CTILine.cpp:
CCTIButton* CCTILine::GetButton(int nId)
{
return m_mapButtons[nId];
}4. After you call OnCallRinging in your event sink, it returns the line number it put the call on. Use it to find the button and change its link to a new account edit page with the phone number defaulted:
//this will already be there
int nReturn = CCTIUserInterface::OnCallRinging(sCallObjectId, nCallType, bPerformSearch, bLogCall, mapInfoFields, mapAttachedData, nLineNumber);//add this stuff
CCTILine* pLine = GetLine(nReturn);
if (pLine) {
CCTIButton* pAccountButton = pLine->GetButton(BUTTON_NEW_ACCOUNT);
if (pAccountButton) {
pAccountButton->SetLinkURL(L"/001/e?acc10="+mapInfoFields[KEY_ANI]);
UIRefresh();
}
}5. Enable this button whenever you set your button enablement for the call being online. In the demo adapter it looks like this, but it could be different in your adapter depending on where and how you enable the buttons (in bold is the button you just added – the other stuff should already be there):
void CDemoUserInterface::EnableStandardLineButtons(int nLine)
{
std::list<int> listEnabledButtons;listEnabledButtons.push_back(BUTTON_NEW_ACCOUNT);
listEnabledButtons.push_back(BUTTON_RELEASE);
listEnabledButtons.push_back(BUTTON_HOLD);
listEnabledButtons.push_back(BUTTON_TRANSFER);
listEnabledButtons.push_back(BUTTON_CONFERENCE);
listEnabledButtons.push_back(BUTTON_NEW_LINE);OnButtonEnablementChange(nLine,listEnabledButtons,false);
}That's all there is to it.
