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"));
19:
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: }
33:
34: #region ISPStsadmCommand Members
35:
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: }
45:
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;
56:
57: InitParameters(keyValues);
58:
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;
70:
71: StringBuilder sb = new StringBuilder();
72: XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
73: xmlWriter.Formatting = Formatting.Indented;
74:
75: Dictionary<Guid, SPField> ctFields = new Dictionary<Guid, SPField>();
76:
77: using (SPSite site = new SPSite(url))
78: {
79: using (SPWeb web = site.AllWebs[Utilities.GetServerRelUrlFromFullUrl(url)])
80: {
81: SPContentTypeCollection availableContentTypes;
82:
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/");
97:
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: }
123:
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;
135:
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: }
149:
150: xmlWriter.WriteRaw(schema);
151: xmlWriter.WriteString("\r\n");
152: }
153: }
154:
155: xmlWriter.WriteString("\r\n");
156:
157: foreach (SPContentType ct in contentTypes)
158: {
159: WriteContentTypeXml(ct, excludeParentFields, removeEncodedSpaces, xmlWriter);
160: }
161:
162: if (Params["includelistbindings"].UserTypedIn)
163: GetListBindings(web, contentTypes, xmlWriter);
164:
165: xmlWriter.WriteEndElement(); // Elements
166: }
167: finally
168: {
169: xmlWriter.Flush();
170: xmlWriter.Close();
171: }
172: }
173: }
174:
175: File.WriteAllText(Params["outputfile"].Value, sb.ToString());
176: return 1;
177: }
178:
179:
180: #endregion
181:
182:
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;
202:
203: XmlElement fieldRefElement = xmlDoc.CreateElement("FieldRef");
204: fieldRefElement.SetAttribute("ID", field.GetAttribute("ID"));
205:
206: string name = field.GetAttribute("Name");
207: if (name.Contains(ENCODED_SPACE) && removeEncodedSpaces)
208: name = name.Replace(ENCODED_SPACE, string.Empty);
209:
210: fieldRefElement.SetAttribute("Name", name);
211: fieldRefsElement.AppendChild(fieldRefElement);
212: }
213: xmlDoc.DocumentElement.RemoveChild(xmlDoc.SelectSingleNode("//Fields"));
214:
215: xmlWriter.WriteString("\r\n");
216: xmlWriter.WriteRaw(xmlDoc.OuterXml);
217: }
218:
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: }
244:
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: }
258: