Update Web Part definitions in the Web Part Gallery

Update Web Part definitions in the Web Part Gallery

SharePoint offers some great web parts out of the box. And most of these web parts are configured with a set of default values allowing end users to easily add and configure these web parts on their pages. In a recent scenario however I found myself required to change some of these properties. As some web parts have some complex logic we decided that a few of the out of the box web parts should have the help URL filled with a reference to a wiki page that contained extra information on the use of the web part.

In order to update an web part you will first have to retrieve it. As it is nothing more then an .xml file containing the web part definition the easiest way is to retrieve it as an XmlDocument.  This can be done if you have the SPListItem as it contains an SPFile that can be opened.

string sQuery = string.Format(@"<Where><Eq><FieldRef Name=""LinkWebPart""></FieldRef><Value Type=""Text"">somewebpartfile.webpart</Value></Eq></Where>", wp);

var oQuery = new SPQuery {
    Query = sQuery,
    RowLimit = 1
};

SPListItemCollection wpItems = webpartGallery.GetItems(oQuery);

if (wpItems.Count != 0) {
     SPListItem wpItem = wpItems[0];

    // set propperties correctly by reading XML 
    XmlDocument wpXml = new XmlDocument();
    wpXml.Load(wpItem.File.OpenBinaryStream());
}

Once you have an XmlDocument reference you can update the file with the properties you require. However there is a slight difference between dwp files and webpart files. The layout of both files differs, as a dwp file will use a different properties then a webpart definition. In order to update a dwp files there is nothing else to do then to create a new node and populate it with the value you require.

var selectedNode = wpXml.DocumentElement;
var helpPageUrl = "http://www.mavention.nl"

if (selectedNode.GetElementsByTagName("HelpLink").Count != 0) {
    selectedNode.GetElementsByTagName("HelpLink")[0].InnerText = helpPageUrl;
}
else {
    var helpUrlProperty = wpXml.CreateElement("HelpLink");
    helpUrlProperty.InnerText = helpPageUrl;
    selectedNode.AppendChild(helpUrlProperty);
}

However to update a .webpart file you are required to setup a namespace both for checking if the node is already present as well as for creating a new node. Besides that all settings are saved within a <property> tag rather then a named tag as it is the case in a .dwp file. Once you have setup the correct namespace you can use it to select the <properties> element that contains all configuration properties and then use it to to select the HelpUrl property (or use it to create a new one). The thing is that if you create a new property you will have to pass the namespace URL rather then the namespace object. 

XmlNamespaceManager ns = new XmlNamespaceManager(wpXml.NameTable);
ns.AddNamespace("wp", "http://schemas.microsoft.com/WebPart/v3");
var selectedNode = wpXml.SelectSingleNode("webParts/wp:webPart/wp:data/wp:properties", ns);
var helpPageUrl = "http://www.mavention.nl"

// Set the HelpLinkUrl
if (selectedNode.SelectNodes("//property[@name='HelpUrl']", ns).Count != 0) {
    selectedNode.SelectNodes("//property[@name='HelpUrl']", ns)[0].InnerText = helpPageUrl;
}
else {
    var helpUrlProperty = wpXml.CreateElement("property", "http://schemas.microsoft.com/WebPart/v3");
    helpUrlProperty.InnerText = helpPageUrl;

    var name = wpXml.CreateAttribute("name");
    name.Value = "HelpUrl";
    var type = wpXml.CreateAttribute("type");
    type.Value = "string";

    helpUrlProperty.Attributes.Append(name);
    helpUrlProperty.Attributes.Append(type);
    selectedNode.AppendChild(helpUrlProperty);
}

Once you have modified the XmlDocument you can save and commit the changes to the web part gallery.

MemoryStream stream = new MemoryStream();
wpXml.Save(stream);
webpartGallery.RootFolder.Files.Add(wpItem.File.Name, stream, true);

Once saved you have an updated web part definition. Of course you can use the logic to update other properties instead of the HelpUrl as long as you use the namespace logic to update the webpart files.

There are 2 comments for this article
  1. Olena Caire at 21:43

    Thank you for the code sample. I found it valuable when you deploy list instance and list view web part in the same VS solution. After the list is created (FeatureActivated method) I get it's ListID and update the web part definition in web part gallery, specifically ListID property.
    A small correction for this code line
    "selectedNode.SelectNodes("//property[@name='HelpUrl']", ns)[0].InnerText" is missing the namespace 'wp' —> //wp:property[@name='HelpUrl']

    Another way to get the web part from the gallery:

    SPList gallist = web.Lists["Web Part Gallery"];

    for (int i = gallist.ItemCount – 1; i >= 0; i–)
    {
    string webpartName = gallist.Items[i].Name;
    webpartName = webpartName.Substring(0, webpartName.LastIndexOf("."));

    if (webpartName == "Home.Page.Accordion")
    {

    // set propperties correctly by reading XML
    XmlDocument wpXml = new XmlDocument();
    wpXml.Load(gallist.Items[i].File.OpenBinaryStream());
    ……………………………..
    ………………………
    }
    }

    • Albert-Jan Schot at 07:25

      Thanks for the update! The reason I use a query rather than looping through all items is speed. In large WebParts galleries it will be quicker to execute a query rather than looping through all WP's.

Leave a Reply