SharePoint calendar has the ability to store recurrent events. User can set an event to be repeated periodically several times. It can be daily, weekly monthly. But this raises a new problem for the developer. Sometimes, for the development, we might need a recurrent events to be returned as a set of different events. Here is what you can do this in a simple manner.

Getting all events for a specified month and year

Let’s assume we are requested to get all the events for a specified month and year. Using SPServices extension is very easy. I have wrap the functionality into my code, obtaining a simple function where you just need to pass name of the calendar list, a specific date into ISO format and a callback function which accepts an array of objects as parameter.

Date.toISOFormat = function (date, ignoreTime) {
    /// <summary>Date object static method to format a date to date ISO string - YYYY-MM-DDThh:mm:ssZ</summary>
    /// <param name="date" type="Date" mayBeNull="false" optional="false"></param>
    /// <param name="ignoreTime" type="Boolean" mayBeNull="false" optional="true"></param>
    /// <returns type="String">A string representing ISO format for specied date</returns>

    // If not specified, time is ignored
    var ignoreTime = ignoreTime || {};

    function pad(number) {
        // Add leading 0 if number is less then 10 (enclosed method)
        var r = String(number);
        if (r.length === 1) r = '0' + r;
        return r;
    }

    var Y = date.getFullYear();
    var M = pad(date.getMonth() + 1);
    var D = pad(date.getDate());
    if (ignoreTime === false) return Y + '-' + M + '-' + D + 'T00:00:00Z';


    var h = pad(date.getHours());
    var m = pad(date.getMinutes());
    var s = pad(date.getSeconds());
    return Y + '-' + M + '-' + D + 'T' + h + ':' + m + ':' + s + 'Z';
}





// Create Shp namespace
var Shp = Shp || {};

Shp.ListItem = function (xmlNode) {
    /// <summary>Object representing list item, built from xml node obtained for lists.asmx web service</summary>
    /// <param name="xmlNode" type="XML" mayBeNull="false" optional="false">XML node for list item</param>
    this.listItem = new Object();
    this._parseResponse(xmlNode);
}

Shp.ListItem.prototype._parseResponse = function (xmlNode) {
    /// <summary>Internal method of ListItem to parse xml node and enclose data into listItem property</summary>
    /// <param name="xmlNode" type="XML" mayBeNull="false" optional="false">XML node for list item</param>
    for (var j = 0; j < xmlNode.attributes.length; j++) {
        var nodeName = xmlNode.attributes[j].nodeName.replace('ows_', '');
        var nodeValue = xmlNode.attributes[j].nodeValue;
        this.listItem[nodeName] = (nodeValue === undefined || nodeValue === null) ? '' : nodeValue;
    }
}

Shp.ListItem.prototype.get_item = function (field) {
    /// <summary>Internal method of ListItem to parse xml node and enclose data into listItem property</summary>
    /// <param name="field" type="String" mayBeNull="false" optional="false">XML node for list item</param>
    var fieldValue = (this.listItem.hasOwnProperty(field) === true) ? this.listItem[field] : '';
    return fieldValue;
}





// Shp.Lists object
Shp.Lists = {};


Shp.Lists._serializeResponse = function (xml) {
    /// <summary>Parse XML server response and convert it into an array of Shp.ListItem objects</summary>
    /// <param name="xml" type="XML" mayBeNull="false" optional="false">A date object. Month data where specified date is included will be returned.</param>
    /// <returns type="Array" elementsType="Shp.ListItem"></returns>

    var items = new Array();
    var rows = xml.getElementsByTagName("z:row");
    for (var i = 0; i < rows.length; i++) {
        items.push(new Shp.ListItem(rows[i]));
    }
    return items;
}

