Using iCal to update and remove calendar items - updates

I have a web app that has its own calendar system. I want to send email notifications to users when they add / update / remove calendar events.
I am using iCal via a nuget package Ical.Net to create the event attachments. This works great when adding a new calendar event. I am able to get the email with the iCal attachment and accept it and it adds it to my google calendar.
The question I have is this: Is there a way using iCal to send an Update or Remove type object that will affect the previously added calendar event?
I tried using the same Uid in the second iCal object, but that didn't work.
Update: This is the meeting creation stream:
BEGIN:VCALENDAR
PRODID:-//github.com/rianjs/ical.net//NONSGML ical.net 2.2//EN
VERSION:2.0
BEGIN:VEVENT
DESCRIPTION:hi
DTEND:20171019T200000
DTSTAMP:20171018T022927Z
DTSTART:20171019T193000
SEQUENCE:0
STATUS:Confirmed
SUMMARY:test
UID:2291d1c2-3b6d-404a-82ac-16807ecfd580
END:VEVENT
END:VCALENDAR
The Update stream is the same with just different start and end times.
UPDATE 2:
I have read a lot of posts and the RFC 2446 to try to get this right and am still having trouble. Here are my 3 examples for the original item, an update to change the date and then a cancel. I end up with a single event only at the original time. It doesn't change the time with the update and it doesn't cancel the event.
I also tried putting the METHOD: line inside the VEVENT because I thought I read that would work when reading the RFC 2446. when I did that I ended up with 3 events. The original and 2 at the new date.
Initial event
BEGIN:VCALENDAR
PRODID:-//RealtimeMD//Medical Portal//EN
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20171020T023000Z
DTEND:20171020T030000Z
DTSTAMP:20171020T015303Z
ORGANIZER;CN=Bob Docter:mailto:drbob#shatalmic.com
UID:24258c34-2888-43ec-ac3d-180ee2603294
CREATED:20171020T015303Z
X-ALT-DESC;FMTTYPE=text/html:
LAST-MODIFIED:20171020T015304Z
LOCATION:RealtimeMD Zoom
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:t
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
Update Event
BEGIN:VCALENDAR
PRODID:-//RealtimeMD//Medical Portal//EN
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20171021T023000Z
DTEND:20171021T030000Z
DTSTAMP:20171020T015303Z
ORGANIZER;CN=Bob Docter:mailto:drbob#shatalmic.com
UID:24258c34-2888-43ec-ac3d-180ee2603294
CREATED:20171020T015303Z
X-ALT-DESC;FMTTYPE=text/html:
LAST-MODIFIED:20171020T015340Z
LOCATION:RealtimeMD Zoom
SEQUENCE:1
STATUS:CONFIRMED
SUMMARY:t
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR
Cancel event
BEGIN:VCALENDAR
PRODID:-//RealtimeMD//Medical Portal//EN
VERSION:2.0
METHOD:CANCEL
BEGIN:VEVENT
DTSTART:20171021T023000Z
DTEND:20171021T030000Z
DTSTAMP:20171020T015303Z
ORGANIZER;CN=Bob Docter:mailto:drbob#shatalmic.com
UID:24258c34-2888-43ec-ac3d-180ee2603294
CREATED:20171020T015303Z
X-ALT-DESC;FMTTYPE=text/html:
LAST-MODIFIED:20171020T015433Z
LOCATION:RealtimeMD Zoom
SEQUENCE:2
STATUS:CANCELLED
SUMMARY:t
TRANSP:OPAQUE
END:VEVENT
END:VCALENDAR

If you are sending those over email, you want to send an iTIP message with a PUBLISH method as per https://www.rfc-editor.org/rfc/rfc5546#section-3.2.1 . This has also implication on how you construct your email message. See https://www.rfc-editor.org/rfc/rfc6047

Related

What is the best practice to design REST URI for the following use case

