Example #1
0
        /// <summary>
        /// Validates the request data.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private CalendarProps ValidateRequestData(HttpContext context)
        {
            CalendarProps calendarProps = new CalendarProps();

            // Security check made sure the calendar ID is good so no need to check it again.
            calendarProps.CalendarId = int.Parse(request.QueryString["calendarid"]);

            string campusIdQueryString = request.QueryString["campusids"] != null ? request.QueryString["campusids"] : string.Empty;

            calendarProps.CampusIds = ParseIds(campusIdQueryString);

            string audienceIdQueryString = request.QueryString["audienceids"] != null ? request.QueryString["audienceids"] : string.Empty;

            calendarProps.AudienceIds = ParseIds(audienceIdQueryString);

            string startDate = request.QueryString["startdate"];

            if (!string.IsNullOrWhiteSpace(startDate))
            {
                calendarProps.StartDate = DateTime.ParseExact(startDate, "yyyyMMdd", CultureInfo.InvariantCulture);
            }

            string endDate = request.QueryString["enddate"];

            if (!string.IsNullOrWhiteSpace(endDate))
            {
                calendarProps.EndDate = DateTime.ParseExact(endDate, "yyyyMMdd", CultureInfo.InvariantCulture);
            }

            return(calendarProps);
        }
Example #2
0
        /// <summary>
        /// Processes the request.
        /// </summary>
        /// <param name="httpContext">The HTTP context.</param>
        public void ProcessRequest(HttpContext httpContext)
        {
            string interactionDeviceType = InteractionDeviceType.GetClientType(httpContext.Request.UserAgent);

            try
            {
                CalendarProps calendarProps = ValidateRequestData(httpContext);

                if (calendarProps == null)
                {
                    return;
                }

                iCalendar icalendar = CreateICalendar(calendarProps, interactionDeviceType);

                iCalendarSerializer serializer = new iCalendarSerializer();
                string s = serializer.SerializeToString(icalendar);

                httpContext.Response.Clear();
                httpContext.Response.ClearHeaders();
                httpContext.Response.ClearContent();
                httpContext.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}_ical.ics", DateTime.Now.ToString("yyyy-MM-dd_hhmmss")));
                httpContext.Response.ContentType = "text/calendar";
                httpContext.Response.Write(s);
            }
            catch (Exception ex)
            {
                ExceptionLogService.LogException(ex, httpContext);
                SendBadRequest(httpContext);
            }
        }
Example #3
0
        /// <summary>
        /// Uses the filter information in the CalendarProps object to get a list of events
        /// </summary>
        /// <param name="calendarProps">The calendar props.</param>
        /// <returns></returns>
        private List <EventItem> GetEventItems(CalendarProps calendarProps)
        {
            RockContext rockContext = new RockContext();

            EventCalendarItemService eventCalendarItemService = new EventCalendarItemService(rockContext);
            var eventIdsForCalendar = eventCalendarItemService
                                      .Queryable()
                                      .Where(i => i.EventCalendarId == calendarProps.CalendarId)
                                      .Select(i => i.EventItemId)
                                      .ToList();

            EventItemService eventItemService = new EventItemService(rockContext);
            var eventQueryable = eventItemService
                                 .Queryable("EventItemAudiences, EventItemOccurrences.Schedule")
                                 .Where(e => eventIdsForCalendar.Contains(e.Id))
                                 .Where(e => e.EventItemOccurrences.Any(o => o.Schedule.EffectiveStartDate <= calendarProps.EndDate && calendarProps.StartDate <= o.Schedule.EffectiveEndDate))
                                 .Where(e => e.IsActive == true)
                                 .Where(e => e.IsApproved);

            // For Campus
            if (calendarProps.CampusIds.Any())
            {
                eventQueryable = eventQueryable.Where(e => e.EventItemOccurrences.Any(c => !c.CampusId.HasValue || calendarProps.CampusIds.Contains(c.CampusId.Value)));
            }

            // For Audience
            if (calendarProps.AudienceIds.Any())
            {
                eventQueryable = eventQueryable.Where(e => e.EventItemAudiences.Any(c => calendarProps.AudienceIds.Contains(c.DefinedValueId)));
            }

            return(eventQueryable.ToList());
        }
