Posts Tagged ‘SharePoint 2010’
Empty tooltip in refinement panel
Sometimes when hovering over a fieldvalue in the refinementpanel the tooltip displays only ‘Refine By:’ without displaying any value.

While another displays an actual value:
Actual term in a tree

Why?
When the fieldvalue is less than 19 characters the tooltip stays empty. Well empty.. it displays ‘Refine By:’. Every fieldvalue with more than 19 characters is displayed in the tooltip. A ‘parent’ term or a whole path.
How to solve?
This behavior can be solved by adjusting the xslt.
Original xslt:
<a href="{$SecureUrl}" title="{$RefineByHeading}: {$UrlTooltip}">
<xsl:value-of select="Value"/>
</a>
Adjusted xslt:
<xsl:variable name="UrlTooltipAdjusted">
<xsl:call-template name="format-tooltip">
<xsl:with-param name="tooltip" select="$UrlTooltip" />
<xsl:with-param name="string" select="Value" />
</xsl:call-template>
</xsl:variable>
<a href="{$SecureUrl}" title="{$RefineByHeading}: $UrlTooltipAdjusted}">
<xsl:value-of select="Value"/>
</a>
<xsl:template name="format-tooltip">
<xsl:param name="tooltip" />
<xsl:param name="string" />
<xsl:choose>
<xsl:when test="$tooltip != ''">
<xsl:value-of select="$tooltip" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$string" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
The ‘format-tooltip’ template checks if the tooltip is empty and replaces the tooltip value with the actual fieldvalue if so.
By doing this the tooltip will never be empty and will always show the value of the fieldvalue.

Summary
Besides the fieldvalues also the tooltip values suffer from a character limitation. The values of the refinement panel have a 19 character display limit, the tooltip doesn’t display the value when the fieldvalue is less than 19 characters.
This and the previous post solve these issues by making minor adjustments to the OOTB xslt.
Refinement panel character display limitation
Search refiners can contain managed metadata fields to refine the results. Sometimes the display mode of the values look a bit weird in the refinement panel.
Suppose a sitecolumn of a library is a managed metadata column bound to a global termset. The termset is the parent of a few terms and all of these terms have one or more children itself. A termtree.
The display format of the column is set to ‘Display the entire path to the term in the field’.

A few documents are uploaded to the library and the metadata column is set to one of the terms.
A full crawl is completed and the refinement panel shows the metadata refiner.
The refinement panel now looks like this:

As can be seen, not the whole fieldvalue is displayed. That’s weird. How do I know which one to use if not the whole value can be seen?
Maybe it’s a css issue or the left column of the screen isn’t wide enough? Starting up Firebug and checking out the value:

The whole value isn’t present as text to display in the panel! I was seriously surprised!
SharePoint returns only the first 19 characters and three dots…
But… hover over de terms and the whole path is displayed. But that’s not a satisfactory solution, the values have to be displayed properly!
Ok, I agree displaying the whole term tree path isn’t that useful in this case, but it’s needed somewhere else in the site, so the display format of the site column has to stay ‘Display the entire path to the term in the field’.
It would be great to have on option to show only the last term value despite the column display format. An excellent configuration place would be an extra attribute in the filter categories definition xml.
Displaying the last term value
The xslt of the refinementpanel can be adjusted to display the last term value now we know the tooltip does know the whole value.
Original xslt:
<a href="{$SecureUrl}" title="{$RefineByHeading}: {$UrlTooltip}">
<xsl:value-of select="Value"/>
</a>
The new xslt looks a little bit different than that.
First the fieldvalue has to be analyzed if it contains ‘:’. The ‘:’ means the value is a child term and the whole tree path is displayed.
From the value which contains the whole tree path the last term is filtered by a recursive xslt template ´substring-after-last´.
Then a check has to be performed if the last term value contains the three dots. If it does, the last term value should be taken from the tooltip value, because the xslt Value doesn’t contain this value. Confusing, isn’t it?
<xsl:variable name="PartOfValue"> <xsl:call-template name="substring-after-last"> <xsl:with-param name="string" select="Value" /> <xsl:with-param name="delimiter" select="':'" /> </xsl:call-template> </xsl:variable> <xsl:variable name="PartOfTooltip"> <xsl:call-template name="substring-after-last"> <xsl:with-param name="string" select="$UrlTooltip" /> <xsl:with-param name="delimiter" select="':'" /> </xsl:call-template> </xsl:variable> <xsl:choose> <xsl:when test="($FilterCategoryType = 'Microsoft.Office.Server.Search.WebControls.TaxonomyFilterGenerator') and ($PartOfValue != '')"> <xsl:if test="not(contains($PartOfValue, '…'))"> <xsl:value-of select="$PartOfValue"/> </xsl:if> <xsl:if test="contains($PartOfValue, '…')"> <xsl:value-of select="$PartOfTooltip"/> </xsl:if> </xsl:when> <xsl:otherwise> <xsl:value-of select="Value"/> </xsl:otherwise> </xsl:choose>
If the fieldvalue contains less than 19 characters, no dots are displayed and the value can be used, but in that case the urltooltip is empty… To solve an empty tooltip, check out my next post.
After the xslt has been implemented the refinementpanel looks like the picture below

