One of the more interesting challenges contractors for public sector clients have is working with older versions of software. On a recent project involving an integration of a solution I’d written using Microsoft Dynamics CRM 2016 and SharePoint 2010, I found myself struggling with an issue around capturing a digital signature.
As a straight SharePoint development project, this can be fairly routine: leverage Microsoft Word and its implementation of signature blocks, similarly leverage SharePoint’s out-of-the-box (OOTB) workflow for collecting signatures, and you’re basically done.
However, the client in question was less than enthusiastic about SharePoint as a platform in general and, because of that, and a few other design criteria, the bulk of the solution wound up being built in Dynamics CRM and SharePoint was largely a simple document repository.
So, how do you not re-invent the wheel for digital signatures and still keep most of your smarts on the Dynamics CRM side? [Bear in mind the production environment where this was going to be enabled was very locked down. Farm solutions were prohibited, so even if you wanted to write your own custom workflow using .NET workflow, you couldn’t because that requires a farm solution.]
We have the OOTB workflow already and we can associate it with the document library in question, but what we really needed was the ability to notify CRM when that workflow completed. Answer: SPWorkflowEventReceiver.
I’ll do my best to keep this simple, and the good news is that it really is. It’s important to note that the basic architecture for the integration of these two worlds — Dynamics CRM and SharePoint — is often a Dynamics CRM solution (.zip) containing the entites, workflows, etc. that your CRM needs and then a SharePoint solution (.wsp) that often contains any workflows, custom content types, etc. that the SharePoint bits need. It’s also probably worth noting for those unfamiliar with the OOTB integration with SharePoint that Dynamics CRM supplies, that it is very basic. By that I mean it is a very thin veneer over SharePoint’s Lists web service, even when not using the SharePoint List Component for CRM.
Step 1: Get On With It!
For this exercise we’ll assume you’re working with the OOTB “Collect Signatures” workflow. You may have to enable the “Workflows” site collection feature to have the “Collect Signatures” workflow show up as an option for a document library. [as shown below]
Once the feature is turned on, you should be able to associate the workflow with whatever document library you’re using to store documents. [as shown below]
It may also be entirely likely that you may have this workflow turned on already, which is fine. Our work on the SharePoint side is going to involve hooking the Collect Signatures workflow, so this is really prep work so we know that workflow is going to trigger the receiver.
Step 2: Create or Extend Your SharePoint {{cta(‘209aad85-272d-4e83-8cdc-4f30262bc921′,’justifyright’)}}Solution
In my case, there was an existing sandbox solution. In my case, I wanted to separate the content types from the workflow event receivers so there are two separate features. However, your taste may vary and that’s entirely up to you on scope of the feature. Happily, SPWorkflowEventReceiver behaves similarly to other event receivers in SharePoint so the creation of the class is also similar. We might start out with a class like this:
using System; using System.Security.Permissions; using Microsoft.SharePoint; using Microsoft.SharePoint.Utilities; using Microsoft.SharePoint.Workflow; namespace DHS.KMDS.SP.Core.SignatureCompletedEventReceiver { /// /// List Workflow Events /// public class SignatureCompletedEventReceiver : SPWorkflowEventReceiver { /// /// A workflow was completed. /// public override void WorkflowCompleted(SPWorkflowEventProperties properties) { using (SPSite site = new SPSite(properties.WebUrl)) { using (SPWeb web = site.OpenWeb()) { SPList taskList = web.Lists["Tasks"]; SPListItem item = taskList.Items.Add(); item["Title"] = "workflow completed"; item.Update(); } } base.WorkflowCompleted(properties); } } }
And we’d configure the elements.xml for this class like so:
<?xml version="1.0" encoding="utf-8"?> <Elements xmlns="http://schemas.microsoft.com/sharepoint/"> <Receivers ListTemplateId="101"> <Receiver> <Name>SignatureCompletedEventReceiverWorkflowCompleted</Name> <Type>WorkflowCompleted</Type> <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly> <Class>DHS.KMDS.SP.Core.SignatureCompletedEventReceiver.SignatureCompletedEventReceiver</Class> <SequenceNumber>10000</SequenceNumber/> </Receiver> </Receivers> </Elements>
Here’s an interesting caveat: control of this event receiver is NOT fine-grained. You can control the list template to narrow it down some, but even then you could theoretically have multiple event receivers hooked to the same list and certainly more than one workflow active on a list.
Step 3: Tightening Things Up
So in order to do a little optimization of our event receiver, we can dig into the SPWorkflowProperties class passed into our WorkflowCompleted event. The things found there are about what you might expect, again similar to other event receiver classes in SharePoint. It’s our basic reference to who fired the event and includes data to find the original URL, list id, workflow id, and so on. In our revised code, we’d check to make sure the action we’re taking is appropriate by looking at our list id and workflow id and ensuring it’s our list (document library) and our workflow (“Collect Signatures”).
It’s also important to note that while our event receiver can and is tied to one and only one event — WorkflowCompleted in our case — that the event itself is not enough. Our event fires for a lot of reasons — the workflow terminated successfully, the workflow threw an exception, or was cancelled by the user, etc. Another filter we might want to add is to make sure we’re only performing actions on the event conditions we care about. For our purposes, we care about successful completion of the collect signatures workflow.
Step 4: Take Action
Our initial prototype had us just writing some data to a local SPList. However, my solution was not enough to write there. Instead, we’re going to use this hook to call Dynamics CRM and let it know the collection of signatures completed and we can update the entity on the CRM side to mark the item there as complete as well. In my case, there were some additional hurdles due to an “impedence mismatch” between SharePoint and CRM, but most environments should be just calling CRM’s Organization Service and updating the entity.
The Wrap-Up
There’s already been a number of articles written about SPWorkflowEventReceivers. Check them out, they mostly say similar things and what I’ve written here isn’t rocket science at all. However, it does illustrate a way for us as developers to avoid the all-too-common pitfall of reinventing the wheel and gives us enough functionality despite being very limited with what we can do when we’re in a locked down environment.
On the downside, there’s no real facility to do this using the app model (Add-in). The documentation isn’t as clear, but it would seem SPWorkflowEventReceivers aren’t part of code that’s supposed to deployable in a SharePoint add-in (server code).
What other ways do you provide functionality for SPWorkflowEventReceivers in your company’s projects? Share your wisdom with us in a comment below!
Found this blog post useful? Make yourself comfortable and check out our blog home page to explore other technologies we use on a daily basis and the fixes we’ve solved in our day to day work. To make your life easier, subscribe to our blog to get instant updates sent straight to your inbox:
{{cta(‘33985992-7ced-4ebd-b7c8-4fcb88ae3da4′,’justifycenter’)}}