Getting SharePoint to talk to Salesforce on the cheap – Part 1

At work, we are using Salesforce.com to meet the needs of several departments. We’ve extended the platform to not just manage CRM data, but to track other business processes as well.  We also use Sharepoint to manage documents, publish dashboards, and maintain our event calendar.

On several occasions, I have expressed my desire for Salesforce and Sharepoint to talk to each other, but apparently it’s not that easy to do without developing web services or purchasing off the shelf solutions. Both of these paths are currently off the table for my small team, so we went a different direction with the tools that are available in Salesforce and Sharepoint.

Background

We have been using attachments functionality in Salesforce to keep documents with their corresponding records, but we have found that attachments are a less than optimal way to manage documents, because they are not searchable and they are difficult to export en masse if we need to move to a different Salesforce instance (we will soon).    We have also looked at Salesforce Content Management, but it takes too many clicks for users to upload files and attach the files to a record.  (We could do some custom stuff to make this work, but we don’t have the time or resources).

Solution

So, we decided to use Sharepoint as our document manager and provide links in Salesforce that reference the Sharepoint documents.  We figured out a relatively quick and easy way of doing this that includes the following steps.  Only the first two steps are manual. (see below).

The ingredients needed to make this work include:

Starting in Sharepoint:

  • Create a Document Library in Sharepoint with at least a field to house the Salesforce ID and Salesforce Name.  We also created a Document Type field (e.g. Contract, MOU, Application, etc).
  • Create a custom edit form (copy the editform.aspx), and place a Content Editor Webpart below the form.  In the CEWP add a javascript that parses a query string. (See below – Thanks to Josh for putting this together).
<script src="/_layouts/jquery-1.3.2.min.js" type="text/javascript"></script> <script type="text/javascript">// <![CDATA[

	$(document).ready(function(){

		var sfID = GetUrlKeyValue('SalesforceID');
		$('input[title="SalesforceID"]').val(sfID);

		var SFRecordType = GetUrlKeyValue('SalesforceRecordType');
		$('input[title="SalesforceRecordType"]').val(SFRecordType);

		var SFName = GetUrlKeyValue('SalesforceName');
		$('input[title="SalesforceName"]').val(SFName);

	});
// ]]></script>
  • Make the Custom Edit Form the default Edit Form for that library.

Moving to Salesforce:

  • Create a custom object called Document and include Document Type as a field and a lookup field to the object to which you will be attaching the documents. (e.g. If the document library will be housing documents related to Accounts, then add a lookup field to Accounts object.)
  • Create a “New Document” button in the Document object with a URL to directed to Sharepoint. You can get the beginning part of the URL by uploading a new document into your Sharepoint List and copying the URL in the address bar. It will look something like this:

http://SiteCollectionName/SiteName/_layouts/Upload.aspx?List=%7BD9F024A7%2D3E1D%2D4475%2D890F%2D973E19D5E997%7D&Root Folder=%2SiteName%2FSalesforceDocuments&Source=http%3A%2F%2F newsource%2Eapollogrp%2Eedu%2FGovtAffairs%2FGATeam%2F SalesforceDocuments%2FForms%2FAllItems%2Easpx%3FFilterField1%3D SalesforceID%26FilterValue1%3D{!Account.Id}&SalesforceID{!Account.Id}& SalesforceName={!Account.Name}

This will generate the url that will be parsed by the code above in the Sharepoint CustomEditForm.

  • Go to the Account Object Page Layout (or whichever related object to which you will be attaching your documents) and make sure that the Documents Object is showing as a related list.  In the Document Object, select the new button that you created in the previous step.

Congratulations, you just created a way to upload new documents Sharepoint from Salesforce.   Give it a try.  Go to an account and click the “New Document” button, and you’ll be taken to your Sharepoint site, where you can upload a new document.  The Salesforce ID and Document Types should have been passed to the Sharepoint form as well.

But we are not done yet!  Next we’ll learn how to send back the Document information from Sharepoint to the Document Object in Salesforce using workflow in Sharepoint and Email Services in Salesforce.  Once complete, the document information will show up in the Account Page Layout on the related Document list with a link directly to the actual document in Sharepoint.

Read Part II.

What if you could do it all over? [Good Book]

Ever thought about going back to that one critical moment or decision you made when you were 18? Maybe it was about a girlfriend/boyfriend; or a class you took; a bet you made (or didn’t); or a vacation you took.

