Show Upcomming Birthdays Based on SharePoint 2013 User Profiles with Search (Office 365)


A lot of customers like to have some kind of anniversary webpart on their corporate intranet. In SharePoint 2013 and Office365 you can provide this functionality using the SharePoint User Profile and Search (and some Javascript).

2013-10-02_22h27_30

In the SharePoint User Profile, users are able to provide their birthday by default. These values are automaticly crawled but you will not be able to apply filters using search. In this blog post I will describe the configuration steps for Office365.

> Go to the Site Settings -> Site Collection Administration -> Search Schema (you can also configure this in the SharePoint Admin level)
> Search for RefinableDate00, Edit the mapping (for SharePoint Onpremise, you are able to add a new one with type Date)
> Give it an alias like Birthday01
> Add “People:SPS-Birthday” as crawled field mapping

2013-10-02_22h07_48

> Be sure you have some people filled in their birthday on their profile
> You are done, o365 will need to wait at most 1 week (yes, you are reading it right). Full crawls are now performed once a week, if you need it by tomorrow you can submit a support ticket to MS support, they will initiate a full crawl on request. Onpremise users just fire off a full crawl.

To test if your managed property is working you can perform a query to your search center.
https://tenantname.sharepoint.com/search/Pages/peopleresults.aspx?k=Birthday01>”01-01-2000″

Our prerequisities are met, and we are able to query for a birthday. So why should we need any code…
As you can see in the “test” query above, we are using the year 2000 to query, this is not just a coincidence. SharePoint stores the Date no Year fields using the year 2000, and SharePoint Online is not different on this one. Unfortunately this makes our Search Results webpart unusefull because you can only use keywords like today-5, yesterday, last week, last month and last year.

The javascript below will perform a restcall to the search service to retrieve the birthdays which are in range (30 days in this example).

(note: jquery is required)

<div id="resultsDiv"></div>
<script type="text/javascript">// <![CDATA[
$(document).ready(function () {
    var e = ExecuteOrDelayUntilScriptLoaded(executeQuery(), "sp.js");
});

Date.prototype.AddDays=function(days)
{
	this.setDate(this.getDate() + days);
	return this;
}

function executeQuery() {

    Results = {
        element: '',
        url: '',

        init: function (element) {
            Results.element = element;

	    var birthday = 'Birthday01';
	    var space = '%20'; var colon = '%3A'; var quote = '%22'; var gt = '%3E'; var lt = '%3C'; var amp = '&';

            // Get current date
            var currentTime = new Date();
	    var startMonth = currentTime.getMonth()+1;
	    var day = currentTime.getDate();

            // Get current date + 30
	    var endTime = new Date();
            var endTime = currentTime.AddDays(30);
	    var endMonth = endTime.getMonth()+1;
            var endDay = endTime.getDate();

            var querytext = "";

	    // build query with the magic 2000 year
            if(startMonth!='12')
	    {
		querytext += birthday + gt + quote + day + '-' + startMonth + '-' + '2000' + quote + space + 'AND' + space + birthday + lt + quote + endDay + '-' + endMonth + '-' + '2000' + quote;
	    }
	    else
	    {
		querytext += birthday + gt + quote + day + '-' + startMonth + '-' + '2000' + quote + space + 'OR' + space + birthday + lt + quote + endDay + '-' + endMonth + '-' + '2000' + quote;
	    }
            Results.url = _spPageContextInfo.webAbsoluteUrl + "/_api/search/query?querytext=%27" + querytext + "%27&sourceid=%27B09A7990-05EA-4AF9-81EF-EDFAB16C4E31%27&selectproperties=%27Title,"+ birthday +",Path%27&sortlist=%27"+ birthday +":ascending%27";
        },

        load: function () {
            $.ajax(
                    {
                        url: Results.url,
                        method: "GET",
                        headers: {
                           "accept": "application/json; odata=verbose",
                        },
                        success: Results.onSuccess,
                        error: Results.onError
                    }
                );
        },

        onSuccess: function (data) {
            var results = data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results;
	    var months = [ "januari", "februari", "maart", "april", "mei", "june", "juli", "augustus", "september", "oktober", "november", "december" ];
            var html = "<div class='birthday'>";

            for (var i = 0; i < results.length; i++) {
		var name = results[i].Cells.results[2].Value;
		var date = new Date(Date.parse(results[i].Cells.results[3].Value));
                var link = results[i].Cells.results[4].Value

                html += "<span>";
                html += "<a href='"+link+"'>" + name + "</a>";
                html += " "
                html += date.getDate() + " "+ months[date.getMonth()];
                html += " ";
            }

	    if (results.length == 0)
            {
              html += "Er zijn geen verjaardagen (bekend).";
            }

            html += "</div>";
            Results.element.html(html);
        },

        onError: function (err) {
            alert(JSON.stringify(err));
        }
    }

    Results.init($('#resultsDiv'));
    Results.load();

}

// ]]></script>