]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1206 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 30 Oct 2007 21:53:10 +0000 (21:53 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Tue, 30 Oct 2007 21:53:10 +0000 (21:53 +0000)
16 files changed:
ChangeLog
SoObjects/Mailer/GNUmakefile
SoObjects/Mailer/NSString+Mail.h [new file with mode: 0644]
SoObjects/Mailer/NSString+Mail.m [new file with mode: 0644]
SoObjects/Mailer/SOGoMailObject+Draft.m
SoObjects/Mailer/SOGoMailObject.m
UI/MailPartViewers/UIxMailPartAlternativeViewer.m
UI/MailerUI/UIxMailActions.m
UI/Scheduler/UIxCalListingActions.m
UI/Scheduler/UIxCalMainView.m
UI/Templates/SchedulerUI/UIxCalMainView.wox
UI/WebServerResources/JavascriptAPIExtensions.js
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/generic.js

index 3d8206ef92b5c6cb900bf29a809bf7df9a9f9549..d47cf7cb0284b9ed4cf8b4cecce5407ed68cf01a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,39 @@
+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
index 381e3fbddf8f99fe8f9ae0032c9210a200675ad8..bd94d2e0e23bd849b2e18d05476c59c42e71e2dc 100644 (file)
@@ -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 (file)
index 0000000..2547110
--- /dev/null
@@ -0,0 +1,34 @@
+/* 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 */
diff --git a/SoObjects/Mailer/NSString+Mail.m b/SoObjects/Mailer/NSString+Mail.m
new file mode 100644 (file)
index 0000000..494becd
--- /dev/null
@@ -0,0 +1,351 @@
+/* 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
index ebe7e04a7b2fddb933ab969aaa827110b3e71448..66ffa44476013aecf456810318655a9aa925bda2 100644 (file)
 #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
index e126222f3eb8fc687a34a2bfe305f09e37dc8d1c..15d531375cae2514eab9b34b07fb3cd52bdd8bf5 100644 (file)
@@ -40,6 +40,7 @@
 #import <NGImap4/NGImap4EnvelopeAddress.h>
 #import <NGMail/NGMimeMessageParser.h>
 
+#import <SoObjects/SOGo/NSArray+Utilities.h>
 #import <SoObjects/SOGo/SOGoPermissions.h>
 #import <SoObjects/SOGo/SOGoUser.h>
 
@@ -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;
 }
 
index 182894f89c8621f6a69cc311403f906d2f795511..f93a18f7e17cf53b17341e6f5d52790d4303a26d 100644 (file)
 @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 {
index 4e962e432a08bc0fd998503c60131d6a0b3739f2..4ab7843c3632b2523411fb877653b70af9976daf 100644 (file)
@@ -66,7 +66,7 @@
 
 - (WOResponse *) replyToAllAction
 {
-  return [self replyToAll: NO];
+  return [self replyToAll: YES];
 }
 
 - (WOResponse *) forwardAction
index 56a608294164a02ee1b8cff2e41c9c8baf525c4a..0937d932ec24d22d6bf7cfddc65e48ecc3593b8c 100644 (file)
@@ -25,6 +25,7 @@
 #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];
index a7c0126f060302b677a72c7daccf147e50f5f3fa..fdbdd81f73855fc1d7b7bb2e3d792c49499b90a8 100644 (file)
@@ -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;
index 8359c472dd3eb611b2917a06bfd603dbd4bb0513..d39e8e7d3bef1f76e0c6a273ac6dc9224991ce96 100644 (file)
@@ -8,9 +8,6 @@
   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>
index 18fe2bd6b50b5a604fa188d28dcfa78244c03c5d..d288fb4b0a0c36050e2373f4aa96708fefbd36b5 100644 (file)
@@ -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++) {
index 57d718f6fa7deeaf561ac5f014410fc5da438230..9c4ba4bdbc4c5c755c8d6e34a34f6a14ec5b78a7 100644 (file)
@@ -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;
 
index 7fd00f8bf419134568349cdb9de26c9c27e745b7..19e4bce063b6a698540e739f15539242344c91ce 100644 (file)
@@ -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);
 
index fd8d1e7da3779379fc3a20a5ef9b12d42675faa6..eef5415f0160817898696a19e3607cf6a1563566 100644 (file)
@@ -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);
index 8eb94035c7931eb2ed6760cf74cc159b0518ac6d..5e7ffceac51fd6074025a32c14573cc1eb38a5a8 100644 (file)
@@ -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;