]> err.no Git - scalable-opengroupware.org/commitdiff
various cleanups, work on forward/reply
authorhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Sun, 30 Jan 2005 17:20:10 +0000 (17:20 +0000)
committerhelge <helge@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Sun, 30 Jan 2005 17:20:10 +0000 (17:20 +0000)
git-svn-id: http://svn.opengroupware.org/SOGo/trunk@506 d1b88da0-ebda-0310-925b-ed51d893ca5b

29 files changed:
SOGo/SoObjects/Mailer/ChangeLog
SOGo/SoObjects/Mailer/SOGoDraftObject.h
SOGo/SoObjects/Mailer/SOGoDraftObject.m
SOGo/SoObjects/Mailer/SOGoDraftsFolder.m
SOGo/SoObjects/Mailer/SOGoMailAccount.m
SOGo/SoObjects/Mailer/SOGoMailObject.h
SOGo/SoObjects/Mailer/Version
SOGo/UI/Mailer/ChangeLog
SOGo/UI/Mailer/UIxFilterList.m
SOGo/UI/Mailer/UIxFilterList.wox
SOGo/UI/Mailer/UIxMailAccountView.m
SOGo/UI/Mailer/UIxMailAccountView.wox
SOGo/UI/Mailer/UIxMailAccountsView.m
SOGo/UI/Mailer/UIxMailAccountsView.wox
SOGo/UI/Mailer/UIxMailEditor.m
SOGo/UI/Mailer/UIxMailEditor.wox
SOGo/UI/Mailer/UIxMailEditorAction.m
SOGo/UI/Mailer/UIxMailEditorAttach.m
SOGo/UI/Mailer/UIxMailFormatter.h
SOGo/UI/Mailer/UIxMailListView.m
SOGo/UI/Mailer/UIxMailListView.wox
SOGo/UI/Mailer/UIxMailMainFrame.m
SOGo/UI/Mailer/UIxMailView.m
SOGo/UI/Mailer/UIxMailView.wox
SOGo/UI/Mailer/UIxSieveEditor.m
SOGo/UI/Mailer/UIxSieveEditor.wox
SOGo/UI/Mailer/UIxSubjectFormatter.m
SOGo/UI/Mailer/Version
SOGo/UI/Mailer/product.plist

index 93da62fa665f974a2d024de05801399f843066de..804434099da2e51adc4c02a478df7b0254160ac1 100644 (file)
@@ -1,3 +1,16 @@
+2005-01-30  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v0.9.59
+
+       * SOGoDraftObject.m: improved attachment/info API to return exceptions,
+         minor improvement to attachment name check, properly generate
+         message/rfc822 mime type for mail attachments, properly generate
+         "text/plain; utf8" header
+
+       * SOGoMailAccount.m: use a constant for INBOX folder name, disabled
+         'Filters' folder unless the 'SOGoEnableSieveFolder' is set (since 
+         Sieve support is incomplete)
+
 2005-01-28  Helge Hess  <helge.hess@skyrix.com>
 
        * v0.9.58
index 0043021c03564ae7b2b7047a330daf11602c0d0f..0c975366ab5c1166329e55a4914cb23f0e922ae4 100644 (file)
@@ -32,6 +32,8 @@
   The SOGoDraftObject is used for composing new messages. It is necessary
   because we can't cache objects in a session. So the contents of the drafts
   folder are some kind of "mail creation transaction".
+
+  TODO: store-info should be an own object, not NSDictionary.
 */
 
 @class NSString, NSArray, NSDictionary, NSData, NSException;
 /* contents */
 
 - (NSDictionary *)fetchInfo;
-- (BOOL)storeInfo:(NSDictionary *)_info;
+- (NSException *)storeInfo:(NSDictionary *)_info;
+
+/* attachments */
 
 - (NSArray *)fetchAttachmentNames;
 - (BOOL)isValidAttachmentName:(NSString *)_name;
-- (BOOL)saveAttachment:(NSData *)_attachment withName:(NSString *)_name;
-- (BOOL)deleteAttachmentWithName:(NSString *)_name;
+- (NSException *)saveAttachment:(NSData *)_attach withName:(NSString *)_name;
+- (NSException *)deleteAttachmentWithName:(NSString *)_name;
 
 /* NGMime representations */
 
index be9909bd413909ce03a76a5b71ddd9216e76c92f..5add88971689dde9e61631b0a8f5c0fdeef44b2c 100644 (file)
@@ -98,21 +98,27 @@ static BOOL       debugOn = NO;
 
 /* contents */
 