And the tooltip displays the whole term tree:

SharePoint returns only the first 19 characters, so what happens when a term itself exists of 19 or more characters?

On the one hand it’s great the 19 character limitation of displaying a whole termtree isn’t applied to this term, on the other hand, it doesn’t look very nice when the text continues outside the refinement panel.
Summary
The values of the refinement panel have a 19 character display limit. When displaying a whole termtree it’s likely this limitation will be exceeded. Just a few xslt adjustments are necessary to display the fieldvalues correct again.
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.
How to delete crawled properties
You all know by now how to delete crawled properties and that you’re not able to delete a single crawled property. When deleting crawled properties, all unmapped properties from a single category get deleted.
- In Central Administration select ‘Manage service applications’
- Select the Search Service Application
- Select ‘Metadata properties’
- Select ‘Categories’
- Edit the category of your choice
- Select ‘Delete all unmapped crawled properties’
- Select ‘Ok’
First thing to notice here: it deletes only unmapped crawled properties. So if your crawled property is still mapped, remove this first.
Second thing: make sure the crawled property isn’t included in the index!
First there are two crawled properties in the SharePoint category (well, there are more, I selected two to see the difference…) :

One is included in the index, the other isn’t.
Next step is to clear the SharePoint category by following all the steps above. The result:

The crawled property included in the index is still present! Even if a full index reset has been performed and the category has been cleaned up, the crawled properties doesn’t get deleted when ‘Included in index’ is set to ‘Yes’.
Summary
Besides unmapping a crawled property, deselect ‘Included in index’ in the crawled property’s properties to delete a crawled property.
This is a valid and working solution for the most of the crawled properties and certainly your custom ones.
When you’re going to test this youself, you’ll notice some of the OOTB unmapped and not include in the index crawled properties will still be present after performing the steps above. This is the case because SharePoint uses some hidden managed properties which are mapped to these crawled properties. Steve Curran explains this in his post which was written for MOSS 2007, but the same principle is still valid for SharePoint 2010:
In table MSSCrawledProperties ows_BaseName can be found with CrawledPropertyId 192.
In table MSSSchemaPropertyMappings CrawledPropertyId 192 is mapped to PID 2147418032.
In table MSSManagedProperties PID 2147418032 is mapped to property TempTitle with ‘Hidden’ and ‘NoDelete’ set to 1:
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.
How to retrieve document set version history
Document set versions are slightly different than item versions. Document sets can be managed by a separate ribbon tab called Document Set and group called Manage.

To create a version of a document set the action Capture Version in this part of the ribbon has to be selected. When selecting the following screen will be shown:

