Programmatically provisioning Variations in SharePoint Server 2010


Last year I wrote an article about programmatically provisioning Variation Hierarchies in SharePoint 2007. The point of that article was that there was really no way you could provision Variations in repeatable way in a supported fashion and had to use reflection to get the job done. The situation in SharePoint 2010 has changed a little. The process of creating Variations has been made more reliable my moving it completely to a Timer Job. So a new approach, requires new code, and here it is.

Updated Aug 30, 2010: the code sample of Step 2 has been updated. There was an issue that was causing SharePoint throw exception while trying to edit Variation Labels that were created programmatically.

Programmatically provisioning Variations

The process of provisioning Variations consists of three steps:

  1. Configuring Variation Settings (Site Actions > Site Settings >Variations)
  2. Configuring Variation Labels (Site Actions > Site Settings > Variation labels)
  3. Creating Variation Hierarchies (Site Actions > Site Settings > Variation labels > Create Hierarchies)

So in order to programmatically provision Variations in a repeatable fashion, you have to cover all three steps in your code.

Before you read further keep in mind that there is still no public API for creating Variations available in SharePoint 2010. As you will notice earlier, the code examples I show in this article, use internal properties. And although you don’t have to use reflection anymore this approach is still unsupported.

Step 1: Configuring Variation Settings

Configuring Variation Settings can be done using the Site Actions > Site Settings > Variation page.

Variation Settings page in SharePoint Server 2010.

As you can see this page hasn’t really changed since MOSS 2007 and contains the same settings as previously what makes it easy for us to write the code for:

private static void ConfigureVariationsSettings(SPWeb rootWeb)
{
    Guid varRelationshipsListId = new Guid(rootWeb.AllProperties["_VarRelationshipsListId"] as string);
    SPList varRelationshipsList = rootWeb.Lists[varRelationshipsListId];
    SPFolder rootFolder = varRelationshipsList.RootFolder;
    // Automatic creation
    rootFolder.Properties["EnableAutoSpawnPropertyName"] = "true";
    // Recreate Deleted Target Page; set to false to enable recreation
    rootFolder.Properties["AutoSpawnStopAfterDeletePropertyName"] = "false";
    // Update Target Page Web Parts
    rootFolder.Properties["UpdateWebPartsPropertyName"] = "true";
    // Resources
    rootFolder.Properties["CopyResourcesPropertyName"] = "true";
    // Notification
    rootFolder.Properties["SendNotificationEmailPropertyName"] = "false";
    rootFolder.Properties["SourceVarRootWebTemplatePropertyName"] = "CMSPUBLISHING#0";
    rootFolder.Update();

    SPListItem item = null;
    if (varRelationshipsList.Items.Count > 0)
    {
        item = varRelationshipsList.Items[0];
    }
    else
    {
        item = varRelationshipsList.Items.Add();
        item["GroupGuid"] = new Guid("F68A02C8-2DCC-4894-B67D-BBAED5A066F9");
    }

    item["Deleted"] = false;
    item["ObjectID"] = rootWeb.ServerRelativeUrl;
    item["ParentAreaID"] = String.Empty;
    item.Update();
}

The Variations Setting are being stored in a hidden List in the Root Web. Because this List is provisioned dynamically, you can get a reference to it by getting the value of the _VarRelationshipsListId property. For every setting from the Variations Settings page there is a corresponding property in the Property Bag of the Root Folder of the hidden List. Probably the most confusing setting here is the Recreate Deleted Target Page setting, which has to be set to false in order to enable the recreation.

Among the properties you can also find the SourceVarRootWebTemplatePropertyName property which contains the name of the Site Definition used to provision new Variation Hierarchies. This corresponds to the Source Variation settings while configuring Variation Labels.

Source Variation settings for configuring Variation Labels.

Next to the settings from the Variations Settings page, the hidden List contains one List Item which contains the configuration of the Variation Home setting.

Using the code snippet above you can provision the Variation Settings in a repeatable way.

Step 2: Configuring Variation Labels

Configuring Variation Labels in SharePoint Server 2010 is exactly the same as in MOSS 2007. You need to create a Variation Label for each variation in your site and once you’re done, you have to create the Variation Hierarchies.

Variation Labels can be provisioned using the following code snippet:

