JavaScript Templating with SharePoint 2013 (Part 3)

Update: I’ve since converted this template to a configurable web part. See http://www.lestersconyers.com/birthdays-and-anniversaries/ for latest.

Welcome to Part 3 of JavaScript Templating. In this post we will use the SharePoint 2013 Search API to build a Birthdays web part based on user profile data. And my procrastination worked for the good because today is actually my birthday! As in the last post I will be using jQuery to retrieve data and Handlebars to build my HTML.

The Finished Product

I’ll be building a template the renders upcoming birthdays. The display is based on Facebook’s birthday reminders. And as an added bonus, we’ll allow you to post a “Happy Birthday” message to the birthday boy’s (or girl’s) news feed.
birthdays list

Getting Started

We’re going to leverage the Birthday user profile property + search as our data source.

  1. To begin, have your users update their profile to include their birthday.
  2. Next you’ll need to alter the search schema.  Create a managed property named “Birthday” and map it to the crawled property “People:SPS-Birthday”. You’re managed property should look a little something like this:
    Property Name: Birthday
    Type: Date and Time
    Searchable (not required)
    Retrievable
    Sortable Yes – active
    Token Normalization
    Mapping to crawled properties: People:SPSBirthday
  3. ALSO, VERY IMPORTANT! Click the Advanced Searchable Settings and set the Full-text index to PeopleIdx and Weight group Context 0.
  4. Now, run a full crawl on the content source that crawls the profiles.

Data Retrieval: Birthdays.js

I programmed most of the code for Birthdays.js using a language I like to call Ctrl+C,Ctrl+V so it’s very similar to Event.js from my last post. However, the getData function is completely different.

this.getData = function () {

        var qryUrl = _spPageContextInfo.webAbsoluteUrl + "/_api/search/postquery";
        //sharepoint defaults the year of the Birthday profile properties to 2000.
        //so set our time back to have accurate querires
        var now2k = moment().year(2000);

        //build a JSON object for our search query
        var searchQry = {
            'request': {
                '__metadata': { 'type': 'Microsoft.Office.Server.Search.REST.SearchRequest' },
                'Querytext': 'Birthday>"' + now2k.subtract('days', 1).format("MM-DD-YYYY") + '" AND Birthday<"' + now2k.add('days', 31).format("MM-DD-YYYY") + '"',
                'EnableQueryRules': 'false',
                'SourceId': 'b09a7990-05ea-4af9-81ef-edfab16c4e31', //only search within the Local People Results
                'SelectProperties': {
                    'results': [
                        'PreferredName',
                        'PictureURL',
                        'Birthday',
                        'Path',
                        'AccountName'
                    ] //only return these properties
                },
                'SortList': {
                    'results': [
                        {
                            'Property': 'Birthday',
                            'Direction' : '0'
                        }
                    ] //sort by Birthday in ASC order
                }
            }
        };

        //go get my data
        $.ajax({
            url: qryUrl,
            type: 'POST',
            contentType : "application/json;odata=verbose;",
            headers: {
                "Accept": "application/json;odata=verbose",
                "X-RequestDigest": $("#__REQUESTDIGEST").val()
            },
            data: JSON.stringify(searchQry), //stringify my search query object
            dataType: 'json',
            success: $.proxy(this.callSuccess, this),
            error: $.proxy(this.callFailed, this)
        });
    };

In this function we’re going to hit the Search REST api to get data. There are several sources on the web the show how to use a common GET request when retrieving data. However, you can also make a POST via ajax call. Using this approach we have to construct a JSON object that represents our query. This method is much less documented. And to make things worse, the little documentation that exists on MSDN is wrong.

When our results come back, the callSuccess function is executed.

