Thursday, December 30, 2010

Integrating SalesForce.com with SumTotal LMS

Recently I obtained a developer’s license for SalesForce to play with and learn more about this environment. One of the things that I wanted to figure out in my playing was to integrate another web application as a tab into the SalesForce interface. I thought that it would be fun to figure out how to link our learning management system (LMS) up with SalesForce. We are using a LMS from SumTotal which is a pretty flexible application.

For those that are not familiar with what a LMS is, it is an application that provides an environment for keeping track of learning activities for users and many can also provide on-line learning content.
I will walk through much of the setup that I went through to get this to work though there are certain elements that I will not show for security reasons.

First off you would need to setup the remote application, in our case the LMS, to be able to receive a pass through authentication from SalesForce. For the SumTotal application I decided to use the MD5 pass through process. For simplicity of administration I decided to implement the authentication process a little differently than most. Most people that I have talked to will create the pass through authentication page and put it into the same web application as the LMS. There are two challenges with this.

I discovered the first of these challenges recently. This is how I had implemented the process initially though we just went through an upgrade of the application and this because a problem. I recommend NOT putting the pass through authentication pages in the same web application as the core LMS application.

The second issue is that the SumTotal web application is a precompiled J# application and putting uncompiled web pages into the same application can be tricky (not impossible, but tricky). For those who are not familiar with web development this can be a bit of a pain.

So with these two issues in mind, I took this approach. First of all we have a defined web instance (not the default instance) in IIS (Internet Information Services) dedicated to the LMS. There are a couple of virtual web folders, or web application, for the core application and its web services in the web site. I created two new virtual folders, or web applications, in the website. One for SSO pass through authentication and one for content that we would like to have exposed to the general public without going through the LMS (not important for this discussion). The SSO web application is not locked down since we want anyone to be able to access the pages to log in.

I created a script specifically to use with SalesForce (I do not like to mix authentication pages between different login processes). For simplicity I used J# to write the script though since I created my own web application for SSO I could have used any language. Below is a sample of the code that I used though you would need to modify a few things for it to work in your environment; css links and URLs. Also as a note I try to typically lock down the script so that it will only accept calls from certain web pages (referrers). Unfortunately when adding this call to SalesForce it makes the call without a referrer (similar to typing in the URL directly in the address line in your browser). This leaves a little bit of a security risk for your LMS which I have not figured out yet. The risk is that anyone who figures out the SSO URL could hack their way into the application (this is why I like to do referrer filtering). The MD5 authentication does not take a password for authentication. It assumes that authentication has already been performed in the source application and “trusts” the value being sent.