private static VariationLabel[] labels = {
    new VariationLabel
    {
        Title = "nl-nl",
        FlagControlDisplayName = "Nederlands",
        Language = "nl-NL",
        Locale = 1043,
        HierarchyCreationMode = CreationMode.PublishingSitesAndAllPages,
        IsSource = true
    },
    new VariationLabel
    {
        Title = "en-us",
        FlagControlDisplayName = "English",
        Language = "en-US",
        Locale = 1033,
        HierarchyCreationMode = CreationMode.PublishingSitesAndAllPages
    }
};

private static void CreateVariations(SPWeb rootWeb)
{
    Guid varListId = new Guid(rootWeb.AllProperties["_VarLabelsListId"] as string);
    SPList varList = rootWeb.Lists[varListId];

    foreach (VariationLabel label in labels)
    {
        SPListItem item = varList.Items.Add();
        item[SPBuiltInFieldId.Title] = label.Title;
        item["Description"] = label.Description;
        item["Flag Control Display Name"] = label.FlagControlDisplayName;
        item["Language"] = label.Language;
        item["Locale"] = label.Locale.ToString();
        item["Hierarchy Creation Mode"] = label.HierarchyCreationMode;
        item["Is Source"] = label.IsSource.ToString();
        item["Hierarchy Is Created"] = false;
        item.Update();
    }
}

Similar to MOSS 2007, configuration of each Variation Label is stored as a List Item in a hidden list (different than the Variations Settings), so for every Variation Label you need to in fact create a List Item and fill it with the required information.

Running the code snippet above will create two Variation Labels in your Site Collection:

Dutch and English Variation Labels created using the code snippet above.

Step 3: Creating Variation Hierarchies

This is the piece that has changed in SharePoint Server 2010 making creating Variation Hierarchies more stable and reliable. In MOSS 2007 Variation Hierarchies were created using a Long Running Operation in the w3wp.exe process. There were scenario’s when, if something went wrong, you had to perform IIS reset. As you can expect this would only break the whole process even further. In SharePoint Server 2010 creating Variation Hierarchies has been moved to a Timer Job which runs separately from the w3wp.exe process, which makes the whole thing more stable.

As the approach of creating Variation Hierarchies changed, the code that we used before became useless. The great thing about the new approach is, that because it relies on a Timer Job, it is easier to start and to control. You can programmatically create Variation Hierarchies using the following code snippet:

private static void CreateHierarchies(SPSite site, SPWeb rootWeb)
{
    site.AddWorkItem(Guid.Empty, DateTime.Now.ToUniversalTime(), new Guid("e7496be8-22a8-45bf-843a-d1bd83aceb25"), rootWeb.ID, site.ID, 1, false, Guid.Empty, Guid.Empty, rootWeb.CurrentUser.ID, null, String.Empty, Guid.Empty, false);
    SPWebApplication webApplication = site.WebApplication;
    SPJobDefinition variationsJob = (from SPJobDefinition job in
                                     webApplication.JobDefinitions
                                     where job.Name == "VariationsCreateHierarchies"
                                     select job).FirstOrDefault();

    if (variationsJob != null)
    {
        DateTime startTime = DateTime.Now.ToUniversalTime();
        variationsJob.RunNow();

        // wait until the job is finished
        while ((from SPJobHistory j in webApplication.JobHistoryEntries where j.JobDefinitionId == variationsJob.Id && j.StartTime > startTime select j).Any() == false)
        {
            Thread.Sleep(100);
        }
    }
}

The first thing that needs to be done is to add a work item. This is needed as the Timer Job responsible for creating Variation Hierarchies is a work-item timer job. Once it’s in place, you can start the job. To do that you need a reference to the Timer Job which is scoped to Web Application.

In the code sample above I use a pause routine that waits until the job finishes. This is important in scenarios when provisioning Variation Hierarchies is not the last step and there are some other steps in the queue which depend on the Variations. You are free to skip this part if you don’t need it in your scenario.

And that’s it. After running this last piece of code the Variation Hierarchies have been created:

Variation Hierarchies created using the code snippet above.

Summary

Variations are a great mechanism for delivering multilingual solutions. Provisioning Variations is a part of the configuration process and you should try to include it in your structure and repeatable deployment process. Although Variations in SharePoint Server 2010 have been improved and made more reliable, there is still no public API available for programmatically configuring and provisioning Variations. Although you don’t need to use reflection anymore in SharePoint Server 2010, you have to use hidden properties, which Variations depend upon, what makes any solution for programmatically provisioning Variations unsupported.

Technorati Tags: SharePoint 2010,SharePoint Server 2010

Others found also helpful: