As part of the upgrade I needed to be able to fix some web parts that did not migrate correctly (either during the upgrade itself or as a result of moving a web). Before I started messing around with the web parts though I wanted to be able to see what I was dealing with. So I decided to create this simple command called gl-enumpagewebparts
that would enable me to list out in XML all the web parts that are on a given page (open or closed).
One thing that I found that was very interesting was that the web part manager export method treats V2 and V3 web parts very differently. But perhaps the biggest annoyance I found was that I couldn’t get the web part zone from the web part instance itself – I had to use the web part manager (SPLimitedWebPartManager
) to get it (took me longer than I’d like to admit to figure that out). This command is really quite simple – it takes in an url to a web part page, loads up an SPLimitedWebPartManager
(for both the shared and personal views) and then loops through the WebParts
collection outputting the results as XML.
I created three separate methods to get the XML details – one is verbose and essentially just uses the built in Export() method to get the XML (you can get these results via the -verbose
parameter), another is a bit simpler and is constructed by hand (this is the default) and a third is actually for use by another command that I created which I’ll be documenting soon. The core code is shown below:
1/// <summary>
2/// Runs the specified command.
3/// </summary>
4/// <param name="command">The command.</param>
5/// <param name="keyValues">The key values.</param>
6/// <param name="output">The output.</param>
7/// <returns></returns>
8public override int Run(string command, StringDictionary keyValues, out string output)
9{
10 output = string.Empty;
11
12 InitParameters(keyValues);
13
14 string url = Params["url"].Value;
15
16 XmlDocument xmlDoc = new XmlDocument();
17 xmlDoc.AppendChild(xmlDoc.CreateElement("WebParts"));
18 xmlDoc.DocumentElement.SetAttribute("page", url);
19
20 using (SPSite site = new SPSite(url))
21 using (SPWeb web = site.OpenWeb()) // The url contains a filename so AllWebs[] will not work unless we want to try and parse which we don't
22 {
23 XmlElement shared = xmlDoc.CreateElement("Shared");
24 xmlDoc.DocumentElement.AppendChild(shared);
25
26 SPLimitedWebPartManager webPartMngr = web.GetLimitedWebPartManager(url, PersonalizationScope.Shared);
27
28 string tempXml = string.Empty;
29 foreach (WebPart wp in webPartMngr.WebParts)
30 {
31 if (Params["verbose"].UserTypedIn)
32 tempXml += GetWebPartDetails(wp, webPartMngr);
33 else
34 tempXml += GetWebPartDetailsSimple(wp, webPartMngr);
35 }
36 shared.InnerXml = tempXml;
37
38 XmlElement user = xmlDoc.CreateElement("User");
39 xmlDoc.DocumentElement.AppendChild(user);
40
41 webPartMngr = web.GetLimitedWebPartManager(url, PersonalizationScope.User);
42 tempXml = string.Empty;
43 foreach (WebPart wp in webPartMngr.WebParts)
44 {
45 if (Params["verbose"].UserTypedIn)
46 tempXml += GetWebPartDetails(wp, webPartMngr);
47 else
48 tempXml += GetWebPartDetailsSimple(wp, webPartMngr);
49 }
50 user.InnerXml = tempXml;
51
52 }
53
54 output += Utilities.GetFormattedXml(xmlDoc);
55
56 return 1;
57}
58
59#endregion
60
61/// <summary>
62/// Gets the web part details.
63/// </summary>
64/// <param name="wp">The web part.</param>
65/// <param name="manager">The web part manager.</param>
66/// <returns></returns>
67internal static string GetWebPartDetails(WebPart wp, SPLimitedWebPartManager manager)
68{
69 StringBuilder sb = new StringBuilder();
70
71 XmlTextWriter xmlWriter = new XmlTextWriter(new StringWriter(sb));
72 xmlWriter.Formatting = Formatting.Indented;
73 manager.ExportWebPart(wp, xmlWriter);
74 xmlWriter.Flush();
75
76 XmlDocument xmlDoc = new XmlDocument();
77 xmlDoc.LoadXml(sb.ToString());
78
79 XmlElement elem = xmlDoc.DocumentElement;
80 if (xmlDoc.DocumentElement.Name == "webParts")
81 {
82 elem = (XmlElement)xmlDoc.DocumentElement.ChildNodes[0];
83
84 // We've found a v3 web part but the export method does not export what the zone ID is so we
85 // have to manually add that in. Unfortunately the Zone property is always null because we are
86 // using a SPLimitedWebPartManager so we have to use the helper method GetZoneID to set the zone ID.
87 XmlElement property = xmlDoc.CreateElement("property");
88 property.SetAttribute("name", "ZoneID");
89 property.SetAttribute("type", "string");
90 property.InnerText = manager.GetZoneID(wp);
91 elem.ChildNodes[1].ChildNodes[0].AppendChild(property);
92 }
93
94 return elem.OuterXml.Replace(" xmlns=\"\"", ""); // Just some minor cleanup to deal with erroneous namespace tags added due to the zoneID being added manually.
95}
The syntax of the command I created can be seen below.
C:\>stsadm -help gl-enumpagewebparts
stsadm -o gl-enumpagewebparts
Lists all the web parts that have been added to the specified page.
Parameters:
-url <web part page URL>
[-verbose]
Here’s an example of how to list all the web parts on a given page and dump to a text file:
stsadm -o gl-enumpagewebparts -url "http://intranet/hr/pages/default.aspx" -verbose > webparts.xml