Me & My SharePoint [FrontEnd]

© Christian Stahl – All about SharePoint branding & customizations

Getting started with REST in SharePoint 2013 – Part VIII — December 31, 2014

Getting started with REST in SharePoint 2013 – Part VIII

Welcome to the last post in this series about REST in SharePoint 2013. In this post it’s time to take a look into Knockout, a JS library that let’s you associate DOM elements with ‘model data’, instead of pushing or appending data by JS you can separate markup from the JS. You can think of Knockout as a general way to make UIs for editing JSON data.

Please read the previous posts in this series before you go on to this post:

Hey what’s Knockout?

Knockout js (shortly called KO) is a popular JS library that helps to create rich & interactive web applications. It works directly with the web application’s underlying data model. Using KO with any web application is simple, clean and straightforward. Its powerful in the context of dynamic UI creation.

In previous example I’ve used jQuery for the Ajax stuff, but what should you use? Well KO doesn’t compete with jQuery or similar low-level DOM APIs. KO provides a complementary, high-level way to link a data model to a UI. KO itself doesn’t depend on jQuery, but you can certainly use jQuery at the same time, and indeed that’s often useful if you want things like animated transitions. I would try to use KO in the first hand if it’s about a more complex structure of data you are going do display in let’s say an app, otherwise if its just about to roll up some stuff from a list in SharePoint I would just do as described in the previous posts in this series. Please note that KO is not the only player here, there are as always a bunch of alternative techniques and framworks out there, but that’s maybe a subject for a another post in the future.

KO in short

  • Declarative bindings
  • Pure JS, minimal and works cross browser
  • A framework that let’s you build interactive CRUD applications in SharePoint with help of REST

Let’s try Knockout!

For this example I have created a custom list called ‘Knockout’ in a subsite called ‘knockout’ below the root site collection. I have added two custom columns (multiple text) to the list called ‘Introduction’ and ‘Content’ and added a few example items. The idea is to display the content from the list in a app or web part on the start page of the root site. How to create an app or web part is out of scoope for this post, if you want to try this you can just add this stuff into a textfile in a document library in the root site and use a content editor and link it to the textfile including the JS and the markup you’ll find down below.

KnockOut

It’s a standard SharePoint list at the top, and below is an example of you can create your own custom look and feel with help of KnockOut

<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.1.0.js"></script>
<script type="text/javascript">
var rootSite = "https://swe.sharepoint.com/";
var subSite = "knockout";
var results = [];

function LoadStuff(index) {
$.ajax({
url: rootSite + subSite + "/_api/web/lists/GetByTitle('KnockoutList')/items",
method: "GET",
headers: {"Accept": "application/json; odata=verbose"},	
  success: function(data){
    results.push.apply(results, data.d.results);
    ko.applyBindings(results);
  },
  error: function (fn, status, error) {
    alert('Error:' + error);
  }	
});
}
$(function(){LoadStuff(0)})
</script>

<div>
<div data-bind="template:{name:'KoList', foreach:results, as:'rows'}"></div>
<script type="text/html" id="KoList">
  <table cellpadding="0" cellspacing="0" class="knockOutTable">
    <tr>
     <td id="td-alfa" data-bind="text:Title"></td>
     <td id="td-beta" data-bind="text:Introduction"></td>
     <td id="td-gamma" data-bind="text:Content"></td>
  </tr>
  </table>
</script>
</div>

Download the complete example with CSS from here

Find more information on the Knockout site

Thats the last post in this series about REST, hope you liked it!

/ Christian

Getting started with REST in SharePoint 2013 – Part VII — November 30, 2014

Getting started with REST in SharePoint 2013 – Part VII

Welcome to post number seven in this series about REST in SharePoint 2013. In the previous posts I wrote about how to interact with the search API.
This time, we will take a look at how to use search queries together with a graphQuery. Graph Query Language (GQL) is designed to query the Office graph via the SharePoint Online Search REST API.

Please read the previous posts in this series before you go on to this post:

Delve

Take advantage of Office Graph in your SharePoint Online apps

Office Graph, can be seen as an underlying intelligence layer that leverages machine learning to surface information and content that is relevant to a user’s project, role and responsibilities within an organization.
Delve is powered by Office Graph (built on top of it), but what’s about to use this Office Graph layer and build an app that you can customize the way you want? You can build an App much like the Delve interface but from you own needs!
The following example will get a list of Items viewed by a actor (in this case the logged in user) the last three months, sort by ranking. You can turn around the information through the GQL in many different ways and combinations and even combine this with SharePoint search. Heres a few example of what you can get:

  • Object related to a specific person or current user
  • trending items
  • Related items
  • Recently viewed items

The following example will get a list of Items viewed by a actor (in this case the current user) the last three months, sort by ranking.

Download all the code here, including CSS. Just paste the code in a txt file and upload this in a document library in your SharePoint Online intranet and add a content editor web part onto a page in SharePoint, a add a reference to the .txt file for testing this out.

