In this final part of the series we’re going to complete the holiday sync app by importing the selected calendar exceptions into Project Server.

We’ll start from where we finished up in Part 2 with our app loading data from both existing project server calendars and our external web service, so to follow this article make sure you start with the solution from the end of Part 2.

Part 3: Importing data into Enterprise Calendars from our App

Now that we have a list of calendar exceptions that we want to import we need to use some JSOM to actually import those exceptions.

To break-down what we need to do here I’m going to review this JSOM in three steps;

Step 1 Prepare context and get objects to update

// Get the Project Server context
var projContext = PS.ProjectContext.get_current();
// Get our Calendar Collection
var eCalColl = projContext.get_calendars();
var eCalendar = eCalColl.getByGuid(calUid);
var eCalBaseExcep = eCalendar.get_baseCalendarExceptions();

In step 1 we instantiate our object variables using the PS.js JSOM library to first get our calendar collection, then get our calendar by GUID from that collection and finally we get our base calendar exceptions from our calendar.

Next we’ll update those collections.

Step 2 Create the exception(s)

// Step 2 Loop through and add each exception
for (var i = 0; i < exceptions.length; i++) {
  var excepInfo = new PS.CalendarExceptionCreationInformation();
  // Set the exception properties
  excepInfo.set_name(exceptions[i].Descriptor);
  excepInfo.set_start(exceptions[i].Date);
  excepInfo.set_finish(exceptions[i].Date);

  // Finally add the exception info to the base calendar object
  eCalBaseExcep.add(excepInfo);
}
// Update the collection
eCalColl.update();

Now we’re going to create each calendar exception using the PS.CalendarExceptionCreationInformation constructor, you’ll find one of those constructors for most of the objects in JSOM, if you want more on this see MSDN; http://msdn.microsoft.com/en-us/library/office/jj669390.aspx.

Once we have our excepInfo object we set the required properties; Name, Start and Finish, then finish by adding that item to our exception collection before moving to the next exception to be added. Finally we update the calendar collection object with all the exceptions created.

Now as we’re working asynchronously the last step is to execute the above update(s);

Step 3 Update the calendar asynchronously

// Finally asynchronously execute the update 
projContext.executeQueryAsync(Function.createDelegate(this, function () {
	// Success update our grid and finish up
	this.grid.setSelectedRows([]);
	// Display the results and remove the progress msg
	SP.UI.Notify.addNotification("Exceptions added successfully", false);
   }), Function.createDelegate(this, function (call, error) {
	// Handle Error
	alert(error.get_message());
}));

So using the JSOM executeQueryAsync function we execute the change and handle the result, here we simply want to notify the user of the result.

Full code block follows, paste this into the bottom of the App.js:

// Function to add calendar exceptions via JSOM
HolidaySync.prototype.addCalendarException = function (calUid, exceptions) {
    // Show a progress message
    this.notifyMsg = SP.UI.Notify.addNotification('<img src="/_layouts/images/loadingcirclests16.gif" style="vertical-align: top;"/> Importing...', true);

    // Step 1 Get the Project Server context and objects
    var projContext = PS.ProjectContext.get_current();

    // Get our Calendar Collection
    var eCalColl = projContext.get_calendars();
    var eCalendar = eCalColl.getByGuid(calUid);
    var eCalBaseExcep = eCalendar.get_baseCalendarExceptions();
    //CSOM Ref (no JSOM): http://msdn.microsoft.com/en-us/library/office/microsoft.projectserver.client.calendarexceptioncollection_di_pj14mref_members.aspx

    // Step 2 Loop through and add each exception
    for (var i = 0; i < exceptions.length; i++) {
        // Create our Calendar Exception Info
        //http://msdn.microsoft.com/en-us/library/office/jj669390.aspx
        var excepInfo = new PS.CalendarExceptionCreationInformation();

        // Append the year to the name to prevent future duplicates
        var exName = exceptions[i].Descriptor + " " + new Date(exceptions[i].Date).getFullYear();

        // Set the exception properties
        excepInfo.set_name(exName);
        excepInfo.set_start(exceptions[i].Date);
        excepInfo.set_finish(exceptions[i].Date);

        // Finally add the exception to the collection
        eCalBaseExcep.add(excepInfo);
    }

    // Update the collection
    eCalColl.update();

    // Step 3 Asynchronously execute the update 
    projContext.executeQueryAsync(Function.createDelegate(this, function () {
        // Success update our grid and finish up
        this.grid.setSelectedRows([]);

        // Display the results and remove the progress msg
        SP.UI.Notify.addNotification("Exceptions added successfully", false);
        SP.UI.Notify.removeNotification(this.notifyMsg);

    }), Function.createDelegate(this, function (call, error) {
        // Handle Error
        SP.UI.Notify.removeNotification(this.notifyMsg);
        alert(error.get_message());
    }));
};

 

Final bit: Import Button

Okay we’re almost done, just one last thing to do and that is to handle the click of the import button. A bit of jQuery will handle that for us, and in which we need to do just one more thing which is to check for any duplicates being imported to prevent import errors.

Paste the following in the main code block of App.js (should be right after the ‘$(“#importBtn”).click(…’ function:

// Button to import selected exceptions
$("#importBtn").click(Function.createDelegate(this, function () {
	var selectedRows = holidaySync.grid.getSelectedRows();

	// Use helper function to check for any duplicates before importing
	var exceptionsToImport = Helpers.removeDuplicates(selectedRows, holidaySync.data);

	// Import the exceptions
	if (exceptionsToImport.length > 0) {
		holidaySync.addCalendarException(holidaySync.data.calendarId, exceptionsToImport);
	}
	else {
		// Mark all existing
		holidaySync.grid.setSelectedRows([]);
		alert("All selected exceptions already exist.");
	}
}));

What we need to do in that function is call our removeDuplicates helper function which was created back in part 1 of this series. The function will return a filtered array of exceptions that we can then pass as a parameter to our addCalendarException function.

Now we should be able to test it and see the following:

pic1

 

All Done

This app documented here is now available on the SharePoint App store, so please rate it if you use it!

Source Download / Repository

You can browse or download the full source code for the completed app on the following GitHub repository:

https://github.com/martinlaukkanen/holidaysync