Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Client-side Content Editor / Script Editor include in order to extend the SharePoint 2013 content by search and search results webparts with a new token for filtering on followed sites by the active user

Client-side Content Editor / Script Editor include in order to extend the SharePoint 2013 content by search and search results webparts with a new token for filtering on followed sites by the active user.

Deployment instruction

  1. Add ce_followedsites.html to your siteassets document library
  2. Add a content editor / script editor to your webpart page / wiki page / publishing page
  3. Link this content editor to this ce_followedsites.html
  4. Use {FollowedSites} (uses '=' as the operator) or {FollowedSitesBeginsWith} (':' as the operator) as a token in your search query. Please note: the search results preview panel does not give back any results. Use the Search tool from codeplex to build your search query.

Second note: you need to configure your search webparts to execute the search queries client-side. You will find this option in the query builder options tab.

<script type="text/javascript">
// Content Editor / Script Editor include in order to extend the SharePoint 2013 content by search and search results webparts with a new token for filtering on followed sites by the active user
// (c) 2015 Carl in 't Veld
// dependencies: jQuery
// This script works by introducing two extensions on top existing SharePoint OOTB functions:
// 1. SP.ClientRuntimeContext
// Extending the function executeQueryAsync, we create a stack in the ClientRuntimeContext instance
// that contains a range promises that must be resolved before the executeQueryAsync ultimately fires
// 2. Microsoft.SharePoint.Client.Search.Query.SearchExecutor
// Extending the function ExecuteQueries we scan on the token {FollowedSites} or {FollowedSitesBeginsWith} and replace it with the followed sites by the user
var oldexecuteQueries = Microsoft.SharePoint.Client.Search.Query.SearchExecutor.prototype.executeQueries;
var oldexecuteQueryAsync = SP.ClientRuntimeContext.prototype.executeQueryAsync;
var token = "{FollowedSites}"; // will use the '=' operator
var tokenBeginsWith = "{FollowedSitesBeginsWith}"; // will use the ':' operator
function replaceTokeninArray(token, queries, followedSites, operator) {
var joinstring = "(";
for (var i = 0; i < followedSites.length; i++) {
joinstring += " path" + operator + "\"" + followedSites[i].Uri + "\"";
joinstring += ")";
for (var idx in queries) {
var item = queries[idx];
var query = item.get_queryTemplate();
var split= query.split(token);
if (split.length > 1) {
var joined = split.join(joinstring);
function replaceLogic(queries, followedSites) {
replaceTokeninArray(token, queries, followedSites, "=");
replaceTokeninArray(tokenBeginsWith, queries, followedSites, ":");
var followedSites; // cache variable for the followed sites
Microsoft.SharePoint.Client.Search.Query.SearchExecutor.prototype.executeQueries = function() {
var activeContext = this.get_context(); // the ClientRuntimeContext that we want to catch during a executeQueryAsync so that we can chain our followed-sites call
var queryIds = arguments[0];
var queries = arguments[1];
var handleExceptions = arguments[2];
var arr = queries; // an array with Microsoft_SharePoint_Client_Search_Query_KeywordQuery items
// check if one of the queries contains the token:
var found = false;
for (var idx in arr) {
var item = arr[idx];
var query = item.get_queryTemplate();
if (query.indexOf(token) !== -1) {
found = true;
// one could force the deferred construction even if the token is not present, by forcing found to true:
// found = true;
if (found) {
// our token is present; we must act
// test if followedSites was already retrieved:
if (followedSites === undefined) {
// followedSites was not retrieved before; has yet to be retrieved
var deferred = jQuery.Deferred();
activeContext.promises = activeContext.promises || [];
activeContext.promises.push(deferred.promise()); // we tell our ClientRuntimeContext extension that we want to wait on something to complete
var that = this;
var myargs =;
// we create an empty results-object that we already return to the original executeQueries-call:
var results = new SP.JsonObjectResult();
// retrieve the followed sites:
if (this.state() === "resolved") {
followedSites = _followedSites;
else {
// failed; most probably because the user has not yet have a social data store, i.e. no mysite yet.
// one could present an informative message but we choose to force that the query does not yield any results:
followedSites = "@@";
// now we have retrieved the followed sites, we can replace the token within the query:
replaceLogic(queries, followedSites)
/* copied from
executeQueries: function Microsoft_SharePoint_Client_Search_Query_SearchExecutor$executeQueries(queryIds, queries, handleExceptions) {
var $v_0 = this.get_context();
var $v_1;
var $v_2 = new SP.ClientActionInvokeMethod(this, 'ExecuteQueries', [ queryIds, queries, handleExceptions ]);
$v_1 = new SP.JsonObjectResult();
$v_0.addQueryIdAndResultObject($v_2.get_id(), $v_1);
return $v_1;
// we have to build up the body of the original executeQueries ourselves completely
// because we have already given back an empty results-object to the originalexecuteQueries-call:
var method = new SP.ClientActionInvokeMethod(that, 'ExecuteQueries', [ queryIds, queries, handleExceptions ]);
activeContext.addQueryIdAndResultObject(method.get_id(), results);
return results;
else {
// use the cached result:
replaceLogic(queries, followedSites)
return oldexecuteQueries.apply(that, myargs);
// no token replacement necessary; just execute
return oldexecuteQueries.apply(this, arguments);
var execute = function (followingManagerEndpoint) {
var dfd = jQuery.Deferred();
url: followingManagerEndpoint + "/my/followed(types=4)",
headers: {
"accept": "application/json;odata=verbose"
success: function (data) {
error: function (xhr, ajaxOptions, thrownError) {
xhr: xhr,
ajaxOptions: ajaxOptions,
thrownError: thrownError
return dfd.promise();
SP.ClientRuntimeContext.prototype.executeQueryAsync = function() {
if (this.promises !== undefined && this.promises.length > 0) {
var that = this;
var myargs =;
var currlength = this.promises.length;
jQuery.when.apply(jQuery, this.promises).then(function() {
if (currlength === that.promises.length) that.promises = []; // flush them all; theoretical performance benefit
oldexecuteQueryAsync.apply(that, myargs);
return oldexecuteQueryAsync.apply(this, arguments);
var getFollowedSitesAsyncDeferred;
function getFollowedSitesAsync() {
if (getFollowedSitesAsyncDeferred && getFollowedSitesAsyncDeferred.state() !== "resolved") {
return getFollowedSitesAsyncDeferred;
getFollowedSitesAsyncDeferred = jQuery.Deferred();
var followingManagerEndpoint = SP.PageContextInfo.get_webAbsoluteUrl() + "/_api/social.following";
execute(followingManagerEndpoint).then(function(data) {
var followedActors = data.d.Followed.results;
var query = "";
/* for (var i = 0; i < followedActors.length; i++) {
query += " path=\"" + followedActors[i].Uri + "\"";
} */
getFollowedSitesAsyncDeferred.resolve(followedActors); // one could move the AND operator to the search webpart
.fail(function(error) {
return getFollowedSitesAsyncDeferred.promise();
})(); // self-calling anonymous function

This comment has been minimized.

Copy link

martin-svendsen commented Jun 21, 2016

Very nice work and thank you for sharing!
I have implemented a solution using your method, and in the beginning it was all great.
Then I added Paging in the Search Results WP, and now I get the a javascript error in "" and another in "sp.runtime.js".
"Object doesn't support property or method 'get_context'" and "Incorrect usage of exception handling scope.".

Does this look familer to you? Have you experienced the same issues? And are you working on a fix for this.
Or is it some where else in my setup?


This comment has been minimized.

Copy link

mattlovelara commented Jul 25, 2016


Could you tell me which managed property should i use with this ? I tried "Path" but it returns every items from sites i can access, not sites i'm following,

Thanks !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.