From 1eda040e8446d8b68c337bc8ad8af59e85463e37 Mon Sep 17 00:00:00 2001 From: Wolfgang Sourdeau Date: Sat, 10 Nov 2007 00:02:30 +0000 Subject: [PATCH] Monotone-Parent: a164259525f718f804b625a9f09b4c3b3c9e83f8 Monotone-Revision: 11f4bdac420d36e45b12ca5d1d393ec34f43c609 Monotone-Author: wsourdeau@inverse.ca Monotone-Date: 2007-11-10T00:02:30 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 16 + SoObjects/Mailer/SOGoMailBodyPart.h | 2 + SoObjects/Mailer/SOGoMailBodyPart.m | 36 +- SoObjects/Mailer/SOGoMailObject.m | 20 +- .../English.lproj/Localizable.strings | 48 +-- .../French.lproj/Localizable.strings | 46 +-- .../German.lproj/Localizable.strings | 14 +- UI/MailPartViewers/UIxMailPartICalAction.m | 105 +++-- UI/MailPartViewers/UIxMailPartICalViewer.h | 16 +- UI/MailPartViewers/UIxMailPartICalViewer.m | 250 ++++++------ UI/MailPartViewers/product.plist | 10 + .../MailPartViewers/UIxMailPartICalViewer.wox | 369 +++++++++--------- UI/WebServerResources/MailerUI.js | 47 ++- 13 files changed, 563 insertions(+), 416 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6ed072424..e7cd85b12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2007-11-09 Wolfgang Sourdeau + * UI/MailPartViewers/UIxMailPartICalViewer.m + ([UIxMailPartICalViewer -acceptLink]) + ([UIxMailPartICalViewer -declineLink]) + ([UIxMailPartICalViewer -tentativeLink]): removed useless methods. + + * UI/MailPartViewers/UIxMailPartICalAction.m ([UIxMailPartICalAction -addToCalendarAction]) + ([UIxMailPartICalAction -deleteFromCalendarAction]): new stub + methods. + + * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject + -lookupImap4BodyPartKey:]): make use of the new method below. + + * SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart + +bodyPartClassForMimeType:mimeTypeinContext:_ctx]): new method + that returns an appropriate Class depending on a given mime type. + * UI/SOGoUI/UIxComponent.m ([UIxComponent -canCreateOrModify]): new boolean accessor that determines whether someone can create (i.e. modify a new entry) or modify an existing entry. diff --git a/SoObjects/Mailer/SOGoMailBodyPart.h b/SoObjects/Mailer/SOGoMailBodyPart.h index bfb30aa2f..c3d6509dd 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.h +++ b/SoObjects/Mailer/SOGoMailBodyPart.h @@ -62,6 +62,8 @@ + (Class) bodyPartClassForKey: (NSString *) _key inContext: (id) _ctx; ++ (Class) bodyPartClassForMimeType: (NSString *) mimeType + inContext: (id) _ctx; @end diff --git a/SoObjects/Mailer/SOGoMailBodyPart.m b/SoObjects/Mailer/SOGoMailBodyPart.m index 550fc04a5..5c32a2ef2 100644 --- a/SoObjects/Mailer/SOGoMailBodyPart.m +++ b/SoObjects/Mailer/SOGoMailBodyPart.m @@ -34,6 +34,8 @@ #import #import +#import + #import "SOGoMailObject.h" #import "SOGoMailManager.h" @@ -305,7 +307,9 @@ static BOOL debugOn = NO; /* factory */ -+ (Class)bodyPartClassForKey:(NSString *)_key inContext:(id)_ctx { ++ (Class) bodyPartClassForKey: (NSString *) _key + inContext: (id) _ctx +{ NSString *pe; pe = [_key pathExtension]; @@ -335,6 +339,36 @@ static BOOL debugOn = NO; return self; } ++ (Class) bodyPartClassForMimeType: (NSString *) mimeType + inContext: (id) _ctx +{ + NSString *classString; + Class klazz; + + if ([mimeType isEqualToString: @"image/gif"] + || [mimeType isEqualToString: @"image/png"] + || [mimeType isEqualToString: @"image/jpg"]) + classString = @"SOGoImageMailBodyPart"; + else if ([mimeType isEqualToString: @"text/calendar"]) + classString = @"SOGoCalendarMailBodyPart"; + else if ([mimeType isEqualToString: @"text/x-vcard"]) + classString = @"SOGoVCardMailBodyPart"; + else if ([mimeType isEqualToString: @"message/rfc822"]) + classString = @"SOGoMessageMailBodyPart"; + else + { + NSLog (@"unhandled mime type: '%@'", mimeType); + classString = nil; + } + + if (classString) + klazz = NSClassFromString (classString); + else + klazz = Nil; + + return klazz; +} + /* etag support */ - (id)davEntityTag { diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index 9397b725e..0cda214ec 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -735,8 +735,22 @@ static BOOL debugSoParts = NO; { // TODO: we might want to check for existence prior controller creation Class clazz; - - clazz = [SOGoMailBodyPart bodyPartClassForKey:_key inContext:_ctx]; + NSArray *parts; + int part; + NSDictionary *partDesc; + NSString *mimeType; + + parts = [[self bodyStructure] objectForKey: @"parts"]; + part = [_key intValue] - 1; + if (part > -1 && part < [parts count]) + { + partDesc = [parts objectAtIndex: part]; + mimeType = [[partDesc keysWithFormat: @"%{type}/%{subtype}"] lowercaseString]; + clazz = [SOGoMailBodyPart bodyPartClassForMimeType: mimeType + inContext: _ctx]; + } + else + clazz = Nil; return [clazz objectWithName:_key inContainer: self]; } @@ -755,7 +769,7 @@ static BOOL debugSoParts = NO; if ([self isBodyPartKey:_key inContext:_ctx]) { if ((obj = [self lookupImap4BodyPartKey:_key inContext:_ctx]) != nil) { - if (debugSoParts) + if (debugSoParts) [self logWithFormat: @"mail looked up part %@: %@", _key, obj]; return obj; } diff --git a/UI/MailPartViewers/English.lproj/Localizable.strings b/UI/MailPartViewers/English.lproj/Localizable.strings index c120183f8..5fadd32d1 100644 --- a/UI/MailPartViewers/English.lproj/Localizable.strings +++ b/UI/MailPartViewers/English.lproj/Localizable.strings @@ -1,30 +1,32 @@ -ACCEPTED = "accepted"; -COMPLETED = "completed"; -DECLINED = "declined"; -DELEGATED = "delegated"; -IN-PROCESS = "in process"; -NEEDS-ACTION = "needs action"; -TENTATIVE = "tentative"; -organized_by_you = "organized by you"; +ACCEPTED = "accepted"; +COMPLETED = "completed"; +DECLINED = "declined"; +DELEGATED = "delegated"; +IN-PROCESS = "in process"; +NEEDS-ACTION = "needs action"; +TENTATIVE = "tentative"; +organized_by_you = "organized by you"; you_are_an_attendee = "you are an attendee"; -add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo."; -publish_info_text = "The sender informs you of the attached event."; -cancel_info_text = "Your invitation or the whole event was canceled."; +add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo."; +publish_info_text = "The sender informs you of the attached event."; +cancel_info_text = "Your invitation or the whole event was canceled."; request_info_no_attendee = "is proposing a meeting to the attendees. You receive this mail as a notification, you are not scheduled as a participant."; -Appointment = "Appointment"; +Appointment = "Appointment"; -Organizer = "Organisateur"; -Time = "Time"; -Attendees = "Attendees"; -request_info = "invites you to participate in a meeting."; -do_add_to_cal = "add to calendar"; -do_del_from_cal = "delete from calendar"; -do_accept = "accept"; -do_decline = "decline"; -do_tentative = "tentative"; -do_update_status = "update status in calendar"; +Organizer = "Organisateur"; +Time = "Time"; +Attendees = "Attendees"; +request_info = "invites you to participate in a meeting."; +"Add to calendar" = "Add to calendar"; +"Delete from calendar" = "Delete from calendar"; +Accept = "Accept"; +Decline = "Decline"; +Tentative = "Tentative"; +"Update status in calendar" = "Update status in calendar"; reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant."; -reply_info = "This is a reply to an event invitation done by you."; +reply_info = "This is a reply to an event invitation done by you."; + +"to" = "to"; "Untitled" = "Untitled"; diff --git a/UI/MailPartViewers/French.lproj/Localizable.strings b/UI/MailPartViewers/French.lproj/Localizable.strings index ad6492f83..b5db9cecc 100644 --- a/UI/MailPartViewers/French.lproj/Localizable.strings +++ b/UI/MailPartViewers/French.lproj/Localizable.strings @@ -1,30 +1,32 @@ -ACCEPTED = "Accepté"; -COMPLETED = "Terminé"; -DECLINED = "Refusé"; -DELEGATED = "Délégué"; -IN-PROCESS = "En cours de traitement"; +ACCEPTED = "Accepté"; +COMPLETED = "Terminé"; +DECLINED = "Refusé"; +DELEGATED = "Délégué"; +IN-PROCESS = "En cours de traitement"; NEEDS-ACTION = "Prise de décision nécessaire"; -TENTATIVE = "Proposition"; -organized_by_you = "vous êtes l'organisateur"; +TENTATIVE = "Proposition"; +organized_by_you = "vous êtes l'organisateur"; you_are_an_attendee = "vous êtes invité"; -add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo."; -publish_info_text = "L'expéditeur vous informe de l'événement attaché."; -cancel_info_text = "Votre invitation ou l'événement au complet a été annulé."; +add_info_text = "iMIP 'ADD' requests are not yet supported by SOGo."; +publish_info_text = "L'expéditeur vous informe de l'événement attaché."; +cancel_info_text = "Votre invitation ou l'événement au complet a été annulé."; request_info_no_attendee = "propose une réunion entre les invités. Ce message tint seulement lieu d'avis, vous n'êtes pas indiqué comme invité."; -Appointment = "Événement"; +Appointment = "Événement"; -Organizer = "Organisateur"; -Time = "Date"; -Attendees = "Invités"; -request_info = "vous invite à une réunion."; -do_add_to_cal = "ajouter à l'agenda"; -do_del_from_cal = "effacer de l'agenda"; -do_accept = "accepter"; -do_decline = "decliner"; -do_tentative = "tentative"; -do_update_status = "mettre l'agenda à jour"; +Organizer = "Organisateur"; +Time = "Date"; +Attendees = "Invités"; +request_info = "vous invite à une réunion."; +"Add to calendar" = "Ajouter à l'agenda"; +"Delete from calendar" = "Effacer de l'agenda"; +Accept = "Accepter"; +Decline = "Decliner"; +Tentative = "Tentative"; +"Update status in calendar" = "Mettre l'agenda à jour"; reply_info_no_attendee = "Vous avez reçu une réponse à un événement mais l'expéditeur n'est pas un invité."; -reply_info = "Ceci est une réponse à un événement que vous avez organisé."; +reply_info = "Ceci est une réponse à un événement que vous avez organisé."; + +"to" = "à"; "Untitled" = "Sans titre"; diff --git a/UI/MailPartViewers/German.lproj/Localizable.strings b/UI/MailPartViewers/German.lproj/Localizable.strings index c120183f8..b96425ebc 100644 --- a/UI/MailPartViewers/German.lproj/Localizable.strings +++ b/UI/MailPartViewers/German.lproj/Localizable.strings @@ -17,15 +17,17 @@ Organizer = "Organisateur"; Time = "Time"; Attendees = "Attendees"; request_info = "invites you to participate in a meeting."; -do_add_to_cal = "add to calendar"; -do_del_from_cal = "delete from calendar"; -do_accept = "accept"; -do_decline = "decline"; -do_tentative = "tentative"; -do_update_status = "update status in calendar"; +"Add to calendar" = "Add to calendar"; +"Delete from calendar" = "Delete from calendar"; +Accept = "Accept"; +Decline = "Decline"; +Tentative = "Tentative"; +"Update status in calendar" = "Update status in calendar"; reply_info_no_attendee = "You received a reply to a scheduling event but the sender of the reply is not a participant."; reply_info = "This is a reply to an event invitation done by you."; +"to" = "to"; + "Untitled" = "Untitled"; "Size" = "Size"; diff --git a/UI/MailPartViewers/UIxMailPartICalAction.m b/UI/MailPartViewers/UIxMailPartICalAction.m index 7f1cf9e8f..f83a33819 100644 --- a/UI/MailPartViewers/UIxMailPartICalAction.m +++ b/UI/MailPartViewers/UIxMailPartICalAction.m @@ -1,76 +1,63 @@ -/* - Copyright (C) 2005 SKYRIX Software AG +/* UIxMailPartICalAction.m - this file is part of SOGo + * + * Copyright (C) 2007 Inverse groupe conseil + * + * Author: Wolfgang Sourdeau + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ - 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. -*/ - -#import -#import -#import +#import #import -#import -#import + +#import @interface UIxMailPartICalAction : WODirectAction @end @implementation UIxMailPartICalAction -- (id)redirectToViewerWithError:(NSString *)_error { - WOResponse *r; - NSString *viewURL; - id mail; - - mail = [[self clientObject] valueForKey:@"mailObject"]; - [self logWithFormat:@"MAIL: %@", mail]; - - viewURL = [mail baseURLInContext:[self context]]; - [self logWithFormat:@" url: %@", viewURL]; - - viewURL = [viewURL stringByAppendingString: - [viewURL hasSuffix:@"/"] ? @"view" : @"/view"]; - - if ([_error isNotNull] && [_error length] > 0) { - viewURL = [viewURL stringByAppendingString:@"?error="]; - viewURL = [viewURL stringByAppendingString: - [_error stringByEscapingURL]]; - } - - r = [[self context] response]; - [r setStatus:302 /* moved */]; - [r setHeader:viewURL forKey:@"location"]; - return r; +- (WOResponse *) _changePartStatusAction: (NSString *) _newStatus +{ + return [self responseWithStatus: 404]; } -- (id)changePartStatusAction:(NSString *)_newStatus { - [self logWithFormat:@"TODO: should %@: %@", _newStatus, [self clientObject]]; - return [self redirectToViewerWithError: - [_newStatus stringByAppendingString:@" not implemented!"]]; +- (WOResponse *) markAcceptedAction +{ + return [self _changePartStatusAction: @"ACCEPTED"]; } -- (id)markAcceptedAction { - return [self changePartStatusAction:@"ACCEPTED"]; +- (WOResponse *) markDeclinedAction +{ + return [self _changePartStatusAction: @"DECLINED"]; } -- (id)markDeclinedAction { - return [self changePartStatusAction:@"DECLINED"]; + +- (WOResponse *) markTentativeAction +{ + return [self _changePartStatusAction: @"TENTATIVE"]; } -- (id)markTentativeAction { - return [self changePartStatusAction:@"TENTATIVE"]; + +- (WOResponse *) addToCalendarAction +{ + return [self responseWithStatus: 404]; +} + +- (WOResponse *) deleteFromCalendarAction +{ + return [self responseWithStatus: 404]; } @end /* UIxMailPartICalAction */ diff --git a/UI/MailPartViewers/UIxMailPartICalViewer.h b/UI/MailPartViewers/UIxMailPartICalViewer.h index 08ed095dd..98d75abe4 100644 --- a/UI/MailPartViewers/UIxMailPartICalViewer.h +++ b/UI/MailPartViewers/UIxMailPartICalViewer.h @@ -10,11 +10,11 @@ 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 + 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 + 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. */ @@ -30,13 +30,13 @@ @interface UIxMailPartICalViewer : UIxMailPartViewer { - iCalCalendar *inCalendar; - iCalEvent *inEvent; - id attendee; + iCalCalendar *inCalendar; + iCalEvent *inEvent; + id attendee; SOGoDateFormatter *dateFormatter; - id item; - id storedEventObject; - iCalEvent *storedEvent; + id item; + id storedEventObject; + iCalEvent *storedEvent; } - (iCalEvent *) authorativeEvent; diff --git a/UI/MailPartViewers/UIxMailPartICalViewer.m b/UI/MailPartViewers/UIxMailPartICalViewer.m index 8b31e5325..ec8c18af8 100644 --- a/UI/MailPartViewers/UIxMailPartICalViewer.m +++ b/UI/MailPartViewers/UIxMailPartICalViewer.m @@ -1,6 +1,6 @@ /* 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 @@ -10,18 +10,18 @@ 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 + 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 + 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. */ @@ -46,44 +46,47 @@ @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]; +- (void) dealloc +{ + [storedEventObject release]; + [storedEvent release]; + [attendee release]; + [item release]; + [inCalendar release]; + [inEvent release]; + [dateFormatter release]; [super dealloc]; } /* maintain caches */ -- (void)resetPathCaches { +- (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; - + [inEvent release]; inEvent = nil; + [inCalendar release]; inCalendar = nil; + [storedEventObject release]; storedEventObject = nil; + [storedEvent release]; storedEvent = nil; + /* not strictly path-related, but useless without it anyway: */ - [self->attendee release]; self->attendee = nil; - [self->item release]; self->item = nil; + [attendee release]; attendee = nil; + [item release]; item = nil; } /* raw content handling */ -- (NSStringEncoding)fallbackStringEncoding { +- (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). + to try Latin-1 as a fallback in any case (be tolerant). */ return NSISOLatin1StringEncoding; } @@ -95,59 +98,73 @@ if (!inCalendar) { inCalendar - = [iCalCalendar parseSingleFromSource: [self flatContentAsString]]; + = [iCalCalendar parseSingleFromSource: [self flatContentAsString]]; [inCalendar retain]; } return inCalendar; } -- (BOOL)couldParseCalendar { +- (BOOL) couldParseCalendar +{ return [[self inCalendar] isNotNull]; } -- (iCalEvent *)inEvent { +- (iCalEvent *) inEvent +{ NSArray *events; - - if (self->inEvent != nil) - return [self->inEvent isNotNull] ? self->inEvent : nil; - + + if (inEvent) + return [inEvent isNotNull] ? inEvent : nil; + events = [[self inCalendar] events]; if ([events count] > 0) { - self->inEvent = [[events objectAtIndex:0] retain]; - return self->inEvent; + inEvent = [[events objectAtIndex:0] retain]; + return inEvent; } else { - self->inEvent = [[NSNull null] retain]; + inEvent = [[NSNull null] retain]; return nil; } } /* formatters */ -- (SOGoDateFormatter *)dateFormatter { - if (self->dateFormatter == nil) { +- (SOGoDateFormatter *) dateFormatter +{ + if (dateFormatter == nil) { dateFormatter = [[context activeUser] dateFormatterInContext: context]; [dateFormatter retain]; } - return self->dateFormatter; + return dateFormatter; } /* below is copied from UIxAppointmentView, can we avoid that? */ -- (void)setAttendee:(id)_attendee { - ASSIGN(self->attendee, _attendee); +- (void) setAttendee: (id) _attendee +{ + ASSIGN(attendee, _attendee); } -- (id)attendee { - return self->attendee; + +- (id) attendee +{ + return attendee; } - (NSString *) _personForDisplay: (iCalPerson *) person { - return [NSString stringWithFormat: @"%@ <%@>", - [person cnWithoutQuotes], - [person rfc822Email]]; + NSString *fn, *email, *result; + + fn = [person cnWithoutQuotes]; + email = [person rfc822Email]; + if ([fn length]) + result = [NSString stringWithFormat: @"%@ <%@>", + fn, email]; + else + result = email; + + return result; } - (NSString *) attendeeForDisplay @@ -155,18 +172,21 @@ return [self _personForDisplay: attendee]; } -- (void)setItem:(id)_item { - ASSIGN(self->item, _item); +- (void) setItem: (id) _item +{ + ASSIGN(item, _item); } -- (id)item { - return self->item; + +- (id) item +{ + return item; } - (NSCalendarDate *) startTime { NSCalendarDate *date; NSTimeZone *timeZone; - + date = [[self authorativeEvent] startDate]; timeZone = [[context activeUser] timeZone]; [date setTimeZone: timeZone]; @@ -178,7 +198,7 @@ { NSCalendarDate *date; NSTimeZone *timeZone; - + date = [[self authorativeEvent] endDate]; timeZone = [[context activeUser] timeZone]; [date setTimeZone: timeZone]; @@ -186,10 +206,13 @@ return date; } -- (BOOL)isEndDateOnSameDay { +- (BOOL) isEndDateOnSameDay +{ return [[self startTime] isDateOnSameDay:[self endTime]]; } -- (NSTimeInterval)duration { + +- (NSTimeInterval) duration +{ return [[self endTime] timeIntervalSinceDate:[self startTime]]; } @@ -209,45 +232,48 @@ return [folder lookupName: @"personal" inContext: context acquire: NO]; } -- (id)storedEventObject { +- (id) storedEventObject +{ /* lookup object in the users Calendar */ id calendar; - - if (self->storedEventObject != nil) - return [self->storedEventObject isNotNull] ? self->storedEventObject : nil; - + + if (storedEventObject) + return [storedEventObject isNotNull] ? 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) { + if (filename) { // 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. + // 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]; + storedEventObject = [tmp retain]; } } - - if (self->storedEventObject == nil) - self->storedEventObject = [[NSNull null] retain]; - - return self->storedEventObject; + + if (storedEventObject == nil) + storedEventObject = [[NSNull null] retain]; + + return storedEventObject; } -- (BOOL)isEventStoredInCalendar { +- (BOOL) isEventStoredInCalendar +{ return [[self storedEventObject] isNotNull]; } -- (iCalEvent *)storedEvent { +- (iCalEvent *) storedEvent +{ return (iCalEvent *) [(SOGoAppointmentObject *)[self storedEventObject] component: NO]; } @@ -262,27 +288,30 @@ return [identity objectForKey: @"email"]; } -- (iCalEvent *)authorativeEvent { +- (iCalEvent *) authorativeEvent +{ /* DB is considered master, when in DB, ignore mail organizer */ return [self isEventStoredInCalendar] ? [self storedEvent] : [self inEvent]; } -- (BOOL)isLoggedInUserTheOrganizer { +- (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 { +- (BOOL) isLoggedInUserAnAttendee +{ NSString *loginEMail; - + if ((loginEMail = [self loggedInUserEMail]) == nil) { [self warnWithFormat:@"Could not determine email of logged in user?"]; return NO; @@ -309,69 +338,70 @@ /* replies */ -- (NGImap4EnvelopeAddress *)replySenderAddress { +- (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 { +- (NSString *) replySenderEMail +{ return [[self replySenderAddress] email]; } -- (NSString *)replySenderBaseEMail { + +- (NSString *) replySenderBaseEMail +{ return [[self replySenderAddress] baseEMail]; } -- (iCalPerson *)inReplyAttendee { +- (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 { + +- (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. - + 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; + 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; + + p = nil; + + e = [self storedEvent]; + if (e) + { + p = [e findParticipantWithEmail: [self replySenderBaseEMail]]; + if (!p) + p = [e findParticipantWithEmail:[self replySenderEMail]]; + } + + return p; } -- (BOOL)isReplySenderAnAttendee { + +- (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 */ diff --git a/UI/MailPartViewers/product.plist b/UI/MailPartViewers/product.plist index a042a538b..145b47245 100644 --- a/UI/MailPartViewers/product.plist +++ b/UI/MailPartViewers/product.plist @@ -25,6 +25,16 @@ actionClass = "UIxMailPartICalAction"; actionName = "markTentative"; }; + addToCalendar = { + protectedBy = "View"; + actionClass = "UIxMailPartICalAction"; + actionName = "addToCalendar"; + }; + deleteFromCalendar = { + protectedBy = "View"; + actionClass = "UIxMailPartICalAction"; + actionName = "deleteFromCalendar"; + }; }; }; }; diff --git a/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox b/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox index 72a2fc92d..87b072dab 100644 --- a/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox +++ b/UI/Templates/MailPartViewers/UIxMailPartICalViewer.wox @@ -1,203 +1,208 @@ +
+ xmlns:var="http://www.skyrix.com/od/binding" + xmlns:label="OGo:label" + xmlns:const="http://www.skyrix.com/od/constant" + xmlns:rsrc="OGo:url" + class="linked_attachment_frame" + > - -
- Parsing Error - - The SOGo/SOPE iCalendar parser could not parse the body of this MIME part. + +
+ Parsing Error + + The SOGo/SOPE iCalendar parser could not parse the body of this MIME part. -
-
-
+
+
+
- -
- - : - + +
+ + : + + + () + + + () + + + + + + +

