Custom Calendar Providers for Outlook 2007

So if you were using Stephen Toub's Custom Calendar Provider sample for Outlook 2003, and you are testing out the awesome new Outlook 2007 features, you may have noticed that the code sample he provided doesn't work any more. Stephen is planning to update the article for 2007 eventually, but until then, here's the fix.

The GetList web service method is supposed to return a <Fields> node filled with <Field> nodes that describe each field in that list, including type information, display names, read-only properties, etc. In the sample above, Stephen is not returning the <Fields> node as a part of the response. The reason is that Outlook 2003 ignored this information when it was performing the sync. Outlook 2007 is a little more intelligent in this regard and it does need this information returned. It's not a big deal when linking to a SharePoint Events list, because SharePoint always returns that data. In an effort to keep the code simple, Stephen ignored it.

So the answer is that we need to add that information back in. So what you'll want to do is edit the WssListResponse.cs file as follows:

  1. Add a new private static method called AddFieldElement as follows:

      /// <summary>
      /// Adds a Field node to the Fields parent node
      /// </summary>
      /// <param name="parentDoc">XmlDocument parent doc used for creating the new element</param>
      /// <param name="parentNode">The parent Fields node to which to add the new Field element</param>
      /// <param name="type">Type attribute</param>
      /// <param name="colName">ColName attribute</param>
      /// <param name="name">Name attribute</param>
      /// <param name="displayName">DisplayName attribute</param>
      /// <param name="readOnly">ReadOnly attribute</param>
      /// <param name="hidden">Hidden attribute</param>
      /// <param name="fromBaseType">FromBaseType attribute</param>
      /// <returns></returns>
      private static XmlElement AddFieldElement(XmlDocument parentDoc,XmlNode parentNode,string type, string colName, string name, string displayName,bool readOnly, bool hidden, bool fromBaseType)
      {
       XmlElement fieldEl = parentDoc.CreateElement(null,"Field",_wssns);
       parentNode.AppendChild(fieldEl);

       if(type != null)  AddAttribute(fieldEl,null,"Type",null,type);
       if(colName != null)  AddAttribute(fieldEl,null,"ColName",null,colName);
       if(name != null)  AddAttribute(fieldEl,null,"Name",null,name);
       if(displayName != null) AddAttribute(fieldEl,null,"DisplayName",null,displayName);
       if(readOnly)   AddAttribute(fieldEl,null,"ReadOnly",null,"TRUE");
       if(hidden)    AddAttribute(fieldEl,null,"Hidden",null,"TRUE");
       if(fromBaseType)  AddAttribute(fieldEl,null,"FromBaseType",null,"TRUE");

       return fieldEl;
       
      }

  2. Modify the GetListDescription Method as follows:

  public static XmlNode GetListDescription(string title, Guid id)
  {
   // Create the response document
   XmlDocument doc = new XmlDocument();

   // Add a List element
   XmlNode list = doc.CreateElement(null, "List",  _wssns);
   doc.AppendChild(list);

   // Add attributes about the list to the List element
   AddAttribute(list, null, "DocTemplateUrl", null, "");
   AddAttribute(list, null, "DefaultViewUrl", null, "/Lists/" + id.ToString("N") + "/AllItems.aspx");
   AddAttribute(list, null, "ID", null, id.ToString("B"));
   AddAttribute(list, null, "Title", null, title);
   AddAttribute(list, null, "Description", null, title);
   AddAttribute(list, null, "ImageUrl", null, "/_layouts/images/itevent.gif");
   AddAttribute(list, null, "Name", null, id.ToString("N"));
   AddAttribute(list, null, "BaseType", null, "0");
   AddAttribute(list, null, "ServerTemplate", null, "106");
   AddAttribute(list, null, "Created", null, DateTime.MinValue.AddDays(2).ToString("s").Replace("T", " "));
   AddAttribute(list, null, "Modified", null, DateTime.MinValue.AddDays(3).ToString("s").Replace("T", " "));
   AddAttribute(list, null, "LastDeleted", null, DateTime.MinValue.AddDays(3).ToString("s").Replace("T", " "));
   AddAttribute(list, null, "Version", null, "0");
   AddAttribute(list, null, "Direction", null, "none");
   AddAttribute(list, null, "ThumbnailSize", null, "");
   AddAttribute(list, null, "WebImageWidth", null, "");
   AddAttribute(list, null, "WebImageHeight", null, "");
   AddAttribute(list, null, "Flags", null, "4096");
   AddAttribute(list, null, "ItemCount", null, "1"); // isn't used, so no point in recomputing size of list
   AddAttribute(list, null, "AnonymousPermMask", null, "");
   AddAttribute(list, null, "RootFolder", null, "/Lists/" + id.ToString("N"));
   AddAttribute(list, null, "ReadSecurity", null, "1");
   AddAttribute(list, null, "WriteSecurity", null, "1");
   AddAttribute(list, null, "Author", null, "1");
   AddAttribute(list, null, "AnonymousPermMask", null, "");
   AddAttribute(list, null, "EventSinkAssembly", null, "");
   AddAttribute(list, null, "EventSinkClass", null, "");
   AddAttribute(list, null, "EventSinkData", null, "");
   AddAttribute(list, null, "EmailInsertsFolder", null, "");
   AddAttribute(list, null, "AllowDeletion", null, "TRUE");
   AddAttribute(list, null, "AllowMultiResponses", null, "FALSE");
   AddAttribute(list, null, "EnableAttachments", null, "TRUE");
   AddAttribute(list, null, "EnableModeration", null, "FALSE");
   AddAttribute(list, null, "EnableVersioning", null, "FALSE");
   AddAttribute(list, null, "Hidden", null, "FALSE");
   AddAttribute(list, null, "MultipleDataList", null, "FALSE");
   AddAttribute(list, null, "Ordered", null, "FALSE");
   AddAttribute(list, null, "ShowUser", null, "TRUE");

   //Create Fields node
   XmlElement fieldsNode = doc.CreateElement(null, "Fields",  _wssns);
   list.AppendChild(fieldsNode);

   XmlElement tmpEl;

   //Append Fields
   tmpEl = AddFieldElement(doc,fieldsNode,"Counter","tp_ID","ID","ID",true,false,true);
   tmpEl = AddFieldElement(doc,fieldsNode,"Text","nvarchar1","Title","Title",false,false,true);
   tmpEl = AddFieldElement(doc,fieldsNode,"DateTime","tp_Modified","Modified","Modified",true,false,true);
   AddAttribute(tmpEl,null,"StorageTZ",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"DateTime","tp_Created","Created","Created",true,false,true);
   AddAttribute(tmpEl,null,"StorageTZ",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"User","tp_Author","Author","Created By",true,false,true);
   AddAttribute(tmpEl,null,"List",null,"UserInfo");
   tmpEl = AddFieldElement(doc,fieldsNode,"User","tp_Editor","Editor","Modified By",true,false,true);
   AddAttribute(tmpEl,null,"List",null,"UserInfo");
   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","tp_Version","owshiddenversion","owshiddenversion",true,true,true);
   AddAttribute(tmpEl,null,"SetAs",null,"owshiddenversion");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Attachments","tp_HasAttachment","Attachments","Attachments",true,false,true);
//   tmpEl = AddFieldElement(doc,fieldsNode,"ModStat","tp_ModerationStatus","_ModerationStatus","Approval Status",true,true,true);
//   AddAttribute(tmpEl,null,"CanToggleHidden",null,"TRUE");
//   AddAttribute(tmpEl,null,"Required",null,"FALSE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Note","ntext1","_ModerationComments","Approver Comments",true,true,true);
//   AddAttribute(tmpEl,null,"CanToggleHidden",null,"TRUE");
//   AddAttribute(tmpEl,null,"Sortable",null,"FALSE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Computed",null,"Edit","Edit",true,false,true);
//   AddAttribute(tmpEl,null,"Sortable",null,"FALSE");
//   AddAttribute(tmpEl,null,"Filterable",null,"FALSE");
//   AddAttribute(tmpEl,null,"AuthoringInfo",null,"(link to edit item)");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Computed",null,"LinkTitleNoMenu","Title",true,false,true);
//   AddAttribute(tmpEl,null,"AuthoringInfo",null,"(linked to item)");
//   AddAttribute(tmpEl,null,"Dir",null,"");
//   AddAttribute(tmpEl,null,"DisplayNameSrcField",null,"Title");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Computed",null,"LinkTitle","Title",true,false,true);
//   AddAttribute(tmpEl,null,"AuthoringInfo",null,"(linked to item with edit menu)");
//   AddAttribute(tmpEl,null,"DisplayNameSrcField",null,"Title");
//   AddAttribute(tmpEl,null,"ClassInfo",null,"Menu");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Computed",null,"SelectTitle","Select",true,true,true);
//   AddAttribute(tmpEl,null,"AuthoringInfo",null,"(web part connection)");
//   AddAttribute(tmpEl,null,"Dir",null,"");
//   AddAttribute(tmpEl,null,"Sortable",null,"FALSE");
//   AddAttribute(tmpEl,null,"CanToggleHidden",null,"TRUE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","tp_InstanceID","InstanceID","InstanceID",true,true,true);
//   AddAttribute(tmpEl,null,"Sortable",null,"TRUE");
//   AddAttribute(tmpEl,null,"Filterable",null,"TRUE");
//   AddAttribute(tmpEl,null,"Min",null,"0");
//   AddAttribute(tmpEl,null,"Max",null,"99991231");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Number","tp_ItemOrder","Order","Order",false,true,true);
   tmpEl = AddFieldElement(doc,fieldsNode,"Guid","tp_Guid","GUID","GUID",true,true,true);
   tmpEl = AddFieldElement(doc,fieldsNode,"DateTime","datetime1","EventDate","Begin",false,false,true);
   AddAttribute(tmpEl,null,"Format",null,"DateTime");
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   AddAttribute(tmpEl,null,"Required",null,"TRUE");
   AddAttribute(tmpEl,null,"Filterable",null,"FALSE");
   AddAttribute(tmpEl,null,"FilterableNoRecurrence",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"DateTime","datetime2","EndDate","End",false,false,true);
   AddAttribute(tmpEl,null,"Format",null,"DateTime");
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   AddAttribute(tmpEl,null,"Filterable",null,"FALSE");
   AddAttribute(tmpEl,null,"FilterableNoRecurrence",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Note","ntext2","Description","Description",false,false,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   AddAttribute(tmpEl,null,"Sortable",null,"False");
   tmpEl = AddFieldElement(doc,fieldsNode,"Text","nvarchar","Location","Location",false,false,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Recurrence","bit1","fRecurrence","Recurrence",false,false,false);
   AddAttribute(tmpEl,null,"DisplayImage",null,"recur.gif");
   AddAttribute(tmpEl,null,"HeaderImage",null,"recur.gif");
   AddAttribute(tmpEl,null,"ClassInfo",null,"Icon");
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   AddAttribute(tmpEl,null,"NoEditFormBreak",null,"TRUE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"CrossProjectLink","bit2","WorkspaceLink","Workspace",false,false,false);
//   AddAttribute(tmpEl,null,"Format",null,"EventList");
//   AddAttribute(tmpEl,null,"DisplayImage",null,"mtgicon.gif");
//   AddAttribute(tmpEl,null,"HeaderImage",null,"mtgicnhd.gif");
//   AddAttribute(tmpEl,null,"ClassInfo",null,"Icon");
//   AddAttribute(tmpEl,null,"Title",null,"Meeting Workspace");
//   AddAttribute(tmpEl,null,"Filterable",null,"TRUE");
//   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","int1","EventType","Event Type",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Guid","uniqueidentifier1","UID","UID",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"DateTime","datetime3","RecurrenceID","Recurrence ID",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   AddAttribute(tmpEl,null,"CalType",null,"1");
   AddAttribute(tmpEl,null,"Format",null,"ISO8601Gregorian");
   tmpEl = AddFieldElement(doc,fieldsNode,"Boolean","bit3","EventCanceled","Event Canceled",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","int2","Duration","Duration",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Note","ntext3","RecurrenceData","RecurrenceData",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","int3","TimeZone","TimeZone",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
   tmpEl = AddFieldElement(doc,fieldsNode,"Note","ntext4","XMLTZone","XMLTZone",false,true,false);
   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"Integer","int4","MasterSeriesItemID","MasterSeriesItemID",false,true,false);
//   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
//   tmpEl = AddFieldElement(doc,fieldsNode,"URL","nvarchar3","Workspace","WorkspaceUrl",false,true,false);
//   AddAttribute(tmpEl,null,"Sealed",null,"TRUE");
//   AddAttribute(tmpEl,null,"ColName2",null,"nvarchar4");

   // Return the XML
   return doc;
  }

You'll notice that I have commented out a few lines in this method. There are probably more lines that can be commented out. We're not returning all the information that SharePoint would return, but it's not all useful to Outlook, so why clog the ethernet lines with useless data?!

Anyway, once you've made those modifications, you can recompile the web application and you should be golden!