Working with SharePoint Recurring Meeting Workspaces

Tuesday, April 17, 2012

1

There seems to be sadly scant documentation out there on working with Meeting Workspaces through the SharePoint API.  Especially when it comes to recurring meetings, some (seemingly) basic pieces of meeting information can be very tricky to retrieve.  Here're a few that I've dealt with on a recent project:

  • What meeting instance is the user currently browsing?
  • Is the current workspace for a recurring meeting?
  • Given a specific date, when is the next recurrence?
  • What are the recurrences for the next X months?

For tips on working with list items in recurring Meeting Workspaces, check out my List Items in SharePoint Recurring Meeting Workspaces post.


What meeting instance is the user currently browsing?

You can get this by creating a SPMeeting object from SPContext.Current:

if (SPContext.Current != null)
    && SPContext.Current.Web != null
    && SPMeeting.IsMeetingWorkspaceWeb(SPContext.Current.Web))
{
  SPMeeting meeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  return meeting.InstanceId;
}


Is the current meeting workspace for a recurring meeting?

You can get this by creating a SPMeeting object from SPContext.Current and seeing if MeetingCount is -1:

if (SPContext.Current != null
    && SPContext.Current.Web != null
    && SPMeeting.IsMeetingWorkspaceWeb(SPContext.Current.Web))
{
  SPMeeting meeting = SPMeeting.GetMeetingInformation(SPContext.Current.Web);
  bool isRecurring = (meeting.MeetingCount == -1);
 
  return isRecurring;
}

What is the next meeting recurrence?  Or the recurrences for the next 6 months?

This one is quite tricky.  My initial thought was to query the hidden Meeting Series list, which keeps track of all the meeting instances in a workspace.

Unfortunately, an instance is only added to the Meeting Series list when a user browses to that instance for the first time.  If you create a new meeting workspace with 10 recurrences, Meeting Series will initially contain a single row, representing the entire meeting series.  When someone browses to say, the 3rd recurrence, that will then be added to Meeting Series.  And so forth.  This actually makes sense, because recurring meetings can be indefinite and the list can't be populated with infinite recurrences!

Poking around the OOB webparts and dlls, I found that SharePoint itself uses this obscure piece of CAML to retrieve future instances, ordered by start date:

<Where>
     <DateRangesOverlap>
         <FieldRef Name="EventDate" />
         <FieldRef Name="EndDate" />
         <FieldRef Name="RecurrenceID" />
         <Value Type="DateTime">
             <Month />
         </Value>
     </DateRangesOverlap>
</Where>
<OrderBy>
     <FieldRef Name="EventDate" />
</OrderBy>

This, combined with a couple key SPQuery properties will return all meeting instances within a specified month, regardless of whether they've been visited or not.  More often than not this query will return more items than the ItemCount for Meeting Series!

SPList list = SPContext.Current.Web.Lists["Meeting Series"];
 
SPQuery query = new SPQuery();
query.CalendarDate = new DateTime(2014, 04, 16);
query.Query = "";   // CAML query above
query.ExpandRecurrence = true;
query.MeetingInstanceId = (int)SPMeeting.SpecialInstance.AllButSeries;   
query.RowLimit = 50;
 
SPListItemCollection results = list.GetItems(query); 

The code above returns all meeting instances in the month specified by CalendarDate (April 2014).  The particular day doesn't matter.  The results may include the last week of March and first week of May.  ExpandRecurrence must be set to TRUE to include unvisited meeting instances.

Aside from <Month /> under <DateRangesOverlap>, the CAML query can also specify: <Now />, <Today />, <Week />, and <Year />.  Unfortunately, <Year /> seems to return completely non-sensical results.  I've tried it on multiple workspaces, and it never returns instances within the specified year like you'd expect.

So.  Back to the original questions.  Since <Year /> doesn't work, to find all instances within the next 6 months, I query one month at a time, incrementing Calendar date with each iteration.  Similarly, to find the next recurrence, I search forward one month at a time until an instance is found, like so:

public static int GetNextMeetingInstanceId(int currentInstanceId)
{
   int nextInstanceId = currentInstanceId;
 
   SPList list = SPContext.Current.Web.Lists["Meeting Series"];
 
   // Convert instanceID to DateTime
   DateTime searchDate = DateTime.ParseExact(currentInstanceId.ToString(), 
                           "yyyyMMdd", CultureInfo.InvariantCulture);
 
   // Search up to the next 12 months for the next instance
   for (int i = 0; i <= 12; i++)
   {
      DateTime monthToSearch= searchDate.AddMonths(i);
 
      // Retrieve instances for the month we're currently searching through, 
      // ordered by start date
 
      SPQuery query = new SPQuery();
      query.CalendarDate = monthToSearch;
      query.Query = ""; // CAML query above
      query.ExpandRecurrence = true;
      query.MeetingInstanceId = (int)SPMeeting.SpecialInstance.AllButSeries; 
      query.RowLimit = 50;
 
      SPListItemCollection results = list.GetItems(query);
 
      foreach (SPListItem instance in results)
      {
         int instanceId = (int)instance["InstanceID"];
         if (instanceId > currentInstanceId)
         {
            nextInstanceId = instanceId;
            return nextInstanceId;
         }
      }
   }
 
   return nextInstanceId;
}


1 comments:

Hi,

Thanks for this wonderful blog.. It gave me a better understanding about meeting workspaces. What i needed to accomplish was to "get the next meeting recurrence" and unfortunately the above code wasn't approved by Microsoft (Our QA provider).

I then made changes to the code as follows:

private static int GetNextMeetingInstanceId(int currentInstanceId, SPWeb web)
{
int nextInstanceId = currentInstanceId;

SPList list = web.Lists[ConstantsStrings.fieldName_MeetingSeries];

SPQuery query = new SPQuery();
DateTime searchDate = DateTime.ParseExact(currentInstanceId.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture); // Convert instanceID to DateTime
//query.CalendarDate = searchDate;
string _searchDate = Microsoft.SharePoint.Utilities.SPUtility.CreateISO8601DateTimeFromSystemDateTime(searchDate);
query.ViewFields = string.Format("", ConstantsStrings.fieldName_InstanceID);
query.ExpandRecurrence = true;
query.MeetingInstanceId = (int)SPMeeting.SpecialInstance.AllButSeries;
query.RowLimit = 1;
query.Query = string.Format(@"



{0}





", _searchDate);
SPListItemCollection coll = list.GetItems(query);
if (coll.Count > 0)
{
nextInstanceId = (int)coll[0][ConstantsStrings.fieldName_InstanceID];
}
return nextInstanceId;
}

And it worked like a charm. Hope this helps someone.
- Pagial

Post a Comment