this.callSuccess = function (data) {

        //convert from what sharepoint gives me to something more usable for handlebars
        data = buildDataObject(data);

        //get the html contents of the template we appended to the body
        var source = $('#' + this.settings.templateId).html();
        //thank God for Handlebars
        var template = Handlebars.compile(source);
        //let Handlebars do all the hard work
        var html = template(data);
        //take the html handlebars generates and add it to the container
        $('#' + this.settings.containerId).html(html);

        //set the keypress event on the textarea for sending birthday wishes
        $('.happy-birthday-wish').keypress(this.sendWish);
    };

When our data is returned, we want to pass it to Handlebars so we can build HTML. However, the JSON object is too complex for Handlebars to digest. So we pass what SharePoint returns to the helper function buildDataObject. More on that a little later. After we get a simpler object, we let Handlebars do it’s thing. After we append the HTML to the container object, we add a keypress event handler to objects with the happy-birthday-wish class. This is the textarea that shows us for people whose birthday is today. Let’s take look at the buildDataObject:

    function buildDataObject(olddata) {
        var data = {
            Birthdays : []
        };

        var qResults = olddata.d.postquery.PrimaryQueryResult.RelevantResults.Table.Rows.results;
        //foreach row of results
        for (var i = 0; i < qResults.length; i++) {
            var r = qResults[i];
            //get the row properties (cells)
            var cells = r.Cells.results;

            var path = '';
            var photoUrl = '';
            var preferredName = '';
            var birthday = '';
            var accountName = '';

            //foreach property
            for (var x = 0; x < cells.length; x++) {
                var c = cells[x];
                //the Key will be the Managed property name
                switch (c.Key) {
                    case "Path": path = c.Value; break; //the path to the user's profile
                    case "Birthday": birthday = c.Value; break;
                    case "PictureURL": photoUrl = c.Value; break;
                    case "PreferredName": preferredName = c.Value; break;
                    case "AccountName": accountName = c.Value; break; //login name. used for posting to news feed
                }
            }

            //set the default image of the photo
            if (!photoUrl) photoUrl = _spPageContextInfo.webAbsoluteUrl + '/_layouts/15/images/person.gif';

            var realDate = moment.utc(birthday);
            var now = moment();

            //check to see if the user's birthday month and day match today's month and day
            var birthdayToday = (realDate.month() == now.month() && realDate.date() == now.date());

            data.Birthdays.push({
                Name: preferredName,
                Birthday: birthdayToday ? 'Today!' : realDate.format("MMMM D"), //if today is their birthday, say Today! Otherwise, show their birthday
                Path: path,
                PhotoUrl: photoUrl,
                AccountName: accountName,
                BirthdayIsToday: birthdayToday
            });
        }

        return data;
    }

In this function I create a JSON object with an array named Birthdays. I then loop through the result rows and values. When I get a value I want, I add it to a local variable. Then I create a simple object that is pushed into the Birthdays array. This simple object is much more Handlebars friendly.

The Template: Birthdays.aspx

