Getting Event Receivers to Fire in SharePoint Content Types

Recently I had a need to hook up an event receiver to a content type. Why not just a list or a list template you might ask?  Well the scenario is the recent move of our Intranet from 2007 whereby we wanted to move our old documents into new libraries but make use of managed Metadata. 

Now we had constructed a custom Managed Metadata Field from scratch and plugged it into our Managed Metadata Service Application and one aspect of doing this is in order for everything to work AND for your Managed Metadata terms to appear as Search Refinements, an event receiver is needed to be registered for each Library that has the Managed Metadata Field.

The problem was that we were using a codeplex tool to migrate the libraries and the tool would create the library. Sure we could add in our Managed Metadata field afterwards via a content type but the event receiver necessary for Search Refinements to work would need to be tied to the Template ID of the list which no doubt would be 101 the standard document library. Didn’t fancy that, as I didn’t want the event receiver firing when there were no Managed Metadata field in a library with template 101.  The answer was to hook up the necessary event receivers to the content type. Here’s how you do it.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="
http://schemas.microsoft.com/sharepoint/&quot;>
    <!– Parent ContentType: Document (0x0101) –>
    <ContentType ID="0x010100401BA56F946B4A2D991C699A44D6116A"
                 Name="Ridgian Document"
                 Group="Ridgian"
                 Description="Ridgian Base Document Content Type"
                 Inherits="TRUE"
                 Version="0">
        <FieldRefs>
            <FieldRef ID="{fa564e0f-0c70-4ab9-b863-0177e6ddd247}" Name="Title" Required="TRUE" />
            <FieldRef ID="{81291702-2603-431B-9EA8-EBDAAD457C1A}" Name="RidgianKeywords" DisplayName="Keywords" Required="TRUE" />
            <FieldRef ID="{A6FDD77B-AD2E-4E8D-9579-065488900F1C}" Name="RidgianKeywordsTaxHTField0" DisplayName="KeywordsTaxHTField0" Hidden="TRUE"/>
            <FieldRef ID="{f3b0adf9-c1a2-4b02-920d-943fba4b3611}" Name="TaxCatchAll" Hidden="TRUE" />
            <FieldRef ID="{8f6b6dd8-9357-4019-8172-966fcd502ed2}" Name="TaxCatchAllLabel" Hidden="TRUE" />
            <FieldRef ID="{BCDA41CE-70D2-490A-AFBF-3D7E576D211F}" Name="External" Description="Set to ‘Yes’ if document can be viewed Externally."/>
        </FieldRefs>
        <XmlDocuments>
            <XmlDocument NamespaceURI="
http://schemas.microsoft.com/sharepoint/events&quot;>
                <spe:Receivers xmlns:spe="
http://schemas.microsoft.com/sharepoint/events&quot;>
                    <Receiver>
                        <Receiver>
                            <Name>TaxonomyItemAddingEventReceiver</Name>
                            <Synchronization>Synchronous</Synchronization>
                            <Type>1</Type>
                            <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
                            <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
                            <SequenceNumber>50</SequenceNumber>
                        </Receiver>
                        <Receiver>
                            <Name>TaxonomyItemUpdatingEventReceiver</Name>
                            <Synchronization>Synchronous</Synchronization>
                            <Type>2</Type>
                            <Assembly>Microsoft.SharePoint.Taxonomy, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
                            <Class>Microsoft.SharePoint.Taxonomy.TaxonomyItemEventReceiver</Class>
                            <SequenceNumber>50</SequenceNumber>
                        </Receiver>
                    </Receiver>
                </spe:Receivers>
            </XmlDocument>
            <XmlDocument NamespaceURI="
http://schemas.microsoft.com/sharepoint/v3/contenttype/forms&quot;>
                <FormTemplates xmlns="
http://schemas.microsoft.com/sharepoint/v3/contenttype/forms&quot;>
                    <Display>RidgianDocumentLibraryForm</Display>
                    <Edit>RidgianDocumentLibraryForm</Edit>
                    <New>RidgianDocumentLibraryForm</New>
                </FormTemplates>
            </XmlDocument>
        </XmlDocuments>
    </ContentType>

</Elements>

 

It’s through the XmlDocument elements within the CAML definition where the connection is made. Of course you can do this in code if you like. The key thing I found in order to get things working properly was to declare the type of the event receiver as a number, not as the description as you would normally do. So the ItemAdding event enumerated value is 1. The ItemUpdating is 2.  I discovered this when looking at the way event receivers are hooked up to DocumentSets when working with the Content Organiser.  If you want the full enumerated values for event receivers, here they are straight from Microsoft.SharePoint.dll courtesy of Redate .NET Reflector, seeing as MSDN don’t actually publish the values …

