Posts Tagged ‘jQuery’
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
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:
- Search Refiners part 1 – Expanding the OOTB search Refinement Panel
- Search Refiners part 2 – Use of CustomFilters
- Search Refiners part 3 – Chart based
- 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.
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
SPServices and jQuery templates: Upcoming birthday’s overview
The use of jQuery templates is very neat. It easily displays content in a structured way according to a defined template. Microsoft created the jQuery template plugin (https://github.com/nje/jquery-tmpl) which can be used by referencing the template script file.
In this article both SPServices and the jQuery template will be used to show the strength of combining these libraries.
Purpose
All employees are highly appreciated at the company there are working (of course). The company decides to send all employees flowers on their birthday. The employee data is stored in a standard SharePoint Contacts list extended with a column to register the birth days. Employees of the HR department are responsible for sending the flowers on time, but they it’s difficult for them to get the upcoming birthdays from the Contact list.
It is desirable to provide the HR department with an overview of upcoming birthdays.
Set up the Contacts list
To create the overview of upcoming birthdays a standard SharePoint Contacts list is used extended with a column with the name ‘Date of birth’ of type Date and Time. To display some additional information in the overview the column ‘Department’, type single line of text, is added to the list.
The list is filled with some data:

Create the overview
The SPServices operation GetListItems is used to get the data from the Contacts list:
$().SPServices({
operation: "GetListItems",
async: false,
debug: true,
listName: "Contacts",
CAMLViewFields: "<ViewFields></ViewFields>",
completefunc: function (xData, Status) {
$(xData.responseXML).find("[nodeName=z:row]").each(function () {
daysleft = CalculateDaysLeft($(this).attr("ows_Date_x0020_of_x0020_birth"));
contacts.push({ FirstName: $(this).attr("ows_FirstName"), LastName: $(this).attr("ows_Title"), Department: $(this).attr("ows_Department"), DaysLeft: daysleft });
});
SortByDaysLeft(contacts);
$("#showContacts").html("");
$("#contactTemplate").tmpl(contacts)
.appendTo("#showContacts");
}
});
The callback function gets the response of the web service. The function CalculateDaysLeft calculates the days left until the date stored in ‘Date of birth’ will occur in the current year. Since this is a helper method it is not showed here.
The firstname, lastname, department and the daysleft are stored in an array of objects named contacts. To display the birthdays in upcoming order the function SortByDaysLeft is called. This is also a helper function and for readability not displayed here.
A jQuery template, contactTemplate, is used to show the results on the page. The template is rendered with the data and appended to a div with the id showContacts. The div functions as the template container.
The template uses the objects in the array:
<script id="contactTemplate" type="text/x-jquery-tmpl">
<div>
${FirstName} ${LastName}
<ul style="list-style-type:none;">
<li>${Department}</li>
<li>${DaysLeft} days left until birthday!!</li>
</ul>
</div>
</script>
The first thing you’ll probably notice is the script tags. Because of these tags the template is embedded in the body of the page. In the template html can be used to format the data to be displayed. The ${FirstName}, ${LastName}, ${Departement} and ${DaysLeft} expressions are used in the template as placeholders for the data present in the array of objects ‘contacts’. ${..} tells the parser the fields have to be replaced with the values passed in the template by the array of objects.
The template is rendered once for each item in the array named contacts.
The upcoming birthdays are displayed with the contactTemplate as shown in the figure below:

This template is quite simple and fulfills the purpose of displaying upcoming birthdays from the Contacts list.
The template displays ‘.. days left until birthday!’ for all items. It’s much nicer to display a message based on the number of days left to provide the HR department with some additional information.
This can be accomplished with code in the template itself with an ‘if ..else..’ statement as shown in the code below.
<script id="contactTemplateInlineCode" type="text/x-jquery-tmpl">
<div>
${FirstName} ${LastName}
<ul style="list-style-type:none;">
<li><a href="#" id=${Department} title="Show all employees of ${Department}">${Department}</a></li>
<li>
{{if (DaysLeft < 10)}} Did you order flowers already? It's almost his birthday, only ${DaysLeft} days!
{{else DaysLeft < 150}} It takes a little while until his birthday: ${DaysLeft} days!
{{else (DaysLeft < 360 )}} A long time until his birthday: ${DaysLeft} days!
{{else (DaysLeft > 359 )}} If you didn't sent any flowers, you're too late now...{{/if}}
</li>
</ul>
</div>
</script>
Besides adjusting the text based of the number of days left, the Department is adjusted to be a link. When selecting the link the function GetAllContactsFromDepartment is called with the ID of the link. The ID of the link is set with the Department value. The values of the array used by the template can be used everywhere in the template. The function GetAllContactsFromDepartment will display the firstname, lastname and day and month of the birthdate of the specified Department with the help of the SPServices library operation GetListItems.
The results of this operation are stored in an array of objects and another template is rendered with this array.
$().SPServices({
operation: "GetListItems",
async: false,
debug: true,
listName: "Contacts",
CAMLViewFields: "<ViewFields></ViewFields>",
CAMLQuery: "<Query><Where><Eq><FieldRef Name='Department'/><Value Type='Text'>" + Department + "</Value></Eq></Where></Query>",
completefunc: function (xData, Status) {
$(xData.responseXML).find("[nodeName=z:row]").each(function () {
var temp = $(this).attr("ows_Date_x0020_of_x0020_birth");
var formattedDate = "";
if (temp.length > 0) {
var month = temp.substring(5, 7);
var day = temp.substring(8, 10);
formattedDate = day + " " + month;
daysLeft = CalculateDaysLeft(temp);
}
contactsOfDepartment.push({ FirstName: $(this).attr("ows_FirstName"), LastName: $(this).attr("ows_Title"), BirthDate: formattedDate, DaysLeft: daysLeft });
});
SortByDaysLeft(contactsOfDepartment);
$("#showContactsFromDepartment").html("<h4>" + Department + "</h4>");
$("#DepartmentTemplate").tmpl(contactsOfDepartment)
.appendTo("#showContactsFromDepartment");
}
});
The DepartmentTemplate used in the code above:
<script id="DepartmentTemplate" type="text/x-jquery-tmpl">
<div style="float:left;width:100%">
<div style="float:left;width:15%">
${FirstName} ${LastName}
</div>
<div style="float:right;width:85%">
${BirthDate}
</div>
</div>
</script>
So within a template a call to fill another template with data can be made.
The upcoming birthdays are now displayed as in the figure below:�

When selecting the Marketing department another template will be filled with data:

All the code displayed in this post is based on jQuery. With references to jQuery, the SPServices library and the jQuery template plugin all the code can be pasted in a Content Editor web part on a SharePoint page.
<script src="/jQueryLibrary/jquery-1.4.4.min.js" type="text/javascript"></script> <script src="/jQueryLibrary/jquery.SPServices-0.5.6.min.js" type="text/javascript"></script> <script src="/jQueryLibrary/jquery.tmpl.js" type="text/javascript"></script>
Summary
By combining libraries like SPServices and jQuery templates it is quite straightforward to display a list of upcoming birthdays from a standard SharePoint Contacts list.
The strength of the use of SPServices here is to get the necessary data in a straightforward way; the strength of the use of jQuery templates is to display the data in a nicely formatted way. Using a combination of both libraries I think the best of both worlds are used the appropriate way.
Extended cascading with SPServices and jQuery
Recently I wrote a plugin which provided cascading dropdowns with jQuery and SPServices, which you can find here.
To be more flexible about how to display this to the user I extended the plugin. With the extended plugin it is possible to show the parent values of the cascading part with radiobuttons and the child part still with a dropdown list. Also a new option is added to load the values of the childdropdown by default or to wait for the user to make a selection in the parent list.
To show the values of the parent list in a radiobutton list use the id of a div element present in the page:
<div id="countriesRadio"></div>
and call the plugin on this div:
<script type="text/javascript" src="../jQueryLibrary/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="../jQueryLibrary/jquery.itidea_spcascadingdropdownExtended.js"></script>
<script type="text/javascript" src="../jQueryLibrary/jquery.SPServices-0.5.6.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$('#countriesRadio').itidea_spcascadingdropdownExtended(
{
relationshipList: "Cities",
relationshipParentList: "Countries",
relationshipParentListColumn: "Title",
relationshipListChildColumn: "Title",
relationshipListParentColumn: "Country",
childdropdown: "citiesRadio",
fillChildByDefault: true
});
});
</script>
In the above code sample the new option fillChildByDefault is set to false. This means the values of the parent will be showed, but no child values will be showed when the page loads. If this option is omitted in the above code the value is set to true by default in the plugin and the child values will be shown when the page loads.
With the above settings the controls look like this:

By omitting the option fillChildByDefault (which is the same as setting this to true):

Summary
With the extended plugin more flexibility is added to show the values to the user. With this plugin it is still possible to show both lists as dropdownlists by replacing the div element by a select element with the same id as shown in the previous post about cascading dropdowns.
Download
Download the plug-in: jquery.itidea_spcascadingdropdownExtended.js
SharePoint 2007 Author search refiner
SharePoint 2010 has a lot of nice features. One of them are search refiners. The search results can be narrowed down by Result Type, Site, Author, Modified Date, etc.

When knowing and seen that feature I’m really disappointed SharePoint 2007 doesn’t has this. Ok, the Advanced Search page is available, but it’s by far not that easy to use as the search refiners in SharePoint 2010.
Another thing popped up into my mind when using the SharePoint 2010 search refiners. Why can’t I select for example multiple authors? Not all, not one, but two or three or whatever I like at that moment with that search action.
Purpose
These two things: Search refiner in SharePoint 2007 and the possibility to select more than one item
are the starting point of this post.
One other thing: I like SPServices and jQuery, so I’m going to try to use these techniques instead of a farm solution.
So let’s create a search refiner on authors in this post. When the user performs a search and the results are displayed the option to select one or more authors to narrow down the search results will be available. All the different authors are displayed with a checkbox to select one or more and narrow down the search results accordingly.
While writing this post a lot of explaining has to be done so I decided to show it first. After that I’ll explain how to get there.
See it in action
The unique authors are listed, the user selects one