-- (BOOL)storeInfo:(NSDictionary *)_info {
+- (NSException *)storeInfo:(NSDictionary *)_info {
   if (_info == nil) {
-    [self warnWithFormat:@"got no info to write for draft!"];
-    return NO;
+    return [NSException exceptionWithHTTPStatus:500 /* server error */
+                       reason:@"got no info to write for draft!"];
   }
   if (![self _ensureDraftFolderPath]) {
     [self errorWithFormat:@"could not create folder for draft: '%@'",
             [self draftFolderPath]];
-    return NO;
+    return [NSException exceptionWithHTTPStatus:500 /* server error */
+                       reason:@"could not create folder for draft!"];
   }
   if (![_info writeToFile:[self infoPath] atomically:YES]) {
     [self errorWithFormat:@"could not write info: '%@'", [self infoPath]];
-    return NO;
+    return [NSException exceptionWithHTTPStatus:500 /* server error */
+                       reason:@"could not write draft info!"];
   }
-  return YES;
+  
+  /* reset info cache */
+  [self->info release]; self->info = nil;
+  
+  return nil /* everything is excellent */;
 }
 - (NSDictionary *)fetchInfo {
   NSString *p;
@@ -166,21 +172,18 @@ static BOOL       debugOn = NO;
 }
 
 - (BOOL)isValidAttachmentName:(NSString *)_name {
-  NSRange r;
+  static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", @" ", nil };
+  unsigned i;
+  NSRange  r;
 
   if (![_name isNotNull])     return NO;
   if ([_name length] == 0)    return NO;
   if ([_name hasPrefix:@"."]) return NO;
   
-  r = [_name rangeOfString:@"/"];
-  if (r.length > 0) return NO;
-  r = [_name rangeOfString:@".."];
-  if (r.length > 0) return NO;
-  r = [_name rangeOfString:@"~"];
-  if (r.length > 0) return NO;
-  r = [_name rangeOfString:@"\""];
-  if (r.length > 0) return NO;
-  
+  for (i = 0; sescape[i] != nil; i++) {
+    r = [_name rangeOfString:sescape[i]];
+    if (r.length > 0) return NO;
+  }
   return YES;
 }
 
@@ -191,36 +194,53 @@ static BOOL       debugOn = NO;
   return [[self draftFolderPath] stringByAppendingPathComponent:_name];
 }
 
-- (BOOL)saveAttachment:(NSData *)_attachment withName:(NSString *)_name {
-  NSString *p;
+- (NSException *)invalidAttachmentNameError:(NSString *)_name {
+  return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
+                     reason:@"Invalid attachment name!"];
+}
 
-  if (_attachment == nil)
-    return NO;
+- (NSException *)saveAttachment:(NSData *)_attach withName:(NSString *)_name {
+  NSString *p;
+  
+  if (![_attach isNotNull]) {
+    return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
+                       reason:@"Missing attachment content!"];
+  }
   
   if (![self _ensureDraftFolderPath]) {
-    [self errorWithFormat:@"could not create folder for draft!"];
-    return NO;
+    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                       reason:@"Could not create folder for draft!"];
   }
   if (![self isValidAttachmentName:_name])
-    return NO;
+    return [self invalidAttachmentNameError:_name];
   
   p = [self pathToAttachmentWithName:_name];
-  return [_attachment writeToFile:p atomically:YES];
+  if (![_attach writeToFile:p atomically:YES]) {
+    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                       reason:@"Could not write attachment to draft!"];
+  }
+  
+  return nil; /* everything OK */
 }
 
