Skip to content

Add a breadcrumb in SharePoint 2013

Hi! Here come’s just a short blog about how to implement a breadcrumb in SharePoint 2013 onprem or online, something that can be just as useful like the other navigation elements like the global navigation.

The user needs to know his location in the website’s hierarchical structure in order to possibly browse back to a higher level in the site hierarchy, useful when the structure of the SharePoint site follows a strict hierarchical structure of similar formatted content e.g not a search driven structure by manage metadata. Note that a breadcrumb should not be used on the startpage of your site collection, you can hide it on the welcome page with a display none in the page layout or similar. That’s cool, but how about to implement this breadcrumb then, well it’s just to add a ASP:SiteMapPath control onto your custom masterpage!

breadcrumb

Let’s try this!

Add this somewhere in the markup of your Custom master page

<div class="customBreadCrumb ms-dialogHidden">
   <asp:SiteMapPath SiteMapProvider="CurrentNavigation" ID="ContentMap" SkipLinkText="" RenderCurrentNodeAsLink="false" NodeStyle-CssClass="ms-sitemapdirectional" runat="server"/>
</div>

And here’s the CSS, note that I’ve created the Arrows with CSS3 with a little help of on of my favorites in CSS3; transform (rotate, scale and skew)

.customBreadCrumb{
padding-left:10px;
padding-right:10px;
padding-bottom:0px;
}
.customBreadCrumb > span{
background-color:transparent;
border-bottom:0px #ccc solid
}
.customBreadCrumb a:link, .customBreadCrumb a:visited {
color:#555;
font-family:Arial;
font-size: 12px
}   
.customBreadCrumb .ms-sitemapdirectional{
font-family:Arial;
font-size: 12px;
color:#999
}
/* hide separators */
#ctl00_ContentMap span:nth-of-type(2), #ctl00_ContentMap span:nth-of-type(4), #ctl00_ContentMap span:nth-of-type(6), #ctl00_ContentMap span:nth-of-type(8) {
display:none
}
.customBreadCrumb {
position:relative;
padding:10px 0px 0px 8px
}
#ctl00_ContentMap  {
display: inline-block;
border: 1px solid #E8DABF;
border-radius: 5px;
overflow: hidden;
width: 100%;
}
#ctl00_ContentMap span, .customBreadCrumb a:link, .customBreadCrumb a:visited{
font-family:Arial;
}
/* change height? change both line height and height and width for the ID #ctl00_ContentMap span:after*/
#ctl00_ContentMap span, #ctl00_ContentMap span:after{
line-height: 30px;
height: 30px
}
#ctl00_ContentMap span:after {
width: 35px;
content: '';
z-index: 1;
border-right: 1px solid #E8DABF;
border-top: 1px solid #E8DABF;
position: absolute;
right: 0;
top: 0;
box-sizing: border-box;
-webkit-transform: translateX(20px) rotate(45deg) scale(0.6) skew(15deg,15deg);
transform: translateX(20px) rotate(45deg) scale(0.6) skew(15deg,15deg);
-ms-transform: translateX(20px) rotate(45deg) scale(0.6) skew(15deg,15deg); /* IE9 */
}
#ctl00_ContentMap span {
list-style: none;
float: left;
display: block;
position: relative;
padding: 0px 14px;
text-align: center;
}
#ctl00_ContentMap span a {
padding:0px 6px 0px 12px;
color: #000;
text-decoration: none;
}
#ctl00_ContentMap span:last-child {
padding-left: 20px;
}
.ms-sitemapdirectional a{
font-family:Arial;
font-size:1.0em;
}
#ctl00_ContentMap span:first-child {
padding-left: 0px;
}
#ctl00_ContentMap span,
#ctl00_ContentMap span:after{
background-color:#fff;
}
#ctl00_ContentMap{
border:1px;border-radius: 2px;
}
#ctl00_ContentMap > span{
margin-bottom:1px;
}
#ctl00_ContentMap span a:hover{
color: green;text-decoration: underline;
}
#ctl00_ContentMap span:first-child{}
#ctl00_ContentMap span:after {
width: 35px;
-webkit-transform: translateX(20px) rotate(45deg) scale(0.5) skew(15deg,15deg);
transform: translateX(20px) rotate(45deg) scale(0.5) skew(15deg,15deg);
-ms-transform: translateX(20px) rotate(45deg) scale(0.5) skew(15deg,15deg);
}
.ms-sitemapdirectional a{}
.customBreadCrumb a:link, .customBreadCrumb a:visited{
color:#555;
}   
.customBreadCrumb .ms-sitemapdirectional{}
#ctl00_ContentMap span:last-child{
overflow:hidden
}

