In my previous articles I have discussed how we can use custom content types and custom page layouts to define document structure in SharePoint. It did not involve any coding or customization. In this article we’ll continue where we left off and see how we can add a hook in SharePoint and enhance the content creation and approval process to make it more valuable to end users.
If we look at our example of creating a custom Content Type for “Functional Specifications”, we could extrapolate it to many different types of documents typically used in an enterprise, e.g. Policy documents, Functional Specs, Technical Specs, Time Sheets, Work-Orders etc. Custom Content types help us with defining the data-structure while layouts help us define the presentation. What if these documents were to require review and approval? SharePoint as most us know, has built in workflow capabilities. In SP 2010, workflows have been made pretty obvious. In fact our custom Pages will not be visible to everyone if we don’t actually publish them and the only way to do that is to start the approval workflow. Okay fine, workflows are still out of box, what are we doing new in that? Well nothing directly.
Imagine what would happen after a document (like our Functional Spec) is approved? A team would start using it. Development cycle would start. Ten days into development new requirements would come in and the spec would have to be re-visited and re-routed for approval. Until the document is approved again it would no longer be in ‘Published’ state. To view the previous state would be slightly messy. Instead of using the out of box revision history, if we built a capability where after every “Publish” the content is converted into a read-only format (like HTML/doc/pdf) and kept in a different location, it would solve our problem. If that location is a version controlled document library, then each version of the complete document would be available for anytime reference! Sounds good, how do we do it?
Hello, SharePoint “Event Receivers”
SharePoint has this infrastructure built in where certain SP objects raise events that can be handled by custom code. The event of interest to us is the ItemUpdated event fired by any SPList. This event is fired every time you make a change and save your list item. If you have a hook in place you can view the entire content and its current values in Event Receiver. So, enough of theory, let’s get hands on and build our own Event Receiver feature for SharePoint 2010.
Building a SharePoint Event Receiver Feature
Visual Studio 2010 makes development for SharePoint really painless. The following steps will help us setup and build an Event Receiver in no time.
Step 1: Start off with a new project and select the “SharePoint 2010” template. Select “Event Receiver” project type. I’ve given the Project name as “CustomContentPublishFeature”. Click OK.
Step 2 The SharePoint Customization Wizard will come up as shown below. It gives you two deployment options for your feature, one is Sandboxed and other is Farm deployment. I selected Sandboxed because I want the feature to be limited to the site collection where I am publishing only.
Click next to get to the “Event Receiver Settings” page.
Step 3: As seen below we’ve selected our “Type of event receiver” to be “List Item Events” and “Event Source” to be the “Pages Library” because that’s the library use has the pages that have our custom content type.
From the list of events that can be handled, select “An item was updated”. You can add more events later too.
Step 4: Click Finish and wait for Visual Studio to do the skeleton work for you. In VS 2008 you have to do most of the skeleton work yourself, but in VS 2010 it’s all setup for you. Once VS finishes processing, you will have a solution structure similar to the below image.
Step 5: The event receiver class will be called EventReciever1. I’ve renamed it to PublishingsPageEventReceiver. I had to rename the class file and the CS file manually.
You will notice as a part of the EventReceiver you’ve a cs file and an xml file called Elements.xml. The Elements file is a kind of glue that binds your code to SP Event framework. Let’s explore it in a little more details.
The <Receivers> node has the collection of custom event receivers that you are packing in this feature. They apply to List Types (indicated by ListTemplateId=850).
The <receiver> node describes each receiver class you have.
<Name> Event Receiver name used by SharePoint in admin screens. You can modify this to anything you want
<Type> refers to the event. Don’t modify this unless you are modifying the corresponding override method in the receiver’s .cs file.
<Assembly> element is a placeholder for the full assembly name. This is auto updated by Visual Studio tooling before deployment. It needs to have the fully qualified assembly name. Note you have a key.snk file so your dll is going to be a signed DLL.
<Class> refers to the fully qualified name of the class that has the event handler method.
<SequenceNumber> if you have multiple event receivers for the same event you can sequence them using the sequence number.
Step 6: Next if you expand the Feature node you’ll see an element called Feature1. I have renamed it to CustomContentPublishFeature. Double click on it bring up the Feature properties editor. There isn’t much for us to do here other than giving it a more meaningful Title and Description. You can also change the deployment scope here from Web to farm etc. We’ll keep it the default - Web.
Close the feature editor.
We are almost done and if you notice we still haven’t written any ‘custom’ code yet. That’s the next part.
Step 7: Right click on the “PublishPagesEventReceiver” and select Add->Class. Name the class ConvertContentTypeToHtml.cs. This class will encapsulate our ‘logic’ to generate HTML from our list item. It just has a single method called GetHtml(…) that has only one input parameter the item that is being updated/published. The included code has the column names hardcoded. In an ideal world we would use an xsl transformation to convert the item’s xml into html. I’ll cover xsl transforms and more customizations in a future article. For now we’ll use the quick-and-dirty ‘code transformation’.
Step 8: Now Right click on the “PublishPagesEventReceiver” and select Add->Class again. Name the class SharePointFileHelper.cs. This class will save our transformed html as a file into a given sharepoint library. This class also has a single method SaveFileToLibrary that takes the site url, library name, html content and file name as parameters.
Step 9: Last step is to tie things up. Open the PublishPagesEventReciever.cs file. Below the base.ItemUpdated(properties) method call add the following code:
In the above code, we do the following:
Retrieve the list item from the properties parameter.
Retrieve the file from the list item
Check if the file status is published. If it’s not published we don’t need to do anything
If it is published, gets the html from the item using our custom ConvertContentTypeToHtml class.
Then user the SharePoint helper class to save the file.
a. The Site URL is easily obtained from the list item itself.
b. We want to publish it to the default document library that’s called “Documents”. If you want the published file to go to a different library just give the correct qualified name.
c. Html – is the html content that needs to be saved.
d. And the file name that we’ve derived from the item’s title.
Debugging Custom Event Receivers
I mentioned earlier also how easy it is to develop for SP 2010 using VS 2010. After you’ve done all the above steps, all you need to do it hit F5 and sit back. If you want to see what VS 2010 is doing behind the scenes you can peek into the ‘Output’ window. A sample view would be something like follows.
Essentially VS is taking care of all the plumbing work needed to deploy and register your feature with SharePoint. All this was possible through the excellent WSP builder tool in VS 2008 too, but you still needed to attach the debugger to the process manually. In VS 2010 you don’t even have to do that. VS will start an IE instance and load the required site up and attach the debugger to the process for you. You just provide your credentials and log in.
To verify everything is correct follow the steps below
Step 1: Put a breakpoint in the first line of ItemUpdated(…) method.
Step 2: Hit F5 to run the application. Let VS start IE and if you are asked credentials for the site provide credentials for the site.
Step 3: Navigate to the Pages library
Step 4: Click on the ‘Contoso Mobile Functional Spec’ that we had created in our previous article.
Step 5: Go to Site Actions->Edit Page
Step 6: Update some values and click on “Save and Close” button. If everything was setup right, the debugger will kick in and you should now hit the break point.
Step 7: Setup another breakpoint inside the if(…) condition. Hit F5 to continue. Don’t be surprised when this breakpoint is not hit. If you inspected the file.Level property value you would notice it’s still Draft hence the ‘if’ condition is not satisfied and no publish happened. If you have come this far thing are moving in the right direction. Remove the breakpoint outside the ‘if’ condition and go back to IE.
Step 8: Select the ‘Publish’ tab on the ribbon and click on ‘Submit’ button.
Step 9: Provide submission comments and click OK. The following screen will come up. This screen initiates the default approval workflow. Provide some details in the Request field and click on Start.
Step 10: If you are wondering who the approval went to, by default it goes to the users in the default group called ‘Approvers’. If you don’t have any users in that group the workflow will still start but you’ll see a couple of errors regarding failure to send mail (even if you have outgoing email setup correctly).
Step 11: If you have a user in the Approver group login as that user and navigate to the page. Select the Publish tab and click on Approve. Provide approval comments and wait for debugger to kick in.
If you don’t have a user in the Approver group just go to Publish tab and click on the ‘Status’ button.
It will navigate to a page that looks as follows:
Click on the ‘Please approve Contoso Mobile-Functional Specification’ link to bring up the approval comments window.
See how the comments that were put in the workflow start have come up here. Click on Approve. Give it a few moments before you hit the debugger in Visual Studio
I’ve put in a dummy check to verify the return value. If the return value is true, everything went fine. Hit F5 and let the session continue.
But wait a second, why did we hit the breakpoint now? That’s because as soon as the workflow was approved, SharePoint internally marked the page as Published. That changed the file.Level property’s value to ‘Published’ and this kicked off the ‘Updated’ event handler. This time the ‘if’ condition was satisfied and we thus hit the breakpoint.
Step 12: Go back to IE instance and click on the ‘Libraries’ link in the left navigation pane
Click on Documents
Step 13: Voila! You see an HTML file with the same name as the title of your functional specification page! Click on the menu button (down arrow that comes up when you hover on the ‘Name’ field) and select ‘Send To->Download a Copy’.
Step 14: Save the file and open it. It will show something like the following
Okay, that’s not the prettiest HTML rendering, but you get the gist. There are some rough edges like the approved by name has a few characters in front of it. Those can be smoothened out when we use a more advanced xsl transform or you can get as fancy as you want in the ConvertContentToHtml class.
But as of now we have successfully built and tested our own Event Receiver and published it as a SharePoint Feature. Hurray!!!
Note: We have so far seen deployment in a development environment only. I will have a quick follow-up on how to do the deployment in a server environment. It’s nothing but a stsadm command followed by activation steps through the site configurations.
We saw how to create an Event Receiver and tie it up to a site as SharePoint Feature. Event Receivers are handy hooks into SharePoint’s content management flow. They allow you to intercept and manipulate default behavior. You can do custom validations, business rules enforcement and lots of other little customizations that don’t come ‘out of box’ in SharePoint. Overall in this (loosely coupled three part) series I’ve tried to solve a common content management problem in an Enterprise business setup. I will follow up with a couple of more enhancements that will have some more custom development. Till then, have fun!
I have the “Feature Extension Runtime” installed through my VS2010 Extensions manager. The attached code was built with this extension enabled. Following is a snapshot of ‘extensions’ I got for my VS 2010 installation. If you haven’t checked out the available extensions so far go to Tools->Extension Manager… and have fun.
The source code of this article can be downloaded over here