Adding Web Parts to the Web Part Gallery Using the SharePoint Object Model

Since my first post was based on removing web parts from the web part gallery of a 'My Site' there have been questions about how to add the web parts back should circumstances require it.  This is pretty straight-forward, but can get more interesting depending on how robust you want to make the end solution.  The Web Part Gallery is a document library that holds a reference to a web part file, either a .dwp or .webpart file.  This file is simply a metadata definition of the web part.  To add a web part back in the gallery we can leverage the code shown below.  This assumes that the web part assembly is installed and the safe control entries are made in the web.config file.

 public static void IterateWebPartGallery(string siteCollectionUrl) {
    string filePath = @"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\PortalLayouts\DWP\owa.dwp";

    using ( SPSite site = new SPSite( siteCollectionUrl ) ) {
        using ( SPWeb web = site.OpenWeb() ) {

            SPList webPartGallery = web.GetCatalog( SPListTemplateType.WebPartCatalog );

            SPFolder folder = webPartGallery.RootFolder;
            if( folder != null ) {
                byte [] webPartFile = File.ReadAllBytes(filePath);
                FileInfo webPartFileInfo = new FileInfo(filePath);

                folder.Files.Add(webPartFileInfo.Name, webPartFile);
                folder.Update();
            }
        }
    }
}

The code gets a reference to the web part gallery in the target site collection by calling the SPWeb.GetCatalog() method.  Once we have that reference we get a reference to the root folder of the gallery and call the folder.Files.Add() method to add the web part file.  Calling folder.Update() persists this information to the database.

While this will simply add the file by parsing its contents it won't give us the fine grained control we may want for adding our web part to the gallery.  If the file uses resources to define the title and description properties then those get loaded into the gallery "as is" using the method above.  This makes our title $Resources:spscore,PeopleWebParts_OWA_Title_Text; and the description $Resources:spscore,PeopleWebParts_OWA_Desc_Text;.

This can be made a bit more robust by figuring out the title and description from the resource file and setting them in the gallery when the web part is loaded.  The SPFolder.Files.Add() method has an overload that accepts a Hashtable reference where we can set additional properties for the web part.  The resource values above indicate the resource file is the spscore resource file, spscore.en-US.resx for the en-US culture.  Resource files in SharePoint are stored in the \12\Resources directory.  I tried to load this file directly using the ResXResourceReader, but received an exception about a missing 'resheader' value in the file.  Since .resx files are just Xml I decided to use an XPathNavigator and parse the values out that way.  You could also dynamically parse the .webpart or .dwp file to figure out what the associated resx file the web part is referencing, but that is beyond the scope of this post.  The final code snippet is shown below.

 string filePath = @"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\PortalLayouts\DWP\owa.dwp";
string resourceDirectory = @"C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\Resources";

using ( SPSite site = new SPSite( siteCollectionUrl ) ) {
    using ( SPWeb web = site.OpenWeb() ) {

        SPList webPartGallery = web.GetCatalog( SPListTemplateType.WebPartCatalog );

        string title = "";
        string description = "";

        XPathDocument xml = new XPathDocument( File.Open( Path.Combine( resourceDirectory, "spscore.en-US.resx" ), FileMode.Open, FileAccess.Read ) );
        XPathNavigator navigator = xml.CreateNavigator();
        string xpathExpression = "/root/Data[@Name='{0}']/Value";
        XPathNavigator node = navigator.SelectSingleNode( string.Format( xpathExpression, "PeopleWebParts_OWA_Title_Text" ) );
        if ( node != null ) {
            title = node.Value;
        }
        node = navigator.SelectSingleNode( string.Format( xpathExpression, "PeopleWebParts_OWA_Desc_Text" ) );
        if ( node != null ) {
            description = node.Value;
        }


        SPFolder folder = webPartGallery.RootFolder;
        if ( folder != null ) {
            byte[] webPartFile = File.ReadAllBytes( filePath );
            FileInfo webPartFileInfo = new FileInfo( filePath );

            Hashtable properties = new Hashtable();
            properties.Add( "Title", title );
            properties.Add( "WebPartDescription", description );
            properties.Add( "Group", "Outlook Web Access" );


            folder.Files.Add( webPartFileInfo.Name, webPartFile, properties );
            folder.Update();
        }
    }
}