Tuesday, July 7, 2015

Retrieving Expanded Calendar Events with REST vs. SOAP

There is very little now in 2013 for which you need SPservices [sic]

from /r/sharepoint

It wouldn’t surprise you to hear that my answer to this is “it depends”. SPServices is nice in that it works the same way across SharePoint versions from 2007-2013, which might be enough reason to use it. But there are still cases where the SOAP services do something that REST doesn’t [yet] provide.

The other day I ran across an example of this. I wanted to pull events from a SharePoint calendar from today forward and I wanted to expand all of the recurring events. Since I was working on SharePoint 2013, I immediately tried to make this work using a REST call.

The first hurdle I faced was that it wasn’t possible to filter on a data like StartDate in a REST query. I know that sounds crazy, but if it’s possible I couldn’t find any way to do it.

The other challenge was the expansion of recurring events. It turned out that this was only possible using SOAP. REST doesn’t know anything about SharePoint calendar events, especially recurrence or all-day events.

I ended going back to an old thread in the SPServices discussions started by Jim Bob Howard (@jbhoward) which gives the CAML to make this work. The code I ended up with is below. In this case, I was using KnockoutJS to populate a customized view of multiple SharePoint calendars – the “rollup” idea. We’ve the various calendar locations stored in a list, and when we get to this code we have an array of calendars to work with.

// Calendar rollup
var calendars = [];
var events = [];
var calendarPromises = [];

// For each calendar, go and get the events
for (var i = 0; i < calendars.length; i++) {

  var thisCalendar = calendars[i];
  var today = moment().format();

  calendarPromises[i] = $().SPServices.SPGetListItemsJson({
      listName : thisCalendar.title,
      webURL : thisCalendar.listUrl,
      CAMLViewFields : "<ViewFields>" +
                          "<FieldRef Name='ID' />" +
                          "<FieldRef Name='Title' />" +
                          "<FieldRef Name='EventDate' />" +
                          "<FieldRef Name='EndDate' />" +
                          "<FieldRef Name='Location' />" +
                          "<FieldRef Name='Description' />" +
                          "<FieldRef Name='Category' />" +
                          "<FieldRef Name='fRecurrence' />" +
                          "<FieldRef Name='RecurrenceData' />" +
                          "<FieldRef Name='fAllDayEvent' />" +
                "</ViewFields>",
      CAMLQuery : "<Query>" +
                          "<Where>" +
                                  "<And>" +
                                          "<DateRangesOverlap>" +
                                                  "<FieldRef Name='EventDate' />" +
                                                  "<FieldRef Name='EndDate' />" +
                                                  "<FieldRef Name='RecurrenceID' />" +
                                                          "<Value Type='DateTime'>" +
                                                          "<Year />" +
                                                  "</Value>" +
                                          "</DateRangesOverlap>" +
                                          "<Or>" +
                                                  "<Eq>" +
                                                          "<FieldRef Name='HomePageHide' />" +
                                                          "<Value Type='Bool'>False</Value>" +
                                                  "</Eq>" +
                                                  "<IsNull>" +
                                                          "<FieldRef Name='HomePageHide' />" +
                                                  "</IsNull>" +
                                          "</Or>" +
                                  "</And>" +
                          "</Where>" +
                          "<OrderBy>" +
                                  "<FieldRef Name='EventDate' />" +
                          "</OrderBy>" +
                "</Query>",
      CAMLQueryOptions : "<QueryOptions>" +
                          "<CalendarDate>" + today + "</CalendarDate>" +
                          "<ExpandRecurrence>TRUE</ExpandRecurrence>" +
                          "<RecurrenceOrderBy>TRUE</RecurrenceOrderBy>" +
                          "<ViewAttributes Scope='RecursiveAll'/>" +
                "</QueryOptions>",
      mappingOverrides : {
        "ows_fAllDayEvent" : {
          "mappedName" : "fAllDayEvent",
          "objectType" : "Boolean"
        },
        "ows_fRecurrence" : {
          "mappedName" : "fRecurrence",
          "objectType" : "Boolean"
        }
      }
    });

}

$.when.apply($, calendarPromises).then(function () {

  var calendarEvents = this;

  // For each calendar, go and get the items and push them into an array
  for (var i = 0; i < calendars.length; i++) {

    $(calendarEvents[i].data).each(function () {
      events.push({
        calendar : calendars[i].title,
        id : this.ID,
        title : this.Title,
        location : this.Location != null ? this.Location : "",
        category : this.Category,
        eventDate : moment(this.EventDate),
        endDate : this.EndDate,
        fAllDayEvent : this.fAllDayEvent,
        fRecurrence : this.fRecurrence,
        RecurrenceData : this.RecurrenceData
      });
    });

  }

  events.sort(function (a, b) {
    return a.eventDate.isAfter(b.eventDate) ? 1 : (a.eventDate.isBefore(b.eventDate) ? -1 : 0);
  });

  ko.applyBindings(new calendarViewModel(), document.getElementById("calendar-rollup"));

});

The code is written to handle a variable number of calendars (we started with four). Because I’m putting the promise for each call to GetListItems in the calendarPromises array, I can use

$.when.apply($, calendarPromises)

to ensure that all of the promises have been fulfilled before processing the results.

I’m also using the fantastic MomentJS library to work with dates and times. Don’t ever try to do that manually again.

I’d love to hear that I don’t need to use SOAP for this anymore, but this seems to be a case where SOAP still wins out. Maybe there’s a place for SPServices in 2013, after all.


by Marc D Anderson via Marc D Anderson's Blog

1 comment:

  1. for fetching events from list there we need to pass either LIST NAME or LIST GUID but by looking at you youre code i think you missed that part or am not able to understand the above stated code

    ReplyDelete