]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartICalViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1275 d1b88da0-ebda-0310...
[scalable-opengroupware.org] / UI / MailPartViewers / UIxMailPartICalViewer.m
1 /*
2   Copyright (C) 2004-2005 SKYRIX Software AG
3   
4   This file is part of OpenGroupware.org.
5
6   OGo is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Public License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   OGo is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING. If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 /*
23   UIxMailPartICalViewer
24  
25   Show plain/calendar mail parts.
26 */
27
28 #import <NGObjWeb/WOResponse.h>
29
30 #import <NGExtensions/NSCalendarDate+misc.h>
31 #import <NGExtensions/NSObject+Logs.h>
32
33 #import <NGImap4/NGImap4EnvelopeAddress.h>
34
35 #import <NGCards/iCalCalendar.h>
36 #import <NGCards/iCalEvent.h>
37 #import <NGCards/iCalPerson.h>
38 #import <NGCards/iCalDateTime.h>
39
40 #import <SoObjects/SOGo/SOGoDateFormatter.h>
41 #import <SoObjects/SOGo/SOGoUser.h>
42 #import <SoObjects/Appointments/iCalEntityObject+SOGo.h>
43 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
44 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
45 #import <SoObjects/Mailer/SOGoMailObject.h>
46 #import <SoObjects/Mailer/SOGoMailBodyPart.h>
47
48 #import "UIxMailPartICalViewer.h"
49
50 @implementation UIxMailPartICalViewer
51
52 - (void) dealloc
53 {
54   [storedEventObject release];
55   [storedEvent release];
56   [attendee release];
57   [item release];
58   [inCalendar release];
59   [inEvent release];
60   [dateFormatter release];
61   [super dealloc];
62 }
63
64 /* maintain caches */
65
66 - (void) resetPathCaches
67 {
68   [super resetPathCaches];
69   [inEvent release]; inEvent = nil;
70   [inCalendar release]; inCalendar = nil;
71   [storedEventObject release]; storedEventObject = nil;
72   [storedEvent release]; storedEvent = nil;
73  
74   /* not strictly path-related, but useless without it anyway: */
75   [attendee release]; attendee = nil;
76   [item release]; item = nil;
77 }
78
79 /* raw content handling */
80
81 - (NSStringEncoding) fallbackStringEncoding
82 {
83   /*
84     iCalendar invitations sent by Outlook 2002 have the annoying bug that the
85     mail states an UTF-8 content encoding but the actual iCalendar content is
86     encoding in Latin-1 (or Windows Western?).
87  
88     As a result the content decoding will fail (TODO: always?). In this case we
89     try to decode with Latin-1.
90  
91     Note: we could check for the Outlook x-mailer, but it was considered better
92     to try Latin-1 as a fallback in any case (be tolerant).
93   */
94   return NSISOLatin1StringEncoding;
95 }
96
97 /* accessors */
98
99 - (iCalCalendar *) inCalendar
100 {
101   if (!inCalendar)
102     {
103       inCalendar
104         = [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
105       [inCalendar retain];
106     }
107
108   return inCalendar;
109 }
110
111 - (BOOL) couldParseCalendar
112 {
113   return (([self inCalendar]));
114 }
115
116 - (iCalEvent *) inEvent
117 {
118   NSArray *events;
119  
120   if (!inEvent)
121     {
122       events = [[self inCalendar] events];
123       if ([events count] > 0)
124         inEvent = [[events objectAtIndex:0] retain];
125     }
126
127   return inEvent;
128 }
129
130 /* formatters */
131
132 - (SOGoDateFormatter *) dateFormatter
133 {
134   if (!dateFormatter)
135     {
136       dateFormatter = [[context activeUser] dateFormatterInContext: context];
137       [dateFormatter retain];
138     }
139
140   return dateFormatter;
141 }
142
143 /* below is copied from UIxAppointmentView, can we avoid that? */
144
145 - (void) setAttendee: (id) _attendee
146 {
147   ASSIGN (attendee, _attendee);
148 }
149
150 - (id) attendee
151 {
152   return attendee;
153 }
154
155 - (NSString *) _personForDisplay: (iCalPerson *) person
156 {
157   NSString *fn, *email, *result;
158
159   fn = [person cnWithoutQuotes];
160   email = [person rfc822Email];
161   if ([fn length])
162     result = [NSString stringWithFormat: @"%@ <%@>",
163                        fn, email];
164   else
165     result = email;
166
167   return result;
168 }
169
170 - (NSString *) attendeeForDisplay
171 {
172   return [self _personForDisplay: attendee];
173 }
174
175 - (void) setItem: (id) _item
176 {
177   ASSIGN(item, _item);
178 }
179
180 - (id) item
181 {
182   return item;
183 }
184
185 - (NSCalendarDate *) startTime
186 {
187   NSCalendarDate *date;
188   NSTimeZone *timeZone;
189  
190   date = [[self authorativeEvent] startDate];
191   timeZone = [[context activeUser] timeZone];
192   [date setTimeZone: timeZone];
193
194   return date;
195 }
196
197 - (NSCalendarDate *) endTime
198 {
199   NSCalendarDate *date;
200   NSTimeZone *timeZone;
201  
202   date = [[self authorativeEvent] endDate];
203   timeZone = [[context activeUser] timeZone];
204   [date setTimeZone: timeZone];
205
206   return date;
207 }
208
209 - (BOOL) isEndDateOnSameDay
210 {
211   return [[self startTime] isDateOnSameDay:[self endTime]];
212 }
213
214 - (NSTimeInterval) duration
215 {
216   return [[self endTime] timeIntervalSinceDate:[self startTime]];
217 }
218
219 /* calendar folder support */
220
221 - (SOGoAppointmentFolder *) calendarFolder
222 {
223   /* return scheduling calendar of currently logged-in user */
224   SOGoUser *user;
225   id folder;
226
227   user = [context activeUser];
228   folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
229                                                 inContext: context
230                                                 acquire: NO];
231
232   return [folder lookupName: @"personal" inContext: context acquire: NO];
233 }
234
235 - (SOGoAppointmentObject *) storedEventObject
236 {
237   /* lookup object in the users Calendar */
238   SOGoAppointmentFolder *calendar;
239   NSString *filename;
240  
241   if (!storedEventObject)
242     {
243       calendar = [self calendarFolder];
244       if ([calendar isKindOfClass: [NSException class]])
245         [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
246       else
247         {
248           filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
249           if (filename)
250             {
251               storedEventObject = [calendar lookupName: filename
252                                             inContext: [self context]
253                                             acquire: NO];
254               if ([storedEventObject isKindOfClass: [NSException class]])
255                 storedEventObject = nil;
256               else
257                 [storedEventObject retain];
258             }
259         }
260     }
261  
262   return storedEventObject;
263 }
264
265 - (BOOL) isEventStoredInCalendar
266 {
267   return (([self storedEventObject]));
268 }
269
270 - (iCalEvent *) storedEvent
271 {
272   if (!storedEvent)
273     {
274       storedEvent = [[self storedEventObject] component: NO secure: NO];
275       [storedEvent retain];
276     }
277
278   return storedEvent;
279 }
280
281 /* organizer tracking */
282
283 - (NSString *) loggedInUserEMail
284 {
285   NSDictionary *identity;
286
287   identity = [[context activeUser] primaryIdentity];
288
289   return [identity objectForKey: @"email"];
290 }
291
292 - (iCalEvent *) authorativeEvent
293 {
294   iCalEvent *authorativeEvent;
295
296   [self storedEvent];
297   if (!storedEvent
298       || ([storedEvent compare: [self inEvent]] == NSOrderedAscending))
299     authorativeEvent = inEvent;
300   else
301     authorativeEvent = [self storedEvent];
302
303   return authorativeEvent;
304 }
305
306 - (BOOL) isLoggedInUserTheOrganizer
307 {
308   return [[self authorativeEvent] userIsOrganizer: [context activeUser]];
309 }
310
311 - (BOOL) isLoggedInUserAnAttendee
312 {
313   return [[self authorativeEvent] userIsParticipant: [context activeUser]];
314 }
315
316 /* derived fields */
317
318 - (NSString *) organizerDisplayName
319 {
320   iCalPerson *organizer;
321   NSString *value;
322
323   organizer = [[self authorativeEvent] organizer];
324   if (organizer)
325     value = [self _personForDisplay: organizer];
326   else
327     value = @"[todo: no organizer set, use 'from']";
328
329   return value;
330 }
331
332 /* replies */
333
334 - (NGImap4EnvelopeAddress *) replySenderAddress
335 {
336   /* 
337      The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
338      the actual attendees. BUT the attendee field contains the reply-status!
339   */
340   id tmp;
341  
342   tmp = [[self clientObject] fromEnvelopeAddresses];
343   if ([tmp count] == 0) return nil;
344   return [tmp objectAtIndex:0];
345 }
346
347 - (NSString *) replySenderEMail
348 {
349   return [[self replySenderAddress] email];
350 }
351
352 - (NSString *) replySenderBaseEMail
353 {
354   return [[self replySenderAddress] baseEMail];
355 }
356
357 - (iCalPerson *) inReplyAttendee
358 {
359   NSArray *attendees;
360  
361   attendees = [[self inEvent] attendees];
362   if ([attendees count] == 0)
363     return nil;
364   if ([attendees count] > 1)
365     [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
366  
367   return [attendees objectAtIndex:0];
368 }
369
370 - (iCalPerson *) storedReplyAttendee
371 {
372   /*
373     TODO: since an attendee can have multiple email addresses, maybe we
374     should translate the email to an internal uid and then retrieve
375     all emails addresses for matching the participant.
376  
377     Note: -findParticipantWithEmail: does not parse the email!
378   */
379   iCalEvent *e;
380   iCalPerson *p;
381
382   p = nil;
383  
384   e = [self storedEvent];
385   if (e)
386     {
387       p = [e findParticipantWithEmail: [self replySenderBaseEMail]];
388       if (!p)
389         p = [e findParticipantWithEmail:[self replySenderEMail]];
390     }
391
392   return p;
393 }
394
395 - (BOOL) isReplySenderAnAttendee
396 {
397   return (([self storedReplyAttendee]));
398 }
399
400 - (iCalPerson *) _emailParticipantWithEvent: (iCalEvent *) event
401 {
402   NSString *emailFrom;
403   SOGoMailObject *mailObject;
404   NGImap4EnvelopeAddress *address;
405
406   mailObject = [[self clientObject] mailObject];
407   address = [[mailObject fromEnvelopeAddresses] objectAtIndex: 0];
408   emailFrom = [address baseEMail];
409
410   return [event findParticipantWithEmail: emailFrom];
411 }
412
413 - (BOOL) hasSenderStatusChanged
414 {
415   iCalPerson *emailParticipant, *calendarParticipant;
416
417   [self inEvent];
418   [self storedEvent];
419   emailParticipant = [self _emailParticipantWithEvent: inEvent];
420   calendarParticipant = [self _emailParticipantWithEvent: storedEvent];
421
422   return ([[emailParticipant partStat]
423             caseInsensitiveCompare: [calendarParticipant partStat]]
424           != NSOrderedSame);
425 }
426
427 @end /* UIxMailPartICalViewer */