I'm trying to get events from a SharePoint site and insert them into FullCalendar using the method described by Josh McCarty here.
An empty calendar shows up successfully using the snippets in Josh's article, however there seems to be an error when the Events List in SharePoint is being queried. I'm doing a slight modification from his example, because my List is in the host web site, not the app, so I have to use the webURL parameter.
When I look in the Chrome Dev console I see "Request format is unrecognized" as an error.
This technique uses SPServices, and a Caml query to fetch events, but I suspect something is wrong with my Caml.
My code (below) is almost verbatim from the example, but gives me an error at or around the line that I marked with debugger; //REQUEST FORMAT IS UNRECOGNIZED occurs here.
// Add events to the calendar. This is where the "magic" happens!
events: function( start, end, callback ) {
// Create an array to hold the events.
var events = [];
// Set the date from which to pull events based on the first visible day in the current calendar view.
// For a month view, this will usually be several days into the previous month.
// We can use FullCalendar's built-in getView method along with the formatDate utility
// function to create a date string in the format that SharePoint requires.
// It must be in the format YYYY-MM-DDTHH:MM:SSZ. Due to time zone differences,
// we will omit everything after the day.
var startDate = $.fullCalendar.formatDate( $( '#calendar' ).fullCalendar( 'getView' ).start, "u" ).split( "T" )[0];
// Get the current view of the calendar (agendaWeek, agendaDay, month, etc.).
// Then set the camlView to the appropriate value to pass to the web service.
// This way we will only retrieve events needed by the current view
// (e.g. the agendaWeek view will only retrieve events during the current week rather
// than getting all events for the current month).
var calView = $( '#calendar' ).fullCalendar( 'getView' ).title;
var camlView = "";
switch( calView ) {
case "agendaWeek":
camlView = "<Week />";
break;
case "agendaDay":
camlView = "<Week />";
break;
default: // Default to month view
camlView = "<Month />";
}
// Set the camlFields, camlQuery, and camlOptions to the appropriate values to pass to the web service.
// You can add additional <ViewFields /> or adjust the CAML query if you have some custom columns that
// you want to filter by or display data from. The values below are the pretty much the minimum you'll
// want to start from to get it working.
var camlFields = "<ViewFields><FieldRef Name='Title' /><FieldRef Name='EventDate' /><FieldRef Name='EndDate' /><FieldRef Name='Location' /><FieldRef Name='Description' /><FieldRef Name='fRecurrence' /><FieldRef Name='RecurrenceData' /><FieldRef Name='RecurrenceID' /><FieldRef Name='fAllDayEvent' /></ViewFields>";
var camlQuery = "<Query><CalendarDate>" + startDate + "</CalendarDate><Where><DateRangesOverlap><FieldRef Name='EventDate' /><FieldRef Name='EndDate' /><FieldRef Name='RecurrenceID' /><Value Type='DateTime'>" + camlView + "</Value></DateRangesOverlap></Where><OrderBy><FieldRef Name='EventDate' /></OrderBy></Query>";
var camlOptions = "<QueryOptions><CalendarDate>" + startDate + "</CalendarDate><RecurrencePatternXMLVersion>v3</RecurrencePatternXMLVersion><ExpandRecurrence>TRUE</ExpandRecurrence></QueryOptions>";
var hostUrl = decodeURIComponent(getQueryStringParameter("SPHostUrl"));
debugger;
// Make the web service call to retrieve events.
$().SPServices({
operation: "GetListItems",
async: false,
webURL: hostUrl,
listName: paidTimeOffListName, // This is the GUID or display name of your calendar. If the calendar is on a different site, you can use the display name with the webURL option (see SPServices.CodePlex.com for more information).
CAMLViewFields: camlFields,
CAMLQuery: camlQuery,
CAMLQueryOptions: camlOptions,
completefunc: function( xData, Status )
{
debugger; //REQUEST FORMAT IS UNRECOGNIZED occurs here.
//.find( '[nodeName="z:row"]' ) unsupported past jQuery 1.7
$(xData.responseXML).SPFilterNode('z:row').each( function()
{
debugger;
// Check for all day events
var fADE = $( this ).attr( 'ows_fAllDayEvent' );
var thisADE = false;
var thisStart = $( this ).attr( 'ows_EventDate' );
var thisEnd = $( this ).attr( 'ows_EndDate' );
if ( typeof fADE !== "undefined" && fADE !== "0" )
{
thisADE = true;
}
// Get the list item ID and recurrence date if present. This will be used to generate the ID query string parameter to link to the event (or the specific instance of a recurring event). The ID query string parameter must be in the format "ID.0.yyyy-MM-ddTHH:mm:ssZ" for recurring events (where "ID" is the list item ID for the event). Event ID's are returned as just a number (for non-recurring events) or several numbers separated by ";#" in 2007 or "." in 2010 to indicate individual instances of recurring events. By splitting and joining the ID this way, thisID will be set to a valid query string parameter whether an event is recurring or not for both versions of SharePoint.
var thisID = $( this ).attr( 'ows_ID' ).split( ';#' ).join( '.' );
// FullCalendar documentation specifies that recurring events should all have the same id value when building the events array (the id is optional, but I'm including it for completeness). We can get the list item ID (which is the same for all instances of recurring events) without the recurrence information by simply splitting thisID.
var eventID = thisID.split( '.' )[0];
// Get the event title. This is displayed on the calendar along with the start time of the event.
var thisTitle = $( this ).attr( 'ows_Title' );
// Get the event description. I don't use it in this example, but you could use it for something, perhaps as a tooltip when hovering over the event.
var thisDesc = $( this ).attr( 'ows_Description' );
// Add the event information to the events array so FullCalendar can display it.
events.push({
title: thisTitle,
id: eventID,
start: thisStart,
end: thisEnd,
allDay: thisADE,
// Adjust this URL to link to the display form for your calendar events. You can include a Source parameter to allow users to easily return to the FullCalendar page.
url: '/Lists/Calendar/DispForm.aspx?ID=' + thisID + '&Source=' + window.location,
description: thisDesc
});
});
callback( events );
}
});
}
I believe I've figured out what the problem is, although unfortunately, it seems to be unfixable.
SPServices won't work cross-domain (as far as I can tell), and since I'm trying to make this call from inside of a SharePoint app to a list not stored in the app, it's considered a cross domain request.
One workaround would be to use something else to get the info, such as REST.
EDIT: Info in the article here seems to contradict my belief:
SPServices supports anonymous access to read from lists, and it works cross-site and cross-domain by simply specifying the webURL in the Web Service operations where it makes sense to do so. Of course, your authentication model has to allow cross-domain access.
So perhaps it is possible.