Posts Tagged ‘C#’

SPHttpUtility vs HTTPUtility

Comparison between SPHttpUtility and HTTPUtility – encoding and decoding

There are different ways of encoding and decoding querystring parameters, for example by using SPHttpUtility or HttpUtility.
While programming SharePoint I always tend to use the SPHttpUtility class, but I didn’t know exactly why. Until I accidentally used the HttpUtility and SPHttpUtility at the same time: I noticed some differences.  

Encode

SharePoint has a Utilities namespace, Microsoft.SharePoint.Utilities, which provides the SPHttpUtility class. One of the methods in this class is UrlKeyValueEncode with several overloads.
The description of the method 

UrlKeyValueEncode(string keyOrValueToEncode)

is ‘Encodes the specified URL query string key or value’. 

The HttpUtility class in the namespace System.Web contains a method UrlEnode, also with overloads. The description of

UrlEncode(string str)

is ‘Encodes a URL string’.
The HttpUtility class doesn’t have a method to encode a query string key or value as UrlKeyValueEncode, but this is the best match.  

Let’s encode a space:
The result while encoding with HttpUtility is ‘+’, encoding with SPHttpUtility results in a ‘%20′.

One of the major differences is the casing of the encoded characters. SPHttpUtility encodes the characters uppercase, HttpUtility lowercase.
Another difference can be seen in the picture below: HttpUtility doesn’t encode all characters, SPHttpUtility does:  

  

Decode

To decode characters the SPHttpUtility class provides the method

UrlKeyValueDecode(string keyOrValueToDecode)

The couterpart of the UrlEncode method in the HttpUtility class is

UrlDecode(string str)