To use versioning of a document set (and items) versioning has to be enabled on the library.
After selecting a version option (when major/minor enabled), adding some comment and selecting the Ok button a document set version is created. To view the versions of the document set the action Version History can be selected in the ribbon.

In this screen the first column displayed is No. This is not the regular version column of the library, but a totally different one. When creating a document set version the version column of the library doesn’t change, only the No value.
That’s nice, but where is the version of the document set actually stored?
The version(s) of a document set are stored in the propertybag of the item itself.
To analyze settings I always start up PowerShell first to check on things. Just because it’s quick and easy. When I get what I want from PowerShell it’s easily turned into C# code. I followed the same procedure to get to the storage of document set versions. Since I couldn’t find the version anywhere in the UI, my first guess was checking the propertybag keys.
$site=Get-SPSite "http://sp2010dev"
$docList = $site.RootWeb.Lists.TryGetList("Documents");
$item = $docList.Items.GetItemById(2)
$prop = $item.Properties
Often I use PowerGui Script Editor which gives an excellent overview of variables:
While scrolling through the property keys I noticed a key named snapshots. By checking out the value of that key I knew I found it!
The following PowerShell command can be used to get the value of the snapshot key:
$item.Properties.get_Item("snapshots")
The version history is stored in xml:
<SnapshotCollection NextSnapshotNumber="3" NextInternalId="1">
<Items />
<Snapshots>
<Snapshot Label="2" Major="True" Created="08/21/2011 07:50:56" By="username">
<Comments>Another version of the document set.</Comments>
<Fields>
<Field Id="8553196d-ec8d-4564-9861-3dbe931050c8">Document set name</Field>
<Field Id="fa564e0f-0c70-4ab9-b863-0177e6ddd247">Document set name</Field>
<Field Id="cbb92da4-fd46-4c7d-af6c-3128c2a5576e">Add a description here.</Field>
</Fields>
<SnapshotItems />
</Snapshot>
<Snapshot Label="1" Major="True" Created="08/21/2011 07:50:20" By="username">
<Comments>This is the first version of this document set.</Comments>
<Fields>
<Field Id="8553196d-ec8d-4564-9861-3dbe931050c8">Document set name</Field>
<Field Id="fa564e0f-0c70-4ab9-b863-0177e6ddd247">Document set name</Field>
<Field Id="cbb92da4-fd46-4c7d-af6c-3128c2a5576e" />
</Fields>
<SnapshotItems />
</Snapshot>
</Snapshots>
</SnapshotCollection>
The Snapshot element with Label attribute 1 is the first version. The comment is displayed and the fields and values of the document set.
The Snapshot element with Label attribute 2 is the second version. The same items are displayed with the addition of the value of the description ‘Add a description here’. That’s what I changed before creating the second version.
The attribute NextSnapshotNumber holds the value of the version number that will be created when creating another version, 3 in this case.
Summary
Document set versions are different than regular item versions. The version history of a document set is stored in the propertybag of the document set itself as xml.
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

