]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartICalViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1066 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 <SOGoUI/SOGoDateFormatter.h>
29 #import <SoObjects/SOGo/SOGoUser.h>
30 #import <SoObjects/Appointments/SOGoAppointmentFolder.h>
31 #import <SoObjects/Appointments/SOGoAppointmentObject.h>
32 #import <SoObjects/Mailer/SOGoMailObject.h>
33 #import <NGCards/NGCards.h>
34 #import <NGImap4/NGImap4EnvelopeAddress.h>
35 #import "common.h"
36
37 #import "UIxMailPartICalViewer.h"
38
39 @implementation UIxMailPartICalViewer
40
41 - (void)dealloc {
42   [self->storedEventObject release];
43   [self->storedEvent   release];
44   [self->attendee      release];
45   [self->item          release];
46   [self->inCalendar    release];
47   [self->inEvent       release];
48   [self->dateFormatter release];
49   [super dealloc];
50 }
51
52 /* maintain caches */
53
54 - (void)resetPathCaches {
55   [super resetPathCaches];
56   [self->inEvent           release]; self->inEvent           = nil;
57   [self->inCalendar        release]; self->inCalendar        = nil;
58   [self->storedEventObject release]; self->storedEventObject = nil;
59   [self->storedEvent       release]; self->storedEvent       = nil;
60   
61   /* not strictly path-related, but useless without it anyway: */
62   [self->attendee release]; self->attendee = nil;
63   [self->item     release]; self->item     = nil;
64 }
65
66 /* raw content handling */
67
68 - (NSStringEncoding)fallbackStringEncoding {
69   /*
70     iCalendar invitations sent by Outlook 2002 have the annoying bug that the
71     mail states an UTF-8 content encoding but the actual iCalendar content is
72     encoding in Latin-1 (or Windows Western?).
73     
74     As a result the content decoding will fail (TODO: always?). In this case we
75     try to decode with Latin-1.
76     
77     Note: we could check for the Outlook x-mailer, but it was considered better
78           to try Latin-1 as a fallback in any case (be tolerant).
79   */
80   return NSISOLatin1StringEncoding;
81 }
82
83 /* accessors */
84
85 - (iCalCalendar *) inCalendar
86 {
87   if (!inCalendar)
88     {
89       inCalendar
90         = [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
91       [inCalendar retain];
92     }
93
94   return inCalendar;
95 }
96
97 - (BOOL)couldParseCalendar {
98   return [[self inCalendar] isNotNull];
99 }
100
101 - (iCalEvent *)inEvent {
102   NSArray *events;
103   
104   if (self->inEvent != nil)
105     return [self->inEvent isNotNull] ? self->inEvent : nil;
106   
107   events = [[self inCalendar] events];
108   if ([events count] > 0) {
109     self->inEvent = [[events objectAtIndex:0] retain];
110     return self->inEvent;
111   }
112   else {
113     self->inEvent = [[NSNull null] retain];
114     return nil;
115   }
116 }
117
118 /* formatters */
119
120 - (SOGoDateFormatter *)dateFormatter {
121   if (self->dateFormatter == nil) {
122     self->dateFormatter =
123       [[SOGoDateFormatter alloc] initWithLocale:[self locale]];
124     [self->dateFormatter setFullWeekdayNameAndDetails];
125   }
126   return self->dateFormatter;
127 }
128
129 /* below is copied from UIxAppointmentView, can we avoid that? */
130
131 - (void)setAttendee:(id)_attendee {
132   ASSIGN(self->attendee, _attendee);
133 }
134 - (id)attendee {
135   return self->attendee;
136 }
137
138 - (NSString *) _personForDisplay: (iCalPerson *) person
139 {
140   return [NSString stringWithFormat: @"%@ <%@>",
141                    [person cnWithoutQuotes],
142                    [person rfc822Email]];
143 }
144
145 - (NSString *) attendeeForDisplay
146 {
147   return [self _personForDisplay: attendee];
148 }
149
150 - (void)setItem:(id)_item {
151   ASSIGN(self->item, _item);
152 }
153 - (id)item {
154   return self->item;
155 }
156
157 - (NSCalendarDate *) startTime
158 {
159   NSCalendarDate *date;
160   NSTimeZone *timeZone;
161   
162   date = [[self authorativeEvent] startDate];
163   timeZone = [[context activeUser] timeZone];
164   [date setTimeZone: timeZone];
165
166   return date;
167 }
168
169 - (NSCalendarDate *) endTime
170 {
171   NSCalendarDate *date;
172   NSTimeZone *timeZone;
173   
174   date = [[self authorativeEvent] endDate];
175   timeZone = [[context activeUser] timeZone];
176   [date setTimeZone: timeZone];
177
178   return date;
179 }
180
181 - (BOOL)isEndDateOnSameDay {
182   return [[self startTime] isDateOnSameDay:[self endTime]];
183 }
184 - (NSTimeInterval)duration {
185   return [[self endTime] timeIntervalSinceDate:[self startTime]];
186 }
187
188 /* calendar folder support */
189
190 - (id)calendarFolder {
191   /* return scheduling calendar of currently logged-in user */
192   SOGoUser *user;
193   id folder;
194
195   user = [context activeUser];
196   folder = [[user homeFolderInContext: context] lookupName: @"Calendar"
197                                                 inContext: context
198                                                 acquire: NO];
199
200   return folder;
201 }
202
203 - (id)storedEventObject {
204   /* lookup object in the users Calendar */
205   id calendar;
206   
207   if (self->storedEventObject != nil)
208     return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
209   
210   calendar = [self calendarFolder];
211   if ([calendar isKindOfClass:[NSException class]]) {
212     [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
213   }
214   else {
215     NSString *filename;
216     
217     filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
218     if (filename != nil) {
219       // TODO: When we get an exception, this might be an auth issue meaning
220       //       that the UID indeed exists but that the user has no access to
221       //       the object.
222       //       Of course this is quite unusual for the private calendar though.
223       id tmp;
224       
225       tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
226       if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
227         self->storedEventObject = [tmp retain];
228     }
229   }
230   
231   if (self->storedEventObject == nil)
232     self->storedEventObject = [[NSNull null] retain];
233   
234   return self->storedEventObject;
235 }
236
237 - (BOOL)isEventStoredInCalendar {
238   return [[self storedEventObject] isNotNull];
239 }
240
241 - (iCalEvent *)storedEvent {
242   return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
243 }
244
245 /* organizer tracking */
246
247 - (NSString *)loggedInUserEMail {
248   return [[[self context] activeUser] primaryEmail];
249 }
250
251 - (iCalEvent *)authorativeEvent {
252   /* DB is considered master, when in DB, ignore mail organizer */
253   return [self isEventStoredInCalendar]
254     ? [self storedEvent]
255     : [self inEvent];
256 }
257
258 - (BOOL)isLoggedInUserTheOrganizer {
259   NSString *loginEMail;
260   
261   if ((loginEMail = [self loggedInUserEMail]) == nil) {
262     [self warnWithFormat:@"Could not determine email of logged in user?"];
263     return NO;
264   }
265   
266   return [[self authorativeEvent] isOrganizer:loginEMail];
267 }
268
269 - (BOOL)isLoggedInUserAnAttendee {
270   NSString *loginEMail;
271   
272   if ((loginEMail = [self loggedInUserEMail]) == nil) {
273     [self warnWithFormat:@"Could not determine email of logged in user?"];
274     return NO;
275   }
276
277   return [[self authorativeEvent] isParticipant:loginEMail];
278 }
279
280 /* derived fields */
281
282 - (NSString *) organizerDisplayName
283 {
284   iCalPerson *organizer;
285   NSString *value;
286
287   organizer = [[self authorativeEvent] organizer];
288   if (organizer)
289     value = [self _personForDisplay: organizer];
290   else
291     value = @"[todo: no organizer set, use 'from']";
292
293   return value;
294 }
295
296 /* replies */
297
298 - (NGImap4EnvelopeAddress *)replySenderAddress {
299   /* 
300      The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
301      the actual attendees. BUT the attendee field contains the reply-status!
302   */
303   id tmp;
304   
305   tmp = [[self clientObject] fromEnvelopeAddresses];
306   if ([tmp count] == 0) return nil;
307   return [tmp objectAtIndex:0];
308 }
309
310 - (NSString *)replySenderEMail {
311   return [[self replySenderAddress] email];
312 }
313 - (NSString *)replySenderBaseEMail {
314   return [[self replySenderAddress] baseEMail];
315 }
316
317 - (iCalPerson *)inReplyAttendee {
318   NSArray *attendees;
319   
320   attendees = [[self inEvent] attendees];
321   if ([attendees count] == 0)
322     return nil;
323   if ([attendees count] > 1)
324     [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
325   
326   return [attendees objectAtIndex:0];
327 }
328 - (iCalPerson *)storedReplyAttendee {
329   /*
330     TODO: since an attendee can have multiple email addresses, maybe we
331           should translate the email to an internal uid and then retrieve
332           all emails addresses for matching the participant.
333           
334     Note: -findParticipantWithEmail: does not parse the email!
335   */
336   iCalEvent  *e;
337   iCalPerson *p;
338   
339   if ((e = [self storedEvent]) == nil)
340     return nil;
341   if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
342     return p;
343   if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
344     return p;
345   return nil;
346 }
347 - (BOOL)isReplySenderAnAttendee {
348   return [[self storedReplyAttendee] isNotNull];
349 }
350
351 /* action URLs */
352
353 - (id)acceptLink {
354   return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
355 }
356 - (id)declineLink {
357   return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
358 }
359 - (id)tentativeLink {
360   return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
361 }
362
363 @end /* UIxMailPartICalViewer */