Partager via


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!

Comments

  • Anonymous
    November 21, 2006
    I've received several emails from folks who have been successfully using my Outlook Calendar Provider

  • Anonymous
    December 05, 2006
    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.
  • Anonymous
    December 05, 2006
    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?

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

  • Anonymous
    December 08, 2006
    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)

  • Anonymous
    January 18, 2007
    "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)

  • Anonymous
    January 19, 2007
    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.

  • Anonymous
    January 26, 2007
    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

  • Anonymous
    January 26, 2007
    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.

  • Anonymous
    January 31, 2007
    I understand my troubles but how should I modify the GetListDescription method to work as Outlook Contact Provider ? Many thanks

  • Anonymous
    January 31, 2007
    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.

  • Anonymous
    January 31, 2007
    The comment has been removed

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

  • Anonymous
    September 07, 2007
    The comment has been removed

  • Anonymous
    September 21, 2007
    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

  • Anonymous
    October 09, 2007
    I would like to know if it's possible to made with post folders. I have an Sharepoint 2007 discussion list and I want to edit the default view to include more fields present in Sharepoint. Is it possible to this in same way as for contacts. Thanks

  • Anonymous
    October 17, 2007
    Francois, I'm not sure what you're asking. Are you asking if it's possible to use stssync with a Discussion List to display the items in your Outlook contacts folder, or are you asking how to create a custom view in SharePoint? If the former, I suppose it's possible as long as you handle the mapping from the different fields into fields that Outlook would expect to see for a contact. If the latter, then I'd point you to http://msdn.microsoft.com/sharepoint. Look for the documentation on CAML. You can create and edit SharePoint views with CAML, but it is not easy at all. It may be easier to use a tool such as SharePoint Designer or FrontPage. I'm not sure if this answers your question though.

  • Anonymous
    October 19, 2007
    Is there any way of putting the events in the main calendar? My impression is that this is needed if the consumer wants to sync with a mobile device.

  • Anonymous
    November 05, 2007
    To the best of my knowledge, that is correct, Stefan. And to answer your question, there's not a way to directly have the stssync put appointments into the calendar. Outlook creates a wrapped PST specifically for SharePoint synchronization. The only way you'd be able to do it is if you then have code that synchronizes between your calendar and the SharePoint events calendar in the PST. This is not an easy undertaking. If you just need the one-way sync, you may be able to just wipe/replace from a new calendar folder in your mailbox. That would be easier code to write, but only allows for one-way sync, and any changes made by the end user would be erased at the next sync.

  • Anonymous
    December 27, 2007
    Michael I didn't see any other person posting the request, so I will. Can you post your code for Calendar and Contacts into one ASP.NET 2.0? Thank you in advance, James

  • Anonymous
    April 08, 2008
    Many of you have been using the Custom Calendar Provider samples created by Stephen Toub and I: Stephen

  • Anonymous
    April 10, 2008
    Hi, got the web service running fine now.. but I have problem getting the Category field working for the contact. AddAttribute(node, Nothing, "ows_Category", Nothing, "Business") AddAttribute(node, Nothing, "ows_Categories", Nothing, "Business") but both doesn't seems to work..  does anyone know what is the correct name for the Category ows_ field for sharepoint? I am running XP Office 2003 thanks

  • Anonymous
    April 10, 2008
    Hi, got the web service running fine now.. but I have problem getting the Category field working for the contact. AddAttribute(node, Nothing, "ows_Category", Nothing, "Business") AddAttribute(node, Nothing, "ows_Categories", Nothing, "Business") but both doesn't seems to work..  does anyone know what is the correct name for the Category ows_ field for sharepoint? my os is XP with office 2003 thanks

  • Anonymous
    April 11, 2008
    Hi Wilson, remember that there are a fixed set of fields that are synchronized. If the field is not part of the original schema for the Contacts list, then it may not be automatically synchronized. The list of fields increases with Outlook 2007, but it's still fixed. Check out my latest blog entry where we have published exactly how to build these types of services. There may be more detail in the specification. (I haven't had time to read it yet myself.) :) http://blogs.msdn.com/pcreehan/archive/2008/04/08/stssync-for-the-masses-toub-s-revenge.aspx

  • Anonymous
    June 10, 2008
    The comment has been removed

  • Anonymous
    March 23, 2009
    I want to implement the outlook calnedar in C#. Can you please tell me how can i do this. thanks