Export Content Types

Posted on Posted in SharePoint 2007, STSADM Commands

I was recently working on a client project where there was an environment already setup with several custom content types and custom site columns that had been created. We wanted to reverse engineer those content types and columns so that we could create a Feature that could be used to deploy them into the various environments. I poked around a bit and couldn’t find anything that would simply give me the CAML that I needed for my Feature. So I decided to create a couple of new commands to help me do this quickly: gl-exportcontenttypes and gl-exportsitecolumns.

I’ll cover gl-exportsitecolumns in a follow up post. Looking at the code you can see that there’s really not much going on. I simply grab the content type(s) to export and get the SchemaXml property. One thing of note is that the SchemaXml property returns back XML that contains the complete field definition – because this won’t work in a Feature I replace the <Field /> elements with a corresponding <FieldRef /> element. If the field definitions are needed then you can simply provide the includefielddefinitions parameter. Note that I’m outputting everything in one file but if you intend to use this in a Feature you’ll want to break this up into multiple files. For the field definitions I also just output the SchemaXml of the SPField objects associated with the content type (note that there will be some attributes that you’ll need to pull when using the resultant output in a Feature). To get the list bindings I loop through all the lists throughout the site collection and add a ContentTypeBindings element for each list that contains a reference to the identified content types.

   1: public class ExportContentTypes : SPOperation
   2: {
   3:     private const string ENCODED_SPACE = "_x0020_";
   4:     /// <summary>
   5:     /// Initializes a new instance of the <see cref="CopyContentTypes"/> class.
   6:     /// </summary>
   7:     public ExportContentTypes()
   8:     {
   9:         SPParamCollection parameters = new SPParamCollection();
  10:         parameters.Add(new SPParam("url", "url", true, null, new SPUrlValidator(), "Please specify the source site collection."));
  11:         parameters.Add(new SPParam("name", "n", false, null, new SPNonEmptyValidator()));
  12:         parameters.Add(new SPParam("group", "g", false, null, new SPNonEmptyValidator()));
  13:         parameters.Add(new SPParam("listname", "list", false, null, new SPNonEmptyValidator()));
  14:         parameters.Add(new SPParam("outputfile", "output", false, null, new SPDirectoryExistsAndValidFileNameValidator()));
  15:         parameters.Add(new SPParam("includelistbindings", "ilb"));
  16:         parameters.Add(new SPParam("includefielddefinitions", "ifd"));
  17:         parameters.Add(new SPParam("excludeparentfields", "epf"));
  18:         parameters.Add(new SPParam("removeencodedspaces", "res"));
  20:         StringBuilder sb = new StringBuilder();
  21:         sb.Append("\r\n\r\nExports Content Types to an XML file.\r\n\r\nParameters:");
  22:         sb.Append("\r\n\t-url <url containing the content types>");
  23:         sb.Append("\r\n\t-outputfile <file to output results to>");
  24:         sb.Append("\r\n\t[-name <name of an individual content type to export>]");
  25:         sb.Append("\r\n\t[-group <content type group name to filter results by>]");
  26:         sb.Append("\r\n\t[-listname <name of a list to export content types from>]");
  27:         sb.Append("\r\n\t[-includelistbindings]");
  28:         sb.Append("\r\n\t[-includefielddefinitions]");
  29:         sb.Append("\r\n\t[-excludeparentfields]");
  30:         sb.Append("\r\n\t[-removeencodedspaces (removes '_x0020_' in field names)]");
  31:         Init(parameters, sb.ToString());
  32:     }
  34:     #region ISPStsadmCommand Members
  36:     /// <summary>
  37:     /// Gets the help message.
  38:     /// </summary>
  39:     /// <param name="command">The command.</param>
  40:     /// <returns></returns>
  41:     public override string GetHelpMessage(string command)
  42:     {
  43:         return HelpMessage;
  44:     }
  46:     /// <summary>
  47:     /// Runs the specified command.
  48:     /// </summary>
  49:     /// <param name="command">The command.</param>
  50:     /// <param name="keyValues">The key values.</param>
  51:     /// <param name="output">The output.</param>
  52:     /// <returns></returns>
  53:     public override int Run(string command, StringDictionary keyValues, out string output)
  54:     {
  55:         output = string.Empty;
  57:         InitParameters(keyValues);
  59:         string url = Params["url"].Value.TrimEnd('/');
  60:         string contentTypeName = null;
  61:         if (Params["name"].UserTypedIn)
  62:             contentTypeName = Params["name"].Value;
  63:         string contentTypeGroup = null;
  64:         if (Params["group"].UserTypedIn)
  65:             contentTypeGroup = Params["group"].Value.ToLowerInvariant();
  66:         if (Params["group"].UserTypedIn && Params["listname"].UserTypedIn)
  67:             throw new SPSyntaxException("The parameters group and listname are incompatible");
  68:         bool excludeParentFields = Params["excludeparentfields"].UserTypedIn;
  69:         bool removeEncodedSpaces = Params["removeencodedspaces"].UserTypedIn;
  71:         StringBuilder sb = new StringBuilder();
  72:         XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
  73:         xmlWriter.Formatting = Formatting.Indented;
  75:         Dictionary<Guid, SPField> ctFields = new Dictionary<Guid, SPField>();
  77:         using (SPSite site = new SPSite(url))
  78:         {
  79:             using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(url)])
  80:             {
  81:                 SPContentTypeCollection availableContentTypes;
  83:                 if (Params["listname"].UserTypedIn)
  84:                 {
  85:                     SPList list = web.Lists[Params["listname"].Value];
  86:                     availableContentTypes = list.ContentTypes;
  87:                 }
  88:                 else
  89:                 {
  90:                     availableContentTypes = web.AvailableContentTypes;
  91:                 }
  92:                 List<SPContentType> contentTypes = new List<SPContentType>();
  93:                 try
  94:                 {
  95:                     xmlWriter.WriteStartElement("Elements");
  96:                     xmlWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/sharepoint/");
  98:                     // Gather up all the content types we want to export out.
  99:                     if (contentTypeName != null)
 100:                     {
 101:                         SPContentType ct = availableContentTypes[contentTypeName];
 102:                         if (ct == null)
 103:                         {
 104:                             output += "The content type specified could not be found.";
 105:                             return 0;
 106:                         }
 107:                         else
 108:                         {
 109:                             contentTypes.Add(ct);
 110:                         }
 111:                     }
 112:                     else
 113:                     {
 114:                         // Loop through all the source content types and create them at the target.
 115:                         foreach (SPContentType ct in availableContentTypes)
 116:                         {
 117:                             if (contentTypeGroup == null || ct.Group.ToLowerInvariant() == contentTypeGroup)
 118:                             {
 119:                                 contentTypes.Add(ct);
 120:                             }
 121:                         }
 122:                     }
 124:                     if (Params["includefielddefinitions"].UserTypedIn)
 125:                     {
 126:                         // If we're including field definitions then we want to show them first as they'll need to appear first when using within a Feature
 127:                         foreach (SPContentType ct in contentTypes)
 128:                         {
 129:                             SPContentType parentCT = ct.Parent;
 130:                             foreach (SPField field in ct.Fields)
 131:                             {
 132:                                 // If the parent content type contains the current field and the user wants to exclude parent fields then continue to the next field.
 133:                                 if (parentCT != null && excludeParentFields && parentCT.Fields.ContainsField(field.InternalName))
 134:                                     continue;
 136:                                 if (!ctFields.ContainsKey(field.Id))
 137:                                     ctFields.Add(field.Id, field);
 138:                             }
 139:                         }
 140:                         xmlWriter.WriteString("\r\n\r\n");
 141:                         foreach (SPField field in ctFields.Values)
 142:                         {
 143:                             string schema = field.SchemaXml;
 144:                             if (field.InternalName.Contains(ENCODED_SPACE) && removeEncodedSpaces)
 145:                             {
 146:                                 schema = schema.Replace(string.Format("Name=\"{0}\"", field.InternalName),
 147:                                                         string.Format("Name=\"{0}\"", field.InternalName.Replace(ENCODED_SPACE, string.Empty)));
 148:                             }
 150:                             xmlWriter.WriteRaw(schema);
 151:                             xmlWriter.WriteString("\r\n");
 152:                         }
 153:                     }
 155:                     xmlWriter.WriteString("\r\n");
 157:                     foreach (SPContentType ct in contentTypes)
 158:                     {
 159:                         WriteContentTypeXml(ct, excludeParentFields, removeEncodedSpaces, xmlWriter);
 160:                     }
 162:                     if (Params["includelistbindings"].UserTypedIn)
 163:                         GetListBindings(web, contentTypes, xmlWriter);
 165:                     xmlWriter.WriteEndElement(); // Elements
 166:                 }
 167:                 finally
 168:                 {
 169:                     xmlWriter.Flush();
 170:                     xmlWriter.Close();
 171:                 }
 172:             }
 173:         }
 175:         File.WriteAllText(Params["outputfile"].Value, sb.ToString());
 176:         return 1;
 177:     }
 180:     #endregion
 183:     /// <summary>
 184:     /// Writes the content type XML.
 185:     /// </summary>
 186:     /// <param name="ct">The ct.</param>
 187:     /// <param name="excludeParentFields">if set to <c>true</c> [exclude parent fields].</param>
 188:     /// <param name="removeEncodedSpaces">if set to <c>true</c> [remove encoded spaces].</param>
 189:     /// <param name="xmlWriter">The XML writer.</param>
 190:     private static void WriteContentTypeXml(SPContentType ct, bool excludeParentFields, bool removeEncodedSpaces, XmlTextWriter xmlWriter)
 191:     {
 192:         SPContentType parentCT = ct.Parent;
 193:         XmlDocument xmlDoc = new XmlDocument();
 194:         xmlDoc.LoadXml(ct.SchemaXml);
 195:         XmlElement fieldRefsElement = xmlDoc.CreateElement("FieldRefs");
 196:         xmlDoc.DocumentElement.AppendChild(fieldRefsElement);
 197:         foreach (XmlElement field in xmlDoc.SelectNodes("//Fields/Field"))
 198:         {
 199:             // If the parent content type contains the current field and the user wants to exclude parent fields then continue to the next field.
 200:             if (parentCT != null && excludeParentFields && parentCT.Fields.ContainsField(field.GetAttribute("Name")))
 201:                 continue;
 203:             XmlElement fieldRefElement = xmlDoc.CreateElement("FieldRef");
 204:             fieldRefElement.SetAttribute("ID", field.GetAttribute("ID"));
 206:             string name = field.GetAttribute("Name");
 207:             if (name.Contains(ENCODED_SPACE) && removeEncodedSpaces)
 208:                 name = name.Replace(ENCODED_SPACE, string.Empty);
 210:             fieldRefElement.SetAttribute("Name", name);
 211:             fieldRefsElement.AppendChild(fieldRefElement);
 212:         }
 213:         xmlDoc.DocumentElement.RemoveChild(xmlDoc.SelectSingleNode("//Fields"));
 215:         xmlWriter.WriteString("\r\n");
 216:         xmlWriter.WriteRaw(xmlDoc.OuterXml);
 217:     }
 219:     /// <summary>
 220:     /// Gets the list bindings.
 221:     /// </summary>
 222:     /// <param name="web">The web.</param>
 223:     /// <param name="contentTypes">The content types.</param>
 224:     /// <param name="xmlWriter">The XML writer.</param>
 225:     private static void GetListBindings(SPWeb web, List<SPContentType> contentTypes, XmlTextWriter xmlWriter)
 226:     {
 227:         foreach (SPList list in web.Lists)
 228:         {
 229:             foreach (SPContentType listCT in list.ContentTypes)
 230:             {
 231:                 foreach (SPContentType ct in contentTypes)
 232:                 {
 233:                     //if ((listCT.Scope != ct.Scope && listCT.Parent.Id == ct.Id) || listCT.Id == ct.Id)
 234:                     if (listCT.Name == ct.Name && listCT.Group == ct.Group)
 235:                     {
 236:                         xmlWriter.WriteStartElement("ContentTypeBinding");
 237:                         xmlWriter.WriteAttributeString("ContentTypeId", ct.Id.ToString());
 238:                         xmlWriter.WriteAttributeString("ListUrl", list.RootFolder.ServerRelativeUrl);
 239:                         xmlWriter.WriteEndElement(); // ContentTypeBinding
 240:                     }
 241:                 }
 242:             }
 243:         }
 245:         foreach (SPWeb subWeb in web.Webs)
 246:         {
 247:             try
 248:             {
 249:                 GetListBindings(subWeb, contentTypes, xmlWriter);
 250:             }
 251:             finally
 252:             {
 253:                 subWeb.Dispose();
 254:             }
 255:         }
 256:     }
 257: }

