@@ -3,10 +3,13 @@ package com.github.choonchernlim.calsync.core
33import com.github.choonchernlim.calsync.exchange.ExchangeEvent
44import com.google.api.client.util.DateTime
55import com.google.api.services.calendar.model.Event
6+ import com.google.api.services.calendar.model.EventAttendee
67import com.google.api.services.calendar.model.EventDateTime
78import com.google.api.services.calendar.model.EventReminder
9+ import microsoft.exchange.webservices.data.core.enumeration.property.LegacyFreeBusyStatus
810import microsoft.exchange.webservices.data.core.enumeration.property.MeetingResponseType
911import microsoft.exchange.webservices.data.core.service.item.Appointment
12+ import microsoft.exchange.webservices.data.property.complex.AttendeeCollection
1013import microsoft.exchange.webservices.data.property.complex.MessageBody
1114import org.apache.commons.lang3.StringEscapeUtils
1215import org.joda.time.format.DateTimeFormat
@@ -97,6 +100,26 @@ class Mapper {
97100 static CalSyncEvent toCalSyncEvent (Event event ) {
98101 assert event
99102
103+ def attendees = event. getAttendees()
104+ .collect {
105+ def response = CalSyncEvent.Attendee.Response . NO_RESPONSE
106+ if (it. getResponseStatus() == " accepted" ) {
107+ response = CalSyncEvent.Attendee.Response . ACCEPTED
108+ } else if (it. getResponseStatus() == " declined" ) {
109+ response = CalSyncEvent.Attendee.Response . DECLINED
110+ } else if (it. getResponseStatus() == " tentative" ) {
111+ response = CalSyncEvent.Attendee.Response . TENTATIVE
112+ }
113+
114+ new CalSyncEvent.Attendee (
115+ address : it. getEmail(). toLowerCase(), // address might be lowercased by Google
116+ name : it. getDisplayName(),
117+ response : response,
118+ isOptional : it. isOptional()
119+ )
120+ }
121+ .sort { it. address } // sort because Google might return attendees in a different order
122+
100123 return new CalSyncEvent (
101124 googleEventId : event. getId(),
102125 startDateTime : toJodaDateTime(event. getStart()),
@@ -105,7 +128,11 @@ class Mapper {
105128 location : event. getLocation(),
106129 reminderMinutesBeforeStart : event. getReminders()?. getOverrides()?. get(0 )?. getMinutes(),
107130 body : event. getDescription() ?: null ,
108- isAllDayEvent : isAllDayEvent(event)
131+ isAllDayEvent : isAllDayEvent(event),
132+ attendees : attendees,
133+ organizerAddress : event. getOrganizer(). getEmail(). toLowerCase(), // address might be lowercased by Google
134+ organizerName : event. getOrganizer(). getDisplayName(),
135+ isBusy : event. getTransparency() != " transparent"
109136 )
110137 }
111138
@@ -121,25 +148,68 @@ class Mapper {
121148 return event. getStart(). getDate() && event. getEnd(). getDate()
122149 }
123150
151+ static List<ExchangeEvent.Attendee > toExchangeAttendeeList (AttendeeCollection attendeeCollection ) {
152+ assert attendeeCollection
153+
154+ return attendeeCollection. collect {
155+ new ExchangeEvent.Attendee (
156+ address : it. address,
157+ name : it. name,
158+ response : it. responseType. name()
159+ )
160+ }
161+ }
162+
163+ static CalSyncEvent.Attendee toCalSyncAttendee (ExchangeEvent.Attendee attendee , Boolean optional ) {
164+ assert attendee
165+
166+ def response = CalSyncEvent.Attendee.Response . NO_RESPONSE
167+ if (attendee. response == MeetingResponseType.Accept . name()) {
168+ response = CalSyncEvent.Attendee.Response . ACCEPTED
169+ } else if (attendee. response == MeetingResponseType.Decline . name()) {
170+ response = CalSyncEvent.Attendee.Response . DECLINED
171+ } else if (attendee. response == MeetingResponseType.Tentative . name()) {
172+ response = CalSyncEvent.Attendee.Response . TENTATIVE
173+ }
174+
175+ new CalSyncEvent.Attendee (
176+ address : attendee. address. toLowerCase(), // address might be lowercased by Google
177+ name : attendee. name,
178+ response : response,
179+ isOptional : optional
180+ )
181+ }
182+
124183 /**
125184 * Maps Exchange Event to CalSyncEvent.
126185 *
127186 * @param exchangeEvent Exchange Event
128187 * @param includeEventBody Whether to include event body or not
129188 * @return CalSyncEvent
130189 */
131- static CalSyncEvent toCalSyncEvent (ExchangeEvent exchangeEvent , Boolean includeEventBody ) {
190+ static CalSyncEvent toCalSyncEvent (ExchangeEvent exchangeEvent , Boolean includeEventBody , Boolean includeAttendees ) {
132191 assert exchangeEvent
133192 assert includeEventBody != null
193+ assert includeAttendees != null
194+
195+ def attendees = includeAttendees ? exchangeEvent. requiredAttendees. collect {
196+ toCalSyncAttendee(it, false )
197+ } + exchangeEvent. optionalAttendees. collect {
198+ toCalSyncAttendee(it, true )
199+ }. sort { it. address } : null // sort because Google might return attendees in a different order
134200
135201 return new CalSyncEvent (
136202 startDateTime : exchangeEvent. startDateTime,
137203 endDateTime : exchangeEvent. endDateTime,
138204 subject : exchangeEvent. subject,
139205 location : exchangeEvent. location,
140- reminderMinutesBeforeStart : exchangeEvent. reminderMinutesBeforeStart,
206+ reminderMinutesBeforeStart : exchangeEvent. isReminderSet ? exchangeEvent . reminderMinutesBeforeStart : null ,
141207 body : includeEventBody ? exchangeEvent. body : null ,
142- isAllDayEvent : exchangeEvent. isAllDayEvent
208+ isAllDayEvent : exchangeEvent. isAllDayEvent,
209+ attendees : attendees,
210+ organizerAddress : exchangeEvent. organizerAddress. toLowerCase(), // address might be lowercased by Google
211+ organizerName : exchangeEvent. organizerName,
212+ isBusy : exchangeEvent. isBusy
143213 )
144214 }
145215
@@ -164,14 +234,46 @@ class Mapper {
164234 ]
165235 ) : null
166236
237+ def attendees = calSyncEvent. attendees. collect {
238+ def isOrganizer = it. address == calSyncEvent. organizerAddress
239+
240+ def response = " needsAction"
241+ if (it. response == CalSyncEvent.Attendee.Response . ACCEPTED ) {
242+ response = " accepted"
243+ } else if (it. response == CalSyncEvent.Attendee.Response . DECLINED ) {
244+ response = " declined"
245+ } else if (it. response == CalSyncEvent.Attendee.Response . TENTATIVE ) {
246+ response = " tentative"
247+ }
248+
249+ def name = it. name
250+ if (isOrganizer) {
251+ name = " $name (Organizer)"
252+ }
253+
254+ new EventAttendee (
255+ email : it. address,
256+ displayName : name,
257+ responseStatus : response,
258+ optional : it. isOptional,
259+ organizer : isOrganizer
260+ )
261+ }
262+
167263 return new Event (
168264 id : calSyncEvent. googleEventId,
169265 start : toGoogleEventDateTime(calSyncEvent. isAllDayEvent, calSyncEvent. startDateTime),
170266 end : toGoogleEventDateTime(calSyncEvent. isAllDayEvent, calSyncEvent. endDateTime),
171267 summary : calSyncEvent. subject,
172268 location : calSyncEvent. location,
173269 reminders : reminders,
174- description : calSyncEvent. body
270+ description : calSyncEvent. body,
271+ attendees : attendees,
272+ transparency : calSyncEvent. isBusy ? " opaque" : " transparent" ,
273+ organizer : new Event.Organizer (
274+ email : calSyncEvent. organizerAddress,
275+ displayName : calSyncEvent. organizerName
276+ )
175277 )
176278 }
177279
@@ -188,12 +290,18 @@ class Mapper {
188290 return new ExchangeEvent (
189291 startDateTime : new org.joda.time.DateTime (appointment. start),
190292 endDateTime : new org.joda.time.DateTime (appointment. end),
191- subject : " ${ MY_RESPONSE_TYPE[ appointment.myResponseType] } - ${ appointment. subject} " ,
293+ subject : appointment. subject,
192294 location : appointment. location,
295+ isReminderSet : appointment. isReminderSet,
193296 reminderMinutesBeforeStart : appointment. reminderMinutesBeforeStart,
194297 body : toPlainText(MessageBody . getStringFromMessageBody(appointment. body)),
195298 isCanceled : appointment. isCancelled,
196- isAllDayEvent : appointment. isAllDayEvent
299+ isAllDayEvent : appointment. isAllDayEvent,
300+ optionalAttendees : toExchangeAttendeeList(appointment. optionalAttendees),
301+ requiredAttendees : toExchangeAttendeeList(appointment. requiredAttendees),
302+ organizerAddress : appointment. organizer. address,
303+ organizerName : appointment. organizer. name,
304+ isBusy : appointment. legacyFreeBusyStatus == LegacyFreeBusyStatus.Busy
197305 )
198306 }
199307
0 commit comments