]> err.no Git - scalable-opengroupware.org/blob - UI/MailPartViewers/UIxMailPartICalViewer.m
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1039 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 <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   NSCalendarDate *date;
159   
160   date = [[self authorativeEvent] startDate];
161   [date setTimeZone:[[self clientObject] userTimeZone]];
162   return date;
163 }
164
165 - (NSCalendarDate *)endTime {
166   NSCalendarDate *date;
167   
168   date = [[self authorativeEvent] endDate];
169   [date setTimeZone:[[self clientObject] userTimeZone]];
170   return date;
171 }
172
173 - (BOOL)isEndDateOnSameDay {
174   return [[self startTime] isDateOnSameDay:[self endTime]];
175 }
176 - (NSTimeInterval)duration {
177   return [[self endTime] timeIntervalSinceDate:[self startTime]];
178 }
179
180 /* calendar folder support */
181
182 - (id)calendarFolder {
183   /* return scheduling calendar of currently logged-in user */
184   return [[[self context] activeUser] schedulingCalendarInContext:
185                                         [self context]];
186 }
187
188 - (id)storedEventObject {
189   /* lookup object in the users Calendar */
190   id calendar;
191   
192   if (self->storedEventObject != nil)
193     return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
194   
195   calendar = [self calendarFolder];
196   if ([calendar isKindOfClass:[NSException class]]) {
197     [self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
198   }
199   else {
200     NSString *filename;
201     
202     filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
203     if (filename != nil) {
204       // TODO: When we get an exception, this might be an auth issue meaning
205       //       that the UID indeed exists but that the user has no access to
206       //       the object.
207       //       Of course this is quite unusual for the private calendar though.
208       id tmp;
209       
210       tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
211       if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
212         self->storedEventObject = [tmp retain];
213     }
214   }
215   
216   if (self->storedEventObject == nil)
217     self->storedEventObject = [[NSNull null] retain];
218   
219   return self->storedEventObject;
220 }
221
222 - (BOOL)isEventStoredInCalendar {
223   return [[self storedEventObject] isNotNull];
224 }
225
226 - (iCalEvent *)storedEvent {
227   return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO];
228 }
229
230 /* organizer tracking */
231
232 - (NSString *)loggedInUserEMail {
233   return [[[self context] activeUser] email];
234 }
235
236 - (iCalEvent *)authorativeEvent {
237   /* DB is considered master, when in DB, ignore mail organizer */
238   return [self isEventStoredInCalendar]
239     ? [self storedEvent]
240     : [self inEvent];
241 }
242
243 - (BOOL)isLoggedInUserTheOrganizer {
244   NSString *loginEMail;
245   
246   if ((loginEMail = [self loggedInUserEMail]) == nil) {
247     [self warnWithFormat:@"Could not determine email of logged in user?"];
248     return NO;
249   }
250   
251   return [[self authorativeEvent] isOrganizer:loginEMail];
252 }
253
254 - (BOOL)isLoggedInUserAnAttendee {
255   NSString *loginEMail;
256   
257   if ((loginEMail = [self loggedInUserEMail]) == nil) {
258     [self warnWithFormat:@"Could not determine email of logged in user?"];
259     return NO;
260   }
261
262   return [[self authorativeEvent] isParticipant:loginEMail];
263 }
264
265 /* derived fields */
266
267 - (NSString *) organizerDisplayName
268 {
269   iCalPerson *organizer;
270   NSString *value;
271
272   organizer = [[self authorativeEvent] organizer];
273   if (organizer)
274     value = [self _personForDisplay: organizer];
275   else
276     value = @"[todo: no organizer set, use 'from']";
277
278   return value;
279 }
280
281 /* replies */
282
283 - (NGImap4EnvelopeAddress *)replySenderAddress {
284   /* 
285      The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
286      the actual attendees. BUT the attendee field contains the reply-status!
287   */
288   id tmp;
289   
290   tmp = [[self clientObject] fromEnvelopeAddresses];
291   if ([tmp count] == 0) return nil;
292   return [tmp objectAtIndex:0];
293 }
294
295 - (NSString *)replySenderEMail {
296   return [[self replySenderAddress] email];
297 }
298 - (NSString *)replySenderBaseEMail {
299   return [[self replySenderAddress] baseEMail];
300 }
301
302 - (iCalPerson *)inReplyAttendee {
303   NSArray *attendees;
304   
305   attendees = [[self inEvent] attendees];
306   if ([attendees count] == 0)
307     return nil;
308   if ([attendees count] > 1)
309     [self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
310   
311   return [attendees objectAtIndex:0];
312 }
313 - (iCalPerson *)storedReplyAttendee {
314   /*
315     TODO: since an attendee can have multiple email addresses, maybe we
316           should translate the email to an internal uid and then retrieve
317           all emails addresses for matching the participant.
318           
319     Note: -findParticipantWithEmail: does not parse the email!
320   */
321   iCalEvent  *e;
322   iCalPerson *p;
323   
324   if ((e = [self storedEvent]) == nil)
325     return nil;
326   if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
327     return p;
328   if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
329     return p;
330   return nil;
331 }
332 - (BOOL)isReplySenderAnAttendee {
333   return [[self storedReplyAttendee] isNotNull];
334 }
335
336 /* action URLs */
337
338 - (id)acceptLink {
339   return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
340 }
341 - (id)declineLink {
342   return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
343 }
344 - (id)tentativeLink {
345   return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
346 }
347
348 @end /* UIxMailPartICalViewer */