Birthdays and Anniversaries

After much procrastinating and work, I finally revisited the Birthdays widget I introduced in JavaScript Templating with SharePoint 2013 (Part 3) about a year ago. The template was well received and liked by many. My initial post was prototype code with a handful of bugs. I’ve since worked fixed those bugs and converted the template to a web part. A web part? Yes..a web part. I know…I know. I should have used a SharePoint hosted app but a web part was too easy to resist! Maybe vNext???

Several people also asked if the original template could be extended to support anniversaries. I’m happy to announce that I finally found time to do that as well!

The Finished Product

Anniversaries

New Features

  • Configuration options - several options are now configurable and passed to the JavaScript plugin. This includes the managed property to query.
  • Client Cache – Both web parts still issue a client-side query to SharePoint’s search REST api. After the first load the returned data is stored in the browser’s localStorage object. After the desired time or after closing the browser, the plugin will re-query for fresh data.
  • Comparison Messages – When configured correctly, plugin can calculate year differences for messages like “19 years of service” or “30 years old.”
  • Show/hide more – Added the ability to toggle additional birthdays or anniversaries to conserve space.

Getting Started

  1. In Central Administration, verify these settings on OOTB user profile properties
    1. Hire date
      1. Type: date
      2. Default Privacy Settings: Everyone
    2. Birthday
      1. Type: date no year
      2. Default Privacy Settings: Everyone
  2. Create new user profile properties. Pay close attention to the data types!
    1. Hire date no year
      1. Name: HireDateNoYear
      2. Display: Hire date no year
      3. Type: date no year
      4. If importing from a source like Active directory, map to the same attribute as Hire date.
    2. Birthday with year
      1. Name: BirthdayWithYear
      2. Display: Birthday with year
      3. Type: date
      4. If importing from a source like Active directory, map to the same attribute as Birthday.
  3. Make sure at least one profile has values for Hire date, Hire date no year, Birthday, and Birthday with year.
  4. Run full crawl
  5. Create managed properties. If using Office365, you’ll need to map crawled properties to the OOTB refinable managed properties (e.g. RefinableDate15)

    Property Name or Alias Type Settings Crawled Properties
    Birthday Date and Time Queryable, Retrievable, Sortable People:SPS-Birthday
    HireDateNoYear Date and Time Queryable, Retrievable, Sortable People:HireDateNoYear
    BirthdayWithYear (optional) Date and Time Retrievable People:BirthdayWithYear
    HireDate (optional) Date and Time Retrievable People:SPS-HireDate
  6. Run another full crawl
  7. Visit github repo to get code or solution file.
  8. Drop web part on page

Options

If you created managed properties with the same names as me, the web part should work with no changes. If you were not able to use the same names…no worry. The search property is configurable. Many of the options are self explanatory so I won’t go into detail. However, I do want to note a few.

  • Search Property - This is the managed property used in the search query. This should be a managed property that maps to a crawled property that maps to a User Profile property of type date no year. By default, this will be Birthdays or HireDateNoYear.
  • Comparison Property  - This property is used to calculate someone’s age or the number of years of their anniversary. I know that age is a touchy subject so by default, the Birthdays web part does not have a value for comparison property. The Anniversaries web part does have a default value for comparison property but it is not required.
  • Comparison Message – After calculating age or years of anniversary, web part displays a trailing message like 19 years of service.

REMEMBER: On the first load, the data is cached in localStorage. If you are debugging or wanting to query on each load, set the Client Cache Minutes property to 0.

Wrapping Up

Sorry to all that were patiently (and impatiently) awaiting this post. I hope it’s worth waiting for. Enjoy!

The Code

https://github.com/lestersconyers/ls.widgets