While encoding results in different outcome, decoding doesn’t.
As can be seen in the picture of the encoded characters above, HttpUtility doesn’t encode e.g. ‘(‘, ‘!’, ‘*’. When decoding these (unencoded) characters with SPHttpUtility the results stay ‘(‘, ‘!’, ‘*’. Check the pictures below.
Encoded with HttpUtility, decoded these characters:  

SPHttpUtility encodes characters ‘(‘, ‘!’ and ‘*’ as ‘%28′, ‘%21′ and ‘%2A’. When decoding these characters with SPHttpUtility, but also with HttpUtility, the results are the same:  

  

Summary

Since decoding gives the same results with SPHttpUtility and HttpUtility, why bother the encoding method?  

Well, something inside me tells me SharePoint is using it somewhere internally. I didn’t figure out where, but why is the SPHttpUtility implemented if SharePoint easily could use HttpUtility?
Besides some feelings, the SPHttpUtility doesn’t use HttpUtility internally. When checking out the SPHttpUtility.UrlKeyValueEncode with ILSpy, the encoded values, uppercase(!), are listed is the readonly string array s_crgstrUrlHexValue.

  

Wikipedia has a definition of percent encoding (http://en.wikipedia.org/wiki/Percent-encoding) :
‘Percent-encoding, also known as URL encoding, is a mechanism for encoding information in a Uniform Resource Identifier (URI) under certain circumstances’  

According to this article reserved characters (Reserved characters are those characters that sometimes have special meaning) must be encoded…
According to RFC 3986 reserved characters are:

And encoded:
  

So HttpUtility doesn’t encode the first five characters, SPHttpUtility encodes all reserved characters.  

To be compatible with 3986 standard it seems SPHttpUtility is the best option to use.

Bind (SP)Gridview field to generic list of strings

Probably this is my lack of knowledge in the (sp)gridview area, but I want to share it anyway.

I realized I always bound gridviews to custom objects only. Recently I wanted to bound a generic list of strings (List<string>) to a field in a gridview and didn’t know how to accomplish this. After trying a few options and some searching I found the answer:
An exclamation mark: !

Example:

BoundField field = new BoundField();
field.DataField = "!";

Never thought of this one myself.

Simple Provider Consumer Visual Webparts

Because of a question on http://sharepoint.stackexchange.com/questions/14325/created-connectable-webparts-but-the-connections-menu-item-is-not-showing I decided to create a provider consumer webpart in his most simple form.   

Create an empty SharePoint project in Visual studio.
Add two Visual WebParts:   

  1. ProviderVisualWebpart
  2. ConsumerVisualWebpart

   

In this most simple form of a provider consumer webpart only one string is passed from the provider to the consumer and it displayed in a Label control on the consumer webpart.   

Code in ProviderVisualWebpart.cs:   


public class ProviderVisualWebPart : System.Web.UI.WebControls.WebParts.WebPart, ITransformableFilterValues
{
    private const string _ascxPath = @"~/_CONTROLTEMPLATES/ConsumerProviderVWP/ProviderVisualWebPart/ProviderVisualWebPartUserControl.ascx";
 

    protected override void CreateChildControls()
    {
        Control control = Page.LoadControl(_ascxPath);
        Controls.Add(control);
    }

    public string ParameterName
    {
        get
        {
            return "Letter";
        }
    }

    public ReadOnlyCollection<string> ParameterValues
    {
        get
        {
            List<string> values = this.GetValues();
            return new ReadOnlyCollection<string>(values);
        }
    }

    private List<string> GetValues()
    {
        List<string> valueList = new List<string>();
        valueList.Add("A");
        return valueList;
    }

    [ConnectionProvider("Letter Filter", "ITransformableFilterValues")]
    public ITransformableFilterValues SetConnectionInterface()
    {
        return this;
    }

    public bool AllowEmptyValue
    {
        get { return true; }
    }

    public bool AllowMultipleValues
    {
        get { return false; }
    }

    public bool AllowAllValue
    {
        get { return true; }
    }
}

    

Code in ConsumerVisualWebpart.cs:   


public class ConsumerVisualWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
    private const string _ascxPath = @"~/_CONTROLTEMPLATES/ConsumerProviderVWP/ConsumerVisualWebPart/ConsumerVisualWebPartUserControl.ascx";

    public ConsumerVisualWebPart()
    {
        _filterProviders = new List<IFilterValues>();
    }

    protected override void CreateChildControls()
    {
        ConsumerVisualWebPartUserControl control = (ConsumerVisualWebPartUserControl)Page.LoadControl(_ascxPath);
 

        control.ValueSendByProviderProperty = "From provider: ";
        foreach (IFilterValues filter in FilterProviders)
        {
            if (filter.ParameterValues != null)
            {
                foreach (string item in filter.ParameterValues)
                {
                    control.ValueSendByProviderProperty += item;
                }
            }
        }
 

        Controls.Add(control);
    }

    protected override void Render(HtmlTextWriter writer)
    {
        base.Render(writer);
        foreach (IFilterValues filter in FilterProviders)
        {
            writer.WriteLine(string.Format("Parameter: {0} <br>", filter.ParameterName));
        }
    }

    /// <summary>
    /// Hold incoming filtervalues
    /// </summary>
    private List<IFilterValues> _filterProviders;
    private List<IFilterValues> FilterProviders
    {
        get { return _filterProviders; }
    }

    [ConnectionConsumer("filter", "UniqueIDForConsumer", AllowsMultipleConnections = true)]
    public void SetFilter(IFilterValues filterValues)
    {
        if (filterValues != null)
        {
            List<ConsumerParameter> parameters = new List<ConsumerParameter>();
            parameters.Add(new ConsumerParameter("Letter", ConsumerParameterCapabilities.SupportsSingleValue | ConsumerParameterCapabilities.SupportsAllValue | ConsumerParameterCapabilities.SupportsEmptyValue));
            filterValues.SetConsumerParameters(new System.Collections.ObjectModel.ReadOnlyCollection<ConsumerParameter>(parameters));
            this.FilterProviders.Add(filterValues);
        }
    }
}

Code in ConsumerVisualWebpartUserControl.ascx.cs:   


public partial class ConsumerVisualWebPartUserControl : UserControl
{
    public string ValueSendByProviderProperty { get; set; }
 

    protected void Page_Load(object sender, EventArgs e)
    {
        ValueSendByProvider.Text = ValueSendByProviderProperty;
    }
}

Control added to VisualWebpart.ascx:   


<asp:Label ID="ValueSendByProvider" runat="server" Text="Label"></asp:Label>

That’s it!   

Put both controls on a page and connect them to each other.   

No connection set:   

 Connect:   

Configure:   

Connection established:   

Cinto

 

Branche: ICT & Internet dienstverlening 

Februari 2011 – april 2011

SharePoint developer

 

Voor Cinto heeft Anita advies uitgebracht welke webparts interessant kunnen zijn voor de een beschikbaar te stellen SharePoint 2010 omgeving aan klanten. Naar aanleiding van dit advies is er samen met Cinto een besluit genomen welke webparts aansluiten bij de doelgroep. Deze webparts heeft Anita ontwikkeld.

De gewenste vormgeving wordt toegepast middels het plaatsen en activeren van een ontwikkeld theme. Buiten het theme wordt een aanvullend css bestand toegepast.

Deze functionaliteit is samengebracht in een SharePoint 2010 webtemplate. Op deze manier zijn sites op een eenvoudige en eenduidige manier aan te maken.

SharePoint Foundation 2010, Visual Studio 2010

Imtech

Branche: ICT & Internet dienstverlening 

Februari 2011 – mei 2011

Lead developer SharePoint

  

Oasen maakt drinkwater voor 750.000 mensen en 7.200 bedrijven in het oosten van Zuid-Holland. Het voorzieningsgebied bestaat uit 32 gemeenten.

Op dit moment is de publieke website van Oasen gebaseerd op MOSS2007. Door een nieuwe visie op de vindbaarheid van content, indeling, interactie met de klant en dynamiek is er een nieuwe website ontwikkeld op basis van SharePoint 2010. Een derde partij heeft de ontwikkeling van het design op zich genomen. Het team van Imtech aangevuld met Anita mag de website ontwikkelen op basis van het User Experience document wat het design laat zien.

Basis voor de technische oplossing voor de nieuwe website zijn het User Experience document en de website van Oasen. De technische oplossing en implementatie van de nieuwe website zijn bepaald door het projectteam. Anita is verantwoordelijk voor deze technische keuzes en oplossingen.

Het projectteam bestaat uit een projectleider, consultant, lead engineer, twee engineers en drie designers. Anita vervult de rol van lead engineer. Scrum wordt toegepast met sprints van 2 a 3 weken. De totale doorlooptijd van het project is 9 weken.

Er wordt waar mogelijk gebruik gemaakt van standaard SharePoint componenten, welke aangepast en uitgebreid worden om de exact gewenste functionaliteit te verkrijgen.

De nieuwe website van Oasen maakt zeer intensief gebruik van de SharePoint Search functionaliteit. Dit is de technische basis en komt in vele onderdelen van de website terug. Content in de website wordt voorzien van metadata (onder andere tags) om de vindbaarheid te verbeteren en om de content te kunnen verfijnen op basis van deze tags. De content is onderverdeeld in verschillende types als nieuws, artikelen, applicaties, veelgestelde vragen, foto’s, video’s en storingen.

De traditionele navigatie binnen een website door middel van een menustructuur is grotendeels vervangen: er wordt gebruik gemaakt van de mogelijkheid om content te verfijnen op basis van tags. Om dit te realiseren wordt er gebruik gemaakt van het standaard RefinementPanel met een aangepaste filterdefinitie (inclusief bijbehorende managed/crawled properties) en xslt. Buiten het standaard RefinementPanel is er een custom refiner ontwikkeld welke content binnen een bepaalde tijdsperiode filtert.

Om de content te tonen is het Core Results webpart diverse keren geimplementeerd in een zeer aangepaste vorm. Content wordt dynamisch toegevoegd aan de zoekresultaten op de verschillende pagina’s middels intensief gebruik van http handlers, jQuery en JSON. Vanuit de zoekresultaten kan er genavigeerd worden naar het weergegeven type content. Foto’s en video’s worden getoond in een lightbox met extra informatie over het getoonde item.

Op de website wordt intensief gebruik gemaakt van het Content Query webpart. De nieuwe ‘slots’ functionaliteit wordt toegepast en er zijn nieuwe xslt item styles ontwikkeld.

Uiteraard is er een masterpage ontwikkeld en diverse page layouts om de website structuur te geven en van een consistente look & feel te voorzien in de stijl welke bij Oasen past. Het design van de website is zeer vernieuwend en dynamisch en brengt de nodige uitdagingen met zich mee in combinatie met SharePoint. Er is geen onderdeel in de website wat overeenkomt met standaard SharePoint design.

Buiten nieuw ontwikkelde functionaliteit zijn er diverse bestaande modules gemigreerd naar de nieuwe site. Als klant van Oasen kan er gebruik worden gemaakt van modules als het doorgeven van meterstanden of een verhuizing, het rekeningnummer wijzigen en het voorschot aanpassen.

Verder worden er grafieken over het waterverbruik getoond. Hier kan bijvoorbeeld mee worden bepaald wanneer de rust in een belangrijke voetbalwedstrijd is.

Diverse watermeters worden getoond om de hoeveelheid water aan te geven welke Oasen levert in diverse regio’s. Deze meters worden grafisch weergegeven door middel van flash technologie. Diverse modules hebben een BizTalk koppeling met achterliggende systemen.

Naast de beschreven (basis) functionaliteit zijn er vele onderdelen ontwikkeld, welke binnenkort op de website van Oasen zullen worden gepubliceerd op http://www.oasen.nl

Diverse onderdelen van SharePoint zijn gebruikt om de functionaliteit te kunnen realiseren, zoals sitecolumns, contenttypes, listinstances, jQuery, HTTP Handlers, application pages, css, xslt, page layouts, masterpage, custom actions, eventreceivers, workflows, user controls, webcontrols, webparts, delegates, timerjobs, Excel services.

De website van Oasen is een publieke website, waarbij performance en optimalisatie uitermate belangrijk zijn.

Voor de registratie van de te realiseren onderdelen is Team Foundation Server 2010 gebruikt. Anita is verantwoordelijk voor het bepalen van te realiseren onderdelen in elke sprint en het creeren van taken gerelateerd aan deze sprint backlog items. Daar Anita verantwoordelijk is voor de technische keuzes in het project is het van belang dat zij ook de inhoud van de sprints bepaalt, zodat er snel een basis voor de website staat. Mede omdat de doorlooptijd van het project maar 9 weken is. Voordat een sprint start wordt in een sessie met de engineers en designer de sprint backlog items verdeeld met in achtneming van de beschikbare uren van de projectleden en de te verdelen items. Tijdens het project is Anita verantwoordelijk voor de planning, voortgang en kwaliteit van het opgeleverde. Anita straalt rust uit over het team en staat ten alle tijde open voor vragen van elk lid van het projectteam.

SharePoint Server 2010, Visual Studio 2010, jQuery, JSON, xslt, Excel services, Team Foundation Server 2010, TFS Sidekicks, Imtech OCD, Scrum.

Search Refiners (part 4) – User selection based

In this series:  

  1. Search Refiners part 1 – Expanding the OOTB search Refinement Panel
  2. Search Refiners part 2 – Use of CustomFilters
  3. Search Refiners part 3 – Chart based
  4. Search Refiners part 4 – User selection based (this post)

After building a chart based search refiner, now it’s time to get some interaction in the selection of search terms. In this post I will show you how to use jQuery in combination with the RefinementManager.
With interaction I mean the user can drag a search term to a place on the screen to refine the results. Unfortunately the OOTB Refinement Panel and the results webpart ‘talk’ to each other by the parameters in the url. Therefor a postback must occur to actually refine the results with the selected term. But a little animation just looks great. 

For the drag and drop functionality I used the standard jQuery library and a custom jQuery UI selection. With this functionality in hand I created my own drag and drop implementation for this specific solution. 

Script part

Because I already have a little experience with programming against the RefinementManager class in the chart based example I first focused on the drag drop functionality.
Two divs are placed on the screen:
1. to list all the terms the user can choose from to select
2. the already selected terms by the user 

The items in both divs has to have the ability to drag items from and to drop items to. The user has to have the possibility to drag items from the ‘terms to select’ part to the ’selected terms’ part and vice versa.
Both the divs contain an unordered list with list items.
So a single div with items in it looks in plain HTML like:

<div>
<ul>
<li>item01</li>
<li>item02</li>
</ul>
</div>

A single item has to be draggable to move it from the ‘terms to select’ to the ’selected terms’ part(and vice versa). So the li element has to be draggable.
On the other hand when a li element a dragged, the list to which the single item has to be added has to be droppable, the ul element. 

To accomplish this just a few lines of jQuery are neccesary, the rest is alredy build in the jQuery libraries.

    var $currentSelectedItems = $("#selectedTerms"),
 $baseItemList = $("#termsToSelect");</pre>
 

    //make the li's in the selectedTerms div draggable
    $("li", $currentSelectedItems).draggable({
        appendTo: $baseItemList,
        helper: "clone"
    });
 

    //make the ul in the selectedTerms div droppable
    $("ul", $currentSelectedItems).droppable({
        accept: "#termsToSelect li",
        hoverClass: "ui-state-hover",
        drop: function (event, ui) {
            moveTerm(ui.draggable, $currentSelectedItems);
        }
    });

The draggable functionality is nothing special. The element passed to appendTo is the container during dragging. Second the clone helper is used here, which means a clone of the actual item is dragged around. Another option here is original, which drags the original item around. 

The droppable functionality uses different options. First I’m telling the element what items to accept: an li element within ‘termsToSelect’. Second a nice hover class and finally a function is specified what to do when the actual drop occurs: move the term to from ‘terms to select’ to ’selected terms’ and navigate to the url which the item received from the RefinementPanel (getting there soon). 

    //move term
    function moveTerm($item, $listToAdjust) {
        $item.fadeOut(function () {
            var $list = $("ul", $listToAdjust);
            $item.appendTo($list).fadeIn();
            window.location.href = $item.find('a').attr("href");
        });
    }

  

The script above is the script which act on the ’selected terms’ part ($currentSelectedItems or $(“#selectedTerms”)). The same functionality have to be added to the ‘terms to select’ part. Because it’s almost the same this code it’s left out here for readability. 

That’s is with all the script, let’s move the code behind all this. 

The webpart

The code behind this (just a regular webpart) works with the RefinmentManager class of the page as in the chart based example, the previous post in this series. The extra functionality here is that the selected term is needed from the RefinmentManager’s filter. If a term is selected or not is just another node in the refinement xml, just as Value and Url as shown in the following code listing:
 

string filterValue = filter.SelectSingleNode("Value").InnerText;
string filterUrl = filter.SelectSingleNode("Url").InnerText;
 

if (filter.SelectSingleNode("Selection").InnerText.Equals("Selected"))
{
    dateSelectedList.Add(new ListItem(filterValue, filterUrl));
}
else
{
    dateToSelectList.Add(new ListItem(filterValue, filterUrl));
}

dateSelectedList and dateToSelectList are both generic lists of ListItems and in the RenderContents method the items in the lists are added to the appropriate containers. 

After putting this all together it looks like the folowing image when no selection are made by the user:

When dragging starts from ‘terms to select’ the location where to drag to is highlighted:
 

Because ‘helper’ was set to ‘clone’ earlier the item that’s dragged, ‘Past Week’, is still in the ‘terms to select’ container and also moving around the screen. 

When dropping the item for a moment the screen looks like (because of the ‘appendto’ option):
  

And then a postback occurs and it looks like:
    

And the terms displayed behave just like the OOTB refiners.    

Summary

The OOTB refinement functionality is great, but it can be made much more attractive with just a little script as shown in this post. What I’ve shown you here is just a little example with one of the possibilities to make it more attractive. There are a lot of jQuery libraries with excellent functionality which you can use to do all kinds of stuff and don’t forget the design. This example looks nothing like a nice production ready refiner, but go wild and make it look awesome.

Search Refiners (part 3) – Chart based

In this series:  

  1. Search Refiners part 1 – Expanding the OOTB search Refinement Panel
  2. Search Refiners part 2 – Use of CustomFilters
  3. Search Refiners part 3 – Chart based (this post)
  4. Search Refiners part 4 – User selection based

 

In the previous two posts about search refiners no code was written to adjust the OOTB Search Refinement Panel, just some XML modifications.
In this post a chart based search refiner will be built and some code is needed to accomplish this. The refinement will be built on the Modified Date.  

Create the refiner

To get the refinement categories and their values, the RefinementManager class will be used.
The RefinementManager will get an instance of the refinement manager on the current page. This means the OOTB Search Refinement Panel has to be present on the current page. Not only it has to be present, it also has to have the filtercategory on which the refinement is going to perform on, in this case the Modified Date. 

Get the instance of the refinement manager:

RefinementManager refinementManager = RefinementManager.GetInstance(this.Page);

To get the refinement values:

XmlDocument refinementXmlDoc = refinementManager.GetRefinementXml();

The GetRefinementXML method gets the filtered xml document of the refinement manager.
For the example is this post only the Modified Date is of interest, so an XPath expression is used to get to the right filter category and values:

XmlNodeList filters = refXmlDocument.DocumentElement.SelectNodes("/FilterPanel/FilterCategory[@ManagedProperty='" + refiner + "']/Filters/Filter[Count>0]");

The refiner variable is declared as:

private const string refiner = "Write";

By looping through the XMLNodes in the XMLNodeList the neccesary values are available, just as in the XML which is rendered by the OOTB Search Refinement Panel. The elements of the childnodes (filter categories) are shown:  

Actually, this was the most interesting part. What’s left is displaying the values to the user, in this case: putting it in a chart; and binding it all together.  

Putting it all together

In CreateChildControls the chart is setup:

private Microsoft.Office.Server.WebControls.Chart chart;
protected override void CreateChildControls()
{
    ChartArea area = new ChartArea("Pie Chart");

    chart = new Microsoft.Office.Server.WebControls.Chart();
    chart.Width = 160;
    chart.Height = 160;
    chart.ChartAreas.Add(area);

    Controls.Add(chart);
}

And in the OnPreRender the refiment manager, the values of the filter category Modified Date and the chart are put together.

protected override void OnPreRender(EventArgs e)
{
    RefinementManager refManager = RefinementManager.GetInstance(this.Page);

    XmlDocument refXmlDocument = refManager.GetRefinementXml();

    if (refXmlDocument != null)
    {
        Series chartSeries = new Series();
        chartSeries.ChartType = SeriesChartType.Pie;

        XmlNodeList filters = refXmlDocument.DocumentElement.SelectNodes("/FilterPanel/FilterCategory[@ManagedProperty='" + refiner + "']/Filters/Filter[Count>0]");

        if (filters.Count != 0)
        {
            foreach (XmlNode filter in filters)
            {
                string xValue = filter.SelectSingleNode("Value").InnerText;
                string yValue = filter.SelectSingleNode("Count").InnerText;

                int i = chartSeries.Points.AddXY(xValue, yValue);

                // display text in chart
                chartSeries.Points[i].Label = xValue;

                //use the url to make the pie parts clickable
                chartSeries.Points[i].Url = filter.SelectSingleNode("Url").InnerText;
            }

            //add 'Any Modified Date' option
            XmlNodeList xmlFilterNodesAllItems = refXmlDocument.DocumentElement.SelectNodes("/FilterPanel/FilterCategory[@ManagedProperty='" + refiner + "']/Filters/Filter[Count='']");
            chart.Titles.Clear();
            System.Web.UI.DataVisualization.Charting.Title allItemsTitle = new System.Web.UI.DataVisualization.Charting.Title();
            allItemsTitle.Text = xmlFilterNodesAllItems[0].SelectSingleNode("Value").InnerText;
            allItemsTitle.Url = xmlFilterNodesAllItems[0].SelectSingleNode("Url").InnerText;
            allItemsTitle.ToolTip = "Refine by: " + allItemsTitle.Text;
            allItemsTitle.ForeColor = Color.FromArgb(0, 114, 188);
            chart.Titles.Add(allItemsTitle);
        }

        if (chartSeries.Points.Count > 0)
            chart.Series.Add(chartSeries);
    }
}

In the XPath expressions used to get the filter values for Modified Date and ‘Any Modified Date’ are slightly different. Look at the Count differences.
By removing the Count in the first XPath expression the chart will display ‘Any Modified Date’ and the chart kind of gets messed up:

and ‘Any Modified Date’ is not clickable, because it get’s no colored part in the pie chart. The reason for this is that the Count value for Any Modified Date is empty.
This is the reason to use Count>0 for the values displayed in the pie chart and Count=” to get Any Modified Date.
The pie chart now looks like this:
  

And after selecting ‘Past Month’:  


  

Summary

To transform a textual search refiner to a chart based search refiner is shown in this post.
As long as the OOTB Search Refinement Panel is present on the page and the filter category used by the code is present in the Filter Category Definition (property of Search Refinement Panel web part), the code in this post can be used to visualize a search refiner.
If it’s not present, you can extend the Refinement Panel webpart. This is not covered in this post, but maybe I’ll show you this in a future post.  

   

Portal for Business

  

Branche: Informatie technologie

Januari 2011

SharePoint consultant/developer

 

Voor diverse klanten van Portal for Business heeft Anita de huisstijl toegepast op intranet sites door middel van het ontwikkelen van custom masterpages en style sheets.
Een intranet bestaat bij de klanten uit meerdere web applicaties. Om globale navigatie consistent te houden op de diverse web applicaties is er een custom site map provider ontwikkeld om dit te realiseren. 

Hiernaast zijn er custom ribbon buttons ontwikkeld en een news aggregator webpart. Dit webpart verzamelt nieuws items van een bepaald niveau binnen een web applicatie tot alle onderliggende subsites en toont deze items. 

De genoemde functionaliteiten zijn zowel voor SharePoint Server 2010 als voor SharePoint Foundation 2010 ontwikkeld. De solutions zijn zoveel mogelijk sandboxed solutions, tenzij de gewenste functionaliteit en daarbij behorende oplossing dit niet toelaat, zoals de custom site map provider. 

SharePoint Server 2010, SharePoint Foundation 2010, Visual Studio 2010, jQuery

Upgrading a feature property

In SharePoint 2010 features can be upgraded, awesome!
I was just testing this when I discovered something curious when updating a property.

Suppose the following feature definition file:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Version="1.0.0.0">
  <Properties>
    <Property Key="MyProp" Value="initialValue" />
  </Properties>
</Feature>

The property value of the MyProp key  is “initialValue”.

After adjusting the feature definition file to perform an upgrade the file looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<Feature xmlns="http://schemas.microsoft.com/sharepoint/" Version="1.1.0.0">
  <UpgradeActions>
    <VersionRange BeginVersion="1.0.0.0" EndVersion="2.0.0.0">
      <CustomUpgradeAction Name="UpgradeToOtherVersion"/>
    </VersionRange>
  </UpgradeActions>
  <Properties>
    <Property Key="MyProp" Value="newValue" />
  </Properties>
</Feature>

And I changed the property value of the MyProp key to “newValue”.
In the FeatureUpgrading event receiver I was checking the property value with the following statement:

SPFeatureProperty property1 = properties.Feature.Properties["MyProp"];
string initalValue = property1.Value;

and the result of property1.Value: “initialValue” ! I was suprised because I expected “newValue”.

After checking some other values and reading documentation on MSDN I came up with the following code to get the new property value:

SPFeatureProperty property2 = properties.Definition.Properties["MyProp"];
string updatedValue = property2.Value;

And the result:

The difference according to the documentation on MSDN is:
properties.Feature.Properties["MyProp"] – Gets the collection of properties for the Feature Link to MSDN
properties.Definition.Properties["MyProp"] – Gets a standardized property bag object that contains per-Feature settings that are specified in the Feature definition Link to MSDN

So I guess my conclusion is:
the property bag for the feature is updated to the new value after upgrading the feature and the property of the feature isn’t.

This sounds weird to me and I’m not sure I understand. Has anyone suggestions or experience with it?

[UPDATE]
While tweeting about this issue Chris O’Brien helped me out on this:
A feature has 1 definition and n instances. The code in FeatureUpgrading is used to upgrade the instances.
The property in the example updates the feature definition and not the running instances.
So properties.Feature.Properties["MyProp"] gets the property value of the running instance and properties.Definition.Properties["MyProp"] get the value of the property in the feature definition.

That’s it and not so weird after all! Thanks Chris!

SPDisposeCheck How to suppress a false positive

Recently I asked a question on Twitter on how to suppress a false positive with the SPDispose check tool and Wictor Wilén pointed me to the right direction.
Several times I’m asked about the solution so I’m sharing it with you now. 

First of all you have to download the SPDisposeCheck tool and the SPDisposeCheckRules for Visual Studio 2010.
SPDisposeCheck tool: http://download.microsoft.com/download/B/4/D/B4D279A0-E159-40BF-A5E8-F49ABDBE95C7/SPDisposeCheck.msi
SPDisposeCheckRules for Visual Studio 2010: http://spdisposecheckstatic.codeplex.com/releases 

After downloading install the SPDisposeCheck tool on your machine and extract the zip file for the rules. The zip file contains a readme-vs2010.txt with instructions. Follow these instructions. 

The SPDisposeCheck tool is installed on the following location:
C:\Program Files (x86)\Microsoft\SharePoint Dispose Check 

At this location a zip file called SPDisposeExamplesSource.zip can be found. In this zip file several classes are stored, one of them is the class SPDisposeCheckIgnoreAttribute.cs.

To suppress a false positive you have to include this class in your project.

After including the attribute to suppress a false positive can be used:

[SPDisposeCheckIgnoreAttribute(SPDisposeCheckID.SPDisposeCheckID_110, "Don't want to do it")]
public void CreatingSPSiteLeak()
{
    SPSite siteCollection = new SPSite("http://moss");
    // siteCollection leaked
}

The above example is an example from another class in the zip file: SPSiteLeak.cs