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