Shp.Lists.getMonthEvents = function (list, date, callback) {
    /// <summary>Get events from a specified calendar list based on a specified date.</summary>
    /// <param name="listName" type="String" mayBeNull="false" optional="false">Calendar list name</param>
    /// <param name="date" type="Date" mayBeNull="false" optional="false">A date object. Month data where specified date is included will be returned.</param>
    /// <param name="callback" type="Function" mayBeNull="false" optional="false"></param>

    // We set calendar date in the middle of the month. Seems SharePoint Online did not return correct results if I set to first day of month
    var calendarDate = new Date(date.getFullYear(), date.getMonth(), 15);
    var month = date.getMonth();
    var year = date.getFullYear();

    function isInRange(dateString) {
        /// enclosed function to check if date is in range
        var dt = dateString.split(' ')[0];
        var y = parseFloat(dt.split('-')[0]);
        var m = parseFloat(dt.split('-')[1]) - 1;
        return (y === year && m === month);
    }


    var caml = "<Query>" +
                   "<Where>" +
                       "<DateRangesOverlap>" +
                           "<FieldRef Name='EventDate' />" +
                           "<FieldRef Name='EndDate' />" +
                           "<FieldRef Name='RecurrenceID' />" +
                           "<Value Type='DateTime' IncludeTimeValue='FALSE'>" +
                               "<Month />" +
                            "</Value>" +
                        "</DateRangesOverlap>" +
                   "</Where>" +
                   "<OrderBy>" +
                       "<FieldRef Name='EventDate' />" +
                    "</OrderBy>" +
               "</Query>";



    jQuery().SPServices({
        operation: 'GetListItems',
        async: true,
        listName: list,
        CAMLQuery: caml,
        CAMLRowLimit: 100,
        CAMLQueryOptions: '<QueryOptions><DateInUtc>FALSE</DateInUtc><ViewAttributes Scope="RecursiveAll" /><CalendarDate>' + Date.toISOFormat(calendarDate) + '</CalendarDate><IncludeMandatoryColumns>TRUE</IncludeMandatoryColumns><RecurrencePatternXMLVersion>v3</RecurrencePatternXMLVersion><ExpandRecurrence>TRUE</ExpandRecurrence></QueryOptions>',
        completefunc: function (data, status) {
            if (status === 'success') {
                var events = Shp.Lists._serializeResponse(data.responseXML);
                var items = [];
                for (var i = 0; i < events.length; i++) {
                    var start = events[i].get_item('EventDate');
                    var end = events[i].get_item('EndDate');

                    if (isInRange(start) === true || isInRange(end) === true) {
                        items.push(events[i]);
                    }
                }
                callback(items);
            }
            else {
                alert('Cannot complete the request');
            }
        }

    });

}

So, to get all the events you just need to call a function in a was similar with this:

jQuery(document).ready(function () {


    Shp.Lists.getMonthEvents('Company Events', new Date(), function (items) {
        for (var i = 0; i < items.length; i++) {
            alert(items[i].get_item('EventDate'));
        }
    });


});

You can notice array of object received by the callback function contains Shp.ListItem objects and event if we don’t use client object model, getting a field value is pretty much similar. Also, as DateRangesOverlap has some issues and is more a calendar view than a view containing events in a month, the code is restricting the results to get accurate data. In SharePoint Online, according to what I have discovered until now, this CAML clause is not event working like in SharePoint 2010 and I get different results if CalendarDate is the first day of the month or, for example, the 15th.

Getting all events for a specified date

This request is a little bit more easier to complete. That’s because data obtain for a date is accurate and you don’t need to adjust the results. In a similar way, you can create function to get the data from calendar list.

Shp.Lists.getDayEvents = function (list, date, callback) {
    /// <summary>Get events from a specified calendar list based on a specified date.</summary>
    /// <param name="listName" type="String" mayBeNull="false" optional="false">Calendar list name</param>
    /// <param name="date" type="Date" mayBeNull="false" optional="false">A date object.</param>
    /// <param name="callback" type="Function" mayBeNull="false" optional="false"></param>

    var caml = "<Query>" +
                   "<Where>" +
                       "<DateRangesOverlap>" +
                           "<FieldRef Name='EventDate' />" +
                           "<FieldRef Name='EndDate' />" +
                           "<FieldRef Name='RecurrenceID' />" +
                           "<Value Type='DateTime' IncludeTimeValue='FALSE'>" +
                               "<Today />" +
                            "</Value>" +
                        "</DateRangesOverlap>" +
                   "</Where>" +
                   "<OrderBy>" +
                       "<FieldRef Name='EventDate' />" +
                    "</OrderBy>" +
               "</Query>";



    jQuery().SPServices({
        operation: 'GetListItems',
        async: true,
        listName: list,
        CAMLQuery: caml,
        CAMLRowLimit: 100,
        CAMLQueryOptions: '<QueryOptions><DateInUtc>FALSE</DateInUtc><ViewAttributes Scope="RecursiveAll" /><CalendarDate>' + Date.toISOFormat(date) + '</CalendarDate><IncludeMandatoryColumns>TRUE</IncludeMandatoryColumns><RecurrencePatternXMLVersion>v3</RecurrencePatternXMLVersion><ExpandRecurrence>TRUE</ExpandRecurrence></QueryOptions>',
        completefunc: function (data, status) {
            if (status === 'success') {
                var events = Shp.Lists._serializeResponse(data.responseXML);
                callback(events);
            }
            else {
                alert('Cannot complete the request');
            }
        }

    });

}

What it seems it Today from CAML string is not actually current date. It represent the date you specified as CalendarDate in QueryOptions section.

However, it seems in SharePoint Online, selecting first day of the month is producing and error and gives me also the event from the last day of previous month. For the moment I have no idea why this is happening, but my intention is to contact Microsoft to ask for more information.

Advertisements