Understanding the SharePoint calendar and how to export it to iCal format
Introduction
One of the challenges of accessing SharePoint calendars via the object model is
that there are so many different types of events – normal events, all-day
events, recurring events, recurrence exceptions, and deleted instances – and
they all look more or less the same!
In this post, I will shed some light on how to work with calendar events,
describe the subtleties of recurrences, and put that knowledge to work exporting
events to the
RFC 2445 iCalendar format.
First, before we can do anything with a Calendar list, we need a way to
distinguish between different event types.
Distinguishing
between calendar item types
Calendar items in SharePoint fall into several categories.
Single events are those which don’t repeat and only appear once on the
calendar, while recurring events may show up any number of times depending on
the recurrence pattern selected by a user.
Either type of event may have a defined start and end time, or it may be
an all-day event which lasts from midnight to 11:59 PM.
Furthermore, individual instances of a recurring event may be deleted or edited,
creating a new event which takes its place.
The event created by a deleted instance instructs the calendar not to
render that day’s instance of the recurring event.
Calendar events can be distinguished by looking at the fRecurrence, fAllDayEvent,
and EventType field values:
|
|
|
|
|
|
|
|
An |
False |
False |
0 |
|
|
An |
False |
True |
0 |
|
|
An |
True |
False |
1 |
|
|
Same as |
True |
True |
1 |
|
|
Created |
True |
False |
4 |
|
|
Same as |
True |
True |
4 |
|
|
Created |
True |
False |
3 |
|
|
Same as |
True |
True |
3 |
Understanding
recurring events
Recurring events have a number of subtleties not found in single events.
Most importantly, one recurring event item expands into any number of
recurring event instances when it is rendered in the calendar view.
Recurring events also make use of fields left empty in single events.
Certain fields are interpreted differently depending on whether the item in
question is a recurring event or a single event:
|
|
|
|
|
|
Start |
Start |
|
|
End |
End |
|
|
The |
The |
Similarly, exceptions and deleted instances have fields which relate the event
back to the parent recurring event:
|
|
|
|
|
The ID |
|
|
The |
Once you’ve determined that you’re working with a recurring event, you’ll likely
want to work with individual instances of the recurrence.
Recurrence
patterns and expanding events
Recurring events store the pattern used to display instances of the recurring
event as XML in the RecurrenceData field.
While this data is best used in a read-only fashion, the following are
examples of the patterns you may encounter:
|
|
|
|
|
<firstDayOfWeek>su</firstDayOfWeek>
<repeatForever>FALSE</repeatForever>
|
|
|
<firstDayOfWeek>su</firstDayOfWeek>
<windowEnd>2007-05-31T22:00:00Z</windowEnd>
|
|
|
<firstDayOfWeek>su</firstDayOfWeek>
<repeatForever>FALSE</repeatForever>
|
|
|
<firstDayOfWeek>su</firstDayOfWeek>
<repeatInstances>10</repeatInstances>
|
Thankfully, you don’t need to parse this XML yourself to get the actual
instances of the recurring event.
Instead, you can use the SharePoint object model to expand recurring events
during a given month:
// Get the Events list
SPSite
site = new SPSite("http://localhost");
SPWeb
web = site.RootWeb;
SPList
calendarList = web.Lists["Calendar"];
// Construct a query that expands recurring events
SPQuery
query = new
SPQuery();
query.ExpandRecurrence = true;
query.Query = "<Where><DateRangesOverlap><FieldRef
Name=\"EventDate\" /><FieldRef Name=\"EndDate\" /><FieldRef Name=\"RecurrenceID\"
/><Value Type=\"DateTime\"><Month /></Value></DateRangesOverlap></Where>";
// Look forward from the beginning of the current month
query.CalendarDate = new
DateTime(DateTime.Now.Year,
DateTime.Now.Month, 1);
// Returns all items (including recurrence instances) that
// would appear in the calendar view for the current month
SPListItemCollection
calendarItems = calendarList.GetItems(query);
foreach
(SPListItem item
in calendarItems)
{
Console.WriteLine(item["Title"]
+ ": starts "
+ item["EventDate"].ToString() +
" and ends "
+ item["EndDate"].ToString());
}
Note that after expansion, each instance of a recurring event has the same ID as
the recurring event that produced it.
In other words, a recurring event with ID 7 will appear as many recurring
event instances in the code above, each with an ID of 7.
Putting it to
work
It’s always easier to learn from an example than from dry technical
descriptions, and to that end I’ve developed a SharePoint Solution which uses
the functionality I’ve described above.
When installed and activated, it adds an “Export Calendar to iCal Format”
button to the Actions menu of every Calendar list in the site collection as
pictured below.
To convert events to RFC 2445 iCalendar format, I loop through each event,
translating recurrence patterns into iCalendar format and associating exceptions
and deleted instances with the recurring event they came from.
The majority of the code deals with interpreting the recurrence pattern;
however, there is also a significant amount of code to translate SharePoint
timezone information as well.
You can download the iCalendar Exporter solution and source code, which have
been released as part of the Community Kit for SharePoint, right here.
Launch Visual Studio, take a look at my source code, and have fun
developing custom solutions with the SharePoint calendar! Please leave a comment
if you have any questions or want to showcase an innovative solution that you've
built.
.postsub h2{
font-size: 1.4em;
margin: 0px;
padding: 5px 0em 10px;}
.post h2{
font-size: 1.2em;
margin: 0px;
padding: 0em 0em 0.5em;}
h2 {
margin-top : 10px;
font-size : 1em;}
.postsub a{
font-weight: bold;
}
a:link {
text-decoration: none;
color: #006bad;}
a:link {
text-decoration: none;
color: #006bad;}
h1 {
margin : 0px;
font-size : 1.5em;}
p {
margin : 10px 0px;}
.postsub img{
padding: 5px 5px 5px 0px;
margin: 5px 5px 5px 0px;}
.post img{
padding: 5px 5px 5px 0px;
margin: 5px 5px 5px 0px;}