SharePoint Blogs / SharePoint University
SharePoint Blogs and SharePoint University - all in one place!
Need SharePoint Training? Attend a SharePoint Bootcamp!

Please delete cookies related to sharepointblogs.com and sharepointu.com to resolve login issues!

Synchronize Content Types and Site Columns between teamsites

This morning I had a conversation with a colleague about Schemalogic. This tool enables you to manage your content types in a central location for your whole farm or even multiple farms.

Based on this concept I thought it might be cool to make a 'master' sitecollection with all site-columns and content-types and reflect those to all other site collections. Don't ask me why I don't use features or other concepts, we're currently evaluating the different possibilities..

This is my quick-and-dirty script to sync the Site Columns and Content Types between sites. It does te following:

1. Create Site Columns if they don't exist
2. Update Site Columns if the are different
3. Create Content Types if they don't exist
4. Add/Remove Site Columns to existing Content Types
5. Remove Site Columns if they are removed from the master site

A number of questions arise when building the script:

- How to handle lookup fields, since they link to a local list. We could ignore these fields or assume the list exists..
- It should remove Content Types if they are removed from the master site (not very complex, just forgot it in this version)
- It should iterate through all site collections instead of just one

Well, I'll continue evaluating the other possibilities, but we'll might go for this method. In meanwhile you are free to use the (beta)code I've written for this concept. Please feel free to comment your findings and suggestions!

static void Main(string[] args)
{
    SPSite sourceRoot = new SPSite("http://wss3dev/sites/TEST2");
    SPWeb sourceWeb = sourceRoot.OpenWeb();

    SPSite targetRoot = new SPSite("http://wss3dev/sites/TEST3");
    SPWeb targetWeb = targetRoot.OpenWeb();

    SyncSiteColumns(sourceWeb, targetWeb);
    SyncContentTypes(sourceWeb, targetWeb);
    DeleteSiteColumns(sourceWeb, targetWeb);
}

/// <summary>
/// Syncs content types based on a sourceweb and a targetweb.
/// Add's Content Types if they don't exist.
/// Add's/Removes Fields in existing Content Types
/// </summary>
/// <param name="sourceWeb"></param>
/// <param name="targetWeb"></param>
private static void SyncContentTypes(SPWeb sourceWeb, SPWeb targetWeb)
{
    foreach (SPContentType sourceType in sourceWeb.ContentTypes)
    {
        if (!sourceType.ReadOnly && !sourceType.Sealed && !sourceType.Hidden) // Exclude non-editable types
        {
            bool update = false;

            if (targetWeb.ContentTypes[sourceType.Name] == null)
            {
                SPContentType parentContentType = targetWeb.ContentTypes[sourceType.Parent.Name];
                SPContentTypeCollection collection = targetWeb.ContentTypes;

                SPContentType newContentType = new SPContentType(parentContentType, collection, sourceType.Name);
                targetWeb.ContentTypes.Add(newContentType);

                newContentType = targetWeb.ContentTypes[sourceType.Name];

                foreach (SPField field in sourceType.Fields)
                {
                    if (newContentType.FieldLinks[field.InternalName] == null)
                    {
                        newContentType.FieldLinks.Add(new SPFieldLink(targetWeb.Fields[field.Title]));
                    }
                }

                newContentType.Update(false); // Do not update children, there are no children yet
            }
            else
            {
                SPContentType targetType = targetWeb.ContentTypes[sourceType.Name];

                foreach (SPField sourceField in sourceType.Fields)
                {
                    if (targetType.FieldLinks[targetWeb.Fields.GetFieldByInternalName(sourceField.InternalName).Id] == null)
                    {
                        targetType.FieldLinks.Add(new SPFieldLink(targetWeb.Fields.GetFieldByInternalName(sourceField.InternalName)));
                        update = true;
                    }
                }

                for (int i = (targetType.FieldLinks.Count - 1); i >= 0; i--)
                {
                    if (sourceType.FieldLinks[targetType.FieldLinks[i ].Name] == null)
                    {
                        targetType.FieldLinks.Delete(targetType.FieldLinks[i ].Name);
                        update = true;
                    }
                }

                if (update)
                    targetType.Update(false);
            }
        }
    }
}

