+2007-10-30 Wolfgang Sourdeau <wsourdeau@inverse.ca>
+
+ * 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 <wsourdeau@inverse.ca>
* SoObjects/Mailer/SOGoMailBodyPart.m ([SOGoMailBodyPart
\
SOGoMailForward.m \
\
- NSData+Mail.m
+ NSData+Mail.m \
+ NSString+Mail.m
Mailer_RESOURCE_FILES += \
Version \
--- /dev/null
+/* NSString+Mail.h - 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.
+ */
+
+#ifndef NSSTRING_MAIL_H
+#define NSSTRING_MAIL_H
+
+#import <Foundation/NSString.h>
+
+@interface NSString (SOGoExtension)
+
+- (NSString *) htmlToText;
+
+@end
+
+#endif /* NSSTRING_MAIL_H */
--- /dev/null
+/* NSString+Mail.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/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSObject.h>
+#import <SaxObjC/SaxAttributes.h>
+#import <SaxObjC/SaxContentHandler.h>
+#import <SaxObjC/SaxLexicalHandler.h>
+#import <SaxObjC/SaxXMLReader.h>
+#import <SaxObjC/SaxXMLReaderFactory.h>
+#import <NGExtensions/NSString+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+
+#import "NSString+Mail.h"
+
+#if 1
+#define showWhoWeAre() \
+ [self logWithFormat: @"invoked '%@'", NSStringFromSelector (_cmd)]
+#else
+#define showWhoWeAre() {}
+#endif
+
+@interface _SOGoHTMLToTextContentHandler : NSObject <SaxContentHandler, SaxLexicalHandler>
+{
+ 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 <SaxAttributes>) 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 <NSObject, SaxLocator>) 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 <NSObject, SaxXMLReader> parser;
+ _SOGoHTMLToTextContentHandler *handler;
+
+ parser = [[SaxXMLReaderFactory standardXMLReaderFactory]
+ createXMLReaderForMimeType: @"text/html"];
+ handler = [_SOGoHTMLToTextContentHandler htmlToTextContentHandler];
+ [parser setContentHandler: handler];
+ [parser parseFromSource: self];
+
+ return [handler result];
+}
+
+@end
#import <NGExtensions/NSString+misc.h>
#import <NGExtensions/NSObject+Logs.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
#import <SoObjects/SOGo/SOGoUser.h>
+#import "NSString+Mail.h"
#import "SOGoMailForward.h"
#import "SOGoMailObject+Draft.h"
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
#import <NGImap4/NGImap4EnvelopeAddress.h>
#import <NGMail/NGMimeMessageParser.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
#import <SoObjects/SOGo/SOGoPermissions.h>
#import <SoObjects/SOGo/SOGoUser.h>
return ma;
}
-- (NSArray *)toOneRelationshipKeys {
+- (NSArray *) toOneRelationshipKeys
+{
return [self relationshipKeysWithParts:NO];
}
-- (NSArray *)toManyRelationshipKeys {
+
+- (NSArray *) toManyRelationshipKeys
+{
return [self relationshipKeysWithParts:YES];
}
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 */
- (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
*/
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
{
/*
*/
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];
}
}
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;
}
[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!
NSString *key;
NSData *data;
- key = [_fetchKeys objectAtIndex:i];
+ key = [[_fetchKeys objectAtIndex:i] objectForKey: @"key"];
data = [(NSDictionary *)[(NSDictionary *)result objectForKey:key]
objectForKey: @"data"];
- (NSDictionary *) fetchPlainTextParts
{
- return [self fetchPlainTextParts:[self plainTextContentFetchKeys]];
+ return [self fetchPlainTextParts: [self plainTextContentFetchKeys]];
}
/* convert parts to strings */
- (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;
}
@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];
}
- (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) {
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 {
- (WOResponse *) replyToAllAction
{
- return [self replyToAll: NO];
+ return [self replyToAll: YES];
}
- (WOResponse *) forwardAction
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSNull.h>
#import <Foundation/NSString.h>
+#import <Foundation/NSTimeZone.h>
#import <Foundation/NSValue.h>
#import <NGObjWeb/WOContext.h>
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;
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];
@implementation UIxCalMainView
-- (NSString *) userUTCOffset
-{
- NSTimeZone *userTZ;
-
- userTZ = [[context activeUser] timeZone];
-
- return [NSString stringWithFormat: @"%d", [userTZ secondsFromGMT]];
-}
-
- (NSArray *) monthMenuItems
{
unsigned int count;
xmlns:label="OGo:label"
className="UIxPageFrame"
title="title">
- <script type="text/javascript">
- var UTCOffset = <var:string value="userUTCOffset"/>;
- </script>
<div class="preload" style="visibility: hidden;">
<img rsrc:src="event-gradient.png"/>
</div>
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++) {
markMailReadInWindow(window, msguid);
}
var msgWin = openMailComposeWindow(url, wId);
- if (msguid) {
- msgWin.messageId = msguid;
- msgWin.messageURL = ApplicationBaseURL + currentMailbox + "/" + msguid;
- }
msgWin.focus();
return false;
/* 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();
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;
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);
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;
}
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);
}
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;
if (http.readyState == 4
&& activeAjaxRequests > 0) {
if (!http.aborted) {
- http.callbackData = userdata;
+ if (userdata)
+ http.callbackData = userdata;
callback(http);
}
activeAjaxRequests -= 1;