Custom Calendar Providers for Outlook 2007

Custom Calendar Providers for Outlook 2007

  • Comments 26

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:
  2.   /// <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;
       
      }

  3. 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!

  • I've received several emails from folks who have been successfully using my Outlook Calendar Provider

  • I made the changes listed, and it still works fine in Outlook 2003, but in Outlook 2007 I still get:

    Task 'SharePoint' reported error (0x8000FFFF) : 'An error occurred either in Outlook or Windows SharePoint Services. Contact the Windows SharePoint Services site administrator.'

    The wss log gives a fairly unhelpful error of:

     <?xml version="1.0" ?>

    - <errors>

     <error>An error occurred either in Outlook or Windows SharePoint Services. Contact the Windows SharePoint Services site administrator.</error>

     </errors>

    I have emailed you a more verbose "bug report" and am hopeful you will be able to assist.

  • Nigel, That's the same error you get without the Fields node added at all. My guess is that Outlook is expecting your GetList response to be formatted slightly differently.

    I haven't received your email yet, so I'm not sure what your "bug report" contains.

    Are you implementing the Application event log sample? Does the View Events link work?

  • Can you please use the contact form to send your "bug report"? I did not receive your email.

  • It occurred to me that Outlook 2007 was launched in November, so there was likely a later version than my beta 2 build 4017.  I am now running with build 4518 and am pleased to say that your changes work :o)  

    Not only do they work, but I was able to apply them to Stephen Toub's OutlookContactProvider too, and am serving up my own custom data.

    I'm told Outlook 2007 supports writing to WSS lists, so how do you specify that the contacts/events folder is writable, which presumably would then show the outlook edit form, and result in calls to UpdateListItems().  At the moment if I try to make changes Outlook tells me "This SharePoint list is read-only in Outlook".

    Thanks for your help thus far :o)

  • "The GetList web service method is supposed to return a <Fields> node filled with <Field> nodes"

    Where can you see the documentation about what is supposed to be returned ?

    I am trying to make the Contact list work in Outlook 2007 but fails (I do not get any error message, but simply never sees any data coming into outlook from my web service, which is based on the outlook 2003 example at http://blogs.msdn.com/toub/archive/2004/12/22/330125.aspx ).

    How can I see what kind of data that actually gets sent when you are using a real sharepoint contact list ?

    (it works without problem so if I can see the data being sent back and forth, then maybe I can see if I will need some "Fields" elements for the contacts also)

  • What I did for the example above was that I called the GetList web service with javascript, and got the response back and examined it. I just used a standard Events list with no added columns from a standard Team site. You could probably do the same thing with a standard Contacts list.

  • Following you example I can get it working with Office 2007 but I still have a problem:

    In Office 2003 I can see all contact info

    In Office 2007 I can see anly the FullName filed

    Can you help me?

    Many thanks.

    Claudio

  • Claudio, You realize the example above is for a custom Calendar provider, right? For a contacts list, obviously the Fields you would return would be different.

  • I understand my troubles but how should I modify the GetListDescription method to work as Outlook Contact Provider ?

    Many thanks

  • You will have to figure that out in a way that makes sense for what you're trying to do. You could extend that method to take an additional parameter that tells you what type of list you are asking for and then construct the XML data based on that, or you could just store the XML for the different types of lists separately in an XML file, load them up and return them. This was just a hard-coded sample to show you the method to use in taking advantage of the stssync capabilities of Outlook. It's up to you to run with it and test it out.

    If you're asking how to get the list data for a Contacts list, just call the GetList web service on a standard contacts list in SharePoint and observe the return value. SharePoint is going to return all the data about that list including field information and View information. At this point, it's not important to include the View information, because Outlook doesn't care about it. In fact, the reason that I created this post was that in Outlook 2003, it didn't even care about the Fields collection, it just assumed certain fields were present. Outlook 2007 got a little smarter about it and started paying attention to the Fields that GetList returns. That's why we needed to add that node into the response that our web service is returning to include the Fields node.

    Hope that helps.

  • Thanks for this write up. For the guy who wanted the contacts fields, here are the defaults:

    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, "Guid", "tp_Guid", "GUID", "GUID", true, true, true);

    if (!isCalendar)

    {

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar3", "FirstName", "First Name", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar5", "FullName", "Full Name", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar6", "Email", "E-mail Address", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar8", "Company", "Company", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar9", "JobTitle", "Job Title", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar10", "WorkPhone", "Work Phone", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar11", "HomePhone", "Home Phone", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar12", "CellPhone", "Mobile Phone", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar13", "WorkFax", "Fax Number", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Note", "ntext2", "WorkAddress", "Address", false, false, false);

    AddAttribute(tmpEl, null, "NumLines", null, "2");

    AddAttribute(tmpEl, null, "Sortable", null, "FALSE");

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar14", "WorkCity", "City", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar15", "WorkState", "State", false, false, false);

    AddAttribute(tmpEl, null, "NumLines", null, "2");

    AddAttribute(tmpEl, null, "Sortable", null, "TRUE");

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar16", "WorkZip", "Postal Code", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "Text", "nvarchar17", "WorkCountry", "Country", false, false, false);

    tmpEl = AddFieldElement(doc, fieldsNode, "URL", "nvarchar18", "WebPage", "Web Page", false, false, false);

    AddAttribute(tmpEl, null, "ColName2", null, "nvarchar19");

    tmpEl = AddFieldElement(doc, fieldsNode, "Note", "ntext3", "Comments", "Notes", false, false, false);

    AddAttribute(tmpEl, null, "Sortable", null, "FALSE");

    }

  • Thanks, James, for taking the time to do the leg work on this one!

  • Firstly, thank you for the great code!

    What are the chances of investigating for us the minimal requirements for reading contacts into outlook using the syssync 1.2 protocol as opposed to this 1.0 method. (i'd LOVE to have two way sync happening)

    I have done everything i can to handcraft the xml copied from a sample web request from a sharepoint 2007 site. Cant seem to get it happening (GetList seems to be the first issue in replicating).

    Am i asking too much?

  • Thanks, all of you for your work, that helped a lot.

    I combined Calendar and Contacts into one ASP.NET 2.0 application and added the tweaks for OL 2007. Tested with 2003 went perfectly OK, test with 2007 is still a todo. I can post the code, if there is interest but I'd like to add a couple of aditional calendar fields. Has anyone found the definitions for the 'show as' (Free, Busy, ...) and Category (I want to set the colors)?

    Would be great, Michael

Page 1 of 2 (26 items) 12