+ + + + + + | + +

+ +

+ + + +

+
+ + +

+ + + +

+
+
+ + + + + +

+
+ + +

+ +

+ + + +

+ Status Update: + , + was: + . +

+
+
+ + + + +

+ +

+
+ +

+ + +

+
+ + + + +

+
+ + + + +

+
+ + - () + - - () + + - - - - - - -

- | - | - - - | - -

- -

- - - -

+ + + + +
+ +
+
- -

- - - -

-
- - - - - - -

-
- - -

- -

- - - -

- Status Update: - , - was: - . -

-
-
- - - - - -

- -

- - -

- - -

- - - - - -

-
- - - - -

-
- - - - - - - - - - - -
- + + + + + + + + + + + + + + + + + + + + + + +
: + + + + +
: + +
: + + + () +
+
+
: + +
-
-
+
+
- -
- - - - - - - - - - - - - - - - - - - - - - -
: - - - - -
: - -
: - - - () -
-
-
: - -
-
-
-
- - + -->
diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index 450b5f2a4..9f8ce6a28 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -9,7 +9,7 @@ if (typeof textMailAccounts != 'undefined') { mailAccounts = new Array(); } -var currentMessages = new Array(); +var currentMessages = {}; var maxCachedMessages = 20; var cachedMessages = new Array(); var currentMailbox = null; @@ -743,13 +743,56 @@ function configureLinksInMessage() { if (editDraftButton) Event.observe(editDraftButton, "click", onMessageEditDraft.bindAsEventListener(editDraftButton)); + + configureiCalLinksInMessage(); +} + +function configureiCalLinksInMessage() { + var buttons = { "iCalendarAccept": "accept", + "iCalendarDecline": "decline", + "iCalendarTentative": "tentative", + "iCalendarAddToCalendar": "addToCalendar", + "iCalendarDeleteFromCalendar": "deleteFromCalendar" }; + + for (var key in buttons) { + var button = $(key); + if (button) { + button.action = buttons[key]; + Event.observe(button, "click", + onICalendarButtonClick.bindAsEventListener(button)); + } + } +} + +function onICalendarButtonClick(event) { + var link = $("iCalendarAttachment").value; + if (link) { + var urlstr = link + "/" + this.action; + triggerAjaxRequest(urlstr, ICalendarButtonCallback, + currentMailbox + "/" + + currentMessages[currentMailbox]); + window.alert(urlstr); + } +} + +function ICalendarButtonCallback(http) { + if (http.readyState == 4) + if (isHttpStatus204(http.status)) { + var oldMsg = http.callbackData; + var msg = currentMailbox + "/" + currentMessages[currentMailbox]; + if (oldMsg == msg) { + deleteCachedMessage(oldMsg); + loadMessage(currentMessages[currentMailbox]); + } + } } function resizeMailContent() { var headerTable = document.getElementsByClassName('mailer_fieldtable')[0]; var contentDiv = document.getElementsByClassName('mailer_mailcontent')[0]; - contentDiv.setStyle({ 'top': (Element.getHeight(headerTable) + headerTable.offsetTop) + 'px' }); + contentDiv.setStyle({ 'top': + (Element.getHeight(headerTable) + headerTable.offsetTop) + 'px' }); } function onMessageContentMenu(event) {