by Andreas

Sharepoint 2010 – programmatically import and use web parts from site gallery

I recently discovered that when I export webparts with custom settings (like a CQWP with styling, filter values etc) and stored them in the site collection gallery, the configuration values were lost when programmatically importing them in a Visual Studio 2010 solution. The .webpart file created upon export contains all the custom settings, but this was ignored when using the standard add method ( SPLimitedWebPartManager.AddWebPart(…) ). All I got was a standard clean out-of-the box instance of the CQWP, which meant manual configuration was required.

To overcome this limitation there is another available method in the SPLimitedWebPartManager class: SPLimitedWebPartManager.ImportWebPart(…). This method takes an XmlReader parameter, which should get its data feed from the .webpart file stored in the gallery. The trick is to find the path to this file, but when you’ve got that it is simple to import it to your current page and then add it a web part zone. Here’s a snippet to help you get started:

    using (SPSite site = new SPSite(SPContext.Current.Site.Url))
    {
        SPList list = site.RootWeb.Lists["Web Part Gallery"];

        for (int i = 0; i < list.ItemCount; i++)
        {
            if (list.Items[i].Title.Contains("The title of my custom webpart"))
            {

... // you can figure out the rest

The following code is in the context of a newly created sub site, and I access the SPLimitedWebPartManager for a publishing page like this:

using (SPSite site = new SPSite(SPContext.Current.Site.Url))
    {
        // get reference to newly created sub site
        using (SPWeb web = site.AllWebs[_NewSiteGuid])
        {

            PublishingWeb pWeb = PublishingWeb.GetPublishingWeb(web);

            PublishingPage page = pWeb.GetPublishingPage(_NewSiteUri);
            // check out page before making changes
            page.CheckOut();

            // access webparts
            SPLimitedWebPartManager wpMgr = web.GetLimitedWebPartManager(page.Uri.ToString(), PersonalizationScope.Shared);

...

A bit further down the actual import and adding occurs:

...

    string error = ""; // holds the potential error message
    // bit of ugly coding here, Robert C. Martin will shoot you if you use this!
    string exportedWebPartXml = new StringReader(site.RootWeb.GetFileAsString(site.RootWeb.Url + "/_catalogs/wp/SomeCustomFancyComponent.webpart")).ReadToEnd();
    // alter contents
    exportedWebPartXml = exportedWebPartXml.Replace("SomeValueThatShouldBeGeneric", "ThisOtherValueThatIsPageDependant");

    // create a reader
    XmlTextReader reader = new XmlTextReader(new StringReader(exportedWebPartXml));

    // wpMgr is an instance of SPLimitedWebPartManager
    System.Web.UI.WebControls.WebParts.WebPart importedWp = wpMgr.ImportWebPart(reader, out error);

    // make some other alterations if applicable
    importedWp.Title = "New title";

    // add the web part to the publishing page
    wpMgr.AddWebPart(importedWp, "TopBarLeftZone", 0);

...

Note: I have removed validation for increased readability, but before someone gets on my back for possible null pointer exception traps I just wanted to say it was deliberate!

If you expect to get anything out of this post, I assume you already know enough basic coding to see what’s going on. Basically, using the path pointing to the .webpart file the contents are read into the XmlTextReader and passed on to the ImportWebPart() method. The return parameter is an instance of your webpart (if you got the path correct…) which can then be added to a web part zone on your page.

Your custom settings are now preserved, and as you see in this code I also do some runtime alterations of the .webpart file contents which practically removes any boundries to what you can do.

by Andreas

Sharepoint trick – when a webpart causes a Server Error

image

This is a useful trick when one of your custom webparts causes the page to fail and gives you the Yellow Screen of Death. By just adding a query string parameter you’ll get access to the “maintenance page”, where you can delete or reset the faulty webpart.

Add “?contents=1” behind the url (giving you something similar to this: http://server/sites/DemoNet/SitePages/WebParts.aspx?contents=1 )

image

Now go back to Visual Studio, quietly admit to yourself that your error handling sucks and make sure you fix it!

by Andreas

Sharepoint 2010 webparts – access embedded resources

Including resources in your webpart (like images, javascript files, sound clips – you name it) can be a pain in the a** in Sharepoint. Why? Because your relative path doesn’t work the way you are used to in ASP.NET web applications. You can’t just reference your resources by the familiar “~imagesmy_custom_image.png” notation, and the reason for that is not going to be covered since my example shows how to avoid dealing with this (involves the fairly complex sub directory structure of Sharepoint and the way you interact with it). Most likely the reason you stumbled across this post is because you’ve already had looked at that part and thought “there’s gotta be another way”. This is it:

Add a reference to your resouce in AssemblyInfo.cs:

[assembly: System.Web.UI.WebResource("TabHeaderContentQueryWebPart.Gfx.RoundedCornerDarkRedLeft.png", "img/png")]

Take note of the name structure: this is an image called RoundedCornerDarkRedLeft.png, stored under the ~Gfx directory (directly under the root of the webpart) in a project with the namespace TabHeaderContentQueryWebPart.

To access the url path to this resource in your code, use the following:

string path = Page.ClientScript.GetWebResourceUrl(this.GetType(), "TabHeaderContentQueryWebPart.Gfx.RoundedCornerDarkRedRight.png");

In this case, the return value is the runtime URL path pointing to the actual resource which I use in the .ImageUrl property for an image object.

! Important note !
(Edited 11th of February 2011)

I forgot one thing when writing this post: you have to set the Build action for your embedded resources to.. ahm.. Embedded Resource. If not, if won’t be deployed with your webpart.

by Andreas

Log4Net i Sharepoint 2010 web parts

All kode (av noenlunde kvalitet…) som kjører i et produksjonsmiljø logger. Eller burde gjøre det. Om det er til fil, database, eventlogg eller remote over eteren har ikke noe å si – et sted bør det holdes varig track på hva som skjer i en applikasjon. Da jeg hev meg på utvikling i Sharepoint var en av de første “utfordringene” hvordan jeg skulle få skrevet til egne loggfiler i stedet for å bruke de interne Sharepointloggene. Hvorfor? Vel.. det skjer mye Sharepoint. Mye som du sannsynligvis ikke er interessert i, og som ikke nødvendigvis er relevant for den feilen du leter etter grunnen til.

Log4Net er en .NET-portering av Log4J. Den gjør det enkelt for en applikasjon å logge til fil. Det er omtrent det jeg tar med av bakgrunn, siden den som leser dette innlegget sannsynligvis har brukt det før og bare vil se hvordan det brukes i Sharepoint. Jeg tar heller ikke med mulighetene for å bruke Log4Net til å sende mail via SMTP, men den er verdt å få med seg at eksisterer.

Sharepoint er bygd på ASP.NET, og du kan derfor bruke samme fremgangsmåte som du hadde gjort i en hvilken som helst annen ASP.NET-applikasjon. Det som utgjør forskjellen er at en web part ikke nødvendigvis har en web.config-fil. I dette eksemplet bruker jeg den som ligger i roten på selve Sharepoint-instansen (På site collection-nivå). Det er ikke noe i veien for å bruke en mer lokal web.config, men det utgjør ikke noe vesentlig forskjell i demosammenheng.

1. Legg til en referanse til log4net.dll
Denne må enten registreres i GAC manuelt, eller så kan du legge den til Package-elementet i web part-prosjektet. Sjekk dette innlegget for en gjennomgang av det.

2. Legg inn den vanlige ConfigSection og konfigurasjon i web.config
I denne demoen bruker jeg web.config for en hel site collection (ligger gjerne her C:inetpubwwwrootwssVirtualDirectories80web.config).

Øverst i web.config legger jeg log4net-noden som første element under “configSections”:




....

Deretter legger jeg appender-config som et element rett før “system.web”:






















.......

3. Legg til en entry i AssemblyInfo.cs
Her settes ConfiguratorAttribute til å peke på web.config som konfigurasjonsfil:


[assembly: log4net.Config.XmlConfiguratorAttribute(ConfigFile = "web.config", Watch = true)]

4. Legg til en statisk instans av loggeren på klassenivå slik som før (denne henter generisk ut klassens navn i stedet for å selv måtte sette dette for hver klasse):


private static readonly log4net.ILog _log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

5. Logging burde nå (med Guds velvilje og litt triksing med skriverettighetene i målmappen) fungere:


_log.Info("Degree Blogg sparte meg akkurat for masse jobb!");
_log.Warn(" (and I'm a poet and didn't know it) ");