-- (BOOL)deleteAttachmentWithName:(NSString *)_name {
+- (NSException *)deleteAttachmentWithName:(NSString *)_name {
   NSFileManager *fm;
   NSString *p;
   
   if (![self isValidAttachmentName:_name])
-    return NO;
+    return [self invalidAttachmentNameError:_name];
   
   fm = [self spoolFileManager];
   p  = [self pathToAttachmentWithName:_name];
   if (![fm fileExistsAtPath:p])
-    return YES; /* well, doesn't exist, so its deleted ;-) */
+    return nil; /* well, doesn't exist, so its deleted ;-) */
   
-  return [fm removeFileAtPath:p handler:nil];
+  if (![fm removeFileAtPath:p handler:nil]) {
+    [self logWithFormat:@"ERROR: failed to delete file: %@", p];
+    return [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                       reason:@"Could not delete attachment from draft!"];
+  }
+  return nil; /* everything OK */
 }
 
 /* NGMime representations */
@@ -229,43 +249,63 @@ static BOOL       debugOn = NO;
   NGMutableHashMap *map;
   NGMimeBodyPart   *bodyPart;
   NSDictionary     *lInfo;
-
+  id body;
+  
   if ((lInfo = [self fetchInfo]) == nil)
     return nil;
   
   /* prepare header of body part */
-  
+
   map = [[[NGMutableHashMap alloc] initWithCapacity:2] autorelease];
+
+  // TODO: set charset in header!
   [map setObject:@"text/plain" forKey:@"content-type"];
+  if ((body = [lInfo objectForKey:@"text"]) != nil) {
+    if ([body isKindOfClass:[NSString class]]) {
+      [map setObject:@"text/plain; charset=utf-8" forKey:@"content-type"];
+      body = [body dataUsingEncoding:NSUTF8StringEncoding];
+    }
+  }
   
   /* prepare body content */
   
   bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
-  [bodyPart setBody:[lInfo objectForKey:@"text"]];
+  [bodyPart setBody:body];
   return bodyPart;
 }
 
 - (NGMimeMessage *)mimeMessageForContentWithHeaderMap:(NGMutableHashMap *)map {
   NSDictionary  *lInfo;
   NGMimeMessage *message;  
+  id body;
 
   if ((lInfo = [self fetchInfo]) == nil)
     return nil;
   
   [map setObject:@"text/plain" forKey:@"content-type"];
+  if ((body = [lInfo objectForKey:@"text"]) != nil) {
+    if ([body isKindOfClass:[NSString class]]) {
+      /* Note: just 'utf8' is displayed wrong in Mail.app */
+      [map setObject:@"text/plain; charset=utf-8" forKey:@"content-type"];
+      body = [body dataUsingEncoding:NSUTF8StringEncoding];
+    }
+  }
+  
   message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
-  [message setBody:[lInfo objectForKey:@"text"]];
+  [message setBody:body];
   return message;
 }
 
 - (NSString *)mimeTypeForExtension:(NSString *)_ext {
   // TODO: make configurable
+  // TODO: use /etc/mime-types
   if ([_ext isEqualToString:@"txt"])  return @"text/plain";
   if ([_ext isEqualToString:@"html"]) return @"text/html";
   if ([_ext isEqualToString:@"htm"])  return @"text/html";
   if ([_ext isEqualToString:@"gif"])  return @"image/gif";
   if ([_ext isEqualToString:@"jpg"])  return @"image/jpeg";
   if ([_ext isEqualToString:@"jpeg"]) return @"image/jpeg";
+  if ([_ext isEqualToString:@"mail"]) return @"message/rfc822";
   return @"application/octet-stream";
 }
 
@@ -289,11 +329,11 @@ static BOOL       debugOn = NO;
   
   if ([type hasPrefix:@"text/"])
     cdtype = @"inline";
-  else if ([type hasPrefix:@"image/"])
+  else if ([type hasPrefix:@"image/"] || [type hasPrefix:@"message"])
     cdtype = @"inline";
   else
     cdtype = @"attachment";
-
+  
   cd = [cdtype stringByAppendingString:@"; filename=\""];
   cd = [cd stringByAppendingString:_name];
   cd = [cd stringByAppendingString:@"\""];
@@ -308,7 +348,7 @@ static BOOL       debugOn = NO;
   NGMimeBodyPart   *bodyPart;
   NSString         *s;
   NSData           *content;
-  BOOL             attachAsString;
+  BOOL             attachAsString, is7bit;
   NSString         *p;
   id body;
 
@@ -323,6 +363,7 @@ static BOOL       debugOn = NO;
     return nil;
   }
   attachAsString = NO;
+  is7bit         = NO;
   
   /* prepare header of body part */
 
@@ -332,6 +373,8 @@ static BOOL       debugOn = NO;
     [map setObject:s forKey:@"content-type"];
     if ([s hasPrefix:@"text/"])
       attachAsString = YES;
+    else if ([s hasPrefix:@"message/rfc822"])
+      is7bit = YES;
   }
   if ((s = [self contentDispositionForAttachmentWithName:_name]))
     [map setObject:s forKey:@"content-disposition"];
@@ -356,6 +399,16 @@ static BOOL       debugOn = NO;
       content = nil;
     }
   }