Jeff Winston, a troubled Radio News Producer dies at the young age of 43 in the first pages of Replay by Keith Grimwood and immediately wakes up in his 18 year old body in 1963. He remembers everything from his previous life and decides very early on that he will not make this new life a copy of the last go around.  He capitalizes on his knowledge of current events and his
lessons already learned. By the time he dies again in 1988, he leaves a legacy much different from his previous life.

This cycle continues (with a some powerful wrinkles along the way) with Jeff taking different paths each time.  I read the last page (on my new nookcolor) over a week ago, but I keep thinking about the plot twists in the book as well as my own life’s plot twists.  What decisions would I make if I was all of a sudden 18 again?

Replay is so thought provoking because it weaves so many themes into the relatively short book.  In fact, the one major criticism I have of this book is that it is too short.  There was one part of this book that foreshadowed some potentially sinister events, but was not addressed in subsequent lifetimes.  Also, there was one specific relationship that was not resolved, but showed promise for even more twists and turns.

Nonetheless, this book is page turner that keeps on turning even after the last pages are done.

My 2010 Professional Development Retrospective

Looking back at 2010 (we can do that now, right?) I’m proud of my professional development.  I’m lucky to have a boss that (with the appropriate rationale) supports my professional development.  In 2010 I was fortunate to attend two conferences, two training courses, and develop some new skills on my own.

Courses and Seminars

This year I went to one technology conference and enrolled in one personal development seminar. Both were valuable experiences that have helped me to make good decisions and foster collaboration.

  • Technology of Participation Facilitation Methods course offered by Partners in Participation. This two-day class was the highlight of my professional development this year. The course focused on three powerful methods for faciltation: Focused Conversation, Consensus Building, and Action Planning. Anyone who facilitates meetings or conversations should attend this training. I walked away from the training with three new facilitation tools and tons more confidence than I had before the training started.
  • Scrum Master Certification Course.  My company offered a Scrum Master certification course as part of our IT department’s push toward becoming an Agile shop.  Although I had participated in Agile projects in the past, I had no formal training.  The Scrum Master course gave me some great insight on some of the intricacies of Agile development.

Conferences

  • SharePoint Technology Conference (also known as SPTechCon). This conference allowed me to see how other organizations and professionals are utilizing the powerful Sharepoint platform. I was able to meet with other business evangelists who have similar roles as I do, as well as learn some practical Sharepoint solutions that were immediately applicable when I got back to the office.
  • Dreamforce. 2010 was the year that I become a cloud evangelist.  I have spearheaded the design of two Salesforce applications and am gathering requirements for another one.  These projects helped me to justify my attendance at the annual Salesforce.com user conference.  This conference was one of my highlights of 2010: the sessions were educational and insightful; the Vendor Expo was eye opening; and the “extras” were phenomenal (Bill Clinton, Stevie Wonder, will.i.am).

Technology “Upgrades”

The formal conferences and training were well worth their prices of admission, but the majority of my professional development was advanced by plugging through tutorials and combing through blogs and websites.  I soaked up as many Salesforce tutorials I could find on the Salesforce.com training site.  In the last year I went from zero knowledge of Salesforce.com to having a strong foundation in the Force.com platform.

Also, I continued to tune my Sharepoint knowledge by actively involving myself in Sharepoint projects throughout the year.  I have been a voracious reader of endusersharepoint.com, Marc Anderson’s Blog, Path to Sharepoint, and other blogs.

Other Professional Development

Some other areas of development this year include:

  1. Balanced Scorecard: I studied the Balanced Scorecard approach and helped my department implement some goals, objectives, and key performance indicators. The was a great exercise for me and it helped our entire department reevaluate our strategies.
  2. Reporting: I helped my staff and our customers to implement some key reporting initiatives.
  3. Business Process Management:  Always my passion, I continue to stay up to date on the writings of Steve Towers, BPTrends, and a bunch of LinkedIn groups (e.g. BP Group and BPM Guru).

2011 and Beyond

I’m proud of my professional development this year and look forward to 2011.  Some professional development goals next year include getting some formal Salesforce training as well as some management development courses.  In the upcoming year, I also plan on participating in some local professional groups including the Arizona Association of Facilitators and the local Salesforce user groups.

Creating an RSVP template in Sharepoint (Part II)

In the last post I outlined how to setup the lists and newform.aspx for the RSVP template.  Let’s test to make sure the form is functioning, then we can complete the project by tying off some loose ends.

Testing the form

To test the application, create an event in the Event List.  Be sure to populate all the fields.  Once the event is create, type the url of the form we just created into the address bar of your browser and add “?SelectedID=1″ to the end of the url.  If you see information about the event on the top part of the form, you’re in good shape.

Go ahead and enter something in each of the fields, and submit the form.  Then check the RSVP list to make sure that the record was correctly inserted into the list.  The most important part of this test is to make sure that the correct event was identified in the new record.

Other (Optional) Stuff

Generating the RSVP url

I created the RSVP template to allow business users to create an RSVP form without IT support. My hope is that I will be able to step out of the RSVP business and make all the tasks self service.   For example, it’s not readily apparent to the average user how to get to the form for a given event, so I created a way to automatically generate the url.   The url for each event has three components.

  1. The link to the newform. It should be something like: http://sitecollectionname/sitename/listname/dynamicnewform.aspx
  2. The ID of the event. This component of the url will be SelectedID=X.
  3. The Source. The page to which the user will be directed after submitting the form like http://sitecollectionname.

The end result would be:

 http://sitecollectionname/sitename/listname/dynamicnewform.aspx?SelectedID=X&Source=http://sitecollectionname

To create this link automatically, create a calculated column in the Event List.  The calculated column will pull from the system created ID field and the field “Redirect after submitting” which was created in Part I:

=Concatenate("http://sitecollectionname/sitename/listname/dynamicnewform.aspx?SelectedID="&[ID]&"&Source="&[Redirect after submitting])

Creating the workflow

The last part of this process is to create the workflow.  The workflow is not required, but it increments the total number of slots left so the system can identify when the event is full or when a registrant should be placed on a waitlist.  Specifically the workflow should increment the field “Number of Places Used” in the event list.  When the Number of Places is greater than number of Places Available, an email would go out to the recipient explaining that he/she has been waitlisted.

That’s it! I hope this is helpful to those who create these types of forms. If you have any questions, please comment below.

Creating an RSVP template in Sharepoint (Part I)

***Alert: If you don’t use Sharepoint, you will find this post irrelevant***

As part of my day job, I spend a fair amount of time working in Sharepoint.  It’s not a perfect application,  but it allows people without any programming skills to build usable applications that only a couple of years ago would have been the responsibility of IT departments.
Occasionally, I get requests to create RSVP forms for employee events.  It’s not a very difficult request, but it takes some time to complete.

  1. Create a list that will hold all the RSVPs.
  2. Create a list that will information about number of slots available for the event.
  3. Spruce up the form to include banner.
  4. Develop a workflow that will increment number of slots remaining.
  5. Develop a workflow that sends out a confirmation email.
  6. Develop a workflow that puts people on waiting list (status=waiting list) if the event is full.

All told, the process can take anywhere between two hours to four hours to develop, test, and get user acceptance.  Over time, these projects add up, so I wanted to create an application that would setup these RSVPs dynamically.   My goal was to make it easy enough so a non-techie could set up an RSVP in under 15 minutes.

High Level Overview

It takes a little bit everything to get this built (i.e. javascript, workflow, data view web parts filtered based on parameters, etc), but in several hours time, this RSVP engine will run itself.

In the form above, the title, image, and text below the image are all related to the specific event. The fields in the form will be used for all events.

High Level Components:

  • Event Info List (easy)
  • RSVP List (easy)
  • Customized NewForm.aspx (moderately complex–you’ll need access to Sharepoint Designer)
  • Workflow to generate a link to an event’s form. (easy)
  • Workflow to increment number of slots left. (easy)
  • Workflow to send confirmation emails. (easy)

Step 1: Create Event Info List

The Event Info List will contain all the information about the list.  I setup my list to include the following fields:

The calculated column “Number of Places Left” uses the following formula:

=[Number of Places]-[Number of Places Used]

An explanation for how to setup the other calculated column (Link to RSVP form) can be found in Part II.

Step 2: Create the RSVP list.

This list will be where all attendees for all events will be stored.  At the minimum, you’ll need a lookup to the Event Name field in the Event Info List, the name of the attendee and a status field.  Some other suggestions include Name of Manager, Phone Number, and email address.

Step 3: Customize the newform.aspx (actually create a new “newform”)