public enum SPEventReceiverType
{
    ContextEvent = 0x7ffe,
    EmailReceived = 0x4e20,
    FieldAdded = 0x2775,
    FieldAdding = 0x65,
    FieldDeleted = 0x2777,
    FieldDeleting = 0x67,
    FieldUpdated = 0x2776,
    FieldUpdating = 0x66,
    InvalidReceiver = -1,
    ItemAdded = 0x2711,
    ItemAdding = 1,
    ItemAttachmentAdded = 0x2717,
    ItemAttachmentAdding = 7,
    ItemAttachmentDeleted = 0x2718,
    ItemAttachmentDeleting = 8,
    ItemCheckedIn = 0x2714,
    ItemCheckedOut = 0x2715,
    ItemCheckingIn = 4,
    ItemCheckingOut = 5,
    ItemDeleted = 0x2713,
    ItemDeleting = 3,
    ItemFileConverted = 0x271a,
    ItemFileMoved = 0x2719,
    ItemFileMoving = 9,
    ItemUncheckedOut = 0x2716,
    ItemUncheckingOut = 6,
    ItemUpdated = 0x2712,
    ItemUpdating = 2,
    ListAdded = 0x2778,
    ListAdding = 0x68,
    ListDeleted = 0x2779,
    ListDeleting = 0x69,
    SiteDeleted = 0x27d9,
    SiteDeleting = 0xc9,
    WebAdding = 0xcc,
    WebDeleted = 0x27da,
    WebDeleting = 0xca,
    WebMoved = 0x27db,
    WebMoving = 0xcb,
    WebProvisioned = 0x27dc,
    WorkflowCompleted = 0x2907,
    WorkflowPostponed = 0x2906,
    WorkflowStarted = 0x2905,
    WorkflowStarting = 0x1f5
}

 

Hope that helps somebody

Cheers

Dave Mc

Advertisements

The XSLT and SharePoint and HttpVDir

Some might say both these technologies come from Old Nick himself , but I’m afraid I’ll have to disagree right there.  XSLT has always been one of my all time favourite technologies, it’s uses are endless and there are many places in SharePoint where it’s a definite bonus to know XSLT to a good level.

So I just wanted to give you an idea of how neat a technology XSLT is and how integrated into the SharePoint stack it is. 

Recently I was working on a custom list view which needed some custom XSLT and on this page I needed to create a link to another custom listview page.  Now the problem was that the URL of our test box was http://ourtest.url/sites/intranet whereas the production farm was http://ourproduction.url .This created an issue in that if we weren’t careful each time we deployed to production we’d have to change the URL in the XSLT manually.

So initially our XSLT looked thus:

<ul class="nav"> 
  <li>
    <a href="/sites/intranet/list/Forms/NotWon.aspx">Not Won</a>
  </li>
  <li>
    <a href="/sites/intranet/list/Forms/Won.aspx">Won</a>
  </li>
</ul>

Thankfully there is a simple solution to this issue and that’s to use one of the XSLT Parameters that SharePoint has thankfully provided for us. 

XSLT Parameters are value handed to us from outside of the Style sheet in this case by SharePoint.  If you navigate to 14/TEMPLATE/LAYOUTS/XSL you will see the OOTB XSLT files used by SharePoint and you’ll see one called mail.xsl.  If you open that up you see a whole bunch of <xsl:param /> tags with difference names.  About half way down you’ll see HttpVDir.  This is really handy as it gives us the full URL of the current SPWeb item.  Check out MSDN for full details (in a second).  And this value is available to you in any custom XSL you care to write for your listviews.  So in order to construct a URL that would update automatically when deployed to other farms, we simply changed the above XSLT to read as follows:

<ul class="nav">
  <li>
    <a href="{$HttpVDir}/sites/intranet/list/Forms/NotWon.aspx">Not Won</a>
  </li>
  <li>
    <a href="{$HttpVDir}/sites/intranet/list/Forms/Won.aspx">Won</a>
  </li>
</ul>

Notice the shortcut syntax in XSLT used with attributes where the node or variable or in this case parameter appears simply in curly braces. Parameters in XSLT are always preceded by a $ sign. Job done.  Have a play around with some of the other parameters and you’ll be pleased that you did!

Cheers

Dave Mc

Page Layout and Text Layout Buttons Disabled on Ribbon

It’s been a while since I’ve blogged, just so much go on with work and other stuff, that I just haven’t gotten around to it, so I’m spending an evening catching up with some content I’ve had on sticky notes on my desktop.  So this first post is a bit vague to be honest, as it’s been so long since I had this issue, that I can’t quite remember what fixed it, BUT I did keep a record of the URL to the forum post which did the trick. 

Basically the clue is in the title, when trying to change the page layout of a custom publishing page, the Page Layout button was disabled on the Ribbon.  This post on the SharePoint Forums allowed me to fix the issue and I think it was Jules Anime’s solution which worked for me. Hope this helps somebody.

Next post coming up very soon.

Cheers

Dave Mc