2006-06-15 21:34:10 +02:00
|
|
|
/*
|
|
|
|
Copyright (C) 2004-2005 SKYRIX Software AG
|
|
|
|
|
|
|
|
This file is part of OpenGroupware.org.
|
|
|
|
|
|
|
|
OGo is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU Lesser General Public License as published by the
|
|
|
|
Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
later version.
|
|
|
|
|
|
|
|
OGo is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
|
|
License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
|
|
License along with OGo; see the file COPYING. If not, write to the
|
|
|
|
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
UIxMailPartICalViewer
|
|
|
|
|
|
|
|
Show plain/calendar mail parts.
|
|
|
|
*/
|
|
|
|
|
2006-09-29 20:31:20 +02:00
|
|
|
#import <SOGoUI/SOGoDateFormatter.h>
|
|
|
|
#import <SOGo/SOGoUser.h>
|
|
|
|
#import <SoObjects/Appointments/SOGoAppointmentFolder.h>
|
|
|
|
#import <SoObjects/Appointments/SOGoAppointmentObject.h>
|
|
|
|
#import <SoObjects/Mailer/SOGoMailObject.h>
|
|
|
|
#import <NGCards/NGCards.h>
|
|
|
|
#import <NGImap4/NGImap4EnvelopeAddress.h>
|
|
|
|
#import "common.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
2006-09-29 20:31:20 +02:00
|
|
|
#import "UIxMailPartICalViewer.h"
|
2006-06-15 21:34:10 +02:00
|
|
|
|
|
|
|
@implementation UIxMailPartICalViewer
|
|
|
|
|
|
|
|
- (void)dealloc {
|
|
|
|
[self->storedEventObject release];
|
|
|
|
[self->storedEvent release];
|
|
|
|
[self->attendee release];
|
|
|
|
[self->item release];
|
|
|
|
[self->inCalendar release];
|
|
|
|
[self->inEvent release];
|
|
|
|
[self->dateFormatter release];
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* maintain caches */
|
|
|
|
|
|
|
|
- (void)resetPathCaches {
|
|
|
|
[super resetPathCaches];
|
|
|
|
[self->inEvent release]; self->inEvent = nil;
|
|
|
|
[self->inCalendar release]; self->inCalendar = nil;
|
|
|
|
[self->storedEventObject release]; self->storedEventObject = nil;
|
|
|
|
[self->storedEvent release]; self->storedEvent = nil;
|
|
|
|
|
|
|
|
/* not strictly path-related, but useless without it anyway: */
|
|
|
|
[self->attendee release]; self->attendee = nil;
|
|
|
|
[self->item release]; self->item = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* raw content handling */
|
|
|
|
|
|
|
|
- (NSStringEncoding)fallbackStringEncoding {
|
|
|
|
/*
|
|
|
|
iCalendar invitations sent by Outlook 2002 have the annoying bug that the
|
|
|
|
mail states an UTF-8 content encoding but the actual iCalendar content is
|
|
|
|
encoding in Latin-1 (or Windows Western?).
|
|
|
|
|
|
|
|
As a result the content decoding will fail (TODO: always?). In this case we
|
|
|
|
try to decode with Latin-1.
|
|
|
|
|
|
|
|
Note: we could check for the Outlook x-mailer, but it was considered better
|
|
|
|
to try Latin-1 as a fallback in any case (be tolerant).
|
|
|
|
*/
|
|
|
|
return NSISOLatin1StringEncoding;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* accessors */
|
|
|
|
|
2006-09-29 20:31:20 +02:00
|
|
|
- (iCalCalendar *) inCalendar
|
|
|
|
{
|
|
|
|
if (!inCalendar)
|
|
|
|
{
|
|
|
|
inCalendar
|
|
|
|
= [iCalCalendar parseSingleFromSource: [self flatContentAsString]];
|
|
|
|
[inCalendar retain];
|
|
|
|
}
|
|
|
|
|
|
|
|
return inCalendar;
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
2006-09-29 20:31:20 +02:00
|
|
|
|
2006-06-15 21:34:10 +02:00
|
|
|
- (BOOL)couldParseCalendar {
|
|
|
|
return [[self inCalendar] isNotNull];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (iCalEvent *)inEvent {
|
|
|
|
NSArray *events;
|
|
|
|
|
|
|
|
if (self->inEvent != nil)
|
|
|
|
return [self->inEvent isNotNull] ? self->inEvent : nil;
|
|
|
|
|
|
|
|
events = [[self inCalendar] events];
|
|
|
|
if ([events count] > 0) {
|
|
|
|
self->inEvent = [[events objectAtIndex:0] retain];
|
|
|
|
return self->inEvent;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
self->inEvent = [[NSNull null] retain];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* formatters */
|
|
|
|
|
|
|
|
- (SOGoDateFormatter *)dateFormatter {
|
|
|
|
if (self->dateFormatter == nil) {
|
|
|
|
self->dateFormatter =
|
|
|
|
[[SOGoDateFormatter alloc] initWithLocale:[self locale]];
|
|
|
|
[self->dateFormatter setFullWeekdayNameAndDetails];
|
|
|
|
}
|
|
|
|
return self->dateFormatter;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* below is copied from UIxAppointmentView, can we avoid that? */
|
|
|
|
|
|
|
|
- (void)setAttendee:(id)_attendee {
|
|
|
|
ASSIGN(self->attendee, _attendee);
|
|
|
|
}
|
|
|
|
- (id)attendee {
|
|
|
|
return self->attendee;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)setItem:(id)_item {
|
|
|
|
ASSIGN(self->item, _item);
|
|
|
|
}
|
|
|
|
- (id)item {
|
|
|
|
return self->item;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSCalendarDate *)startTime {
|
|
|
|
NSCalendarDate *date;
|
|
|
|
|
|
|
|
date = [[self authorativeEvent] startDate];
|
2006-09-08 17:20:39 +02:00
|
|
|
[date setTimeZone:[[self clientObject] userTimeZone]];
|
2006-06-15 21:34:10 +02:00
|
|
|
return date;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSCalendarDate *)endTime {
|
|
|
|
NSCalendarDate *date;
|
|
|
|
|
|
|
|
date = [[self authorativeEvent] endDate];
|
2006-09-08 17:20:39 +02:00
|
|
|
[date setTimeZone:[[self clientObject] userTimeZone]];
|
2006-06-15 21:34:10 +02:00
|
|
|
return date;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isEndDateOnSameDay {
|
|
|
|
return [[self startTime] isDateOnSameDay:[self endTime]];
|
|
|
|
}
|
|
|
|
- (NSTimeInterval)duration {
|
|
|
|
return [[self endTime] timeIntervalSinceDate:[self startTime]];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calendar folder support */
|
|
|
|
|
|
|
|
- (id)calendarFolder {
|
|
|
|
/* return scheduling calendar of currently logged-in user */
|
|
|
|
return [[[self context] activeUser] schedulingCalendarInContext:
|
|
|
|
[self context]];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id)storedEventObject {
|
|
|
|
/* lookup object in the users Calendar */
|
|
|
|
id calendar;
|
|
|
|
|
|
|
|
if (self->storedEventObject != nil)
|
|
|
|
return [self->storedEventObject isNotNull] ? self->storedEventObject : nil;
|
|
|
|
|
|
|
|
calendar = [self calendarFolder];
|
|
|
|
if ([calendar isKindOfClass:[NSException class]]) {
|
|
|
|
[self errorWithFormat:@"Did not find Calendar folder: %@", calendar];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
NSString *filename;
|
|
|
|
|
|
|
|
filename = [calendar resourceNameForEventUID:[[self inEvent] uid]];
|
|
|
|
if (filename != nil) {
|
|
|
|
// TODO: When we get an exception, this might be an auth issue meaning
|
|
|
|
// that the UID indeed exists but that the user has no access to
|
|
|
|
// the object.
|
|
|
|
// Of course this is quite unusual for the private calendar though.
|
|
|
|
id tmp;
|
|
|
|
|
|
|
|
tmp = [calendar lookupName:filename inContext:[self context] acquire:NO];
|
|
|
|
if ([tmp isNotNull] && ![tmp isKindOfClass:[NSException class]])
|
|
|
|
self->storedEventObject = [tmp retain];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (self->storedEventObject == nil)
|
|
|
|
self->storedEventObject = [[NSNull null] retain];
|
|
|
|
|
|
|
|
return self->storedEventObject;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isEventStoredInCalendar {
|
|
|
|
return [[self storedEventObject] isNotNull];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (iCalEvent *)storedEvent {
|
2007-02-15 22:06:34 +01:00
|
|
|
return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component];
|
2006-06-15 21:34:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* organizer tracking */
|
|
|
|
|
|
|
|
- (NSString *)loggedInUserEMail {
|
|
|
|
return [[[self context] activeUser] email];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (iCalEvent *)authorativeEvent {
|
|
|
|
/* DB is considered master, when in DB, ignore mail organizer */
|
|
|
|
return [self isEventStoredInCalendar]
|
|
|
|
? [self storedEvent]
|
|
|
|
: [self inEvent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isLoggedInUserTheOrganizer {
|
|
|
|
NSString *loginEMail;
|
|
|
|
|
|
|
|
if ((loginEMail = [self loggedInUserEMail]) == nil) {
|
|
|
|
[self warnWithFormat:@"Could not determine email of logged in user?"];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [[self authorativeEvent] isOrganizer:loginEMail];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL)isLoggedInUserAnAttendee {
|
|
|
|
NSString *loginEMail;
|
|
|
|
|
|
|
|
if ((loginEMail = [self loggedInUserEMail]) == nil) {
|
|
|
|
[self warnWithFormat:@"Could not determine email of logged in user?"];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return [[self authorativeEvent] isParticipant:loginEMail];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* derived fields */
|
|
|
|
|
|
|
|
- (NSString *)organizerDisplayName {
|
|
|
|
iCalPerson *organizer;
|
|
|
|
NSString *cn;
|
|
|
|
|
|
|
|
if ((organizer = [[self authorativeEvent] organizer]) != nil) {
|
2007-02-13 16:56:52 +01:00
|
|
|
cn = [organizer valueForKey:@"cnWithoutQuotes"];
|
2006-06-15 21:34:10 +02:00
|
|
|
if ([cn isNotNull] && [cn length] > 0)
|
|
|
|
return cn;
|
|
|
|
|
|
|
|
cn = [organizer valueForKey:@"rfc822Email"];
|
|
|
|
if ([cn isNotNull] && [cn length] > 0)
|
|
|
|
return cn;
|
|
|
|
|
|
|
|
return @"[error: unable to derive organizer name]";
|
|
|
|
}
|
|
|
|
|
|
|
|
return @"[todo: no organizer set, use 'from']";
|
|
|
|
}
|
|
|
|
|
|
|
|
/* replies */
|
|
|
|
|
|
|
|
- (NGImap4EnvelopeAddress *)replySenderAddress {
|
|
|
|
/*
|
|
|
|
The iMIP reply is the sender of the mail, the 'attendees' are NOT set to
|
|
|
|
the actual attendees. BUT the attendee field contains the reply-status!
|
|
|
|
*/
|
|
|
|
id tmp;
|
|
|
|
|
|
|
|
tmp = [[self clientObject] fromEnvelopeAddresses];
|
|
|
|
if ([tmp count] == 0) return nil;
|
|
|
|
return [tmp objectAtIndex:0];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSString *)replySenderEMail {
|
|
|
|
return [[self replySenderAddress] email];
|
|
|
|
}
|
|
|
|
- (NSString *)replySenderBaseEMail {
|
|
|
|
return [[self replySenderAddress] baseEMail];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (iCalPerson *)inReplyAttendee {
|
|
|
|
NSArray *attendees;
|
|
|
|
|
|
|
|
attendees = [[self inEvent] attendees];
|
|
|
|
if ([attendees count] == 0)
|
|
|
|
return nil;
|
|
|
|
if ([attendees count] > 1)
|
|
|
|
[self warnWithFormat:@"More than one attendee in REPLY: %@", attendees];
|
|
|
|
|
|
|
|
return [attendees objectAtIndex:0];
|
|
|
|
}
|
|
|
|
- (iCalPerson *)storedReplyAttendee {
|
|
|
|
/*
|
|
|
|
TODO: since an attendee can have multiple email addresses, maybe we
|
|
|
|
should translate the email to an internal uid and then retrieve
|
|
|
|
all emails addresses for matching the participant.
|
|
|
|
|
|
|
|
Note: -findParticipantWithEmail: does not parse the email!
|
|
|
|
*/
|
|
|
|
iCalEvent *e;
|
|
|
|
iCalPerson *p;
|
|
|
|
|
|
|
|
if ((e = [self storedEvent]) == nil)
|
|
|
|
return nil;
|
|
|
|
if ((p = [e findParticipantWithEmail:[self replySenderBaseEMail]]))
|
|
|
|
return p;
|
|
|
|
if ((p = [e findParticipantWithEmail:[self replySenderEMail]]))
|
|
|
|
return p;
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
- (BOOL)isReplySenderAnAttendee {
|
|
|
|
return [[self storedReplyAttendee] isNotNull];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* action URLs */
|
|
|
|
|
|
|
|
- (id)acceptLink {
|
|
|
|
return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"];
|
|
|
|
}
|
|
|
|
- (id)declineLink {
|
|
|
|
return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"];
|
|
|
|
}
|
|
|
|
- (id)tentativeLink {
|
|
|
|
return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end /* UIxMailPartICalViewer */
|