40 thoughts on “Birthdays and Anniversaries

  1. Pingback: JavaScript Templating with SharePoint 2013 (Part 3) | Lester Sconyers Jr

  2. M. Petersen

    When i try to activate the feature i get:

    Failed to instantiate file “Style Library/ls.widgets/base/handlebars-helpers.js” from module “Style Library”: The specified list does not exist.

    Reply
    1. M. Petersen

      The folders are there but “handlebars-helpers.js” is not, i have tried copying them manually to the library, still same error.

      Reply
  3. Justin

    So, I’m not a web developer or programmer by any means. SharePoint was thrown on me so I have been trying to figure it out. Anyways, so do I just installed the wsp using the powershell, activate the feature, then add the webpart to the page? Or are there more things that I’m not aware of to make this work?

    Thanks,

    Reply
      1. Justin

        I followed the instructions for setting up the profile properties and search managed properties. Nothing shows up under Birthdays and Anniversaries says “We’re sorry. Something didn’t go right”

        Thanks for the assist.

        Reply
          1. Justin

            I corrected the name on the webpart and I’m no longer getting the error message. I’m getting Sorry. No birthdays upcoming and same message for Anniversaries. So now I’m trying to figure out why it’s not pulling data. I double checked the managed search fields and everything is exactly as you mentioned in the article. Is there a way to search for those fields in the normal search to verify they are searchable?

            Thanks,

          2. LesterLester Post author

            Birthday>=”01-01-2000″ AND Birthday<=”12-31-2000″ should return all people with a Birthday for the year.

  4. Justin

    This is all I see in the Console:

    Deprecation warning: moment().add(period, number) is deprecated. Please use moment().add(number, period).
    utilities.js is already loaded
    Birthdays.js is already loaded
    Bad Request

    Reply
    1. Kipland Iles

      Great solution Lester. Been waiting awhile for the Anniversaries. One thing I will mention is that most folks will probably load both web parts on a single page which would result in utilities.js and birthdays.js to try to load again as can be seen in the console. I have used start.js in the past to prevent this but SP2013 may have better ways to do this (thinking Scriptlink.Register).

      Reply
    2. Kipland Iles

      Nevermind Lester, I looked at the source and see you are using the global namespace trick to prevent multiple loading. Good Job. I did find a potential issue, however. Date (no year) I think assumes the year is 2000. If I import the actual HireDate into the HireDateNoYear (using Powershell to update the profile property from a CSV export) I get no results. I ran into a similar issue importing complete birthdays into the SPS-Birthday property. I ended up creating a new field in the CSV file that sets the year to 2000 for birthdays and that fixed my issues. I’ll try doing the same for HireDate. I say this to warn folks that may be importing a complete date from AD or CSV into HireDateNoYear.

      Reply
      1. Kipland Iles

        And I just verified that setting the year to 2000 works. I have Anniversaries now!!!!

        Reply
        1. LesterLester Post author

          Interesting. The year has always gotten set to 2000 for me when importing from AD. Never tried it from CSV but glad you got it working!

          Reply
          1. Kipland Iles

            I don’t have HireDate and Birthdays setup in AD, yet, so I have to import just the HireDates and Birthdays from the same HR Export directly into SharePoint 2013. If the data contains a year then that does get stored. I did not realize until I exported all of the properties that my imports stored those dates with the actual year. In fact, I initially was doing imports without a year and SharePoint actually imported them with the CURRENT YEAR. That was not an issue until we went from 2014 to 2015 and I had no Birthdays. That’s what got me digging.

            If you just type in the Month and Day from Central Admin it always stores it as 2000.

            I may have to pick your brain to determine how best to update the AD Schema to accept HireDate and Birthday so I can simply update AD and let the Profile sync do the rest for me.

  5. Marcel de Groot

    I’ve completed the installation and followed the Getting Started Guideline.
    The webpart also gives the message “we’re sorry. Something didn’t go right”
    In my console i get an Internal Server Error.

    Can you assist me?

    Reply
    1. ahmed saber

      am facing the same error “internal server error”, could you please assist me as I see you have the same error and you resolved it, thanks.

      Reply
  6. Rochelle

    I am going to try this tomorrow since employees are asking for it, even though the language is far above my head!

    Reply
  7. Ivan

    Lester,

    Thank you very much for your work! It’s been of great help.

    I would recommend formatting the dates for the search query like “YYYY-MM-DD”, so that the web part can be used on sites with different language settings (now the ajax request to the search service gets Internal Server Error for languages that format dates like dd.mm.yyyy).

    Reply
  8. zizo

    lester it’s a great work really and wish to add this solution to my environment, but I have this error “We’re sorry. Something didn’t go right”, when I open console on chrome I get 500 internal server error, hereunder description for my error, how can I solve this?

    -1, Microsoft.Office.Server.Search.REST.SearchServiceException

    The search request cannot be empty.

    Reply
  9. Kipland Iles

    Hey Lester. I have one more feature request to add to your solution that might complete the series – New Hires. This should be primarily the same code for Anniversaries but we would need to query for HireDate within the last 30 days, for instance. Maybe provide a start and end date parameter in the webpart or allow for KQL named date ranges (example: HireDate:”last month” and HireDate:”this month”). I could actually do this with your existing Anniversaries webpart if I had a “Days Backward” parameter and by setting the Search Property to HireDate. If I get the time I may grab your source and add that feature.

    Reply
  10. Sergey

    Hello Lester!
    You creat great web part, but unfortunely I have a trouble with it, I get pepole only begining from next year (2016) and can not see any at this year (2015). I’m suppose there is a little bug with datatime conversion maybe due my localization different from English. If you give me hint where should I find it I will be very thank you

    Reply
  11. Anton

    Hi Lester,
    When i have the webpart in edit mode it works fine. But when i save it, it keeps saying Loading.
    Nothing happens. Any idea what that could be?

    Its in SharePoint Online. Scripts are on.

    Reply
        1. Abhijit

          Hi Lester,

          Getting bellow error from consol.

          Uncaught TypeError: jQuery(…).SPBirthdays is not a function(anonymous function) @ Test.aspx:876

          jQuery(‘#’ + eleId).SPBirthdays(
          { daysForward : 7,localStoreTimeout : 30,maxDisplay : 4,noDataMessage : ‘Sorry. No anniversaries upcoming.’,wishPlaceholder : ‘Say happy anniversary…’,cmprProperty : ‘HireDate’,cmprMessage : ‘years of service’,srchProperty : ‘HireDateNoYear’ }
          );

          Reply
  12. Abhijit

    I am getting bellow error in consol,

    POST http://dskdl-spsvr/_api/search/postquery 400 (Bad Request)
    k.cors.a.crossDomain.send @ jquery.js:8623
    n.extend.ajax @ jquery.js:8152
    getDataFromSearch @ birthdays.js:150
    getData @ birthdays.js:91
    SPBirthdays @ birthdays.js:399
    (anonymous function) @ birthdays.js:410
    n.extend.each @ jquery.js:375
    n.fn.n.each @ jquery.js:139
    $.fn.SPBirthdays @ birthdays.js:403
    (anonymous function) @ Test.aspx:886
    utilities.js:26 Bad Request

    Reply
      1. Kevin

        Drilled down in the console a little more and I have this error block:

        {error: {code: “-1, Microsoft.Office.Server.Search.REST.SearchServiceException”,…}}
        error: {code: “-1, Microsoft.Office.Server.Search.REST.SearchServiceException”,…}
        code: “-1, Microsoft.Office.Server.Search.REST.SearchServiceException”
        message: {lang: “en-US”, value: “Invalid parameter: SortList.”}
        lang: “en-US”
        value: “Invalid parameter: SortList.”

        Reply
        1. Kevin

          I got past this error by completing step number 5. I wasn’t 100% sure if this was required as got slightly confused with the O365 wording.
          I’ve started a full crawl which will take around a day to complete. Maybe tomorrow we will have Birthdays!!!

          Reply
  13. Pablo

    Great job, Only one problem, when retrieve data the component has “Invalid date” but photo and name are correts. any sujestion??

    Reply
  14. Pingback: Birthdays and Anniversaries – spomani

  15. ian

    Hi, we have been using the birthday webpart for almost a year now, it’s great! one issue has just come up though. The last week and half of December birthdays do not display. it stopped on December 22 and skipped to January 1st. there are profiles with birthdays set from December 22 – December 31 but they do not show. any suggestions?

    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>