Client side social dashboard with SharePoint 2010 and SPServices
The social dashboard functionality
|
Functionality
|
Employee
|
Manager
|
Board member
|
|
Tags of current user
|
V
|
V
|
V
|
|
Most used tags
|
V
|
V
|
V
|
|
Drill down to url
|
V
|
V
|
V
|
|
Top active users
|
V
|
V
|
V
|
|
Drill down to tag
|
X
|
V
|
V
|
|
Activity of employees in own department
|
X
|
V
|
V
|
|
Activity of employees in a selectable department
|
X
|
X
|
V
|
<script src="/jQueryLibrary/jquery-1.4.4.min.js" type="text/javascript"></script> <script src=" /jQueryLibrary/jquery.SPServices-0.5.8.min.js" type="text/javascript"></script> <script src=" /jQueryLibrary/jquery.tmpl.js" type="text/javascript"></script>
Tags of current user
var currentUserAccount = $().SPServices.SPGetCurrentUser({
fieldName: "Name"
});
$().SPServices({
operation: "GetTagsOfUser",
userAccountName: $().SPServices.SPGetCurrentUser(),
completefunc: function (xData, Status) {
$(xData.responseXML).find("SocialTagDetail").each(function () {
tagName = $("Term>Name", $(this)).text();
tagsofuser.push({ Tag: tagName, Count: 1 });
});
tagsofuser = uniqueTags(tagsofuser);
SortByCount(tagsofuser);
$("#currentUserTagsText").show();
$("#currentUserTags").html("");
$("#tagsofuserTemplate").tmpl(tagsofuser)
.appendTo("#currentUserTags");
}
});
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <GetTagsOfUserResponse xmlns="http://microsoft.com/webservices/SharePointPortalServer/SocialDataService"> <GetTagsOfUserResult> <SocialTagDetail> <Url>http://sp2010/Lists/Tasks/AllItems.aspx</Url> <Owner>sp2010\mark</Owner> <LastModifiedTime>2010-12-19T13:28:09.437</LastModifiedTime> <Title>Tasks - All Tasks</Title> <Term> <Id>974a854f-31b4-431f-91cb-a6289f58c978</Id> <Name>I like it</Name> </Term> <IsPrivate>false</IsPrivate> </SocialTagDetail> Rest of the SocialTagDetails are intentionally left out� </GetTagsOfUserResult> </GetTagsOfUserResponse> </soap:Body> </soap:Envelope>
<div><script id="tagsofuserTemplate" type="text/x-jquery-tmpl">
<div> </div>
<div><div style="float:left;width:40%">
<div> </div>
<div> ${Tag}
<div> </div>
<div></div>
<div> </div>
<div><div style="float:left;width:20%">
<div> </div>
<div> ${Count}
<div> </div>
<div></div><br />
<div> </div>
<div></script>
<div>
Most used tags
$().SPServices({
operation: "GetAllTagTerms",
debug: false,
completefunc: function (xData, Status) {
$(xData.responseXML).find("SocialTermDetail").each(function () {
termName = $("Term>Name", $(this)).text();
termGuid = $("Term>Id", $(this)).text();
counter = $("Count", $(this)).text();
terms.push({ Term: termName, Count: counter, TermGuid: termGuid });
});
SortByCount(terms);
terms.length = 5;
$("#showterms").html("");
$("#termTemplate").tmpl(terms)
.appendTo("#showterms");
}
});
<div><div style="float:left;width:40%">
<div> </div>
<div> <a href="#" id=${TermGuid}>
<div> </div>
<div> ${Term}
<div> </div>
<div> </a>
<div> </div>
<div></div>
<div> </div>
<div><div style="float:left;width:20%">
<div> </div>
<div> ${Count}
<div> </div>
<div></div>
<div>
$().SPServices({
operation: "GetAllTagUrls",
termID: termId,
debug: true,
completefunc: function (xData, Status) {
$(xData.responseXML).find("SocialUrlDetail").each(function () {
url = $("Url", $(this)).text();
countUrl = $("Count", $(this)).text();
urlsofterm.push({ Url: url, Count: countUrl });
});
SortByCount(urlsofterm);
$("#urlsoftermContainer").show();
$("#showurlsofterm").html("");
$("#urlTemplate").tmpl(urlsofterm)
.appendTo("#showurlsofterm");
}
});