This is the most complicated step of the project.  There are several parts to this step.

  1. Customize the RSVP form.
  2. Create a Data View Web Part pulling data from the Event List. (Remember the RSVP form is static, but the information about the event is dependant on chosen event.)
  3. Setup Parameters and Filter so the approriate event is selected on the Data View Web Part.
  4. Update the form (with a Content Editor Web Part) so that the appropriate event is selected on the RSVP form.

Part I: Customize the newform from the RSVP created in Step 2.

Although I’m not going to explain the process of customizing/creating a newform.aspx, a simple google query came up with some of the following walkthroughs:

http://www.risetime.com/blog/archive/2009/06/03/create-a-custom-newform-aspx-for-a-sharepoint-list.aspx
http://office.microsoft.com/en-us/sharepoint-designer-help/create-a-custom-list-form-HA010119111.aspx

Once the form is created, you can delete or move fields as necessary.  DO NOT DELETE ”EVENT NAME” FROM THE FROM.  The event name is needed to make sure that the RSVP is connected to the corresponding event.  We’ll hide this field in a subsequent step.  (See Section 2 Below)

Part 2: Create a data view webpart using the Event List

To add the event information create a data view webpart that shows data from the Event List we created in Step 1. (See Section 1 Below)

Part 3: Setup Parameters and Filter String

Once the two sections are setup, we have to setup Section 1 to return the appropriate Event based on a query string.  To do this, setup a Parameter for the data view web part in Section 1:

Then setup the Filter Criteria using the Parameterwe setup above:

Part 4: Update the form

Ok, so now we have the lists created and the NewForm created.  We’re almost done, but now we have to make sure that the form defaults to the correct event.  To do this, save and close your work in Sharepoint Designer.  Then open the form you just created in Sharepoint and add a content editor webpart.  Then insert the script below which will do two things:

  1. It will select the current event in the dropdown list.
  2. It will hide the event on the form.  (Once the event is selected, we don’t want users changing it.)
<script language="javascript" type="text/javascript">
_spBodyOnLoadFunctionNames.push("fillDefaultValues");
_spBodyOnLoadFunctionNames.push("hideFields");
function fillDefaultValues() {
var qs = location.search.substring(1, location.search.length);
var args = qs.split("&");
var vals = new Object();
for (var i=0; i < args.length; i++) {
var nameVal = args[i].split("=");
var temp = unescape(nameVal[1]).split('+');
nameVal[1] = temp.join(' ');
vals[nameVal[0]] = nameVal[1];
}
setLookupFromFieldName("Name of Event", vals["SelectedID"]);
}
function setLookupFromFieldName(fieldName, value) {
if (value == undefined) return;
var theSelect = getTagFromIdentifierAndTitle("select","Lookup",fieldName);
// if theSelect is null, it means that the target list has more than
// 20 items, and the Lookup is being rendered with an input element
if (theSelect == null) {
var theInput = getTagFromIdentifierAndTitle("input","",fieldName);
ShowDropdown(theInput.id); //this function is provided by SharePoint
var opt=document.getElementById(theInput.opt);
setSelectedOption(opt, value);
OptLoseFocus(opt); //this function is provided by SharePoint
} else {
setSelectedOption(theSelect, value);
}
}
function setSelectedOption(select, value) {
var opts = select.options;
var l = opts.length;
if (select == null) return;
for (var i=0; i < l; i++) {
if (opts[i].value == value) {
select.selectedIndex = i;
return true;
}
}
return false;
}
function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i=0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}
}
return null;
}
function hideFields() {
var control = getTagFromIdentifierAndTitle ("select","Lookup","Name of Event");
control.parentNode.parentNode.parentNode.style.display="none";
}
function getTagFromIdentifierAndTitle(tagName, identifier, title) {
var len = identifier.length;
var tags = document.getElementsByTagName(tagName);
for (var i=0; i < tags.length; i++) {
var tempString = tags[i].id;
if (tags[i].title == title && (identifier == "" || tempString.indexOf(identifier) == tempString.length - len)) {
return tags[i];
}
}
return null;
}
</script>

Now that the form is complete, some workflows must be created to tie all this together.  In the next couple of days, I will be posting information about how to setup the following functionality:

  • Generate a dynamic link to an event’s form.
  • Increment number of slots left.
  • Send confirmation emails.

To see the rest of this tutorial please go to Part II.