/// <summary>
/// Syncs Sitecolumns based on a sourceweb and targetweb
/// Updates a sitecolumn if it is different.
/// Add's a sitecolumn if does not exist.
/// </summary>
/// <param name="sourceWeb"></param>
/// <param name="targetWeb"></param>
private static void SyncSiteColumns(SPWeb sourceWeb, SPWeb targetWeb)
{
    foreach (SPField field in sourceWeb.Fields)
    {
        if (!targetWeb.Fields.ContainsField(field.Title))
        {
            targetWeb.Fields.AddFieldAsXml(field.SchemaXml);
        }
        else
        {
            SPField targetField = targetWeb.Fields.GetFieldByInternalName(field.InternalName);

            if (targetField.SchemaXml != field.SchemaXml)
            {
                targetField.SchemaXml = field.SchemaXml;
                targetField.Update(true);
            }
        }
    }

    targetWeb.Update();
}

/// <summary>
/// Removes sitecolumns from the target-site which do not exist in the source-site
/// </summary>
/// <param name="sourceWeb"></param>
/// <param name="targetWeb"></param>
private static void DeleteSiteColumns(SPWeb sourceWeb, SPWeb targetWeb)
{
    for (int i = (targetWeb.Fields.Count - 1); i >= 0; i--)
    {
        if (!sourceWeb.Fields.ContainsField(targetWeb.Fields[ i].Title))
        {
            targetWeb.Fields.Delete(targetWeb.Fields[i ].Title);
        }
    }
}

Posted 06-11-2008 6:45 AM by Sander de Koning

Comments

2 Static » Blog Archive » Synchronize Content Types and Site Columns between teamsites wrote 2 Static &raquo; Blog Archive &raquo; Synchronize Content Types and Site Columns between teamsites
on 06-11-2008 4:05 PM

Pingback from  2 Static  &raquo; Blog Archive   &raquo; Synchronize Content Types and Site Columns between teamsites

Links (6/12/2008) « Steve Pietrek - Everything SharePoint wrote Links (6/12/2008) &laquo; Steve Pietrek - Everything SharePoint
on 06-12-2008 9:11 PM

Pingback from  Links (6/12/2008) &laquo; Steve Pietrek - Everything SharePoint

Anil Maskara wrote re: Synchronize Content Types and Site Columns between teamsites
on 06-25-2008 7:09 AM

In my case i would like to syncronise the content type as a new content type is added or updated.

Is it possible??

Sander de Koning wrote re: Synchronize Content Types and Site Columns between teamsites
on 06-25-2008 8:02 AM

Hi Anil,

I can imagine why you would want this. The SPContentType object itself has no events attached, so you should monitor for changes yourself.

It might be possible to monitor with a scheduled task and check the Version property. Although I can not guarantee the version is really updated by SharePoint. The SDK calls the property: "Reserved for future use"

this should be possible;

Anil maskara wrote re: Synchronize Content Types and Site Columns between teamsites
on 07-17-2008 12:34 PM

Thanks

Robin wrote re: Synchronize Content Types and Site Columns between teamsites
on 10-15-2008 1:03 PM

Hoi Sander,

bedankt voor je post! Bevestigt hetgeen waar ik ook al mee bezig ben geweest (kwam alleen te laat achter je post, anders had het mij een hoop werk bespaard ;)..

Perry wrote re: Synchronize Content Types and Site Columns between teamsites
on 06-19-2009 3:30 PM

This looks problematic, because you are copying the SourceId, which should be pointing to the local list, and should not be copied from source to target:

targetField.SchemaXml = field.SchemaXml;

Perry wrote re: Synchronize Content Types and Site Columns between teamsites
on 06-19-2009 3:30 PM

This looks problematic, because you are copying the SourceId, which should be pointing to the local list, and should not be copied from source to target:

targetField.SchemaXml = field.SchemaXml;

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Need SharePoint Training? Attend a SharePoint Bootcamp!
Posts (c) their respective authors. Everything else (c) 2009 SharePoint Experts, Inc.