iCal client support for multiple event requests - c#

When a user signs up on our website, I'd like to send them an email that allows them to automatically update their calendar with the classes they have enrolled in. In most cases this will be multiple days/events.
As a test I'm using DDay.ical to create a multi-event request. However, it doesn't seem like either Outlook or the iPhone mail app notices the second event in the ical attachment.
I know that multiple events are supported in the iCal standard. How that doesn't mean that all clients support that scenarios. Do other clients support multi-event ical requests?
I don't think I'm doing anything wrong in code, but I'll post my code fragment to be sure:
// Create event part.
iCalendar iCal1 = new iCalendar();
iCal1.AddLocalTimeZone();
iCal1.Method = "REQUEST";
Event evt1 = iCal1.Create<Event>();
evt1.Start = new iCalDateTime(new DateTime(2014, 8, 4, 12, 30, 00, DateTimeKind.Local));
evt1.End = evt1.Start.AddMinutes(30);
evt1.IsAllDay = false;
evt1.Summary = string.Format("Lesson - {0}", evt1.Start.ToString("MM/dd"));
evt1.Location = "Anytown";
// Add recipients for appointment.
Attendee att1 = new Attendee("mailto:" + "me#MyDomain.com");
att1.RSVP = false;
att1.CommonName = "Me Jones";
evt1.Attendees.Add(att1);
Event evt2 = iCal1.Create<Event>();
evt2.Start = new iCalDateTime(new DateTime(2014, 8, 11, 12, 30, 00, DateTimeKind.Local));
evt2.End = evt1.Start.AddMinutes(30);
evt2.IsAllDay = false;
evt2.Summary = string.Format("Lesson - {0}", evt2.Start.ToString("MM/dd"));
evt2.Location = "AnyTown";
// Add recipients for appointment.
Attendee att2 = new Attendee("mailto:" + "me#MyDomain.com");
att2.RSVP = false;
att2.CommonName = "Me Jones";
evt2.Attendees.Add(att2);
iCalendarSerializer serializer1 = new iCalendarSerializer();
string t = serializer1.SerializeToString(iCal1);
Byte[] bytes = System.Text.Encoding.ASCII.GetBytes(t);
using (var ms = new System.IO.MemoryStream(bytes))
{
using (var a = new System.Net.Mail.Attachment(ms, "meeting.ics", "text/calendar")) //Either load from disk or use a MemoryStream bound to the bytes of a String
{
a.ContentDisposition.Inline = true; //Mark as inline
msg.Attachments.Add(a); //Add it to the message
Mailer.Send(msg);
}
}

Unfortunately, you're completely dependent on the implementation of Icalendar in the various email clients, and these are generally very protective of their users' calendars. They generally all support multi-event Icalendars, but invitations that "go straight in" to a users calendar have to be sent one event at a time. I'm not aware of any exceptions to this.
To process an Icalendar attachment containing more than one event, in Outlook for example, you need to save the attachment to disk, navigate to it and open it. It then opens as a separate calendar, and you need to drag the events one by one into your calendar. Nightmare. This will rarely be worth the trouble to develop.
Another option of course is to host the Icalendar on your website and get your users to subscribe by entering the calendar URL in their client. This has the advantage that changes propagate automatically, but email clients will still treat the events as external (no automatic reminders, in outlook the default is to display them in a separate pane, Gmail at least displays events from different calendars on the same grid.)

Related

Add an alert to an event in Ical.net

