From: wolfgang Date: Tue, 30 Oct 2007 21:53:10 +0000 (+0000) Subject: git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1206 d1b88da0-ebda-0310... X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5c9ec4619b7f19cb1db6bf19aa081710dbdb0a59;p=scalable-opengroupware.org git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1206 d1b88da0-ebda-0310-925b-ed51d893ca5b --- diff --git a/ChangeLog b/ChangeLog index 3d8206ef..d47cf7cb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,39 @@ +2007-10-30 Wolfgang Sourdeau + + * UI/Scheduler/UIxCalListingActions.m ([UIxCalListingActions + -eventsListAction]): substitude the start and end date with their + UTC counterpart, taking into account the offset from UTC related + to those dates instead of the current one. This renders + -[UIxCalMainView -userUTCOffset] obsolete because the computings + that occured in javascript are now done server-side. + + * UI/Scheduler/UIxCalMainView.m ([UIxCalMainView -userUTCOffset]): + removed obsolete method. + + * SoObjects/Mailer/NSString+Mail.m ([NSString -htmlToText]): new + method converting html content to plain text. + + * SoObjects/Mailer/NSString+Mail.[hm]: new category module + enhancing NSString with utility methods pertaining to mail handling. + + * SoObjects/Mailer/SOGoMailObject.m + ([-shouldFetchPartOfType:_typesubtype:_subtype]): removed obsolete method. + ([SOGoMailObject + -addRequiredKeysOfStructure:infopath:ptoArray:keysacceptedTypes:types]): + modified method to be always recursive and to take an array of the + accepted mime-types as parameter. The returned array now contains + the mime-type as well as the part keys. + + * SoObjects/Mailer/SOGoMailObject+Draft.m ([SOGoMailObject + -contentForEditingOnParts:_prtskeys:_k]): removed obsolete method. + ([SOGoMailObject -contentForEditing]): rewrote method to take into + account the first text/plain part or the first text/html part + converted to text/plain with our new -[NSString htmlToText] + category method. + + * UI/MailerUI/UIxMailActions.m ([-replyToAllAction]): invoke + "replyToAll:" with YES as parameter instead of NO. + 2007-10-29 Wolfgang Sourdeau * SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart diff --git a/SoObjects/Mailer/GNUmakefile b/SoObjects/Mailer/GNUmakefile index 381e3fbd..bd94d2e0 100644 --- a/SoObjects/Mailer/GNUmakefile +++ b/SoObjects/Mailer/GNUmakefile @@ -32,7 +32,8 @@ Mailer_OBJC_FILES += \ \ SOGoMailForward.m \ \ - NSData+Mail.m + NSData+Mail.m \ + NSString+Mail.m Mailer_RESOURCE_FILES += \ Version \ diff --git a/SoObjects/Mailer/NSString+Mail.h b/SoObjects/Mailer/NSString+Mail.h new file mode 100644 index 00000000..25471101 --- /dev/null +++ b/SoObjects/Mailer/NSString+Mail.h @@ -0,0 +1,34 @@ +/* NSString+Mail.h - 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. + */ + +#ifndef NSSTRING_MAIL_H +#define NSSTRING_MAIL_H + +#import + +@interface NSString (SOGoExtension) + +- (NSString *) htmlToText; + +@end + +#endif /* NSSTRING_MAIL_H */ diff --git a/SoObjects/Mailer/NSString+Mail.m b/SoObjects/Mailer/NSString+Mail.m new file mode 100644 index 00000000..494becdc --- /dev/null +++ b/SoObjects/Mailer/NSString+Mail.m @@ -0,0 +1,351 @@ +/* NSString+Mail.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 +#import +#import +#import +#import +#import + +#import "NSString+Mail.h" + +#if 1 +#define showWhoWeAre() \ + [self logWithFormat: @"invoked '%@'", NSStringFromSelector (_cmd)] +#else +#define showWhoWeAre() {} +#endif + +@interface _SOGoHTMLToTextContentHandler : NSObject +{ + NSArray *ignoreContentTags; + NSArray *specialTreatmentTags; + + BOOL ignoreContent; + BOOL orderedList; + BOOL unorderedList; + unsigned int listCount; + + NSMutableString *result; +} + ++ (id) htmlToTextContentHandler; + +- (NSString *) result; + +@end + +@implementation _SOGoHTMLToTextContentHandler + ++ (id) htmlToTextContentHandler +{ + static id htmlToTextContentHandler; + + if (!htmlToTextContentHandler) + htmlToTextContentHandler = [self new]; + + return htmlToTextContentHandler; +} + +- (id) init +{ + if ((self = [super init])) + { + ignoreContentTags = [NSArray arrayWithObjects: @"head", @"script", + @"style", nil]; + specialTreatmentTags = [NSArray arrayWithObjects: @"body", @"p", @"ul", + @"li", @"table", @"tr", @"td", @"th", + @"br", @"hr", @"dt", @"dd", nil]; + [ignoreContentTags retain]; + [specialTreatmentTags retain]; + + ignoreContent = NO; + result = nil; + + orderedList = NO; + unorderedList = NO; + listCount = 0; + } + + return self; +} + +- (void) dealloc +{ + [ignoreContentTags release]; + [specialTreatmentTags release]; + [result release]; + [super dealloc]; +} + +- (NSString *) result +{ + NSString *newResult; + + newResult = [NSString stringWithString: result]; + [result release]; + result = nil; + + return newResult; +} + +/* SaxContentHandler */ +- (void) startDocument +{ + showWhoWeAre(); + + [result release]; + result = [NSMutableString new]; +} + +- (void) endDocument +{ + showWhoWeAre(); + + ignoreContent = NO; +} + +- (void) startPrefixMapping: (NSString *) prefix + uri: (NSString *) uri +{ + showWhoWeAre(); +} + +- (void) endPrefixMapping: (NSString *) prefix +{ + showWhoWeAre(); +} + +- (void) _startSpecialTreatment: (NSString *) tagName +{ + if ([tagName isEqualToString: @"br"] + || [tagName isEqualToString: @"p"]) + [result appendString: @"\n"]; + else if ([tagName isEqualToString: @"hr"]) + [result appendString: @"______________________________________________________________________________\n"]; + else if ([tagName isEqualToString: @"ul"]) + { + [result appendString: @"\n"]; + unorderedList = YES; + } + else if ([tagName isEqualToString: @"ol"]) + { + [result appendString: @"\n"]; + orderedList = YES; + listCount = 0; + } + else if ([tagName isEqualToString: @"li"]) + { + if (orderedList) + { + listCount++; + [result appendFormat: @" %d. ", listCount]; + } + else + [result appendString: @" * "]; + } + else if ([tagName isEqualToString: @"dd"]) + [result appendString: @" "]; +} + +- (void) _endSpecialTreatment: (NSString *) tagName +{ + if ([tagName isEqualToString: @"ul"]) + { + [result appendString: @"\n"]; + unorderedList = NO; + } + else if ([tagName isEqualToString: @"ol"]) + { + [result appendString: @"\n"]; + orderedList = NO; + } + else if ([tagName isEqualToString: @"dt"]) + { + [result appendString: @":\n"]; + } + else if ([tagName isEqualToString: @"li"] + || [tagName isEqualToString: @"dd"]) + [result appendString: @"\n"]; +} + +- (void) startElement: (NSString *) element + namespace: (NSString *) namespace + rawName: (NSString *) rawName + attributes: (id ) attributes +{ + NSString *tagName; + + showWhoWeAre(); + + if (!ignoreContent) + { + tagName = [rawName lowercaseString]; + if ([ignoreContentTags containsObject: tagName]) + ignoreContent = YES; + else if ([specialTreatmentTags containsObject: tagName]) + [self _startSpecialTreatment: tagName]; + } +} + +- (void) endElement: (NSString *) element + namespace: (NSString *) namespace + rawName: (NSString *) rawName +{ + NSString *tagName; + + showWhoWeAre(); + + if (ignoreContent) + { + tagName = [rawName lowercaseString]; + if ([ignoreContentTags containsObject: tagName]) + ignoreContent = NO; + else if ([specialTreatmentTags containsObject: tagName]) + [self _endSpecialTreatment: tagName]; + } +} + +- (void) characters: (unichar *) characters + length: (int) length +{ + if (!ignoreContent) + [result appendString: [NSString stringWithCharacters: characters + length: length]]; +} + +- (void) ignorableWhitespace: (unichar *) whitespaces + length: (int) length +{ + showWhoWeAre(); +} + +- (void) processingInstruction: (NSString *) pi + data: (NSString *) data +{ + showWhoWeAre(); +} + +- (void) setDocumentLocator: (id ) locator +{ + showWhoWeAre(); +} + +- (void) skippedEntity: (NSString *) entity +{ + showWhoWeAre(); +} + +/* SaxLexicalHandler */ +- (void) comment: (unichar *) chars + length: (int) len +{ + showWhoWeAre(); +} + +- (void) startDTD: (NSString *) name + publicId: (NSString *) pub + systemId: (NSString *) sys +{ + showWhoWeAre(); +} + +- (void) endDTD +{ + showWhoWeAre(); +} + +- (void) startEntity: (NSString *) entity +{ + showWhoWeAre(); +} + +- (void) endEntity: (NSString *) entity +{ + showWhoWeAre(); +} + +- (void) startCDATA +{ + showWhoWeAre(); +} + +- (void) endCDATA +{ + showWhoWeAre(); +} + +@end + +// @interface NSDictionary (SOGoDebug) + +// - (void) dump; + +// @end + +// @implementation NSDictionary (SOGoDebug) + +// - (void) dump +// { +// NSEnumerator *keys; +// NSString *key; +// NSMutableString *dump; + +// dump = [NSMutableString new]; +// [dump appendFormat: @"\nNSDictionary dump (%@):\n", self]; +// keys = [[self allKeys] objectEnumerator]; +// key = [keys nextObject]; +// while (key) +// { +// [dump appendFormat: @"%@: %@\n", key, [self objectForKey: key]]; +// key = [keys nextObject]; +// } +// [dump appendFormat: @"--- end ---\n"]; + +// NSLog (dump); +// [dump release]; +// } + +// @end + +@implementation NSString (SOGoExtension) + +- (NSString *) htmlToText +{ + id parser; + _SOGoHTMLToTextContentHandler *handler; + + parser = [[SaxXMLReaderFactory standardXMLReaderFactory] + createXMLReaderForMimeType: @"text/html"]; + handler = [_SOGoHTMLToTextContentHandler htmlToTextContentHandler]; + [parser setContentHandler: handler]; + [parser parseFromSource: self]; + + return [handler result]; +} + +@end diff --git a/SoObjects/Mailer/SOGoMailObject+Draft.m b/SoObjects/Mailer/SOGoMailObject+Draft.m index ebe7e04a..66ffa444 100644 --- a/SoObjects/Mailer/SOGoMailObject+Draft.m +++ b/SoObjects/Mailer/SOGoMailObject+Draft.m @@ -27,8 +27,10 @@ #import #import +#import #import +#import "NSString+Mail.h" #import "SOGoMailForward.h" #import "SOGoMailObject+Draft.h" @@ -67,98 +69,54 @@ return newSubject; } -- (NSString *) contentForEditingOnParts: (NSDictionary *) _prts - keys: (NSArray *) _k + +- (NSString *) _contentForEditingFromKeys: (NSArray *) keys { - static NSString *textPartSeparator = @"\n---\n"; - NSMutableString *ms; - unsigned int count, max; - NSString *k, *v; - - ms = [NSMutableString stringWithCapacity: 16000]; - - max = [_k count]; - for (count = 0; count < max; count++) + NSArray *types; + NSDictionary *parts; + NSString *rawPart, *content; + int index; + BOOL htmlContent; + + if ([keys count]) { - k = [_k objectAtIndex: count]; - - // TODO: this is DUP code to SOGoMailObject - if ([k isEqualToString: @"body[text]"]) - k = @""; - else if ([k hasPrefix: @"body["]) { - k = [k substringFromIndex: 5]; - if ([k length] > 0) - k = [k substringToIndex: ([k length] - 1)]; - } - - v = [_prts objectForKey: k]; - if ([v isKindOfClass: [NSString class]] - && [v length] > 0) + types = [keys objectsForKey: @"mimeType"]; + index = [types indexOfObject: @"text/plain"]; + if (index == NSNotFound) { - if (count > 0) - [ms appendString: textPartSeparator]; - [ms appendString: v]; + index = [types indexOfObject: @"text/html"]; + htmlContent = YES; } + if (index == NSNotFound) + content = @""; else - [self logWithFormat:@"Note: cannot show part %@", k]; + { + parts = [self fetchPlainTextStrings: keys]; + rawPart = [[parts allValues] objectAtIndex: 0]; + if (htmlContent) + content = [rawPart htmlToText]; + else + content = rawPart; + } } + else + content = @""; - return ms; + return content; } -#warning this method should be fixed to return the first available text/plain \ - part, and otherwise the first text/html part converted to text - (NSString *) contentForEditing { - NSArray *keys; - NSDictionary *parts; - NSMutableArray *topLevelKeys = nil; - unsigned int count, max; - NSRange r; - NSString *contentForEditing; - -// SOGoMailObject *co; - -// co = self; -// keys = [co plainTextContentFetchKeys]; -// infos = [co fetchCoreInfos]; -// partInfos = [infos objectForKey: keys]; -// NSLog (@"infos: '%@'", infos); - - keys = [self plainTextContentFetchKeys]; - max = [keys count]; - if (max > 0) - { - if (max > 1) - { - /* filter keys, only include top-level, or if none, the first */ - for (count = 0; count < max; count++) - { - r = [[keys objectAtIndex: count] rangeOfString: @"."]; - if (!r.length) - { - if (!topLevelKeys) - topLevelKeys = [NSMutableArray arrayWithCapacity: 4]; - [topLevelKeys addObject: [keys objectAtIndex: count]]; - } - } - - if ([topLevelKeys count] > 0) - /* use top-level keys if we have some */ - keys = topLevelKeys; - else - /* just take the first part */ - keys = [NSArray arrayWithObject: [keys objectAtIndex: 0]]; - } + NSMutableArray *keys; + NSArray *acceptedTypes; - parts = [self fetchPlainTextStrings: keys]; - contentForEditing = [self contentForEditingOnParts: parts - keys: keys]; - } - else - contentForEditing = nil; + acceptedTypes + = [NSArray arrayWithObjects: @"text/plain", @"text/html", nil]; + keys = [NSMutableArray new]; + [self addRequiredKeysOfStructure: [self bodyStructure] + path: @"" toArray: keys acceptedTypes: acceptedTypes]; - return contentForEditing; + return [self _contentForEditingFromKeys: keys]; } - (NSString *) contentForReply diff --git a/SoObjects/Mailer/SOGoMailObject.m b/SoObjects/Mailer/SOGoMailObject.m index e126222f..15d53137 100644 --- a/SoObjects/Mailer/SOGoMailObject.m +++ b/SoObjects/Mailer/SOGoMailObject.m @@ -40,6 +40,7 @@ #import #import +#import #import #import @@ -181,10 +182,13 @@ static BOOL debugSoParts = NO; return ma; } -- (NSArray *)toOneRelationshipKeys { +- (NSArray *) toOneRelationshipKeys +{ return [self relationshipKeysWithParts:NO]; } -- (NSArray *)toManyRelationshipKeys { + +- (NSArray *) toManyRelationshipKeys +{ return [self relationshipKeysWithParts:YES]; } @@ -398,11 +402,11 @@ static BOOL debugSoParts = NO; NSData *content; id result, fullResult; - fullResult = [self fetchParts:[NSArray arrayWithObject: @"RFC822"]]; + fullResult = [self fetchParts: [NSArray arrayWithObject: @"RFC822"]]; if (fullResult == nil) return nil; - if ([fullResult isKindOfClass:[NSException class]]) + if ([fullResult isKindOfClass: [NSException class]]) return fullResult; /* extract fetch result */ @@ -440,50 +444,57 @@ static BOOL debugSoParts = NO; - (NSString *) contentAsString { - NSString *s; + id s; NSData *content; - - if ((content = [self content]) == nil) - return nil; - if ([content isKindOfClass:[NSException class]]) - return (id)content; - - s = [[NSString alloc] initWithData: content - encoding: NSISOLatin1StringEncoding]; - if (s == nil) { - [self logWithFormat: - @"ERROR: could not convert data of length %d to string", - [content length]]; - return nil; - } - return [s autorelease]; -} -/* bulk fetching of plain/text content */ + content = [self content]; + if (content) + { + if ([content isKindOfClass: [NSData class]]) + { + s = [[NSString alloc] initWithData: content + encoding: NSISOLatin1StringEncoding]; + if (s) + [s autorelease]; + else + [self logWithFormat: + @"ERROR: could not convert data of length %d to string", + [content length]]; + } + else + s = content; + } + else + s = nil; -- (BOOL) shouldFetchPartOfType: (NSString *) _type - subtype: (NSString *) _subtype -{ - /* - This method decides which parts are 'prefetched' for display. Those are - usually text parts (the set is currently hardcoded in this method ...). - */ - _type = [_type lowercaseString]; - _subtype = [_subtype lowercaseString]; - - return (([_type isEqualToString: @"text"] - && ([_subtype isEqualToString: @"plain"] - || [_subtype isEqualToString: @"html"] - || [_subtype isEqualToString: @"calendar"])) - || ([_type isEqualToString: @"application"] - && ([_subtype isEqualToString: @"pgp-signature"] - || [_subtype hasPrefix: @"x-vnd.kolab."]))); + return s; } -- (void) addRequiredKeysOfStructure: (id) _info - path: (NSString *) _p - toArray: (NSMutableArray *) _keys - recurse: (BOOL) _recurse +/* bulk fetching of plain/text content */ + +// - (BOOL) shouldFetchPartOfType: (NSString *) _type +// subtype: (NSString *) _subtype +// { +// /* +// This method decides which parts are 'prefetched' for display. Those are +// usually text parts (the set is currently hardcoded in this method ...). +// */ +// _type = [_type lowercaseString]; +// _subtype = [_subtype lowercaseString]; + +// return (([_type isEqualToString: @"text"] +// && ([_subtype isEqualToString: @"plain"] +// || [_subtype isEqualToString: @"html"] +// || [_subtype isEqualToString: @"calendar"])) +// || ([_type isEqualToString: @"application"] +// && ([_subtype isEqualToString: @"pgp-signature"] +// || [_subtype hasPrefix: @"x-vnd.kolab."]))); +// } + +- (void) addRequiredKeysOfStructure: (NSDictionary *) info + path: (NSString *) p + toArray: (NSMutableArray *) keys + acceptedTypes: (NSArray *) types { /* This is used to collect the set of IMAP4 fetch-keys required to fetch @@ -494,19 +505,19 @@ static BOOL debugSoParts = NO; */ NSArray *parts; unsigned i, count; - BOOL fetchPart; NSString *k; id body; - NSString *sp; + NSString *sp, *mimeType; id childInfo; - - /* Note: if the part itself doesn't qualify, we still check subparts */ - fetchPart = [self shouldFetchPartOfType: [_info valueForKey: @"type"] - subtype: [_info valueForKey: @"subtype"]]; - if (fetchPart) + + mimeType = [[NSString stringWithFormat: @"%@/%@", + [info valueForKey: @"type"], + [info valueForKey: @"subtype"]] + lowercaseString]; + if ([types containsObject: mimeType]) { - if ([_p length] > 0) - k = [NSString stringWithFormat: @"body[%@]", _p]; + if ([p length] > 0) + k = [NSString stringWithFormat: @"body[%@]", p]; else { /* @@ -516,40 +527,37 @@ static BOOL debugSoParts = NO; */ k = @"body[text]"; } - [_keys addObject: k]; + [keys addObject: [NSDictionary dictionaryWithObjectsAndKeys: k, @"key", + mimeType, @"mimeType", nil]]; } - if (_recurse) + parts = [info objectForKey: @"parts"]; + count = [parts count]; + for (i = 0; i < count; i++) { - /* recurse */ - parts = [(NSDictionary *)_info objectForKey: @"parts"]; - count = [parts count]; - for (i = 0; i < count; i++) - { - sp = (([_p length] > 0) - ? [_p stringByAppendingFormat: @".%d", i + 1] - : [NSString stringWithFormat: @"%d", i + 1]); - - childInfo = [parts objectAtIndex: i]; - - [self addRequiredKeysOfStructure: childInfo - path: sp toArray: _keys - recurse: YES]; - } + sp = (([p length] > 0) + ? [p stringByAppendingFormat: @".%d", i + 1] + : [NSString stringWithFormat: @"%d", i + 1]); - /* check body */ - body = [(NSDictionary *)_info objectForKey: @"body"]; - if (body) - { - sp = [[body valueForKey: @"type"] lowercaseString]; - if ([sp isEqualToString: @"multipart"]) - sp = _p; - else - sp = [_p length] > 0 ? [_p stringByAppendingString: @".1"] : @"1"; - [self addRequiredKeysOfStructure: body - path: sp toArray: _keys - recurse: YES]; - } + childInfo = [parts objectAtIndex: i]; + + [self addRequiredKeysOfStructure: childInfo + path: sp toArray: keys + acceptedTypes: types]; + } + + /* check body */ + body = [info objectForKey: @"body"]; + if (body) + { + sp = [[body valueForKey: @"type"] lowercaseString]; + if ([sp isEqualToString: @"multipart"]) + sp = p; + else + sp = [p length] > 0 ? [p stringByAppendingString: @".1"] : @"1"; + [self addRequiredKeysOfStructure: body + path: sp toArray: keys + acceptedTypes: types]; } } @@ -560,10 +568,13 @@ static BOOL debugSoParts = NO; keys which are marked by the -shouldFetchPartOfType:subtype: method. */ NSMutableArray *ma; - - ma = [NSMutableArray arrayWithCapacity:4]; + NSArray *types; + + types = [NSArray arrayWithObjects: @"text/plain", @"text/html", + @"text/calendar", @"application/pgp-signature", nil]; + ma = [NSMutableArray arrayWithCapacity: 4]; [self addRequiredKeysOfStructure: [self bodyStructure] - path: @"" toArray: ma recurse: YES]; + path: @"" toArray: ma acceptedTypes: types]; return ma; } @@ -577,7 +588,7 @@ static BOOL debugSoParts = NO; [self debugWithFormat: @"fetch keys: %@", _fetchKeys]; - result = [self fetchParts:_fetchKeys]; + result = [self fetchParts: [_fetchKeys objectsForKey: @"key"]]; result = [result valueForKey: @"RawResponse"]; // hackish // Note: -valueForKey: doesn't work! @@ -589,7 +600,7 @@ static BOOL debugSoParts = NO; NSString *key; NSData *data; - key = [_fetchKeys objectAtIndex:i]; + key = [[_fetchKeys objectAtIndex:i] objectForKey: @"key"]; data = [(NSDictionary *)[(NSDictionary *)result objectForKey:key] objectForKey: @"data"]; @@ -615,7 +626,7 @@ static BOOL debugSoParts = NO; - (NSDictionary *) fetchPlainTextParts { - return [self fetchPlainTextParts:[self plainTextContentFetchKeys]]; + return [self fetchPlainTextParts: [self plainTextContentFetchKeys]]; } /* convert parts to strings */ @@ -649,19 +660,20 @@ static BOOL debugSoParts = NO; - (NSDictionary *) stringifyTextParts: (NSDictionary *) _datas { NSMutableDictionary *md; + NSDictionary *info; NSEnumerator *keys; - NSString *key; - - md = [NSMutableDictionary dictionaryWithCapacity:4]; + NSString *key, *s; + + md = [NSMutableDictionary dictionaryWithCapacity:4]; keys = [_datas keyEnumerator]; - while ((key = [keys nextObject]) != nil) { - NSDictionary *info; - NSString *s; - - info = [self lookupInfoForBodyPart:key]; - if ((s = [self stringForData:[_datas objectForKey:key] partInfo:info])) - [md setObject:s forKey:key]; - } + while ((key = [keys nextObject])) + { + info = [self lookupInfoForBodyPart: key]; + s = [self stringForData: [_datas objectForKey:key] partInfo: info]; + if (s) + [md setObject: s forKey: key]; + } + return md; } diff --git a/UI/MailPartViewers/UIxMailPartAlternativeViewer.m b/UI/MailPartViewers/UIxMailPartAlternativeViewer.m index 182894f8..f93a18f7 100644 --- a/UI/MailPartViewers/UIxMailPartAlternativeViewer.m +++ b/UI/MailPartViewers/UIxMailPartAlternativeViewer.m @@ -50,15 +50,15 @@ @implementation UIxMailPartAlternativeViewer - (void)dealloc { - [self->childInfo release]; + [childInfo release]; [super dealloc]; } /* caches */ - (void)resetBodyInfoCaches { - [self->childInfo release]; self->childInfo = nil; - self->childIndex = 0; + [childInfo release]; childInfo = nil; + childIndex = 0; [super resetBodyInfoCaches]; } @@ -110,8 +110,8 @@ - (void)selectChildInfo { unsigned idx; - [self->childInfo release]; self->childInfo = nil; - self->childIndex = 0; + [childInfo release]; childInfo = nil; + childIndex = 0; idx = [self selectPartIndexFromTypes:[self childPartTypes]]; if (idx == NSNotFound) { @@ -120,25 +120,25 @@ return; } - self->childIndex = idx + 1; - self->childInfo = + childIndex = idx + 1; + childInfo = [[[[self bodyInfo] valueForKey:@"parts"] objectAtIndex:idx] retain]; } /* accessors */ - (id)childInfo { - if (self->childInfo == nil) + if (childInfo == nil) [self selectChildInfo]; - return self->childInfo; + return childInfo; } -- (unsigned int)childIndex { - if (self->childIndex == 0) +- (unsigned int) childIndex { + if (childIndex == 0) [self selectChildInfo]; - return self->childIndex - 1; + return childIndex - 1; } - (NSString *)childPartName { diff --git a/UI/MailerUI/UIxMailActions.m b/UI/MailerUI/UIxMailActions.m index 4e962e43..4ab7843c 100644 --- a/UI/MailerUI/UIxMailActions.m +++ b/UI/MailerUI/UIxMailActions.m @@ -66,7 +66,7 @@ - (WOResponse *) replyToAllAction { - return [self replyToAll: NO]; + return [self replyToAll: YES]; } - (WOResponse *) forwardAction diff --git a/UI/Scheduler/UIxCalListingActions.m b/UI/Scheduler/UIxCalListingActions.m index 56a60829..0937d932 100644 --- a/UI/Scheduler/UIxCalListingActions.m +++ b/UI/Scheduler/UIxCalListingActions.m @@ -25,6 +25,7 @@ #import #import #import +#import #import #import @@ -287,6 +288,24 @@ return formattedDate; } +- (NSString *) _adjustedDateForSeconds: (unsigned int) seconds + forAllDay: (BOOL) forAllDay +{ + NSCalendarDate *date; + unsigned int newSeconds, offset; + + date = [NSCalendarDate dateWithTimeIntervalSince1970: seconds]; + [date setTimeZone: userTimeZone]; + + offset = [userTimeZone secondsFromGMTForDate: date]; + if (forAllDay) + newSeconds = seconds + [userTimeZone secondsFromGMT] - offset; + else + newSeconds = seconds + offset; + + return [NSString stringWithFormat: @"%u", newSeconds]; +} + - (WOResponse *) eventsListAction { NSArray *fields, *oldEvent; @@ -309,9 +328,15 @@ newEvent = [NSMutableArray arrayWithArray: oldEvent]; isAllDay = [[oldEvent objectAtIndex: 7] boolValue]; interval = [[oldEvent objectAtIndex: 4] intValue]; + [newEvent replaceObjectAtIndex: 4 + withObject: [self _adjustedDateForSeconds: interval + forAllDay: isAllDay]]; [newEvent addObject: [self _formattedDateForSeconds: interval forAllDay: isAllDay]]; interval = [[oldEvent objectAtIndex: 5] intValue]; + [newEvent replaceObjectAtIndex: 5 + withObject: [self _adjustedDateForSeconds: interval + forAllDay: isAllDay]]; [newEvent addObject: [self _formattedDateForSeconds: interval forAllDay: isAllDay]]; [newEvents addObject: newEvent]; diff --git a/UI/Scheduler/UIxCalMainView.m b/UI/Scheduler/UIxCalMainView.m index a7c0126f..fdbdd81f 100644 --- a/UI/Scheduler/UIxCalMainView.m +++ b/UI/Scheduler/UIxCalMainView.m @@ -42,15 +42,6 @@ static NSMutableArray *yearMenuItems = nil; @implementation UIxCalMainView -- (NSString *) userUTCOffset -{ - NSTimeZone *userTZ; - - userTZ = [[context activeUser] timeZone]; - - return [NSString stringWithFormat: @"%d", [userTZ secondsFromGMT]]; -} - - (NSArray *) monthMenuItems { unsigned int count; diff --git a/UI/Templates/SchedulerUI/UIxCalMainView.wox b/UI/Templates/SchedulerUI/UIxCalMainView.wox index 8359c472..d39e8e7d 100644 --- a/UI/Templates/SchedulerUI/UIxCalMainView.wox +++ b/UI/Templates/SchedulerUI/UIxCalMainView.wox @@ -8,9 +8,6 @@ xmlns:label="OGo:label" className="UIxPageFrame" title="title"> - diff --git a/UI/WebServerResources/JavascriptAPIExtensions.js b/UI/WebServerResources/JavascriptAPIExtensions.js index 18fe2bd6..d288fb4b 100644 --- a/UI/WebServerResources/JavascriptAPIExtensions.js +++ b/UI/WebServerResources/JavascriptAPIExtensions.js @@ -82,14 +82,21 @@ Date.prototype.sogoDayName = function() { Date.prototype.daysUpTo = function(otherDate) { var days = new Array(); - var day1Date = new Date(); - day1Date.setTime(this.getTime()); - day1Date.setHours(0, 0, 0, 0); - var day2Date = new Date(); - day2Date.setTime(otherDate.getTime()); - day2Date.setHours(23, 59, 59, 999); - var day1 = day1Date.getTime(); - var day2 = day2Date.getTime(); + var day1 = this.getTime(); + var day2 = otherDate.getTime(); + if (day1 > day2) { + var tmp = day1; + day1 = day2; + day2 = tmp; + } +// var day1Date = new Date(); +// day1Date.setTime(this.getTime()); +// day1Date.setHours(0, 0, 0, 0); +// var day2Date = new Date(); +// day2Date.setTime(otherDate.getTime()); +// day2Date.setHours(23, 59, 59, 999); +// var day1 = day1Date.getTime(); +// var day2 = day2Date.getTime(); var nbrDays = Math.floor((day2 - day1) / 86400000) + 1; for (var i = 0; i < nbrDays; i++) { diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index 57d718f6..9c4ba4bd 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -29,10 +29,6 @@ function openMessageWindow(msguid, url) { markMailReadInWindow(window, msguid); } var msgWin = openMailComposeWindow(url, wId); - if (msguid) { - msgWin.messageId = msguid; - msgWin.messageURL = ApplicationBaseURL + currentMailbox + "/" + msguid; - } msgWin.focus(); return false; @@ -137,9 +133,9 @@ function markMailReadInWindow(win, msguid) { /* mail list reply */ function openMessageWindowsForSelection(action, firstOnly) { - if (document.body.hasClassName("popup")) - win = openMessageWindow(window.messageId, - window.messageURL + "/" + action); + if (document.body.hasClassName("popup")) { + return true; + } else { var messageList = $("messageList"); var rows = messageList.getSelectedRowsId(); @@ -247,6 +243,17 @@ function deleteSelectedMessagesCallback(http) { log ("deleteSelectedMessagesCallback: problem during ajax request " + http.status); } +function deleteDraft(url) { + /* this is called by UIxMailEditor with window.opener */ + new Ajax.Request(url, { + method: 'post', + onFailure: function(transport) { + if (!isHttpStatus204) + log("draftDeleteCallback: problem during ajax request: " + transport.status); + } + }); +} + function moveMessages(rowIds, folder) { var failCount = 0; diff --git a/UI/WebServerResources/SchedulerUI.js b/UI/WebServerResources/SchedulerUI.js index 7fd00f8b..19e4bce0 100644 --- a/UI/WebServerResources/SchedulerUI.js +++ b/UI/WebServerResources/SchedulerUI.js @@ -617,17 +617,11 @@ function drawCalendarEvent(eventData, sd, ed) { var viewEndDate = ed.asDate(); var startDate = new Date(); + startDate.setTime(eventData[4] * 1000); var endDate = new Date(); - if (eventData[7] == 0) { - startDate.setTime(eventData[4] * 1000 + (1000 * UTCOffset)); - endDate.setTime(eventData[5] * 1000 + (1000 * UTCOffset)); - } - else { - startDate.setTime(eventData[4] * 1000); - endDate.setTime(eventData[5] * 1000); - } + endDate.setTime(eventData[5] * 1000); -// log ("s: " + startDate+ "; e: " + endDate); +// log ("s: " + startDate + "; e: " + endDate); var days = startDate.daysUpTo(endDate); diff --git a/UI/WebServerResources/UIxMailEditor.js b/UI/WebServerResources/UIxMailEditor.js index fd8d1e7d..eef5415f 100644 --- a/UI/WebServerResources/UIxMailEditor.js +++ b/UI/WebServerResources/UIxMailEditor.js @@ -232,15 +232,12 @@ function clickedEditorSave(sender) { if (input) input.parentNode.removeChild(input); - var toolbar = document.getElementById("toolbar"); - if (!document.busyAnim) - document.busyAnim = startAnimation(toolbar); - window.shouldPreserve = true; document.pageform.action = "save"; document.pageform.submit(); - refreshMailbox(); + if (window.opener && window.open && !window.closed) + window.opener.refreshFolderByType('draft'); return false; } @@ -355,13 +352,13 @@ function onMailEditorClose(event) { if (window.shouldPreserve) window.shouldPreserve = false; else { - var url = "" + window.location; - var parts = url.split("/"); - parts[parts.length-1] = "delete"; - url = parts.join("/"); - http = createHTTPClient(); - http.open("POST", url, false /* not async */); - http.send(""); + if (window.opener && window.opener.open && !window.opener.closed) { + var url = "" + window.location; + var parts = url.split("/"); + parts[parts.length-1] = "delete"; + url = parts.join("/"); + window.opener.deleteDraft(url); + } } Event.stopObserving(window, "beforeunload", onMailEditorClose); diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index 8eb94035..5e7ffcea 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -228,12 +228,19 @@ function openContactWindow(url, wId) { } function openMailComposeWindow(url, wId) { + var parentWindow = this; + if (!wId) wId = "" + (new Date().getTime()); - var w = window.open(url, wId, + else + if (document.body.hasClassName("popup")) + parentWindow = window.opener; + + var w = parentWindow.open(url, wId, "width=680,height=520,resizable=1,scrollbars=1,toolbar=0," + "location=0,directories=0,status=0,menubar=0" + ",copyhistory=0"); + w.focus(); return w; @@ -290,7 +297,8 @@ function triggerAjaxRequest(url, callback, userdata) { if (http.readyState == 4 && activeAjaxRequests > 0) { if (!http.aborted) { - http.callbackData = userdata; + if (userdata) + http.callbackData = userdata; callback(http); } activeAjaxRequests -= 1;