The syntax of the command can be seen below:

C:\>stsadm -help gl-exportcontenttypes

stsadm -o gl-exportcontenttypes

Exports Content Types to an XML file.

        -url <url containing the content types>
        -outputfile <file to output results to>
        [-name <name of an individual content type to export>]
        [-group <content type group name to filter results by>]
        [-listname <name of a list to export content types from>]
        [-removeencodedspaces (removes '_x0020_' in field names)]

Here’s an example of how to dump the XMl for all the content types in a particular group:

stsadm -o gl-exportcontenttypes -url "http://intranet" -outputfile c:\contentTypes.xml -group "Custom Content Types" -includelistbindings -includefielddefinitions

24 thoughts on “Export Content Types

  1. I don’t currently have any plans to create one but I’ve given it some thought – it wouldn’t take a whole lot of effort. If I do it I’ll want to handle workflows and other items associated with the content type which will add a lot of time to it – I just don’t have a whole lot of time at the moment 🙂

  2. This is really cool, thanks for the hard work. I am somewhat of an SP newbie and I was hoping it wouldn’t take much transformation to make the output from this into the correct input for a feature. A lot of it looks right. What do you think it would take? I might could even create some XSLT that would do the trick.

  3. John – I use the export content types command for Feature development all the time (I will typically create my site columns and content types via the browser and then use this to export – this is the whole reason I created the command). Typically I only ever have to modify the fields and even then only just a little – there’s usually just a couple of attributes that need to be pulled and then if you want to use resource files you have to add all that in.

  4. Gary, thanks for creating this, but I’m having a problem deploying it. When I run stsadm.exe -o addsolution -filename Lapointe.SharePoint.STS.Commands.wsp to install it, I’m getting an ‘Object reference not set to an instance of an object’ error. Any ideas?

  5. I’ve not seen that error when trying to add the solution before. Try pasting the following into a batch file and run that from folder where the WSP is saved:

    SET SOLUTION_NAME=”Lapointe.SharePoint.STSADM.Commands.wsp”
    SET STSADM=”C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\bin\stsadm”
    %STSADM% -o addsolution -filename %SOLUTION_NAME%
    %STSADM% -o deploysolution -local -allowgacdeployment -name %SOLUTION_NAME%

    If you still have issues just change the WSP extension to .CAB and put the dll in the GAC and the XML files in the 12\config folder (that’s all the WSP is doing – but by using a solution to deploy it will deploy to the whole farm rather than just the one server).

  6. Hi Gary

    First, thank you for all

    Please, can you give us some guidelines on how importing a content type.
    For example, is there an equivalent for the “shortcut” SPFiledCollection.AddFieldAsXml() which is used to import site columns ?

    Or must we inevitably parse the exported XML, create an SPContentType Object, and affect their properties one by one ?

  7. There is a method that will do what you want but it’s marked as internal so technically we can’t use it. Your best bet is to put it in a feature and deploy the feature – otherwise you’ll have to programmatically add all the various artifacts (field links, event receivers, etc.).

  8. I’ve installed and deployed your solution (which looks great, by the way). But when I try to export a list, I am getting the message for each View page that “A web part or Web form control type could not be found or is not registered as safe. The web part will still be exported”. I am logged on as the site admin account.

    But when I try to import it to another web application, I get teh message “Warning: Could not find web part -Unknown: #####”.

    This is just the basic Announcements list that I used to test. Do you know what I am doing wrong?

    Thanks, I really need these commands.

  9. Hi,

    Thanx for the great commands.

    I’ve got a problem with the expot of content types, it sets to many values in the XML.

    When i install the exported xml i’ve got errors for the following properties:

    Maybe you can fix this, in the meanwhile i will have to replace them all by hand.

  10. Hello again Gary,

    Can you give me a guideline to make a feature to install the content types in the XML file?, i dont know what is the correct way…


  11. Hi Gary,
    We have to move custom content types from one box to another while preserving their GUIDs as there are lot of applications referring to these content types via their GUID.
    Does your exportcontenttype preserves the GUID? iF yes, then have u created import content type :))…and if no, then can you suggest me a way to achieve this

  12. Right now I don’t have an import but if you’re within the same farm then you can use the copy command – otherwise your best bet would be to use features.

  13. I’m in the same situation. We’re developing a tool to help us manage our sharepoint environnements and the “import content type” is like the holy grail to us. But we have no clue how to Import them from the Xml exported.

    Do you have any hint on how?

  14. I’m missing many fields from my content types, have you ever experienced that? They appeared in the list of field definitions but not as ref in content types. I did it by hand but I wondered if it happened to someone else.
    The tool still saved me a lot of time! Thanks a lot for that!

  15. First let me thank your effort and publishing the code. You are a man !!! . I was wandering quite a long time to get into exporting Content types between the sites, it down size my time.

  16. Gary

    I like the way you coded very nicely. I tested it seems to be working fine.

    We are in process of migration from MOSS to SP2010. We have created our own classification plan in word document. Now we want to update all the content type from multiple site collection with our new classification plan.

    I have couple of questions
    1. How can I generate XML of all the contentypes from multiple site collections from MOSS?
    2. How do we map our classifications and update it to SP2010 content?

    Please advise

Comments are closed.