var actorId = 'ME' 
var actorGQL = "ACTOR(" + actorId + "\\, action\\:1001)";

  var searchQuery = _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?Querytext='*'&Properties='GraphQuery:" + actorGQL + "'&RowLimit=10&SelectProperties='Path,Title,Rank,ViewsLifeTime,FileType,LastModifiedTime'"
	$.ajax({
		url: searchQuery,
		method: "GET",
		headers: {"Accept": "application/json; odata=verbose"},
		success: onQuerySuccess,
		error: onQueryFail
	});

   	function onQuerySuccess(data) {
  
		var items = [];
			items.push("<ul id='listContainer'>");
	        items.push("<div id='listHeader'>" + 'Delve says..' + "</div>");

		    if (data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results.length == 0) {
		        $("#listResult").append('No results..');
		    }
		    else{
			$(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results).each(function(){

			var path = keyValue(this, 'Path');
			var title = keyValue(this, 'Title');
			var FileType = keyValue(this, 'FileType');	
			

	
			// get the file extension 
				var iconUrl =  "/_layouts/15/images/";
				var filename = path.substring(path.lastIndexOf('.')+1);
				
				if (filename == 'pdf') {
					var iconPath = iconUrl + 'ic' + filename + '.png';
				}
				else if (filename == 'PDF'){
					var iconPath = iconUrl + 'ic' + filename + '.png';
				}
				else{
					var iconPath = iconUrl + 'ic' + filename + '.png';
				}
				
				
				
			// convert date
			var modified = moment(keyValue(this, 'LastModifiedTime')).format("YYYY-MM-DD");
				if (!moment(modified,'YYYY-MM-DD').isValid()) {
					var modified = 'Date is not applicable..';
				} 
				else {
					var modified = moment(keyValue(this, 'LastModifiedTime')).format("YYYY-MM-DD");
				}		

				items.push('<li id="' + 'listContent' + '">' +     		
				    			'<span id="' + 'listContentInner' + '">' + 
					    			'<img src="' +  iconPath + '">' + ' <a href="' + path + '">' +  title + '</a>' +
					        	'</span>' + 
					        	'<span id="' + 'listContentInnerTime' + '">' + 
					        		' - ' + modified +
					        	'</span>' + 
				    		'</li>');
				    		
					});

				items.push("</ul>");
			
			$("#listResult").html(items.join(''));

   			} 
   		}

    function keyValue(row, fldName) {
        var ret = null;
        $.each(row.Cells.results, function () {
            if (this.Key == fldName) {
                ret = this.Value;
            }
        });
        return ret;
    } 

    function onQueryFail(sender, args) {
		$("#listResult").append('Query failed. Error:' + args.get_message());
    }

Read more about GQL and search REST API at MSDN
http://msdn.microsoft.com/en-us/office/office365/howto/query-Office-graph-using-gql-with-search-rest-api

Don’t use this in production yet, it could be some changes in the API. The Office Graph are in the time of writing in preview status.

Stay in tune for more posts!
/ Christian

Getting started with REST in SharePoint 2013 – Part VI — October 30, 2014

Getting started with REST in SharePoint 2013 – Part VI

Welcome to the sixt post in this series about REST in SharePoint 2013. In the previous posts I wrote about how to get external data into your SharePoint page. This time I’m stepping back into how to read information from SharePoint, and this time we will take a look into an example about how to use the SharePoint 2013 Search REST API, one of the most powerful REST access points in SharePoint, and yes, this will work the same no matter if it’s SharePoint Online or SharePoint Onprem.

Please read the previous posts in this series before you go on to this post:

SharePoint 2013 Search REST API

First of all, take a look at the overview at Office Dev center: http://msdn.microsoft.com/en-us/library/office/jj163876(v=office.15).aspx – from this page you can learn all the basic stuff about how to construct queries and parameters. The REST Search API gives a lot of possibilities, you can basically retrive just anything that the serach in SharePoint will index! Take a look at Chris O’Briens blog post as an cool example about how to use REST search API, an example that shows how to get related items to the content in an app.

In the following example I’ll show you how to create a table based on a search Query, just a single Word that’s stored as a variable. Instead of the Word ‘SharePoint’ you could for example store something more dynamic like a metadata onto the current page. Let’s say you add this in a page layout and use it to display other pages that are in some way related to this page.

App

Let’g go!

 

var queryText = 'SharePoint'
var startrow = '&amp;startrow=0'
var rowlimit = '&amp;rowlimit=10'
var searchQuery = _spPageContextInfo.webAbsoluteUrl + &quot;/_api/search/query?querytext='&quot; + queryText + &quot;'&quot; + rowlimit + startrow;

$.ajax({
url: searchQuery,
method: &quot;GET&quot;,
headers: {&quot;Accept&quot;: &quot;application/json; odata=verbose&quot;},
success: onQuerySuccess,
error: onQueryFail
});

