Second Life of a Hungarian SharePoint Geek

March 28, 2013

Dynamically populating Group Calendars, Part I – the basics

Filed under: Calendar, JavaScript, jQuery, SP 2010 — Tags: , , , — Peter Holpar @ 13:54

A few month ago I was working on a prototype of an internal SharePoint application to make it easier to HR and project managers to track and administer the status (like holiday, sick leave) of the employees.

My plan included a group calendar to administer the employee status (note: this feature seems to be already obsolete in SP 2013, see section Group Work site template and Group Work solution on TechNet). User should be able to choose a project from the ribbon, and the calendars of the project members would be displayed in the group calendar view. The ribbon extension itself was not difficult (I will discuss it in the next part of this post), however the dynamic refresh of the group calendar UI was really a bit challenging. The main topic of this post is how to add / remove users to / from the group calendar.

The only post I found on a similar issue is related to resources (I recommend you to read that post first, as I don’t repeat all of the details discussed there!), but it provided at least a good starting point for me, and the discovery of the JavaScript libraries and a lot of fiddlering began…

I won’t describe here all the steps I followed, but rather the most important results.

Note: that the methods and properties below are not officially documented, and using them from your custom code is not supported. Be aware, that they can be changed due to Service Packs or other updates without any prior notice, immediately breaking solutions build upon these techniques, so use them at your own risks.

The bulk of the functionality related to the group calendar is encapsulated into the SP.UI.ApplicationPages.Calendar.js (see the debug version: SP.UI.ApplicationPages.Calendar.debug.js). If not mentioned otherwise, all of the described JS objects can be found in this file.

The SP.UI.ApplicationPages.ResolveEntity (defined in SP.js / SP.debug.js) is primarily a data container to hold information on a calendar entity resolved by SharePoint.

When you add a new user, group or resource entity to the group calendar (for example using the People Picker, either the text box or the dialog), or add / edit / delete events, the calendar control sends an asynchronous request to the server to resolve the entity to be able to refresh the UI, and the “Loading calendar…” message is displayed.

image

To provide the async communication between the calendar control on the webpage and the data on the server side, the CalendarService.ashx ASP.NET Web Handler is used. For example, if we are adding the calendar of the CONTOSO Administrator to the group calendar the following data is send through a POST request to /_layouts/CalendarService.ashx:

cmd=query&listName=ecd80beb-7ba7-4ad0-810a-3e2a95f07c30&viewName=e7d89ac0-7bd6-42b7-9ee5-de7e8b4773bb&dataSourceId=00000000-0000-0000-0000-000000000000&viewType=weekgroup&entity=1;#CONTOSO\administrator;Administrator@contoso.com&selectedDate=&options=1

image

The response is a JSON representation of the events for the Administrator (assuming this user has a single meeting as illustrated above):

[{"Options":1,"Table":null,"DatePicker":null,"Dates":null,"RangeJDay":null,"Navs":null,"Items":{"Data":[[0,1,2,150563,150563,3,3,4,5,5,0,60,0,0,0,6,7]],"Strings":["8","Admin event","Office","3/25/2013","5:00 am","6:00 am","","0x7fffffffffffffff"]}}]

or as visualized by Fiddler:

image

Parameters sent to the CalendarService.ashx should be straightforward. The parameter entity is the key of the entity (in this case 1;#CONTOSO\administrator;Administrator@contoso.com) to be resolved, we have this value from the People Picker. I don’t discuss now the process, how the People Picker gets this values when resolving users asynchronously, it would worth another post.

See the get_key function of the ResolveEntity object to understand how the key for different entity types are built up. For example, in case of a user having an e-mail address the key ($8_0 property of the ResolveEntity) looks like this:

this.$8_0 = ‘1;#’ + this.accountName + ‘;’ + this.email;

Compare this pattern with the value we used above for CONTOSO\administrator.

The entityType property of the ResolveEntity defines the type of entity. Supported values are:

SP.UI.ApplicationPages.ResolveEntity.typE_EVENT = ‘0’;
SP.UI.ApplicationPages.ResolveEntity.typE_USER = ‘1’;
SP.UI.ApplicationPages.ResolveEntity.typE_RESOURCE = ‘2’;
SP.UI.ApplicationPages.ResolveEntity.typE_EXCHANGE = ‘3’;

The value typE_USER means either a user or a group, the isGroup property of the ResolveEntity object makes a difference between user and group subtypes.

How to add entities to the calendar?

We should use the SP.UI.ApplicationPages.CalendarSelector (defined in SP.js / SP.debug.js) and the SP.UI.ApplicationPages.RibbonCalendarSelector (defined in SP.UI.ApplicationPages.Calendar.js / SP.UI.ApplicationPages.Calendar.debug.js) objects to achieve this goal.

// calSel is of tpye SP.UI.ApplicationPages.CalendarSelector
var calSel = SP.UI.ApplicationPages.CalendarSelector.instance();
// sel is of tpye SP.UI.ApplicationPages.RibbonCalendarSelector
var sel = calSel.getSelector(1, scopeKey);
sel.selectEntities(entitiesXml, false);

By calling the getSelector function the the fix value of 1 means a user entity type, and scopeKey is the context identifier for the calendar, and can be extracted from the page content when working with jQuery:

var calRootDiv = jQuery(".ms-acal-rootdiv");
var scopeKey = jQuery(calRootDiv).attr(‘ctxid’);

The entities defined by the entitiesXml will be appended to the existing list of items in the calendar. The format of the XML is shown for resources in the original post from Thomas Zepeda McMillan, and will be further investigated for user entities in my forthcoming post.

How to remove entities from the calendar?

You can think that setting the Append attribute of the Entities node of the XML we pass as parameter to the selectEntities function to a value to False has the effect that the new entities would be not appended to the existing list of entities, but the former entities would be cleared first, however, based on my experience, that is not the case. So we have to find a way to remove existing entities first.

As a first step, we could lookup the SP.UI.ApplicationPages.CalendarContainer for the specific scope (the SP.UI.ApplicationPages.CalendarInstanceRepository object is defined in SP.js / SP.debug.js):

var calCont = SP.UI.ApplicationPages.CalendarInstanceRepository.lookupInstance(scopeKey);

Using this CalendarContainer we can get a reference to the SP.UI.ApplicationPages.EntityPaginator:

var entPag = calCont.$a_1;

Entities displayed on the calendar are stored as an  Array of SP.UI.ApplicationPages.ResolveEntity objects:

var arr = entPag.$1y_1;
// just test: display some of the properties
alert(arr[0].entityType + ‘, ‘ + arr[0].get_key() + ‘, ‘ + arr[0].displayName + ‘, ‘ + arr[0].accountName);

The $AH function of the EntityPaginator removes the item with the specified index from the array plus refreshes the UI. For example, to remove first entity we should call:

entPag.$AH(0);

To remove all of the items, we should iterate through the array:

while(arr.length > 0) {
  entPag.$AH(0);
}

The $5P_1 method of EntityPaginator notifies all event handlers of "pagechanged" to force a control refresh. We should be able to remove all of the entities via clearing the Array content and call the $5P_1 method to refresh the UI directly as:

Array.clear(arr); // or Array.clear(entPag.$AH);
entPag.$5P_1();

however I found that this method – although might seem OK at first sight – is not working perfectly, especially when one would like to page in calendar between weeks.

Handling calendar paging / refresh events

Although not strictly related to the topic, it may be useful to know, that we can subscribe our custom event handlers for such events.

Using the add_$9x and the remove_$9x functions of the EntityPaginator can we inject (and remove) our event handlers for calendar refresh events (like paging):

entPag.add_$9x(Function.createDelegate(null, function() { alert(‘paged’); } ));

or alternatively we can apply a direct approach like this one:

function pageChanged() {
  alert(‘page changed’);
}

entPag.get_events().addHandler(‘pagechanged’, pageChanged);

Next steps

In this post I discussed the fundamentals we need to fulfill the original goal to create a dynamic group calendar view. In the next post I show you how to develop the ribbon extension that utilizes the methods described now.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: