02/10/2013

Add Site Column to existing Content Type in code

How to add a new Site Column to an existing Content Type from code


This post describes how to create a new column, add it to an existing Content Type in an existing feature and last run an upgrade in PowerShell of the feature.

Feature.xml file:
<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="
http://schemas.microsoft.com/sharepoint/" Version="2.0.1.0">
  <ElementManifests>
    <ElementManifest Location="NewColName\Elements.xml" />
  </ElementManifests> 
  <UpgradeActions>
    <VersionRange EndVersion="3.0.0.0">
      <ApplyElementManifests>
        <ElementManifest Location="NewColName\Elements.xml" />
      </ApplyElementManifests>
      <CustomUpgradeAction Name="AddFieldToContentType">
        <Parameters>
          <Parameter Name="FieldId">x-x-x-x-x</Parameter>
          <Parameter Name="ContentTypeId">xxxx</Parameter>
          <Parameter Name="PushDown">TRUE</Parameter>
        </Parameters>
      </CustomUpgradeAction>
    </VersionRange>
  </UpgradeActions>
</Feature>


The NewColName\Elements.xml contains the definition of the new column and shall be added in both the ElementManifest section and the UpgradeActions-ApplyElementManifest section. The ElementManifest section is used when deploying the feature the first time, and the UpgradeAction section is used when upgrading the feature. The new version of the feature is 2.0.1.0, and the upgrade action shall be active until version 3.0.0.0 (EndVersion attribute).
The CustomUpgradeAction refers to an upgrade action named "AddFieldToContentType". The action takes three parameters the new field ID the existing Content type ID and a parameter called PushDown, witch configures of the Content Type changes shall be pushed down to child Content Types.

The new column is defined in NewColName\Elements.xml:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="
http://schemas.microsoft.com/sharepoint/"> 
  <Field

    Name="NewColName"
    StaticName="NewColName"
    Type="Text"
    Required="FALSE"
    DisplayName="NewColName"
    Description="The new column"
    Group="NewGroup"
    ID="{x-x-x-x-x}"
    Overwrite="TRUE" OverwriteInChildScopes="FALSE" 
    SourceID="http://schemas.microsoft.com/sharepoint/v3"
    xmlns="http://schemas.microsoft.com/sharepoint/" />
</Elements>


Create an event receiver and override the FeatureUpgrading method, this method is invoked when the feature is upgraded (surprise!) and the upgrade action name is taken as a parameter:
public override void FeatureUpgrading(
  SPFeatureReceiverProperties properties,
  string upgradeActionName,
  System.Collections.Generic.IDictionary<string, string> parameters)
{
  if (properties.Feature.Parent is SPSite)
  {
    SPWeb web = ((SPSite) properties.Feature.Parent).RootWeb;
    switch (upgradeActionName)
    {
      case "AddFieldToContentType":
        string fieldId = parameters["FieldId"];
        string contentTypeId = parameters["ContentTypeId"];
        bool updateChilds = true;
        bool flag;
        updateChilds=bool.TryParse(parameters["PushDown"],out flag);
        AddFieldToContentType(web,contentTypeId,fieldId,updateChilds);
        break;
      default:
        break;
    }
  }
}


AddFieldToContentType method:
private void AddFieldToContentType(SPWeb web, string contentTypeId, string fieldId, bool updateChilds)
{
  SPContentType type = web.ContentTypes[new SPContentTypeId(contentTypeId)];
  type.FieldLinks.Add(new SPFieldLink(web.Fields[new Guid(fieldId)]));
  type.Update(updateChilds, updateChilds);
}


The last thing to do is to override the FeatureActivated method, this method is invoked when the feature is activated and it will make sure that the new Field also is added to the Content Type the first time the feature is activated:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
  if (properties.Feature.Parent is SPSite)
  {
    SPWeb site = ((SPSite) properties.Feature.Parent).RootWeb;
    const string fieldId = "x-x-x-x-x";
    const string contentTypeId = "xxx";
    AddFieldToContentType(site, contentTypeId, fieldId, true);
  }
}


So to upgrade the feature run this script in PowerShell :
$versionFolder = "C:\temp\"
$solutionName = “x.wsp”
$solutionPath = $versionFolder + $solutionName
$siteUrl = "
http://.../"
$featureId = "{x-x-x-x-x}"

Update-SPSolution –Identity $solutionName –LiteralPath $solutionPath –GacDeployment
# wait for it to finish
$site = get-spsite $siteUrl
$feature = $site.Features | where {$_.Definition.Id -eq $featureId}

if($feature)
{
    $ex = $feature.Upgrade($true)
    Write-Host $ex #if anything went wrong this is printed
}

2 comments:

  1. Before running the powershell commands above i should deploy the feature by right clicking on the visual studion project. assuming that if i am doing in development.
    When i deploy it is showing error as Invalid field "new field id". Please assist me.

    ReplyDelete
    Replies
    1. Hi,
      In development you actually do not need to upgrade the feature at all. By default the following is done when you right click on a project and choose deploy in Visual Studio:
      - Builds the project (creates the wsp file)
      - Runs pre deployment commands (if any)
      - IIS reset
      - Retracts the project (deactivates all features, retracts old wsp and deletes old wsp)
      - Adds and deploys the new wsp file
      - Activates all features in the project
      But in a test, QA or production environment it is not an option to deactivate features. So that’s when the provided script will come in handy.

      As for your error:
      I did not describe how to create the new column. Create this by right clicking the project in Visual Studio and choose Add > New Item. Then choose Site column in the Add new Item dialog, give the new column a name and click ok. This will generate an Element.xml file that is similar to my code listing above (NewColName\Elements.xml). Then use the generated ID attribute instead of "new field id" in the Feature.xml file, the event receiver and in the PowerShell script.

      Hope this helps.

      Delete