Me & My SharePoint [FrontEnd]

© Christian Stahl – All about SharePoint branding & customizations

Most visited sites in SharePoint 2013 — April 30, 2015

Most visited sites in SharePoint 2013

Hi! Here come’s just a short blog post about how to Query the REST search api in SharePoint 2013, Online or On prem to display most visited sites that could look something like the image below. Take not that you should be able to get something similar with a content search web part with help of a custom display template as well. But if you need to display this in an app part or a script web part I hope this could be in any help.

TopSite

Let’s try this!

Download my experimental code from here, and please if you found something that can be written better or if you have any other ideas regarding this, drop me a comment!

Take a look at some other posts about REST in SharePoint I’ve written Before, you’ll find them over here

The main parts of this is the search API

_api/search/query?querytext=

And the ViewsLifeTime property

ViewsLifeTime

/Christian

How to create a text-size switcher in SharePoint with HTML5 Web storage — March 31, 2015

How to create a text-size switcher in SharePoint with HTML5 Web storage

I’ve written a couple of post earlier about how to work with JS and cookies, that’s cool but now in the era of the client side it’s time to take a look into how to work with HTML5 Web Storage and Local Storage and in the example, how to create a simple text-size switcher.

What is Web Storage and what can we do with it?

Ok, with local storage, a web application like SharePoint can store data locally within the user’s browser. Before HTML5, app data had to be stored in cookies, included in every server request. Local storage is more secure, and large amounts of data can be stored locally, without affecting website performance.
Yeah localStorage works somewhat like a database; it stores some data locally on the user’s browser, which then can be retrieved using JavaScript. The data will always be available in the user’s browser, even after the user has closed the browser.

There are many different ways you could use web storage, like:

  • Navigation: Remember the last state, add an active class into the DOM
  • Forms: Remember form values
  • Branding: Let the user decide things like an alternate CSS (style sheet switcher)
  • WebService calls: If something doesn’t change then there is no point in loading it over the Internet compared to local access which is so much faster

Local Storage v/s Cookie

Cookies, the old way of saving data are primarily for reading server-side. Local storage can only be read client-side, so if you need to create a function for the client and not for the server, use Local Storage. Got it? The Local Storage stays on the client, while cookies are sent to the server through the HTTP header. Cookies are not considered secure and not able to hold much data either.

More to note

The Local Storage stores data with no expiration date and gets cleared only through JavaScript or by clearing the Browser Cache or Locally Stored Data, unlike cookie expiry. There’s two kind of ‘storage objects’, Local and Session (storage). A Local storage will keep the data stored when you close the tab or a the broser window and the Session storage will not keep anything stored. Local Storage comes with HTML5, so if you need to create an app supporting really old browsers (for example IE7) you can use cookies as a fallback by for example modernizr as the detector. However Local Storage are a part of the HTML5 specs, so if you allow HTML5 in your master page, which comes out of the box in SharePoint 2013 and the users have IE8, latest Google Chrome, Safari, Firefox etc your ready to go!

Demo: Create a text-size switcher

This example will change the SharePoint body font size from small to large in Three steps. This will work in SharePoint 2013, onprem or Online. I’ve tested this in IE11 and latest Google Chrome. Take a look at a video of the text-size switcher. Click at the SWF file and open it.

I’ll just provide you the HTML, CSS and JS – you could implement this in a custom masterpage, APP or for example in a text file linked from a content editor web part. In this case I’m using _spBodyOnLoadFunctionNames and if your using a custom master page, be sure to include a reference to an external JS file in the bottom of the master page, just above the ending body tag. Keep the reference to jQuery in the head section of your master page. You can download a masterpage with a css file and js file if you need to take a look from here.

Markup

<div class="radioFontHolders">
<div class="letterFontsHolder">
<div class="miniLetter">Aa</div>
<div class="midLetter">Aa</div>
<div class="bigLetter">Aa</div>
<div style="clear:both"></div>
</div>           
<div class="radioHolders">
<input type="radio" id="radio-1-1" name="radio-1-set" class="regular-radio" value="miniFont" /><label for="radio-1-1"></label>
<input type="radio" id="radio-1-2" name="radio-1-set" class="regular-radio" value="midFont" /><label for="radio-1-2"></label>
<input type="radio" id="radio-1-3" name="radio-1-set" class="regular-radio" value="maxFont" /><label for="radio-1-3"></label>
</div>
</div>

JS

// Using SP JS API to push the function instead of document ready or window load
_spBodyOnLoadFunctionNames.push("Demo.Intranet.Contrast");

// Establish Namespace
Type.registerNamespace('Demo')
Demo.Intranet = Demo.Intranet || {};

// start set contrast ---
Demo.Intranet.Contrast = function () {

jQuery('.radioHolders input').click(function () {
var fontCode = jQuery(this).val();
if (fontCode == 'miniFont'){
jQuery('#miniFont').attr("checked", "checked");
jQuery("body").removeClass("midFont MaxFont").addClass("miniFont");
}
else if (fontCode == 'midFont'){
jQuery('#midFont').attr("checked", "checked");
jQuery("body").removeClass("miniFont MaxFont").addClass("midFont");
}
else if (fontCode == 'maxFont'){
jQuery('#maxFont').attr("checked", "checked");
jQuery("body").removeClass("miniFont midFont").addClass("MaxFont");
}
else{}

localStorage.setItem("fontCode", fontCode); 
});

if (localStorage.getItem("fontCode") !== null) {
var fontCode = localStorage.getItem("fontCode");
if (fontCode == 'miniFont'){
jQuery('#radio-1-1').attr("checked", "checked");
jQuery("body").removeClass("midFont MaxFont").addClass("miniFont");
}
else if (fontCode == 'midFont'){
jQuery('#radio-1-2').attr("checked", "checked");
jQuery("body").removeClass("miniFont MaxFont").addClass("midFont");
}
else if (fontCode == 'maxFont'){
jQuery('#radio-1-3').attr("checked", "checked");
jQuery("body").removeClass("miniFont midFont").addClass("MaxFont");
}
else{}
}
}

CSS

.radioFontHolders{
border:0px #e1e1e1 solid;
padding:10px 5px 10px 5px;
margin-top:30px;width:100%;
}
.letterFontsHolder{
padding:1px;width:170px;
}
.miniLetter, .midLetter, .bigLetter{
float:left;font-family:"Times New Roman", Times, serif;
font-weight: 700;height:32px;
border-bottom:1px #ccc solid;
color:#035183
}
.miniLetter{
width:33.33333%;
font-size:18px;
width:40px;line-height:41px;
padding-left:7px;
}
.midLetter{
width:33.33333%;
font-size:26px;width:50px;line-height:36px;
padding-left:7px;
}
.bigLetter{
width:33.33333%;
font-size:40px;width:60px;line-height:26px;
}
.font-1, .font-2, .font-3{
width:28px;float:left;height:20px;
}
/* set the fonts */
.miniFont{font-size:0.9em}
.midFont{font-size:1.3em}
.MaxFont{font-size:1.5em}
.radioHolders{
margin-top:10px;
margin-bottom:10px;
z-index: 1;width: 200px;
z-index:999;background-color: transparent
}
/* radios */
.regular-radio{display: none}
.regular-radio + label {
margin-left:10px;
-webkit-appearance: none;
background-color: #fafafa;
border: 1px solid #cacece;
box-shadow: 0 1px 2px rgba(0,0,0,0.05), inset 0px -15px 10px -12px rgba(0,0,0,0.05);
padding: 9px;
border-radius: 50px;
display: inline-block;
position: relative;
background-image: -webkit-linear-gradient(hsla(0,0%,100%,.1), hsla(0,0%,0%,.1));
border-radius: 100%;
zoom: 1; cursor: pointer;
display: inline-block;
margin-right: 12px;
-webkit-appearance: none;
}
.regular-radio:nth-child(1) + label:after{background-color:#CDCDCD}
.regular-radio:nth-child(3) + label:after{background-color:#CDCDCD}
.regular-radio + label:after{background-color:#CDCDCD}
/* selected */
.regular-radio:checked + label:after {
content: ' ';
width: 12px;
height: 12px;
border-radius: 50px;
position: absolute;
top: 3px;
background: #84B942;
box-shadow: inset 0px 0px 10px rgba(0,0,0,0.3);
text-shadow: 0px;
left: 3px;
font-size: 32px;
border-radius: 100%;
box-shadow: inset 0 0 0 1px hsla(0,0%,0%,.4), 0 1px 1px hsla(0,0%,100%,.8);
content: '';
display: block;
}
/* not selected */
.regular-radio + label:after {
content: ' ';
width: 12px;
height: 12px;
border-radius: 50px;
position: absolute;
top: 3px;
box-shadow: inset 0px 0px 10px rgba(0,0,0,0.3);
text-shadow: 0px;
left: 3px;
border-radius: 100%;
box-shadow: inset 0 0 0 1px hsla(0,0%,0%,.4), 0 1px 1px hsla(0,0%,100%,.8);
content: '';
display: block;
}

Thanks, Christian

Add a breadcrumb in SharePoint 2013 — February 28, 2015

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 — January 31, 2015

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 — 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!

Getting started with REST in SharePoint 2013 – Part III — July 31, 2014

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!

Follow

Get every new post delivered to your Inbox.

Join 1,385 other followers