<script id="birthdays-template" type="text/x-handlebars-template">
{{#each Birthdays}}

<div class="birthday-item clearfix">

<div class="birthday-photo">
            <img src="{{PhotoUrl}}" /></div>

<div class="birthday-info">

<h2><a href={{Path}}>{{Name}}</a></h2>

<h3>{{Birthday}}</h3>

            {{#if BirthdayIsToday}}
            <textarea rows="1" class="happy-birthday-wish" data-user="{{AccountName}}" data-name="{{Name}}" placeholder="Wish {{Name}} a happy birthay..." />
            {{/if}}</div>

</div>

 {{/each}}
</script>

The template is pretty simple. But note the if block. If the user’s birthday is today, I show a text area. We add a keypress handler on these textareas in the callSuccess function in Birthdays.js. A user can enter a birthday message. When they hit enter, the sendWish function should fire.

this.sendWish = function (e) {
        if (e.keyCode == 13 && !e.shiftKey) {
            e.preventDefault();

            //disable the textarea
            $(this).prop('disabled', true);

            var url = _spPageContextInfo.webAbsoluteUrl + '/_api/social.feed/my/feed/post';
            //get the domain\user_name of the birthday person. this was added to the data-user attribute in the template
            var userName = $(this).attr('data-user');
            //get the birthday message
            var message = $(this).val();

            //construct a json object the represents our birthday wish
            var wish = {
                'restCreationData': {
                    '__metadata': {
                        'type': 'SP.Social.SocialRestPostCreationData'
                    },
                    'ID': null,
                    'creationData': {
                        '__metadata': {
                            'type': 'SP.Social.SocialPostCreationData'
                        },
                        'Attachment': null,
                        'ContentItems': { //use ContentItems to mention the birthday person
                            'results': [
                                {
                                    '__metadata': {
                                        'type': 'SP.Social.SocialDataItem'
                                    },
                                    'AccountName': userName,
                                    'ItemType': 0,
                                    'Uri': null
                                }
                            ]
                        },
                        'ContentText': '@{0} ' + message,
                        'UpdateStatusText': false
                    }
                }
            };

            $.ajax({
                url: url,
                type: 'POST',
                contentType: "application/json;odata=verbose;",
                headers: {
                    "Accept": "application/json;odata=verbose",
                    "X-RequestDigest": $("#__REQUESTDIGEST").val()
                },
                data: JSON.stringify(wish),
                dataType: 'json',
                context: $(this), //pass in the textarea as the context so we can work with it on success or error
                success: function (data) {
                    //get the birthday person's name from the textarea
                    var name = $(this).attr('data-name');
                    //let user know post succeded
                    var html = "<span class="\&quot;birthday-wish-result\&quot;">You wrote on " + name + "'s newfeed.</span>";
                    var p = $(this).parent();
                    //remove the textarea
                    $(this).remove();
                    //append the success message
                    p.append(html);
                },
                error: function (xhr, status, error) {
                    alert('dang');
                }
            });
        }

    };

This function is pretty cool. SharePoint 2013 also exposes a Social REST API that we can use to post to newsfeeds! Again, this isn’t highly documented. In our example we’ll use this api to post the happy birthday message. Unfortunately you cannot post directly to a different user’s newsfeed. Instead we post to the current user’s news feed but mention the birthday user. Like Facebook, enter a birthday message and hit Enter.

birthdays message

Sweet success!

birthdays message success

Your message shows up as a Mention on the user’s news feed!

birthdays newsfeed

Componentization: A Web Part

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Birthdays.ascx.cs" Inherits="LS.JavaScriptTemplates.WebParts.Birthdays.Birthdays" %></pre>
<div id="birthdays-container"><!--my template will be added here--></div>
<pre>
<!--get the plugins I need -->
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/plugins/jquery.1.10.2.js"></script>
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/plugins/moment.2.1.0.min.js"></script>
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/plugins/handlebars.1.0.0.js"></script>

<!--get the custom scripts I need -->
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/handlebarhelpers.js"></script>
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/utility.js"></script>
<script type="text/javascript" src="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/scripts/birthdays.js"></script>
<!--css-->
 	<link href="<%= SPContext.Current.Site.Url.TrimEnd('/')  %>/content/birthdays.css" rel="stylesheet" />

<!--Initialize my js object here to get data and bind template-->
<script type="text/javascript">
var bDaysControl = new BirthdaysControl({
        siteUrl: '<%= SPContext.Current.Site.Url.TrimEnd('/')  %>',
        containerId: 'birthdays-container', //the div I want my template rendered in
        templateUrl: 'templates/birthdays.aspx', //site relative url of events.aspx
        templateId: 'birthdays-template' //matches the ID of the script tag in events.aspx
    });

    bDaysControl.init();
</script>

Wrapping Up

A Birthdays web part is something I commonly hear customers mention when they are revamping their intranet. If you are deploying SharePoint 2013, I hope you find this post useful and practical.

Finally, a special “Happy Birthday” to all who were born on October 7. I’m happy to share a birthday with you.

The Code

LS.JavaScriptTemplates.zipUpdate: I’ve since converted this template to a configurable web part. See http://www.lestersconyers.com/birthdays-and-anniversaries/ for latest.

Additional Literature

74 thoughts on “JavaScript Templating with SharePoint 2013 (Part 3)

  1. Pingback: SharePoint 2013 Good Links | Share your knowledge

  2. Amancio

    Hi Lester, followed the steps (2013 on-premise):
    Mapped the crawled property added my birthday (November 20) and did a full crawl
    Added and installed the .wsp through PowerShell
    Activated the feature
    Added the birthday webpart to a page but I’m getting a “My bad. Something didn’t go right”.

    What is the best way to solve the error?
    Regards, Amancio

    Reply
    1. LesterLester Post author

      You’ll have to add a breakpoint in the callFailed function to get the error status. Also, Fiddler will help reveal errors.

      Reply
  3. Amancio

    Addition: It seems to be that you need the English (United States) Locale on the site collection and on the My site. The error message is gone but I’m not getting any result yet.

    Reply
    1. Caroline

      Hi, Did you perhaps figure out how to resolve the blank/ no results showing in the web part?
      Your response will be much appreciated.

      Reply
        1. Caroline

          Hi Lester

          Yes i do, exactly as per the instruction above. Is there anything else i should look at perhaps?

          Reply
          1. LesterLester Post author

            Understood. Given that you don’t get an error it would indicate that things are set up correctly. Just not returning results. I’m assuming that you’ve verified that users have birthdays in their profiles? Also the web part shows birthdays for the next 30 days by default. You can increase that to 360 to get more birthdays. Other than that I’m not sure but it doesn’t sound like the web part.

            You could also do a search in a search center to determine if you get results that way.

    2. Jurgen

      It worked for me in Dutch after I changed the date format in the query.
      From
      Birthday<"' + now2k.add('days', 31).format("MM-DD-YYYY")
      to
      Birthday<"' + now2k.add('days', 31).format("DD-MM-YYYY")
      both of them of course

      Reply
      1. Martijn

        Mine is not working, getting the error

        Birthdays
        My bad. Something didn’t go right.

        Dutch environment, i can find users when i search for birthday>01-01-2000.

        The things i did was
        Setup the mapping and crawl
        Add the solution on sitecollection – solution
        Add to an page

        Jurgen did you do something else?

        Reply
        1. LesterLester Post author

          Looks like you need to modify the date format string. The original code is for MM-DD-YYYY (e.g. 09/25/2014) but you’ll need DD-MM-YYYY (e.g. 25/09/2014).

          Reply
          1. Martijn

            Not working, i changed this:

            { ‘type’: ‘Microsoft.Office.Server.Search.REST.SearchRequest’ },
            ‘Querytext’: ‘Birthday>”‘ + now2k.subtract(‘days’, 1).format(“DD-MM-YYYY”) + ‘” AND Birthday<"' + now2k.add('days', 31).format("DD-MM-YYYY") + '"'

            Still getting the same error :( .

    1. LesterLester Post author

      The web part title is Birthdays by default. However, if you simply deploy as a sandbox solution, the title may be blank when you add it to a page. You can always change the title by editing the web part properties.

      Reply
  4. Karen

    Hi,

    Thanks for the template. I am new to developing, how would I hide this webpart if there are no events or birthdays for the given timeframe? Currently it just displays an empty webpart box.

    Thanks

    Reply
    1. LesterLester Post author

      You’re more than welcome. In the success function, you could check the length of the results. If there aren’t any go up the ancestor tree to find the web part and hide it. Something like:

      if(data.Birthdays.length == 0){$(‘#’ + this.settings.containerId).closest(‘.ms-webpartzone-cell’).hide();}

      Haven’t tested that.

      Reply
    1. Karen

      Sorry just one follow up. The blank web part loads with the loading message then disappears. Is there a way to have it not load anything if there are no Birthdays instead of load the webpart then disappearing?

      Thanks

      Reply
      1. LesterLester Post author

        You could. The more likely scenario is that you’d want to hide the web part by default, then show if you get results. I’m sure it can be done but not sure how I’d do that. I’d recommend just showing a message like “No upcoming birthdays” instead of hiding the web part.

        Reply
  5. Karen

    Hi Lester,

    Sorry, one more question.

    To use this example to pull Anniversaries based on Hire Date, how would you query the Hire Date with the current date?

    Thanks

    Reply
    1. LesterLester Post author

      In it’s current state, the query will not work with the OOTB user profile property named HireDate. Unlike Birthday, HireDate keeps the correct year instead of 2000. You’d have to create a new User Profile property of type ‘date no year’ to hold the hire date.

      I’ve started working on that actually, as well as fixing many bugs in the Birthdays code. I’l probably be putting them on github or something soon. Stay tuned.

      Reply
  6. Karen

    Hi Lester,

    I added a custom field for the Anniversary month and that worked really well. The problem now is the number of results returned. How could I add pagination to the template so that all results are not displayed at once?

    Tahnks

    Reply
  7. Yuri

    Hi Lester. i am trying to test the project to see how it functions. I keep getting : Remote SharePoint site connections are not supported.
    How Do I test in Offline mode?
    PS, I’m a total noob and have to create a webapp to display bithdays on our intranet portal home page.

    Thanks in advance.

    Reply
    1. LesterLester Post author

      It can’t be tested in Offline mode. I think the solution is a Sandbox solution so you’ll need to open it on a machine that has SharePoint installed.

      Reply
        1. LesterLester Post author

          You’ll need to inspect the object that is returned from the ajax calls. I’d recommend using Fiddler but Chrome developer tools is a good second option.

          Reply
  8. Smita

    The solution is working fine for SharePoint on-premise. But while adding the solution in Sharepoint Online it is showing error “My bad. Something didn’t go right.”. How can we use it in sharepoint online?

    Reply
    1. LesterLester Post author

      I haven’t tried it online so I’m not sure. What is the underlying error? You can see it using the browser’s developer tools or Fiddler.

      Reply
  9. Zuhal Kılıçaslan

    Hi Lester,
    I’m trying to create a new managed property for Birthday. But i can’t select its type as Date and Time. Some of types are disable. Only text and Yes/No types are enable. What can i do? How can i activate this types? Have u any suggestion?

    Your feedback will be greatly appreciated. Thanks.

    Reply
    1. LesterLester Post author

      Not sure about that one. Are you creating the managed property via Central Admin? Doesn’t sound like it. If you’re in O365 or something, are you creating the managed property at the site collection or the site level?

      Reply
    1. LesterLester Post author

      Aah. I forgot about that O365 caveat. So basically they have predefined Managed Properties that you have to use. For example RefinableDate19. You’ll have to map the crawled property People:SPS-Birthday to RefinableDate19. Then you’ll have to modify the code to query on RefinableDate19 instead of Birthday. I’ll look to make this control o365 friendly in the near future.

      Reply
      1. Zuhal Kılıçaslan

        Okay Lester. I understood. I ll try to modify the codes. Thank you for your helps:)

        Reply
  10. Kipland Iles

    Great Job, Lester. I did as you instructed and deployed as a sandbox solution. Everything works. HR liked it so much that they want the same thing for Work Anniversaries (HireDate). I started hacking your JS but I really don’t have the environment (VS2010-2013) to create the webpart and solution (might just do it purely in JS without the webpart).

    Do you have any plans to extend this for work anniversaries? The biggest difference I see from birthdays is that we do have actual years and I want to group the results based on the year like “Celebrating X year(s) of Service”. I would keep everything else the same including posting back on my wall (er – newsfeed).

    So when can I download it ;-) .

    Again – great job. I’m thinking of using this approach instead of the dreaded CSWP. Just need a better way to componitize it all (guess I better go ahead and get VS2013).

    Reply
    1. LesterLester Post author

      Thanks. I’ve been meaning to get this on GitHub and possibly the SharePoint online app store. I’ve found a few bugs in this approach and…I have the code for anniversaries with the years of service as well :) . I’ve been a bit behind in life so when things slow down, I’ll get it posted.

      Reply
      1. Kipland Iles

        Great – Can’t wait. Note on the online app store, though. For us on-prem guys that haven’t created the app domain, yet (and might not) your WSP is much better. Just a thought. Also – something that goes hand in hand with Work Anniversaries is a New Hire feature. So when rolling up and grouping Anniversaries the top group (current year and current month) in the list would be New Hires. I didn’t know if you thought of that but I seem to always get a request to show birthdays, anniversaries, and new hires from HR.

        HR likes to do these as a monthly thing (for print and distribution) but your rolling time-span of 7 days seems adequate to me for the web since it’s dynamic. You put some thought into this, didn’t you :-)

        Hope you’re catching up in life.

        Reply
  11. NoCode

    I feel so dumb but how can I install this? I know how to upload solutions and activate them, but i have no club with all the Javascript files and all the other stuff you have in these folders. I have set the managed properties in CA, now just need to install it, but this is the part I’m lacking.

    Sorry for my no0b questions

    Reply
    1. LesterLester Post author

      You’ll have to install the solution somehow. It was developed as a Sandbox solution but it can be installed via PowerShell for all the farm to use. If you’re in Office 365, you’ll have to just install as a sandbox solution. Once it is installed just activate the feature and drop the web part on a page. The lights should just come on *fingers crossed*

      Reply
  12. NoCode

    Thanks Lester,

    No, this is not O365, this is onPrim. I have access to CA, and admin the site and everything, but I do not have access to the server.

    This is exactly what i have been trying to do for a while now. I just got the Employee Directory setup so it does pull all the information from the UPS. So, that was working great, i just thought it would be a great idea to have the Birthdays and HireDate for anniversaries on the Corp Site. I was hoping a simple Content query or something would be able to search that field in the UPS and display the correct info. I didn’t know it was going to be this complicated.

    I will see if i can get PS access or server access. Our Engineering department does not give that up so easily.

    Thanks for the info

    Reply
  13. NoCode

    So, just to clarify, and I’m sorry for the questions,

    But looking at the link you sent, I understand how to upload solutions (.wsp) files and activate them, but what about all the other folders/files you have in your solution. I guess that is where I’m getting thrown off. Dont i need those? If i upload the .wsp and activate it, where or what do I do with all the other files/folders?

    I understand these questions are basic so if you don’t have time to help me, i understand that too. This is just one part of everything i have not learned yet.

    Reply
  14. LesterLester Post author

    Those files (and this post) is intended to show you how the work is done just in case you want to do something similar or customize it. However, the wsp file contains the completed code. You shouldn’t have to do anything to them. Just deploy the solution and drop the web part on the page.

    Reply
  15. NoCode

    Your amazing.

    So sorry for all the questions but when i found your solution, i had done many many many searches to find a solution that would work for our company on our SP13 server. Your solution is amazing and 100% what i been looking for.

    Thanks again for your contribution to everyone.

    Going to try to install now

    Reply
  16. NoCode

    One last question, I just noticed that the .wsp file is not here. I am guessing I’m suppose to make it myself? I think the light is shining now..So, all those files/folders are the package and I edit it(if needed), then i create the .wsp file?

    Now i just need to figure out how to do that…lol

    Reply
  17. NoCode

    Got it

    Thanks again.. I just installed it on a test site. I need to play around with it to get some data in it, but Ill take care of that.

    Thanks again for all your help & sorry again to bug you

    Reply
  18. NoCode

    Perfect

    I got it working. At first I did not have any data, but i went and changed my birthday to today in my profile, and ran a full crawl, and it showed up. I told myself happy birthday and it posted to the newsfeed. Works amazing.

    I see you have another one in there called “events”, that says “My bad, something didn’t go right”. I read the post above from the other person so i understand what your saying, but my question is

    What is “Events” used for, if anything yet, and have you thought about making one for “Anniversaries? If we edit the “Hire date”, or is that what events is suppose to do?

    Also, I’m guessing the birthdays only show the current day. Do you think there is any possibility you could edit the code to send me a .wsp for birthdays “This Month”?

    Reply
  19. NoCode

    lester
    Disregard the post above about the birthdays and current month..Once i flipped some dates around, i see it already does current month.

    Sorry about that.

    your thoughts on “hiredate”?

    Reply
    1. Kipland Iles

      Hey NOCODE. Let’s don’t Pester Lester! Sorry I digress.

      He has contributed a great solution but do your due dilligence and read his posts, thoroughly; download the zips he so graciously donated to the community, and check the VS solution files (primarily the BIN/DEBUG folder for the WSP file) and install the WSP as a Sandboxed solution. Be sure you have provisioned your User Profile Service and that you have the Birthday (and HireDate) populated from Active Directory or other method (Powershell import or BCS). I had to populate birthdays and hire date manually using Central Administration because those properties were not in AD (they will be soon, though).

      When setting up a new farm the first services to configure IMHO are UPS and Search. If that’s not done right then nothing Lester provided will help.

      BTW – you must create a new list called Events with the columns defined as Lester described in his previous post if your want to use the Events feature. His code doesn’t do that for you.

      Reply
  20. Kip Iles

    For anyone that had issue with the HTML5 placeholder in Birthdays.aspx not working for older browsers you can do this…

    {{#if BirthdayIsToday}}
    <textarea onfocus="if(this.value=='Wish {{Name}} a happy birthday...') this.value='';" onblur="if(this.value=='') this.value='Wish {{Name}} a happy birthday...'" rows="1" class="happy-birthday-wish" data-user="{{AccountName}}" data-name="{{Name}}">Wish {{Name}} a happy birthday...</textarea>
    {{/if}}

    Reply
  21. Parth

    Thank you Lester for sharing this Code.

    I have done Exactly as you Mentioned.
    Step 1:- Mapped Birthday Filed of User Profile Service with AD customBirthday Field.
    Step 2:- Create a managed property named “Birthday” and map it to the crawled property “People:SPS-Birthday”.
    Step 3:- Full Sync in User Profile Service & Checking in My Site if Birthday Field is added with Value of Birthday-> Test Successful
    Step 4 :- Full Crawling of the Content Source & People Search is Working fine
    Step 5 :- Deploying WSP in to CA and Adding webpart to Page.

    As a result of this Page is not showing any Error but Page is coming as Blank. I checked everything Still Page is not showing Any Result. There are User having Birthday Today in upcoming days.

    Still No result :-( Please help me.
    I am using SharePoint 2013

    Reply
    1. Parth

      I got it working :-) I Just Checked Queryable in Editing Managed Property and it’s Giving me expected result.

      But I have some other Query. When i try to Open Project code you Provided, Its showing compatibility issue in Visual Studio 2010 & 2012 with below information.

      Unsupported
      This version of Visual Studio does not have the following project types installed or does not support them. You can still open these projects in the version of Visual Studio in which they were originally created.
      – LS.JavaScriptTemplates, “D:\abcd\xyz\LS.JavaScriptTemplates\LS.JavaScriptTemplates\LS.JavaScriptTemplates\LS.JavaScriptTemplates.csproj”

      No changes required
      These projects can be opened in this version of Visual Studio without changing them. They will continue to open in Visual Studio 2010 SP1 and in this version of Visual Studio.
      – LS.JavaScriptTemplates, “D:\acbd\xyz\LS.JavaScriptTemplates\LS.JavaScriptTemplates\LS.JavaScriptTemplates.sln”

      Can you please Tell me steps of fix to open this project so i can customize it my way. Thanks.

      Reply
  22. Oscar Polanco

    Hi Lester,

    Thanks for this solution, it works perfectly. I have a problem, because from an automatic synchronization from the AD, It loads in the Birthday the year, for example ’1985-01-25′, ’1980-05-14′ and so on. So now, what can I do to solution this problem? because the query with the year 2000 obviously doesn’t work.

    Thank you

    greetings from Colombia

    Reply
    1. LesterLester Post author

      I’m not sure how that’s possible. Verify that your Birthday profile property is of type DateNoYear. DateNoYear properties don’t store a year but default to 2000.

      Reply
          1. LesterLester Post author

            Interesting. What is the raw value on the user profile property? And what crawled property is your Birthday managed property mapped to?

    1. Kipland Iles

      Hi Oscar. I verified what you discovered using my current on-premises 2013 SP1 installation. I have been importing birthdays from a CSV file using powershell and the year is stored based on the year of the actual import. If I go back to the profile and clear the value first, then type it back in manually just using the month and year then the year defaults back to 2000. This is causing me some grief, too, since some birthdays have 2000, some have 2014, and now some have 2015. I’m using the SearchQueryToolV2 as well. Even if I insure that the CSV has a TEXT value for birthday properly formatted is still stores it internally with the current year.

      $mySiteHostSite = Get-SPSite $mySiteUrl
      $mySiteHostWeb = $mySiteHostSite.OpenWeb()
      $context = Get-SPServiceContext $mySiteHostSite
      $upm = New-Object Microsoft.Office.Server.UserProfiles.UserProfileManager($context)
      $up = $upm.GetUserProfile($userAccountLookup.AccountName)
      $up[$_].Value =
      $up.Commit()

      This will store 2015-01-05T00:00:00.0000000Z in SPS-Birthday and Birthday:today will find it after a crawl of the people index; however, manually typed birthday of same date will return 2000-01-05T00:00:00.0000000Z.

      It looks like a bug to me as DateNoYear should mean just that.

      I don’t bring that in using UPS since I don’t store Birthdays in AD but I expect that something similar is happening with UPS. I may have to manually set the year to 2000 and provide the full date during the import to maintain consistency. I did notice that some checks happen during the import. For instance February 29 will not import since there is no Feb 29 in 2015. I was able to type it in manually, though.

      Reply
  23. Vinayak Puthran

    Hi Lester,

    I’ve downloaded and implemented your code, but I am getting the error:
    Microsoft.Office.Server.Search.REST.ServerSearchException,
    message:{lang:en-US, value:We didn’t understand your search terms. Make sure they’re using proper syntax.}

    Please help. I have create the managed property but the code just isn’t working.

    Regards,
    Vinayak Puhtran.

    Reply
  24. Daniel Silva

    hi lester , my application is developed in sharepoint 2013 in Spanish. I get only the following message … “My bad. Something did not go right.”
    but it has not been due.
    Please supporting.

    Reply
      1. Jack

        Will this work in SharePoint online? Would love to try it out, but i am not missing the “how to”, appreciate your help.

        Reply
  25. Pingback: Birthdays and Anniversaries | Lester Sconyers Jr

  26. Jack

    Saw the caveats, thank you for those. I am unsure which files I need to use those, the readme’s in github are failrly sparse.

    I was expecting a wsp, but the folders are empty.

    Thanks again

    Reply
  27. Chris

    Hi Lester,
    Any idea on how to over come the problem with the year being appended to the SPS-Birthday field? As mentioned above, I am uploading the birthdays via script and the current year is being appended to the field even though it is of type DateNoYear. When I enter the birthday manually the year “2000″ is appended.

    I see that this was briefly discussed, but I don’t see any mention of resolution or a potential work around.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>