Top active users
$().SPServices({
operation: "GetUserCollectionFromSite",
completefunc: function (xData, Status) {
$(xData.responseXML).find("User").each(function () {
userName = $(this).attr("LoginName");
userNames.push({ UserName: userName });
});
GetTopTagsOfUsers(userNames);
}
});
function CountTagsOfUser(useraccountname, f) {
var tagCount = "";
var tagsofuser = [];
$().SPServices({
operation: "CountTagsOfUser",
userAccountName: useraccountname,
debug: true,
completefunc: function (xData, Status) {
$(xData.responseXML).find("CountTagsOfUserResult").each(function () {
tagCount = $(this).text();
tagsofuser.push({ Count: tagCount, UserAccountName: useraccountname });
});
SortByCount(tagsofuser);
if (typeof f == "function") f(tagsofuser);
return tagsofuser;
}
});
}
function GetTopTagsOfUsers(usernames) {
var result = [];
$.each(usernames, function (key, value) {
CountTagsOfUser(value["UserName"], function (items) {
$.each(items, function (i, n) {
result.push({ Count: n["Count"], UserAccountName: n["UserAccountName"] });
});
if (usernames.length == result.length) {
SortByCount(result);
result.length = 5;
$("#showcounttagsofuser").html("");
if (isCEO || isManager) {
$("#counttagsofuserTemplate").tmpl(result)
.appendTo("#showcounttagsofuser");
} else {
$("#counttagsofuserTemplateForUser").tmpl(result)
.appendTo("#showcounttagsofuser");
}
}
});
});
}
<script id="counttagsofuserTemplateForUser" type="text/x-jquery-tmpl">
<div style="float:left;width:40%">
${UserAccountName}
</div>
<div style="float:left;width:20%">
${Count}
</div>
</script>
<script id="counttagsofuserTemplate" type="text/x-jquery-tmpl">
<div style="float:left;width:40%">
<a href="#" id=${UserAccountName}>
${UserAccountName}
</a>
</div>
<div style="float:left;width:20%">
${Count}
</div>
</script>
What’s the difference between a ‘regular’ employee, a manager and a board member?
|
UserName
|
Department
|
Manager
|
Role
|
|
Alex
|
ICT
|
Andrew
|
Employee
|
|
Andrew
|
ICT
|
|
Manager
|
|
Anita
|
Marketing
|
Mark
|
Employee
|
|
Chris
|
HR
|
Dave
|
Employee
|
|
Dave
|
HR
|
|
Manager
|
|
Jeff
|
Marketing
|
Mark
|
Employee
|
|
Mark
|
Marketing
|
|
Manager
|
|
Paul
|
CEO
|
|
Board member
|
var CEOdepartment = "CEO";
$().SPServices({
operation: "GetUserProfileByName",
AccountName: currentUserAccount,
completefunc: function (xData, Status) {
$(xData.responseXML).find("GetUserProfileByNameResult>PropertyData").each(function () {
//check for CEO department
if ($("Name", $(this)).text().toUpperCase() == "Department".toUpperCase()) {
departmentOfCurrentUser = $("Values>ValueData>Value", $(this)).text();
if (departmentOfCurrentUser.toUpperCase() == CEOdepartment.toUpperCase()) {
isCEO = true;
GetDepartmentsAndUsers();
}
}
else if ($("Name", $(this)).text().toUpperCase() == "Manager".toUpperCase()) {
managerName = $("Values>ValueData>Value", $(this)).text();
if (managerName == "") {
if (!isCEO) {
isManager = true;
GetDepartmentsAndUsers();
}
}
}
});
}
});
<?xml version="1.0"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <soap:Body> <GetUserProfileByNameResponse xmlns="http://microsoft.com/webservices/SharePointPortalServer/UserProfileService"> <GetUserProfileByNameResult> <PropertyData> <IsPrivacyChanged>false</IsPrivacyChanged> <IsValueChanged>false</IsValueChanged> <Name>AccountName</Name> <Privacy>Public</Privacy> <Values> <ValueData> <Value xsi:type="xsd:string">sp2010\paul</Value> </ValueData> </Values> </PropertyData> <PropertyData> <IsPrivacyChanged>false</IsPrivacyChanged> <IsValueChanged>false</IsValueChanged> <Name>Department</Name> <Privacy>Public</Privacy> <Values> <ValueData> <Value xsi:type="xsd:string">CEO</Value> </ValueData> </Values> </PropertyData> </GetUserProfileByNameResult> </GetUserProfileByNameResponse> </soap:Body> </soap:Envelope>
Activity of employees in own or a selectable department
var queryTextSQL = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>"
queryTextSQL += "<Query>"
queryTextSQL += "<Context>"
queryTextSQL += "<QueryText language='en-US' type='MSSQLFT'>"
if (isCEO) {
queryTextSQL += "SELECT Title, Rank, Size, Description, Write, Path, AccountName, Department FROM scope() WHERE ( (\"SCOPE\" = 'People') ) ORDER BY \"Rank\" DESC"
} else {
queryTextSQL += "SELECT Title, Rank, Size, Description, Write, Path, AccountName, Department FROM scope() WHERE ( (\"SCOPE\" = 'People') ) AND (CONTAINS (Department,'" + departmentOfCurrentUser + "')) ORDER BY \"Rank\" DESC"
}
queryTextSQL += "</QueryText>"
queryTextSQL += "</Context>"
queryTextSQL += "</Query>"
queryTextSQL += "</QueryPacket>";
$().ready(function () {
var resultText = "";
$().SPServices({
operation: "QueryEx",
queryXml: queryTextSQL,
completefunc: function (xData, Status) {
$(xData.responseXML).find("RelevantResults").each(function (i) {
accountName = $("ACCOUNTNAME", $(this)).text();
departments[i] = $("DEPARTMENT", $(this)).text();
users.push({ AccountName: accountName, Department: departments[i] });
});
if (isCEO) {
$('#departmentContainer').show();
fillDepartmentsDropdown('#departmentSelection', $.unique(departments), "Select department");
}
if (isCEO || isManager) {
$('#userContainer').show();
fillUsersDropdown('#userSelection', users, "Select user");
}
}
});
});
function fillUsersDropdown(dropdownName, arrayToUse, text) {
$(dropdownName).html("");
$.each(arrayToUse,
function (key, value) {
$(dropdownName).append('<option value="' + value["AccountName"] + '">' + value["AccountName"] + '</option>');
});
$(dropdownName).prepend("<option value='0' selected='true'>" + text + "</option>");
$(dropdownName).find("option:first")[0].selected = true;
}
<div style="float: left; width: 100%; display: none;" id="departmentContainer"> <div style="float: left; width: 15%"> Select department</div> <select id="departmentSelection" style="float: left; width: 15%"> </select> </div> <br /> <div id="userContainer" style="float: left; width: 100%; display: none;"> <div style="float: left; width: 15%"> Select user</div> <select id="userSelection" style="float: left; width: 15%"> </select> <div style="clear: both; float: left; width: 25%"> <a href="#" onclick="GetTagsOfSelection(userSelection.value, departmentSelection.value);return false"> Show tags of selection</a></div> <h4 style="clear: both; float: left; display: none;" id="showtagsofuserText"> Tags of selection:</h4> <div id="showtagsofuser" style="clear: both;"> </div> </div>
$('select').change(function (e) {
if (this.id == "departmentSelection") {
$('#userSelection option').each(function (i, option) {
$(option).remove();
});
j = 0;
childArray.length = 0;
if (this[this.selectedIndex].value != 0) {
departmentSelected = this[this.selectedIndex].text;
//filter the users array on the selected department
filteredUsers = $.grep(users, function (filter) {
return filter["Department"] == departmentSelected;
});
fillUsersDropdown('#userSelection', filteredUsers, "Select user");
}
else {
fillUsersDropdown('#userSelection', users, "Select user");
}
}
});
function GetTagsOfSelection(userSelection, departmentSelection) {
var result = [];
if (userSelection != 0) {
GetTagsOfUser(userSelection, true, true);
} else if (departmentSelection != 0) {
$.each(filteredUsers, function (key, value) {
GetTagsOfUser(value["AccountName"], false, true, false, function (items) {
$.each(items, function (i, n) {
result.push({ Tag: n["Tag"], Count: 1 });
});
result = uniqueTags(result);
SortByCount(result);
$("#showtagsofuserText").show();
$("#showtagsofuser").html("");
$("#tagsofuserTemplate").tmpl(result)
.appendTo("#showtagsofuser");
//}
});
});
}
}
The result
Summary
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


