function onQuerySuccess(data) {

var items = [];
items.push(&quot;&lt;ul id='listContainer'&gt;&quot;);
items.push(&quot;&lt;div id='listHeader'&gt;&quot; + 'Search results..' + &quot;&lt;/div&gt;&quot;);

if (data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results.length == 0) {
$(&quot;#listResult&quot;).append('No results..');
}
else{
$(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results).each(function(){

var path = keyValue(this, 'Path');
var title = keyValue(this, 'Title');
var created = moment(keyValue(this, 'Write')).format(&quot;YYYY-MM-DD&quot;);
if (!moment(created,'YYYY-MM-DD').isValid()) {
var created = 'Date is not applicable..';
}
else {
var created = moment(keyValue(this, 'Write')).format(&quot;YYYY-MM-DD&quot;);
}

items.push('&lt;li id=&quot;' + 'listContent' + '&quot;&gt;' +
'&lt;span id=&quot;' + 'listContentInner' + '&quot;&gt;' +
'&lt;a href=&quot;' + path + '&quot;&gt;' +  title + '&lt;/a&gt;' +
'&lt;/span&gt;' +
'&lt;span id=&quot;' + 'listContentInnerTime' + '&quot;&gt;' +
' - ' + created +
'&lt;/span&gt;' +
'&lt;/li&gt;');
});

items.push(&quot;&lt;/ul&gt;&quot;);

$(&quot;#listResult&quot;).html(items.join(''));

}
}

function keyValue(row, fldName) {
var ret = null;
$.each(row.Cells.results, function () {
if (this.Key == fldName) {
ret = this.Value;
}
});
return ret;
}

function onQueryFail(sender, args) {
$(&quot;#listResult&quot;).append('Query failed. Error:' + args.get_message());
}

It’s a bit tricky to find the structure of the nodes, but if you using for example Advanced REST client in google chrome you’ll find it. Take a look at this image, hope this will help you to figure out the path for the data results: data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results

Nodes

You can download the file from here, including the CSS and script references

Getting started with REST in SharePoint 2013 – Part V — September 28, 2014

Getting started with REST in SharePoint 2013 – Part V

Welcome to the fifth post in this series about REST in SharePoint 2013. In the previous posts I wrote about how you can connect to a external data source with REST, fetching Flickr photos to SharePoint using JSON and jQuery. In this post I will show you another example of how you can build an APP that get external data into your SharePoint page.

Please read the previous posts in this series before you go on to this post:

Display the current weather from your location in SharePoint

I built this one as a SharePoint hosted APP and it’s published at the SharePoint Store. If you want to try the app in your SharePoint Online public or Intranet or maybe your SharePoint 2013 on prem, go to the SharePoint store and add it. You can find more information about the APP at the Office Store

CurrentWeather

Functions

With help of this script, you can display all the necessary stuff about the current or a forcast of the weather for a specific Place. For example:

  • Temperature
  • Wind speed
  • Wind bearing
  • Humidity
  • Pressure

Download the code

Feel free to modify this the way you want and if you find some cool implementations or Changes in the code please add a comment. Download the Visual Studio Projects here with all stuff included.

Just a few notes about the APP

Basically, I’m using the forecast.IO API to get the weather. You need an API key, and it’s free up to 1000 calls per day and they have a great documentation of the API. In the APP i do also use ‘auto position’ as an option in the APP part settings, I did not used the HTML5 Geolocation because The HTML5 Geolocation specification mandates that before any location data can be retrieved the browser first request permission from the user to share its location.

Only when the user allows does the browser retrieves the users location. Therefore I thought it would be enough to use the client’ IP information, in most cases it’s ok, however if your ISP have routing your autoposition will not work perfect so I added the option to turn of ‘Autoposition’ and instead you can add an exact position with Longitude and Latitude coordinates.

In order to get a name for the location, in case you using autoposition I called Google maps API. This API don’t support HTTPS to take care about this cross domain issue for IE 8 and 9 the solution is to write a little Proxy as a condition.

Note that if you don’t want the autoposition option, you can just use the forecast.IO API, no need for Google Map API or the Free IP services.

        function crossDomainAjax(url, successCallback) {
                // IE8 & 9 only Cross domain JSON GET request
                if ('XDomainRequest' in window && window.XDomainRequest !== null) {
                    var xdr = new XDomainRequest(); // Use Microsoft XDR
                    xdr.open('get', url);
                    xdr.onload = function () {
                        var dom = new ActiveXObject('Microsoft.XMLDOM'),
                            JSON = $.parseJSON(xdr.responseText);
                        dom.async = false;
                        if (JSON == null || typeof (JSON) == 'undefined') {
                            JSON = $.parseJSON(data.firstChild.textContent);
                        }
                        successCallback(JSON);
                    };
                    xdr.onerror = function () {
                        _result = false;
                    };
                    xdr.send();
                }
                    // End IE8 & 9 only Cross domain JSON GET request
                    // Do normal jQuery AJAX for everything else          
                else {
                    $.ajax({
                        url: urlGoogle + lati + ',' + longi + "&sensor=false",
                        cache: false,
                        dataType: 'json',
                        type: 'GET',
                        success: function (data, success) {
                            successCallback(data);
                        }
                    });
                }
                // End Do normal jQuery AJAX for everything else  
        }

If you don’t want to use this as a APP, it’s just JS/HTML/CSS so you can modify this and implement this into a content editor, a page layout or into a web part, maybe as a part in the header for the start page? This script can be implemented in many different ways.

barc Continue reading

Getting started with REST in SharePoint 2013 – Part IV — August 31, 2014

Getting started with REST in SharePoint 2013 – Part IV

Welcome to the fourth post in this series about REST in SharePoint 2013. In the previous posts I wrote about how to interact with SharePoint lists, but now it’s time to look in to how to retrieve information from an external source with help of JSON. There’s a lot of web services with APIs that provides JSON like Facebook, Twitter, Foursquare, bing maps, Netflix. Yahoo weather and many many more. In this post I’ll show you an easy example of how to get information from Flickr, as you may know a popular photo-sharing site that has been around for years and has now 5 billions of photos. And yes, this will work the same no matter if it’s SharePoint Online (public or internal) or SharePoint Onprem.

Please read the previous posts in this series before you go on to this post:

Bring Flickr photos to SharePoint using JSON and jQuery

The following code will give you a quick start to read the latest 3 images from my account at Flickr. You’ll see my id ‘69239123@N08’, please change this with you own ID. To find your ID, go to this page: http://idgettr.com/. You can add the following script into a content editor web part or use SharePoint Designer and it into a page. If you only want to display the latest image, change 4 to 0 in the return false. Don’t forget to reference to latest jquery as well.

Before you start scripting, take a look at the documentation at Flickr API here

<script type="text/javascript">
$.getJSON("https://api.flickr.com/services/feeds/photos_public.gne?id=69239123@N08&format=json&jsoncallback=?", 
function(data){
$.each(data.items, function(i,item){
	$("<img/>").attr("src", item.media.m).appendTo("#images")
		.wrap("<a href='" + item.link + "'></a>");
		if ( i == 4 ) return false;
	});
});
</script>	
<div id="images"></div>	

FlickR

Extended version

Ok, the following script is similar to the one above but have some styling and is more template based, prepared to extend with your own HTML, for example if you would like to create an image slider out of the images. You’ll also have some settings like tags i the code.

<style type="text/css">
.thumbs li {list-style: none; float: left; margin: 5px; padding: 3px; background: #eee; -moz-box-shadow: 0 0 4px #444; -webkit-box-shadow: 0 0 2px #000}
.thumbs li a {}
.thumbs li img { display: block; height: 350px}
.thumbs li a img { border: none}
#flickrRheader{font-size:x-large;display:block;text-align:center}
#flickrRdescription{font-size: small;display:block;text-align:center}
#flicRUserID{display:none}
}
</style>

<div id="flicRUserID">69239123@N08</div>
<div id="flickRcontainer">
	<ul id="custom" class="thumbs"></ul>
</div>

<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.11.1.min.js"></script>

<script type="text/javascript">
(function($) {
	$.fn.jflickrfeed = function(settings, callback) {
		settings = $.extend(true, {
			flickrbase: 'https://api.flickr.com/services/feeds/',
			feedapi: 'photos_public.gne',
			qstrings: {
			    //tags: "SharePoint",
				format: 'json',
				jsoncallback: '?'
			},
			cleanDescription: true,
			useTemplate: true,
			itemTemplate: '',
			itemCallback: function(){}
		}, settings);

		var url = settings.flickrbase + settings.feedapi + '?';
		var first = true;

		for(var key in settings.qstrings){
			if(!first)
				url += '&';
			url += key + '=' + settings.qstrings[key];
			first = false;
		}

		return $(this).each(function(){
			var $flickRcontainer = $(this);
			var flickRcontainer = this;
			$.getJSON(url, function(data){
				$.each(data.items, function(i,item){
					if(i < settings.limit){
						// Add Image Sizes
						// http://www.flickr.com/services/api/misc.urls.html
						item['image_s'] = item.media.m.replace('_m', '_s');
						item['image_t'] = item.media.m.replace('_m', '_t');
						item['image_m'] = item.media.m.replace('_m', '_m');
						item['image'] = item.media.m.replace('_m', '');
						item['image_b'] = item.media.m.replace('_m', '_b');
						delete item.media;
						// Template
						if(settings.useTemplate){
							var template = settings.itemTemplate;
							for(var key in item){
								var rgx = new RegExp('{{' + key + '}}', 'g');
								template = template.replace(rgx, item[key]);
							}
							$flickRcontainer.append(template)
						}
						
						//itemCallback
						settings.itemCallback.call(flickRcontainer, item);
					}
				});
				if($.isFunction(callback)){
					callback.call(flickRcontainer, data);
				}
			});
		});
	}
})(jQuery);

$(document).ready(function(){
var flickRuser = $("#flicRUserID").html();
	$('#custom').jflickrfeed({
		limit: 1,
		qstrings: {
			id: flickRuser		
		},
		itemTemplate: '<li>'+
		'<a href="{{image}}" title="{{title}}">' +
		'<img src="{{image}}" alt="{{title}}" />' +
		'</a>' +
		'<span id="flickrRheader">{{title}}</span>' +
    	'</li>'
	});
});

</script>

If you like to have this packed as an app, you can dynamic handle the parameters like user ID and number of photos into the settings of the APP. I have a VS2013 project you can download and modify here.

VSsettings

In the next post I’ll show you how to create a current weather app with REST and JSON, with help from developer.io weather service. Stay in tune!

Customizing SharePoint 2013 global navigation with CSS and jQuery – Part 2 — December 31, 2013

Customizing SharePoint 2013 global navigation with CSS and jQuery – Part 2

In this second post, I will show how you can create your own markup for the global navigation. This could be a great option when you need to take advantage of HTML 5 tags and additional jQuery plugins that requires simpler markup than SharePoint gives you out of the box.
Alternative to create a complete new control via code, an easy way is to use a standard old good .NET repeater control, instead of the SharePoint:AspMenu control and let the repeater connect to a asp:SiteMapDataSource. To use a repeater control instead of the SharPoint:AspMenu is most likely something suitable for a public SharePoint site.

simple
The repeater gives you a possibility to simplify the markup and just render a minimum of UL and Lis where you can set you own names for IDs or classes.
In order to add a selected class for selected links in the navigation you’ll need to use code behind or java script to do this. The thing here is that JS this will be valid as long as the page is still open, as soon as the user goes to a new page the JS will be reset. To overcome this you can use a cookie or read the URL, parse it, and then add a selected class to the current navigation node or as in this solution just use jQuery with location.pathname to compare the selected link to what’s in the URL.

Ok, if you only need a plain navigation without any dropdown items, replace the old control with this stuff:

<SharePoint:AjaxDelta id="DeltaTopNavigation" BlockElement="true" CssClass="ms-displayInline ms-core-navigation" role="navigation" runat="server">
<SharePoint:DelegateControl runat="server" ControlId="TopNavigationDataSource" Id="topNavigationDelegate"></SharePoint:DelegateControl>
<asp:ContentPlaceHolder id="PlaceHolderTopNavBar" runat="server">
<nav class="SpNav">
<ul id="SpNavUL">
<asp:Repeater ID="GlobalNavMenuItems" runat="server" DataSourceID="topSiteMap">
<ItemTemplate>
<li>
<asp:HyperLink ID="SpNavLinks" runat="server" NavigateUrl='<%# Eval("Url")%>' Text='<%# Eval("Title") %>'></asp:HyperLink>
</li>
</ItemTemplate>
</asp:Repeater>
<asp:SiteMapDataSource ShowStartingNode="False" SiteMapProvider="CombinedNavSiteMapProvider" id="topSiteMap" runat="server" StartingNodeUrl="sid:1002" />
</ul>
</nav>	 
</asp:ContentPlaceHolder>
</SharePoint:AjaxDelta>

And the CSS:

#SpNavUL, #SpNavUL ul {
margin:0;padding:0;list-style:none;
}
#SpNavUL{
margin: 0px auto;
border: 1px solid #222;
background-color: #111;
background-image: linear-gradient(#444, #111);
border-radius: 6px;
box-shadow: 0 1px 1px #777;
}
#SpNavUL:before,
#SpNavUL:after {
content: "";
display: table;
}
#SpNavUL:after {
clear: both;
}
#SpNavUL li {
float: left;
border-right: 1px solid #222;
box-shadow: 1px 0 0 #444;
position: relative;
}
#SpNavUL a {
float: left;
padding: 12px 30px;
color: #999;
text-transform: uppercase;
font: bold 12px Arial, Helvetica;
text-decoration: none;
}
#SpNavUL li:hover > a {
color: #fafafa;
}
.SpNavActive{
color:#04acec!important;
}

