The Fabulous 40 templates for SharePoint, or Application templates for Windows SharePoint Services 3.0, became very popular in many MOSS 2007 environments. They were originally created just as examples of what could be done with custom templates in SharePoint, however, they were quickly adopted into many production environments as-is, dispite the original intent. Looking over the list of available templates, it's easy to see why. There is a lot there.
Unfortunately, these widely used templates do not migrate well to SharePoint 2010 and 2013. If you ignore all of the migration warnings and errors about missing templates and content types, you'll end up with quite a mess. Some fair the migration a little better than others, while some look like they're total losses.
A while back I was a part of a SharePoint 2007 to 2010 migration of a pretty sizable farm. It consisted of around 8500 site collections across 4 web applications, had self-service site creation enabled, and had the Fab40 templates installed. The client did not want to have any loss of functionality for the templates that were actively being utilized by the users. If you could create a Project Tracking Workspace site, or an IT Team Workspace prior to the migration, the newly upgraded farm should also. It was important that the users saw this upgrade as a step forward, not a step backwards. Losing the templates they utilized heavily would not have gone over well. So, it wasn't just a matter of migrating all of that existing content over, but also bringing forward functional templates for the creation of new sites in the future as well. Through some server object model surfing, iterating through all sites across the entire farm, I was able to determine just how many were utilizing which templates. Based on those numbers, it was decided to target only a specific 12 templates, out of the 40, for migration and conversion to the new 2010 farm.
To retain the same functionality going forward, the goals were to:
- allow the user to create new sites from these templates, same as before
- fully migrate the existing sites with no loss of data what so ever
- elliminate all migration errors thrown during the upgrade
To do this I utilized a very powerful SharePoint project template that comes with Visual Studio (2010 at the time). The "SharePoint 2010 - Import Solution Package" template. This template prompts you for a WSP file, and then exploads it's contents out into a new Visual Studio project. A ton of the work converting these solutions was done right there. All list definitions, content types, elements, feature manifests, almost everything are right there. Compiled code, such as feature event receivers however, were simply dlls, and not quite as straight forward.
Challenge Number One - The Event Recievers
The event receivers and any other event handlers that existed needed to be rewritten. Many signs pointed to these as being the source of a lot of the compatibility issues on both migrated and newly created sites. Since I didn't have the code to these, I had to figure out what they were suppose to be doing, and recreate that same functionality Using .NET Reflector or ILSpy (my prefered choice), I was able to look inside these assemblies and trace their logic. The decompiled code from utilities like these is never pretty. It's full of repeated code and goto statements. GOTOs! So simply taking that code as-is is not advised, and a rewrite is in order to ensure readability and maintainability for future developers.
Doing this also widened the scope of this challenge a bit, bringing to light another interesting problem. We don't have the strong name key (.snk file) that Microsoft signed the original MOSS 2007 solutions with. Our new solution will be signed with a completely different strong name key. So even if you use the same namespaces, class names, and assembly output names, the PublicKeyToken will still make all references different. All of these references to the newly written event handlers/receivers have to be updated in the feature manifests that include them. For example, every reference of the event handler:
KnowledgeBaseEventHandler, Version=184.108.40.206, Culture=neutral, PublicKeyToken=71e9bce111e9429c
will need be replaced with your own. This will take care of any new sites being created in 2010 using these converted templates. But, however, there is more to it than just this.
Any of the already existing sites being migrated will have lists with references to the old handlers/receivers, as these values are stored in the content databases we'll be migrating. These need to be fixed post-migration. There were many situations like this, that I will try and cover, where the post-migration sites needed to be massaged a little, programmatically, using the server object model. In this case, I had to rip through every site that uses a Fab40 template, and then run through all of it's lists, and for each list a collection of event receivers, replacing the old for the new. Here is how I did that via a post migration C# console app:
static public void EventReceiversFix(SPWeb pWeb)
using (SPWeb lWeb = pWeb.Site.OpenWeb(pWeb.ID))
SPListCollection lLists = lWeb.Lists;
for (int i = 0; i < lLists.Count; i++)
SPList lList = lLists[i];
SPEventReceiverDefinitionCollection lReceivers = lList.EventReceivers;
for (int j = 0; j < lReceivers.Count; j++)
SPEventReceiverDefinition lReceiver = lReceivers[j];
if (lReceiver.Assembly == "KnowledgeBaseEventHandler, Version=220.127.116.11, Culture=neutral, PublicKeyToken=71e9bce111e9429c")
lReceiver.Assembly = "ApplicationTemplateCore2010, Version=18.104.22.168, Culture=neutral, PublicKeyToken=0a54b2ce78cfb020";
lReceiver.Class = "MyCompany.ApplicationTemplateCore.KnowledgeBaseEventHandler";
Logging.WriteLog("...Fixed KnowledgeBaseEventHandler reference");
else if (lReceiver.Assembly == "DocumentReviewEventHandler, Version=22.214.171.124, Culture=neutral, PublicKeyToken=71e9bce111e9429c")
lReceiver.Assembly = "DocumentLibraryReview2010, Version=126.96.36.199, Culture=neutral, PublicKeyToken=0a54b2ce78cfb020";
lReceiver.Class = "MyCompany.DocumentLibraryReview2010.DocumentReviewEventHandler";
Logging.WriteLog("...Fixed DocumentReviewEventHandler reference");
This can also be done via a PowerShell script. I just wasn't fully up on my PowerShell at the time I wrote this, and opted for creating a console app instead. The above method is a single piece of a much larger post-migration process. The console app ran through all web applications in the farm, and all site collections under those applications, calling fix methods, such as the one above, for various items that needed attention.
I will cover more such challenges faced in the next part of this series.