Example #4
0
        /// <summary>
        /// Uses the filter information in the CalendarProps object to get a list of attendance records and orders them by schedule ID.
        /// </summary>
        /// <param name="calendarProps">The calendar props.</param>
        /// <returns></returns>
        private List <Attendance> GetAttendances(CalendarProps calendarProps)
        {
            using (var rockContext = new RockContext())
            {
                var attendanceService = new AttendanceService(rockContext);
                var attendances       = attendanceService
                                        .GetConfirmedScheduled()
                                        .AsNoTracking()
                                        .Where(a => a.PersonAlias.PersonId == calendarProps.PersonId)
                                        .Where(a => a.Occurrence.OccurrenceDate >= calendarProps.StartDate)
                                        .Where(a => a.Occurrence.OccurrenceDate <= calendarProps.EndDate)
                                        .OrderBy(a => a.Occurrence.ScheduleId);

                return(attendances.ToList());
            }
        }
Example #5
0
        /// <summary>
        /// Validates the request data.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <returns></returns>
        private CalendarProps ValidateRequestData(HttpContext httpContext)
        {
            CalendarProps calendarProps = new CalendarProps();

            Guid?personAliasGuid = httpContext.Request.QueryString["paguid"].AsGuidOrNull();

            if (personAliasGuid == null)
            {
                SendBadRequest(httpContext);
                return(null);
            }

            using (var rockContext = new RockContext())
            {
                var personAliasService = new PersonAliasService(rockContext);
                int?personId           = personAliasService.Queryable().AsNoTracking().Where(pa => pa.Guid == personAliasGuid).Select(pa => pa.PersonId).Cast <int?>().FirstOrDefault();

                if (personId == null)
                {
                    SendBadRequest(httpContext);
                    return(null);
                }

                calendarProps.PersonId = personId.Value;
            }

            string startDate = httpContext.Request.QueryString["startdate"];

            if (!string.IsNullOrWhiteSpace(startDate))
            {
                calendarProps.StartDate = DateTime.ParseExact(startDate, "yyyyMMdd", CultureInfo.InvariantCulture);
            }

            string endDate = httpContext.Request.QueryString["enddate"];

            if (!string.IsNullOrWhiteSpace(endDate))
            {
                calendarProps.EndDate = DateTime.ParseExact(endDate, "yyyyMMdd", CultureInfo.InvariantCulture);
            }

            return(calendarProps);
        }
Example #6
0
        /// <summary>
        /// Creates the iCalendar object and populates it with events
        /// </summary>
        /// <param name="calendarProps">The calendar props.</param>
        /// <returns></returns>
        private iCalendar CreateICalendar(CalendarProps calendarProps)
        {
            // Get a list of Rock Calendar Events filtered by calendarProps
            List <EventItem> eventItems = GetEventItems(calendarProps);

            // Create the iCalendar
            iCalendar icalendar = new iCalendar();

            icalendar.AddLocalTimeZone();

            // Create each of the events for the calendar(s)
            foreach (EventItem eventItem in eventItems)
            {
                foreach (EventItemOccurrence occurrence in eventItem.EventItemOccurrences)
                {
                    if (occurrence.Schedule == null)
                    {
                        continue;
                    }

                    iCalendarSerializer serializer = new iCalendarSerializer();
                    iCalendarCollection ical       = (iCalendarCollection)serializer.Deserialize(occurrence.Schedule.iCalendarContent.ToStreamReader());

                    foreach (var icalEvent in ical[0].Events)
                    {
                        // We get all of the schedule info from Schedule.iCalendarContent
                        Event ievent = icalEvent.Copy <Event>();

                        ievent.Summary  = !string.IsNullOrEmpty(eventItem.Name) ? eventItem.Name : string.Empty;
                        ievent.Location = !string.IsNullOrEmpty(occurrence.Location) ? occurrence.Location : string.Empty;

                        ievent.DTStart.SetTimeZone(icalendar.TimeZones[0]);
                        ievent.DTEnd.SetTimeZone(icalendar.TimeZones[0]);

                        // Rock has more descriptions than iCal so lets concatenate them
                        string description = CreateEventDescription(eventItem, occurrence);

                        // Don't set the description prop for outlook to force it to use the X-ALT-DESC property which can have markup.
                        if (interactionDeviceType != "Outlook")
                        {
                            ievent.Description = description.ConvertBrToCrLf()
                                                 .Replace("</P>", "")
                                                 .Replace("</p>", "")
                                                 .Replace("<P>", Environment.NewLine)
                                                 .Replace("<p>", Environment.NewLine)
                                                 .Replace("&nbsp;", " ")
                                                 .SanitizeHtml();
                        }

                        // HTML version of the description for outlook
                        ievent.AddProperty("X-ALT-DESC;FMTTYPE=text/html", "<html>" + description + "</html>");

                        // classification: "PUBLIC", "PRIVATE", "CONFIDENTIAL"
                        ievent.Class = "PUBLIC";

                        if (!string.IsNullOrEmpty(eventItem.DetailsUrl))
                        {
                            Uri result;
                            if (Uri.TryCreate(eventItem.DetailsUrl, UriKind.Absolute, out result))
                            {
                                ievent.Url = result;
                            }
                            else if (Uri.TryCreate("http://" + eventItem.DetailsUrl, UriKind.Absolute, out result))
                            {
                                ievent.Url = result;
                            }
                        }

                        // add contact info if it exists
                        if (occurrence.ContactPersonAlias != null)
                        {
                            ievent.Organizer            = new Organizer(string.Format("MAILTO:{0}", occurrence.ContactPersonAlias.Person.Email));
                            ievent.Organizer.CommonName = occurrence.ContactPersonAlias.Person.FullName;

                            // Outlook doesn't seems to use Contacts or Comments
                            string contactName  = !string.IsNullOrEmpty(occurrence.ContactPersonAlias.Person.FullName) ? "Name: " + occurrence.ContactPersonAlias.Person.FullName : string.Empty;
                            string contactEmail = !string.IsNullOrEmpty(occurrence.ContactEmail) ? ", Email: " + occurrence.ContactEmail : string.Empty;
                            string contactPhone = !string.IsNullOrEmpty(occurrence.ContactPhone) ? ", Phone: " + occurrence.ContactPhone : string.Empty;
                            string contactInfo  = contactName + contactEmail + contactPhone;

                            ievent.Contacts.Add(contactInfo);
                            ievent.Comments.Add(contactInfo);
                        }

                        // TODO: categories - comma delimited list of whatever, might use audience
                        foreach (var a in eventItem.EventItemAudiences)
                        {
                            ievent.Categories.Add(a.DefinedValue.Value);
                        }

                        //// No attachments for now.
                        ////if ( eventItem.PhotoId != null )
                        ////{
                        ////    // The DDay Attachment obj doesn't allow you to name the attachment. Nice huh? So just add prop manually...
                        ////    ievent.AddProperty( "ATTACH;VALUE=BINARY;ENCODING=BASE64;X-FILENAME=\"" + eventItem.Photo.FileName + "\"", Convert.ToBase64String( eventItem.Photo.ContentStream.ReadBytesToEnd().ToArray() ) );
                        ////}

                        icalendar.Events.Add(ievent);
                    }
                }
            }

            return(icalendar);
        }
