Ok, I'll admit that this is not the most snappily-named blog title; however it will address a specific problem that I've hit a few times at different customers; which is how to make automated modifications to sites after they have been deployed and used.

The Problem

The scenario I am hoping to address here is when you build a site structure based on standard site templates but one day a requirement arises to change all of the sites for some reason (very common given the 'organic' nature of SharePoint). There are lots of reasons why you might need to do this; here are just some of the common ones:

- Changing master page, CSS or theme on specific sites or part of any overall structure

- Associating content types or site columns with all libraries

- Adding specific security settings to a site

The list goes on, this basically applies to anything that you could do through Site Settings but want to automate.

The problem is that the sites are already out there being used and therefore you cannot change the template or definition that the site is based on (which you'd do if you knew about the requirement beforehand). The other approach is manually configuring the sites via the UI, but this is just not realistic in large structures.

Throughout this article I'll use the scenario of adding a content type (let's say the standard 'Dublin Core Columns' content type) to every document library in the site structure, but this could be applied to a plethora of different scenarios .... anywhere where you need to execute some one-off code to do something to an existing site or range of sites.

The Solution

To address this requirement, we build what is known as a 'Code Call-out Feature', this is basically a feature that simply executes some code when it is activated. Once written and installed, you can activate the feature by using STSADM.exe command line utility. You can use a batch file to repeat this command throughout your entire site structure if desired (more on this later).

Before you get worried about the fact that we are talking about code here, don't worry – it is really very simple, even if you are not a 'developer'. The following steps have been written for someone that has only very basic experience with Visual Studio, so if you are a more established developer some of this may be a little obvious.

Step 1 – Setup your Visual Studio project

The first step is to setup your Visual Studio project for the .net assembly that will get called when your feature is activated. Simply follow these steps:

a. Open Visual Studio and create a C# 'Class Library' project called 'CTAssociation'

b. In Solution Explorer, right-click on your project and click 'Add Reference', go to the 'Browse' tab and choose Microsoft.SharePoint.dll (normally located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\Microsoft.SharePoint.dll)

c. Go to the project properties (Projects > CTAssociation properties) and do the following:

- On the Application tab set the Assembly name to 'CTAssociation.DublinCore' and the namespace to 'CTAssociation'

- On the Signing tab, check 'Sign the assembly' and create a new Strong Name Key file (choose 'new' from the drop-down)

d. Open 'Class1.cs' and change it so that it looks like this (I've highlighted the things that you need to add or change):

e. Save and Build your project (ensure you have no errors)

2 – Add your assembly to the GAC

Providing your project built OK, you should now have a new DLL called CTAssociation.DublinCore.dll located in My Documents\Visual Studio 2005\Projects\CTAssociation\CTAssociation\Bin\Debug. We need to add this to the Global Assembly Cache (GAC). To do this follow these steps:

  1. Go to Start > All Programs > Administrative Tools > Microsoft .net Framework 2.0 Configuration
  2. Expand My Computer > Assembly cache, right click and choose 'Add'
  3. Navigate to your DLL (My Documents\Visual Studio 2005\Projects\CTAssociation\CTAssociation\Bin\Debug\CTAssociation.DublinCore.dll) and add it

Now that the assembly has been added to the GAC, you will be able to get the Public key Token which will be required later one, to do this follow these steps:

  1. In the .net Configuration Utility click on My Computer > Assembly Cache and click 'View List of Assemblies in the Assembly cache' which is located on the right-side pane.
  2. Located your assembly (CTAssociation.DublinCore)
  3. Double click your assembly to open its properties and take a careful note of the Public Key Token (I like to copy it and paste into notepad)
  4. Close the .net Framework Configuration tool

3 – Setup your Feature

Now that your assembly has been added to the GAC, we need to build the feature that calls it when activated. The 'Code Callout feature' is one of the simplest ones to create, follow these steps

All features have to have a unique identifier called a GUID. The easiest way to create a GUID is to use a tool that comes with Visual Studio called GUIDgen.exe. Follow these steps:

  1. Navigate to C:\Program Files\Microsoft Visual Studio 8\Common7\Tools and load GuidGen.exe (search for it if it is not in this location)
  2. Click 'New Guid'
  3. Set the format to '4. Registry format'
  4. Copy the contents of the result but without the {}. You should have a string of random numbers and letters in 5 blocks delimited by a hyphen. Again, I like to paste this into Notepad to avoid typos.

Now that you have your GUID and Public key Token, you can create the actual feature. To do this follow these steps:

  1. In Windows Explorer, navigate to your Features directory (usually C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES)
  2. Create a new folder in \template\Features called 'CTAssociationDublinCore'
  3. Within the CTAssociationDublinCore folder, create a new XML file called feature.xml
  4. Add the following code to feature.xml and save (replace the ID with your new GUID and PublicKeyToken with the one you took note of earlier)

    <Feature

        Scope="Web"

        Title="Content Type Association - Dublin Core Columns"

        Id="BD26E0C3-3AC7-4c07-90AD-54FF3A908A95"

        ReceiverAssembly="CTAssociation.DublinCore, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d43c4c8ff8fcf2b6"

        ReceiverClass="CTAssociation.DublinCore"

        xmlns="http://schemas.microsoft.com/sharepoint/">

    </Feature>

4 – Install your Feature

Installing your feature could not be simpler. There is an STSADM.exe parameters called 'InstallFeature'. To install the feature follow these steps:

  1. Open a command prompt and navigate to C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN
  2. Enter the following command

    stsadm.exe -o installfeature -name "CTAssociationDublinCore" –force

Providing all is well your feature should now be installed. Next thing to do is test that is basically works (i.e. throws the "This feature was activated!!" message when activated). To do this follow these steps:

  1. Open the browser and navigate to any site in your site collection
  2. Go to Site Actions > Site Settings > Modify All Site Settings
  3. Click on Site Features
  4. You should see 'Content Type Association – Dublin Core' in the list of feature, simply click 'Activate'.
  5. If all is well you should see this message:

    <screen of error>

5 – Writing the code to add a Content Type

At this point you have all the plumbing in place to starting adding functionality to your feature. This is the point where you add the code to make the modification that you require.

It is not in the scope of this article to explain how to do this in detail; the article is more about how to create the feature. However, in our example of adding a new content type to all document libraries, the code would look something like this:

SPWeb oWeb = (SPWeb)properties.Feature.Parent;

SPContentType oCT = oThisWeb.Site.RootWeb.ContentTypes["Dublin Core Columns"];

foreach (SPList oList in oThisWeb.Lists)

{

//check if the current list is a document library

if (oList.BaseTemplate == SPListTemplateType.DocumentLibrary)

{

dDocLibCount += 1;

oList.ContentTypesEnabled = true;

try

{

oList.ContentTypes.Add(oCT);

//if the above throws an exception, it means the content type is already on this list. This does not matter so catch the exception and carry on

}

catch

{

}

}

//if there were no document libraries throw an exception

if (dDocLibCount == 0)

{

    throw new Exception("There were no lists of the Document Library type in this Site, therefore no updates were made and the Content Type was not associated with any lists.");

}

//update the list

oList.Update();

}

Summary

In summary, code-callout features are a very powerful capability which allow you to make changes to you existing Sharepoint solutions after they have been deployed and are actively being used.