And the jQuery:

$(document).ready(function(){
$(function() {
$('#SpNavUL a[href^="/' + location.pathname.split("/")[1] + '"]').addClass('SpNavActive');
});    
});

If you need to display dropdown items in the repeater control you have to use a bit server side script in the repeater to get the child nodes. You need to enable code blocks via web config file to get this working and if you need to do this for a production environment, don’t forget to handle the changes for the web.config in a WSP. Note that you will not be able to modify the web.config file for a SharePoint Online site.

double

Download all the stuff here including how to do with a repeater that includes a drop down menu.

Stay in tune for the next post in this series about the global navigation in SharePoint 2013

Please drop a comment if you have any question or just ideas for the next posts!

/ Christian

Extending the DVWP with jQuery search, sort and pagination — May 2, 2013

Extending the DVWP with jQuery search, sort and pagination

In this post I’ll show you the quick steps how to extend the Data View Web Part (DVWP) with help of a jQuery plugin named DataTables.
The DVWP can connect to a SharePoint list and you can use it for example to display content in tabular view from a SharePoint list. Let’s say you want to create a news rollup that reads from the pages library with some special styling and condition, the DVWP is one of the options for this task. There’s a lot you can do with only XSLT, HTML and CSS but sometimes you need to add jQuery for some specific functions.

In this example I’ll show how to extend a basic DVWP news rollup with search-as-you type, sort function and pagination.

In order for jQuery DataTables to be able to function correctly, the HTML for the dataview table must be laid out in a well formed manner with the ‘thead’ and ‘tbody’ sections declared. Remember that use filters and limit number of object by server side in the DVWP especially if you’re dealing with large data sets, the datatable.js is there to serve a user friendly presentation layer.

The steps

  1. Create the DVWP
  2. Add the HTML that’s needed for the datatable plugin
  3. Add jQuery and CSS in separate files and reference this from your custom master page

jQuery
What you need for the plugin:

$(document).ready(function() {
$('#example').dataTable({
"oLanguage": {
"sInfo": "_START_ - _END_ of _TOTAL_ pages",
"sSearch": "Search:",
"sZeroRecords": "",
"sInfoEmpty": "No results..",
"sInfoFiltered": "",
"oPaginate": {
"sFirst": "<<",
"sLast": ">>",
"sNext": ">",
"sPrevious": "<"
}},
"sPaginationType": "full_numbers",
"iDisplayLength": 4,
"bLengthChange": false
});
var oTable = $('#example').dataTable();
oTable.fnSort([[0,'desc']]);
});

References
The references in your master page should looks like this:

<SharePoint:CssRegistration name="/Style Library/DVWP/Styles/MSDN.css" After="corev4.css" runat="server"/>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/jquery-1.9.0.min.js"></script>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/DataTables-1.9.4/media/js/jquery.dataTables.js"></script>
<script type="text/javascript" src="/Style Library/DVWP/Scripts/addOns.js"></script>

Download
Download the aspx including the DVWP, jQuery and CSS taken from this example. The news pages in this example have the path /press/nyheter/ and the page library is named Sidor. Change the name and URL in the DVWP as you prefer as well as the select query. Do also download the DataTable plugin here.

I like the DVWP, it’s easy to work with and there’s a lot of ways to extend it, however this plugin works great with REST, librarys such as SPServices or in custom code. You can even use it in a custom format (template) for Content Query Web Part as well, maybe I’ll write a post about how to do this later on.

This technique works great in SharePoint 2007, 2010 and 2013, in foundation or server, online or OnPrem.

/ Christian

User information in SharePoint 2010 with help of the SPCWC control — December 21, 2012

User information in SharePoint 2010 with help of the SPCWC control

In this post I’ll show you how you can retrieve something from the current user’s profile and then do something based of its value with a little help of jQuery and CSS. In this example I have used a typical AD field (office) that are imported into SharePoint’s profile information. You can use any field that exist as a property in the profile information like Country, manager, profile picture or even use a custom property in SharePoint.

workingInLondon

Let’s say we want display something at the Intranet for those users that works in London and display some other stuff for the one’s that works in Paris. This can be used in a many different scenarios, hide/show web parts, controls or fields in the page layout or maybe set a cookie based on the value or do something related to the branding, redirect users to different pages and much more. Hopes this example gives you some ideas for a cool implementation of your own!

Let’s try this out

Use SharePoint Designer 2010 and open the custom master page, your custom CSS file and your JS file.

Masterpage
First you need to declare SPCWC tag prefix in your custom master page. It’s just to include the following line for example just below the last existing tag prefix

<%@ Register Tagprefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls" Assembly="Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Then you need to load and make the profile properties available through the pages that are connected to your custom master page. This attribute is needed for some of the properties, but in this example you don’t need it. You can for example paste the following tag just below the existing WebPartPages:SPWebPartManager tag.

<SPSWC:ProfilePropertyLoader id="m_objLoader" LoadFullProfileOfCurrentUser="true" runat="server"/>

Finally it’s time to include the HTML somewhere in your custom master page including the profile property value.

<div class="Office">
<SPSWC:ProfilePropertyValue PropertyName="Office" ApplyFormatting="false" runat="server"/>
</div>
<div class="OfficeOutput IfOffice s4-notdlg"></div>

jQuery
Add the following script in an external JS file referenced from your master page.

$(document).ready(function(){	
var office = $('.Office > span').text();
if(office == 'Stockholm') {
$(".OfficeOutput").show().html('Working in Stockholm!');
}
else if(office == 'London') {
$(".OfficeOutput").show().html('Working in London!');
}
else {
// do something else
} 
});

CSS
Add the following CSS in an external CSS file referenced from your custom master page.

.Office > span{display:none}
.OfficeOutput{display:none}
.IfOffice{
padding:4px; background-color:#304F6D;
color:#fff; font-family:"Segoe UI"; font-size:8pt
}

That’s all, hopes this gives some ideas or inspiration. Please comment if you have questions or if just want to discuss this.
/ Christian

10 jQuery snippets for SharePoint 2010 — September 27, 2012

10 jQuery snippets for SharePoint 2010

I like to collect pieces of code snippets, sooner or later I’ll found them useful and they could save time and other efforts. My intention with this post is just to share a couple of my snippets and I hope that you’ll found something useful here.

Maybe you see something that can be written more efficient or if you have some cool snippets of your own you like to share, please drop this in a comment. These snippets are all plugin independent and should be easy to follow and modify and they can be used in SharePoint Designer 2010, Visual Studio or be included in a text file in SharePoint linked from a content editor. Don’t forget to create a reference to latest jQuery in the master page or into the text file.

1. Text manipulation
In this example I replace ‘All site content’ with help of the each function.

/* --- Doc ready ---*/
$(document).ready(function() {
$('.ms-splinkbutton-text').each(function(i){ $(this).text($(this).text().replace('All Site Content','More stuff here..'))
})
})

2. Check the URL
If the URL contains ‘news’, let’s do something conditionally with JS.

if(document.URL.indexOf("news") != -1){ 
alert("News site"); 
} else{ 
alert("Not the news site"); 
}

Another way is to getting a URL parameter and maybe their values and to something based on a condition with help of jQuery. Let’s give the background different colors depending if the view are sorted by Desc or Asc

var url = window.location.href; 
/* --- Doc ready ---*/
$(document).ready(function() {
if (url.search("&SortDir=Asc") > 0) { 
$(".ms-viewheadertr").css('background-color','lime');
};
else if (url.search("&SortDir=Desc") > 0) { 
$(".ms-viewheadertr").css('background-color','yellow');
};
/* --- End doc ready ---*/
});

3. Timestamp
If each page you create needs to have a unique name, you can set a time stamp when it creates. In this example I’ve used year to milliseconds and a random number at the end. This may be useful for a news site with many pages.

// create a timestamp with random
var now = new Date();
var year = now.getFullYear();
var month = now.getMonth();
var day = now.getDay();
var hours = now.getHours();
var minutes = now.getMinutes();
var seconds = now.getSeconds();
var milliseconds = now.getMilliseconds();
var rand = Math.floor((Math.random()*1000000)+1);
var pageID = ('ID-')
var CustomInput = pageID + '' + year + '' + month + '' + day + '' + hours + '' + minutes + '' + seconds + '' + milliseconds + '' + rand;

/* --- Doc ready ---*/
$(document).ready(function() {
$("input[name='ctl00$PlaceHolderMain$nameInput']").val(CustomInput);
/* --- End doc ready ---*/
});

If you only want this function for let’s say a news site, you can use an If statement and identify the URL. But don’t forget that URL can be changed.

/* --- Doc ready ---*/
$(document).ready(function() {
if(document.URL.indexOf("news") != -1) { 
// Unique page title
$("input[name='ctl00$PlaceHolderMain$nameInput']").val(CustomInput);
} else{}
/* --- End doc ready ---*/
});

4. Change the attribute
Let’s say you want to change for example the title tag for the ‘I Like It’ button, you can do like this to set a custom attribute.

/* --- Doc ready ---*/
$(document).ready(function() {
$(".ms-socialNotif-Container > a").attr({
title: "Click the button if you like this page",
}); 
/* --- End doc ready ---*/
});

5. Change CSS
Let’s change the header text to red if the name is Link.

/* --- Doc ready ---*/
$(document).ready(function() {
$(".ms-WPTitle span:contains('Links')").css("color", "red"); 
});

Another way is to use a condition and a variable for this, if the web part header is equal to Shared Documents set the color to green, if the text is eq to Link set a border around the web part and set the text to red color. Normally I set the CSS into a CSS file, and you can use addClass to set the class as an option to set the CSS inline the script if you like.

/* --- Doc ready ---*/
$(document).ready(function() {
var WPtitle = $('.ms-WPTitle span'); 
for (var i = 0; i <= WPtitle.length; i++) { 
if ($(WPtitle[i]).text() == 'Links') { 
$(WPtitle[i]).css({'color': 'red'});
$(WPtitle[i]).parents().eq(10).css({'border': '1px black solid!important'});
}
else if ($(WPtitle[i]).text() == 'Shared Documents') { 
$(WPtitle[i]).css({'color': 'green'}); 
}} 
/* --- End doc ready ---*/
});

6. Add expand / collapse web parts
The following code will get expand/collapse for all standard web parts.

/* --- Doc ready ---*/
$(document).ready(function() {
$(function($) { 
$('.s4-wpTopTable').find('tr:first h3').append('<a class=\'min\' style=\'float:left; margin-right:5px\'><img src=\'/_layouts/images/collapse.gif\'/></a>'); 
var Collapse = "/_layouts/images/collapse.gif"; 
var Expand = "/_layouts/images/expand.gif"; 
$('.min').click(function(){      
var img = $(this).children(); 
$(this).closest('.s4-wpTopTable').find('tr:first').next().toggle().is(":visible") ? img.attr('src',Collapse) : img.attr('src',Expand ); 
}); 
});
});

7. Modify form field
jQuery can be used in many ways for standard list forms in SharePoint and this fist example shows how to set read only, a color and a specific width for the title field in edit mode.

/* --- Doc ready ---*/
$(document).ready(function() {
$("input[title='Title']").attr("readonly","true").css('background-color','#ccc').width(70);
/* --- End doc ready ---*/
});

Next example shows how to set a field limit and add a counter that shows number of characters left

// Show Nr of Characters left in a common list field
(function($){  
$.fn.fieldLimit = function(options) {  
return this.each(function() {  
var characters = 30;
$(this).keyup(function(){
if($(this).val().length > characters){
$(this).val($(this).val().substr(0, characters));
}	
var remaining = characters - $(this).val().length;
$(options.result).html(remaining + " characters left");			
});
});  
};  
})(jQuery);

/* --- Doc ready ---*/
$(document).ready(function() {
$('.ms-formtable').prepend("<div class='CharactersLeft'></div>");
$('input[title$=Title]').fieldLimit({
result:".CharactersLeft",
});
/* --- End doc ready ---*/
});

8. Check site template
If you need to do something based on which site template a site has been created from, you can identify this with help of the site template ID. I have only covered a few templates below.

/* --- Doc ready ---*/
$(document).ready(function(){
CurrentTemplate = g_wsaSiteTemplateId;
TeamSite = 'STS#0'
EnterpriseWiki = 'ENTERWIKI#0';
PublishingSite = 'CMSPUBLISHING#0';
if (CurrentTemplate == TeamSite){
alert('Im a Team site');}
else if (CurrentTemplate == EnterpriseWiki){
alert('Im a Enterprise Wiki');}
else if (CurrentTemplate == PublishingSite){
alert('Im a Publishing Site');}
else {
alert('Sitetemplate not defined yet..');}
/* --- End doc ready ---*/
});

9. Welcome message
This example shows how to work with variables. You’ll also find some plain old good JS date stuff that can be useful when you need to check times.

/* --- Doc ready ---*/
$(document).ready(function(){
var WelcomeMenuContent = $('.ms-welcomeMenu > a.ms-menu-a > span');
var UserName = WelcomeMenuContent.text();
var FirstName = UserName.split(" ")[0];
var Display;
var Digital = new Date()
var Hours = Digital.getHours()
Morning = 'Good morning' + " " + FirstName;
Lunch = 'Lunch time' + " " + FirstName;
Evening = 'Good evening' + " " + FirstName;
Night = 'Time to go home' + " " + FirstName;
TimeElse = 'Welcome' + " " + FirstName;
if (Hours >= 5 && Hours <= 11) 
WelcomeMenuContent.text(Morning);
else if (Hours == 12) 
WelcomeMenuContent.text(Lunch);
else if (Hours >= 13 && Hours <= 17) 
WelcomeMenuContent.text(Evening);
else if (Hours >= 18 && Hours <= 23) 
WelcomeMenuContent.text(Night);
else
WelcomeMenuContent.text(TimeElse);	
/* --- End doc ready ---*/
}); 

10. Append today’s date
While we’re talking about get date with JS, let’s see how you can use this to display current date somewhere at the page like this.

var d = new Date();
var month = d.getMonth();
var date = d.getDate();
var year = d.getFullYear();
/* --- Doc ready ---*/
$(document).ready(function() {
$('.s4-pagedescription').append("<div style='float:right'>" + month + "/" + date + "/" + year + "</div>"); 
});

Stay in tune!

/ Christian Ståhl

Use jQuery & cookies in SharePoint 2010 — August 31, 2012

Use jQuery & cookies in SharePoint 2010

In this article I’ll show you how to use Cookies in jQuery for your SharePoint Designer 2010 no code applications.

With help of a cookie you can write information to the user’s computer and this can be useful if you need to store stuff like visitors preferences, for example if the user has checked a check box or not at some page. Cookies can be very useful in many ways, but in some situations you need to write such information to the server like a database instead, cookies should not be used for critical information or any kind of sensitive information especially at a public faced site. However a cookie can be a great choice for many tasks in your front end development.