Example #7
0
        /// <summary>
        /// Creates the iCalendar object and populates it with events
        /// </summary>
        /// <param name="calendarProps">The calendar props.</param>
        /// <returns></returns>
        private iCalendar CreateICalendar(CalendarProps calendarProps, string interactionDeviceType)
        {
            // Get a list of confirmed attendances filtered by calendarProps
            List <Attendance> attendances = GetAttendances(calendarProps);

            // Create the iCalendar
            iCalendar icalendar = new iCalendar();

            icalendar.AddLocalTimeZone();
            TimeSpan duration          = TimeSpan.MinValue;
            int      currentScheduleId = -1;

            // Create each of the attendances
            foreach (var attendance in attendances)
            {
                using (var rockContext = new RockContext())
                {
                    var attendanceOccurrenceService = new AttendanceOccurrenceService(rockContext);

                    var schedule = attendanceOccurrenceService
                                   .Queryable()
                                   .AsNoTracking()
                                   .Where(a => a.Id == attendance.OccurrenceId)
                                   .Select(a => a.Schedule)
                                   .FirstOrDefault();

                    string scheduleName = schedule.Name;

                    // TODO: Construct a description that includes the group leader contact info and the URL to the schedule toolbox.
                    string description = schedule.Description;

                    string locationName = attendanceOccurrenceService
                                          .Queryable()
                                          .AsNoTracking()
                                          .Where(a => a.Id == attendance.OccurrenceId)
                                          .Select(a => a.Location.Name)
                                          .FirstOrDefault() ?? string.Empty;

                    if (schedule.Id != currentScheduleId)
                    {
                        // We have to get the duration from Schedule.iCal for this attendance.
                        // Attendances are ordered by scheduleId so this only happens once for each unique schedule.
                        iCalendarSerializer serializer = new iCalendarSerializer();
                        iCalendarCollection ical       = ( iCalendarCollection )serializer.Deserialize(schedule.iCalendarContent.ToStreamReader());
                        duration          = ical[0].Events[0].Duration;
                        currentScheduleId = schedule.Id;
                    }

                    var iCalEvent = new DDay.iCal.Event();
                    iCalEvent.Summary  = scheduleName;
                    iCalEvent.Location = locationName;
                    iCalEvent.DTStart  = new DDay.iCal.iCalDateTime(attendance.StartDateTime);
                    iCalEvent.DTStart.SetTimeZone(icalendar.TimeZones[0]);
                    iCalEvent.Duration = duration;

                    // Don't set the description prop for outlook to force it to use the X-ALT-DESC property which can have markup.
                    if (interactionDeviceType != "Outlook")
                    {
                        iCalEvent.Description = description.ConvertBrToCrLf().SanitizeHtml();
                    }

                    // HTML version of the description for outlook
                    iCalEvent.AddProperty("X-ALT-DESC;FMTTYPE=text/html", "<html>" + description + "</html>");

                    // classification: "PUBLIC", "PRIVATE", "CONFIDENTIAL"
                    iCalEvent.Class = "PUBLIC";

                    //Add contact info for the group leader
                    int groupId = attendanceOccurrenceService
                                  .Queryable()
                                  .AsNoTracking()
                                  .Where(a => a.Id == attendance.OccurrenceId)
                                  .Select(a => a.GroupId)
                                  .FirstOrDefault() ?? -1;

                    Person groupLeader = new GroupMemberService(rockContext).GetLeaders(groupId).AsNoTracking().Select(m => m.Person).FirstOrDefault() ?? null;

                    if (groupLeader != null)
                    {
                        iCalEvent.Organizer            = new Organizer(string.Format("MAILTO:{0}", groupLeader.Email));
                        iCalEvent.Organizer.CommonName = groupLeader.FullName;

                        // Outlook doesn't seem to use Contacts or Comments
                        string contactName  = !string.IsNullOrEmpty(groupLeader.FullName) ? "Name: " + groupLeader.FullName : string.Empty;
                        string contactEmail = !string.IsNullOrEmpty(groupLeader.Email) ? ", Email: " + groupLeader.Email : string.Empty;
                        string contactInfo  = contactName + contactEmail;

                        iCalEvent.Contacts.Add(contactInfo);
                        iCalEvent.Comments.Add(contactInfo);
                    }

                    icalendar.Events.Add(iCalEvent);
                }
            }

            return(icalendar);
        }
        /// <summary>
        /// Creates the iCalendar object and populates it with events
        /// </summary>
        /// <param name="calendarProps">The calendar props.</param>
        /// <returns></returns>
        private Calendar CreateICalendar(CalendarProps calendarProps)
        {
            // Get a list of Rock Calendar Events filtered by calendarProps
            List <EventItem> eventItems = GetEventItems(calendarProps);

            // Create the iCalendar.
            // Allow ICal to create the VTimeZone object from the local time zone to ensure that we get the correct name and daylight saving offset.
            var icalendar = new Calendar();

            var vtz = VTimeZone.FromLocalTimeZone();

            icalendar.AddTimeZone(vtz);

            var timeZoneId = vtz.TzId;

            // Create each of the events for the calendar(s)
            foreach (EventItem eventItem in eventItems)
            {
                foreach (EventItemOccurrence occurrence in eventItem.EventItemOccurrences)
                {
                    if (occurrence.Schedule == null)
                    {
                        continue;
                    }

                    var serializer = new CalendarSerializer();
                    var ical       = (CalendarCollection)serializer.Deserialize(occurrence.Schedule.iCalendarContent.ToStreamReader());

                    foreach (var icalEvent in ical[0].Events)
                    {
                        // We get all of the schedule info from Schedule.iCalendarContent
                        var ievent = icalEvent.Copy <Ical.Net.Event>();

                        ievent.Summary  = !string.IsNullOrEmpty(eventItem.Name) ? eventItem.Name : string.Empty;
                        ievent.Location = !string.IsNullOrEmpty(occurrence.Location) ? occurrence.Location : string.Empty;

                        // Create the list of exceptions.
                        // Exceptions must meet RFC 5545 iCalendar specifications to be correctly processed by third-party calendar applications
                        // such as Microsoft Outlook and Google Calendar. Specifically, an exception must have exactly the same start time
                        // and time zone as the template event, and the time zone must be expressed as an IANA name.
                        // The most recent version of iCal.Net (v2.3.5) that supports .NET framework v4.5.2 has some inconsistencies in the
                        // iCalendar serialization process, so we need to force the Start, End and Exception dates to render in exactly the same format.
                        ievent.Start = new CalDateTime(icalEvent.Start.Value, timeZoneId);
                        ievent.Start.IsUniversalTime = false;

                        ievent.End = new CalDateTime(icalEvent.End.Value, timeZoneId);
                        ievent.End.IsUniversalTime = false;

                        var eventStartTime = new TimeSpan(ievent.DtStart.Hour, ievent.DtStart.Minute, ievent.DtStart.Second);

                        var newExceptionDatesList = new List <Ical.Net.Interfaces.DataTypes.IPeriodList>();

                        foreach (var exceptionDateList in ievent.ExceptionDates)
                        {
                            var newDateList = new PeriodList()
                            {
                                TzId = timeZoneId
                            };

                            foreach (var exceptionDate in exceptionDateList)
                            {
                                var newDateTime = exceptionDate.StartTime.HasTime ? exceptionDate.StartTime.Value : exceptionDate.StartTime.Value.Add(eventStartTime);

                                newDateTime = new DateTime(newDateTime.Year, newDateTime.Month, newDateTime.Day, newDateTime.Hour, newDateTime.Minute, newDateTime.Second, newDateTime.Millisecond, DateTimeKind.Local);

                                var newDate = new CalDateTime(newDateTime);
                                newDate.IsUniversalTime = false;

                                newDateList.Add(newDate);
                            }

                            newExceptionDatesList.Add(newDateList);
                        }

                        ievent.ExceptionDates = newExceptionDatesList;

                        // Rock has more descriptions than iCal so lets concatenate them
                        string description = CreateEventDescription(eventItem, occurrence);

                        // Don't set the description prop for outlook to force it to use the X-ALT-DESC property which can have markup.
                        if (interactionDeviceType != "Outlook")
                        {
                            ievent.Description = description.ConvertBrToCrLf()
                                                 .Replace("</P>", "")
                                                 .Replace("</p>", "")
                                                 .Replace("<P>", Environment.NewLine)
                                                 .Replace("<p>", Environment.NewLine)
                                                 .Replace("&nbsp;", " ")
                                                 .SanitizeHtml();
                        }

                        // HTML version of the description for outlook
                        ievent.AddProperty("X-ALT-DESC;FMTTYPE=text/html", "<html>" + description + "</html>");

                        // classification: "PUBLIC", "PRIVATE", "CONFIDENTIAL"
                        ievent.Class = "PUBLIC";

                        if (!string.IsNullOrEmpty(eventItem.DetailsUrl))
                        {
                            Uri result;
                            if (Uri.TryCreate(eventItem.DetailsUrl, UriKind.Absolute, out result))
                            {
                                ievent.Url = result;
                            }
                            else if (Uri.TryCreate("http://" + eventItem.DetailsUrl, UriKind.Absolute, out result))
                            {
                                ievent.Url = result;
                            }
                        }

                        // add contact info if it exists
                        if (occurrence.ContactPersonAlias != null)
                        {
                            ievent.Organizer            = new Organizer(string.Format("MAILTO:{0}", occurrence.ContactPersonAlias.Person.Email));
                            ievent.Organizer.CommonName = occurrence.ContactPersonAlias.Person.FullName;

                            // Outlook doesn't seems to use Contacts or Comments
                            string contactName  = !string.IsNullOrEmpty(occurrence.ContactPersonAlias.Person.FullName) ? "Name: " + occurrence.ContactPersonAlias.Person.FullName : string.Empty;
                            string contactEmail = !string.IsNullOrEmpty(occurrence.ContactEmail) ? ", Email: " + occurrence.ContactEmail : string.Empty;
                            string contactPhone = !string.IsNullOrEmpty(occurrence.ContactPhone) ? ", Phone: " + occurrence.ContactPhone : string.Empty;
                            string contactInfo  = contactName + contactEmail + contactPhone;

                            ievent.Contacts.Add(contactInfo);
                            ievent.Comments.Add(contactInfo);
                        }

                        // TODO: categories - comma delimited list of whatever, might use audience
                        foreach (var a in eventItem.EventItemAudiences)
                        {
                            ievent.Categories.Add(a.DefinedValue.Value);
                        }

                        //// No attachments for now.
                        ////if ( eventItem.PhotoId != null )
                        ////{
                        ////    // The DDay Attachment obj doesn't allow you to name the attachment. Nice huh? So just add prop manually...
                        ////    ievent.AddProperty( "ATTACH;VALUE=BINARY;ENCODING=BASE64;X-FILENAME=\"" + eventItem.Photo.FileName + "\"", Convert.ToBase64String( eventItem.Photo.ContentStream.ReadBytesToEnd().ToArray() ) );
                        ////}

                        icalendar.Events.Add(ievent);
                    }
                }
            }

            return(icalendar);
        }