2007-11-09 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ * 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.
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
{
{
if ((self = [super init]))
{
- displayName = nil;
entries = nil;
ldapSource = nil;
}
- (void) dealloc
{
- [displayName release];
[entries release];
[ldapSource release];
[super dealloc];
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
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 */
+ (Class) bodyPartClassForKey: (NSString *) _key
inContext: (id) _ctx;
++ (Class) bodyPartClassForMimeType: (NSString *) mimeType
+ inContext: (id) _ctx;
@end
#import <NGExtensions/NGBase64Coding.h>
#import <NGImap4/NGImap4Connection.h>
+#import <SoObjects/SOGo/NSDictionary+Utilities.h>
+
#import "SOGoMailObject.h"
#import "SOGoMailManager.h"
/* factory */
-+ (Class)bodyPartClassForKey:(NSString *)_key inContext:(id)_ctx {
++ (Class) bodyPartClassForKey: (NSString *) _key
+ inContext: (id) _ctx
+{
NSString *pe;
pe = [_key pathExtension];
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 {
{
// 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];
}
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;
}
@end
+@interface SOGoFolder (GroupDAVExtensions)
+
+- (NSString *) groupDavResourceType;
+
+@end
+
#endif /* SOGOFOLDER_H */
#import <NGObjWeb/SoSelectorInvocation.h>
+#import <SaxObjC/XMLNamespaces.h>
+
#import "NSString+Utilities.h"
#import "SOGoFolder.h"
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
@end
-@interface SOGoGCSFolder (GroupDAVExtensions)
-
-- (NSString *) groupDavResourceType;
-
-@end
-
#endif /* __SOGo_SOGoGCSFolder_H__ */
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 */
-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";
-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";
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";
-/*
- 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 <NGObjWeb/SoObject.h>
-#import <NGObjWeb/WOContext.h>
-#import <NGObjWeb/WOResponse.h>
+/* UIxMailPartICalAction.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * 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 <Foundation/NSString.h>
#import <NGObjWeb/WODirectAction.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSString+misc.h>
+
+#import <UI/Common/WODirectAction+SOGo.h>
@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 */
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.
*/
@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;
/*
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
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.
*/
@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;
}
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
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];
{
NSCalendarDate *date;
NSTimeZone *timeZone;
-
+
date = [[self authorativeEvent] endDate];
timeZone = [[context activeUser] timeZone];
[date setTimeZone: timeZone];
return date;
}
-- (BOOL)isEndDateOnSameDay {
+- (BOOL) isEndDateOnSameDay
+{
return [[self startTime] isDateOnSameDay:[self endTime]];
}
-- (NSTimeInterval)duration {
+
+- (NSTimeInterval) duration
+{
return [[self endTime] timeIntervalSinceDate:[self startTime]];
}
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];
}
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;
/* 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 */
actionClass = "UIxMailPartICalAction";
actionName = "markTentative";
};
+ addToCalendar = {
+ protectedBy = "View";
+ actionClass = "UIxMailPartICalAction";
+ actionName = "addToCalendar";
+ };
+ deleteFromCalendar = {
+ protectedBy = "View";
+ actionClass = "UIxMailPartICalAction";
+ actionName = "deleteFromCalendar";
+ };
};
};
};
<?xml version="1.0" standalone="yes"?>
+<!DOCTYPE div>
<div xmlns="http://www.w3.org/1999/xhtml"
- 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"
->
+ 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"
+ >
<!-- TODO: add iMIP actions -->
- <var:if condition="couldParseCalendar" const:negate="1">
- <fieldset>
- <legend>Parsing Error</legend>
-
- The SOGo/SOPE iCalendar parser could not parse the body of this MIME part.
+ <var:if condition="couldParseCalendar" const:negate="1">
+ <fieldset>
+ <legend>Parsing Error</legend>
+
+ The SOGo/SOPE iCalendar parser could not parse the body of this MIME part.
+
+ <pre><var:string value="flatContentAsString" /></pre>
+ </fieldset>
+ </var:if>
+
+ <var:if condition="couldParseCalendar">
+ <fieldset>
+ <legend>
+ <var:string label:value="Appointment"/>:
+ <var:string value="inEvent.summary" /> <!-- TODO: shorted title -->
+
+ <var:if condition="isLoggedInUserTheOrganizer">
+ (<var:string label:value="organized_by_you"/>)
+ </var:if>
+ <var:if condition="isLoggedInUserAnAttendee">
+ (<var:string label:value="you_are_an_attendee"/>)
+ </var:if>
+ </legend>
+
+ <var:if condition="inCalendar.method" const:value="REQUEST">
+ <!-- sent to attendees to propose or update a meeting -->
+ <var:if condition="isLoggedInUserAnAttendee">
+ <p class="uix_ical_toolbar" id="iCalendarToolbar">
+ <input id="iCalendarAttachment" type="hidden"
+ var:value="pathToAttachmentObject"/>
+ <input id="iCalendarAccept" class="button"
+ type="button" label:value="Accept"/>
+ <input id="iCalendarDecline" class="button"
+ type="button" label:value="Decline"/>
+ <input id="iCalendarTentative" class="button"
+ type="button" label:value="Tentative"/>
+ <var:if condition="isEventStoredInCalendar" const:negate="YES">
+ | <input id="iCalendarAddToCalendar" class="button"
+ type="button" label:value="Add to calendar"/>
+ </var:if>
+ </p>
+
+ <p>
+ <var:string label:value="Organizer" />
+ <a var:href="inEvent.organizer.email"
+ ><var:string value="organizerDisplayName" /></a>
+ <var:string label:value="request_info" />
+ </p>
+ </var:if>
+
+ <var:if condition="isLoggedInUserAnAttendee" const:negate="YES">
+ <p>
+ <var:string label:value="Organizer" />
+ <a var:href="inEvent.organizer.email"
+ ><var:string value="organizerDisplayName" /></a>
+ <var:string label:value="request_info_no_attendee" />
+ </p>
+ </var:if>
+ </var:if>
+
+
+ <var:if condition="inCalendar.method" const:value="REPLY">
+ <!-- sent to organizer to update the status of the participant -->
+ <var:if condition="isReplySenderAnAttendee" const:negate="1">
+ <p><var:string label:value="reply_info_no_attendee" /></p>
+ </var:if>
+
+ <var:if condition="isReplySenderAnAttendee">
+ <p class="uix_ical_toolbar">
+ <a var:href="addStatusReplyLink"
+ var:_newstat="$inReplyAttendee.partStatWithDefault"
+ var:_sender="replySenderBaseEMail"
+ label:string="do_update_status"/>
+ </p>
+
+ <!-- TODO: replies to events not in the calendar? -->
+
+ <p>
+ Status Update:
+ <var:string label:value="$inReplyAttendee.partStatWithDefault" />,
+ was:
+ <var:string
+ label:value="$storedReplyAttendee.partStatWithDefault" />.
+ </p>
+ </var:if>
+ </var:if>
- <pre><var:string value="flatContentAsString" /></pre>
- </fieldset>
- </var:if>
+ <var:if condition="inCalendar.method" const:value="CANCEL">
+ <!-- sent to attendees to notify of the attendee being removed or the
+ event being deleted -->
+ <var:if condition="isEventStoredInCalendar">
+ <p class="uix_ical_toolbar">
+ <input id="iCalendarDeleteFromCalendar" class="button"
+ type="button" label:value="Delete from calendar"/>
+ </p>
+ </var:if>
+
+ <p>
+ <!-- todo: if there are no attendees, the whole meeting was stopped -->
+ <var:string label:value="cancel_info_text" />
+ </p>
+ </var:if>
- <var:if condition="couldParseCalendar">
- <fieldset>
- <legend>
- <var:string label:value="Appointment"/>:
- <var:string value="inEvent.summary" /> <!-- TODO: shorted title -->
- <var:if condition="isLoggedInUserTheOrganizer">
- (<var:string label:value="organized_by_you"/>)
+ <var:if condition="inCalendar.method" const:value="ADD">
+ <!-- TODO -->
+ <p><var:string label:value="add_info_text" /></p>
</var:if>
- <var:if condition="isLoggedInUserAnAttendee">
- (<var:string label:value="you_are_an_attendee"/>)
+
+
+ <var:if condition="inCalendar.method" const:value="PUBLISH">
+ <!-- none-scheduling event sent to someone for adding to the calendar -->
+ <p><var:string label:value="publish_info_text" /></p>
</var:if>
- </legend>
-
-
- <!-- var:if condition="inCalendar.method" const:value="REQUEST" -->
- <!-- sent to attendees to propose or update a meeting -->
- <var:if condition="isLoggedInUserAnAttendee">
- <p class="uix_ical_toolbar">
- <a var:href="acceptLink" label:string="do_accept"/> |
- <a var:href="declineLink" label:string="do_decline"/> |
- <a var:href="tentativeLink" label:string="do_tentative"/>
- <var:if condition="isEventStoredInCalendar" const:negate="1">
- | <a var:href="addToCalendarLink" label:string="do_add_to_cal" />
- </var:if>
- </p>
- <p>
- <var:string label:value="Organizer" />
- <a var:href="inEvent.organizer.email"
- ><var:string value="organizerDisplayName" /></a>
- <var:string label:value="request_info" />
- </p>
- </var:if>
- <var:if condition="isLoggedInUserAnAttendee" const:negate="1">
- <p>
- <var:string label:value="Organizer" />
- <a var:href="inEvent.organizer.email"
- ><var:string value="organizerDisplayName" /></a>
- <var:string label:value="request_info_no_attendee" />
- </p>
+ <var:if condition="isLoggedInUserTheOrganizer">
+ <!--
+ Possible Status:
+ REPLY => check whether it matches, if not suggest change, show
+ comment
+ REFRESH => add button to resent iCal
+ COUNTER => show panel to decide on counter
+ -->
</var:if>
- <!-- var:if -->
-
-
- <var:if condition="inCalendar.method" const:value="REPLY">
- <!-- sent to organizer to update the status of the participant -->
- <var:if condition="isReplySenderAnAttendee" const:negate="1">
- <p><var:string label:value="reply_info_no_attendee" /></p>
+ <var:if condition="isLoggedInUserTheOrganizer" const:negate="1">
+ <!--
+ Possible Status:
+ REQUEST => ACCEPT, TENTATIVELY, DECLINE buttons with comment field
+ - only show buttons for attendees
+ PUBLISH => just the 'add to calendar' button, rewrite organizer?
+ ADD / CANCEL
+ DECLINE-COUNTER
+ -->
</var:if>
- <var:if condition="isReplySenderAnAttendee">
- <p class="uix_ical_toolbar">
- <a var:href="addStatusReplyLink"
- var:_newstat="$inReplyAttendee.partStatWithDefault"
- var:_sender="replySenderBaseEMail"
- label:string="do_update_status"/>
- </p>
-
- <!-- TODO: replies to events not in the calendar? -->
-
- <p>
- Status Update:
- <var:string label:value="$inReplyAttendee.partStatWithDefault" />,
- was:
- <var:string
- label:value="$storedReplyAttendee.partStatWithDefault" />.
- </p>
- </var:if>
- </var:if>
-
-
- <var:if condition="inCalendar.method" const:value="CANCEL">
- <!-- sent to attendees to notify of the attendee being removed or the
- event being deleted -->
- <var:if condition="isEventStoredInCalendar">
- <p class="uix_ical_toolbar">
- <a var:href="delFromCalendarLink" label:string="do_del_from_cal"/>
- </p>
+
+ <!-- the user comment is used in replies -->
+ <var:if condition="inEvent.userComment.isNotEmpty">
+ <div class="linked_attachment_meta" style="background-color: white;">
+ <var:string value="inEvent.userComment" const:insertBR="1" />
+ </div>
+ <br />
</var:if>
- <p>
- <!-- todo: if there are no attendees, the whole meeting was stopped -->
- <var:string label:value="cancel_info_text" />
- </p>
- </var:if>
-
-
- <var:if condition="inCalendar.method" const:value="ADD">
- <!-- TODO -->
- <p><var:string label:value="add_info_text" /></p>
- </var:if>
-
-
- <var:if condition="inCalendar.method" const:value="PUBLISH">
- <!-- none-scheduling event sent to someone for adding to the calendar -->
- <p><var:string label:value="publish_info_text" /></p>
- </var:if>
-
-
- <var:if condition="isLoggedInUserTheOrganizer">
- <!--
- Possible Status:
- REPLY => check whether it matches, if not suggest change, show
- comment
- REFRESH => add button to resent iCal
- COUNTER => show panel to decide on counter
- -->
- </var:if>
- <var:if condition="isLoggedInUserTheOrganizer" const:negate="1">
- <!--
- Possible Status:
- REQUEST => ACCEPT, TENTATIVELY, DECLINE buttons with comment field
- - only show buttons for attendees
- PUBLISH => just the 'add to calendar' button, rewrite organizer?
- ADD / CANCEL
- DECLINE-COUNTER
- -->
- </var:if>
-
-
- <!-- the user comment is used in replies -->
- <var:if condition="inEvent.userComment.isNotEmpty">
<div class="linked_attachment_meta" style="background-color: white;">
- <var:string value="inEvent.userComment" const:insertBR="1" />
+ <table border="0" class="linked_attachment_meta">
+ <tr>
+ <td><var:string label:value="Time"/>:</td>
+ <td>
+ <!-- TODO: we need a better viewer for that -->
+ <var:string value="startTime" formatter="dateFormatter" />
+ <var:string label:value="to" />
+ <var:string value="endTime" formatter="dateFormatter" />
+ </td>
+ </tr>
+
+ <tr>
+ <td><var:string label:value="Organizer"/>:</td>
+ <td>
+ <a var:href="authorativeEvent.organizer.email"
+ ><var:string value="organizerDisplayName" /></a>
+ </td>
+ </tr>
+
+ <tr>
+ <td valign="top"><var:string label:value="Attendees"/>:</td>
+ <td>
+ <var:foreach list="authorativeEvent.participants" item="attendee">
+ <a var:href="attendee.email"><var:string value="attendeeForDisplay"/></a>
+ (<var:string label:value="$attendee.partStatWithDefault" />)
+ <br />
+ </var:foreach>
+ </td>
+ </tr>
+
+ <var:if condition="inEvent.comment.isNotEmpty">
+ <tr> <!-- description in iCal -->
+ <td valign="top"><var:string label:value="Comment"/>:</td>
+ <td>
+ <var:string value="authorativeEvent.comment" const:insertBR="1" />
+ </td>
+ </tr>
+ </var:if>
+ </table>
</div>
- <br />
- </var:if>
-
-
- <div class="linked_attachment_meta" style="background-color: white;">
- <table border="0" class="linked_attachment_meta">
- <tr>
- <td><var:string label:value="Time"/>:</td>
- <td>
- <!-- TODO: we need a better viewer for that -->
- <var:string value="startTime" formatter="dateFormatter" />
- <var:string label:value="to" />
- <var:string value="endTime" formatter="dateFormatter" />
- </td>
- </tr>
-
- <tr>
- <td><var:string label:value="Organizer"/>:</td>
- <td>
- <a var:href="authorativeEvent.organizer.email"
- ><var:string value="organizerDisplayName" /></a>
- </td>
- </tr>
-
- <tr>
- <td valign="top"><var:string label:value="Attendees"/>:</td>
- <td>
- <var:foreach list="authorativeEvent.participants" item="attendee">
- <a var:href="attendee.email"><var:string value="attendeeForDisplay"/></a>
- (<var:string label:value="$attendee.partStatWithDefault" />)
- <br />
- </var:foreach>
- </td>
- </tr>
-
- <var:if condition="inEvent.comment.isNotEmpty">
- <tr> <!-- description in iCal -->
- <td valign="top"><var:string label:value="Comment"/>:</td>
- <td>
- <var:string value="authorativeEvent.comment" const:insertBR="1" />
- </td>
- </tr>
- </var:if>
- </table>
- </div>
- </fieldset>
- </var:if><!-- could parse -->
-
-<!--
+ </fieldset>
+ </var:if><!-- could parse -->
+
+ <!--
<var:string value="appointment" />
<br />
<br />
<br />
--->
+ -->
<pre style="display: none;"><var:string value="flatContentAsString" /></pre>
</div>
mailAccounts = new Array();
}
-var currentMessages = new Array();
+var currentMessages = {};
var maxCachedMessages = 20;
var cachedMessages = new Array();
var currentMailbox = null;
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) {