SharePoint: Setting Web Properties using a Feature in Custom Site Definitions


In this post we’ll create a feature which sets property values of the SPWeb instance against which the feature is activated. The feature is designed to be used within custom site defintions to allow the created web site to be configured in ways suitable to the site defintion.

The feature has the following characteristics;

  1. There are no feature manifests
  2. The properties to set and their values will be provided using feature defintion properties
  3. The feature will have a feature receiver
  4. The feature should be able to set the SPWeb’s object model properties
  5. The feature should be able to set or create an SPWeb’s property bag properties

Building the Feature.

Create a standard Web scoped feature with a feature receiver class;

<Feature  Id="cfb3318a-88bd-45c4-9037-ab3d09cd32b5"
          Title="Per Web Properties"
          Description="This feature is used to set Web properties and is intended for use within Site Definitions."
          Version="12.0.0.0"
          Hidden="FALSE"
          Scope="Web"
          DefaultResourceFile="core"
          ReceiverAssembly="PerWebProperties, Version=1.0.0.0, Culture=neutral, PublicKeyToken=34303ed68254af5e"
          ReceiverClass="PerWebProperties.PerWebProperties"
          xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>

As you can see there are no feature manifest files. Next write the code for the feature receivers FeatureActivated method;

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
	var featWeb = (SPWeb)properties.Feature.Parent;
	var featWebID = featWeb.ID;

	using (var web = featWeb.Site.OpenWeb(featWebID))
	{
		var propsUpdated = false;
		foreach (SPFeatureProperty featProperty in properties.Feature.Properties)
		{
			if (string.IsNullOrEmpty(featProperty.Name)) continue;
			if (string.IsNullOrEmpty(featProperty.Value)) continue;

			var featPropNameParts = featProperty.Name.Split(new[] {':'}, StringSplitOptions.RemoveEmptyEntries);
			if (featPropNameParts.Length == 1)
			{
				// set object model properties
				switch(featPropNameParts[0].ToLower())
				{
					// special case properties
					case "locale":
					{
						var ietfLocaleString = featProperty.Value;
						if (!web.Locale.IetfLanguageTag.Equals(ietfLocaleString, StringComparison.OrdinalIgnoreCase))
						{
							var nci = new CultureInfo(ietfLocaleString);
							web.Locale = nci;
						}
						break;
					}
					// for non special case properties just use reflection
					default:
					{
						SetWebProperty(web, featPropNameParts[0], featProperty.Value);
						break;
					}
				}
			}
			else if (featPropNameParts.Length == 2)
			{
				if (featPropNameParts[0].ToLower() == "p")
				{
					// Properties (SPPropertyBag)
					if (web.Properties.ContainsKey(featPropNameParts[1]))
						web.Properties[featPropNameParts[1]] = featProperty.Value;
					else
					{
						web.Properties.Add(featPropNameParts[1], featProperty.Value);
						propsUpdated = true;
					}
				}
			}
		}

		if (propsUpdated)
			web.Properties.Update();
		web.Update();
	}
}

The code which sets SPWeb properties with reflection is shown;

private static void SetWebProperty(SPWeb web, string propertyName, string propertyValue)
{
	if (web == null)
		throw new ArgumentNullException("web");
	if (string.IsNullOrEmpty(propertyName))
		throw new ArgumentNullException("propertyName");

	var t = web.GetType();
	if (t == null) throw new Exception("Invalid reflection object");
	var pi = t.GetProperty(propertyName);
	if (pi == null) throw new Exception("Invalid property: " + propertyName);

	if (!pi.CanWrite)
		throw new NotSupportedException("Property " + propertyName + " is Read Only!");

	try
	{
		var ovalue = Convert.ChangeType(propertyValue, pi.PropertyType);
		pi.SetValue(web, ovalue, null);
	}
	catch (Exception ex)
	{
		throw new Exception(string.Format("Unable to set property: {0} to: {1}",
										propertyName, propertyValue), ex);
	}
}

Finally build, package and deploy the solution to the farm.

Using the Feature in a Custom Site Defintion.

To use the feature in a custom site definition add the following snippet to the WebFeatures section of your site defintions Configuration element, obviously setting those properties as appropriate.

 <!-- Per Web Properties -->
 <Feature ID="cfb3318a-88bd-45c4-9037-ab3d09cd32b5">
	  <Properties xmlns="http://schemas.microsoft.com/sharepoint/">
			<Property Key="MasterUrl" Value="/_catalogs/masterpage/default.master"/>
			<Property Key="CustomMasterUrl" Value="/_catalogs/masterpage/default.master"/>
			<Property Key="Locale" Value="en-GB"/>
			<Property Key="p:myCustomProperty" Value="woo hoo, some useless data value"/>
	  </Properties>
 </Feaure>

You can specify the property name in 2 ways, to specify a property bag property, prefix the property name value with p:

E.g. p:myCustomProperty

To specify an object model property to set, just specify the SPWeb property name;

E.g. MasterUrl

Published by

Phil Harding

SharePoint Consultant, Developer, Father, Husband and Climber.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.