<%@ Page Language="VJ#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
public String user;
private void getUserName(Object obj, System.EventArgs e)
{
// Load NameValueCollection object.
NameValueCollection coll = get_Request().get_QueryString();
String test = coll.get_Item("username");
String Name = coll.get_Item("username");
String username = Name;
user = username;
rtUserID.set_Value(username);
String validreferrer1 = "";
int validreferrer = -1;
String errorurl = "/sumtotal/app/SilentLogon_Error.aspx?UserName=" + user + "&SilentLogonError=-5";
try
{
Uri referrerUrl = get_Request().get_UrlReferrer();
String httpreferrer = referrerUrl.get_AbsoluteUri().ToString();
get_Response().Write(httpreferrer);
if(httpreferrer.indexOf(validreferrer1) > -1)
{
validreferrer=1;
}
if(validreferrer < 0)
{
get_Response().Redirect(errorurl);
}
}
catch (Throwable t)
{
//get_Response().Redirect(errorurl);
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title> New Document </title>
<meta name="Generator" content="Microsoft FrontPage 4.0" />
<script type="text/javascript" language="JavaScript" src="md5.js"></script>
<meta name="Author" content="" />
<meta name="Keywords" content="" />
<meta name="Description" content="" />
<link rel='stylesheet' type='text/css' href='/SumTotal/client/media/css/default_800/SYS_base.css' />
<link rel='stylesheet' type='text/css' href='/SumTotal/client/media/css/default_800/Custom_SYS_base.css' />
</head>
<body onload='content_onload()'>
<script type="text/javascript" language="JavaScript">
//<!--
function getAuthCode()
{
var strEmp = String(document.getElementById('rtUserID').value);
var strtime_stampStamp = String(document.getElementById('time_stamp').value);
var strKey = String(document.getElementById('MD5Key').value);
document.getElementById('auth_token').value=hex_md5(strEmp+strtime_stampStamp+strKey);
}
function content_onload()
{
var dt = new Date();
document.getElementById('time_stamp').value= dt.getUTCFullYear() + ":" + (dt.getUTCMonth()+1) + ":" + dt.getUTCDate() + ":" + dt.getUTCHours () + ":" + dt.getUTCMinutes() + ":" + dt.getUTCSeconds();
getAuthCode();
document.getElementById('frmLogon').submit();
}
//-->
</script>
<form onload="getUserName" id="frmLogon" runat="server" method="post" action="/SumTotal/app/SYS_login.aspx">
<input type="hidden" runat="server" id="rtUserID" name='rtUserID' />
<input type="hidden" runat="server" id="time_stamp" name="time_stamp" value="2003:07:10:09:30:05" />
<input type="hidden" runat="server" id="MD5Key" name="MD5Key" value="12345" />&nbsp;
<input type="hidden" runat="server" id="auth_token" name="auth_token" />
<table width='100%' cellpadding="0" cellspacing="0" border="0">
<tr>
<td id='mainLeftTD' class='clsPageHeaderLeft' align=left valign=center nowrap>
<table>
<tr>
<td align=left valign=center nowrap>&nbsp;
<image border=0 src='/SumTotal/client/media/images/default_dlx_800/page_header_logo.gif' alt=''>
</td>
</tr>
</table>
</td>
<td id='mainRightTD' class='clsPageHeaderRight' style='background-image: url(/SumTotal/client/media/images/default_dlx_800/page_header_background.gif)' cellpadding=0 cellspacing=0 border=0>
</td>
</tr>
</table>
<br />
<br />
<br />
<br />
<table border="0" width="100%">
<tr>
<td>&nbsp;</td>
<td></td>
</tr>
<tr>
<td>&nbsp;</td>
<td></td>
</tr>
<tr>
<td>&nbsp;</td>
<td></td>
</tr>
<tr>
<td>&nbsp;</td>
<td></td>
</tr>
<tr>
<td colspan="2" align="center">
<font size="4"><b>You are now being logged into the SumTotal LMS as <%=user%>.... <i>Please wait</i></b></font>
</td>
</tr>
<tr>
<td align="center">
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<font size="1"><i>If you are not redirected in 5 seconds please press the button below.</i></font>
</td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="submit" value="Log On" name="btnToLogonPage" onclick='getAuthCode()' />
</td>
</tr>
</table>
</form>
</body>
</html>
Now that you have the SSO script in place you will need to expose the LMS to the outside world. If you are using the hosted version of the application it should already be exposed though I am not sure how to setup custom authentication paths using that model. Our LMS is an on-site installation. There are many methods for exposing a website to the outside world which is beyond the topic of this article.

For this exercise I created a test domain and test account to play with so that I do not impact any live accounts.

That is all that is needed on the LMS side of things though I recommend testing the URL to the new pass through page to make sure that it works prior to going to the SalesForce setup.

If you have not already created a developer account in SalesForce I recommend it. You can go to http://developer.force.com/ to create the account. When you create an account you will get a blank SalesForce site to play with (in the screenshots my site will have miscellaneous items in it due to may playing around in the site prior to this article).

First off you will need to decide what data element in the user profile to use that will match the user ID in the LMS. In the dropdown next to your login name you will see an option for “setup”. Select that option and the setup for the site will come up. Close to the bottom of the left hand side under “Administration Setup” there is an option called “Manage Users”. Open that up and select “Users”. Click on the user in the right hand side that you want to test with. There are many options that can be used. I selected to use the “Employee Number” option. The value in that field will match the user id that I setup in the LMS, in this case it is “testuser”.

clip_image002

Now that you have a user value that matches something in the LMS it is time to add the link in SalesForce. While still in “setup” on the left hand side go to the “App Setup” section, open the “create” menu and select “Tabs”. Mine looks like the below image though yours may be blank depending upon if you have added anything as of yet.

clip_image004

Click on the “New” button under the “Web Tabs”. A wizard will come up to help you through the tab creation process.

The first page in the wizard is below. You can use either format. I am going to use the “full page width” option because I think it looks better. Select “Next” after you have made your selection.

clip_image006

On the next wizard screen there are a lot of options that control how the linked object is displayed within the SalesForce environment. First give your new tab a Label and a Name. Select a tab style (color and icon) that you want to be associated to it (I selected the “Presenter” style). By default the “height” of the tab is set to 600. This controls whether the scroll bar will be displayed depending upon how big the page is. We try to have things in 800 pixel size so I entered 800. I have not tested the LMS with a mobile device so I am not sure if it is “mobile ready”. I do not have any splash pages defined so I do not need to select anything here. Lastly add a description. This is best practice to always add descriptions to your objects. Click “Next” when you are done.

clip_image008

The next wizard screen is where you add the URL link and pass the values that the application will need. The two dropdowns are there just for ease of use in finding the right syntax to use to pass the right value from SalesForce to the application. I put my URL in the URL box (you will note that my actual URL in this screen is not visible. You would need to have your actual URL to put in this space. One note is that you are only able to pass values via the query string. After you are done with your URL string press the “Next” button.

clip_image010

The next two screens control who can use the new tab and what applications it is available under. Once you click “Save” on the last tab you will be brought back to the main page that shows all the tabs in the application. You should also see the new tab at the top of the SalesForce interface.

clip_image012

Now if I go and click on the LMS tab in the SalesForce interface I will now see the pass through page for a moment and then it will forward on to the home page for the user.

clip_image014

2 comments:

  1. Interesting article Robert! We also use SumTotal and Salesforce, but our SumTotal users are not necessarily Salesforce users, but are all contacts within Salesforce. Curious, did you do any work with interfacing/updating SF users with complete course information or were you able to extract any other useful data from the LMS?

    ReplyDelete
  2. John, Thanks for your comment. Our implementation does not necessitate the need to store learning information in Salesforce. Mostly we use it to consolidate the number of touch points our sales reps have to streamline their workflows.

    With that being said, there are a few different ways to do what you are asking about. First off it depends upon what type of SumTotal installation you are working off of. If you have the onsite solution things get easier. If you have their SaaS solution things get a little complicated.

    On the Salesforce side you would need to create a new object that contains the fields that you are interested in storing information in.

    If you have an onsite installation of SumTotal then you would need to put together a process to pull the information from the database. You can do this in several ways...Custom Report in SumTotal and export to a file and then use the Salesforce data loader to process the file into the new object. The second option would be to use Informatica Cloud and pull from the database and push the information directly into the Salesforce object.

    If you have the SaaS solution then things get a little more complicated because you will not have access to the database. You would have to create a custom report and export the information and then use the data loader or informatica cloud to process that file into Salesforce. It is possible to automate all of this though it can be a bit tricky.

    I hope this helps. If there is anything I can do to help let me know.

    ReplyDelete