I am using ical.net to provide outlook internet calendar integration for my solution.
I have several events from 00:00 a.m. to 00:00 a.m. (next day).
When I add an alarm to the events, in Outlook these events show with no alert.
This is the code how I added the alarms and events.
foreach (var taskItem in taskItems.Where(t => t.DueDate != null && t.DueDate.HasValue == true))
{
var hyperlink = Request.GetBaseUrl();
hyperlink = string.Format("{0}/TaskBoard/Tasks?listId={1}", hyperlink, taskItem.ListId);
var dueDate = new DateTime(taskItem.DueDate.Value.Ticks, DateTimeKind.Utc);
var alarm = new Alarm()
{
Summary = taskItem.Title,
Trigger = new Trigger(TimeSpan.FromMinutes(-15)),
Action = AlarmAction.Display
};
var calendarEvent = new Event
{
Class = "PUBLIC",
Summary = taskItem.Title,
Created = new CalDateTime(taskItem.Created.Value),
Description = string.Format("Open board: {0}", hyperlink),
Start = new CalDateTime(dueDate),
End = new CalDateTime(dueDate.AddDays(1)),
Uid = taskItem.Id.ToString(),
Location = taskItem.ListTitle
};
calendarEvent.Alarms.Add(alarm);
calendar.Events.Add(calendarEvent);
}
this is the resulting iCal file content
BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN
VERSION:2.0
X-WR-CALNAME:Agile Kanban - Meine Aufgaben
BEGIN:VEVENT
CLASS:PUBLIC
CREATED:20170814T114839
DESCRIPTION:Open board: https://localhost:44300/TaskBoard/Tasks?listId=637
90e98-cacc-4f03-992f-f3276db06dda
DTEND:20170827T220000Z
DTSTAMP:20170829T170757Z
DTSTART:20170826T220000Z
LOCATION:Room1
SEQUENCE:0
SUMMARY:Task changed
UID:1d4b10bf-7434-41d9-8dd2-311e3679b0a7
BEGIN:VALARM
ACTION:Display
SUMMARY:Task changed
TRIGGER:-PT15M
END:VALARM
END:VEVENT
END:VCALENDAR
How are the event added to Outlook ?
If they are made available as an http subscription, Outlook is probably ignoring it on purpose. How one wants to be notified in advance is a really a personal choice so calendar clients tend to ignore alarms from outside sources, whether added via an invitations (see Sent email with iCal to outlook with valarm reminder ) or via public calendar subscriptions.
If you are doing an import of the task and the alarms still do not show up, there might be a problem with your iCalendar stream so seing the actual iCalendar stream instead of your code would be more useful.
Finally, I vaguely remember Outlook handling only absolute alarms (see https://www.rfc-editor.org/rfc/rfc5545#section-3.8.6.3) for VTODO but I do not know whether it is still the case.

Is it possible to directly open the Outlook meeting window?

I am trying to have my application to open the Outlook meeting window with some pre-populated fields.
I have found that this question was already asked here.
However, the code provided in the answer(which works fine) doesn't open the meeting window but the appointement window. Those are two different things that are handled differently in Outlook and what I need is indeed the meeting window.
Is there any way to achieve this or do I absolutely have to open the appointement window first and then invite people to turn it into a meeting?
Create an appointment just as in the other question, but then set the MeetingStatus property of the appointment.
Microsoft.Office.Interop.Outlook.Application outlookApplication = new Microsoft.Office.Interop.Outlook.Application(); ;
Microsoft.Office.Interop.Outlook.AppointmentItem appointmentItem = (Microsoft.Office.Interop.Outlook.AppointmentItem)outlookApplication.CreateItem(Microsoft.Office.Interop.Outlook.OlItemType.olAppointmentItem);
// This line was added
appointmentItem.MeetingStatus = Microsoft.Office.Interop.Outlook.OlMeetingStatus.olMeeting;
appointmentItem.Subject = "Meeting Subject";
appointmentItem.Body = "The body of the meeting";
appointmentItem.Location = "Room #1";
appointmentItem.Start = DateTime.Now;
appointmentItem.Recipients.Add("test#test.com");
appointmentItem.End = DateTime.Now.AddHours(1);
appointmentItem.ReminderSet = true;
appointmentItem.ReminderMinutesBeforeStart = 15;
appointmentItem.Importance = Microsoft.Office.Interop.Outlook.OlImportance.olImportanceHigh;
appointmentItem.BusyStatus = Microsoft.Office.Interop.Outlook.OlBusyStatus.olBusy;
appointmentItem.Recipients.ResolveAll();
appointmentItem.Display(true);
One more note to NineBerries good solution, because I had an issue here:
The line
appointmentItem.Recipients.ResolveAll();
is necessary if you have optional attendees in the meeting.
Otherwise they will be reset to "required" even if you set
recipient.Type = Microsoft.Office.Interop.Outlook.OlMeetingRecipientType.olOptional;
before, which is due to "late auto-resolving" of names in Outlook (or so it seems).

DocuSign SOAP API Title Tab does not keep value

I am using DocuSign SOAP API in an ASP.NET app in C# to send some docs for e-signature.
One of the field is the title tab. I have the following code for that.
When testing, the tab correctly shows the title, which is picked up from the back-end DB. But when I see the completed document, the title is changed to something else. Does anyone know how can I resolve this?
When signing, if I modify the value - add and remove space - it works OK.
tab5 = new DocuSignAPI.Tab();
tab5.RecipientID = rcpt1.ID;
tab5.DocumentID = docId;
tab5.Type = DocuSignAPI.TabTypeCode.Custom;
tab5.CustomTabType = DocuSignAPI.CustomTabType.Text;
tab5.Name = "clientTitle";
tab5.CustomTabTypeSpecified = true;
tab5.Value = (dr["Rcpt_1_Role"]).ToString();
tab5.Type = DocuSignAPI.TabTypeCode.Title;
tab5.AnchorTabItem = new DocuSignAPI.AnchorTab();
tab5.AnchorTabItem.AnchorTabString = "CLIENT TITLE:";
tab5.AnchorTabItem.Unit = DocuSignAPI.UnitTypeCode.Pixels;
tab5.AnchorTabItem.UnitSpecified = false;
tab5.AnchorTabItem.IgnoreIfNotPresent = true;
tab5.AnchorTabItem.UnitSpecified = true;
tab5.AnchorTabItem.YOffset = -10;
tab5.AnchorTabItem.XOffset = 100;
When using certain DocuSign tab types (such as titleTabs or emailTabs for instance) the DocuSign platform will populate some of that information from the user's account if they have one.
For example, if the user has a DocuSign account where they have entered the title "CEO", then whenever you send an envelope to that exact recipient (name and email combo) and you use a titleTab the system will populate from their account.
I do not believe there is a way to override this, probably your best option is to just use a textTab instead and with that you can populate with any data from a database or wherever else you want to supply it from.

Creating appointment on Exchange server calendar as other user without impersonation (EWS)

I am creating simple app for appointments scheduling and I want to implement ability for me to create appointments for my users.
I managed to create,update and delete my calendar on Exchange Server, and I somewhat managed to create appointments adding my colleagues as RequiredAttendees like so:
//service variable is being created using my credidentals
Appointment meeting = new Appointment(service);
meeting.Subject = "Some subject ";
meeting.Body = "Some body.";
meeting.Start = DateTime.Now;
meeting.End = meeting.Start.AddHours(4);
meeting.Location = "Some Location";
meeting.RequiredAttendees.Add("myCollegue#mail.com");
meeting.ReminderMinutesBeforeStart = 60;
meeting.Save(new FolderId(WellKnownFolderName.Calendar,
"myCollegue#mail.com"),
SendInvitationsMode.SendToAllAndSaveCopy);
But it is just setting him as required attendee. Next thing is I tried using impersonation, but I can't access hosting server to set myself as master and others to have to share calendar with me (due to permissions and stuff) so I had to scrape that as well. Also, he set me up to be his publishing author on his calendar.
Is there something I am missing, or can't seem to find on MSDN sites?
EDIT: I am able to create appointment in his calendar in outlok.
If anyone comes across same issues as I did in here please follow these steps:
Make sure that person for which you are creating appointment sets you up (on exchange server or in outlok as "Editing author" with all permissions.
After that you can create appointments for him (verify this by going to your outlok and creating some test appointments).
This code works for me:
Folder inboxFolder = Folder.Bind(service, new FolderId(WellKnownFolderName.Calendar, "your.colleague#company.com"));
Appointment appointmentOther = new Appointment(service);
appointmentOther.Subject = "Test 2";
appointmentOther.Body = "Body text";
appointmentOther.Start = DateTime.Now;
appointmentOther.End = DateTime.Today.AddHours(16);
appointmentOther.Location = "My Office";
appointmentOther.IsReminderSet = true;
appointmentOther.ReminderMinutesBeforeStart = 30;
appointmentOther.Save(inboxFolder.Id,SendInvitationsMode.SendToNone);
Good luck :)

What is the proper way to serve iCal Events?

I am attempting to allow users to add events from an online calendar to the calendars on their device using DDay.iCal. This seems to work fine on iOS and on desktop platforms, but I am running into a snag with Android devices. I run into this message:
Is there a better way to serve this event that would keep that from happening?
public ActionResult ICS(int id)
{
// Get event from Database
var heEvent = HEEvent.GetEventDetails(id);
// Create iCal object
var iCal = new iCalendar();
iCal.Method = "PUBLISH";
// Create iCal Event
var icalEvent = iCal.Create<DDay.iCal.Event>();
icalEvent.Summary = heEvent.Name;
icalEvent.Start = new iCalDateTime(heEvent.TimeBegin.Year, heEvent.TimeBegin.Month, heEvent.TimeBegin.Day, heEvent.TimeBegin.Hour, heEvent.TimeBegin.Minute, 00);
TimeSpan calculatedEventDuration = heEvent.DateEnd.Subtract(heEvent.TimeBegin);
if (calculatedEventDuration.Hours > 1) { icalEvent.Duration = calculatedEventDuration; }
else { icalEvent.Duration = TimeSpan.FromHours(1); } // default to 1 hour if event time is less
icalEvent.Location = heEvent.Location;
// Create a serialization context and serializer factory.
// These will be used to build the serializer for our object.
ISerializationContext ctx = new SerializationContext();
ISerializerFactory factory = new DDay.iCal.Serialization.iCalendar.SerializerFactory();
// Get a serializer for our object
IStringSerializer serializer = factory.Build(iCal.GetType(), ctx) as IStringSerializer;
string output = serializer.SerializeToString(iCal);
var contentType = "text/calendar";
var bytes = Encoding.UTF8.GetBytes(output);
return File(bytes, contentType, String.Format(#"{0}.ics", heEvent.Name.Replace(" ", "_")));
}
Your error, you don't need me to tell you, has nothing to do with Ical. It's entirely between you, your phone, and your technology budget. (Buy an SD card !)
As regards serving Icalendars, you often hear them called feeds, but clients subscribed to an Icalendar poll the calendar url regularly, requesting the whole calendar each time. To avoid lots of needless processing, you will want to persist the calendar(s) somehow so that changes propagate without regenerating the same calendar thousands of times. For this, I suggest the file system and good use of HTTP caching headers. When your calendar is modified, write it as a static file in a web facing directory. Perhaps you're already doing this. Then set sensible caching headers on your webserver and away you go.

Categories