Posts Tagged ‘Visual Studio 2010’
Ever tried to update a user subtype from code?
User subtypes are one of the many new and nice things in SharePoint 2010.
The other day I was editing user profiles and changed the subtype for this profile in code. After changing the user subtype Commit() was called and I thought I was done.
But then it just started, because the user profile wasn’t updated at all. No exception, no message at all, just the old user subtype.
The used code:
ProfileSubtypeManager psubm = ProfileSubtypeManager.Get(context); ProfileSubtype newSubtype = psubm.GetProfileSubtype(newSubtypeName); currentUserProfile.ProfileSubtype = newSubtype; currentUserProfile.Commit();
Changing the user subtype in the UI really updated the setting, so it was time to debug the SharePoint UserProfiles assembly.
The short version of what the debugging session(s) told me:
A UserProfileUpdateWrapper was created with the following xml
<?xml version="1.0" encoding="utf-16"?> <MSPROFILE> <PROFILE ProfileName="UserProfile"> <USER NewUser="0" NTAccount="account" UserID="3db01e67-abb0-4946-8fa8-85943768cb79">
The next step is iterating through the user profile fields to check if the value ‘IsDirty’ aka the value has been changed. This is the only trigger to actually update a user profile.
When adjusting the user subtype from the UI a few properties are updated, even when they’re not changed. The final XML looks like the following:
<?xml version="1.0" encoding="utf-16"?> <MSPROFILE> <PROFILE ProfileName="UserProfile"> <USER NewUser="0" NTAccount="account" UserID="3db01e67-abb0-4946-8fa8-85943768cb79"> <PROPERTY PropertyName="Assistant" Privacy="1" PropertyValue="" /> <PROPERTY PropertyName="PictureURL" Privacy="1" PropertyValue="" /> <PROPERTY PropertyName="SPS-TimeZone" Privacy="1" PropertyValue="" /> </USER> </PROFILE> </MSPROFILE>
and a real update of the user profile occurred.
To make the update work from code appearently another property has to be updated together with the change of subtype. After a test with an update of a random property, the user subtype was updated too.
Summary
Changing only the user subtype from code, does trigger the Commit() method, but doesn’t call the update profile, because no user profile property was changed. Use a dummy property to update every time the user subtype has changed.
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:
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.
To ‘this’ or not to ‘this’ with the Client Object model
Recently I developed a CustomAction on a list with some client script in the CommandAction. Nothing fancy or new.
I put a .js file in the _layouts folder and referenced the file by a CustomAction with Location ‘ScriptLink’ and ScriptSrc. Put some code in the file to get some data I needed and called executeQueryAsync with two delegates like this:
clientContext.executeQueryAsync(Function.createDelegate(this, this.OnSucceeded), Function.createDelegate(this, this.OnFailed));
Deployed as a Farm solution on the environment and everything went well so far.
After this, minds changed and it had to become a sandboxed solution. Ok, let’s do it.
1. Set the Sandboxed Solution property to True on the project
2. Removed the CustomAction with Location=’ScriptLink’
3. Put the code from the js file from the _layouts folder in the CommandUIHandler’s attribute CommandAction
4. Deployed the sandboxed solution
I always use FireFox when programming and all of a sudden I received the following error:
b is undefined
![]()
and things stopped working. While in the Farm solution the CustomAction worked as expected and the OnSucceeded and OnFailed were executed.
I put in some alert statements to see where the code broke and it seemed the OnSucceeded and the OnFailed methods weren’t executed at all.
By removing the ‘this’ keyword in the createDelegate everything went well again:
clientContext.executeQueryAsync(Function.createDelegate(this, OnSucceeded), Function.createDelegate(this, OnFailed));
This behaviour seems a bit strange to me. When putting the code in a separate js file and deploying it to the _layouts folder the ‘this’ keyword can be used, but putting the exact same code in the CommandAction of CommandUIHandler the OnSucceeded and OnFailed methods can’t be found anymore using the ‘this’ keyword.
By the way, in IE you will receive the following error:
‘b’ is null or not an object

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:
- ProviderVisualWebpart
- 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:
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 3) – Chart based
In this series:
- Search Refiners part 1 – Expanding the OOTB search Refinement Panel
- Search Refiners part 2 – Use of CustomFilters
- Search Refiners part 3 – Chart based (this post)
- 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
Using async call to enable a custom ribbon button
It is not always appropriate to enable custom buttons on the ribbon all the time. Sometimes one or multiple conditions have to be met to enable the button. As showed in one of my previous posts ‘A better user experience with the dialog framework and notifications‘ the attribute EnabledScript of the CommandUIHandler can be used to test when to enable or disable the button. The check in the example used in the previous post is quite simple and checks if one or more items are selected in the list.
To perform other, less simplistic, checks, it is likely you have to use asynchronous calls when working with ECMA script. Working with async calls in the ribbon is a bit different than working with synchronous calls and this is what this post is about.
Functionality
Suppose you want to display a button on the ribbon on a default Tasks list. The Tasks list contains a column named Status. The button will only be enabled when exactly one item is selected in the list and when this selected item has a Status, a column/field value, not equal to Completed.
The example will focus on the asynchronous part of the solution, not on creating a button on the ribbon.
Solution
First of all a function is called from the EnabledScript attribute of CommandUIHandler.
<CommandUIHandler Command="TaskCompletedCommand"
CommandAction="javascript:alert('Not implemented.');"
EnabledScript="javascript:enableTaskNotCompletedButton();">
</CommandUIHandler>
In this function a check is performed to make sure exactly one item is selected in the list.
After this check we have to get the Status field value of the selected item. Based on this value we enable or disable the button on the ribbon. When the Status field value is equal to Completed the button has to be disabled otherwise enabled. The return value of the function has to be false to disable a button, true to enable.
The enableTaskNotCompletedButton function looks like this at the moment(will be adjusted during the post!):
function enableTaskNotCompletedButton() {
var result = false;//default return value of the function which disables the button
var selectedItems = SP.ListOperation.Selection.getSelectedItems();
//check if exactly one item is selected
if (CountDictionary(selectedItems) == 1) {
if (this.itemId != selectedItems[0]['id']) {
this.itemId = selectedItems[0]['id'];
GetTaskStatus(this.itemId);
}
else if (this.StatusValue == "Completed") {
result = false; //can be omitted, just for readibility
}
}
return result;
}
To get the value of the Status field of the selected item an asynchronous call has to be made by the Client Object model. This call loads the value of the Status field in a variable so a check on this value can be performed.
Since this is an async call the ribbon doesn’t know when this function completes and is not going to wait for it to complete. Therefor you have to tell the ribbon explicitly that the async function is completed and that is has to come back to see if the button has to be enabled or disabled.
Here the RefreshCommandUI method comes in which can be found in the Core.js. This function causes the Ribbon to refresh and EnableScript functions get called on the ribbon buttons.
By calling this method the function in the EnabledScript attribute is called again.
The following code is used for this example.
function GetTaskStatus(itemId) {
var clientContext = SP.ClientContext.get_current();
var currentList = clientContext.get_web().get_lists().getById(SP.ListOperation.Selection.getSelectedList());
this.singleItem = currentList.getItemById(itemId);
clientContext.load(this.singleItem, 'Status');
clientContext.executeQueryAsync(Function.createDelegate(this, this.OnSucceeded), Function.createDelegate(this, this.OnFailed));
}
function OnSucceeded() {
this.StatusValue = this.singleItem.get_item('Status');
RefreshCommandUI();
}
function OnFailed(sender, args) {
alert('Error occurred: ' + args.get_message());
}
When programming the functions needed you have to keep in mind the function defined in EnabledScript fires twice.
The first time it is the ‘initial time’, the second time is when calling the RefreshCommandUI method.
By keeping this in mind you have to keep track of a few things in the code, which can be used when it comes back on the second fire of the function.
The first item to keep track of is the item id. When selecting an item, deselecting and selecting the same item again, you don’t want the code to check the Status field value again, because you know that value already.
The most important value you want to keep track of is the Status field value of the selected item. The first time the function fires and the async call is made the Status field value is stored. Then the RefreshCommandUI is called
and fires the function again. This is the time you want to know the previous set Status field value, because this is the time to return true or false to enable or disable the button.
With this knowledge the definite enableTaskNotCompletedButton function:
function enableTaskNotCompletedButton() {
var result = false;//default return value of the function which disables the button
var selectedItems = SP.ListOperation.Selection.getSelectedItems();
//check if exactly one item is selected
if (CountDictionary(selectedItems) == 1) {
if (this.itemId != selectedItems[0]['id']) {
this.itemId = selectedItems[0]['id'];
GetTaskStatus(this.itemId);
}
else if (this.StatusValue == "Completed") {
result = false; //can be omitted, just for readibility
}
//when RefreshCommandUI() makes the EnabledScript fire again, none of the above checks are valid
//when the Status is not equal to Completed and the stored itemId is the same as the selected item id
else if (this.StatusValue != "Completed" && this.itemId == selectedItems[0]['id']) {
result = true;
}
}
return result;
}
Conclusion
When using async calls to check if a button has to be enabled in the ribbon you have to tell the ribbon when the async method is finished to check if the status of the button has to be updated.
The RefreshCommandUI method helps you out with asynchronous calls. Besides this method a variable has to be set to keep track of the status(enable or disable) of the button.
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!













