Tuesday, March 29, 2011

Displaying Default Resources on the Group Calendar

[IMPORTANT NOTE]  It has been pointed out that some of the forum users who have tried this solution cannot get it to work. I forgot to mention that I'm using JQuery-1.5.1.min.js, which is why you see $() references in the solution. You could use substitute the JQuery variable "ID" for the actual ID of the Web Part Zone, e.g. "WPQ1", but I would suggest uploading the JQuery min file to your Style Library and using the following reference:

<script src="/Style%20Library/JQuery-1.5.1.min.js" type="text/javascript">

Sorry for the miscommunication.

It's been awhile since I've posted any ground-breaking new discoveries, so I thought I would pass on a bit of info about the Group Calendar and displaying default groups. Disclaimer: this is still in beta, and not running on a production site yet.

I've stumbled across a few blog posts about the group calendar, and the need to display a default set of resources on the calendar. The OOB functionality requires the user to select a resource/resource group before items show up on the calendar. Being the stubborn developer that I am, I decided that I would come up with a fix for this based on a specific requirement from one of our Business Units. This solution works for Resource Groups, but I'm sure it can be modified to easily display resources or people, depending on your requirements.

Looking at the calendar in action, you will quickly realize that the calendar uses the picker dialog for selecting resources, and AJAX to render selected items on the calendar. So, how does one go about calling the necessary functions to populate the calendar? Magic! ;-)

I started by viewing the source of the parent page to get an idea of what was actually taking place when the picker dialog closes. Here is the event you are looking for:

Great! But how does this help you? You may notice that the dialog result is xml, and that setting the result requires a call to SP.UI.ApplicationPages.CalendarSelector.instance().getSelector(type, id). After a bit of searching in the SP.UI.Calendar.debug.js file, I found some information around the ResourceSelector and retrieving the selector type. If you do a search of the source code in your view source window for "add_resource", you'll see that the "Add Resources" link attribute "evtid" is "add_resource". Searching through the SP.UI.Calendar.debug.js on "add_resource" yielded this handy bit of information:

$p0.$z('add_resource', Function.createDelegate(this, function ($p1_0) {
            __spPickerDialogFunc(1, this.$w_1, true);

This shows that the type is 1, and add = true. Once I had that information, I just needed to track down the XML data being passed from the picker dialog. That's where Fiddler comes in. If you aren't familiar with Fiddler, you should be. I won't go into too much detail, but will give you the gist below.

First, I opened an instance of fiddler and launched IE, browsing to my home page (which contains my group calendar). I then clicked the "Add Resources" link, and added a Resource Group called "All Computers" and clicked "OK".

You'll see below that the events now display for each resource in the calendar.

I then went into fiddler, and took a look at the final request from Picker.aspx, and began scanning the TextView for any information that might be helpful.

And there it is, in an ecma encoded string. The full XML return value from the dialog window. I copied the ret variable and began scripting a new function for the home page.

NOTE:  If you don't already know this, you can build your javascript into an html or txt file and upload it to a document library for reference in a Content Editor Web Part. I've found that if you just paste the javascript directly into the Content Editor, SharePoint has a nasty habit of correcting it for you, which can really break your code. I typically put my text/html files into a folder called "ctrl" in the Style Library. That way, if I reuse the code on multiple pages, I can update one single file and call it a day.

Here is my final script to populate the calendar:

<script type="text/javascript" src="/Style Library/Jquery.min.js"></script>
<script type="text/javascript">

function _setDefaultResources() {
        var el = $(".ms-acal-rootdiv");
        var xml = "\u003cEntities Append=\u0022True\u0022 Error=\u0022\u0022 DoEncodeErrorMessage=\u0022True\u0022 Separator=\u0022;\u0022 MaxHeight=\u00223\u0022\u003e\u003cEntity Key=\u0022All Computers\u0022 DisplayText=\u0022All Computers\u0022 IsResolved=\u0022True\u0022 Description=\u0022\u0022\u003e\u003cExtraData\u003e\u003cArrayOfDictionaryEntry xmlns:xsi=\u0022http:\u002f\u002fwww.w3.org\u002f2001\u002fXMLSchema-instance\u0022 xmlns:xsd=\u0022http:\u002f\u002fwww.w3.org\u002f2001\u002fXMLSchema\u0022\u003e\u003cDictionaryEntry\u003e\u003cKey xsi:type=\u0022xsd:string\u0022\u003eResourceMembers\u003c\u002fKey\u003e\u003cValue xsi:type=\u0022xsd:string\u0022\u003e1;#Computer 01;#2;#Computer 02;#3;#Computer 03;#4;#Computer 04;#5;#Computer 05;#6;#Computer 06;#7;#Computer 07;#8;#Computer 08;#9;#Computer 09;#10;#Computer 10;#11;#Computer 11;#12;#Computer 12;#13;#Computer 13;#14;#Computer 14;#15;#Computer 15\u003c\u002fValue\u003e\u003c\u002fDictionaryEntry\u003e\u003cDictionaryEntry\u003e\u003cKey xsi:type=\u0022xsd:string\u0022\u003eSPResourceId\u003c\u002fKey\u003e\u003cValue xsi:type=\u0022xsd:string\u0022\u003e30\u003c\u002fValue\u003e\u003c\u002fDictionaryEntry\u003e\u003cDictionaryEntry\u003e\u003cKey xsi:type=\u0022xsd:string\u0022\u003ePrincipalType\u003c\u002fKey\u003e\u003cValue xsi:type=\u0022xsd:string\u0022\u003eResourceGroup\u003c\u002fValue\u003e\u003c\u002fDictionaryEntry\u003e\u003c\u002fArrayOfDictionaryEntry\u003e\u003c\u002fExtraData\u003e\u003cMultipleMatches \u002f\u003e\u003c\u002fEntity\u003e\u003c\u002fEntities\u003e";

        var sel = SP.UI.ApplicationPages.CalendarSelector.instance().getSelector(1, $(el).attr('ctxid'));
        sel.selectEntities(xml, true);
ExecuteOrDelayUntilScriptLoaded(_setDefaultResources, "sp.ribbon.js");

A few important items to note... if you have multiple calendars on a single page, you will need to find the id of the calendar you are populating. The ctxid is actually a dynamically populated id (via javascript) of the web part context, which in this case is WPQ2. Make sure that you are using the ExecuteOrDelayUntilScriptLoaded( function, "sp.ribbon.js") call. The context id of the web part is not assigned until sp.ribbon.js is fully loaded. Once you have all that, it will work like a charm.

As always, if you have any questions, feel free to ping me. Hopefully, this will save you some time and make you look like a rockstar!