/ Christian

What’s up 2015

Ok readers, so it was time to sum up the past year and take a look into what to expect in this new year. This year will be a kind of change in my career, from February 1. The big news is that I will start up my own company, ‘eVolution Interactive Sthlm’ after working as a SharePoint consultant at two different consulting companies since 2006, Humandata and CGI. Of course it is just incredibly exciting and I will then hopefully be able to take me a little more time to work on community around SharePoint, anyway, I will not slowing down any way when it comes to writing blog posts, other articles and speaking at conferences, etc.

At the moment, I have not yet fixed any public website with a description of what services I can help with, contact information, etc., for those who are interested in hiring me, but as soon as I have it in place, I will publish a link on this blog!

Plans for the future

Well the details will change constantly and nobody knows the future, but I will do my best and work as hard as I can to get my business to go around. Right now I’m looking for an office space, need to find an inspiring place somewhere in central Stockholm, I have a few options I’m considering at the moment so we’ll see what happens here.

I will as before continue doing what I love most about, consulting and educate. I will publish some apps, continue to write blog posts and speak at SharePoint conferences both in Sweden and internationally.

2014

A fun year at full speed, two major challenging projects on SharePoint 2013 in which I participated as a front-end developer and web designer. Besides all this, I have also taken the certification ‘70-480 Programming in HTML5 with JavaScript and CSS3’ this summer, which actually was quite a challenging one I must say. Yes that’s right, I also work as a teacher at LabCenter in Stockholm and at EC. I do educate in the area of SharePoint branding, apps and client code scripting etc. The education part of my job is exciting and will definitely have to continue this year as well. I’m also members of the management team for EC.

Besides all this, I have traveled a lot and talked at a few different SharePoint Conferences around like Miami and Las Vegas SharePoint Conference 2014, Dubai SP Saturday, Slovenia SharePoint Days 2014, Stockholm, SEF2014 and the yearly MVP Summit in Seattle. Every one of this conferences and experience in itself really deserves its own blog post. I have met and had exchanges of so many people, meet old friends and got new ones, people from different cultures and from different backgrounds, that part is perhaps what has given most in itself.

Some pictures from a few of the events the past year
coll1

coll2

coll3

coll4

coll5

coll6

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

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

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

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 Read more…

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!

Getting started with REST in SharePoint 2013 – Part III

Welcome to the third post in this series about REST in SharePoint 2013, this post is a follow up to the previous post about how to create a web part with help of REST web service that will display something from a SharePoint list. In this post I’ll show you how to use the same approach but integrate a typical jQuery plugin, a image slider to the REST script. This will work the same no matter if it’s SharePoint Online or Onprem.

gondolas

The goal in this post is to create a jquery image slide using content editor web-part or in a single page and of course, if you prefer you can pack this a web part with Visual Studio instead. There’s a lot of jQuery sliders out there, in this example I used Basic jQuery Slider I like this plugin because it’s simple and lightweight and are easy to style in many different ways.
There are two core files required to get up and running with Basic jQuery Slider, the JavaScript plugin and the jQuery library plus a portion of css for the styling. Take a look at the refererences and the css in a single page you can download from here .

One thing to note about the REST script is that the ajax call happens asynchronously, so it’s important that you initialize the slider inside the ajax success callback. Do also note that I’ve created a couple of variables for the image path and build the URL with jQuery’s Pop, Split and Shift methods. That’s because I wanted to read from the automatic thumbnail folder (_w) instead of the the folder for the full image.

