Friday, 16 April 2010

Update existing sharepoint content types when deploying using a feature

I’ve been a little quiet recently, mainly because I’ve had my head down with a large SharePoint and K2 blackpearl project in Manchester, in which I’ve got lots to blog about but just haven’t had the time. However, I think this is pretty important - today I solved a little problem that was bugging me.

My scenario is this;

We have SharePoint sites, created from STP files as part of our line of business application (I know, I know, the STP bit is a bit stupid and it’s hard to update, but we are where we are), and the document libraries within these created sites use content types deployed in the root site which are defined and deployed with a feature.

I needed to add a new field to one of the content types and remove a field from another one. Sounds easy enough – I updated the XML for the definition, redeployed and the definition in the site content type library updated as expected. What I didn’t expect though was all the sites already provisioned didn’t get those changes applied, and even worse, when I provisioned a new site (from the STP) the document library in that also didn’t have the changes. It was almost like the site document libraries had their own copy of the content types.

Of course that’s EXACTLY what the problem is. Solution 1 is not to deploy changes through the features. Using the UI to add or remove the columns as necessary and select the option to also update anything that uses that content type. For me this is a non starter – I’d have to re-export all my STPs so that future provisioned sites get the changes as well as make the manual changes to the content type and remember to propagate down – something that doesn’t sit well with my build and deploy strategy.

So, I found another solution. By attaching a feature receiver I can programmatically change content types and have those changes propagated down through everything that uses it. It’s still not ideal, but it works well. I keep my XML definition static and apply additive changes through the feature receiver in code, which looks like this;

  1. public class ContentTypeFeatureReceiver : SPFeatureReceiver
  2. {
  3.     /// <remarks/>
  4.     [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
  5.     public override void FeatureInstalled(SPFeatureReceiverProperties properties)
  6.     {
  7.     }
  8.  
  9.     /// <remarks/>
  10.     [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
  11.     public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
  12.     {
  13.     }
  14.  
  15.     /// <remarks/>
  16.     [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
  17.     public override void FeatureActivated(SPFeatureReceiverProperties properties)
  18.     {
  19.         SPSite site = properties.Feature.Parent as SPSite;
  20.         if (site == null)
  21.             return;
  22.  
  23.         SPWeb rootWeb = site.RootWeb;
  24.  
  25.         // Here, modify any of the existing content types
  26.         rootWeb.AddFieldToContentType("FieldToAdd", "Content Type Add To");
  27.         rootWeb.RemoveFieldFromContentType("FieldToRemove", "Content Type To Remove From");
  28.     }
  29.  
  30.     /// <remarks/>
  31.     [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
  32.     public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
  33.     {
  34.     }
  35.  
  36. }

The AddFieldToContentType and RemoveFieldFromContentType methods are extension methods as follows;

  1. public static class SPWebExtensions
  2. {
  3.     /// <remarks/>
  4.     public static void AddFieldToContentType(this SPWeb site, string fieldName, string contentTypeName)
  5.     {
  6.         if (!site.Fields.ContainsField(fieldName))
  7.         {
  8.             Console.WriteLine("Could not add {0} to {1} - the field {0} does not exist!", fieldName, contentTypeName);
  9.             return;
  10.         }
  11.  
  12.         SPField field = site.Fields[fieldName];
  13.         SPContentType contentType = site.ContentTypes[contentTypeName];
  14.         if (contentType == null)
  15.         {
  16.             Console.WriteLine("Could not add {0} to {1} - the content type {1} does not exist!", fieldName, contentTypeName);
  17.             return;
  18.         }
  19.  
  20.         if (contentType.FieldLinks[field.InternalName] != null) return;
  21.         contentType.FieldLinks.Add(new SPFieldLink(field));
  22.         contentType.Update(true);
  23.     }
  24.  
  25.     /// <remarks/>
  26.     public static void RemoveFieldFromContentType(this SPWeb site, string fieldName, string contentTypeName)
  27.     {
  28.         if (!site.Fields.ContainsField(fieldName))
  29.         {
  30.             Console.WriteLine("Could not remove {0} from {1} - the field {0} does not exist!", fieldName, contentTypeName);
  31.             return;
  32.         }
  33.  
  34.         SPField field = site.Fields[fieldName];
  35.         SPContentType contentType = site.ContentTypes[contentTypeName];
  36.         if (contentType == null)
  37.         {
  38.             Console.WriteLine("Could not remove {0} from {1} - the content type {1} does not exist!", fieldName, contentTypeName);
  39.             return;
  40.         }
  41.         if (contentType.FieldLinks[field.InternalName] == null) return;
  42.         contentType.FieldLinks.Delete(field.InternalName);
  43.         contentType.Update(true);
  44.     }
  45. }

Finally, you still have to define the site column before you can attach it.

No comments:

Post a Comment