Search results based on the author selection and the initial search text:

Now you know what it does, let me explain it.
Analyzing the search
When a user types his search text in the standard search box, SharePoint 2007 performs a search by encoding the query and posting it to the results page. That’s basically just it, a basic query.
The Advanced Search page uses SQL syntax and can contain almost every query and result one can possibly imagine. For the purpose in this post we have to use the SQL syntax.
The next step is to analyze the Advanced Search box webpart and how a search is performed.

By typing a search string in the ‘All of these words’ box and performing the search, the results are displayed. When analyzing the source of this page the SQL query can be found.
Searching on the word ‘item’ and the property ‘Author Contains anita’ results in the following SQL query (removed the encoding):
SELECT WorkId, Rank, Title, Author, Size, Path, Description, Write, SiteName, CollapsingStatus, HitHighlightedSummary, HitHighlightedProperties, ContentClass, IsDocument, PictureThumbnailURL from scope() where freetext(defaultproperties, ‘+item’) And (Author like ‘%anita%’) Order By Rank desc
How does SharePoint know how to format the query like this? How does it know to put ‘item’ and the author search in the right place of the query?
With the help of the IE Developer toolbar:
‘All of these words’ has the name ASB_TQS_AndQ_tb.
The first ‘(Pick property)’ has the name ASB_PS_plb_0, the operator ASB_PS_olb_0 and the value ASB_PS_pvtb_0. The number at the end of the name will increase when multiple properties are used. For the second property the names ending with _1 are used. The And Or operator when using multiple properties is called ASB_PS_lolb_0 and increases when more than two properties are used. These names are used to make up the SQL query.
There are more controls on this page but only the ones mentioned above will be used in this example.
The code
To perform the right search, the SQL syntax will be used the way the Advanced Search webpart is using it by using html controls with the same names mentioned above.
Getting the authors
When the user performs a search, the results are displayed. The get all the authors of these search results to display the names in a checkbox label later on, the operation QueryEx of the SPServices library is used.
First the query is set up:
var queryTextSQL = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>" queryTextSQL += "<Query>" queryTextSQL += "<Context>" queryTextSQL += "<QueryText language='en-US' type='MSSQLFT'>" //use the same scope (Advanced Search) queryTextSQL += "SELECT Title, Rank, Size, Description, Write, Path, Author FROM scope() WHERE FREETEXT (DEFAULTPROPERTIES, '+" + sessvars.searchValue + "') ORDER BY \"Rank\" DESC" queryTextSQL += "</QueryText>" queryTextSQL += "</Context>" queryTextSQL += "</Query>" queryTextSQL += "</QueryPacket>";
To use the SQL syntax the type attribute is set to MSSQLFT.
The sessvars.searchValue is filled with the initial search text which the user typed in the search box. Sessions variables without cookies are used here. More information can be found here: http://www.thomasfrank.se/sessionvars.html
After performing a search this search text is displayed in the querystring of the url.
![]()
In code it is stored in a variable using $().SPServices.SPGetQueryString() with the following code:
var queryStringVals = $().SPServices.SPGetQueryString(); var searchItem = queryStringVals["k"];
This value is needed to get it into the SQL query and to put it back in the search box after performing the search. As you know when performing a ‘regular’ advanced search query, the search text is gone and not displayed in the search box anymore. To prevent ‘losing’ the user who performed the search the value will be put back in the search box.
After setting up the query let’s get the unique authors for the results:
$().SPServices({
operation: "QueryEx",
queryXml: queryTextSQL,
completefunc: function(xData, Status) {
k=0;
$(xData.responseXML).find("RelevantResults").each(function() {
$(this).find("AUTHOR").each(function() {
authors[k]=$(this).text();
k++;
});
});
if(authors.length > 0)
{
authors = $.unique(authors);
CreateCheckboxes(authors, "Author", "Contains");
}
}
});
The code fires the query and gets the authors from the results. The CreateCheckboxes method will create all the necessary controls and will be explained in the next part.
The controls
In this example an author search refiner will be built where a name of an author is the value in the query. To accomplish this, the control (using a checkbox in this example) which displays a single author has to have the name ASB_PS_pvtb_0, the next author ASB_PS_pvtb_1, etc.
The other names of controls which have to be used are ASB_PS_plb_x (x starting at 0) to set the property name and ASB_PS_olb_x as the operator. Authors are used here, so ASB_PS_plb_x has always the value ‘AUTHOR’ and the operator has always the value ‘Or’ since an item can have one author at the same time. These two controls are hidden controls, since these controls are of no use to the user. Only the checkboxes are displayed.
The CreateCheckboxes method will set this up for each individual author:
function CreateCheckboxes(authors, propertyName, operator)
{
var authorContainer= $('#someElementId');
var j=1;
$.each(authors, function( iteration, item )
{
//add or statement
if(j==1)
{
//<input value="Or" />
authorContainer.append(
$(document.createElement("div"))
.append(
createHiddenNamedElement("input","nameprefix$ASB_PS_lolb_" + (j-1), "Or")
)
)
}
authorContainer.append(
$(document.createElement("div"))
.append(
$(document.createElement("input")).attr({
type: 'checkbox'
,name: 'nameprefix$ASB_PS_pvtb_' + j
,id: 'author_' + item
,value: item
})
.click( function( event )
{
var cbox = $(this)[0];
if(cbox.checked)
{
//remember selected checkboxes to put back after postback
sessvars['numberOfAuthorsSelected']++;
sessvars['numberOfAuthorsSelected' + sessvars['numberOfAuthorsSelected']] = escape(cbox.value);
}
else{
//remember only the selected items
var total = sessvars['numberOfAuthorsSelected'];
sessvars['numberOfAuthorsSelected']=0;
for(var i=1;i<=total;i++)
{
if(sessvars['numberOfAuthorsSelected' + i] != escape(cbox.value))
{
sessvars['numberOfAuthorsSelected']++;
sessvars['numberOfAuthorsSelected' + sessvars['numberOfAuthorsSelected']] = sessvars['numberOfAuthorsSelected' + i];
}
}
}
} )
)
.append(
$(document.createElement('label')).attr({
'for': 'author_' + item
})
.text( item )
)
//add hidden input values
.append(
createHiddenNamedElement("input","nameprefix$ASB_PS_plb_" + j, propertyName)
)
.append(
createHiddenNamedElement("input","nameprefix$ASB_PS_olb_" + j, operator)
)
)
j++;
} );
SetSelection();
}
First the operator ‘Or’ is created. After that, the author checkboxes are created. In the click event the tracking of selecting and unselecting the checkboxes is performed. Then the label is set for the checkbox and last the hidden controls are created to store the property name and the operator.
At last the SetSelection() method will be called. This method just selects the appropriate checkboxes based on the stored values.�
To perform the actual search a search button is needed:
<input onclick='WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("nameprefix$ASB_BS_SRCH_1", "", false, "", "results.aspx", false, false))' type="submit" value="Search" />
The url has to be set to the current page so the results, according to the selected authors and the search text provided, are displayed in the search results webpart on the same page.
One last important issue
All properties are treated as numbers by default. When performing a search with the code created right now, an error will occur when selecting an author and perform the search:
‘Invalide parameter: Author. Expect a number. ITIDEA\anita.boerboom is given instead.’
To tell SharePoint to treat the Author property like text the following control has to be added to the page with the name ASB_TextDT_Props :
<input type="hidden" name="ASB_TextDT_Props" id="Hidden4" value="Author" />
When multiple properties have to be treated like text the values can be separated by #;#, e.g. Title#;#Author
When properties have to be treated like dates, use ASB_DateTimeDT_Props the same way as the text property described.
Summary
Before creating a search refiner a lot of investigating was necessary, but it can be built for SharePoint 2007 fully in script.
Also this example can be extended to show the number of items per author and of course other refiners can be built on other properties.
Cascading dropdowns with jQuery and SPServices on a page or webpart
The SPServices library on Codeplex (http://spservices.codeplex.com) provides a lot of useful functionality to access SharePoint data.
One of the functions is the SPServices.SPCascadeDropdowns function. Here is a link to the documentation of this function: http://spservices.codeplex.com/wikipage?title=$().SPServices.SPCascadeDropdowns&referringTitle=Documentation
As explained on the site this function can be used to set up cascading dropdown on SharePoint forms. Repeat: on SharePoint forms.
Sometimes it’s handy to use cascading dropdown lists on a regular page or in a webpart. For this purpose I made a simple jQuery plug-in to provide in this need. The functionality is not that extended as the SPServices.SPCascadeDropdowns, but I’ll guess you get the point.
Purpose
The purpose in this example is to make cascading dropdowns with countries and cities. This information is stored in two SharePoint lists and displayed in a Content Editor Webpart on a page.
Set up the lists
Create a list named Countries. Fill in some country names in the default Title column.
Create a list named Cities. Create a lookup column to the Countries list and name the column Country. Do not allow multiple values, the plug-in isn’t ready for that. Fill in some cities and link them to a country.
Add a document library e.g. with the name ICTLibrary. Of course you are free to choose the names, but be aware of the references in the code in this article, I use the names mentioned here.
Upload three items to this list:
- jquery-1.4.4.min.js
- jquery-SPServices-0.5.8.min.js
- jquery.itidea_spcascadingdropdown.js
The last file is the plug-in which provides the cascading functionality on a page.
Create the cascading dropdownlists
To create the cascading dropdownlists on a page is quite easy. Just paste the following code to a Content Editor Webpart on a page:
<script type="text/javascript" src="../ICTLibrary/jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="../ICTLibrary/jquery.itidea_spcascadingdropdown.js"></script>
<script type="text/javascript" src="../ICTLibrary/jquery.SPServices-0.5.8.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$('#countries').itidea_spcascadingdropdown(
{
relationshipList: "Cities",
relationshipParentList : "Countries",
relationshipParentListColumn : "Title",
relationshipListChildColumn : "Title",
relationshipListParentColumn : "Country",
childdropdown : "cities"
});
});
</script>
<select id="countries" style="width:150px;">
</select>
<select id="cities">
</select>
There are a few things to know about the options:
relationshipList
The name of the list which contains the parent/child relationships.
relationshipParentList
The name of the list which contains the parent items.
relationshipParentListColumn
The StaticName of the values column in the parent list.
relationshipListChildColumn
The StaticName of the child column in the relationshipList
relationshipListParentColumn
The StaticName of the parent column in the relationshipList
childdropdown
The id of the child dropdownlist
promptText
The default text displayed in the dropdownlists
Most of the options are named the same as in the SPServices.SPCascadeDropdowns, because you are probably familiar with these names.
The cascading dropdownlists are looking like this now when nothing is selected:
Countries and all cities are loaded in the dropdownlists:
Summary
The plug-in is helpful if you want simple cascading dropdown lists on a page or in a webpart with the use of SharePoint data.
Download
Download the plug-in: jquery.itidea_spcascadingdropdown.js Updated version (November 30 2010; order by was hard-coded on ‘Title’ column)
For an extended plugin check out this post.
Example of using the SPServices Search web service
Instead of CAML you can also use a search query by using SQL or keyword syntax to search the SharePoint environment client-side by using the Search web service. The SPServices library supports the Query operation to do this.
Hereby I show you a few examples of how to use it.
First let’s start building the query. The query is based on the Microsoft.Search.Query schema, which can be found here: http://msdn.microsoft.com/en-us/library/ms563775.aspx
A query using keyword syntax:
var queryText = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>" queryText += "<Query>" queryText += "<Context>" queryText += "<QueryText language='en-US' type='STRING'>" queryText += "Estonia"; queryText += "</QueryText>" queryText += "</Context>" queryText += "</Query>" queryText += "</QueryPacket>";
To fire this query by the Query operation of the Search web service via SPServices the following code can be used:
var title, url = "";
$().SPServices({
operation: "Query",
queryXml: queryText,
completefunc: function(xData, Status) {
$(xData.responseXML).find("QueryResult").each(function() {
//let's see what the response looks like
$("#result").text($(this).text());
});
}
});
#results is a div element with the id results to show the response of the query web service. The response format returned by the query web service for the Query operation is described by the Microsoft.Search.Response schema (http://msdn.microsoft.com/en-us/library/ms578335.aspx)
The result of running the above example:
<ResponsePacket xmlns="urn:Microsoft.Search.Response"> <Response> <Range> <StartAt>1</StartAt> <Count>1</Count> <TotalAvailable>1</TotalAvailable> <Results> <Document relevance="428" xmlns="urn:Microsoft.Search.Response.Document"> <Title>Estonia</Title> <Action> <LinkUrl size="0" fileExt="aspx">http://your_url/Lists/Country/DispForm.aspx?ID=24</LinkUrl> </Action> <Description /> <Date>2010-04-08T13:45:47+02:00</Date> </Document> </Results> </Range> <Status>SUCCESS</Status> </Response> </ResponsePacket>
One result is returned here to show a simple and small piece of xml. The results element contains the actual result of the query, the document element contains a single content item in the search results.
A query using SQL syntax:
var queryText = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>"
queryText += "<Query>"
queryText += "<Context>"
queryText += "<QueryText language='en-US' type='MSSQLFT'>"
queryText += "SELECT Title, Rank, Size, Description, Write, Path FROM portal..scope() WHERE CONTAINS ('Estonia') ORDER BY \"Rank\" DESC"
queryText += "</QueryText>"
queryText += "</Context>"
queryText += "</Query>"
queryText += "</QueryPacket>";
And the result:
<ResponsePacket xmlns="urn:Microsoft.Search.Response"> <Response> <Range> <StartAt>1</StartAt> <Count>1</Count> <TotalAvailable>1</TotalAvailable> <Results> <Document xmlns="urn:Microsoft.Search.Response.Document"> <Action> <LinkUrl fileExt="aspx">http://your_url/Lists/Country/DispForm.aspx?ID=24</LinkUrl> </Action> <Properties mlns="urn:Microsoft.Search.Response.Document.Document"> <Property> <Name>TITLE</Name> <Type>String</Type> <Value>Estonia</Value> </Property> <Property> <Name>RANK</Name> <Type>Int64</Type> <Value>1000</Value> </Property> <Property> <Name>SIZE</Name> <Type>Int64</Type> <Value>0</Value> </Property> <Property> <Name>WRITE</Name> <Type>DateTime</Type> <Value>2010-04-08T13:45:47+02:00</Value> </Property> <Property> <Name>PATH</Name> <Type>String</Type> Value>http://your_url/Lists/Country/DispForm.aspx?ID=24</Value> </Property> </Properties> </Document> </Results> </Range> <Status>SUCCESS</Status> </Response> </ResponsePacket>
The results of the keyword and SQL syntax are slightly different. Keep this in mind while traversing the xml and getting the title for instance.
By using the SQL syntax and getting the title in the resulting xml, the Property element is involved:
$().SPServices({
operation: "Query",
queryXml: queryText,
completefunc: function(xData, Status) {
$(xData.responseXML).find("QueryResult").each(function() {
var x = $("<xml>" + $(this).text() + "</xml>");
//let's see what the response looks like
//$("#result").text($(this).text());
//traverse the xml to get the items
x.find("Document").each(function() {
//when using SQL syntax
url = $("Action>LinkUrl", $(this)).text();
$(this).find("Property").each(function() {
if ($("Name", $(this)).text() == "TITLE") {
title = $("Value", $(this)).text();
}
});
//end SQL syntax
$("#result").text("title: " + title + " - LinkUrl: " + url);
});
});
}
});
By using the keyword syntax and getting the title, it’s just a direct child element of the Document element and this code can be used:
$().SPServices({
operation: "Query",
queryXml: queryText,
completefunc: function(xData, Status) {
$(xData.responseXML).find("QueryResult").each(function() {
var x = $("<xml>" + $(this).text() + "</xml>");
//let's see what the response looks like
//$("#result").text($(this).text());
//traverse the xml to get the items
x.find("Document").each(function() {
url = $("Action>LinkUrl", $(this)).text();
//when using keyword syntax
title = $("Title", $(this)).text();
//end keyword syntax
$("#result").text("title: " + title + " - LinkUrl: " + url);
});
});
}
});
The examples described here give just one result for demonstration purposes. This can be seen at the elements count and totalavailable in the result xml. Count is the actual number of items returned, while totalavailable is the total number of results returned by the query web service. When getting real life results please mind the number of items returned by default is 10. Of course the query can be adjusted with a custom count:
var queryText = "<QueryPacket xmlns='urn:Microsoft.Search.Query' Revision='1000'>" queryText += "<Query>" queryText += "<Range><Count>50</Count></Range>"; queryText += "<Context>" queryText += "<QueryText language='en-US' type='STRING'>" queryText += "Estonia"; queryText += "</QueryText>" queryText += "</Context>" queryText += "</Query>" queryText += "</QueryPacket>";
To fully optimize the query to your needs please check out the Microsoft.Search.Query schema for a lot of useful options.
Simple Alert Me option on a SharePoint page with SPServices
On SharePoint lists you can receive email notifications when items change in a list, the ‘Alert me’ function in the Actions menu of a list.