I hope this post gives you some inspiration to get started to create web parts displaying something from SharePoint lists, and there’s a plenty of useful jQuery plugins for different kind of task to play around with out there!

Please read the first two posts before you go on to this post:

Create a image slider with REST

if (_spPageContextInfo.webServerRelativeUrl === '/') {
	var webUrl = '';
}    
else{
	var webUrl = _spPageContextInfo.webServerRelativeUrl;
}

// start ajax
$.ajax({
	url: webUrl + "/_api/Lists/GetByTitle('PublicImages')/items?$select=EncodedAbsUrl,Title",
	type: "GET",
	headers: {"Accept": "application/json;odata=verbose"},
	cache:false,
	// start success                  
	success: function(data){
	
		var items = [];
		 	items.push("<div id='banner-slide'>");
			items.push("<ul class='bjqs'>");
	        
		// if content exist
		var dataResult = data.d.results;
		    if (dataResult.length == 0) {
		        items.push("<div id='listNoRecords'>" + 'No images were found..' + "</div>");
		    }    
		    else{
				// start data.d.results
				$(data.d.results).each(function(){
				var imgURL = this.EncodedAbsUrl;
				var mainurl = imgURL.substring(0, imgURL.lastIndexOf("/") + 1);			
				var fileextension = imgURL.substring(imgURL.lastIndexOf(".") + 1, imgURL.length);
				var imgnameVar = imgURL, imgname;
					imgname = imgnameVar.split('/').pop().split('.').shift(); 
				var thumb = '_w/';
				var extension = '.JPG';

				items.push('<li>' + 
				        	' <img src="' + mainurl + thumb + imgname + '_' + fileextension + extension + '"/' + 'title='  +'"' + this.Title +'"' + ' />' +
				    	   '</li>');
	
				});
				// end data.d.results		
	    	}    
	    	// end if content exist
				
				items.push("</ul>");
				items.push("</div>");

				// Add HTML and it's content to the listResult div
				$("#listResult").html(items.join(''));
				
				// Start Image Slider
				$('#banner-slide').bjqs({
					animtype      : 'slide', //fade
		            width         : 340,
					animduration : 450, // how fast the animation are
					animspeed : 4000, // the delay between each slide
					showcontrols : false, // show next and prev controls
					showmarkers : true, // Show individual slide markers
					centermarkers : true, // Center markers horizontally
					keyboardnav : true, // enable keyboard navigation
					hoverpause : true, // pause the slider on hover
					usecaptions : true, // show captions for images using the image title tag
					randomstart : true // start slider at random slide
				});

	}
	// end success  
	
});
// end ajax

I will be back soon so stay tuned for the next post!

Getting started with REST in SharePoint 2013 – Part II

Welcome to the second post in this series about REST in SharePoint 2013. Please read the first post before you go on to this post Get started with REST in SharePoint 2013 – Part I

In this post I will show you how create a web part with help of REST web service that will display a list of all sub sites immediately below the current site, similar to what you can find under ‘Site Contents’. We’ll also need to have this permission trimmed.

subsites

Before you start scripting and build the HTML and CSS for this, you’ll first need to construct the RESTful HTTP / oData request in order to retrieve the entities, in this case all sub sites of some site. Assume you have a number of sub sites under a site named Projects. Change the domain name and try the following basic query in your browser.

https://contoso.sharepoint.com/projects/_api/web/webs/?$select=title

Once you get the results back in your browser in pure XML and you have construct the query properly with the filters you want, it’s time to create the script that will return the result in JSON format and will create the markup with the styling.

Let’s start

You can use notepad, SharePoint Designer or Visual Studio or the tool of your choice. For example you can just add the following script, the CSS and the jQuery references in one single text file to a folder in your SharePoint environment and use a content editor web part to link to the text file.

Here’s the script that will create a list of all sub sites. You can download the script with the CSS and jQuery references from here.

