public string GetAsICalFormat(IEnumerable <Event> events, string calendarName, string calenderDescription) { var calendar = new Calendar { Method = "PUBLISH", TimeZones = { new VTimeZone("Europe/Amsterdam") }, Properties = { new CalendarProperty("X-PUBLISHED-TTL", "PT15M"), new CalendarProperty("X-WR-TIMEZONE", "Europe/Amsterdam"), new CalendarProperty("X-WR-CALNAME", calendarName), new CalendarProperty("X-WR-CALDESC", calenderDescription), } }; calendar.AddTimeZone("Europe/Amsterdam"); foreach (var @event in events.Where(x => x.Start.DateTime != null && x.End.DateTime != null)) { if ([email protected] || [email protected]) { continue; } calendar.Events.Add(new CalendarEvent { Description = @event.Description, Summary = @event.Summary, Location = @event.Location, Attendees = @event.Attendees == null ? new List <Attendee>() : @event.Attendees.Select(x => new Attendee { CommonName = x.DisplayName, Value = new Uri($"mailto:{x.Email}") }).ToList(), Organizer = @event.Organizer == null ? null : new Organizer { CommonName = @event.Organizer.DisplayName, Value = new Uri($"mailto:{@event.Organizer.Email}"), SentBy = new Uri($"mailto:{@event.Organizer.Email}") }, Start = new CalDateTime(@event.Start.DateTime.Value), End = new CalDateTime(@event.End.DateTime.Value), }); } var serializer = new CalendarSerializer(); return(serializer.SerializeToString(calendar)); }
protected void Page_Load(object sender, EventArgs e) { if (!IsPostBack) { if (!Page.User.Identity.IsAuthenticated) { throw new MyFlightbookException("Unauthorized!"); } int idClub = util.GetIntParam(Request, "c", 0); if (idClub == 0) { throw new MyFlightbookException("Invalid club"); } Club c = Club.ClubWithID(idClub); if (c == null) { throw new MyFlightbookException(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Invalid club: {0}", idClub)); } string szIDs = util.GetStringParam(Request, "sid"); string[] rgSIDs = szIDs.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); if (rgSIDs.Length == 0) { throw new MyFlightbookException("No scheduled events to download specified"); } bool fIsAdmin = c.HasAdmin(Page.User.Identity.Name); using (Ical.Net.Calendar ic = new Ical.Net.Calendar()) { ic.AddTimeZone(new VTimeZone(c.TimeZone.Id)); string szTitle = string.Empty; foreach (string sid in rgSIDs) { ScheduledEvent se = ScheduledEvent.AppointmentByID(sid, c.TimeZone); if (se == null) { throw new MyFlightbookException(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Invalid scheduled event ID: {0}", sid)); } if (!fIsAdmin && Page.User.Identity.Name.CompareOrdinal(se.OwningUser) != 0) { throw new MyFlightbookException("Attempt to download appointment that you don't own!"); } ClubAircraft ca = c.MemberAircraft.FirstOrDefault(ca2 => ca2.AircraftID.ToString(System.Globalization.CultureInfo.InvariantCulture).CompareOrdinal(se.ResourceID) == 0); Event ev = ic.Create <Event>(); ev.Uid = se.ID; ev.IsAllDay = false; ev.Start = new CalDateTime(se.StartUtc, TimeZoneInfo.Utc.Id); ev.End = new CalDateTime(se.EndUtc, TimeZoneInfo.Utc.Id); ev.Start.HasTime = ev.End.HasTime = true; // has time is false if the ultimate time is midnight. szTitle = ev.Description = ev.Summary = String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0}{1}", ca == null ? string.Empty : String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0} - ", ca.DisplayTailnumber), se.Body); ev.Location = c.HomeAirport == null ? c.HomeAirportCode : String.Format(System.Globalization.CultureInfo.CurrentCulture, "{0} - {1}", c.HomeAirportCode, c.HomeAirport.Name); Alarm a = new Alarm(); a.Action = AlarmAction.Display; a.Description = ev.Summary; a.Trigger = new Trigger(); a.Trigger.DateTime = ev.Start.AddMinutes(-30); ev.Alarms.Add(a); ic.Method = "PUBLISH"; } CalendarSerializer s = new CalendarSerializer(); string output = s.SerializeToString(ic); Page.Response.Clear(); Page.Response.ContentType = "text/calendar"; Response.AddHeader("Content-Disposition", String.Format(System.Globalization.CultureInfo.InvariantCulture, "inline;filename={0}", Branding.ReBrand(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}appt.ics", szTitle)).Replace(" ", "-"))); Response.Write(output); Response.Flush(); Response.End(); } } }
/// <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, string interactionDeviceType) { // Get a list of confirmed attendances filtered by calendarProps List <Attendance> attendances = GetAttendances(calendarProps); // Create the iCalendar var icalendar = new Calendar(); icalendar.AddTimeZone(TimeZoneInfo.Local); 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. var serializer = new CalendarSerializer(); var ical = ( CalendarCollection )serializer.Deserialize(schedule.iCalendarContent.ToStreamReader()); duration = ical[0].Events[0].Duration; currentScheduleId = schedule.Id; } var iCalEvent = new Event(); iCalEvent.Summary = scheduleName; iCalEvent.Location = locationName; iCalEvent.DtStart = new CalDateTime(attendance.StartDateTime, icalendar.TimeZones[0].TzId); 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(" ", " ") .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); }
public string GetCalendar() { var now = DateTime.Now; var estart = now.AddDays(1); var eend = now.AddMinutes(30); var calendar = new Ical.Net.Calendar(); IList <Attendee> attendees = new List <Attendee>(); Organizer orgniser = new Organizer { CommonName = "Face My Resume", Value = new Uri($"mailto:[email protected]") }; //attendees.Add(new Attendee("MAILTO:[email protected]") //{ // CommonName = "Siva Shankaran", // Role = ParticipationRole.RequiredParticipant, // ParticipationStatus = ToDoParticipationStatus.Tentative, // Rsvp = true, // Type = "INDIVIDUAL" //}); attendees.Add(new Attendee("MAILTO:[email protected]") { //Role = ParticipationRole.RequiredParticipant, //ParticipationStatus = ToDoParticipationStatus.Tentative, //Rsvp = true, //Type = "INDIVIDUAL" }); Alarm ealarm = new Alarm(); ealarm.Action = AlarmAction.Display; ealarm.Trigger = new Trigger(TimeSpan.FromMinutes(-30)); const string eTz = "Eastern Standard Time"; var tzi = TimeZoneInfo.FindSystemTimeZoneById(eTz); var timezone = VTimeZone.FromSystemTimeZone(tzi); calendar.AddTimeZone(timezone); // calendar.AddTimeZone(new VTimeZone("America/New_York")); calendar.Method = "REQUEST"; var e = new CalendarEvent { Start = new CalDateTime(estart), End = new CalDateTime(eend), LastModified = new CalDateTime(now), Location = "", Alarms = { ealarm }, Sequence = 0, Summary = "Telephonic Interview", Description = "Calendar sent using Face My Resume App", Status = "CONFIRMED", Attendees = attendees, Organizer = orgniser }; calendar.Events.Add(e); var e2 = new CalendarEvent { Start = new CalDateTime(eend), End = new CalDateTime(eend), LastModified = new CalDateTime(now), Location = "", Alarms = { ealarm }, Sequence = 0, Summary = "InPerson Interview", Description = "Calendar sent using Face My Resume App", Status = "CONFIRMED", Attendees = attendees, Organizer = orgniser }; //calendar.Events.Add(e2); var serializer = new CalendarSerializer(); var serializedCalendar = serializer.SerializeToString(calendar); //return serializedCalendar.Replace("github.com/rianjs/","facemyresume.com/app/").Replace("ical.net", "cal"); return(serializedCalendar.Replace("\n ", "")); }