From f05b20a51d2bad03d9400d9a895cb1e8619432fc Mon Sep 17 00:00:00 2001 From: wolfgang Date: Sat, 10 Nov 2007 00:02:58 +0000 Subject: [PATCH] git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1259 d1b88da0-ebda-0310-925b-ed51d893ca5b --- ChangeLog | 16 + SoObjects/Contacts/SOGoContactGCSFolder.m | 19 - SoObjects/Contacts/SOGoContactLDAPFolder.m | 37 +- SoObjects/Mailer/SOGoMailBodyPart.h | 2 + SoObjects/Mailer/SOGoMailBodyPart.m | 36 +- SoObjects/Mailer/SOGoMailObject.m | 20 +- SoObjects/SOGo/SOGoFolder.h | 6 + SoObjects/SOGo/SOGoFolder.m | 20 + SoObjects/SOGo/SOGoGCSFolder.h | 6 - SoObjects/SOGo/SOGoGCSFolder.m | 18 - .../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 | 248 ++++++------ UI/MailPartViewers/product.plist | 10 + .../MailPartViewers/UIxMailPartICalViewer.wox | 363 +++++++++--------- UI/WebServerResources/MailerUI.js | 47 ++- 19 files changed, 598 insertions(+), 479 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6ed07242..e7cd85b1 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/Contacts/SOGoContactGCSFolder.m b/SoObjects/Contacts/SOGoContactGCSFolder.m index f8a7b4a2..7fd56ba8 100644 --- a/SoObjects/Contacts/SOGoContactGCSFolder.m +++ b/SoObjects/Contacts/SOGoContactGCSFolder.m @@ -267,25 +267,6 @@ return @"vcard-collection"; } -// /* GET */ - -// - (id) GETAction: (id)_ctx -// { -// // TODO: I guess this should really be done by SOPE (redirect to -// // default method) -// WOResponse *r; -// NSString *uri; - -// uri = [[_ctx request] uri]; -// if (![uri hasSuffix:@"/"]) uri = [uri stringByAppendingString:@"/"]; -// uri = [uri stringByAppendingString:@"view"]; - -// r = [_ctx response]; -// [r setStatus:302 /* moved */]; -// [r setHeader:uri forKey:@"location"]; -// return r; -// } - /* sorting */ - (NSComparisonResult) compare: (id) otherFolder { diff --git a/SoObjects/Contacts/SOGoContactLDAPFolder.m b/SoObjects/Contacts/SOGoContactLDAPFolder.m index 7345c9c5..b35f0d0d 100644 --- a/SoObjects/Contacts/SOGoContactLDAPFolder.m +++ b/SoObjects/Contacts/SOGoContactLDAPFolder.m @@ -98,7 +98,6 @@ { if ((self = [super init])) { - displayName = nil; entries = nil; ldapSource = nil; } @@ -121,7 +120,6 @@ - (void) dealloc { - [displayName release]; [entries release]; [ldapSource release]; [super dealloc]; @@ -132,14 +130,14 @@ ASSIGN (ldapSource, newLDAPSource); } -- (NSString *) displayName +- (NSArray *) davNamespaces { - return displayName; + return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"]; } -- (NSArray *) davNamespaces +- (NSString *) groupDavResourceType { - return [NSArray arrayWithObject: @"urn:ietf:params:xml:ns:carddav"]; + return @"vcard-collection"; } - (id) lookupName: (NSString *) objectName @@ -261,35 +259,26 @@ return result; } -- (NSArray *) davResourceType -{ - NSArray *rType, *groupDavCollection; - - groupDavCollection = [NSArray arrayWithObjects: @"vcard-collection", - XMLNS_GROUPDAV, nil]; - rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, nil]; - - return rType; -} - -- (NSString *) davContentType +- (NSString *) davDisplayName { - return @"httpd/unix-directory"; + return displayName; } -- (BOOL) davIsCollection +- (BOOL) isFolderish { return YES; } -- (NSString *) davDisplayName +/* folder type */ + +- (NSString *) folderType { - return displayName; + return @"Contact"; } -- (BOOL) isFolderish +- (NSString *) outlookFolderClass { - return YES; + return @"IPF.Contact"; } /* sorting */ diff --git a/SoObjects/Mailer/SOGoMailBodyPart.h b/SoObjects/Mailer/SOGoMailBodyPart.h index bfb30aa2..c3d6509d 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 550fc04a..5c32a2ef 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 9397b725..0cda214e 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/SoObjects/SOGo/SOGoFolder.h b/SoObjects/SOGo/SOGoFolder.h index 1292b96e..afb55cfc 100644 --- a/SoObjects/SOGo/SOGoFolder.h +++ b/SoObjects/SOGo/SOGoFolder.h @@ -46,4 +46,10 @@ @end +@interface SOGoFolder (GroupDAVExtensions) + +- (NSString *) groupDavResourceType; + +@end + #endif /* SOGOFOLDER_H */ diff --git a/SoObjects/SOGo/SOGoFolder.m b/SoObjects/SOGo/SOGoFolder.m index 3f49d254..aae0a632 100644 --- a/SoObjects/SOGo/SOGoFolder.m +++ b/SoObjects/SOGo/SOGoFolder.m @@ -26,6 +26,8 @@ #import +#import + #import "NSString+Utilities.h" #import "SOGoFolder.h" @@ -202,6 +204,24 @@ return @"httpd/unix-directory"; } +- (NSArray *) davResourceType +{ + NSArray *rType, *groupDavCollection; + + if ([self respondsToSelector: @selector (groupDavResourceType)]) + { + groupDavCollection + = [NSArray arrayWithObjects: [self groupDavResourceType], + XMLNS_GROUPDAV, nil]; + rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, + nil]; + } + else + rType = [NSArray arrayWithObject: @"collection"]; + + return rType; +} + /* folder type */ - (NSString *) outlookFolderClass diff --git a/SoObjects/SOGo/SOGoGCSFolder.h b/SoObjects/SOGo/SOGoGCSFolder.h index 21fb8934..6a89dd4b 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.h +++ b/SoObjects/SOGo/SOGoGCSFolder.h @@ -87,10 +87,4 @@ @end -@interface SOGoGCSFolder (GroupDAVExtensions) - -- (NSString *) groupDavResourceType; - -@end - #endif /* __SOGo_SOGoGCSFolder_H__ */ diff --git a/SoObjects/SOGo/SOGoGCSFolder.m b/SoObjects/SOGo/SOGoGCSFolder.m index a5118ea8..5b938f09 100644 --- a/SoObjects/SOGo/SOGoGCSFolder.m +++ b/SoObjects/SOGo/SOGoGCSFolder.m @@ -381,24 +381,6 @@ static NSString *defaultUserID = @""; return nil; } -- (NSArray *) davResourceType -{ - NSArray *rType, *groupDavCollection; - - if ([self respondsToSelector: @selector (groupDavResourceType)]) - { - groupDavCollection - = [NSArray arrayWithObjects: [self groupDavResourceType], - XMLNS_GROUPDAV, nil]; - rType = [NSArray arrayWithObjects: @"collection", groupDavCollection, - nil]; - } - else - rType = [NSArray arrayWithObject: @"collection"]; - - return rType; -} - - (NSArray *) toOneRelationshipKeys { /* toOneRelationshipKeys are the 'files' contained in a folder */ diff --git a/UI/MailPartViewers/English.lproj/Localizable.strings b/UI/MailPartViewers/English.lproj/Localizable.strings index c120183f..5fadd32d 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 ad6492f8..b5db9cec 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 c120183f..b96425eb 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 7f1cf9e8..f83a3381 100644 --- a/UI/MailPartViewers/UIxMailPartICalAction.m +++ b/UI/MailPartViewers/UIxMailPartICalAction.m @@ -1,76 +1,63 @@ -/* - Copyright (C) 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. -*/ - -#import -#import -#import +/* 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. + */ + +#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"]; +- (WOResponse *) _changePartStatusAction: (NSString *) _newStatus +{ + return [self responseWithStatus: 404]; +} - 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 *) markAcceptedAction +{ + return [self _changePartStatusAction: @"ACCEPTED"]; } -- (id)changePartStatusAction:(NSString *)_newStatus { - [self logWithFormat:@"TODO: should %@: %@", _newStatus, [self clientObject]]; - return [self redirectToViewerWithError: - [_newStatus stringByAppendingString:@" not implemented!"]]; +- (WOResponse *) markDeclinedAction +{ + return [self _changePartStatusAction: @"DECLINED"]; } -- (id)markAcceptedAction { - return [self changePartStatusAction:@"ACCEPTED"]; +- (WOResponse *) markTentativeAction +{ + return [self _changePartStatusAction: @"TENTATIVE"]; } -- (id)markDeclinedAction { - return [self changePartStatusAction:@"DECLINED"]; + +- (WOResponse *) addToCalendarAction +{ + return [self responseWithStatus: 404]; } -- (id)markTentativeAction { - return [self changePartStatusAction:@"TENTATIVE"]; + +- (WOResponse *) deleteFromCalendarAction +{ + return [self responseWithStatus: 404]; } @end /* UIxMailPartICalAction */ diff --git a/UI/MailPartViewers/UIxMailPartICalViewer.h b/UI/MailPartViewers/UIxMailPartICalViewer.h index 08ed095d..98d75abe 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 8b31e532..ec8c18af 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; -} -- (BOOL)isReplySenderAnAttendee { - return [[self storedReplyAttendee] isNotNull]; -} -/* action URLs */ + p = nil; + + e = [self storedEvent]; + if (e) + { + p = [e findParticipantWithEmail: [self replySenderBaseEMail]]; + if (!p) + p = [e findParticipantWithEmail:[self replySenderEMail]]; + } -- (id)acceptLink { - return [[self pathToAttachmentObject] stringByAppendingString:@"/accept"]; -} -- (id)declineLink { - return [[self pathToAttachmentObject] stringByAppendingString:@"/decline"]; + return p; } -- (id)tentativeLink { - return [[self pathToAttachmentObject] stringByAppendingString:@"/tentative"]; + +- (BOOL) isReplySenderAnAttendee +{ + return [[self storedReplyAttendee] isNotNull]; } @end /* UIxMailPartICalViewer */ diff --git a/UI/MailPartViewers/product.plist b/UI/MailPartViewers/product.plist index a042a538..145b4724 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 72a2fc92..87b072da 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 450b5f2a..9f8ce6a2 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) { -- 2.39.5