In some occasions there are multiple lists where the data in it is interesting to monitor. For example values of currencies or commodities. When a webpart is used to display values of these lists to users it can be difficult for users to set the email notifications on one or more of these lists, because the user doesn’t (have to) know which lists are involved.To make it all a bit user friendly I’m going to give the user the possibility to easily add an email notification at the same page the webpart is displayed. The user can stay on one page to analyze some data and create a notification for the lists used (the source data lists) in the webpart.
The webpart (not specified further here) to analyze some data uses a subset of all the lists available at the SharePoint site. The lists used are listed in another list, from now on called BaseList. So the BaseList contains all the lists (static name) used as source data.
Content editors can easily add other lists with data to the SharePoint site for analyzing purposes. To use these lists for analyzing purposes in the webpart they can add (or remove) that list to the BaseList. The webpart uses all the lists specified in BaseList and the users can set notifications to these lists.
For displaying all the possible email notifications to set on the page, SPServices comes to the rescue.
Because of the BaseList it is easy to determine which source data lists are involved. To set an email alert always the same default SharePoint page from the _layouts folder is used: SubNew.aspx with the listguid as parameter. If these two are combined, the solution is already there.
First a function is created to retrieve all the lists from the BaseList:
function GetAvailableLists(listName) {
counter = 0;
$().SPServices({
operation: "GetListItems",
listName: listName,
async: false,
CAMLViewFields: '<ViewFields><FieldRef/><FieldRef/></ViewFields>',
completefunc: function(xData, Status) {
$(xData.responseXML).find("[nodeName= z:row]").each(function() {
GetListId($(this).attr('ows_NameOfList'), $(this).attr('ows_DisplayNameOfList'));
counter += 1;
});
}
});
}
The ViewFields contains two columns of the BaseList, one for the static name of the list and one with a self made up display name. The display name will be used as text of the link generated below, the static name for retrieving the guid of the list.
In the code above the function GetListId() is used:
function GetListId(listName, displayName) {
var id = "";
$().SPServices({
operation: "GetList",
listName: listName,
async: false,
completefunc: function(xData, Status) {
id = $(xData.responseXML).find("List").attr("ID");
if (id != null) {
items[counter] = "<a href='/_layouts/SubNew.aspx?List=" + id + "&Source=" + returnToPage + "'>" + displayName + "</a>";
}
}
});
}
This piece of code gets the guid of the list and formats the right url for the notification. The Source parameter here is set to a returnpage. It is convenient for the user to be redirected to page he was coming from otherwise he will be redirected to the AllItems.aspx page from the list where the notification has been set. The formatted url is stored in an array, because I have another function for displaying it nicely at the page, that I’m going to leave to your imagination.
Copy all the code in a Content Editor webpart and you’re done.
The result on the page is dependent on how you format it, but practically no more than a bunch of links, e.g.:
With the link set to:
http://<your site>/_layouts/SubNew.aspx?List={1FAFC5F9-F8D8-4CB6-852E-5AD4DB12CB04}&Source=Default.aspx
















