private string CalendarEventJSON(SharepointCalendarEvent ce) { StringBuilder returnMe = new StringBuilder(); // Create some friendly text to use in the dashboard string StartDateFriendly = ce.EventStart.DayOfWeek.ToString().Substring(0, 3) + ", " + ce.EventStart.ToString("MMM dd"); string EndDateFriendly = ce.EventEnd.DayOfWeek.ToString().Substring(0, 3) + ", " + ce.EventEnd.ToString("MMM dd"); double DaysUntil = ce.EventStart.Subtract(DateTime.Today).TotalDays; if ((DaysUntil > 0.0) && (DaysUntil < 1.0)) { StartDateFriendly = "Today"; } if ((DaysUntil > 1.0) && (DaysUntil < 2.0)) { StartDateFriendly = "Tomorrow"; } returnMe.Append("{ "); returnMe.Append("\"startdate\" : \"" + ce.EventStart.ToShortDateString() + "\","); returnMe.Append("\"startdatefriendly\" : \"" + StartDateFriendly + "\","); returnMe.Append("\"daysuntil\" : \"" + DaysUntil.ToString() + "\","); returnMe.Append("\"starttime\" : \"" + ce.EventStart.ToShortTimeString() + "\","); returnMe.Append("\"enddate\" : \"" + ce.EventEnd.ToShortDateString() + "\","); returnMe.Append("\"endtime\" : \"" + ce.EventEnd.ToShortTimeString() + "\","); returnMe.Append("\"enddatefriendly\" : \"" + EndDateFriendly + "\","); returnMe.Append("\"totalhours\" : \"" + ce.Duration.Hours.ToString() + "\","); returnMe.Append("\"totaldays\" : \"" + ce.Duration.Days.ToString() + "\","); returnMe.Append("\"title\" : \"" + ce.Title + "\","); returnMe.Append("\"location\" : \"" + ce.Location + "\","); returnMe.Append("\"description\" : \"" + ce.Description + "\","); returnMe.Append("\"allday\" : \"" + ce.AllDay + "\""); returnMe.Append(" }"); return(returnMe.ToString()); }
private static List <SharepointCalendarEvent> ParseSharepointList(ClientContext sharepointContext, List sharepointList) { List <SharepointCalendarEvent> returnMe = new List <SharepointCalendarEvent>(); List <SharepointCalendarEvent> deletedRecurringEvents = new List <SharepointCalendarEvent>(); List <SharepointCalendarEvent> recurringEvents_Unexpanded = new List <SharepointCalendarEvent>(); CamlQuery query = CamlQuery.CreateAllItemsQuery(); Microsoft.SharePoint.Client.ListItemCollection sharepointListItems = sharepointList.GetItems(query); sharepointContext.Load(sharepointListItems); sharepointContext.ExecuteQuery(); foreach (ListItem item in sharepointListItems) { try { // We need to deal with "Deleted" events // These are events that cancel out existing events, if the events are recurring // Deal with recurring events, somehow string title = string.Empty; if (item.FieldValues.ContainsKey("Title")) { if (!string.IsNullOrEmpty((string)item["Title"])) { title = item["Title"].ToString(); } } ; string location = string.Empty; string description = string.Empty; if (item.FieldValues.ContainsKey("Description")) { if (!string.IsNullOrEmpty((string)item["Description"])) { // The description will almost always contain HTML, which we can't include in the JSON file or it won't validate // For now, we dont need the description // In the future perhaps we can strip out the HTML tags somehow description = Helpers.SanitizeForJSON(item["Description"].ToString()); } } string author = string.Empty; // Haven't figured out how to get this yet DateTime eventStarts = DateTime.MinValue; if (item.FieldValues.ContainsKey("EventDate")) { eventStarts = Parsers.ParseDate(item["EventDate"].ToString()); } DateTime eventEnds = DateTime.MinValue; if (item.FieldValues.ContainsKey("EndDate")) { eventEnds = Parsers.ParseDate(item["EndDate"].ToString()); } ; bool isTimeGMT = true; // Previously we would have had to check the times to see if they are sane, but I think when loading data this way, all times are UTC instead // Events that are "all day" events don't need to be adjusted because they are just 0:00 to 23:59. Adjusting them will set them into the previous day and mess things up. // Determine if this is an "all day" event. bool allDay = false; if (item.FieldValues.ContainsKey("fAllDayEvent")) { allDay = Parsers.ParseBool(item["fAllDayEvent"].ToString()); } // Times returned are all in UTC - adjust to local time if (isTimeGMT) { if (!allDay) { eventStarts = eventStarts + TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now); eventEnds = eventEnds + TimeZone.CurrentTimeZone.GetUtcOffset(DateTime.Now); } } bool recurring = false; if (item.FieldValues.ContainsKey("fRecurrence")) { recurring = Parsers.ParseBool(item["fRecurrence"].ToString()); } string recurrenceData = string.Empty; if (item.FieldValues.ContainsKey("RecurrenceData")) { if (!string.IsNullOrEmpty((string)item["RecurrenceData"])) { recurrenceData = item["RecurrenceData"].ToString(); } } ; SharepointCalendarEvent newCalendarEvent = new SharepointCalendarEvent() { Title = title, Location = location, Description = description, Author = author, AllDay = allDay, Recurring = recurring, EventStart = eventStarts, EventEnd = eventEnds, RecurrenceInfo = recurrenceData }; // Correct start and end dates if the event is recurring if (recurring) { // Deal with recurring events differently - Their dates will be screwed up, so use the data from them to create "phantom" events that line up with the dates required // Events that start with "Deleted" cancel out recurring events if (item["Title"].ToString().StartsWith("Deleted:")) { newCalendarEvent.Title = newCalendarEvent.Title.Remove(0, 8).Trim(); deletedRecurringEvents.Add(newCalendarEvent); } else { recurringEvents_Unexpanded.Add(newCalendarEvent); } } else { returnMe.Add(newCalendarEvent); } } catch { } } // Deal with recurring events #region Deal with recurring events foreach (SharepointCalendarEvent ev in recurringEvents_Unexpanded) { // Figure out when this even reocurrs // the field "RecurrenceData" can have data in two different formats - either a string that looks like this: // Every 1 month(s) on the fourth Wednesday // Every 1 week(s) on: Wednesday // Every 1 month(s) on the fourth Wednesday // or XML data that looks like this: // <recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><weekly we="TRUE" weekFrequency="1" /></repeat><repeatForever>FALSE</repeatForever></rule></recurrence> // This apparently means every wednesday, every week // <recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><monthlyByDay we="TRUE" weekdayOfMonth="first" monthFrequency="1" /></repeat><repeatForever>FALSE</repeatForever></rule></recurrence> // This apparently means every first wednesday of every month // <recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat><monthlyByDay we="TRUE" weekdayOfMonth="second" monthFrequency="1" /></repeat><repeatForever>FALSE</repeatForever></rule></recurrence> // This apparently means every second wednesday of every month // The "<firstDayOfWeek>su</firstDayOfWeek>" means that Sunday is considered the first day of the week // Time range for phantom events // Daily events: 2 weeks back, 6 weeks ahead // Weekly events: 4 weeks back, 8 weeks ahead // Monthly events: 2 months back, 12 months ahead // Yearly events: 1 year back, 5 years ahead // Repeating: // Could be: // <repeatInstances>10</repeatInstances> // <repeatForever>FALSE</repeatForever> // <windowEnd>2007-05-31T22:00:00Z</windowEnd> // parse XML recurrence data if (ev.RecurrenceInfo.StartsWith("<recurrence")) { // Extract the <repeat> section(s), because we don't really care about the rest // Regex: <repeat>(.+?)<\/repeat> Regex regex = new Regex(@"<repeat>(.+?)<\/repeat>"); MatchCollection matches = regex.Matches(ev.RecurrenceInfo); foreach (Match match in matches) { if (match.Success) { // Strip the <repeat> and </repeat> from the start and end of the segment string segment = match.Value.Substring(8, match.Value.Length - (8 + 9)); // Daily if (segment.StartsWith("<daily")) { // parse the "dayFrequency", then just multiply int dayFrequency = 1; Regex dayFrequencyRegex = new Regex(@"dayFrequency=\""(\d+)\"""); Match dayFrequencyMatch = dayFrequencyRegex.Match(segment); if (dayFrequencyMatch.Success) { dayFrequency = Parsers.ParseInt(dayFrequencyMatch.Value.Substring(14, dayFrequencyMatch.Value.Length - (14 + 1))); if (dayFrequency < 1) { dayFrequency = 1; } } // Get the original start date, and accellerate it to a date closer to now DateTime startDate = ev.EventStart; if (startDate <= DateTime.Now) { while (startDate <= DateTime.Now.AddDays(-7)) { startDate = startDate.AddDays(dayFrequency); } } // Get the current week (Sunday), then subtract 1 week //DateTime startOfWeek = DateTime.Today.AddDays(-1 * (int)(DateTime.Today.DayOfWeek)).AddDays(-7); for (int dayCounter = 0; dayCounter <= 30; dayCounter += dayFrequency) { DateTime newEventStartDate = startDate.AddDays(dayCounter); //DateTime newEventEndDate = newEventStartDate.Add(ev.Duration); returnMe.Add(ev.CloneWithNewDates(newEventStartDate, newEventStartDate)); } } // Weekly if (segment.StartsWith("<weekly")) { // For weekly events we want to create phantom events 4 weeks back, and 8 weeks ahead (12 weeks total) // We need to factor in the weekFrequency="1", to handle events that might be every second week or something int weekFrequency = 1; Regex weekFrequencyRegex = new Regex(@"weekFrequency=\""(\d+)\"""); Match weeklyFrequencyMatch = weekFrequencyRegex.Match(segment); if (weeklyFrequencyMatch.Success) { weekFrequency = Parsers.ParseInt(weeklyFrequencyMatch.Value.Substring(15, weeklyFrequencyMatch.Value.Length - (15 + 1))); if (weekFrequency < 1) { weekFrequency = 1; } } // Get the original start date, and accellerate it to a date closer to now DateTime startDate = ev.EventStart; if (startDate <= DateTime.Now) { while (startDate <= DateTime.Now.AddDays(-28)) { startDate = startDate.AddDays(weekFrequency * 7); } } // Get the current week (Sunday), then subtract 4 weeks from it for a start date //DateTime startOfWeek = DateTime.Today.AddDays(-1 * (int)(DateTime.Today.DayOfWeek)).AddDays(-28); // Translate detected days into day numbers (Sunday = 0) List <int> eventDayNumbers = new List <int>(); if (segment.Contains("su=\"TRUE\"")) { eventDayNumbers.Add(0); } if (segment.Contains("mo=\"TRUE\"")) { eventDayNumbers.Add(1); } if (segment.Contains("tu=\"TRUE\"")) { eventDayNumbers.Add(2); } if (segment.Contains("we=\"TRUE\"")) { eventDayNumbers.Add(3); } if (segment.Contains("th=\"TRUE\"")) { eventDayNumbers.Add(4); } if (segment.Contains("fr=\"TRUE\"")) { eventDayNumbers.Add(5); } if (segment.Contains("sa=\"TRUE\"")) { eventDayNumbers.Add(6); } // Get the sunday of the week of the start date of the event DateTime startingWeekSunday = startDate.AddDays(-1 * (int)startDate.DayOfWeek); for (int weekCounter = 0; weekCounter < 12; weekCounter += weekFrequency) { foreach (int dayNum in eventDayNumbers) { DateTime newEventStartDate = startingWeekSunday.AddDays(dayNum).AddDays(7 * weekCounter); returnMe.Add(ev.CloneWithNewDates(newEventStartDate, newEventStartDate)); } } } // Monthly, given day numbers (Every 2nd of the month, for example) if (segment.StartsWith("<monthly ")) { // If the user sets it to every ___ day of every __ month, it looks like this: // <monthly monthFrequency="2" day="6" /> int monthFrequency = 1; Regex monthFrequencyRegex = new Regex(@"monthFrequency=\""(\d+)\"""); Match monthFrequencyMatch = monthFrequencyRegex.Match(segment); if (monthFrequencyMatch.Success) { monthFrequency = Parsers.ParseInt(monthFrequencyMatch.Value.Substring(16, monthFrequencyMatch.Value.Length - 17)); if (monthFrequency < 1) { monthFrequency = 1; } } // We ignore the actual day of the start date, in favor of this value (this is what Sharepoint does anyway) int dayNumber = 0; Regex dayNumberRegex = new Regex(@"day=\""(\d+)\"""); Match dayNumberMatch = dayNumberRegex.Match(segment); if (dayNumberMatch.Success) { dayNumber = Parsers.ParseInt(dayNumberMatch.Value.Substring(5, dayNumberMatch.Value.Length - (5 + 1))); if (dayNumber < 1) { dayNumber = 1; } } // Rebuild the event start date based on the recurrence information ev.EventStart = new DateTime(ev.EventStart.Year, ev.EventStart.Month, dayNumber, ev.EventStart.Hour, ev.EventStart.Minute, ev.EventStart.Second); // Find the original month and acellerate to a time closer to now, so we don't incorrectly assume that this month includes the event (if the month frequency causes it to skip the current month) DateTime startDate = ev.EventStart; if (startDate <= DateTime.Now) { while (startDate <= DateTime.Now.AddDays(-1 * (31 * (monthFrequency + 1)))) { startDate = startDate.AddMonths(monthFrequency); } } for (int monthCounter = 0; monthCounter < 12; monthCounter += monthFrequency) { DateTime newEventStartDate = startDate.AddMonths(monthFrequency * monthCounter); returnMe.Add(ev.CloneWithNewDates(newEventStartDate, newEventStartDate)); } } // Monthly by Day (Every third wednesday, for example) if (segment.StartsWith("<monthlyByDay ")) { // weekdayOfMonth="first" // First|Second|Third|Fourth|Last // Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday|Weekday|Weekend Day, etc // Sharepoint only lets monthly events happen on a single day per event so we dont need to deal with multiple days int monthFrequency = 1; Regex monthFrequencyRegex = new Regex(@"monthFrequency=\""(\d+)\"""); Match monthFrequencyMatch = monthFrequencyRegex.Match(segment); if (monthFrequencyMatch.Success) { monthFrequency = Parsers.ParseInt(monthFrequencyMatch.Value.Substring(16, monthFrequencyMatch.Value.Length - 17)); if (monthFrequency < 1) { monthFrequency = 1; } } // Get the weekday of month string string weekdayOfMonthString = string.Empty; Regex weekdayOfMonthRegex = new Regex(@"weekdayOfMonth=\""(.+?)\"""); Match weekdayOfMonthMatch = weekdayOfMonthRegex.Match(segment); if (weekdayOfMonthMatch.Success) { weekdayOfMonthString = weekdayOfMonthMatch.Value.Substring(16, weekdayOfMonthMatch.Value.Length - 17); } // Make a list of years and months that we care about, because the contextual days will differ year to year // If we're missing the weekday of the month string, we can't continue if (!string.IsNullOrEmpty(weekdayOfMonthString)) { int goalIteration = 0; switch (weekdayOfMonthString.ToLower()) { case "first": goalIteration = 1; break; case "second": goalIteration = 2; break; case "third": goalIteration = 3; break; case "fourth": goalIteration = 4; break; case "last": goalIteration = 999; break; } DayOfWeek goalDayOfWeek = DayOfWeek.Saturday; if (segment.Contains("su=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Sunday; } if (segment.Contains("mo=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Monday; } if (segment.Contains("tu=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Tuesday; } if (segment.Contains("we=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Wednesday; } if (segment.Contains("th=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Thursday; } if (segment.Contains("fr=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Friday; } if (segment.Contains("sa=\"TRUE\"")) { goalDayOfWeek = DayOfWeek.Saturday; } // Get the original start date, and accellerate it to a date closer to now DateTime startDate = ev.EventStart; if (startDate <= DateTime.Now) { while (startDate <= DateTime.Now.AddDays(-1 * monthFrequency * 31)) { startDate = startDate.AddMonths(monthFrequency); } } // Make a list of months, in the form of datetimes of the first of those months, for each phantom event we want to create // We'll need to go through all the below BS for eachof them seperately, because they will differ year to year List <DateTime> phantomEventMonths = new List <DateTime>(); for (int monthCount = 0; monthCount <= 12; monthCount += monthFrequency) { phantomEventMonths.Add(new DateTime(startDate.Year, startDate.Month, 1).AddMonths(monthCount)); } foreach (DateTime phantomEventMonth in phantomEventMonths) { DateTime newEventStartDate = phantomEventMonth; if (goalIteration == 999) { // We're finding the last day of the month instead of the first day if (segment.ToLower().Contains("weekend_day=\"true\"")) { newEventStartDate = Helpers.GetDayOfMonth_Backwards(phantomEventMonth.Year, phantomEventMonth.Month, 1, Helpers.GetWeekendDays()); } else if (segment.ToLower().Contains("weekday=\"true\"")) { newEventStartDate = Helpers.GetDayOfMonth_Backwards(phantomEventMonth.Year, phantomEventMonth.Month, 1, Helpers.GetWeekdays()); } else if (segment.ToLower().Contains("day=\"true\"")) { // Just the last day of the month newEventStartDate = new DateTime(phantomEventMonth.Year, phantomEventMonth.Month, DateTime.DaysInMonth(phantomEventMonth.Year, phantomEventMonth.Month)); } else { // Find the day specifically newEventStartDate = Helpers.GetDayOfMonth_Backwards(phantomEventMonth.Year, phantomEventMonth.Month, 1, goalDayOfWeek); } } else { if (segment.ToLower().Contains("weekend_day=\"true\"")) { newEventStartDate = Helpers.GetDayOfMonth(phantomEventMonth.Year, phantomEventMonth.Month, goalIteration, Helpers.GetWeekendDays()); } else if (segment.ToLower().Contains("weekday=\"true\"")) { newEventStartDate = Helpers.GetDayOfMonth(phantomEventMonth.Year, phantomEventMonth.Month, goalIteration, Helpers.GetWeekdays()); } else if (segment.ToLower().Contains("day=\"true\"")) { newEventStartDate = new DateTime(phantomEventMonth.Year, phantomEventMonth.Month, goalIteration); } else { // Find the day specifically newEventStartDate = Helpers.GetDayOfMonth(phantomEventMonth.Year, phantomEventMonth.Month, goalIteration, goalDayOfWeek); } } returnMe.Add(ev.CloneWithNewDates(newEventStartDate, newEventStartDate)); } } } // Yearly if (segment.StartsWith("<yearly")) { // Don't support yearly events at the moment - I don't have the patience to deal with this right now, and theres little chance a yearly event will be in our calendars } } } } } #endregion // Deal with deleted events List <SharepointCalendarEvent> finalReturnedEventsList = new List <SharepointCalendarEvent>(); foreach (SharepointCalendarEvent ev in returnMe) { bool foundDeletedEvent = false; foreach (SharepointCalendarEvent deletedEvent in deletedRecurringEvents) { if (( (ev.Title == deletedEvent.Title) && (ev.EventStart.Year == deletedEvent.EventStart.Year) && (ev.EventStart.Month == deletedEvent.EventStart.Month) && (ev.EventStart.Day == deletedEvent.EventStart.Day) )) { foundDeletedEvent = true; } } if (!foundDeletedEvent) { finalReturnedEventsList.Add(ev); } } return(finalReturnedEventsList.OrderBy(e => e.EventStart).ThenBy(e => e.EventEnd).ToList()); }
private static string EventSignature(SharepointCalendarEvent ev) { return(EventSignature(ev.Title, ev.EventStart)); }