<div id="listResult"></div>
<script type="text/javascript">
// If the web part will live in the root web or a subsite
if (_spPageContextInfo.webServerRelativeUrl === '/') {
var webUrl = '';
}    
else{
var webUrl = _spPageContextInfo.webServerRelativeUrl;
}
// start ajax
$.ajax({
url: webUrl + "/_api/web/webs/?$select=title,ServerRelativeUrl,Created,effectivebasepermissions&$filter=(effectivebasepermissions/high%20gt%2032)&$orderby=Created desc",

type: "GET",
headers: {"Accept": "application/json;odata=verbose"},
cache:false,
// start success                  
success: function(data){
//console.log(data);
var items = [];
items.push("<ul id='listContainer'>");
items.push("<div id='listHeader'>" + 'Subsites ' + "</div>");     
// if content exist
var dataResult = data.d.results;
if (dataResult.length == 0) {
items.push("<div id='listNoRecords'>" + 'No subsites were found' + "</div>");
}    
else{
// start data.d.results
$(data.d.results).each(function(){
var CreatedDate = moment(this.Created).startOf('hour').fromNow()
items.push('<li id="' + 'listContent' + '">' + 
'<span id="' + 'listContentInner' + '">' + 
'<img src="/_layouts/15/images/SharePointFoundation16.png" />' + ' <a href="' + this.ServerRelativeUrl + '">' +  this.Title + '</a>' +  
'</span>' + 
'<span id="' + 'listContentInnerTime' + '">' + 
' - ' + CreatedDate + 
'</span>' + 
'</li>');			    		
});
// end data.d.results		
}    
// end if content exist
items.push("</ul>");
$("#listResult").html(items.join(''));
}
// end success  
});
// end ajax
</script>
<script type="text/javascript" src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.6.0/moment.min.js"></script>

A couple of things

Filter and sorting

You can filter the oData query by conditions or sort the result, for example:

  • $filter=ID eq 1 (only the object with ID eq to 1)
  • $top=5 (limit to 5 items)
  • $orderby=Created desc (sort by descending order)

There’s a lot of oData operators that are supported in SharePoint’s REST service like numeric comparsions, string comparsions and Date/TIme functions. Read more about this at MSDN.

Add the results to the DOM

All the data in the array are stored in a variable called Items. The trick I used in the script for appending the data to the DOM is to use jQuery’s Push() method, in this way you’ll have good control of the HTML you create.

_spPageContextInfo

In this example I added a condition that will add a slash to the beginning of the URI if it’s not a top site, otherwise I don’t want this trailing slash there. The _spPageContextInfo will simply get the site paths after the domain name.

if (_spPageContextInfo.webServerRelativeUrl === '/') {
var webUrl = '';
}    
else{
var webUrl = _spPageContextInfo.webServerRelativeUrl;
}
url: webUrl + "/_api/web/webs/?$select=title,ServerRelativeUrl,Created,effectivebasepermissions&$filter=(effectivebasepermissions/high%20gt%2032)&$orderby=Created desc",

Date parsing

The REST API in SharePoint returns the date in ISO8601 which means that you’ll get the date and time back in a format like this: 2014-06-30T22:56:00Z. To format date and time in JS is tricky. To avoid this I’ve used a great JS plugin called Moment.js this one will help you and take care of the parsing. MomentJS will also give you the option to display the result in a clean way like 2014-06-30 or for example a format like ‘1 month ago’.

Security trimming

The result from a REST query are not security trimmed so if you want to display only the sites for which the users has access to, you need to secure trim the REST URI with a filter using the EffectiveBasePermissions with a following parameter like this

filter=(effectivebasepermissions/high%20gt%2032)

There’s a permission in SharePoint to allow web service called “Use Remote Interfaces”. All users should have this permission to be able to read results from a REST call.

If you have users in the out of the box group ‘visitors’ which have the permission level = Read, you’ll need to add ‘Use Remote Interfaces’ to this permission level. You can find the page with this URL: http://YourSite/_layouts/15/editrole.aspx?role=Read
URI

Do also note that the ‘Use Remote Interfaces’ can be disabled and enabled on the Web Application level in central admin.

Do always test your REST calls for users with different permission levels.

Stay tuned for the next post in this series!

</christian>

Getting started with REST in SharePoint 2013 – Part I

Welcome to my new series about using REST in SharePoint 2013! My intention in writing this is to show some quick and easy to follow examples of how to use REST web services (Representational State Transfer) and oData to access and querying in SharePoint 2013 and all this will be in focused on the user interface, the branding and web development aspects of using REST in SharePoint, like how to retrieve and manipulate list data or site data, how to create ‘data views’, rollups using latest standards of HTML, CSS and jQuery spiced with Ajax.

If you have previously worked with XSLT and data view web parts I hope this series will bring some inspiration to get going with JS for your apps, web parts or pages in SharePoint.

REST-1

This image shows an example of how you can roll up items from a list, toggling for more information and filter by some metadata. More about this in one of the upcoming posts

With REST you can make an http request to get information from different kinds of data sources like a SharePoint list. If you try it in your browser it would be just like going to a website but instead of returning a web page, you will get back the data in XML.

In other words you can easily consume the services in SharePoint that the API offers like list data, social feeds or search if you for example needs to expose its content in an app or similar. REST do also permits the data formats XML or JSON and supports CRUD operations. REST is one of the main APIs that are provided in SharePoint 2013, foundation or server and SharePoint Online but it’s not enabled for a public SharePoint online site over http.

I’ll not going into all the technical details here, read more about REST at Office Dev Center – MSDN library. [http://msdn.microsoft.com/en-us/library/office/jj164022(v=office.15).aspx]

Now let’s look into on how to start use REST/oData to fetch information from a SharePoint list.

Part 1 – A basic example

In this example we’ll examine how to access the REST/oData web services direct in the browser. Just replace the https://contoso/ with your URL. Furthermore make sure you have a document library in the site with the name Documents including some documents. Just make sure you have permission to access this site and the library.

  1. Paste this URL in your browser and hit enter:
    https://contoso/_api/web/lists/getbytitle('Documents')/items?$select=Title

    – the browser will now give you the desired result in XML.

  2. Now it’s time to parse the XML into a human readable format. To do this, I push the objects in the array into HTML and set the style with CSS.

    Use Visual Studio or SharePoint Designer and create an empty .aspx page for example in the root web in SharePoint to try this out. If you prefer you can also add the script, HTML and CSS into a .txt file and link this file by using the content editor web part. This will work in SharePoint Online or in SharePoint on prem, but not for and public site in SharePoint Online with HTTP.

    Download the page here to take a look.

    The key point is the script which looks like this:

$.ajax({
url: "/_api/web/lists/getbytitle('Documents')/items?$select=Title&$top=5",
type: "GET",
headers: {"Accept": "application/json;odata=verbose"},
cache:false,                
success: function(data){
console.log(data);
var items = [];
$(data.d.results).each(function(){
items.push('<ul id="' + 'listUL' + '">' + 
'<li id="' + 'listLI' + '">' + 
this.Title +
'</li>' + 
'</ul>');
});
items.push("</div>");
$("#listResult").html(items.join(''))}
}); 

Note that you need to add an reference to jQuery One thing to note is that the URL have a limitation of 256 characters, for normally usages like according to my examples in this series this shouldn’t be any issues, and if you need to more that the limit allows you can use CSOM with for example an old good CAML query or maybe use an data view web part instead.

In the next post I’ll will write more about how to build the queries with help of the following options:

  • Select
  • Filter
  • Expand
  • Order by
  • Filter
  • Top

I will also show a bit more advanced example of how to create a rollup of ‘the latest 5 blog posts’ on the start page of the home site including HTML and CSS for the UI.

More posts will come and here’s a list of some of the topics I will write about:

  • More examples of how to work with SharePoint lists and site information
  • Tools for site exploring, query building, debugging and JSON viewing
  • How to create and Image slideshow
  • Using REST to get external content
  • CRUD operations (Create Read Update Delete)
  • Using Knockout or Angular for data binding
  • Using DataTables, MixItUp and Moment.js for stuff like sorting, pagination and date parsing
  • Interact to the user profiles with help of SPservices
  • Packing you REST script as an APP

I will be back soon so stay tuned for the next post!

Follow

Get every new post delivered to your Inbox.

Join 1,347 other followers