Martin's profileMartin Hatch {Tales from...PhotosBlogListsMore Tools Help

Blog


    13 October

    [Code Update] SDK and Microsoft Press Both Wrong?? Custom Fields in the schema.xml

    UPDATE
    For those who missed my original article (SDK and Microsoft Press Both Wrong?? Custom Fields in the schema.xml) , you can find it here!
     
    I finally got round to posting the code that goes with this article ..
     
    Please find below the details on code for an SPFeatureReceiver class, that will automatically push down changes from your Content Type Features
    (you will need to attach the code to your feature using the ReceiverAssembly and ReceiverClass attributes in your feature.
     
    The code needs to be created as a Class in a Class Library, with Microsoft.SharePoint.dll added as a reference, and then added as a Using statement in the class itself.
     
    Code Sample
     
    EDIT - Thanks to a quick spot from Nick's SharePoint blog, I have updated the code to match! Thanks Nick!
     
    class
    ContentTypeInstaller : SPFeatureReceiver
    {
    // CODE DESCRIPTION
    // ----------------
    /*
    * This is a feature handler which should be paired
    * with a content type feature.
    *
    * We have identified a problem with Content Types,
    * where the content type site columns are not pushed
    * down to custom list definitions, until they are
    * "modified" first.
    *
    * This feature will interrogate all of the associated
    * xml files that are attached to the feature.
    * Once found, it will "modify" each of the custom fields
    * that are referenced.
    *
    * This should allow list definitions to use the content
    * type columns, without declaring them in the schema.xml
    */
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
    using(SPSite site = (SPSite)properties.Feature.Parent)
    {
    SPWeb web = site.OpenWeb(
    "");
    // loop through each of the elements in the feature
    foreach (SPElementDefinition element in properties.Definition.GetElementDefinitions(CultureInfo.CurrentCulture))
    {
    #region
    Loop through feature elements
     
    // retrieve the xml content for the feature element
    XmlNode content = element.XmlDefinition;
    // only continue if the element is a content type definition
    if (content.Name == "ContentType")
    {
    // grab a new Content Type object
    string strCTypeID = content.Attributes["ID"].Value;
    SPContentType cType = web.ContentTypes[
    new SPContentTypeId(strCTypeID)];
    #region
    Get FieldRef Order from Content type
    // grab the original order, we will need this later
    string[] fieldOrder = new string[cType.FieldLinks.Count];
    int x = 0;
    foreach (SPFieldLink fieldlink in cType.FieldLinks)
    {
    fieldOrder[x] = fieldlink.Name;
    x++;
    }
    #endregion
    #region
    Add new columns to the content type
    // loop through each sub-node in the Content Type file
    foreach (XmlNode node in content.ChildNodes)
    {
    #region
    loop through sub nodes
    // only continue for
    // the FieldRefs collection
    if (node.Name == "FieldRefs")
    {
    foreach (XmlNode fieldRef in node.ChildNodes)
    {
    #region
    Loop through FieldRefs
    // only apply for FieldRef tags
    if (fieldRef.Name == "FieldRef")
    {
    // get the FieldID and use it to
    // retrieve the SPField object
    string fieldID = fieldRef.Attributes["ID"].Value;
    //SPFieldLink fieldLink = cType.FieldLinks[new Guid(fieldID)];
    SPField field = cType.Fields[
    new Guid(fieldID)];
    // first we need to remove the fieldref
    cType.FieldLinks.Delete(
    new Guid(fieldID));
    // and save, pushing this change down
    cType.Update(
    true);
    // NOTE - this will NOT delete any content
    // in existing lists!
    // now add the field back in again
    cType.FieldLinks.Add(
    new SPFieldLink(field));
    // and call an update, pushing down all changes
    cType.Update(
    true);
    // NOTE - this is what adds the column to those
    // lists who don't already have it.
    }
    #endregion
    }
     
    }
    #endregion
    }
    #endregion
    #region
    Apply changes
    // reset the field order
    // it is possible that adding and
    // removing fields would have
    // affected this
    cType.FieldLinks.Reorder(fieldOrder);
    // force update of the content type,
    // pushing down to children
    cType.Update(
    true);
    #endregion
    }
    #endregion
    }
    }
    }
    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
    }
    public override void FeatureInstalled(SPFeatureReceiverProperties properties)
    {
    }
    public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
    {
    }
    }
    Enjoy, and happy coding!

    Comments (9)

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.
    Martin Hatch has turned off comments on this page.
    Martin Hatchwrote:
    Paul, you could write a Timer job to reactivate the feature?
    Otherwise you can use PowerShell or scripts to call the STSADM commands to activate the features?
    Finally, you could do this with an Object Model command line app?
    22 Oct.
    Paulwrote:
    Will this code activate with a solution upgrade, or are you required to deactivate and reactivate the feature for every site collection? I'd love any ideas around automating this at the web app or farm level.
    29 Sept.
    Saiwrote:
    Hi martin
    I have one question. In my sharepoint site already one content type is there. That content type consists one column(dropdown). My requiremnt is, i have to fill that column with items which will come from external data base(e.g. Oracle). So how can i achive this. Please help. Thanks in advance.
    13 Aug.
    Martin Hatchwrote:
    No, the read-only attribute applies to the user interface. Code pretty much ignores it.
    28 July
    Paulwrote:
    Hi Martin, do I need to do anything special if my CTs have been set to read-only via the UI?
    21 July
    T8tube.comwrote:
    thanks
    18 Apr.
    Picture of Anonymous
    Peter wrote:
    Cool feature. But what would be even more cool, is a receiver that would push down all changes as well.

    Let's say you have a content type that is activated on a list and all is fine. Then some time later you decide to change that content type feature xml, say add a few more columns, rename a few, delete one - that sort of changes. As things are OOB with MOSS/WSS those changes are not pushed through on lists the the content type already is attached. This is "by design":
    http://msdn.microsoft.com/en-us/library/aa543504.aspx (Look in the Important-section)

    The issue is known in the dev community:
    http://social.msdn.microsoft.com/Forums/en-US/sharepointecm/thread/75548e96-6bae-4c32-bf68-2965570a3579/

    But so far I haven't seen an open source feature that would fix this little thing once and for all. It would be a really neat feature to have.

    Cheers!


    29 Oct.
    Martin Hatchwrote:
    Thanks. I added a link to the original post to the article.
    13 Oct.
    Great post Martin. You should include the content from the MSDN forum post as well though!
    13 Oct.