A user can singup in an application
/users
GET /id get userDetails
POST saveUserDetails
A admin can create events for a user to register
/events
POST saveEvent
PUT /{event-id} for updating event
DELETE /{event-id} deleting an event
Now, the question is where would you place user to event registration uri?
will it be
/events/{event-id}/register
or
/users/{user-id}/events?event-id=xxx
and I also want to get list of the events to which the user has registered
Please help me with better API design
There's no right answers here, so I'll just throw in the hat the patterns that I would probably use:
POST /user - register a new user
GET /user/:id - Get user info
PUT /user/:id - Update user info
POST /event - create an event
PUT /event/:id - Update an event
GET /event/:id - Get event details
DELETE /event/:id - Delete event
GET /event/:id/attendees - Get list of attendees
POST /event/:id/attendees - Add user to list of attendees
GET /user/:id/event - Get list of events that user is attendee of
The main goal is to register to an event. Then you should start with /events
This is why /events/{event-id}/register looks fine to me.
There are various ways by which you can achieve this but what I see as the best practice is that you should be able to understand from the URL itself that what you're trying to achieve.
For example,
Admin wants to create an event:
POST: {basePath}/a/events or {basePath}/admin/events
a | admin: defines that this action is being performed by an Admin
events: defines that operation is being performed on events entity
Now User wants to register for the event, hence
POST: {basePath}/u/events/{eventId} or {basePath}/user/events/{eventId}
u | user: defines that this action is being performed by the User
events: defines that operation is being performed on events entity. Which can be multiple entities hence adding eventId in the path variable to define which you need to register.
now in the above example, I am assuming you can get the User details in the request
or capture it from the session but if that is not the case you want to follow then you might need to use below example (assuming path variable is the only option for you to get the User context)
POST: {basePath}/u/{Id}/events/{eventId} or {basePath}/users/{userId}/events/{eventId}
You can read more here about naming guide if you like.

EWS API: Anything I call update_item for a meeting, it ends up canceling the meeting for any attendees