Some examples of the usefulness of a cookie

  • Change color schemes
  • Hide / Show elements at the page
  • Resizing fonts, scale up or down
  • Form, auto save inputs in fields, selected check box or radio button
  • Accordion / tabs, remember last state of collapsed/opened
  • Navigation, remember the last location
  • Welcome message, for example if you want to show a popup only once at specific date at the start page.

jQuery doesn’t support cookies natively but you can use a super small plugin called ‘jquery.cookie.js’  that contains all kind of basic functions and options you’ll need for your scripting. This plugin is also included in jQuery UI development-bundle.

Jquery.cookies gives a couple of options like:

  • Expires
    The lifetime for the cookie. You can use a number of days or a specific date. If omitted, the cookie is a session cookie
  • Path
    Where the cookie is valid. If you want it to be valid at every site in the site collection, just use a slash /, if you want it to be valid at let’s say only the news site set e.g /news/

Get going

Let’s start with a simple example just to get going, the following example will set the top row to a red or green background color depending of which link you click. Let’s say you clicked at green one and then reload the page, the green background color will remain. If you restart the browser, the top row will be white again. This script do not have any expired date, therefore, the cookie will expire automatically when you close the browser.

Let’s try this out:

Download jquery.cookies.js and create a reference to this file in your custom master, don’t forget to include an reference to latest jQuery API as well.

HTML
Put this somewhere in your custom master page

<!-- Session -->
<div class="Wrapper">
	<div class="Red">Red</div>
	<div class="Pipe">|</div>
	<div class="Green">Green</div>
</div>

jQuery
Put this in an external JS file referenced from your master page

$(document).ready(function() {

// Click red
$('.Red').click(function() {
$('.Wrapper').css("background-color","red");
$.cookie('Wrapper', 'red');
});

// Click green
$('.Green').click(function() {
$('.Wrapper').css("background-color","green");
$.cookie('Wrapper', 'green');
});

// Cookies
var Wrapper = $.cookie('Wrapper');
if (Wrapper == 'red') {
$('.Wrapper').css("background-color","red");
};
if (Wrapper == 'green') {
$('.Wrapper').css("background-color","green");
};

});

CSS
Put this in an external CSS file referenced from your master page

.Wrapper{
width:100%;height:16px;
background-color:#fff;color:#000;
}
.Red{
cursor:pointer;padding-left:5px;
float:left;
}
.Green{
cursor:pointer;float:left;
}
.Pipe{
float:left;padding:0px 5px 0px 5px
}

Maybe you want to set the cookie to a life span of 2 days, take a look at the jQuery script and the part where you set the name and the property:

$.cookie('Wrapper-', 'Green-');

Just add a path and a value; a number or a specific date, this will create the cookie

$.cookie('Wrapper-', 'Green-', {path: '/', expires: 2});

Take a look at the cookie in your disk, if you are using IE, you will find it through this path

C:\Users\...\AppData\Local\Microsoft\Windows\Temporary Internet Files

The file will contain the name of the cookie, the property and the path like this

Wrapper-Green-ecmtest67/108815182001923024639810215318430245996*

How to use cookies in a Data View web part

If we take this a step further and try to use cookies in a typical news roller DVWP. In this example, If you click at the icon next to the header for a news item that section will disappear, a kind of ‘I Have Read This’ function. At the bottom of the web part there’s a reset link that will delete the cookie.

The HTML part of the DVWP, it’s important to use @ID for the id, classes and href as the script shows

<div class="NewsRow {@ID}">
<div class="NewsContentContainer">			
<div class="NewsTitle">
<div class="NewsHider"><a href="{@ID}" id="{@ID}"><img alt="" src="/Style%20Library/Custom/Images/IhaveReadThis.png" width="22" height="22" /></a></div>
<div class="NewsTitleInner"><a href="/news/Pages/{@LinkFilename}"><xsl:value-of select="@Title" /></a></div>
<div class="GeneralClearBoth"></div>
</div>		
</div>
<div class="NewsAuthorCreatedWrap">
<span class="NewsAuthor">By <xsl:value-of select="@PublishingContact.title" />,</span> <span class="NewsCreated"><xsl:value-of select="@ArticleStartDate" /></span>
</div>
<div class="NewsContent">
<xsl:call-template name="FirstNWords">
<xsl:with-param name="TextData" select="$DescText"/>
<xsl:with-param name="WordCount" select="15"/>
<xsl:with-param name="MoreText" select="'...'"/>
</xsl:call-template>
</div>
</div>

The jQuery goes here

jQuery(function ($) {
var removedLinks = 'removedLinks'
,Options = {
expires: 7,
path: '/news'
},
c=$.cookie(removedLinks)||'#DebuggingCode'
$(c).remove();
$(".NewsHider a").click(function (e) {
e.preventDefault();
var LinkClass = '.'+ $(this).attr('id'),
removeLinksClasses=c.split(',')
$(LinkClass).remove()
removeLinksClasses.push(LinkClass)
c=removeLinksClasses.join(',') 
$.cookie(removedLinks, c, Options)
});
$('#NewsResetCookie').click(function(){
$.cookie(removedLinks,'',{expires:-1,path:'/news'})
})
});

If you prefer to use this in the ItemStyle.xsl and use it as a format for the Content Query web part, it works fine as well. If you want to take a look at a video of this, take a look here: http://youtu.be/kKEE_yktk8c

Hopes this give some ideas or inspiration for your cookie scripting adventures!
/ Christian