+  else if (is7bit) {
+    /* 
+       Note: Apparently NGMimeFileData objects are not processed by the MIME
+             generator!
+    */
+    body = [[NGMimeFileData alloc] initWithPath:p removeFile:NO];
+    [map setObject:@"7bit" forKey:@"content-transfer-encoding"];
+    [map setObject:[NSNumber numberWithInt:[body length]] 
+        forKey:@"content-length"];
+  }
   else {
     /* 
        Note: in OGo this is done in LSWImapMailEditor.m:2477. Apparently
@@ -531,6 +584,10 @@ static BOOL       debugOn = NO;
   message = [self mimeMessage];
   if (![message isNotNull])
     return nil;
+  if ([message isKindOfClass:[NSException class]]) {
+    [self errorWithFormat:@"error: %@", message];
+    return nil;
+  }
   
   gen     = [[NGMimeMessageGenerator alloc] init];
   tmpPath = [[gen generateMimeFromPartToFile:message] copy];
index f31d6f49fdb19d2b29a949a97c1a4cabac962398..62f10ab5065bf93c5df0e2c63019128489924bca 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include "SOGoDraftsFolder.h"
 #include "SOGoDraftObject.h"
index 1e3c67411a9d64a997e0dae6f73275e95cffa4bd..658af71eb89a299d6ace190fd0bd9d7f375962f0 100644 (file)
 
 @implementation SOGoMailAccount
 
+static NSString *inboxFolderName  = @"INBOX";
 static NSString *draftsFolderName = @"Drafts";
 static NSString *sieveFolderName  = @"Filters";
+static NSArray  *rootFolderNames  = nil;
+
++ (void)initialize {
+  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
+
+  if ([ud boolForKey:@"SOGoEnableSieveFolder"]) {
+    rootFolderNames = [[NSArray alloc] initWithObjects:
+                                       inboxFolderName, 
+                                       draftsFolderName, 
+                                       sieveFolderName, 
+                                     nil];
+  }
+  else {
+    rootFolderNames = [[NSArray alloc] initWithObjects:
+                                       inboxFolderName, 
+                                       draftsFolderName, 
+                                     nil];
+  }
+}
 
 /* listing the available folders */
 
 - (NSArray *)toManyRelationshipKeys {
   // TODO: hardcoded, if we want to support shared fldrs, this needs to change
-  static NSArray *accFolderNames = nil;
-  if (accFolderNames == nil) {
-    accFolderNames = [[NSArray alloc] initWithObjects:
-                                       @"INBOX", @"Drafts", @"Filters", nil];
-  }
-  return accFolderNames;
+  return rootFolderNames;
 }
 
 /* hierarchy */
@@ -145,20 +160,20 @@ static NSString *sieveFolderName  = @"Filters";
 /* special folders */
 
 - (SOGoMailFolder *)inboxFolderInContext:(id)_ctx {
-  // TODO: use some profile to determine real location
+  // TODO: use some profile to determine real location, use a -traverse lookup
   SOGoMailFolder *folder;
   
   if (self->inboxFolder != nil)
     return self->inboxFolder;
   
-  folder = [self lookupName:@"INBOX" inContext:_ctx acquire:NO];
+  folder = [self lookupName:inboxFolderName inContext:_ctx acquire:NO];
   if ([folder isKindOfClass:[NSException class]]) return folder;
   
   return ((self->inboxFolder = [folder retain]));
 }
 
 - (SOGoMailFolder *)sentFolderInContext:(id)_ctx {
-  // TODO: use some profile to determine real location
+  // TODO: use some profile to determine real location, use a -traverse lookup
   SOGoMailFolder *folder;
   
   if (self->sentFolder != nil)
index e35ad5d1d7ad3bf10be810c75ee57990104728e3..eea288f18fe5a2759a0d66d4814aa7d2eff0e46f 100644 (file)
@@ -51,7 +51,7 @@
 
 /* core infos */
 
-- (id)fetchCoreInfos;
+- (id)fetchCoreInfos; // TODO: what does it do?
 
 - (NGImap4Envelope *)envelope;
 - (NSString *)subject;
index 6d5b940ec4ce8b24601af377634efc1fa4b263d2..563de9bc5f687eccf0e2f019f764372e39436abf 100644 (file)
@@ -1,6 +1,6 @@
 # Version file
 
-SUBMINOR_VERSION:=58
+SUBMINOR_VERSION:=59
 
 # v0.9.55 requires NGExtensions v4.5.136
 # v0.9.44 requires libNGMime    v4.3.194
index 6923eee31569de03cae93347d9cdd181d2041fa3..5559e5efa08e927408f7351864f7bd3e390b7afc 100644 (file)
@@ -1,3 +1,26 @@
+2005-01-30  Helge Hess  <helge.hess@opengroupware.org>
+
+       * v0.9.80
+
+       * UIxMailEditorAction.m: work on forwarding and reply
+       
+       * UIxFilterList.wox, UIxMailAccountView.wox, UIxMailAccountsView.wox,
+         UIxSieveEditor.wox: fixed title (needs localization)
+
+       * UIxMailMainFrame.m: always generate title as given by the parent
+         component (titles need to get fixed!), was fixed to OpenGroupware.org
+         before unless in debug mode
+       
+       * UIxMailEditor.m: use new storeInfo API, generate a proper panelTitle
+         (needs localization)
+
+       * UIxMailEditorAttach.m: use new attachment API
+
+       * UIxMailView.wox: remoted link to screenshot, use mail subject as
+         title
+
+       * UIxMailListView.m: disabled a log
+
 2005-01-28  Helge Hess  <helge.hess@skyrix.com>
 
        * v0.9.79
index d6d99966bd857ecce9b1132f70af4e03b5aec538..08cb7831b0fbc7fb5dd3f5fbfb602530fc94f8e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
   return self->filters;
 }
 
+- (NSString *)panelTitle {
+  return [self labelForKey:@"Mail Filters"];
+}
+
 /* JavaScript code */
 
 - (NSString *)clickedFilterJS {
index 1306ab495ababd69551970714e6c9fea4b7ab3f6..3bbdc1494b47b7f6692e8b3c1402c0f26e1e1706 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
 >
 
   <div class="titlediv" style="white-space: nowrap;">
index baeaa01c25b4eaf13d1caad228f7974fad7d090d..a0d605773677193382c4daa6f9f18a594476175f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id: UIxMailAccountView.m 308 2004-09-20 10:51:12Z helge $
 
 #include <SOGoUI/UIxComponent.h>
 
 
 @implementation UIxMailAccountView
 
+/* title */
+
+- (NSString *)objectTitle {
+  return [[self clientObject] nameInContainer];
+}
+- (NSString *)panelTitle {
+  NSString *s;
+  
+  s = [self labelForKey:@"Mail Account"];
+  s = [s stringByAppendingString:@": "];
+  s = [s stringByAppendingString:[self objectTitle]];
+  return s;
+}
+
 @end /* UIxMailAccountView */
index 954e4edd0481f168fbc8b17a86fb2e5385cc3b57..28b145c0b1ab8d40bb1fd62f1e540d9ec16ba0c9 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
 >
   <div class="embedwhite_out">
     <div class="embedwhite_in">
index 15fed2091d29586ed95772d2daa9db2f6418b871..cbef1b2b3235366b649dd196d113c97db25ef206 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id: UIxMailAccountsView.m 308 2004-09-20 10:51:12Z helge $
 
 #include <SOGoUI/UIxComponent.h>
 
@@ -32,4 +31,8 @@
 
 @implementation UIxMailAccountsView
 
+- (NSString *)panelTitle {
+  return [self labelForKey:@"SOGo Mail Accounts"];
+}
+
 @end /* UIxMailAccountsView */
index 2ab332b218418cfb3cb778338087a4be2d30415a..1198cf8ab6a05e8fcb2d0ffc4f70ecc9b7c44e71 100644 (file)
@@ -7,13 +7,16 @@
   xmlns:label="OGo:label"
   xmlns:rsrc="OGo:url"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
 >
   <div class="embedwhite_out">
     <div class="embedwhite_in" style="height: 300px">
+      <var:string label:value="Welcome to the SOGo Mailer. Use the folder tree on the left to browse your mail accounts!" />
+<!--
       <h3>SOGo Mail - Available Accounts</h3>
 
       <a rsrc:href="tbird_073_accountview.png">screenshot</a>
+-->
     </div>
   </div>
 </var:component>
index b6954df49d70438023449597524c181c91914ffb..627052d2038df7b4af33049ceaaf7bf525a7ecd0 100644 (file)
@@ -127,6 +127,12 @@ static NSArray *infoKeys = nil;
   return [self->bcc isNotNull] ? self->bcc : [NSArray array];
 }
 
+/* title */
+
+- (NSString *)panelTitle {
+  return [self labelForKey:@"Compose Mail"];
+}
+
 /* info loading */
 
 - (void)loadInfo:(NSDictionary *)_info {
@@ -197,8 +203,10 @@ static NSArray *infoKeys = nil;
   NSDictionary *info;
   
   if ((info = [self storeInfo]) != nil) {
-    if (![[self clientObject] storeInfo:info]) {
-      [self errorWithFormat:@"failed to store draft!"];
+    NSException *error;
+    
+    if ((error = [[self clientObject] storeInfo:info]) != nil) {
+      [self errorWithFormat:@"failed to store draft: %@", error];
       // TODO: improve error handling
       return NO;
     }
index 08dbe64003abaffa494041af675d3207a145510f..c51c4dad6b7a891b3406418d471b4f5794896beb 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
   const:hideFolderTree="1"
 >
  <div id="compose_panel">
index 69d64667273a98f1ec6be8ceea3892d1e0060e16..6a88bdd58290b4be0378e1d0be55921e47f8058d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include <NGObjWeb/WODirectAction.h>
 
   needs to be created in advance.
 */
 
+@class SOGoDraftObject;
+
 @interface UIxMailEditorAction : WODirectAction
+{
+  SOGoDraftObject *newDraft;
+}
+
 @end
 
 #include <SOGo/SoObjects/Mailer/SOGoDraftsFolder.h>
+#include <SOGo/SoObjects/Mailer/SOGoDraftObject.h>
 #include <SOGo/SoObjects/Mailer/SOGoMailAccount.h>
+#include <SOGo/SoObjects/Mailer/SOGoMailObject.h>
 #include "common.h"
 
 @implementation UIxMailEditorAction
 
+- (void)dealloc {
+  [self->newDraft release];
+  [super dealloc];
+}
+
+/* caches */
+
+- (void)reset {
+  [self->newDraft release]; self->newDraft = nil;
+}
+
 /* lookups */
 
 - (SOGoDraftsFolder *)draftsFolder {
@@ -58,7 +76,7 @@
 /* errors */
 
 - (id)didNotFindDraftsError {
-  // TODO: make a nice page
+  // TODO: make a nice error page
   return [@"did not find drafts folder in object: "
           stringByAppendingString:[[self clientObject] description]];
 }
   return [@"could not create a new draft in folder: "
           stringByAppendingString:[_draftsFolder description]];
 }
+- (id)didNotFindMailError {
+  return [NSException exceptionWithHTTPStatus:404 /* Not Found */
+                     reason:@"Did not find mail for operation!"];
+}
 
-/* actions */
+/* compose */
 
 - (id)composeAction {
   SOGoDraftsFolder *drafts;
   if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
   url = [url stringByAppendingString:@"edit"];
   
+  // TODO: debug log
   [self logWithFormat:@"compose on %@: %@", drafts, url];
   
   r = [[self context] response];
   [r setStatus:302 /* moved */];
   [r setHeader:url forKey:@"location"];
+  [self reset];
   return r;
 }
 
+/* creating new draft object */
+
+- (id)newDraftObject {
+  SOGoDraftsFolder *drafts;
+  
+  drafts = [self draftsFolder];
+  if (![drafts isNotNull])
+    return [self didNotFindDraftsError];
+  if ([drafts isKindOfClass:[NSException class]])
+    return drafts;
+
+  return [drafts newObjectInContext:[self context]];
+}
+
+- (NSException *)_setupNewDraft {
+  SOGoDraftObject *tmp;
+  
+  /* create draft object */
+  
+  if ([(tmp = [self newDraftObject]) isKindOfClass:[NSException class]])
+    return (NSException *)tmp;
+  if (![tmp isNotNull]) { /* Note: should never happen? */
+    [self logWithFormat:@"WARNING: got no new draft object and no error!"];
+    return [self didNotFindDraftsError]; // TODO: not exact
+  }
+  
+  ASSIGN(self->newDraft, tmp);
+  [self logWithFormat:@"NEW DRAFT: %@", self->newDraft];
+  
+  return nil;
+}
+
+- (WOResponse *)redirectToEditNewDraft {
+  WOResponse *r;
+  NSString   *url;
+  
+  if (![self->newDraft isNotNull]) {
+    [self logWithFormat:@"ERROR(%s): missing new draft (already -reset?)",
+           __PRETTY_FUNCTION__];
+    return nil;
+  }
+  
+  url = [self->newDraft baseURLInContext:[self context]];
+  if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
+  url = [url stringByAppendingString:@"edit"];
+  
+  // TODO: debug log
+  [self logWithFormat:@"compose on %@", url];
+  
+  r = [[self context] response];
+  [r setStatus:302 /* moved */];
+  [r setHeader:url forKey:@"location"];
+  [self reset];
+  return r;
+}
+
+/* response actions */
+
+- (id)replyToAll:(BOOL)_replyToAll {
+  NSException *error;
+  id tmp;
+  
+  /* ensure mail exists and is filled */
+  
+  // TODO: we could transport the body structure in a hidden field of the mail
+  //       viewer to avoid refetching the core-info?
+  tmp = [[self clientObject] fetchCoreInfos];
+  if ([tmp isKindOfClass:[NSException class]])
+    return tmp;
+  if (![tmp isNotNull])
+    return [self didNotFindMailError];
+
+  /* setup draft */
+  
+  if ((error = [self _setupNewDraft]) != nil)
+    return error;
+  
+#if 0
+  [self logWithFormat:@"CORE: %@", [[self clientObject] fetchCoreInfos]];
+#endif
+  [self reset];
+  return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
+                     reason:@"Sorry, reply is not yet implemented!"];
+}
+
+- (id)replyAction {
+  return [self replyToAll:NO];
+}
+- (id)replyallAction {
+  return [self replyToAll:YES];
+}
+
+- (NSString *)getAttachmentNameForSubject:(NSString *)_subject {
+  /* SOGoDraftObject disallows some strings - anything else required? */
+  static NSString *sescape[] = { 
+    @"/", @"..", @"~", @"\"", @"'", @" ", @".", nil 
+  };
+  static int maxFilenameLength = 64;
+  NSString *s;
+  unsigned i;
+  
+  if (![_subject isNotNull] || [_subject length] == 0)
+    return _subject;
+  s = _subject;
+  
+  if ([s length] > maxFilenameLength)
+    s = [s substringToIndex:maxFilenameLength];
+  
+  for (i = 0; sescape[i] != nil; i++)
+    s = [s stringByReplacingString:sescape[i] withString:@"_"];
+  
+  return [s stringByAppendingString:@".mail"];
+}
+
+- (NSString *)forwardSubject:(NSString *)_subject {
+  if (![_subject isNotNull] || [_subject length] == 0)
+    return _subject;
+  
+  /* Note: this is how Thunderbird 1.0 creates the subject */
+  _subject = [@"[Fwd: " stringByAppendingString:_subject];
+  _subject = [_subject stringByAppendingString:@"]"];
+  return _subject;
+}
+
+- (id)forwardAction {
+  NSException  *error;
+  NSData       *content;
+  NSDictionary *info;
+  id result;
+
+  /* fetch message */
+  
+  if ((content = [[self clientObject] content]) == nil)
+    return [self didNotFindMailError];
+  if ([content isKindOfClass:[NSException class]])
+    return content;
+  
+  /* setup draft */
+  
+  if ((error = [self _setupNewDraft]) != nil)
+    return error;
+  
+  /* set subject (do we need to set anything else?) */
+
+  info = [NSDictionary dictionaryWithObjectsAndKeys:
+                        [self forwardSubject:[[self clientObject] subject]],
+                        @"subject",
+                      nil];
+  if ((error = [self->newDraft storeInfo:info]) != nil)
+    return error;
+  
+  /* attach message */
+  
+  // TODO: use subject for filename?
+  error = [self->newDraft saveAttachment:content withName:@"forward.mail"];
+  if (error != nil)
+    return error;
+  
+  // TODO: we might want to pass the original URL to the editor for a final
+  //       redirect back to the message?
+  result = [self redirectToEditNewDraft];
+  [self reset];
+  return result;
+}
+
 @end /* UIxMailEditorAction */
index 11b54a12ff7c944bf74df18a74cf94d7037db89b..900e78bc4b0019cb91601cdfedfa3fe925da7025 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include <SOGoUI/UIxComponent.h>
 
 }
 
 - (BOOL)saveFileData:(NSData *)_data name:(NSString *)_name {
+  NSException *error;
+  
   if (_data == nil)
     return NO;
   if ([_name length] == 0) {
     return NO;
   
   // TODO: add size limit?
-  return [[self clientObject] saveAttachment:_data withName:_name];
+  error = [[self clientObject] saveAttachment:_data withName:_name];
+  if (error != nil) {
+    [self logWithFormat:@"ERROR: could not save: %@", error];
+    return NO;
+  }
+  return YES;
 }
 
 /* actions */
 }
 
 - (id)deleteAttachmentAction {
-  if (![[self clientObject] deleteAttachmentWithName:[self attachmentName]]) {
-    // TODO: improve error handling
-    return [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"failed to delete attachment ..."];
-  }
+  NSException *error;
+
+  error = [[self clientObject] deleteAttachmentWithName:[self attachmentName]];
+  
+  if (error != nil)
+    return error;
   
   return [self redirectToLocation:@"viewAttachments"];
 }
index c197eb29e3daa864fc90f04d37fe9105970e03db..49dded896bf893911f4eefc09a1fe229ca26aa16 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
 
 @end
 
+/*
+  TODO: the subject formatter should deal with the various 're:' like prefixes
+        and translate them into the native languages?
+        (or something like Re(5): ?)
+*/
+
 @interface UIxSubjectFormatter : UIxMailFormatter
 {
   unsigned maxLength;
index 09c7338d63e9e367e8d174801dde13c3d8ee43c0..3e061676d253f4b42aa2c7cf45b72bbec61448a5 100644 (file)
@@ -82,6 +82,20 @@ static int attachmentFlagSize = 8096;
   return NO;
 }
 
+/* title */
+
+- (NSString *)objectTitle {
+  return [[self clientObject] nameInContainer];
+}
+- (NSString *)panelTitle {
+  NSString *s;
+  
+  s = [self labelForKey:@"View Mail Folder"];
+  s = [s stringByAppendingString:@": "];
+  s = [s stringByAppendingString:[self objectTitle]];
+  return s;
+}
+
 /* derived accessors */
 
 - (BOOL)isMessageDeleted {
@@ -379,8 +393,9 @@ static int attachmentFlagSize = 8096;
 /* actions */
 
 - (id)defaultAction {
-  // TODO: remove log
+#if 0
   [self logWithFormat:@"default action ..."];
+#endif
   self->firstMessageNumber = 
     [[[[self context] request] formValueForKey:@"idx"] intValue];
   return self;
index 6b550ec1ca86bdc25806485ddd34e6c6acf7fe52..6e4bbf129a37898c1f7a101105468a8ec060fc5c 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
 >
   <div class="titlediv" style="white-space: nowrap;">
 <!-- TODO: enable once implemented: #1209, #1210
index 01f7b44d89c18e079e7d8a221f2ad2db67992ccf..2a732b69156627a4d3445af3d6895b1d3944be7d 100644 (file)
   ASSIGNCOPY(self->title, _value);
 }
 - (NSString *)title {
-  if ([self isUIxDebugEnabled])
-    return self->title;
+  if ([self->title length] == 0)
+    return @"OpenGroupware.org";
   
-  return [self labelForKey:@"OpenGroupware.org"];
+  return self->title;
 }
 
 - (void)setItem:(id)_item {
index 351c5ddc18c36584af9adfe1476fb800cfc087bf..204419f791c82b19e91ce877250daf000896cb9c 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
@@ -18,7 +18,6 @@
   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.
 */
-// $Id$
 
 #include <SOGoUI/UIxComponent.h>
 
   return self->currentAddress;
 }
 
+- (NSString *)objectTitle {
+  return [[self clientObject] subject];
+}
+- (NSString *)panelTitle {
+  NSString *s;
+  
+  s = [self labelForKey:@"View Mail"];
+  s = [s stringByAppendingString:@": "];
+  s = [s stringByAppendingString:[self objectTitle]];
+  return s;
+}
+
 /* fetching */
 
 - (id)message {
 /* viewers */
 
 - (id)contentViewerComponent {
+  // TODO: I would prefer to flatten the body structure prior rendering,
+  //       using some delegate to decide which parts to select for alternative.
   id info;
   
   info = [[self clientObject] bodyStructure];
index 154bb53c2726643c60ac7f2e4611bcada0545d91..80514f10e3360565284a8598a03aaa161fbb7769 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
   const:hideFolderTree="1"
 >
   <!-- 
@@ -37,8 +37,9 @@
         <var:string value="clientObject.date" 
                     formatter="context.mailDateFormatter"/>
 
-        <!-- TODO: -->
+        <!-- TODO:
         (<a rsrc:href="tbird_073_viewer.png">screenshot</a>)
+         -->
       </td>
     </tr>
 
index 7a25bdedfa829becbb1d026ba683c4ff68c8fa66..1046401d657393650db0339b6811914911ca118d 100644 (file)
   return [self->scriptText isNotNull] ? self->scriptText : @"";
 }
 
+- (NSString *)panelTitle {
+  return [self labelForKey:@"Edit Mail Filter"];
+}
+
 /* requests */
 
 - (BOOL)shouldTakeValuesFromRequest:(WORequest *)_rq inContext:(WOContext*)_c {
index 34b709987490f5dfc8595dc7e6c015b1ba3e58fa..d3d7fcfad04e366dcd0ab601391db86654d4cfd0 100644 (file)
@@ -7,7 +7,7 @@
   xmlns:rsrc="OGo:url"
   xmlns:label="OGo:label"
   className="UIxMailMainFrame"
-  title="name"
+  title="panelTitle"
   const:hideFolderTree="1"
 >
  <div id="compose_panel">
index 1ef2fc1a69fd6534cf200f1841bed2e48e34a1cc..60647c6621dde983ef23fae4762ac0abfb22cf13 100644 (file)
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2004 SKYRIX Software AG
+  Copyright (C) 2004-2005 SKYRIX Software AG
 
   This file is part of OpenGroupware.org.
 
index 209d038c7d4ee55ed066d5cb2a4b868f5dca4c96..cbd97192a091f6cf9adc80e018eef8d6a505d820 100644 (file)
@@ -1,7 +1,8 @@
 # version file
 
-SUBMINOR_VERSION:=79
+SUBMINOR_VERSION:=80
 
+# v0.9.80 requires SoObjects/Mailer v0.9.59
 # v0.9.78 requires SoObjects/Mailer v0.9.58
 # v0.9.77 requires SoObjects/Mailer v0.9.57
 # v0.9.74 requires SoObjects/Mailer v0.9.56
index edefca9ba704485ea2d8408558c707af81118800..c35e679d9ae7feb14d9ca823332de462c5d75151 100644 (file)
             ( // third group
               { link = "delete"; 
                 cssClass = "tbicon_delete"; label = "Delete"; },
-            ),
-/* TODO: enable when implemented
-            ( // third group
-// TODO: enable when delete works (#1212)
-              { link = "#"; 
-                cssClass = "tbicon_delete"; label = "Delete"; },
-// TODO: enable when we know how to mark junk (#971)
+/* TODO: enable when we know how to mark junk (#971)
               { link = "#"; 
                 cssClass = "tbicon_junk";   label = "Junk";   },
-            ),
 */
+            ),
             ( /* fourth group */
 /* TODO: enable when we can print (#1207)
               { link = "#"; cssClass = "tbicon_print"; label = "Print"; },