I'm using the EWS API (targeting Exchange 2010) I cannot figure out why anything I make a request to update a calendar meeting/item. Is there something I'm missing or is this the expected behavior? I'm able to make a change from Outlook to the meeting without it sending out a cancellation email to all attendees except the organizer.
The below is a sample of the relevant portion of the SOAP request. I replaced the actual attendee's email addresses, item ID and change key.
{:send_meeting_invitations_or_cancellations=>"SendOnlyToChanged",:conflict_resolution=>"AutoResolve", :item_changes=>[{:updates=>[{:set_item_field=>{:field_uRI=>{:field_uRI=>"item:Subject"},
:calendar_item=>{:sub_elements=>[{:subject=>{:text=>"Thursday 11AM Recurring"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"item:Body"}, :calendar_item=>{:sub_elements=>[{:body=>{"BodyType"=>"HTML", :text=>"HI"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:Location"},:calendar_item=>{:sub_elements=>[{:location=>{:text=>"LA, CA"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:Start"}, :calendar_item=>{:sub_elements=>[{:start=>{:text=>"2019-03-07T19:00:00.0000000"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:StartTimeZone"}, :calendar_item=>{:sub_elements=>[{:start_time_zone=>{"Id"=>"UTC"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:End"}, :calendar_item=>{:sub_elements=>[{:end=>{:text=>"2019-03-07T19:30:00.0000000"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:EndTimeZone"},:calendar_item=>{:sub_elements=>[{:end_time_zone=>{"Id"=>"UTC"}}]}}},
{:set_item_field=>{:field_uRI=>{:field_uRI=>"calendar:RequiredAttendees"},:calendar_item=>{:sub_elements=>[{:required_attendees=>{:sub_elements=>[
{:mailbox=>{:name=>"Joe ABC",:email_address=>"joe#abc.com", :routing_Type=>"SMTP"}},
{:mailbox=>{:name=>"John ABC", :email_address=>"john#abc.com",:routing_Type=>"SMTP"}}]}}]}}}],
:item_id=>{:id=>"AAMkAGJjNWIzNzI2LTM1N2YtNDVkMi05NzU5LTRj",
:change_key=>"DwAAABYAAADWK9s0Y5iSSLP"}}]}
If you use Set_Item_Field to update the required attendees this will overwrite the current property so if any attendees aren't in the updated collection your effectively canceling the meeting for those attendees. If you want to use Set_Item_Field to update attendees then you need to include all the attendees for that type because your replacing the collection for the recipient type not appending to it.

How do I construct a link to a Google Calendar event?

If I have the event ID of a Google Calendar event, for example:
4htgpmm1hmak744r0kbkdcodar#google.com
How can I construct a URL to view the event details in the Google Calendar interface?
For example, if I go to a google calendar event, I see a URL such as this:
https://calendar.google.com/calendar/r/eventedit/NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
What function/algorithm can I run to go from 4htgpmm1hmak744r0kbkdcodar#google.com to NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw?
Bonus: A bonus would be if there was also a shortcut method that worked on a CalendarEvent via Google Apps Script. For example, if there was a method such as getUrl() just like there is a getId() method.
Since the same event can exist in multiple calendars (e.g. by inviting others to it), the event id is not sufficient information.
To construct a URL you need both the event id (refresher for how find it), AND the Calendar ID (see below for instructions as to how to find it). You'll then need to encode both as base64, and finally you'll you have a valid Google Calendar event link.
For the following instructions, imagine we have an event, Event1, and it exists in both calendars, CalendarA and CalendarB.
In the Google Calendar interface, you would be able to view any of these combinations:
Event1 in CalendarA
Event1 in CalendarB
Let's assign the following IDs:
Event1 ID: 4htgpmm1hmak744r0kbkdcodar#google.com
CalendarA ID: eugu9kan1ddi1pmi6sk3ib56g#group.calendar.google.com
CalendarB ID: afd3zeuguoepmi32s5i56g#group.calendar.google.com
To construct an event URL:
Get the event-id, which is the event id without the #google.com
E.g. for Event1 we have 4htgpmm1hmak744r0kbkdcodar
Get the calendar-id, which is the calendar id with the #group.calendar.google.com replaced with #g
E.g. for CalendarA we have eugu9kan1ddi1pmi6sk3ib56g#g
Join the two values with a space in the middle.
<event-id> <calendar-id>
E.g. for Event1 + CalendarA, we have:
4htgpmm1hmak744r0kbkdcodar eugu9kan1ddi1pmi6sk3ib56g#g
Encode the value as base64 (e.g. on https://www.base64decode.org):
NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw==
Remove the training ==:
NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
You can now append this value to https://www.google.com/calendar/event?eid= (view event page) or https://calendar.google.com/calendar/r/eventedit/ (edit event page):
https://www.google.com/calendar/event?eid=NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
or
https://calendar.google.com/calendar/r/eventedit/NGh0Z3BtbTFobWFrNzQ0cjBrYmtkY29kYXIgZXVndTlrYW4xZGRpMXBtaTZzazNpYjWoNmdAZw
You're done! That URL will take you to the calendar event in the Google interface.
How to find the calendar ID
If you need instructions for how to find the calendar id...
To find the calendar ID:
Go to the calendar settings
Scroll down until you find the calendar id field.
This might be a value that ends with #group.calendar.google.com, your email address, or some other value.
If you are having trouble finding the calendar id, another tool that might help is adding the eventdeb=1 parameter to the URL, going to the Troubleshooting info of an event, and then looking for either the organizer or participant values, both of which contain calendar ids.
In Apps Script
var eventUrl = "https://www.google.com/calendar/event?eid=" +
Utilities.base64Encode(event.getId().split('#')[0] +
" " +
event.getOriginalCalendarId())
.replace(/\=/g, '');
#Senseful had was a super helpful answer, and it works!
A few important things to note adding to the above. Firstly, when base64 encoding, you'll end up with a == at the end, remove that
Second, the base calendar URL will not work when the calendar the event belongs to does not belong in the primary account the user is logged in with. For example, if you're logged into 3 accounts, you'll have a different URL for each one e.g.
https://calendar.google.com/calendar/r/...
https://calendar.google.com/calendar/b/2/r/...
https://calendar.google.com/calendar/b/3/r/...
and so on... I don't think there's a solution that'll help with this situation since it's impossible to predict what accounts a specific user is logged into at any time.

Change exisiting calendar event series date

I have a problem regarding the changing of dates of a Google Calendar Event series. The API doesn't have any methods for changing the dates.
Is there a way I can do this? I am creating a form where when you edit the due date of the request in the spreadsheet, it will also edit the calendar event series.
Your statement is incorrect it is possible to change or update the date of a Google calendar event using the Google Calendar API.
Option nr 1:
You can patch the event in the body you include the variables you would like to update or patch. In this case date.
Events: patch Updates an event.
Request
PATCH https://www.googleapis.com/calendar/v3/calendars/{calendarId}/events/{eventID}?key={YOUR_API_KEY}
{
"start": {
"dateTime": "2008-09-26T12:01:00+02:00"
}
}
Response
200 OK
Option nr 2
Another option would be to first use Events.get to get the event change what you need then do events.update. This will do an update on everything in the event. While Patch just changes one what you need.

Copy a programmatically created recurring event?

Using Google Apps Script, I created a calendar that has a complex recurring event. However, I'm not able to copy that event to another calendar as a recurring event. Note: the recurring rule can't be generated or edited via the web interface.
For a real example, let's say a user wants to copy the recurring event from this publicly shared Google calendar. The event is a kind of template event for a course schedule of a Thursday course at my university.
Here are two difficulties preventing the user from copying the recurring event into another calendar the user has access to:
clicking on an instance of the event and saying "copy to my calendar" only copies that single event, even though the event is defined as repeating. This is not a good solution, since the user would have to do this more than 10 times to get all the events for a semester.
clicking on an instance of the event and then "More details" , then trying to "Copy to xxxx" under the "More actions" menu (where xxxx is a calendar the user owns) appears to work. However, when clicking Save, there is an error: "An error has occurred. Please try again later."
EDIT here's a screen shot of what the Event looks like in Google Calendar (click on event, "more details").
Note that this recurring event is every Monday from the start of the semester to the end, with several exceptions. No holidays (e.g., Mon 2013-02-25), but also including Wed 2013-02-27, on which day the courses will be given as if it were a Monday (according to the university course schedule).
I repeat: the recurring events look fine in Google Calendar, but they can't be copied in there entirety to another calendar.
The GAS function that creates the calendars (not all the code is here):
function createCalendar(calendarName, courseCode, weekday, times, room, isLab) {
Logger.log("Last day: " + LAST_TRIMESTER_DATE);
// hack the last day so that it's not midnight but rather just before the next day, i.e., 23:59:59
var adjustedLastDay = LAST_TRIMESTER_DATE;
adjustedLastDay.setTime(adjustedLastDay.getTime() + (23*60*60*1000) + (59*60*1000) + (59*1000));
Logger.log("Adjusted last day: " + adjustedLastDay);
var eventRecurrence = CalendarApp.newRecurrence();
eventRecurrence.addDailyRule().until(adjustedLastDay).interval(1).onlyOnWeekday(weekday);
// get the day of the week of the first day
var weekdayOfFirstDay = Utilities.formatDate(FIRST_TRIMESTER_DATE, LONG_TIME_ZONE, "EEEE").toUpperCase();
// if this calendar is for a lab, exclude the first week of days
if (isLab) {
eventRecurrence.addDailyExclusion().times(1);
} else {
// it's a course, so exclude the first day if it's not the course day
// -- this is kind of a bug, since the "first day" of the event will always try to be created, even if it's not "onlyOnWeekday" specified
if (weekdayOfFirstDay != weekday.toString()) {
eventRecurrence.addDailyExclusion().times(1);
// eventRecurrence.addDateExclusion(FIRST_TRIMESTER_DATE);
}
}
// Exclude all holidays
for (var i = 0; i
This appears to be a bug in Google Calendar, in that it has trouble copying complex recurrence rules. You can report it to the team by using the "Send feedback" link under the gear icon in the Google Calendar interface.
The other option would be to try and export the ICA and have the user import it. You might be even able to do both with some sort of link button. Here is an example of how to obtain the ica file for a google event;
http://google.com/calendar/ical/[CALENDARID]/public[or private depending]/full/[eventID].ics
This should give the code to the user and they can manually import it into their calendars. You can generate the url and send in an email to all students who sign up for the named course.
T