]> err.no Git - scalable-opengroupware.org/commitdiff
git-svn-id: http://svn.opengroupware.org/SOGo/inverse/trunk@1147 d1b88da0-ebda-0310...
authorwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 17 Aug 2007 02:47:37 +0000 (02:47 +0000)
committerwolfgang <wolfgang@d1b88da0-ebda-0310-925b-ed51d893ca5b>
Fri, 17 Aug 2007 02:47:37 +0000 (02:47 +0000)
76 files changed:
ChangeLog
Main/SOGo.m
NEWS
SoObjects/Appointments/SOGoAppointmentFolder.m
SoObjects/Appointments/SOGoCalendarComponent.m
SoObjects/Appointments/iCalEntityObject+SOGo.m
SoObjects/Mailer/GNUmakefile
SoObjects/Mailer/SOGoDraftObject.h
SoObjects/Mailer/SOGoDraftObject.m
SoObjects/Mailer/SOGoDraftsFolder.h
SoObjects/Mailer/SOGoDraftsFolder.m
SoObjects/Mailer/SOGoMailAccount.h
SoObjects/Mailer/SOGoMailAccount.m
SoObjects/Mailer/SOGoMailAccounts.h
SoObjects/Mailer/SOGoMailAccounts.m
SoObjects/Mailer/SOGoMailBaseObject.h
SoObjects/Mailer/SOGoMailBaseObject.m
SoObjects/Mailer/SOGoMailFolder.h
SoObjects/Mailer/SOGoMailFolder.m
SoObjects/Mailer/SOGoMailIdentity.h [deleted file]
SoObjects/Mailer/SOGoMailIdentity.m [deleted file]
SoObjects/Mailer/SOGoMailObject+Draft.h [new file with mode: 0644]
SoObjects/Mailer/SOGoMailObject+Draft.m [new file with mode: 0644]
SoObjects/Mailer/SOGoMailObject.h
SoObjects/Mailer/SOGoMailObject.m
SoObjects/Mailer/SOGoUser+Mail.h [deleted file]
SoObjects/Mailer/SOGoUser+Mail.m [deleted file]
SoObjects/SOGo/NSArray+Utilities.h
SoObjects/SOGo/NSArray+Utilities.m
SoObjects/SOGo/NSDictionary+Utilities.h
SoObjects/SOGo/NSDictionary+Utilities.m
SoObjects/SOGo/SOGoFolder.m
SoObjects/SOGo/SOGoMailer.h
SoObjects/SOGo/SOGoUser.h
SoObjects/SOGo/SOGoUser.m
UI/Common/GNUmakefile
UI/Common/WODirectAction+SOGo.h [new file with mode: 0644]
UI/Common/WODirectAction+SOGo.m [new file with mode: 0644]
UI/MailPartViewers/UIxMailPartICalViewer.m
UI/MailerUI/English.lproj/Localizable.strings
UI/MailerUI/French.lproj/Localizable.strings
UI/MailerUI/GNUmakefile
UI/MailerUI/Toolbars/SOGoMailObject.toolbar
UI/MailerUI/UIxMailAccountActions.m
UI/MailerUI/UIxMailActions.h [new file with mode: 0644]
UI/MailerUI/UIxMailActions.m [new file with mode: 0644]
UI/MailerUI/UIxMailEditor.m
UI/MailerUI/UIxMailEditorAction.h [deleted file]
UI/MailerUI/UIxMailEditorAction.m [deleted file]
UI/MailerUI/UIxMailFolderActions.h
UI/MailerUI/UIxMailFolderActions.m
UI/MailerUI/UIxMailForwardAction.m [deleted file]
UI/MailerUI/UIxMailListView.m
UI/MailerUI/UIxMailMainFrame.h
UI/MailerUI/UIxMailMainFrame.m
UI/MailerUI/UIxMailReplyAction.m [deleted file]
UI/MailerUI/UIxMailView.m
UI/MailerUI/product.plist
UI/MainUI/GNUmakefile
UI/SOGoUI/SOGoACLAdvisory.m
UI/Scheduler/UIxCalView.m
UI/Scheduler/UIxComponentEditor.m
UI/Templates/ContactsUI/UIxContactsListView.wox
UI/Templates/MailerUI/UIxMailEditor.wox
UI/Templates/MailerUI/UIxMailMainFrame.wox
UI/Templates/MailerUI/UIxMailView.wox
UI/Templates/UIxPageFrame.wox
UI/WebServerResources/ContactsUI.js
UI/WebServerResources/MailerUI.css
UI/WebServerResources/MailerUI.js
UI/WebServerResources/SOGoDragHandles.js
UI/WebServerResources/SchedulerUI.js
UI/WebServerResources/UIxMailEditor.css
UI/WebServerResources/UIxMailEditor.js
UI/WebServerResources/UIxMailPopupView.js
UI/WebServerResources/generic.js

index c9653a6b3144b8d0466973e722a83122cd212682..cf536a10df715d1a56142277e7eef6da9526ed8a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,209 @@
+2007-08-16  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailerUI/UIxMailEditor.m ([-patchFlagsInStore]): removed
+       useless stub method, of which the intention was implemented in
+       SOGoDraftObject.
+       ([-lookupSentFolderUsingAccount]): removed obsolete method.
+       ([-selectedMailIdentity]): removed obsolete method.
+       ([-lookupSentFolderUsingFrom]): removed obsolete method.
+       ([-storeMailInSentFolder:_path]): removed obsolete method, of
+       which the mechanism has been put in -[SOGoDraftObject sendMail]
+       method.
+       ([UIxMailEditor -_saveFormInfo], [UIxMailEditor -defaultAction])
+       ([UIxMailEditor -saveAction], [UIxMailEditor -sendAction]):
+       adapted algorithms to the new SOGoDraftObject methods.
+       ([-deleteAction]): removed method since local draft objects cannot
+       be removed by the user.
+
+       * UI/MailerUI/UIxMailFolderActions.m ([UIxMailFolderActions
+       -expungeAction]): new method replacing the one previously found in
+       UIxMailListView.
+       ([UIxMailFolderActions -createFolderAction])
+       ([UIxMailFolderActions -renameFolderAction])
+       ([UIxMailFolderActions -deleteFolderAction])
+       ([UIxMailFolderActions -emptyTrashAction])
+       ([UIxMailFolderActions -subscribeAction])
+       ([UIxMailFolderActions -unsubscribeAction]): error situations
+       should have http return code 500 instead of 403.
+
+       * UI/MailerUI/UIxMailAccountActions.m ([UIxMailAccountActions
+       -composeAction]): new method replacing the one previously in
+       UIxMailMainFrame.
+
+       * SoObjects/Mailer/SOGoMailObject.m ([SOGoMailObject
+       -imap4URLString]): removed overriden method (see below).
+
+       * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder -aclUsers]):
+       cache the mailbox acl.
+       ([SOGoMailFolder -aclsForUser:uid]): cache the mailbox acl.
+       ([SOGoMailFolder -setRoles:rolesforUser:uid]): reset the mailbox
+       acl cache.
+       ([SOGoMailFolder -httpURLForAdvisoryToUser:uid]): modified to use
+       the new method of determining the users mail accounts.
+
+       * SoObjects/Mailer/SOGoMailBaseObject.m ([-imap4URLString]): no
+       longer adds a "/" at the end of the string (the default for
+       folders), therefore this will be overriden in SOGoMailFolder
+       rather than in SOGoMailObject.
+
+       * UI/MailerUI/UIxMailListView.m ([-expungeAction]): removed
+       method, moved into the new UIxMailActions module class.
+
+       * SoObjects/Mailer/SOGoDraftsFolder.m ([SOGoDraftsFolder
+       -newDraft]): new method that returns a new SOGoDraftObject
+       instance with a unique filename based on the current timestamp and
+       the "newDraft" prefix.
+       ([SOGoDraftsFolder
+       -lookupName:nameinContext:localContextacquire:acquire]): overriden
+       method by detecting local drafts with their "newDraft" prefix.
+       ([SOGoDraftsFolder -isInDraftsFolder]): returns YES.
+
+       * SoObjects/Mailer/SOGoDraftsFolder.[hm]: rewrote class module
+       from scratch by making it a subclass of SOGoMailFolder.
+
+       * UI/MailerUI/UIxMailReplyAction.m: removed obsolete class
+       module.
+
+       * UI/MailerUI/UIxMailForwardAction.m: removed obsolete class
+       module.
+
+       * UI/MailerUI/UIxMailEditorAction.[hm]: removed obsolete class
+       module.
+
+       * SoObjects/Mailer/SOGoDraftObject.m ([SOGoDraftObject -init]):
+       new method, initializing the new ivars: IMAP4ID, headers, text,
+       sourceURL and sourceFlag.
+       ([-spoolFileManager], [SOGoDraftObject -userSpoolFolderPath])
+       ([-_ensureUserSpoolFolderPath])
+       ([-saveMimeMessageToTemporaryFileWithHeaders:])
+       ([SOGoDraftObject -mimeMessageWithHeaders:_headers])
+       ([-deleteTemporaryMessageFile:], [-delete], [-content])
+       ([-GETAction:_ctx], [-DELETEAction:_ctx], [-fetchParts:])
+       ([-uid], [-flags], [-size], [-envelope]): removed methods.
+       ([SOGoDraftObject -storeInfo]): rewrote method to take the new
+       ivars into account.
+       ([SOGoDraftObject -setSourceURL:newSourceURL])
+       ([SOGoDraftObject -setSourceFlag:newSourceFlag]): new accessor
+       methods to store the url of the original message between accesses
+       so that, depending on the action taken (forward or reply), the
+       correct flag can be given.
+       ([SOGoDraftObject -setIMAP4ID:]): new accessor method that sets
+       the imap4 id that is returned for the message whenever it is saved
+       into the drafts folder.
+       ([SOGoDraftObject -save]): new method that takes in charge the
+       saving of the message in the IMAP drafts folder.
+       ([SOGoDraftObject -fetchMailForReplying:sourceMailtoAll:toAll]):
+       new method that fills the original data of the new message with
+       the reply content of the original message.
+       ([SOGoDraftObject -fetchMailForForwarding:sourceMail]): same as
+       above for message forwarding.
+       ([-spoolFileManager]): removed useless method (only returned the
+       default filemanager...)
+       ([SOGoDraftObject -mimeMessageAsData]): new method that returns
+       the resulting message as an NSData chunk.
+
+       * SoObjects/Mailer/SOGoMailObject+Draft.m: new extension module
+       containing a rewrite of the mail action methods found in
+       UI/Mailer/, that needed to be put in the SOGoMailObject class.
+       ([SOGoMailObject -subjectForReply]): new method that returns a
+       subject suitable for replies.
+       ([SOGoMailObject -contentForReply]): new method that returns the
+       textual content of an email, quoted for replying.
+       ([SOGoMailObject -filenameForForward]): new method that returns
+       the name of the filename that should contain the forwarded
+       message, based on its subject.
+       ([SOGoMailObject -subjectForForward]): explicit.
+
+2007-08-15  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * UI/MailerUI/UIxMailMainFrame.m ([UIxMailMainFrame
+       -mailAccounts]): rewrote method to return the name of the mail
+       accounts now available with the -[SOGoUser mailAccounts] method.
+       ([UIxMailMainFrame -showLinkBanner]): removed method.
+       ([UIxMailMainFrame -bannerToolbarStyle]): removed method.
+       ([UIxMailMainFrame -bannerConsumeStyle]): removed method.
+       ([UIxMailMainFrame -rootURL]): removed method.
+       ([UIxMailMainFrame -userRootURL]): removed method.
+       ([UIxMailMainFrame -calendarRootURL]): removed method.
+       ([UIxMailMainFrame -contactsRootURL]): removed method.
+       ([UIxMailMainFrame -hasErrorText])
+       ([UIxMailMainFrame -errorText])
+       ([UIxMailMainFrame -errorAlertJavaScript]): removed methods.
+       ([-composeAction]): removed method. Now provided by
+       UIxMailAccountActions.
+       ([UIxMailMainFrame -setHideFolderTree:_flag]): removed method.
+       ([UIxMailMainFrame -hideFolderTree]): removed method.
+       ([UIxMailMainFrame -treeRootClassName]): removed method.
+       ([UIxMailMainFrame +initialize]): removed method.
+       SOGoMailTreeRootClass userdefaults will no longer have any effect.
+
+       * UI/Common/WODirectAction+SOGo.m ([WODirectAction
+       -redirectToLocation:newLocation]): new method that implements the
+       same functionality as WOComponent.
+
+       * UI/Common/WODirectAction+SOGo.[hm]: new class extension module.
+
+       * UI/MailerUI/UIxMailView.m ([UIxMailView -mailIsDraft]): new
+       method that returns whether the current mail is store in the
+       drafts folder hierarchy.
+
+       * SoObjects/SOGo/SOGoUser.m ([-fullEmail]): removed method.
+       ([-primaryEmail]): removed method.
+       ([SOGoUser -primaryIMAP4AccountString]): removed method.
+       ([SOGoUser -mailAccounts]): new method that returns an array
+       containing description dictionaries for all the user mail
+       accounts. Each account also contain the user's identities for that
+       account.
+       ([SOGoUser -allIdentities]): new utility method that returns all
+       the user identities on all accounts.
+       ([SOGoUser -primaryIdentity]): new method return the first
+       identity of the first account.
+
+       * SoObjects/Mailer/SOGoMailFolder.m ([SOGoMailFolder -httpURLForAdvisoryToUser:uid]): 
+
+       * SoObjects/Mailer/SOGoMailAccount.m ([SOGoMailAccount
+       -isInDraftsFolder]): returns NO.
+       ([-preferredIdentity]): removed method, replaced with -[SOGoUser
+       primaryIdentity].
+       ([SOGoMailAccount -draftsFolderInContext:_ctx]): new method.
+
+       * SoObjects/Mailer/SOGoMailBaseObject.m ([SOGoMailBaseObject
+       -isInDraftsFolder]): new method that detects if self is contained
+       by the drafts folder by calling itself on the container object.
+
+       * SoObjects/Mailer/SOGoMailAccounts.m ([-fetchAllIdentities]):
+       removed method.
+       ([-fetchIdentitiesWithEmitterPermissions]): removed method.
+       ([SOGoMailAccounts -toManyRelationshipKeys]): rewrote method to
+       return the name of the mail accounts now available with the
+       -[SOGoUser mailAccounts] method.
+
+       * SoObjects/Mailer/SOGoUser+Mail.[hm]: removed useless class
+       extension module.
+
+       * SoObjects/Mailer/SOGoMailIdentity.[hm]: removed useless class
+       module.
+
+       * SoObjects/SOGo/NSArray+Utilities.m ([NSArray
+       -keysWithFormat:format]): method that forward the method of the
+       same name to each member of the array, considering they all are
+       instances of NSDictionary.
+       ([NSArray -objectsForKey:key]): same principle as above.
+       ([NSArray -flattenedArray]): new method that transforms an array
+       of arrays into a single array containing all the elements of the
+       subarrays.
+
+       * SoObjects/SOGo/NSDictionary+Utilities.m ([NSDictionary
+       -keysWithFormat:keyFormat]): new method inspired by the python
+       string formatting system and which replaces occurences of "%{key}"
+       by the corresponding keys.
+
+2007-08-13  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
+
+       * Main/SOGo.m ([SOGo -run]): check for channel-type specific
+       sql script before the generic one when initializing mandatory
+       system tables.
+
 2007-08-09  Wolfgang Sourdeau  <wsourdeau@inverse.ca>
 
        * SoObjects/SOGo/NSString+Utilities.m ([NSString
index b4d247f8cd0df4bf33f89ce8bfb56ebe7a4decd6..702297498368dfb5e2b10ec88e7182f2d163e093 100644 (file)
@@ -151,15 +151,18 @@ static BOOL debugObjectAllocation = NO;
                  tableURL: (NSString *) url
                   andType: (NSString *) tableType
 {
-  NSString *tableName, *descFile;
+  NSString *tableName, *descFile, *tableFile, *fileSuffix;
   EOAdaptorChannel *tc;
   NGBundleManager *bm;
   NSBundle *bundle;
   unsigned int length;
+  NSURL *channelURL;
 
   bm = [NGBundleManager defaultBundleManager];
-  
-  tc = [cm acquireOpenChannelForURL: [NSURL URLWithString: url]];
+
+  channelURL = [NSURL URLWithString: url];
+  fileSuffix = [channelURL scheme];
+  tc = [cm acquireOpenChannelForURL: channelURL];
 
   tableName = [url lastPathComponent];
   if ([tc evaluateExpressionX:
@@ -167,8 +170,13 @@ static BOOL debugObjectAllocation = NO;
     {
       bundle = [bm bundleWithName: @"MainUI" type: @"SOGo"];
       length = [tableType length] - 3;
-      descFile = [bundle pathForResource: [tableType substringToIndex: length]
+      tableFile = [tableType substringToIndex: length];
+      descFile
+       = [bundle pathForResource: [NSString stringWithFormat: @"%@-%@",
+                                            tableFile, fileSuffix]
                         ofType: @"sql"];
+      if (!descFile)
+       descFile = [bundle pathForResource: tableFile ofType: @"sql"];
       if (![tc evaluateExpressionX:
                 [NSString stringWithContentsOfFile: descFile]])
        [self logWithFormat: @"table '%@' successfully created!", tableName];
diff --git a/NEWS b/NEWS
index 3c5dfca9f9727a2566aa1c15dafc1226ceb9874a..8aed0efb6704de501dbecb38a10062887ca6c610 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -9,7 +9,8 @@
 - added support for limiting LDAP queries with the SOGoLDAPQueryLimit and
   the SOGoLDAPSizeLimit settings;
 - fixed a bug where folders starting with digits would not be displayed;
-- improved IE7 and Safari support: priority menus, attendees selector;
+- improved IE7 and Safari support: priority menus, attendees selector,
+  search fields, textarea sizes;
 - added the ability to print messages from the mailer toolbar;
 - added the ability to use and configure SMTP as the email transport instead
   of sendmail;
index 7e0c0e6dcd70d04008aed0222d7ae7031b945ea4..5e7955a7e61022b8971b4268008b1b160be3e8ac 100644 (file)
@@ -746,7 +746,8 @@ static NSNumber   *sharedYes = nil;
     privacySqlString = @"and (c_isopaque = 1)";
   else
     {
-      email = [activeUser primaryEmail];
+#warning we do not manage all the user's possible emails
+      email = [[activeUser primaryIdentity] objectForKey: @"email"];
       
       privacySqlString
         = [NSString stringWithFormat:
index 808275d36565508e0246d6298e9a2af208f93464..be754cf6515ff79840dc932e22343522a9c0302b 100644 (file)
@@ -274,14 +274,12 @@ static BOOL sendEMailNotifications = NO;
   iCalPerson *person;
   NSString *newContent;
   NSException *ex;
-  NSString *myEMail;
   
   ex = nil;
 
   component = [self component: NO];
   if (component)
     {
-      myEMail = [[context activeUser] primaryEmail];
       person = [self findParticipantWithUID: owner];
       if (person)
         {
index 5b100f9a54690c38b8b4c0ab5e5dc0281f9f917d..6b67f2da4a451b7485330295778e616ffb8a05a5 100644 (file)
@@ -23,6 +23,7 @@
 #import <Foundation/NSArray.h>
 #import <Foundation/NSEnumerator.h>
 
+#import <SoObjects/SOGo/NSArray+Utilities.h>
 #import <SoObjects/SOGo/SOGoUser.h>
 
 #import "iCalEntityObject+SOGo.h"
 - (BOOL) userIsParticipant: (SOGoUser *) user
 {
   NSEnumerator *emails;
+  NSArray *identities;
   NSString *currentEmail;
   BOOL response;
 
   response = NO;
 
-  emails = [[user allEmails] objectEnumerator];
+  identities = [user allIdentities];
+  emails = [[identities objectsForKey: @"email"] objectEnumerator];
   currentEmail = [emails nextObject];
   while (!response && currentEmail)
     if ([self isParticipant: currentEmail])
 - (BOOL) userIsOrganizer: (SOGoUser *) user
 {
   NSEnumerator *emails;
+  NSArray *identities;
   NSString *currentEmail;
   BOOL response;
 
   response = NO;
 
-  emails = [[user allEmails] objectEnumerator];
+  identities = [user allIdentities];
+  emails = [[identities objectsForKey: @"email"] objectEnumerator];
   currentEmail = [emails nextObject];
   while (!response && currentEmail)
     if ([self isOrganizer: currentEmail])
index fbe4346ed1b4cabc05f00b0859a4126d0f00d77e..a0fc74d4dfabf667547622cd9acf2992aa2bfcc1 100644 (file)
@@ -18,6 +18,7 @@ Mailer_OBJC_FILES += \
        SOGoMailFolder.m                \
        SOGoSharedInboxFolder.m         \
        SOGoMailObject.m                \
+       SOGoMailObject+Draft.m          \
        SOGoTrashFolder.m               \
        \
        SOGoMailBodyPart.m              \
@@ -28,9 +29,6 @@ Mailer_OBJC_FILES += \
        \
        SOGoDraftsFolder.m              \
        SOGoDraftObject.m               \
-       \
-       SOGoMailIdentity.m              \
-       SOGoUser+Mail.m                 \
 
 Mailer_RESOURCE_FILES += \
        Version         \
index 2995f7227d9d88ff916a58babec76c4d34a5cd1f..95b7c5fc406258a386ccecf96de7ff732e4f5021 100644 (file)
@@ -22,7 +22,7 @@
 #ifndef __Mailer_SOGoDraftObject_H__
 #define __Mailer_SOGoDraftObject_H__
 
-#include <Mailer/SOGoMailBaseObject.h>
+#import "SOGoMailBaseObject.h"
 
 /*
   SOGoDraftsFolder
   TODO: store-info should be an own object, not NSDictionary.
 */
 
-@class NSString, NSArray, NSDictionary, NSData, NSException;
-@class NGMimeMessage, NGImap4Envelope;
+@class NSArray;
+@class NSData;
+@class NSDictionary;
+@class NSException;
+@class NGImap4Envelope;
+@class NGMimeMessage;
+@class NSMutableDictionary;
+@class NSString;
+
+@class SOGoMailObject;
 
 @interface SOGoDraftObject : SOGoMailBaseObject
 {
-  NSString        *path;
-  NSDictionary    *info; /* stores the envelope information */
+  NSString *path;
   NGImap4Envelope *envelope;
+  int IMAP4ID;
+  NSMutableDictionary *headers;
+  NSString *text;
+  NSString *sourceURL;
+  NSString *sourceFlag;
 }
 
 /* contents */
+- (void) fetchInfo;
+- (NSException *) storeInfo;
+
+- (void) fetchMailForEditing: (SOGoMailObject *) sourceMail;
+- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
+                       toAll: (BOOL) toAll;
+- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail;
+
+- (void) setHeaders: (NSDictionary *) newHeaders;
+- (NSDictionary *) headers;
+- (void) setText: (NSString *) newText;
+- (NSString *) text;
+
+/* for replies and forwards */
+- (void) setSourceURL: (NSString *) newSurceURL;
+- (void) setSourceFlag: (NSString *) newSourceFlag;
 
-- (NSDictionary *) fetchInfo;
-- (NSException *) storeInfo: (NSDictionary *) _info;
+- (void) setIMAP4ID: (int) newIMAPID;
+- (int) IMAP4ID;
 
 /* attachments */
 
 /* NGMime representations */
 
 - (NGMimeMessage *) mimeMessage;
-
-- (NSString *) saveMimeMessageToTemporaryFile;
-- (NSString *) saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_addh;
-
-- (NSException *) sendMail;
+- (NSData *) mimeMessageAsData;
 
 /* operations */
 
-- (NSException *) delete;
+- (NSException *) sendMail;
+- (NSException *) save;
 
-/* fake being a SOGoMailObject */
+// /* fake being a SOGoMailObject */
 
-- (id) fetchParts: (NSArray *) _parts;
+// - (id) fetchParts: (NSArray *) _parts;
 
 @end
 
index 1d627aba14d2dbd2a8e47438ce6be3f85f3bff51..e42ec5e4ba86947642d7926d917671f9515c9585 100644 (file)
@@ -23,6 +23,7 @@
 #import <Foundation/NSAutoreleasePool.h>
 #import <Foundation/NSDictionary.h>
 #import <Foundation/NSKeyValueCoding.h>
+#import <Foundation/NSURL.h>
 #import <Foundation/NSUserDefaults.h>
 #import <Foundation/NSValue.h>
 
@@ -37,6 +38,9 @@
 #import <NGExtensions/NSNull+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NGQuotedPrintableCoding.h>
+#import <NGExtensions/NSString+misc.h>
+#import <NGImap4/NGImap4Connection.h>
+#import <NGImap4/NGImap4Client.h>
 #import <NGImap4/NGImap4Envelope.h>
 #import <NGImap4/NGImap4EnvelopeAddress.h>
 #import <NGMail/NGMimeMessage.h>
 
 #import <SoObjects/SOGo/NSCalendarDate+SOGo.h>
 #import <SoObjects/SOGo/SOGoMailer.h>
+#import "SOGoMailAccount.h"
+#import "SOGoMailFolder.h"
+#import "SOGoMailObject.h"
+#import "SOGoMailObject+Draft.h"
 
 #import "SOGoDraftObject.h"
 
 static NSString *contentTypeValue = @"text/plain; charset=utf-8";
+static NSString *headerKeys[] = {@"subject", @"to", @"cc", @"bcc", 
+                                @"from", @"replyTo", nil};
 
 @interface NSString (NGMimeHelpers)
 
@@ -88,155 +98,425 @@ static BOOL        draftDeleteDisabled = NO; // for debugging
 static BOOL        debugOn = NO;
 static BOOL        showTextAttachmentsInline  = NO;
 
-+ (void)initialize {
++ (void) initialize
+{
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   
   /* Note: be aware of the charset issues before enabling this! */
-  showTextAttachmentsInline = [ud boolForKey:@"SOGoShowTextAttachmentsInline"];
+  showTextAttachmentsInline = [ud boolForKey: @"SOGoShowTextAttachmentsInline"];
   
-  if ((draftDeleteDisabled = [ud boolForKey:@"SOGoNoDraftDeleteAfterSend"]))
+  if ((draftDeleteDisabled = [ud boolForKey: @"SOGoNoDraftDeleteAfterSend"]))
     NSLog(@"WARNING: draft delete is disabled! (SOGoNoDraftDeleteAfterSend)");
   
-  TextPlainType  = [[NGMimeType mimeType:@"text"      subType:@"plain"]  copy];
-  MultiMixedType = [[NGMimeType mimeType:@"multipart" subType:@"mixed"]  copy];
+  TextPlainType  = [[NGMimeType mimeType: @"text" subType: @"plain"]  copy];
+  MultiMixedType = [[NGMimeType mimeType: @"multipart" subType: @"mixed"]  copy];
 }
 
-- (void)dealloc {
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      IMAP4ID = -1;
+      headers = [NSMutableDictionary new];
+      text = @"";
+      sourceURL = nil;
+      sourceFlag = nil;
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [headers release];
+  [text release];
   [envelope release];
-  [info release];
   [path release];
+  [sourceURL release];
+  [sourceFlag release];
   [super dealloc];
 }
 
 /* draft folder functionality */
 
-- (NSFileManager *)spoolFileManager {
-  return [[self container] spoolFileManager];
-}
-- (NSString *)userSpoolFolderPath {
+- (NSString *) userSpoolFolderPath
+{
   return [[self container] userSpoolFolderPath];
 }
-- (BOOL)_ensureUserSpoolFolderPath {
-  return [[self container] _ensureUserSpoolFolderPath];
-}
 
 /* draft object functionality */
 
-- (NSString *)draftFolderPath {
-  if (path != nil)
-    return path;
-  
-  path = [[[self userSpoolFolderPath] stringByAppendingPathComponent:
-                                             [self nameInContainer]] copy];
+- (NSString *) draftFolderPath
+{
+  if (!path)
+    {
+      path = [[self userSpoolFolderPath] stringByAppendingPathComponent:
+                                          nameInContainer];
+      [path retain];
+    }
+
   return path;
 }
-- (BOOL)_ensureDraftFolderPath {
+
+- (BOOL) _ensureDraftFolderPath
+{
   NSFileManager *fm;
+
+  fm = [NSFileManager defaultManager];
   
-  if (![self _ensureUserSpoolFolderPath])
-    return NO;
-  
-  if ((fm = [self spoolFileManager]) == nil) {
-    [self errorWithFormat:@"missing spool file manager!"];
-    return NO;
-  }
-  return [fm createDirectoriesAtPath:[self draftFolderPath] attributes:nil];
+  return ([fm createDirectoriesAtPath: [container userSpoolFolderPath]
+             attributes: nil]
+         && [fm createDirectoriesAtPath: [self draftFolderPath]
+                attributes:nil]);
 }
 
-- (NSString *)infoPath {
-  return [[self draftFolderPath] 
-               stringByAppendingPathComponent:@".info.plist"];
+- (NSString *) infoPath
+{
+  return [[self draftFolderPath]
+          stringByAppendingPathComponent: @".info.plist"];
 }
 
 /* contents */
 
-- (NSException *)storeInfo:(NSDictionary *)_info {
-  if (_info == nil) {
-    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: '%@'",
+- (void) setHeaders: (NSDictionary *) newHeaders
+{
+  id headerValue;
+  unsigned int count;
+
+  for (count = 0; count < 6; count++)
+    {
+      headerValue = [newHeaders objectForKey: headerKeys[count]];
+      if (headerValue)
+       [headers setObject: headerValue
+                forKey: headerKeys[count]];
+      else
+       [headers removeObjectForKey: headerKeys[count]];
+    }
+}
+
+- (NSDictionary *) headers
+{
+  return headers;
+}
+
+- (void) setText: (NSString *) newText
+{
+  ASSIGN (text, newText);
+}
+
+- (NSString *) text
+{
+  return text;
+}
+
+- (void) setSourceURL: (NSString *) newSourceURL
+{
+  ASSIGN (sourceURL, newSourceURL);
+}
+
+- (void) setSourceFlag: (NSString *) newSourceFlag
+{
+  ASSIGN (sourceFlag, newSourceFlag);
+}
+
+- (NSException *) storeInfo
+{
+  NSMutableDictionary *infos;
+  NSException *error;
+
+  if ([self _ensureDraftFolderPath])
+    {
+      infos = [NSMutableDictionary new];
+      [infos setObject: headers forKey: @"headers"];
+      if (text)
+       [infos setObject: text forKey: @"text"];
+      if (IMAP4ID > -1)
+       [infos setObject: [NSNumber numberWithInt: IMAP4ID]
+              forKey: @"IMAP4ID"];
+      if (sourceURL && sourceFlag)
+       {
+         [infos setObject: sourceURL forKey: @"sourceURL"];
+         [infos setObject: sourceFlag forKey: @"sourceFlag"];
+       }
+
+      if ([infos writeToFile: [self infoPath] atomically:YES])
+       error = nil;
+      else
+       {
+         [self errorWithFormat: @"could not write info: '%@'",
+               [self infoPath]];
+         error = [NSException exceptionWithHTTPStatus:500 /* server error */
+                              reason: @"could not write draft info!"];
+       }
+
+      [infos release];
+    }
+  else
+    {
+      [self errorWithFormat: @"could not create folder for draft: '%@'",
             [self draftFolderPath]];
-    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 [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"could not write draft info!"];
-  }
-  
-  /* reset info cache */
-  [info release]; info = nil;
-  
-  return nil /* everything is excellent */;
+      error = [NSException exceptionWithHTTPStatus:500 /* server error */
+                          reason: @"could not create folder for draft!"];
+    }
+
+  return error;
 }
-- (NSDictionary *)fetchInfo {
+
+- (void) _loadInfosFromDictionary: (NSDictionary *) infoDict
+{
+  id value;
+
+  value = [infoDict objectForKey: @"headers"];
+  if (value)
+    [self setHeaders: value];
+
+  value = [infoDict objectForKey: @"text"];
+  if ([value length] > 0)
+    [self setText: value];
+
+  value = [infoDict objectForKey: @"IMAP4ID"];
+  if (value)
+    [self setIMAP4ID: [value intValue]];
+
+  value = [infoDict objectForKey: @"sourceURL"];
+  if (value)
+    [self setSourceURL: value];
+  value = [infoDict objectForKey: @"sourceFlag"];
+  if (value)
+    [self setSourceFlag: value];
+}
+
+- (NSString *) relativeImap4Name
+{
+  return [NSString stringWithFormat: @"%d", IMAP4ID];
+}
+
+- (void) fetchInfo
+{
   NSString *p;
+  NSDictionary *infos;
+  NSFileManager *fm;
 
-  if (info != nil)
-    return info;
-  
   p = [self infoPath];
-  if (![[self spoolFileManager] fileExistsAtPath:p]) {
-    [self debugWithFormat:@"Note: info object does not yet exist: %@", p];
-    return nil;
-  }
+
+  fm = [NSFileManager defaultManager];
+  if ([fm fileExistsAtPath: p])
+    {
+      infos = [NSDictionary dictionaryWithContentsOfFile: p];
+      if (infos)
+       [self _loadInfosFromDictionary: infos];
+//       else
+//     [self errorWithFormat: @"draft info dictionary broken at path: %@", p];
+    }
+  else
+    [self debugWithFormat: @"Note: info object does not yet exist: %@", p];
+}
+
+- (void) setIMAP4ID: (int) newIMAP4ID
+{
+  IMAP4ID = newIMAP4ID;
+}
+
+- (int) IMAP4ID
+{
+  return IMAP4ID;
+}
+
+- (int) _IMAP4IDFromAppendResult: (NSDictionary *) result
+{
+  NSDictionary *results;
+  NSString *flag, *newIdString;
+
+  results = [[result objectForKey: @"RawResponse"]
+             objectForKey: @"ResponseResult"];
+  flag = [results objectForKey: @"flag"];
+  newIdString = [[flag componentsSeparatedByString: @" "] objectAtIndex: 2];
+
+  return [newIdString intValue];
+}
+
+- (NSException *) save
+{
+  NGImap4Client *client;
+  NSException *error;
+  NSData *message;
+  NSString *folder;
+  id result;
+
+  error = nil;
+  message = [self mimeMessageAsData];
+
+  client = [[self imap4Connection] client];
+  folder = [imap4 imap4FolderNameForURL: [container imap4URL]];
+  result
+    = [client append: message toFolder: folder
+             withFlags: [NSArray arrayWithObjects: @"seen", @"draft", nil]];
+  if ([[result objectForKey: @"result"] boolValue])
+    {
+      if (IMAP4ID > -1)
+       error = [imap4 markURLDeleted: [self imap4URL]];
+      IMAP4ID = [self _IMAP4IDFromAppendResult: result];
+      [self storeInfo];
+    }
+  else
+    error = [NSException exceptionWithHTTPStatus:500 /* Server Error */
+                        reason: @"Failed to store message"];
+
+  return error;
+}
+
+- (void) fetchMailForEditing: (SOGoMailObject *) sourceMail
+{
+#warning unimplemented
+}
+
+- (void) _addEMailsOfAddresses: (NSArray *) _addrs
+                      toArray: (NSMutableArray *) _ma
+{
+  unsigned i, count;
+
+  for (i = 0, count = [_addrs count]; i < count; i++)
+    [_ma addObject:
+          [(NGImap4EnvelopeAddress *) [_addrs objectAtIndex: i] email]];
+}
+
+- (void) _fillInReplyAddresses: (NSMutableDictionary *) _info
+                   replyToAll: (BOOL) _replyToAll
+                     envelope: (NGImap4Envelope *) _envelope
+{
+  /*
+    The rules as implemented by Thunderbird:
+    - if there is a 'reply-to' header, only include that (as TO)
+    - if we reply to all, all non-from addresses are added as CC
+    - the from is always the lone TO (except for reply-to)
+    
+    Note: we cannot check reply-to, because Cyrus even sets a reply-to in the
+          envelope if none is contained in the message itself! (bug or
+          feature?)
+    
+    TODO: what about sender (RFC 822 3.6.2)
+  */
+  NSMutableArray *to;
+  NSArray *addrs;
+  
+  to = [NSMutableArray arrayWithCapacity:2];
+
+  /* first check for "reply-to" */
   
-  info = [[NSDictionary alloc] initWithContentsOfFile:p];
-  if (info == nil)
-    [self errorWithFormat:@"draft info dictionary broken at path: %@", p];
+  addrs = [_envelope replyTo];
+  if ([addrs count] == 0)
+    /* no "reply-to", try "from" */
+    addrs = [_envelope from];
+
+  [self _addEMailsOfAddresses: addrs toArray: to];
+  [_info setObject: to forKey: @"to"];
+
+  /* CC processing if we reply-to-all: add all 'to' and 'cc'  */
   
-  return info;
+  if (_replyToAll)
+    {
+      to = [NSMutableArray arrayWithCapacity:8];
+
+      [self _addEMailsOfAddresses: [_envelope to] toArray: to];
+      [self _addEMailsOfAddresses: [_envelope cc] toArray: to];
+    
+      [_info setObject: to forKey: @"cc"];
+    }
+}
+
+- (void) fetchMailForReplying: (SOGoMailObject *) sourceMail
+                       toAll: (BOOL) toAll
+{
+  NSString *contentForReply;
+  NSMutableDictionary *info;
+
+  [sourceMail fetchCoreInfos];
+
+  info = [NSMutableDictionary dictionaryWithCapacity: 16];
+  [info setObject: [sourceMail subjectForReply] forKey: @"subject"];
+  [self _fillInReplyAddresses: info replyToAll: toAll
+       envelope: [sourceMail envelope]];
+  contentForReply = [sourceMail contentForReply];
+  [self setText: contentForReply];
+  [self setHeaders: info];
+  [self setSourceURL: [sourceMail imap4URLString]];
+  [self setSourceFlag: @"Answered"];
+  [self storeInfo];
+}
+
+- (void) fetchMailForForwarding: (SOGoMailObject *) sourceMail
+{
+  NSDictionary *info, *attachment;
+
+  [sourceMail fetchCoreInfos];
+
+  info = [NSDictionary dictionaryWithObject: [sourceMail subjectForForward]
+                      forKey: @"subject"];
+  [self setHeaders: info];
+  [self setSourceURL: [sourceMail imap4URLString]];
+  [self setSourceFlag: @"$Forwarded"];
+
+  /* attach message */
+  
+  // TODO: use subject for filename?
+//   error = [newDraft saveAttachment:content withName:@"forward.mail"];
+  attachment = [NSDictionary dictionaryWithObjectsAndKeys:
+                              [sourceMail filenameForForward], @"filename",
+                            @"message/rfc822", @"mime-type",
+                            nil];
+  [self saveAttachment: [sourceMail content]
+       withMetadata: attachment];
+  [self storeInfo];
 }
 
 /* accessors */
 
-- (NSString *)sender {
+- (NSString *) sender
+{
   id tmp;
   
-  if ((tmp = [[self fetchInfo] objectForKey:@"from"]) == nil)
+  if ((tmp = [headers objectForKey: @"from"]) == nil)
     return nil;
   if ([tmp isKindOfClass:[NSArray class]])
-    return [tmp count] > 0 ? [tmp objectAtIndex:0] : nil;
+    return [tmp count] > 0 ? [tmp objectAtIndex: 0] : nil;
+
   return tmp;
 }
 
 /* attachments */
 
-- (NSArray *)fetchAttachmentNames {
+- (NSArray *) fetchAttachmentNames
+{
   NSMutableArray *ma;
-  NSFileManager  *fm;
-  NSArray        *files;
-  unsigned i, count;
-  
-  fm = [self spoolFileManager];
-  if ((files = [fm directoryContentsAtPath:[self draftFolderPath]]) == nil)
-    return nil;
-  
-  count = [files count];
-  ma    = [NSMutableArray arrayWithCapacity:count];
-  for (i = 0; i < count; i++) {
-    NSString *filename;
-    
-    filename = [files objectAtIndex:i];
-    if ([filename hasPrefix:@"."])
-      continue;
-    
-    [ma addObject:filename];
-  }
+  NSFileManager *fm;
+  NSArray *files;
+  unsigned count, max;
+  NSString *filename;
+
+  fm = [NSFileManager defaultManager];
+  files = [fm directoryContentsAtPath: [self draftFolderPath]];
+
+  max = [files count];
+  ma = [NSMutableArray arrayWithCapacity: max];
+  for (count = 0; count < max; count++)
+    {
+      filename = [files objectAtIndex: count];
+      if (![filename hasPrefix: @"."])
+       [ma addObject: filename];
+    }
+
   return ma;
 }
 
-- (BOOL)isValidAttachmentName:(NSString *)_name {
+- (BOOL) isValidAttachmentName: (NSString *) _name
+{
   static NSString *sescape[] = { @"/", @"..", @"~", @"\"", @"'", @" ", nil };
   unsigned i;
   NSRange  r;
 
   if (![_name isNotNull])     return NO;
   if ([_name length] == 0)    return NO;
-  if ([_name hasPrefix:@"."]) return NO;
+  if ([_name hasPrefix: @"."]) return NO;
   
   for (i = 0; sescape[i] != nil; i++) {
     r = [_name rangeOfString:sescape[i]];
@@ -245,16 +525,18 @@ static BOOL        showTextAttachmentsInline  = NO;
   return YES;
 }
 
-- (NSString *)pathToAttachmentWithName:(NSString *)_name {
+- (NSString *) pathToAttachmentWithName: (NSString *) _name
+{
   if ([_name length] == 0)
     return nil;
   
   return [[self draftFolderPath] stringByAppendingPathComponent:_name];
 }
 
-- (NSException *)invalidAttachmentNameError:(NSString *)_name {
+- (NSException *) invalidAttachmentNameError: (NSString *) _name
+{
   return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                     reason:@"Invalid attachment name!"];
+                     reason: @"Invalid attachment name!"];
 }
 
 - (NSException *) saveAttachment: (NSData *) _attach
@@ -264,12 +546,12 @@ static BOOL        showTextAttachmentsInline  = NO;
   
   if (![_attach isNotNull]) {
     return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                       reason:@"Missing attachment content!"];
+                       reason: @"Missing attachment content!"];
   }
   
   if (![self _ensureDraftFolderPath]) {
     return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                       reason:@"Could not create folder for draft!"];
+                       reason: @"Could not create folder for draft!"];
   }
   name = [metadata objectForKey: @"filename"];
   if (![self isValidAttachmentName: name])
@@ -279,7 +561,7 @@ static BOOL        showTextAttachmentsInline  = NO;
   if (![_attach writeToFile: p atomically: YES])
     {
       return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                         reason:@"Could not write attachment to draft!"];
+                         reason: @"Could not write attachment to draft!"];
     }
 
   mimeType = [metadata objectForKey: @"mime-type"];
@@ -291,115 +573,121 @@ static BOOL        showTextAttachmentsInline  = NO;
             writeToFile: p atomically: YES])
        {
          return [NSException exceptionWithHTTPStatus:500 /* Server Error */
-                             reason:@"Could not write attachment to draft!"];
+                             reason: @"Could not write attachment to draft!"];
        }
     }
   
   return nil; /* everything OK */
 }
 
-- (NSException *)deleteAttachmentWithName:(NSString *)_name {
+- (NSException *) deleteAttachmentWithName: (NSString *) _name
+{
   NSFileManager *fm;
   NSString *p;
-  
-  if (![self isValidAttachmentName:_name])
-    return [self invalidAttachmentNameError:_name];
-  
-  fm = [self spoolFileManager];
-  p  = [self pathToAttachmentWithName:_name];
-  if (![fm fileExistsAtPath:p])
-    return nil; /* well, doesn't exist, so its deleted ;-) */
-  
-  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 */
+  NSException *error;
+
+  error = nil;
+
+  if ([self isValidAttachmentName:_name]) 
+    {
+      fm = [NSFileManager defaultManager];
+      p = [self pathToAttachmentWithName:_name];
+      if ([fm fileExistsAtPath: p])
+       if (![fm removeFileAtPath: p handler: nil])
+         error
+           = [NSException exceptionWithHTTPStatus: 500 /* Server Error */
+                          reason: @"Could not delete attachment from draft!"];
+    }
+  else
+    error = [self invalidAttachmentNameError:_name];
+
+  return error;  
 }
 
 /* NGMime representations */
 
-- (NGMimeBodyPart *)bodyPartForText
+- (NGMimeBodyPart *) bodyPartForText
 {
   /*
     This add the text typed by the user (the primary plain/text part).
   */
   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];
+  map = [[[NGMutableHashMap alloc] initWithCapacity: 1] 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: contentTypeValue
-          forKey: @"content-type"];
-//       body = [body dataUsingEncoding:NSUTF8StringEncoding];
-    }
-  }
+  [map setObject: @"text/plain" forKey: @"content-type"];
+  if (text)
+    [map setObject: contentTypeValue forKey: @"content-type"];
+
+//   if ((body = text) != nil) {
+//     if ([body isKindOfClass: [NSString class]]) {
+//       [map setObject: contentTypeValue
+//        forKey: @"content-type"];
+// //       body = [body dataUsingEncoding:NSUTF8StringEncoding];
+//     }
+//   }
   
   /* prepare body content */
   
   bodyPart = [[[NGMimeBodyPart alloc] initWithHeader:map] autorelease];
-  [bodyPart setBody:body];
+  [bodyPart setBody: text];
+
   return bodyPart;
 }
 
-- (NGMimeMessage *)mimeMessageForContentWithHeaderMap:(NGMutableHashMap *)map
+- (NGMimeMessage *) mimeMessageForContentWithHeaderMap: (NGMutableHashMap *) map
 {
-  NSDictionary  *lInfo;
   NGMimeMessage *message;  
-  BOOL     addSuffix;
+//   BOOL     addSuffix;
   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: contentTypeValue
-          forKey: @"content-type"];
+  body = text;
+  if (body)
+    {
+//       if ([body isKindOfClass:[NSString class]])
+       /* Note: just 'utf8' is displayed wrong in Mail.app */
+       [map setObject: contentTypeValue
+            forKey: @"content-type"];
 //       body = [body dataUsingEncoding:NSUTF8StringEncoding];
-    else if ([body isKindOfClass:[NSData class]] && addSuffix) {
-      body = [[body mutableCopy] autorelease];
+//       else if ([body isKindOfClass:[NSData class]] && addSuffix) {
+//     body = [[body mutableCopy] autorelease];
+//       }
+//       else if (addSuffix) {
+//     [self warnWithFormat: @"Note: cannot add Internet marker to body: %@",
+//           NSStringFromClass([body class])];
+//       }
+
+       message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
+       [message setBody: body];
     }
-    else if (addSuffix) {
-      [self warnWithFormat:@"Note: cannot add Internet marker to body: %@",
-             NSStringFromClass([body class])];
-    }
-  }
-  
-  message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
-  [message setBody:body];
+  else
+    message = nil;
+
 
   return message;
 }
 
-- (NSString *)mimeTypeForExtension:(NSString *)_ext {
+- (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";
+  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";
 }
 
-- (NSString *)contentTypeForAttachmentWithName:(NSString *)_name {
+- (NSString *) contentTypeForAttachmentWithName: (NSString *) _name
+{
   NSString *s, *p;
   NSData *mimeData;
   
@@ -416,35 +704,37 @@ static BOOL        showTextAttachmentsInline  = NO;
     {
       s = [self mimeTypeForExtension:[_name pathExtension]];
       if ([_name length] > 0)
-       s = [s stringByAppendingFormat:@"; name=\"%@\"", _name];
+       s = [s stringByAppendingFormat: @"; name=\"%@\"", _name];
     }
 
   return s;
 }
 
-- (NSString *)contentDispositionForAttachmentWithName:(NSString *)_name {
+- (NSString *) contentDispositionForAttachmentWithName: (NSString *) _name
+{
   NSString *type;
   NSString *cdtype;
   NSString *cd;
   
   type = [self contentTypeForAttachmentWithName:_name];
   
-  if ([type hasPrefix:@"text/"])
+  if ([type hasPrefix: @"text/"])
     cdtype = showTextAttachmentsInline ? @"inline" : @"attachment";
-  else if ([type hasPrefix:@"image/"] || [type hasPrefix:@"message"])
+  else if ([type hasPrefix: @"image/"] || [type hasPrefix: @"message"])
     cdtype = @"inline";
   else
     cdtype = @"attachment";
   
-  cd = [cdtype stringByAppendingString:@"; filename=\""];
+  cd = [cdtype stringByAppendingString: @"; filename=\""];
   cd = [cd stringByAppendingString:_name];
-  cd = [cd stringByAppendingString:@"\""];
+  cd = [cd stringByAppendingString: @"\""];
   
   // TODO: add size parameter (useful addition, RFC 2183)
   return cd;
 }
 
-- (NGMimeBodyPart *)bodyPartForAttachmentWithName:(NSString *)_name {
+- (NGMimeBodyPart *) bodyPartForAttachmentWithName: (NSString *) _name
+{
   NSFileManager    *fm;
   NGMutableHashMap *map;
   NGMimeBodyPart   *bodyPart;
@@ -458,10 +748,10 @@ static BOOL        showTextAttachmentsInline  = NO;
 
   /* check attachment */
   
-  fm = [self spoolFileManager];
+  fm = [NSFileManager defaultManager];
   p  = [self pathToAttachmentWithName:_name];
   if (![fm isReadableFileAtPath:p]) {
-    [self errorWithFormat:@"did not find attachment: '%@'", _name];
+    [self errorWithFormat: @"did not find attachment: '%@'", _name];
     return nil;
   }
   attachAsString = NO;
@@ -472,14 +762,14 @@ static BOOL        showTextAttachmentsInline  = NO;
   map = [[[NGMutableHashMap alloc] initWithCapacity:4] autorelease];
 
   if ((s = [self contentTypeForAttachmentWithName:_name]) != nil) {
-    [map setObject:s forKey:@"content-type"];
-    if ([s hasPrefix:@"text/"])
+    [map setObject:s forKey: @"content-type"];
+    if ([s hasPrefix: @"text/"])
       attachAsString = YES;
-    else if ([s hasPrefix:@"message/rfc822"])
+    else if ([s hasPrefix: @"message/rfc822"])
       is7bit = YES;
   }
   if ((s = [self contentDispositionForAttachmentWithName:_name]))
-    [map setObject:s forKey:@"content-disposition"];
+    [map setObject:s forKey: @"content-disposition"];
   
   /* prepare body content */
   
@@ -507,9 +797,9 @@ static BOOL        showTextAttachmentsInline  = NO;
              generator!
     */
     body = [[NGMimeFileData alloc] initWithPath:p removeFile:NO];
-    [map setObject:@"7bit" forKey:@"content-transfer-encoding"];
+    [map setObject: @"7bit" forKey: @"content-transfer-encoding"];
     [map setObject:[NSNumber numberWithInt:[body length]] 
-        forKey:@"content-length"];
+        forKey: @"content-length"];
   }
   else {
     /* 
@@ -522,9 +812,9 @@ static BOOL        showTextAttachmentsInline  = NO;
     encoded = [content dataByEncodingBase64];
     [content release]; content = nil;
     
-    [map setObject:@"base64" forKey:@"content-transfer-encoding"];
+    [map setObject: @"base64" forKey: @"content-transfer-encoding"];
     [map setObject:[NSNumber numberWithInt:[encoded length]] 
-        forKey:@"content-length"];
+        forKey: @"content-length"];
     
     /* Note: the -init method will create a temporary file! */
     body = [[NGMimeFileData alloc] initWithBytes:[encoded bytes]
@@ -538,55 +828,61 @@ static BOOL        showTextAttachmentsInline  = NO;
   return bodyPart;
 }
 
-- (NSArray *)bodyPartsForAllAttachments {
+- (NSArray *) bodyPartsForAllAttachments
+{
   /* returns nil on error */
-  NSMutableArray *bodyParts;
   NSArray  *names;
   unsigned i, count;
-  
+  NGMimeBodyPart *bodyPart;
+  NSMutableArray *bodyParts;
+
   names = [self fetchAttachmentNames];
-  if ((count = [names count]) == 0)
-    return [NSArray array];
-  
-  bodyParts = [NSMutableArray arrayWithCapacity:count];
-  for (i = 0; i < count; i++) {
-    NGMimeBodyPart *bodyPart;
-    
-    bodyPart = [self bodyPartForAttachmentWithName:[names objectAtIndex:i]];
-    if (bodyPart == nil)
-      return nil;
-    
-    [bodyParts addObject:bodyPart];
-  }
+  count = [names count];
+  bodyParts = [NSMutableArray arrayWithCapacity: count];
+
+  for (i = 0; i < count; i++)
+    {
+      bodyPart = [self bodyPartForAttachmentWithName: [names objectAtIndex: i]];
+      [bodyParts addObject: bodyPart];
+    }
+
   return bodyParts;
 }
 
-- (NGMimeMessage *)mimeMultiPartMessageWithHeaderMap:(NGMutableHashMap *)map
-  andBodyParts:(NSArray *)_bodyParts
+- (NGMimeMessage *) mimeMultiPartMessageWithHeaderMap: (NGMutableHashMap *) map
+                                        andBodyParts: (NSArray *) _bodyParts
 {
   NGMimeMessage       *message;  
   NGMimeMultipartBody *mBody;
   NGMimeBodyPart      *part;
   NSEnumerator        *e;
   
-  [map addObject:MultiMixedType forKey:@"content-type"];
-    
-  message = [[[NGMimeMessage alloc] initWithHeader:map] autorelease];
-  mBody   = [[NGMimeMultipartBody alloc] initWithPart:message];
-  
+  [map addObject: MultiMixedType forKey: @"content-type"];
+
+  message = [[NGMimeMessage alloc] initWithHeader: map];
+  [message autorelease];
+  mBody = [[NGMimeMultipartBody alloc] initWithPart: message];
+
   part = [self bodyPartForText];
-  [mBody addBodyPart:part];
-  
+  [mBody addBodyPart: part];
+
   e = [_bodyParts objectEnumerator];
-  while ((part = [e nextObject]) != nil)
-    [mBody addBodyPart:part];
-  
-  [message setBody:mBody];
-  [mBody release]; mBody = nil;
+  part = [e nextObject];
+  while (part)
+    {
+      [mBody addBodyPart: part];
+      part = [e nextObject];
+    }
+
+  [message setBody: mBody];
+  [mBody release];
+
   return message;
 }
 
-- (void)_addHeaders:(NSDictionary *)_h toHeaderMap:(NGMutableHashMap *)_map {
+- (void) _addHeaders: (NSDictionary *) _h
+         toHeaderMap: (NGMutableHashMap *) _map
+{
   NSEnumerator *names;
   NSString *name;
 
@@ -602,398 +898,225 @@ static BOOL        showTextAttachmentsInline  = NO;
   }
 }
 
-- (BOOL)isEmptyValue:(id)_value {
+- (BOOL) isEmptyValue: (id) _value
+{
   if (![_value isNotNull])
     return YES;
   
-  if ([_value isKindOfClass:[NSArray class]])
+  if ([_value isKindOfClass: [NSArray class]])
     return [_value count] == 0 ? YES : NO;
   
-  if ([_value isKindOfClass:[NSString class]])
+  if ([_value isKindOfClass: [NSString class]])
     return [_value length] == 0 ? YES : NO;
-  
+
   return NO;
 }
 
-- (NGMutableHashMap *)mimeHeaderMapWithHeaders:(NSDictionary *)_headers {
+- (NGMutableHashMap *) mimeHeaderMapWithHeaders: (NSDictionary *) _headers
+{
   NGMutableHashMap *map;
-  NSDictionary *lInfo; // TODO: this should be some kind of object?
   NSArray      *emails;
   NSString     *s, *dateString;
   id           from, replyTo;
   
-  if ((lInfo = [self fetchInfo]) == nil)
-    return nil;
-  
   map = [[[NGMutableHashMap alloc] initWithCapacity:16] autorelease];
   
   /* add recipients */
   
-  if ((emails = [lInfo objectForKey:@"to"]) != nil) {
+  if ((emails = [headers objectForKey: @"to"]) != nil) {
     if ([emails count] == 0) {
-      [self errorWithFormat:@"missing 'to' recipient in email!"];
+      [self errorWithFormat: @"missing 'to' recipient in email!"];
       return nil;
     }
-    [map setObjects:emails forKey:@"to"];
+    [map setObjects: emails forKey: @"to"];
   }
-  if ((emails = [lInfo objectForKey:@"cc"]) != nil)
-    [map setObjects:emails forKey:@"cc"];
-  if ((emails = [lInfo objectForKey:@"bcc"]) != nil)
-    [map setObjects:emails forKey:@"bcc"];
+  if ((emails = [headers objectForKey: @"cc"]) != nil)
+    [map setObjects:emails forKey: @"cc"];
+  if ((emails = [headers objectForKey: @"bcc"]) != nil)
+    [map setObjects:emails forKey: @"bcc"];
   
   /* add senders */
   
-  from    = [lInfo objectForKey:@"from"];
-  replyTo = [lInfo objectForKey:@"replyTo"];
+  from    = [headers objectForKey: @"from"];
+  replyTo = [headers objectForKey: @"replyTo"];
   
   if (![self isEmptyValue:from]) {
     if ([from isKindOfClass:[NSArray class]])
-      [map setObjects:from forKey:@"from"];
+      [map setObjects: from forKey: @"from"];
     else
-      [map setObject:from forKey:@"from"];
+      [map setObject: from forKey: @"from"];
   }
   
-  if (![self isEmptyValue:replyTo]) {
+  if (![self isEmptyValue: replyTo]) {
     if ([from isKindOfClass:[NSArray class]])
-      [map setObjects:from forKey:@"reply-to"];
+      [map setObjects:from forKey: @"reply-to"];
     else
-      [map setObject:from forKey:@"reply-to"];
+      [map setObject:from forKey: @"reply-to"];
   }
   else if (![self isEmptyValue:from])
-    [map setObjects:[map objectsForKey:@"from"] forKey:@"reply-to"];
+    [map setObjects:[map objectsForKey: @"from"] forKey: @"reply-to"];
   
   /* add subject */
   
-  if ([(s = [lInfo objectForKey:@"subject"]) length] > 0)
+  if ([(s = [headers objectForKey: @"subject"]) length] > 0)
     [map setObject: [s asQPSubjectString: @"utf-8"]
-        forKey:@"subject"];
-//     [map setObject: [s asQPSubjectString: @"utf-8"] forKey:@"subject"];
+        forKey: @"subject"];
+//     [map setObject: [s asQPSubjectString: @"utf-8"] forKey: @"subject"];
   
   /* add standard headers */
 
   dateString = [[NSCalendarDate date] rfc822DateString];
-  [map addObject: dateString forKey:@"date"];
-  [map addObject: @"1.0"                forKey:@"MIME-Version"];
-  [map addObject: userAgent             forKey:@"X-Mailer"];
+  [map addObject: dateString forKey: @"date"];
+  [map addObject: @"1.0" forKey: @"MIME-Version"];
+  [map addObject: userAgent forKey: @"X-Mailer"];
 
   /* add custom headers */
   
-  [self _addHeaders:[lInfo objectForKey:@"headers"] toHeaderMap:map];
-  [self _addHeaders:_headers                        toHeaderMap:map];
+//   [self _addHeaders: [lInfo objectForKey: @"headers"] toHeaderMap:map];
+  [self _addHeaders: _headers toHeaderMap: map];
   
   return map;
 }
 
-- (NGMimeMessage *)mimeMessageWithHeaders:(NSDictionary *)_headers {
-  NSAutoreleasePool *pool;
+- (NGMimeMessage *) mimeMessageWithHeaders: (NSDictionary *) _headers
+{
   NGMutableHashMap  *map;
   NSArray           *bodyParts;
   NGMimeMessage     *message;
-  
-  pool = [[NSAutoreleasePool alloc] init];
-  
-  if ([self fetchInfo] == nil) {
-    [self errorWithFormat:@"could not locate draft fetch info!"];
-    return nil;
-  }
-  
-  if ((map = [self mimeHeaderMapWithHeaders:_headers]) == nil)
-    return nil;
-  [self debugWithFormat:@"MIME Envelope: %@", map];
-  
-  if ((bodyParts = [self bodyPartsForAllAttachments]) == nil) {
-    [self errorWithFormat:
-            @"could not create body parts for attachments!"];
-    return nil; // TODO: improve error handling, return exception
-  }
-  [self debugWithFormat:@"attachments: %@", bodyParts];
-  
-  if ([bodyParts count] == 0) {
-    /* no attachments */
-    message = [self mimeMessageForContentWithHeaderMap:map];
-  }
-  else {
-    /* attachments, create multipart/mixed */
-    message = [self mimeMultiPartMessageWithHeaderMap:map 
-                   andBodyParts:bodyParts];
-  }
-  [self debugWithFormat:@"message: %@", message];
 
-  message = [message retain];
-  [pool release];
-  return [message autorelease];
-}
-- (NGMimeMessage *)mimeMessage {
-  return [self mimeMessageWithHeaders:nil];
-}
-
-- (NSString *)saveMimeMessageToTemporaryFileWithHeaders:(NSDictionary *)_h {
-  NGMimeMessageGenerator *gen;
-  NSAutoreleasePool *pool;
-  NGMimeMessage *message;
-  NSString      *tmpPath;
+  message = nil;
 
-  pool = [[NSAutoreleasePool alloc] init];
-  
-  message = [self mimeMessageWithHeaders:_h];
-  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];
-  [gen release]; gen = nil;
-  
-  [pool release];
-  return [tmpPath autorelease];
-}
-- (NSString *)saveMimeMessageToTemporaryFile {
-  return [self saveMimeMessageToTemporaryFileWithHeaders:nil];
-}
-
-- (void)deleteTemporaryMessageFile:(NSString *)_path {
-  NSFileManager *fm;
+  map = [self mimeHeaderMapWithHeaders: _headers];
+  if (map)
+    {
+      [self debugWithFormat: @"MIME Envelope: %@", map];
   
-  if (![_path isNotNull])
-    return;
+      bodyParts = [self bodyPartsForAllAttachments];
+      if (bodyParts)
+       {
+         [self debugWithFormat: @"attachments: %@", bodyParts];
+  
+         if ([bodyParts count] == 0)
+           /* no attachments */
+           message = [self mimeMessageForContentWithHeaderMap: map];
+         else
+           /* attachments, create multipart/mixed */
+           message = [self mimeMultiPartMessageWithHeaderMap: map 
+                           andBodyParts: bodyParts];
+         [self debugWithFormat: @"message: %@", message];
+       }
+      else
+       [self errorWithFormat:
+               @"could not create body parts for attachments!"];
+    }
 
-  fm = [NSFileManager defaultManager];
-  if (![fm fileExistsAtPath:_path])
-    return;
-  
-  [fm removeFileAtPath:_path handler:nil];
+  return message;
 }
 
-- (NSArray *)allRecipients {
-  NSDictionary   *lInfo;
-  NSMutableArray *ma;
-  NSArray        *tmp;
-  
-  if ((lInfo = [self fetchInfo]) == nil)
-    return nil;
-  
-  ma = [NSMutableArray arrayWithCapacity:16];
-  if ((tmp = [lInfo objectForKey:@"to"]) != nil)
-    [ma addObjectsFromArray:tmp];
-  if ((tmp = [lInfo objectForKey:@"cc"]) != nil)
-    [ma addObjectsFromArray:tmp];
-  if ((tmp = [lInfo objectForKey:@"bcc"]) != nil)
-    [ma addObjectsFromArray:tmp];
-  return ma;
+- (NGMimeMessage *) mimeMessage
+{
+  return [self mimeMessageWithHeaders: nil];
 }
 
-- (NSException *) sendMail
+- (NSData *) mimeMessageAsData
 {
-  NSException *error;
-  NSString    *tmpPath;
-  
-  /* save MIME mail to file */
-  
-  tmpPath = [self saveMimeMessageToTemporaryFile];
-  if (![tmpPath isNotNull]) {
-    return [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"could not save MIME message for draft!"];
-  }
-  
-  /* send mail */
-  error = [[SOGoMailer sharedMailer] sendMailAtPath: tmpPath
-                                    toRecipients: [self allRecipients]
-                                    sender: [self sender]];
+  NGMimeMessageGenerator *generator;
+  NSData *message;
 
-  /* delete temporary file */
-  [self deleteTemporaryMessageFile:tmpPath];
+  generator = [NGMimeMessageGenerator new];
+  message = [generator generateMimeFromPart: [self mimeMessage]];
+  [generator release];
 
-  return error;
+  return message;
 }
 
-/* operations */
+- (NSArray *) allRecipients
+{
+  NSMutableArray *allRecipients;
+  NSArray *recipients;
+  NSString *fieldNames[] = {@"to", @"cc", @"bcc"};
+  unsigned int count;
 
-- (NSException *)delete {
-  NSFileManager *fm;
-  NSString      *p, *sp;
-  NSEnumerator  *e;
-  
-  if ((fm = [self spoolFileManager]) == nil) {
-    [self errorWithFormat:@"missing spool file manager!"];
-    return [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"missing spool file manager!"];
-  }
-  
-  p = [self draftFolderPath];
-  if (![fm fileExistsAtPath:p]) {
-    return [NSException exceptionWithHTTPStatus:404 /* not found */
-                       reason:@"did not find draft!"];
-  }
-  
-  e = [[fm directoryContentsAtPath:p] objectEnumerator];
-  while ((sp = [e nextObject])) {
-    sp = [p stringByAppendingPathComponent:sp];
-    if (draftDeleteDisabled) {
-      [self logWithFormat:@"should delete draft file %@ ...", sp];
-      continue;
-    }
-    
-    if (![fm removeFileAtPath:sp handler:nil]) {
-      return [NSException exceptionWithHTTPStatus:500 /* server error */
-                         reason:@"failed to delete draft!"];
-    }
-  }
+  allRecipients = [NSMutableArray arrayWithCapacity: 16];
 
-  if (draftDeleteDisabled) {
-    [self logWithFormat:@"should delete draft directory: %@", p];
-  }
-  else {
-    if (![fm removeFileAtPath:p handler:nil]) {
-      return [NSException exceptionWithHTTPStatus:500 /* server error */
-                         reason:@"failed to delete draft directory!"];
+  for (count = 0; count < 3; count++)
+    {
+      recipients = [headers objectForKey: fieldNames[count]];
+      if ([recipients count] > 0)
+       [allRecipients addObjectsFromArray: recipients];
     }
-  }
-  return nil;
-}
 
-- (NSData *)content {
-  /* Note: does not cache, expensive operation */
-  NSData   *data;
-  NSString *p;
-  
-  if ((p = [self saveMimeMessageToTemporaryFile]) == nil)
-    return nil;
-  
-  data = [NSData dataWithContentsOfMappedFile:p];
-  
-  /* delete temporary file */
-  [self deleteTemporaryMessageFile:p];
-
-  return data;
-}
-- (NSString *)contentAsString {
-  NSString *str;
-  NSData   *data;
-  
-  if ((data = [self content]) == nil)
-    return nil;
-  
-  str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
-  if (str == nil) {
-    [self errorWithFormat:@"could not load draft as ASCII (data size=%d)",
-         [data length]];
-    return nil;
-  }
-
-  return [str autorelease];
+  return allRecipients;
 }
 
-/* actions */
-
-- (id)DELETEAction:(id)_ctx {
+- (NSException *) sendMail
+{
   NSException *error;
-
-  if ((error = [self delete]) != nil)
-    return error;
+  SOGoMailFolder *sentFolder;
+  NSData *message;
+  NSURL *sourceIMAP4URL;
   
-  return [NSNumber numberWithBool:YES]; /* delete worked out ... */
-}
-
-- (id) GETAction: (id) _ctx
-{
-  /* 
-     Override, because SOGoObject's GETAction uses the less efficient
-     -contentAsString method.
-  */
-  WORequest *rq;
-
-  rq = [_ctx request];
-  if ([rq isSoWebDAVRequest]) {
-    WOResponse *r;
-    NSData     *content;
-    
-    if ((content = [self content]) == nil) {
-      return [NSException exceptionWithHTTPStatus:500
-                         reason:@"Could not generate MIME content!"];
+  /* send mail */
+  sentFolder = [[self mailAccountFolder] sentFolderInContext: context];
+  if ([sentFolder isKindOfClass: [NSException class]])
+    error = (NSException *) sentFolder;
+  else
+    {
+      message = [self mimeMessageAsData];
+      error = [[SOGoMailer sharedMailer] sendMailData: message
+                                        toRecipients: [self allRecipients]
+                                        sender: [self sender]];
+      if (!error)
+       {
+         error = [sentFolder postData: message flags: @"seen"];
+         if (!error)
+           {
+             [self imap4Connection];
+             if (IMAP4ID > -1)
+               [imap4 markURLDeleted: [self imap4URL]];
+             if (sourceURL && sourceFlag)
+               {
+                 sourceIMAP4URL = [NSURL URLWithString: sourceURL];
+                 [imap4 addFlags: sourceFlag toURL: sourceIMAP4URL];
+               }
+           }
+       }
     }
-    r = [_ctx response];
-    [r setHeader:@"message/rfc822" forKey:@"content-type"];
-    [r setContent:content];
-    return r;
-  }
-  
-  return [super GETAction:_ctx];
-}
 
-/* fake being a SOGoMailObject */
-
-- (id)fetchParts:(NSArray *)_parts {
-  return [NSDictionary dictionaryWithObject:self forKey:@"fetch"];
+  return error;
 }
 
-- (NSString *)uid {
-  return [self nameInContainer];
-}
-- (NSArray *)flags {
-  static NSArray *seenFlags = nil;
-  seenFlags = [[NSArray alloc] initWithObjects:@"seen", nil];
-  return seenFlags;
-}
-- (unsigned)size {
-  // TODO: size, hard to support, we would need to generate MIME?
-  return 0;
-}
+/* operations */
 
-- (NSArray *)imap4EnvelopeAddressesForStrings:(NSArray *)_emails {
-  NSMutableArray *ma;
-  unsigned i, count;
-  
-  if (_emails == nil)
-    return nil;
-  if ((count = [_emails count]) == 0)
-    return [NSArray array];
-
-  ma = [NSMutableArray arrayWithCapacity:count];
-  for (i = 0; i < count; i++) {
-    NGImap4EnvelopeAddress *envaddr;
-
-    envaddr = [[NGImap4EnvelopeAddress alloc] 
-               initWithString:[_emails objectAtIndex:i]];
-    if ([envaddr isNotNull])
-      [ma addObject:envaddr];
-    [envaddr release];
-  }
-  return ma;
-}
+- (NSString *) contentAsString
+{
+  NSString *str;
+  NSData *message;
 
-- (NGImap4Envelope *)envelope {
-  NSDictionary *lInfo;
-  id from, replyTo;
-  
-  if (envelope != nil)
-    return envelope;
-  if ((lInfo = [self fetchInfo]) == nil)
-    return nil;
-  
-  if ((from = [self sender]) != nil)
-    from = [NSArray arrayWithObjects:&from count:1];
+  message = [self mimeMessageAsData];
+  if (message)
+    {
+      str = [[NSString alloc] initWithData: message
+                             encoding: NSUTF8StringEncoding];
+      if (!str)
+       [self errorWithFormat: @"could not load draft as UTF-8 (data size=%d)",
+             [message length]];
+      else
+       [str autorelease];
+    }
+  else
+    {
+      [self errorWithFormat: @"message data is empty"];
+      str = nil;
+    }
 
-  if ((replyTo = [lInfo objectForKey:@"replyTo"]) != nil) {
-    if (![replyTo isKindOfClass:[NSArray class]])
-      replyTo = [NSArray arrayWithObjects:&replyTo count:1];
-  }
-  
-  envelope = 
-    [[NGImap4Envelope alloc] initWithMessageID:[self nameInContainer]
-                            subject:[lInfo objectForKey:@"subject"]
-                            from:from replyTo:replyTo
-                            to:[lInfo objectForKey:@"to"]
-                            cc:[lInfo objectForKey:@"cc"]
-                            bcc:[lInfo objectForKey:@"bcc"]];
-  return envelope;
+  return str;
 }
 
 /* debugging */
 
-- (BOOL)isDebuggingEnabled {
+- (BOOL) isDebuggingEnabled
+{
   return debugOn;
 }
 
index 982f28ef72b14d0f35cf6d71f3f7e7e1f95224ff..ea97a6f61257971a77de6c9e71b27fc021689d55 100644 (file)
@@ -1,49 +1,32 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoDraftsFolder_H__
-#define __Mailer_SOGoDraftsFolder_H__
-
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
-
-/*
-  SOGoDraftsFolder
-    Parent object: SOGoMailAccount
-    Child objects: SOGoDraftObject's
-  
-  The SOGoDraftsFolder 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".
-*/
-
-@interface SOGoDraftsFolder : SOGoMailBaseObject
-{
-}
-
-/* new objects */
-
-- (NSString *)makeNewObjectNameInContext:(id)_ctx;
-- (NSString *)newObjectBaseURLInContext:(id)_ctx;
-- (id)newObjectInContext:(id)_ctx;
+/* SOGoDraftsFolder.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.
+ */
+
+#import "SOGoMailFolder.h"
+
+@class SOGoDraftObject;
+
+@interface SOGoDraftsFolder : SOGoMailFolder
+
+- (SOGoDraftObject *) newDraft;
+- (BOOL) isInDraftsFolder;
 
 @end
-
-#endif /* __Mailer_SOGoDraftsFolder_H__ */
index 2509ff059ea725697e16cd540a5c853322ec2521..8cd7b2afbef8ea96c147fcb3dbf761f2c8a796d9 100644 (file)
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
 Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
 License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <unistd.h>
-
-#import <Foundation/NSArray.h>
+/* SOGoDraftsFolder.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/NSDate.h>
+#import <Foundation/NSString.h>
 #import <Foundation/NSUserDefaults.h>
 
-#import <NGObjWeb/NSException+HTTP.h>
-#import <NGExtensions/NSFileManager+Extensions.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSObject+Logs.h>
+#import <NGObjWeb/WOContext+SoObjects.h>
+#import <SoObjects/SOGo/SOGoUser.h>
 
-#import <SoObjects/SOGo/SOGoUserFolder.h>
 #import "SOGoDraftObject.h"
 
 #import "SOGoDraftsFolder.h"
 
-@implementation SOGoDraftsFolder
-
 static NSString *spoolFolder = nil;
 
+static NSTimeInterval lastNew = 0;
+static unsigned int newCount;
+
+@implementation SOGoDraftsFolder
+
 + (void) initialize
 {
-  NSUserDefaults *ud;;
+  NSUserDefaults *ud;
 
   if (!spoolFolder)
     {
       ud = [NSUserDefaults standardUserDefaults];
-      spoolFolder = [[ud stringForKey:@"SOGoMailSpoolPath"] copy];
-      if ([spoolFolder length] < 3)
+      spoolFolder = [ud stringForKey:@"SOGoMailSpoolPath"];
+      if (![spoolFolder length])
        spoolFolder = @"/tmp/";
+      [spoolFolder retain];
 
       NSLog(@"Note: using SOGo mail spool folder: %@", spoolFolder);
     }
 }
 
-/* new objects */
-
-- (NSString *) makeNewObjectNameInContext: (id) _ctx
-{
-  static int counter = 1; // THREAD
-
-  return [NSString stringWithFormat:@"draft_%08d_%08x", getpid(), counter++];
-}
-
-- (NSString *) newObjectBaseURLInContext: (id) _ctx
-{
-  NSString *s, *n;
-  
-  n = [self makeNewObjectNameInContext:_ctx];
-  if (![n isNotNull]) return nil;
-  
-  s = [self baseURLInContext:_ctx];
-  if (![s isNotNull]) return nil;
-  if (![s hasSuffix:@"/"]) s = [s stringByAppendingString:@"/"];
-  return [s stringByAppendingString:n];
-}
-
-- (id) newObjectInContext: (id) _ctx
-{
-  return [self lookupName:[self makeNewObjectNameInContext:_ctx]
-              inContext:_ctx acquire:NO];
-}
-
-/* draft folder functionality */
-
-- (NSFileManager *) spoolFileManager
-{
-  return [NSFileManager defaultManager];
-}
-
-- (NSString *) spoolFolderPath
+- (NSString *) generateNameForNewDraft
 {
-  return spoolFolder;
-}
+  NSString *newName, *login;
+  unsigned int currentTime;
 
-- (NSString *) userSpoolFolderPath
-{
-  NSString *p, *n;
-  
-  p = [self spoolFolderPath];
-  n = [[self lookupUserFolder] nameInContainer];
+  currentTime = [[NSDate date] timeIntervalSince1970];
+  if (currentTime == lastNew)
+    newCount++;
+  else
+    {
+      lastNew = currentTime;
+      newCount = 1;
+    }
 
-  return [p stringByAppendingPathComponent:n];
-}
+  login = [[context activeUser] login];
+  newName = [NSString stringWithFormat: @"newDraft%u-%u",
+                     currentTime, newCount];
 
-- (BOOL) _ensureUserSpoolFolderPath
-{
-  NSFileManager *fm;
-  
-  if ((fm = [self spoolFileManager]) == nil) {
-    [self errorWithFormat:@"missing spool file manager!"];
-    return NO;
-  }
-  return [fm createDirectoriesAtPath:[self userSpoolFolderPath]
-            attributes:nil];
+  return newName;
 }
 
-- (NSArray *) fetchMailNames
+- (SOGoDraftObject *) newDraft
 {
-  NSString *p;
-  
-  if ((p = [self userSpoolFolderPath]) == nil)
-    return nil;
-  
-  return [[self spoolFileManager] directoryContentsAtPath:p];
+  return [SOGoDraftObject objectWithName: [self generateNameForNewDraft]
+                         inContainer: self];
 }
 
-/* folder methods (used by template) */
-
-- (NSArray *) fetchUIDsMatchingQualifier: (id) _q
-                           sortOrdering: (id) _so
+- (id) lookupName: (NSString *) name
+       inContext: (WOContext *) localContext
+         acquire: (BOOL) acquire
 {
-  // TODO: retrieve contained objects
-  NSArray *allUids;
-  
-  allUids = [self fetchMailNames];
-  if (![allUids isNotNull]) {
-    [self logWithFormat:@"Note: no uids in drafts folder: %@",
-           [self userSpoolFolderPath]];
-    return [NSArray array];
-  }
-  
-  // TODO: should sort uids (q=%@,so=%@): %@", _q, _so, allUids];
-  return allUids;
-}
-- (NSArray *)fetchUIDs: (NSArray *)_uids parts: (NSArray *)_parts {
-  /* FLAGS, ENVELOPE, RFC822.SIZE */
-  NSMutableArray  *drafts;
-  unsigned i, count;
-  
-  if (_uids == nil)
-    return nil;
-  if ((count = [_uids count]) == 0)
-    return [NSArray array];
-  
-  drafts = [NSMutableArray arrayWithCapacity:count];
-  for (i = 0; i < count; i++) {
-    SOGoDraftObject *draft;
-    id parts;
-    
-    draft = [self lookupName:[_uids objectAtIndex:i] inContext:nil acquire:NO];
-    if (![draft isNotNull] || [draft isKindOfClass:[NSException class]])
-      continue;
-    
-    parts = [draft fetchParts:_parts];
-    if ([parts isNotNull])
-      [drafts addObject:parts];
-  }
-  
-  return drafts;
-}
+  id object;
 
-/* name lookup */
+  if ([name hasPrefix: @"newDraft"])
+    object = [SOGoDraftObject objectWithName: name inContainer: self];
+  else
+    object = [super lookupName: name
+                   inContext: localContext
+                   acquire: acquire];
 
-- (id) lookupDraftMessage: (NSString *) _key
-               inContext: (id) _ctx
-{
-  // TODO: we might want to check for existence prior controller creation
-  return [[[SOGoDraftObject alloc] initWithName:_key 
-                                  inContainer:self] autorelease];
+  return object;
 }
 
-- (id) lookupName: (NSString *) _key
-       inContext: (id) _ctx
-         acquire: (BOOL) _flag
-{
-  id obj;
-  
-  /* first check attributes directly bound to the application */
-  if ((obj = [super lookupName:_key inContext:_ctx acquire:NO]) != nil)
-    return obj;
-  
-  if ((obj = [self lookupDraftMessage:_key inContext:_ctx]) != nil)
-    return obj;
-  
-  /* return 404 to stop acquisition */
-  return [NSException exceptionWithHTTPStatus:404 /* Not Found */];
-}
-
-/* WebDAV */
-
-- (BOOL) davIsCollection
+- (BOOL) isInDraftsFolder
 {
   return YES;
 }
 
-- (NSArray *) toOneRelationshipKeys
+- (NSString *) userSpoolFolderPath
 {
-  return [self fetchMailNames];
-}
+  NSString *login;
 
-/* folder type */
+  login = [[context activeUser] login];
 
-- (NSString *) outlookFolderClass
-{
-  return @"IPF.Drafts";
+  return [NSString stringWithFormat: @"%@/%@",
+                  spoolFolder, login];
 }
 
-@end /* SOGoDraftsFolder */
+@end
index d9bd07513cad13e3eebd6bca34a7c1e357ab2da5..dff405376a427f958593061328bde394dca823fe 100644 (file)
@@ -22,7 +22,7 @@
 #ifndef __Mailer_SOGoMailAccount_H__
 #define __Mailer_SOGoMailAccount_H__
 
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
+#import <SoObjects/Mailer/SOGoMailBaseObject.h>
 
 /*
   SOGoMailAccount
   password, etc)
 */
 
-@class NSString, NSArray;
-@class SOGoMailFolder, SOGoMailIdentity;
+@class NSArray;
+@class NSString;
+@class SOGoDraftsFolder;
+@class SOGoMailFolder;
 
 @interface SOGoMailAccount : SOGoMailBaseObject
 {
   SOGoMailFolder *inboxFolder;
+  SOGoDraftsFolder *draftsFolder;
   SOGoMailFolder *sentFolder;
   SOGoMailFolder *trashFolder;
 }
 
 /* folder pathes */
 
-- (NSArray *)allFolderPaths;
-- (NSArray *)additionalRootFolderNames; /* stuff like filters and drafts */
+- (NSArray *) allFolderPaths;
+- (NSArray *) additionalRootFolderNames; /* stuff like filters and drafts */
+- (BOOL) isInDraftsFolder;
 
 /* shared accounts */
 
-- (BOOL)isSharedAccount;
-- (NSString *)sharedAccountName;
-
-/* identity */
-
-- (SOGoMailIdentity *)preferredIdentity;
+- (BOOL) isSharedAccount;
+- (NSString *) sharedAccountName;
 
 /* special folders */
 
-- (NSString *)inboxFolderNameInContext:(id)_ctx;
-- (NSString *)draftsFolderNameInContext:(id)_ctx;
-- (NSString *)sieveFolderNameInContext:(id)_ctx;
-- (NSString *)sentFolderNameInContext:(id)_ctx;
-- (NSString *)trashFolderNameInContext:(id)_ctx;
+- (NSString *) inboxFolderNameInContext: (id)_ctx;
+- (NSString *) draftsFolderNameInContext: (id)_ctx;
+- (NSString *) sieveFolderNameInContext: (id)_ctx;
+- (NSString *) sentFolderNameInContext: (id)_ctx;
+- (NSString *) trashFolderNameInContext: (id)_ctx;
 
-- (SOGoMailFolder *)inboxFolderInContext:(id)_ctx;
-- (SOGoMailFolder *)sentFolderInContext:(id)_ctx;
-- (SOGoMailFolder *)trashFolderInContext:(id)_ctx;
+- (SOGoMailFolder *) inboxFolderInContext: (id)_ctx;
+- (SOGoDraftsFolder *) draftsFolderInContext: (id)_ctx;
+- (SOGoMailFolder *) sentFolderInContext: (id)_ctx;
+- (SOGoMailFolder *) trashFolderInContext: (id)_ctx;
 
 /* user defaults */
 - (NSString *) sharedFolderName;
index 93927275bf710296377670c6c43500f14a58af31..bf2a7a5878b2f4668b83c0315bbea1a34e4e74ba 100644 (file)
 #import "SOGoMailFolder.h"
 #import "SOGoMailManager.h"
 #import "SOGoDraftsFolder.h"
-#import "SOGoUser+Mail.h"
 
 #import "SOGoMailAccount.h"
 
 @implementation SOGoMailAccount
 
-static NSArray  *rootFolderNames      = nil;
-static NSString *inboxFolderName      = @"INBOX";
-static NSString *draftsFolderName     = @"Drafts";
-static NSString *sieveFolderName      = @"Filters";
+static NSArray *rootFolderNames = nil;
+static NSString *inboxFolderName = @"INBOX";
+static NSString *draftsFolderName = @"Drafts";
+static NSString *sieveFolderName = @"Filters";
 static NSString *sentFolderName = nil;
 static NSString *trashFolderName = nil;
-static NSString *sharedFolderName     = @""; // TODO: add English default
+static NSString *sharedFolderName = @""; // TODO: add English default
 static NSString *otherUsersFolderName = @""; // TODO: add English default
-static BOOL     useAltNamespace       = NO;
+static BOOL useAltNamespace = NO;
 
-+ (void)initialize {
++ (void) initialize
+{
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   NSString *cfgDraftsFolderName;
 
@@ -82,22 +82,43 @@ static BOOL     useAltNamespace       = NO;
 
   NSLog(@"Note: using shared-folders name:      '%@'", sharedFolderName);
   NSLog(@"Note: using other-users-folders name: '%@'", otherUsersFolderName);
-  if ([ud boolForKey:@"SOGoEnableSieveFolder"]) {
+  if ([ud boolForKey: @"SOGoEnableSieveFolder"])
     rootFolderNames = [[NSArray alloc] initWithObjects:
                                        draftsFolderName, 
                                        sieveFolderName, 
                                      nil];
-  }
-  else {
+  else
     rootFolderNames = [[NSArray alloc] initWithObjects:
                                        draftsFolderName, 
                                      nil];
-  }
+}
+
+- (id) init
+{
+  if ((self = [super init]))
+    {
+      inboxFolder = nil;
+      draftsFolder = nil;
+      sentFolder = nil;
+      trashFolder = nil;
+    }
+
+  return self;
+}
+
+- (void) dealloc
+{
+  [inboxFolder release];
+  [draftsFolder release];
+  [sentFolder release];
+  [trashFolder release];
+  [super dealloc];  
 }
 
 /* shared accounts */
 
-- (BOOL)isSharedAccount {
+- (BOOL) isSharedAccount
+{
   NSString *s;
   NSRange  r;
   
@@ -110,23 +131,29 @@ static BOOL     useAltNamespace       = NO;
   return [s rangeOfString:@".-."].length > 0 ? YES : NO;
 }
 
-- (NSString *)sharedAccountName {
+- (NSString *) sharedAccountName
+{
   return nil;
 }
 
 /* listing the available folders */
 
-- (NSArray *)additionalRootFolderNames {
+- (NSArray *) additionalRootFolderNames
+{
   return rootFolderNames;
 }
 
+- (BOOL) isInDraftsFolder
+{
+  return NO;
+}
+
 - (NSArray *) toManyRelationshipKeys
 {
   NSMutableArray *folders;
   NSArray *imapFolders, *additionalFolders;
 
-  folders = [NSMutableArray new];
-  [folders autorelease];
+  folders = [NSMutableArray array];
 
   imapFolders = [[self imap4Connection] subfoldersForURL: [self imap4URL]];
   additionalFolders = [self additionalRootFolderNames];
@@ -141,16 +168,10 @@ static BOOL     useAltNamespace       = NO;
   return folders;
 }
 
-/* identity */
-
-- (SOGoMailIdentity *)preferredIdentity {
-  return [[context activeUser] primaryMailIdentityForAccount:
-                                [self nameInContainer]];
-}
-
 /* hierarchy */
 
-- (SOGoMailAccount *)mailAccountFolder {
+- (SOGoMailAccount *) mailAccountFolder
+{
   return self;
 }
 
@@ -175,11 +196,13 @@ static BOOL     useAltNamespace       = NO;
 
 /* IMAP4 */
 
-- (BOOL)useSSL {
+- (BOOL) useSSL
+{
   return NO;
 }
 
-- (NSString *)imap4LoginFromHTTP {
+- (NSString *) imap4LoginFromHTTP
+{
   WORequest *rq;
   NSString  *s;
   NSArray   *creds;
@@ -231,21 +254,29 @@ static BOOL     useAltNamespace       = NO;
 
 /* name lookup */
 
-- (id)lookupFolder:(NSString *)_key ofClassNamed:(NSString *)_cn
-  inContext:(id)_cx
+- (id) lookupFolder: (NSString *) _key
+       ofClassNamed: (NSString *) _cn
+         inContext: (id) _cx
 {
   Class clazz;
+  SOGoMailFolder *folder;
 
-  if ((clazz = NSClassFromString(_cn)) == Nil) {
-    [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'", 
+  if ((clazz = NSClassFromString(_cn)) == Nil)
+    {
+      [self logWithFormat:@"ERROR: did not find class '%@' for key: '%@'", 
            _cn, _key];
-    return [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"did not find mail folder class!"];
-  }
-  return [[[clazz alloc] initWithName:_key inContainer:self] autorelease];
+      return [NSException exceptionWithHTTPStatus:500 /* server error */
+                         reason:@"did not find mail folder class!"];
+    }
+
+  folder = [clazz objectWithName: _key inContainer: self];
+
+  return folder;
 }
 
-- (id)lookupImap4Folder:(NSString *)_key inContext:(id)_cx {
+- (id) lookupImap4Folder: (NSString *) _key
+              inContext: (id) _cx
+{
   NSString *s;
 
   s = [_key isEqualToString: [self trashFolderNameInContext:_cx]]
@@ -254,11 +285,15 @@ static BOOL     useAltNamespace       = NO;
   return [self lookupFolder:_key ofClassNamed:s inContext:_cx];
 }
 
-- (id)lookupDraftsFolder:(NSString *)_key inContext:(id)_ctx {
-  return [self lookupFolder:_key ofClassNamed:@"SOGoDraftsFolder" 
-              inContext:_ctx];
+- (id) lookupDraftsFolder: (NSString *) _key
+               inContext: (id) _ctx
+{
+  return [self lookupFolder: _key ofClassNamed: @"SOGoDraftsFolder" 
+              inContext: _ctx];
 }
-- (id)lookupFiltersFolder:(NSString *)_key inContext:(id)_ctx {
+
+- (id) lookupFiltersFolder: (NSString *) _key inContext: (id) _ctx
+{
   return [self lookupFolder:_key ofClassNamed:@"SOGoSieveScriptsFolder" 
               inContext:_ctx];
 }
@@ -332,6 +367,29 @@ static BOOL     useAltNamespace       = NO;
   return inboxFolder;
 }
 
+- (SOGoDraftsFolder *) draftsFolderInContext: (id) _ctx
+{
+  SOGoMailFolder *lookupFolder;
+  // TODO: use some profile to determine real location, use a -traverse lookup
+
+  if (!draftsFolder)
+    {
+      lookupFolder = (useAltNamespace
+                     ? (id) self
+                     : [self inboxFolderInContext:_ctx]);
+      if (![lookupFolder isKindOfClass: [NSException class]])
+       draftsFolder
+         = [lookupFolder lookupName: [self draftsFolderNameInContext:_ctx]
+                         inContext: _ctx acquire: NO];
+      if (![draftsFolder isNotNull])
+       draftsFolder = [NSException exceptionWithHTTPStatus: 404 /* not found */
+                                   reason: @"did not find Drafts folder!"];
+      [draftsFolder retain];
+    }
+
+  return draftsFolder;
+}
+
 - (SOGoMailFolder *) sentFolderInContext: (id) _ctx
 {
   SOGoMailFolder *lookupFolder;
index 3bccf908797ca6fe24a21f42c32a391cb612f5be..96475f7ac6f285b3a7206a855e4a23ec32f332cb 100644 (file)
@@ -22,7 +22,7 @@
 #ifndef __Mailer_SOGoMailAccounts_H__
 #define __Mailer_SOGoMailAccounts_H__
 
-#include <SOGo/SOGoObject.h>
+#import <SOGo/SOGoObject.h>
 
 /*
   SOGoMailAccounts
 @class NSArray;
 
 @interface SOGoMailAccounts : SOGoObject
-{
-}
-
-- (NSArray *)fetchAllIdentities;
-- (NSArray *)fetchIdentitiesWithEmitterPermissions;
 
 @end
 
index 71d4f0b8ceb2114a0df705803d8834e6076dfada..09a8cfd6412fdc98ed148ab0a36ee4f85fd3398e 100644 (file)
@@ -26,7 +26,8 @@
 #import <NGExtensions/NSNull+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 
-#import "SOGoUser+Mail.h"
+#import "../SOGo/NSArray+Utilities.h"
+#import "../SOGo/SOGoUser.h"
 
 #import "SOGoMailAccounts.h"
 
@@ -38,72 +39,28 @@ static NSString *AgenorShareLoginMarker  = @".-.";
 
 - (BOOL) isInHomeFolderBranchOfLoggedInAccount: (NSString *) userLogin
 {
-  return [[[self container] nameInContainer] isEqualToString: userLogin];
+  return [[container nameInContainer] isEqualToString: userLogin];
 }
 
-- (NSArray *)toManyRelationshipKeys {
-  SOGoUser *user;
-  id        account;
-//   NSArray   *shares;
-  NSString *userLogin;
-  
-  /*
-    Note: this is not strictly correct. The accounts being retrieved should be
-          the accounts based on the container object of this folder. Given
-         sufficient rights (eg delegation rights!), this would allow you to
-         browse the hierarchies of other users.
-         
-         But then, the home-folder would need to know about mail
-          functionality which isn't perfect either.
-         => TODO
-  */
-  user = [context activeUser];
-  userLogin = [user login];
-  
-  /* for now: return nothing if the home-folder does not belong to the login */
-  if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
-    [self warnWithFormat:@"User %@ tried to access mail hierarchy of %@",
-         [user login], [[self container] nameInContainer]];
-    return nil;
-  }
-  
-  account = [user primaryIMAP4AccountString];
-  if ([account isNotNull]) account = [NSArray arrayWithObject:account];
-
-  return account;
-//   shares  = [user valueForKey:@"additionalIMAP4AccountStrings"]
-//   return ([shares count] == 0)
-//     ? account
-//     : [account arrayByAddingObjectsFromArray:shares];
-}
-
-- (NSArray *) fetchIdentitiesWithOnlyEmitterAccess: (BOOL) _flag
+- (NSArray *) toManyRelationshipKeys
 {
-  NSString *accountString;
+  NSArray *accounts;
 
-  accountString = [[context activeUser] primaryIMAP4AccountString];
-
-  return [NSArray arrayWithObject: accountString];
-}
+  accounts = [[context activeUser] mailAccounts];
 
-- (NSArray *)fetchAllIdentities {
-  return [self fetchIdentitiesWithOnlyEmitterAccess:NO];
-}
-
-- (NSArray *)fetchIdentitiesWithEmitterPermissions {
-  return [self fetchIdentitiesWithOnlyEmitterAccess:YES];
+  return [accounts objectsForKey: @"name"];
 }
 
 /* name lookup */
 
-- (BOOL)isValidMailAccountName:(NSString *)_key {
-  if ([_key length] == 0)
-    return NO;
-  
-  return YES;
+- (BOOL) isValidMailAccountName: (NSString *) _key
+{
+  return ([_key length] > 0);
 }
 
-- (id)mailAccountWithName:(NSString *)_key inContext:(id)_ctx {
+- (id) mailAccountWithName: (NSString *) _key
+                inContext: (id) _ctx
+{
   static Class ctClass = Nil;
   id ct;
   
@@ -118,22 +75,22 @@ static NSString *AgenorShareLoginMarker  = @".-.";
   return [ct autorelease];
 }
 
-- (id)sharedMailAccountWithName:(NSString *)_key inContext:(id)_ctx {
+- (id) sharedMailAccountWithName: (NSString *) _key
+                      inContext: (id) _ctx
+{
   static Class ctClass = Nil;
-  id ct;
-  
+
+  if (ctClass == Nil)
+    ctClass = NSClassFromString (@"SOGoSharedMailAccount");
   if (ctClass == Nil)
-    ctClass = NSClassFromString(@"SOGoSharedMailAccount");
-  if (ctClass == Nil) {
     [self errorWithFormat:@"missing SOGoSharedMailAccount class!"];
-    return nil;
-  }
   
-  ct = [[ctClass alloc] initWithName:_key inContainer:self];
-  return [ct autorelease];
+  return [ctClass objectWithName: _key inContainer: self];
 }
 
-- (id)lookupName:(NSString *)_key inContext:(id)_ctx acquire:(BOOL)_flag
+- (id) lookupName: (NSString *) _key
+       inContext: (id) _ctx
+         acquire: (BOOL) _flag
 {
   id obj;
   NSString *userLogin;
@@ -145,8 +102,8 @@ static NSString *AgenorShareLoginMarker  = @".-.";
     return obj;
   
   if (![self isInHomeFolderBranchOfLoggedInAccount: userLogin]) {
-    [self warnWithFormat:@"User %@ tried to access mail hierarchy of %@",
-         userLogin, [[self container] nameInContainer]];
+    [self warnWithFormat:@ "User %@ tried to access mail hierarchy of %@",
+         userLogin, [container nameInContainer]];
     
     return [NSException exceptionWithHTTPStatus:403 /* Forbidden */
                        reason:@"Tried to access the mail of another user"];
index d2231786884b9199c0bb385b941d7e1048c5c111..ec2d8baaac27e2f85cffb5e7d3b289fb9297b473 100644 (file)
   NGImap4Connection *imap4;
 }
 
-- (id)initWithImap4URL:(NSURL *)_url inContainer:(id)_container;
+- (id) initWithImap4URL: (NSURL *) _url
+           inContainer: (id) _container;
 
 /* hierarchy */
 
-- (SOGoMailAccount *)mailAccountFolder;
-- (SOGoMailAccounts *)mailAccountsFolder;
+- (SOGoMailAccount *) mailAccountFolder;
+- (SOGoMailAccounts *) mailAccountsFolder;
+- (BOOL) isInDraftsFolder;
 
 /* IMAP4 */
 
-- (NGImap4Connection *)imap4Connection;
-- (NGImap4ConnectionManager *)mailManager;
+- (NGImap4Connection *) imap4Connection;
+- (NGImap4ConnectionManager *) mailManager;
 
 - (NSString *) relativeImap4Name;
 - (NSMutableString *) imap4URLString;
 
-- (NSURL *)imap4URL;
-- (NSString *)imap4Login;
-- (NSString *)imap4Password;
+- (NSURL *) imap4URL;
+- (NSString *) imap4Login;
+- (NSString *) imap4Password;
 
-- (void)flushMailCaches;
+- (void) flushMailCaches;
 
 /* IMAP4 names */
 
-- (BOOL)isBodyPartKey:(NSString *)_key inContext:(id)_ctx;
+- (BOOL) isBodyPartKey: (NSString *) _key inContext: (id) _ctx;
 
 @end
 
index 91d3c8d881edc7de1829e76dbb6dcf54af4cce10..11b74259bf96140aa5eca45711590c1f8dffb7fc 100644 (file)
@@ -95,6 +95,11 @@ static BOOL debugOn = YES;
   return o;
 }
 
+- (BOOL) isInDraftsFolder
+{
+  return [container isInDraftsFolder];
+}
+
 /* IMAP4 */
 
 - (NGImap4ConnectionManager *) mailManager
@@ -146,7 +151,7 @@ static BOOL debugOn = YES;
 
   urlString = [container imap4URLString];
   imap4Name = [[self relativeImap4Name] stringByEscapingURL];
-  [urlString appendFormat: @"%@/", imap4Name];
+  [urlString appendFormat: @"%@", imap4Name];
 
   return urlString;
 }
index 491734c730878c63f2c43aac2e4bfeb408a86101..89d8c68d6653f75d132e11d82ec79f3719d084c3 100644 (file)
@@ -40,6 +40,7 @@
 {
   NSMutableArray *filenames;
   NSString *folderType;
+  NSDictionary *mailboxACL;
 }
 
 /* messages */
index 3232947fe3ab037b9ccc7c5186f803d3fd1332de..7087a17113f9fec456b38084576a9978a96efb4b 100644 (file)
@@ -85,6 +85,7 @@ static BOOL useAltNamespace = NO;
                     inContainer: newContainer]))
     {
       [self _adjustOwner];
+      mailboxACL = nil;
     }
 
   return self;
@@ -94,6 +95,7 @@ static BOOL useAltNamespace = NO;
 {
   [filenames  release];
   [folderType release];
+  [mailboxACL release];
   [super dealloc];
 }
 
@@ -104,6 +106,17 @@ static BOOL useAltNamespace = NO;
   return [nameInContainer substringFromIndex: 6];
 }
 
+
+- (NSMutableString *) imap4URLString
+{
+  NSMutableString *urlString;
+
+  urlString = [super imap4URLString];
+  [urlString appendString: @"/"];
+
+  return urlString;
+}
+
 /* listing the available folders */
 
 - (NSArray *) toManyRelationshipKeys
@@ -179,22 +192,22 @@ static BOOL useAltNamespace = NO;
                            sortOrdering: (id) _so
 {
   /* seems to return an NSArray of NSNumber's */
-  return [[self imap4Connection] fetchUIDsInURL:[self imap4URL]
-                                qualifier:_q sortOrdering:_so];
+  return [[self imap4Connection] fetchUIDsInURL: [self imap4URL]
+                                qualifier: _q sortOrdering: _so];
 }
 
 - (NSArray *) fetchUIDs: (NSArray *) _uids
                  parts: (NSArray *) _parts
 {
-  return [[self imap4Connection] fetchUIDs:_uids inURL:[self imap4URL]
-                                parts:_parts];
+  return [[self imap4Connection] fetchUIDs: _uids inURL: [self imap4URL]
+                                parts: _parts];
 }
 
 - (NSException *) postData: (NSData *) _data
                     flags: (id) _flags
 {
-  return [[self imap4Connection] postData:_data flags:_flags
-                                toFolderURL:[self imap4URL]];
+  return [[self imap4Connection] postData: _data flags: _flags
+                                toFolderURL: [self imap4URL]];
 }
 
 - (NSException *) expunge
@@ -460,14 +473,22 @@ static BOOL useAltNamespace = NO;
   return imapAcls;
 }
 
+- (void) _readMailboxACL
+{
+  mailboxACL
+    = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
+  [mailboxACL retain];
+}
+
 - (NSArray *) aclUsers
 {
   NSArray *users;
-  NSDictionary *imapAcls;
 
-  imapAcls = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
-  if ([imapAcls isKindOfClass: [NSDictionary class]])
-    users = [imapAcls allKeys];
+  if (!mailboxACL)
+    [self _readMailboxACL];
+
+  if ([mailboxACL isKindOfClass: [NSDictionary class]])
+    users = [mailboxACL allKeys];
   else
     users = nil;
 
@@ -506,17 +527,19 @@ static BOOL useAltNamespace = NO;
 
 - (NSArray *) aclsForUser: (NSString *) uid
 {
-  NSDictionary *imapAcls;
   NSMutableArray *acls;
   NSString *userAcls;
 
   acls = [self _sharesACLs];
-  imapAcls = [[self imap4Connection] aclForMailboxAtURL: [self imap4URL]];
-  if ([imapAcls isKindOfClass: [NSDictionary class]])
+
+  if (!mailboxACL)
+    [self _readMailboxACL];
+
+  if ([mailboxACL isKindOfClass: [NSDictionary class]])
     {
-      userAcls = [imapAcls objectForKey: uid];
+      userAcls = [mailboxACL objectForKey: uid];
       if (!([userAcls length] || [uid isEqualToString: defaultUserID]))
-       userAcls = [imapAcls objectForKey: defaultUserID];
+       userAcls = [mailboxACL objectForKey: defaultUserID];
       if ([userAcls length])
        [acls addObjectsFromArray: [self _imapAclsToSOGoAcls: userAcls]];
     }
@@ -541,6 +564,8 @@ static BOOL useAltNamespace = NO;
       [client deleteACL: folderName uid: currentUID];
       currentUID = [uids nextObject];
     }
+  [mailboxACL release];
+  mailboxACL = nil;
 }
 
 - (void) setRoles: (NSArray *) roles
@@ -551,6 +576,9 @@ static BOOL useAltNamespace = NO;
   acls = [self _sogoAclsToImapAcls: roles];
   folderName = [[self imap4Connection] imap4FolderNameForURL: [self imap4URL]];
   [[imap4 client] setACL: folderName rights: acls uid: uid];
+
+  [mailboxACL release];
+  mailboxACL = nil;
 }
 
 - (NSString *) defaultUserID
@@ -592,14 +620,20 @@ static BOOL useAltNamespace = NO;
 {
   SOGoUser *user;
   NSString *otherUsersPath, *url;
+  SOGoMailAccount *thisAccount;
+  NSDictionary *mailAccount;
 
   user = [SOGoUser userWithLogin: uid roles: nil];
   otherUsersPath = [self otherUsersPathToFolder];
   if (otherUsersPath)
-    url = [NSString stringWithFormat: @"%@/%@%@",
-                   [self soURLToBaseContainerForUser: uid],
-                   [user primaryIMAP4AccountString],
-                   otherUsersPath];
+    {
+      thisAccount = [self mailAccountFolder];
+      mailAccount = [[user mailAccounts] objectAtIndex: 0];
+      url = [NSString stringWithFormat: @"%@/%@%@",
+                     [self soURLToBaseContainerForUser: uid],
+                     [mailAccount objectForKey: @"name"],
+                     otherUsersPath];
+    }
   else
     url = nil;
 
diff --git a/SoObjects/Mailer/SOGoMailIdentity.h b/SoObjects/Mailer/SOGoMailIdentity.h
deleted file mode 100644 (file)
index c340fde..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
-  Copyright (C) 2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoMailIdentity_H__
-#define __Mailer_SOGoMailIdentity_H__
-
-#import <Foundation/NSObject.h>
-
-/*
-  SOGoMailIdentity
-  
-  A user identity bound to an account.
-  
-  Note: currently this is not a SoObject. This might change later on.
-  
-  
-  In Thunderbird you have a set of accounts which in turn have a set of
-  identities. There is one default identity.
-  
-  The identities then have:
-  - settings
-    - a name
-    - a from-email
-    - a reply-to
-    - an organization
-    - a signature
-    - a vcard (to be attached)
-  - folder settings
-    - Sent-Folder and bcc
-    - Drafts + Templates
-  - composition
-    - whether to use HTML
-    - whether to quote the source message (reply below, above the quote or
-      select the quote)
-*/
-
-@class NSString;
-
-@interface SOGoMailIdentity : NSObject
-{
-  NSString *name;
-  NSString *email;
-  NSString *replyTo;
-  NSString *organization;
-  NSString *signature;
-  NSString *vCard;
-  NSString *sentFolderName;
-  NSString *sentBCC;
-  NSString *draftsFolderName;
-  NSString *templatesFolderName;
-  struct {
-    int composeHTML:1;
-    int reserved:31;
-  } idFlags;
-}
-
-/* accessors */
-
-- (void)setName:(NSString *)_value;
-- (NSString *)name;
-
-- (void)setEmail:(NSString *)_value;
-- (NSString *)email;
-
-- (void)setReplyTo:(NSString *)_value;
-- (NSString *)replyTo;
-
-- (void)setOrganization:(NSString *)_value;
-- (NSString *)organization;
-
-- (void)setSignature:(NSString *)_value;
-- (NSString *)signature;
-- (BOOL)hasSignature;
-
-- (void)setVCard:(NSString *)_value;
-- (NSString *)vCard;
-- (BOOL)hasVCard;
-
-- (void)setSentFolderName:(NSString *)_value;
-- (NSString *)sentFolderName;
-
-- (void)setSentBCC:(NSString *)_value;
-- (NSString *)sentBCC;
-
-- (void)setDraftsFolderName:(NSString *)_value;
-- (NSString *)draftsFolderName;
-
-- (void)setTemplatesFolderName:(NSString *)_value;
-- (NSString *)templatesFolderName;
-
-@end
-
-#endif /* __Mailer_SOGoMailIdentity_H__ */
diff --git a/SoObjects/Mailer/SOGoMailIdentity.m b/SoObjects/Mailer/SOGoMailIdentity.m
deleted file mode 100644 (file)
index 424d089..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
-  Copyright (C) 2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <Foundation/NSString.h>
-
-#import <NGExtensions/NSNull+misc.h>
-
-#import "SOGoMailIdentity.h"
-
-@implementation SOGoMailIdentity
-
-- (void)dealloc {
-  [name                release];
-  [email               release];
-  [replyTo             release];
-  [organization        release];
-  [signature           release];
-  [vCard               release];
-  [sentFolderName      release];
-  [sentBCC             release];
-  [draftsFolderName    release];
-  [templatesFolderName release];
-  [super dealloc];
-}
-
-/* accessors */
-
-- (void)setName:(NSString *)_value {
-  ASSIGNCOPY(name, _value);
-}
-- (NSString *)name {
-  return name;
-}
-
-- (void)setEmail:(NSString *)_value {
-  ASSIGNCOPY(email, _value);
-}
-- (NSString *)email {
-  return email;
-}
-
-- (void)setReplyTo:(NSString *)_value {
-  ASSIGNCOPY(replyTo, _value);
-}
-- (NSString *)replyTo {
-  return replyTo;
-}
-
-- (void)setOrganization:(NSString *)_value {
-  ASSIGNCOPY(organization, _value);
-}
-- (NSString *)organization {
-  return organization;
-}
-
-- (void)setSignature:(NSString *)_value {
-  ASSIGNCOPY(signature, _value);
-}
-- (NSString *)signature {
-  return signature;
-}
-- (BOOL)hasSignature {
-  return [[self signature] isNotEmpty];
-}
-
-- (void)setVCard:(NSString *)_value {
-  ASSIGNCOPY(vCard, _value);
-}
-- (NSString *)vCard {
-  return vCard;
-}
-- (BOOL)hasVCard {
-  return [[self vCard] isNotEmpty];
-}
-
-- (void)setSentFolderName:(NSString *)_value {
-  ASSIGNCOPY(sentFolderName, _value);
-}
-- (NSString *)sentFolderName {
-  return sentFolderName;
-}
-
-- (void)setSentBCC:(NSString *)_value {
-  ASSIGNCOPY(sentBCC, _value);
-}
-- (NSString *)sentBCC {
-  return sentBCC;
-}
-
-- (void)setDraftsFolderName:(NSString *)_value {
-  ASSIGNCOPY(draftsFolderName, _value);
-}
-- (NSString *)draftsFolderName {
-  return draftsFolderName;
-}
-
-- (void)setTemplatesFolderName:(NSString *)_value {
-  ASSIGNCOPY(templatesFolderName, _value);
-}
-- (NSString *)templatesFolderName {
-  return templatesFolderName;
-}
-
-/* description */
-
-- (NSString *)description {
-  NSMutableString *ms;
-
-  ms = [NSMutableString stringWithCapacity:128];
-  [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])];
-  
-  if (name  != nil) [ms appendFormat:@" name='%@'",  name];
-  if (email != nil) [ms appendFormat:@" email='%@'", email];
-  
-  if (sentFolderName != nil) 
-    [ms appendFormat:@" sent='%@'", sentFolderName];
-  
-  if ([sentBCC length] > 0) [ms appendString:@" sent-bcc"];
-  if ([vCard length]   > 0) [ms appendString:@" vcard"];
-  
-  [ms appendString:@">"];
-  return ms;
-}
-
-@end /* SOGoMailIdentity */
diff --git a/SoObjects/Mailer/SOGoMailObject+Draft.h b/SoObjects/Mailer/SOGoMailObject+Draft.h
new file mode 100644 (file)
index 0000000..5da1cda
--- /dev/null
@@ -0,0 +1,38 @@
+/* SOGoMailObject+Draft.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 SOGOMAILOBJECT_DRAFT_H
+#define SOGOMAILOBJECT_DRAFT_H
+
+#import "SOGoMailObject.h"
+
+@interface SOGoMailObject (SOGoDraftObjectExtensions)
+
+- (NSString *) subjectForReply;
+- (NSString *) contentForReply;
+
+- (NSString *) subjectForForward;
+- (NSString *) filenameForForward;
+
+@end
+
+#endif /* SOGOMAILOBJECT_DRAFT_H */
diff --git a/SoObjects/Mailer/SOGoMailObject+Draft.m b/SoObjects/Mailer/SOGoMailObject+Draft.m
new file mode 100644 (file)
index 0000000..a5d8da0
--- /dev/null
@@ -0,0 +1,205 @@
+/* SOGoMailObject+Draft.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 <NGExtensions/NSString+misc.h>
+#import <NGExtensions/NSObject+Logs.h>
+
+#import "SOGoMailObject+Draft.h"
+
+#define maxFilenameLength 64
+
+@implementation SOGoMailObject (SOGoDraftObjectExtensions)
+
+- (NSString *) subjectForReply
+{
+  static NSString *replyPrefixes[] = {
+    @"Re:", // regular
+    @"RE:", // Outlook v11 (English?)
+    @"AW:", // German Outlook v11
+    @"Re[", // numbered Re, eg "Re[2]:"
+    nil
+  };
+  BOOL hasPrefix;
+  unsigned int i;
+  NSString *subject, *newSubject;
+
+  hasPrefix = NO;
+
+  subject = [[self envelope] subject];
+  i = 0;
+  while (!hasPrefix && replyPrefixes[i])
+    if ([subject hasPrefix: replyPrefixes[i]])
+      hasPrefix = YES;
+    else
+      i++;
+
+  if (hasPrefix)
+    newSubject = subject;
+  else
+    newSubject = [NSString stringWithFormat: @"Re: %@", subject];
+
+  return newSubject;
+}
+
+- (NSString *) contentForReplyOnParts: (NSDictionary *) _prts
+                                 keys: (NSArray *) _k
+{
+  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++)
+    {
+      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)
+       {
+         if (count > 0)
+           [ms appendString: textPartSeparator];
+         [ms appendString: [v stringByApplyingMailQuoting]];
+       }
+      else
+       [self logWithFormat:@"Note: cannot show part %@", k];
+    }
+
+  return ms;
+}
+
+#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 *) contentForReply
+{
+  NSArray *keys;
+  NSDictionary *parts;
+  NSMutableArray *topLevelKeys = nil;
+  unsigned int count, max;
+  NSRange r;
+  NSString *contentForReply;
+
+//   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]];
+       }
+
+      parts = [self fetchPlainTextStrings: keys];
+      contentForReply = [self contentForReplyOnParts: parts
+                             keys: keys];
+    }
+  else
+    contentForReply = nil;
+
+  return contentForReply;
+}
+
+- (NSString *) filenameForForward
+{
+  NSString *subject;
+  NSMutableString *newSubject;
+  static NSString *sescape[] = { 
+    @"/", @"..", @"~", @"\"", @"'", @" ", @".", nil 
+  };
+  unsigned int count, length;
+
+  subject = [[self envelope] subject];
+  length = [subject length];
+  if (!length)
+    {
+      subject = @"forward";
+      length = [subject length];
+    }
+
+  if (length > maxFilenameLength)
+    length = maxFilenameLength;
+  newSubject = [NSMutableString
+                stringWithString: [subject substringToIndex: length]];
+  count = 0;
+  while (sescape[count])
+    {
+      [newSubject replaceString: sescape[count]
+                 withString: @"_"];
+      count++;
+    }
+  [newSubject appendString: @".eml"];
+
+  return newSubject;
+}
+
+- (NSString *) subjectForForward
+{
+  NSString *subject, *newSubject;
+
+  subject = [[self envelope] subject];
+  if ([subject length] > 0)
+    newSubject = [NSString stringWithFormat: @"[Fwd: %@]", subject];
+  else
+    newSubject = subject;
+
+  return newSubject;
+}
+
+@end
index 72dae38852c86934c7df8c4ad2b1edcdcd3ddc0d..2dd21d42b4119a294cdef807f69ec59099f3d094 100644 (file)
@@ -86,7 +86,7 @@
 /* deletion */
 
 - (BOOL)isDeletionAllowed;
-- (NSException *)trashInContext:(id)_ctx;
+- (NSException *) trashInContext:(id)_ctx;
 - (NSException *) moveToFolderNamed: (NSString *) folderName
                           inContext: (id)_ctx;
 
index 5eb140692aa7590456868c62b1db1b2cdbf77563..9465de049c5b3bee04a7204671bd13564f622729 100644 (file)
@@ -100,7 +100,8 @@ static BOOL debugSoParts       = NO;
     NSLog(@"Note(SOGoMailObject): etag caching disabled!");
 }
 
-- (void)dealloc {
+- (void) dealloc
+{
   [headers    release];
   [headerPart release];
   [coreInfos  release];
@@ -114,18 +115,6 @@ static BOOL debugSoParts       = NO;
   return [nameInContainer stringByDeletingPathExtension];
 }
 
-- (NSMutableString *) imap4URLString
-{
-  NSMutableString *urlString;
-  NSString *imap4Name;
-
-  urlString = [container imap4URLString];
-  imap4Name = [[self relativeImap4Name] stringByEscapingURL];
-  [urlString appendFormat: @"%@", imap4Name];
-
-  return urlString;
-}
-
 /* hierarchy */
 
 - (SOGoMailObject *)mailObject {
@@ -134,7 +123,8 @@ static BOOL debugSoParts       = NO;
 
 /* part hierarchy */
 
-- (NSString *)keyExtensionForPart:(id)_partInfo {
+- (NSString *) keyExtensionForPart: (id) _partInfo
+{
   NSString *mt, *st;
   
   if (_partInfo == nil)
@@ -200,7 +190,8 @@ static BOOL debugSoParts       = NO;
 
 /* message */
 
-- (id)fetchParts:(NSArray *)_parts {
+- (id) fetchParts: (NSArray *) _parts
+{
   // TODO: explain what it does
   /*
     Called by -fetchPlainTextParts:
@@ -227,7 +218,8 @@ static BOOL debugSoParts       = NO;
   return [msgs count] > 0 ? YES : NO;
 }
 
-- (id)fetchCoreInfos {
+- (id) fetchCoreInfos
+{
   id msgs;
   
   if (coreInfos != nil)
@@ -244,19 +236,23 @@ static BOOL debugSoParts       = NO;
     return nil;
   
   coreInfos = [[msgs objectAtIndex:0] retain];
+
   return coreInfos;
 }
 
-- (id)bodyStructure {
+- (id) bodyStructure
+{
   id body;
 
   body = [[self fetchCoreInfos] valueForKey: @"body"];
   if (debugBodyStructure)
     [self logWithFormat: @"BODY: %@", body];
+
   return body;
 }
 
-- (NGImap4Envelope *)envelope {
+- (NGImap4Envelope *) envelope
+{
   return [[self fetchCoreInfos] valueForKey: @"envelope"];
 }
 
@@ -458,7 +454,9 @@ static BOOL debugSoParts       = NO;
 
 /* bulk fetching of plain/text content */
 
-- (BOOL)shouldFetchPartOfType:(NSString *)_type subtype:(NSString *)_subtype {
+- (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 ...).
@@ -475,9 +473,10 @@ static BOOL debugSoParts       = NO;
                   || [_subtype hasPrefix: @"x-vnd.kolab."])));
 }
 
-- (void)addRequiredKeysOfStructure:(id)_info path:(NSString *)_p
-  toArray:(NSMutableArray *)_keys
-  recurse:(BOOL)_recurse
+- (void) addRequiredKeysOfStructure: (id) _info
+                              path: (NSString *) _p
+                           toArray: (NSMutableArray *) _keys
+                           recurse: (BOOL) _recurse
 {
   /* 
      This is used to collect the set of IMAP4 fetch-keys required to fetch
@@ -486,67 +485,70 @@ static BOOL debugSoParts       = NO;
      
      The method calls itself recursively to walk the body structure.
   */
-  NSArray  *parts;
+  NSArray *parts;
   unsigned i, count;
   BOOL fetchPart;
+  NSString *k;
   id body;
-  
-  /* Note: if the part itself doesn't qualify, we still check subparts */
-  fetchPart = [self shouldFetchPartOfType:[_info valueForKey: @"type"]
-                   subtype:[_info valueForKey: @"subtype"]];
-  if (fetchPart) {
-    NSString *k;
+  NSString *sp;
+  id childInfo;
     
-    if ([_p length] > 0) {
-      k = [[@"body[" stringByAppendingString:_p] stringByAppendingString: @"]"];
-    }
-    else {
-      /*
-       for some reason we need to add ".TEXT" for plain text stuff on root
-       entities?
-       TODO: check with HTML
-      */
-      k = @"body[text]";
+
+  /* Note: if the part itself doesn't qualify, we still check subparts */
+  fetchPart = [self shouldFetchPartOfType: [_info valueForKey: @"type"]
+                   subtype: [_info valueForKey: @"subtype"]];
+  if (fetchPart)
+    {
+      if ([_p length] > 0)
+       k = [NSString stringWithFormat: @"body[%@]", _p];
+      else
+       {
+         /*
+           for some reason we need to add ".TEXT" for plain text stuff on root
+           entities?
+           TODO: check with HTML
+         */
+         k = @"body[text]";
+       }
+      [_keys addObject: k];
     }
-    [_keys addObject:k];
-  }
-  
-  if (!_recurse)
-    return;
-  
-  /* recurse */
-  
-  parts = [(NSDictionary *)_info objectForKey: @"parts"];
-  for (i = 0, count = [parts count]; i < count; i++) {
-    NSString *sp;
-    id childInfo;
-    
-    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];
-  }
-  
-  /* check body */
-  
-  if ((body = [(NSDictionary *)_info objectForKey: @"body"]) != nil) {
-    NSString *sp;
 
-    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];
-  }
+  if (_recurse)
+    {
+      /* 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];
+       }
+      
+      /* 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];
+       }
+    }
 }
 
-- (NSArray *)plainTextContentFetchKeys {
+- (NSArray *) plainTextContentFetchKeys
+{
   /*
     The name is not 100% correct. The method returns all body structure fetch
     keys which are marked by the -shouldFetchPartOfType:subtype: method.
@@ -554,8 +556,8 @@ static BOOL debugSoParts       = NO;
   NSMutableArray *ma;
   
   ma = [NSMutableArray arrayWithCapacity:4];
-  [self addRequiredKeysOfStructure:[[self clientObject] bodyStructure]
-       path: @"" toArray:ma recurse:YES];
+  [self addRequiredKeysOfStructure: [[self clientObject] bodyStructure]
+       path: @"" toArray: ma recurse: YES];
   return ma;
 }
 
@@ -853,7 +855,7 @@ static BOOL debugSoParts       = NO;
   
   /* b) mark deleted */
   
-  error = [[self imap4Connection] markURLDeleted:[self imap4URL]];
+  error = [[self imap4Connection] markURLDeleted: [self imap4URL]];
   if (error != nil) return error;
   
   /* c) expunge */
diff --git a/SoObjects/Mailer/SOGoUser+Mail.h b/SoObjects/Mailer/SOGoUser+Mail.h
deleted file mode 100644 (file)
index 432de0f..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
-  Copyright (C) 2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#ifndef __Mailer_SOGoUser_Mail_H__
-#define __Mailer_SOGoUser_Mail_H__
-
-#include <SoObjects/Mailer/SOGoMailBaseObject.h>
-
-/*
-  SOGoUser(Mail)
-
-  TODO: document
-
-  This category adds mail related stuff to the SOGo user class.
-*/
-
-#include <SOGo/SOGoUser.h>
-
-@class NSArray;
-@class SOGoMailIdentity;
-
-@interface SOGoUser(Mail)
-
-- (SOGoMailIdentity *)primaryMailIdentity;
-- (NSArray *)fetchAllMailIdentitiesWithOnlyEmitterAccess:(BOOL)_onlyGC;
-- (SOGoMailIdentity *)primaryMailIdentityForAccount:(NSString *)_account;
-
-@end
-
-#endif /* __Mailer_SOGoUser_Mail_H__ */
diff --git a/SoObjects/Mailer/SOGoUser+Mail.m b/SoObjects/Mailer/SOGoUser+Mail.m
deleted file mode 100644 (file)
index 2cde839..0000000
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <Foundation/NSArray.h>
-#import <Foundation/NSUserDefaults.h>
-#import <Foundation/NSKeyValueCoding.h>
-
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSObject+Logs.h>
-
-#import "SOGoMailIdentity.h"
-
-#import "SOGoUser+Mail.h"
-
-@implementation SOGoUser(Mail)
-
-- (NSString *)agenorSentFolderName {
-  /* Note: specialty: the Sent folder is called the same in all accounts */
-  static NSString *s = nil;
-  if (s == nil) {
-    NSUserDefaults *ud;
-    
-    ud = [NSUserDefaults standardUserDefaults];
-    s = [[ud stringForKey:@"SOGoSentFolderName"] copy];
-    if (![s isNotEmpty]) s = @"Sent";
-    [self logWithFormat:@"Note: using SOGoSentFolderName: '%@'", s];
-  }
-  return s;
-}
-
-- (NSString *)agenorSentFolderForAccount:(NSString *)_account {
-  // TODO: support different locations for shares!
-  NSString *p;
-  
-  if (![_account isNotEmpty]) 
-    return nil;
-  
-  // if ([_account rangeOfString:@".-."].length == 0)
-  // TODO: check whether we need special handling for shares!
-  p = [_account stringByAppendingString:@"/"];
-  p = [p stringByAppendingString:[self agenorSentFolderName]];
-  return p;
-}
-
-- (SOGoMailIdentity *)primaryMailIdentity {
-  SOGoMailIdentity *identity;
-  NSString *account;
-
-  account = [self valueForKey:@"primaryIMAP4AccountString"];
-  
-  identity = [[[SOGoMailIdentity alloc] init] autorelease];
-  [identity setName: [self cn]];
-  [identity setEmail: [self primaryEmail]];
-  [identity setSentFolderName:[self agenorSentFolderForAccount:account]];
-  return identity;
-}
-
-- (SOGoMailIdentity *)mailIdentityForAccount:(NSString *)_account
-  emitter:(NSString *)_em
-{
-  SOGoMailIdentity *identity;
-  
-  identity = [[[SOGoMailIdentity alloc] init] autorelease];
-  [identity setName:[self cn]]; // TODO: should we use something else?
-  if ([_em isNotEmpty]) [identity setEmail:_em];
-  [identity setSentFolderName:[self agenorSentFolderForAccount:_account]];
-  return identity;
-}
-
-- (NSArray *)fetchAllMailIdentitiesWithOnlyEmitterAccess:(BOOL)_onlyGC {
-  NSMutableArray *identities;
-  NSEnumerator *accounts;
-  NSDictionary *shares;
-  NSString  *account;
-  id        identity;
-  
-  identity = [self primaryMailIdentity];
-  shares = [self valueForKey:@"additionalIMAP4AccountsAndEMails"];
-  if ([shares count] == 0)
-    return [NSArray arrayWithObject: identity];
-  
-  identities = [NSMutableArray arrayWithCapacity:[shares count] + 1];
-  if (identity != nil) [identities addObject:identity];
-  
-  accounts = [shares keyEnumerator];
-  while ((account = [accounts nextObject]) != nil) {
-    NSString *emitter;
-    
-    emitter = [shares objectForKey:account];
-    if (_onlyGC && ![emitter isNotNull]) continue;
-    
-    identity = [self mailIdentityForAccount:account emitter:emitter];
-    if (identity != nil)
-      [identities addObject:identity];
-  }
-  
-  return identities;
-}
-
-- (SOGoMailIdentity *)primaryMailIdentityForAccount:(NSString *)_account {
-  NSEnumerator *accounts;
-  NSDictionary *shares;
-  NSString  *account;
-  id        identity;
-  
-  identity = [self primaryMailIdentity];
-  shares = [self valueForKey:@"additionalIMAP4AccountsAndEMails"];
-  if ([shares count] == 0)
-    return identity;
-  
-  /* scan shares for ID */
-  accounts = [shares keyEnumerator];
-  while ((account = [accounts nextObject]) != nil) {
-    NSString *emitter;
-    
-    if (![account isEqualToString:_account])
-      continue;
-    
-    emitter  = [shares objectForKey:account];
-    identity = [self mailIdentityForAccount:_account emitter:emitter];
-    if ([identity isNotNull])
-      return identity;
-  }
-  
-  return identity;
-}
-
-@end /* SOGoUser(Mail) */
index b330cf85435401e7623d217f6bc3c1dbb107b019..181f636cd656481d2cb8db8ea92583996ee401ea 100644 (file)
@@ -32,6 +32,9 @@
 - (NSString *) jsonRepresentation;
 
 - (NSArray *) stringsWithFormat: (NSString *) format;
+- (NSArray *) keysWithFormat: (NSString *) format;
+- (NSArray *) objectsForKey: (NSString *) key;
+- (NSArray *) flattenedArray;
 
 - (BOOL) containsCaseInsensitiveString: (NSString *) match;
 
index 77dabdcbd2c2758cfdeb9631c2f777377807e264..4845b33df7040f3bf0f97082e4d1a8e616edfd11 100644 (file)
@@ -33,6 +33,7 @@
   id currentObject;
 
   formattedStrings = [NSMutableArray arrayWithCapacity: [self count]];
+
   objects = [self objectEnumerator];
   currentObject = [objects nextObject];
   while (currentObject)
   return formattedStrings;
 }
 
+- (NSArray *) keysWithFormat: (NSString *) format
+{
+  NSMutableArray *formattedStrings;
+  NSEnumerator *objects;
+  id currentObject;
+
+  formattedStrings = [NSMutableArray arrayWithCapacity: [self count]];
+
+  objects = [self objectEnumerator];
+  currentObject = [objects nextObject];
+  while (currentObject)
+    {
+      [formattedStrings addObject: [currentObject keysWithFormat: format]];
+      currentObject = [objects nextObject];
+    }
+
+  return formattedStrings;
+}
+
+- (NSArray *) objectsForKey: (NSString *) key
+{
+  NSMutableArray *objectsForKey;
+  unsigned int count, max;
+  id value;
+
+  max = [self count];
+  objectsForKey = [NSMutableArray arrayWithCapacity: max];
+
+  for (count = 0; count < max; count++)
+    {
+      value = [[self objectAtIndex: count] objectForKey: key];
+      [objectsForKey addObject: value];
+    }
+
+  return objectsForKey;
+}
+
+- (NSArray *) flattenedArray
+{
+  NSMutableArray *flattenedArray;
+  NSEnumerator *objects;
+  id currentObject;
+
+  flattenedArray = [NSMutableArray array];
+  objects = [self objectEnumerator];
+  currentObject = [objects nextObject];
+  while (currentObject)
+    {
+      [flattenedArray addObjectsFromArray: currentObject];
+      currentObject = [objects nextObject];
+    }
+
+  return flattenedArray;
+}
+
 - (void) makeObjectsPerform: (SEL) selector
                  withObject: (id) object1
                  withObject: (id) object2
index df40a3427933188269b54bbc79c4484ea7297f01..bc054c813e87179f0a2ac92734661dcbff0a4c1c 100644 (file)
@@ -30,6 +30,7 @@
 @interface NSDictionary (SOGoDictionaryUtilities)
 
 - (NSString *) jsonRepresentation;
+- (NSString *) keysWithFormat: (NSString *) keyFormat;
 
 @end
 
index 5b21ce2c99c731996d6d4f338825ea08f54a9c78..6caaeaa67033ff7bb47016a4f6ef2e7d7cc0d40b 100644 (file)
@@ -23,6 +23,7 @@
 #import <Foundation/NSArray.h>
 #import <Foundation/NSString.h>
 
+#import "NSArray+Utilities.h"
 #import "NSObject+Utilities.h"
 #import "NSDictionary+Utilities.h"
 
   return representation;
 }
 
+- (NSString *) keysWithFormat: (NSString *) keyFormat
+{
+  NSArray *keys, *allKeys;
+  unsigned int count, max;
+  NSMutableString *keysWithFormat;
+  id value;
+
+  keysWithFormat = [NSMutableString stringWithString: keyFormat];
+
+  allKeys = [self allKeys];
+  keys = [allKeys stringsWithFormat: @"%{%@}"];
+
+  max = [allKeys count];
+  for (count = 0; count < max; count++)
+    {
+      value = [self objectForKey: [allKeys objectAtIndex: count]];
+      [keysWithFormat replaceString: [keys objectAtIndex: count]
+                     withString: [value description]];
+    }
+
+  return keysWithFormat;
+}
+
 @end
index 5b6f5d2c28ab377a048e305a8c5b5ff83fa13f82..d6676c0e54b72c23cf237df30c90caf08d4e47c1 100644 (file)
 #import <stdlib.h>
 
 #import <Foundation/NSArray.h>
+#import <Foundation/NSDate.h>
 #import <Foundation/NSDictionary.h>
 #import <Foundation/NSException.h>
+#import <Foundation/NSKeyValueCoding.h>
 #import <Foundation/NSURL.h>
 
 #import <NGObjWeb/SoObject.h>
@@ -134,10 +136,7 @@ static NSString *defaultUserID = @"<default>";
   static GCSFolderManager *folderManager = nil;
 
   if (!folderManager)
-    {
-      folderManager = [GCSFolderManager defaultFolderManager];
-      [folderManager setFolderNamePrefix: @"SOGo"];
-    }
+    folderManager = [GCSFolderManager defaultFolderManager];
 
   return folderManager;
 }
index 29a4c6fcadc40e7994b8c629b213e693a9b249ce..3108e68723d01da1d6783be4ed310ef2b95e05d3 100644 (file)
 
 + (id) sharedMailer;
 
+- (NSException *) sendMailData: (NSData *) data
+                 toRecipients: (NSArray *) recipients
+                       sender: (NSString *) sender;
 - (NSException *) sendMailAtPath: (NSString *) filename
                    toRecipients: (NSArray *) recipients
                          sender: (NSString *) sender;
-
 - (NSException *) sendMimePart: (id <NGMimePart>) part
                  toRecipients: (NSArray *) recipients
                        sender: (NSString *) sender;
index 7d68164245e1dcddc0d15684b68c8123ed942521..5d4bd9ad33ce46a20f5079fbb32bd83e4c6fa4ed 100644 (file)
@@ -58,6 +58,7 @@ extern NSString *SOGoWeekStartFirstFullWeek;
   NSUserDefaults *userSettings;
   NSTimeZone *userTimeZone;
   SOGoDateFormatter *dateFormatter;
+  NSMutableArray *mailAccounts;
 }
 
 + (SOGoUser *) userWithLogin: (NSString *) login
@@ -67,11 +68,11 @@ extern NSString *SOGoWeekStartFirstFullWeek;
 
 /* properties */
 
-- (NSString *) fullEmail;
+// - (NSString *) fullEmail;
 
-- (NSString *) primaryEmail;
-- (NSString *) systemEmail;
-- (NSArray *) allEmails;
+// - (NSString *) primaryEmail;
+// - (NSString *) systemEmail;
+// - (NSArray *) allEmails;
 
 - (BOOL) hasEmail: (NSString *) email;
 
@@ -82,7 +83,7 @@ extern NSString *SOGoWeekStartFirstFullWeek;
 
 /* shares and identities */
 
-- (NSString *) primaryIMAP4AccountString;
+// - (NSString *) primaryIMAP4AccountString;
 
 // - (NSString *) primaryIMAP4AccountString;
 // - (NSString *) primaryMailServer;
@@ -99,6 +100,10 @@ extern NSString *SOGoWeekStartFirstFullWeek;
 - (NSTimeZone *) timeZone;
 - (NSTimeZone *) serverTimeZone;
 
+- (NSArray *) mailAccounts;
+- (NSArray *) allIdentities;
+- (NSDictionary *) primaryIdentity;
+
 /* folders */
 
 - (id) homeFolderInContext: (id) _ctx;
index d212d2c1e48686864bed1f7db824016f7ea40d96..3dc5273a521c290c6e923bf6ec3ace314310fe12 100644 (file)
@@ -23,6 +23,7 @@
 #import <Foundation/NSNull.h>
 #import <Foundation/NSTimeZone.h>
 #import <Foundation/NSUserDefaults.h>
+#import <Foundation/NSValue.h>
 #import <NGObjWeb/WOApplication.h>
 #import <NGObjWeb/SoObject.h>
 #import <NGExtensions/NSNull+misc.h>
@@ -189,33 +190,33 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
 
 /* properties */
 
-- (NSString *) fullEmail
-{
-  return [[LDAPUserManager sharedUserManager] getFullEmailForUID: login];
-}
+// - (NSString *) fullEmail
+// {
+//   return [[LDAPUserManager sharedUserManager] getFullEmailForUID: login];
+// }
 
-- (NSString *) primaryEmail
-{
-  if (!allEmails)
-    [self _fetchAllEmails];
+// - (NSString *) primaryEmail
+// {
+//   if (!allEmails)
+//     [self _fetchAllEmails];
 
-  return [allEmails objectAtIndex: 0];
-}
+//   return [allEmails objectAtIndex: 0];
+// }
 
-- (NSString *) systemEmail
+- (NSArray *) allEmails
 {
   if (!allEmails)
     [self _fetchAllEmails];
 
-  return [allEmails lastObject];
+  return allEmails;  
 }
 
-- (NSArray *) allEmails
+- (NSString *) systemEmail
 {
   if (!allEmails)
     [self _fetchAllEmails];
 
-  return allEmails;  
+  return [allEmails lastObject];
 }
 
 - (BOOL) hasEmail: (NSString *) email
@@ -234,11 +235,6 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
   return cn;
 }
 
-- (NSString *) primaryIMAP4AccountString
-{
-  return [NSString stringWithFormat: @"%@@%@", login, fallbackIMAP4Server];
-}
-
 // - (NSString *) primaryMailServer
 // {
 //   return [[self userManager] getServerForUID: [self login]];
@@ -347,6 +343,115 @@ NSString *SOGoWeekStartFirstFullWeek = @"FirstFullWeek";
   return serverTimeZone;
 }
 
+/* mail */
+- (NSArray *) mailAccounts
+{
+#warning should be implemented with the user defaults interfaces
+  NSMutableDictionary *mailAccount, *identity;
+  NSMutableArray *identities;
+  NSString *name, *fullName;
+
+  if (!mailAccounts)
+    {
+      mailAccount = [NSMutableDictionary dictionary];
+      name = [NSString stringWithFormat: @"%@@%@", login, fallbackIMAP4Server];
+      [mailAccount setObject: login forKey: @"userName"];
+      [mailAccount setObject: fallbackIMAP4Server forKey: @"serverName"];
+      [mailAccount setObject: name forKey: @"name"];
+
+      identity = [NSMutableDictionary dictionary];
+      fullName = [self cn];
+      if (![fullName length])
+       fullName = login;
+      [identity setObject: fullName forKey: @"fullName"];
+      [identity setObject: [self systemEmail] forKey: @"email"];
+      [identity setObject: [NSNumber numberWithBool: YES] forKey: @"isDefault"];
+
+      identities = [NSMutableArray array];
+      [identities addObject: identity];
+      [mailAccount setObject: identities forKey: @"identities"];
+
+      mailAccounts = [NSMutableArray new];
+      [mailAccounts addObject: mailAccount];
+    }
+
+  return mailAccounts;
+}
+
+/*
+@interface SOGoMailIdentity : NSObject
+{
+  NSString *name;
+  NSString *email;
+  NSString *replyTo;
+  NSString *organization;
+  NSString *signature;
+  NSString *vCard;
+  NSString *sentFolderName;
+  NSString *sentBCC;
+  NSString *draftsFolderName;
+  NSString *templatesFolderName;
+  struct
+  {
+    int composeHTML:1;
+    int reserved:31;
+  } idFlags;
+}
+
+- (void) setName: (NSString *) _value;
+- (NSString *) name;
+
+- (void) setEmail: (NSString *) _value;
+- (NSString *) email;
+
+- (void) setReplyTo: (NSString *) _value;
+- (NSString *) replyTo;
+
+- (void) setOrganization: (NSString *) _value;
+- (NSString *) organization;
+
+- (void) setSignature: (NSString *) _value;
+- (NSString *) signature;
+- (BOOL) hasSignature;
+
+- (void) setVCard: (NSString *) _value;
+- (NSString *) vCard;
+- (BOOL) hasVCard;
+
+- (void) setSentFolderName: (NSString *) _value;
+- (NSString *) sentFolderName;
+
+- (void) setSentBCC: (NSString *) _value;
+- (NSString *) sentBCC;
+
+- (void) setDraftsFolderName: (NSString *) _value;
+- (NSString *) draftsFolderName;
+
+- (void) setTemplatesFolderName: (NSString *) _value;
+- (NSString *) templatesFolderName;
+
+@end */
+
+- (NSArray *) allIdentities
+{
+  NSArray *identities;
+
+  [self mailAccounts];
+  identities = [mailAccounts objectsForKey: @"identities"];
+
+  return [identities flattenedArray];
+}
+
+- (NSDictionary *) primaryIdentity
+{
+  NSDictionary *defaultAccount;
+
+  [self mailAccounts];
+  defaultAccount = [mailAccounts objectAtIndex: 0];
+
+  return [[defaultAccount objectForKey: @"identities"] objectAtIndex: 0];
+}
+
 /* folders */
 
 // TODO: those methods should check whether the traversal stack in the context
index cd4320a210436abf97285321e8fc5d2adb7c651e..455202c2059ca8cddc22ac8c557f030694beaa39 100644 (file)
@@ -22,7 +22,9 @@ CommonUI_OBJC_FILES +=                \
        UIxTabItem.m            \
        UIxUserRightsEditor.m   \
        \
-       UIxToolbar.m
+       UIxToolbar.m            \
+       \
+       WODirectAction+SOGo.m \
 
 CommonUI_RESOURCE_FILES += \
        Version                 \
diff --git a/UI/Common/WODirectAction+SOGo.h b/UI/Common/WODirectAction+SOGo.h
new file mode 100644 (file)
index 0000000..92ec032
--- /dev/null
@@ -0,0 +1,37 @@
+/* WODirectAction+SOGo.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 WODIRECTACTION_SOGO_H
+#define WODIRECTACTION_SOGO_H
+
+#import <NGObjWeb/WODirectAction.h>
+
+@class NSString;
+@class WOResponse;
+
+@interface WODirectAction (SOGoExtension)
+
+- (WOResponse *) redirectToLocation: (NSString *) newLocation;
+
+@end
+
+#endif /* WODIRECTACTION_SOGO_H */
diff --git a/UI/Common/WODirectAction+SOGo.m b/UI/Common/WODirectAction+SOGo.m
new file mode 100644 (file)
index 0000000..ddeea2a
--- /dev/null
@@ -0,0 +1,42 @@
+/* WODirectAction+SOGo.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 <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WOResponse.h>
+
+#import "WODirectAction+SOGo.h"
+
+@implementation WODirectAction (SOGoExtension)
+
+- (WOResponse *) redirectToLocation: (NSString *) newLocation
+{
+  WOResponse *response;
+
+  response = [context response];
+  [response setStatus: 302 /* moved */];
+  [response setHeader: newLocation forKey: @"location"];
+
+  return response;
+}
+
+@end
index 600a473adf3ee9929d50e64a9f888d15901aa9f5..6e7a690c8336f20f634e96a46f43b0fa9b0016b6 100644 (file)
 
 /* organizer tracking */
 
-- (NSString *)loggedInUserEMail {
-  return [[[self context] activeUser] primaryEmail];
+- (NSString *) loggedInUserEMail
+{
+  NSDictionary *identity;
+
+  identity = [[context activeUser] primaryIdentity];
+
+  return [identity objectForKey: @"email"];
 }
 
 - (iCalEvent *)authorativeEvent {
index f253e9c39b26911063e432ec3486f450249b278e..01ee000220b4ca4d2ae41e426d85d738e72a61fc 100644 (file)
@@ -69,6 +69,8 @@
 "Addressbook"   = "Addressbook";
 "Anais"         = "Anais";
 
+"Edit Draft..." = "Edit Draft...";
+
 "This mail is being sent from an unsecure network!" = "This mail is being sent from an unsecure network!";
 
 /* Popup "show" */
index 1c604684313dd63896f8454038600d03369cd576..1a48c90164827ee6a74cede33d7d66512a2c9b17 100644 (file)
@@ -69,6 +69,8 @@
 "cc"            = "Cc";
 "bcc"           = "Cci";
 
+"Edit Draft..." = "Modifier le brouillon...";
+
 "This mail is being sent from an unsecure network!" = "Ce mail est envoyé depuis un réseau non sécurisé !";
 
 /* Popup "show" */
index 0fb3d52a1d3f1c11028e3fcb585ddc39c668f90c..cc8411a8b508b5aa1cad9c56d905571ec3a68c3e 100644 (file)
@@ -27,16 +27,17 @@ MailerUI_OBJC_FILES += \
        \
        UIxMailAccountActions.m         \
        UIxMailFolderActions.m          \
+       UIxMailActions.m                \
        UIxMailEditor.m                 \
-       UIxMailEditorAction.m           \
-       UIxMailReplyAction.m            \
-       UIxMailForwardAction.m          \
        UIxMailToSelection.m            \
        UIxMailWindowCloser.m           \
        \
+       UIxMailUserRightsEditor.m       \
+#      UIxMailEditorAction.m           \
+#      UIxMailReplyAction.m            \
+#      UIxMailForwardAction.m          \
 #      UIxFilterList.m                 \
 #      \
-       UIxMailUserRightsEditor.m
 #      UIxSieveEditor.m
 
 MailerUI_RESOURCE_FILES += \
index 8df3661db3b60c3696931c32274e198fa16f7ceb..ac4ad5a824b720fbd9744354227cef491bd44b4d 100644 (file)
@@ -10,8 +10,8 @@
     { link     = "#";
       isSafe   = NO;
       image = "tb-mail-write-flat-24x24.png";
-      onclick  = "return openMessageWindow(null, 'compose');";
-      cssClass = "tbicon_compose"; label = "Write";    },
+      onclick  = "return onComposeMessage();";
+      cssClass = "tbicon_compose"; label = "Write"; },
     { link  = "#"; target = "addressbook";
       onclick  = "openAddressbook(this);return false;";
       image = "tb-mail-addressbook-flat-24x24.png";
index 6342e92f32dd758e1213967997ede9a9392cff2b..f7cb0be53b1f1af3d38931c3a50ab7f245d361f6 100644 (file)
 #import <Foundation/NSEnumerator.h>
 
 #import <NGObjWeb/WOContext.h>
+#import <NGObjWeb/WORequest.h>
 #import <NGObjWeb/WOResponse.h>
 #import <NGImap4/NGImap4Connection.h>
 #import <SoObjects/Mailer/SOGoMailAccount.h>
+#import <SoObjects/Mailer/SOGoDraftObject.h>
+#import <SoObjects/Mailer/SOGoDraftsFolder.h>
 #import <SoObjects/SOGo/NSObject+Utilities.h>
+#import <SoObjects/SOGo/NSString+Utilities.h>
+
+#import "../Common/WODirectAction+SOGo.h"
 
 #import "UIxMailAccountActions.h"
 
   return response;
 }
 
+/* compose */
+
+- (WOResponse *) composeAction
+{
+  SOGoDraftsFolder *drafts;
+  SOGoDraftObject *newDraftMessage;
+  NSString *urlBase, *url;
+  NSString *mailTo;
+
+  drafts = [[self clientObject] draftsFolderInContext: context];
+  newDraftMessage = [drafts newDraft];
+
+  mailTo = [[self request] formValueForKey: @"mailto"];
+  if ([mailTo length] > 0)
+    {
+      [newDraftMessage setHeaders: [NSDictionary dictionaryWithObject: mailTo
+                                                forKey: @"to"]];
+      [newDraftMessage storeInfo];
+    }
+
+  urlBase = [newDraftMessage baseURLInContext: context];
+  url = [urlBase composeURLWithAction: @"edit"
+                parameters: nil
+                andHash: NO];
+
+  return [self redirectToLocation: url];  
+}
+
 @end
diff --git a/UI/MailerUI/UIxMailActions.h b/UI/MailerUI/UIxMailActions.h
new file mode 100644 (file)
index 0000000..950639e
--- /dev/null
@@ -0,0 +1,31 @@
+/* UIxMailActions.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 UIXMAILACTIONS_H
+#define UIXMAILACTIONS_H
+
+#import <NGObjWeb/WODirectAction.h>
+
+@interface UIxMailActions : WODirectAction
+@end
+
+#endif /* UIXMAILACTIONS_H */
diff --git a/UI/MailerUI/UIxMailActions.m b/UI/MailerUI/UIxMailActions.m
new file mode 100644 (file)
index 0000000..7e177cd
--- /dev/null
@@ -0,0 +1,107 @@
+/* UIxMailActions.m - this file is part of SOGo
+ *
+ * Copyright (C) 2007 Inverse groupe conseil
+ *
+ * Author: Wolfgang Sourdeau <wsourdeau@inverse.ca>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This file is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#import <Foundation/NSString.h>
+
+#import <SoObjects/Mailer/SOGoDraftObject.h>
+#import <SoObjects/Mailer/SOGoDraftsFolder.h>
+#import <SoObjects/Mailer/SOGoMailAccount.h>
+#import <SoObjects/Mailer/SOGoMailObject.h>
+
+#import "../Common/WODirectAction+SOGo.h"
+
+#import "UIxMailActions.h"
+
+@implementation UIxMailActions
+
+- (WOResponse *) editAction
+{
+  SOGoMailAccount *account;
+  SOGoMailObject *co;
+  SOGoDraftsFolder *folder;
+  SOGoDraftObject *newMail;
+  NSString *newLocation;
+
+  co = [self clientObject];
+  account = [co mailAccountFolder];
+  folder = [account draftsFolderInContext: context];
+  newMail = [folder newDraft];
+  [newMail fetchMailForEditing: co];
+  [newMail storeInfo];
+
+  newLocation = [NSString stringWithFormat: @"%@/edit",
+                         [newMail baseURLInContext: context]];
+
+  return [self redirectToLocation: newLocation];
+}
+
+- (WOResponse *) replyToAll: (BOOL) toAll
+{
+  SOGoMailAccount *account;
+  SOGoMailObject *co;
+  SOGoDraftsFolder *folder;
+  SOGoDraftObject *newMail;
+  NSString *newLocation;
+
+  co = [self clientObject];
+  account = [co mailAccountFolder];
+  folder = [account draftsFolderInContext: context];
+  newMail = [folder newDraft];
+  [newMail fetchMailForReplying: co toAll: toAll];
+
+  newLocation = [NSString stringWithFormat: @"%@/edit",
+                         [newMail baseURLInContext: context]];
+
+  return [self redirectToLocation: newLocation];
+}
+
+- (WOResponse *) replyAction
+{
+  return [self replyToAll: NO];
+}
+
+- (WOResponse *) replyToAllAction
+{
+  return [self replyToAll: NO];
+}
+
+- (WOResponse *) forwardAction
+{
+  SOGoMailAccount *account;
+  SOGoMailObject *co;
+  SOGoDraftsFolder *folder;
+  SOGoDraftObject *newMail;
+  NSString *newLocation;
+
+  co = [self clientObject];
+  account = [co mailAccountFolder];
+  folder = [account draftsFolderInContext: context];
+  newMail = [folder newDraft];
+  [newMail fetchMailForForwarding: co];
+
+  newLocation = [NSString stringWithFormat: @"%@/edit",
+                         [newMail baseURLInContext: context]];
+
+  return [self redirectToLocation: newLocation];
+}
+
+@end
index 89d8cd2ab841cf9f09b07c79464fbecaa45d1944..a221609e1e33e212febbb28a501099605b3507dc 100644 (file)
 #import <Foundation/NSString.h>
 #import <Foundation/NSUserDefaults.h>
 
-#import <NGObjWeb/WORequest.h>
-#import <NGObjWeb/SoSubContext.h>
 #import <NGObjWeb/NSException+HTTP.h>
+#import <NGObjWeb/SoSubContext.h>
+#import <NGObjWeb/WORequest.h>
+#import <NGObjWeb/WOResponse.h>
 #import <NGExtensions/NSNull+misc.h>
 #import <NGExtensions/NSObject+Logs.h>
 #import <NGExtensions/NSString+misc.h>
@@ -41,8 +42,9 @@
 #import <SoObjects/Mailer/SOGoMailFolder.h>
 #import <SoObjects/Mailer/SOGoMailAccount.h>
 #import <SoObjects/Mailer/SOGoMailAccounts.h>
-#import <SoObjects/Mailer/SOGoMailIdentity.h>
 #import <SoObjects/SOGo/SOGoUser.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
+#import <SoObjects/SOGo/NSDictionary+Utilities.h>
 #import <SOGoUI/UIxComponent.h>
 
 /*
@@ -51,9 +53,6 @@
   An mail editor component which works on SOGoDraftObject's.
 */
 
-@class NSArray, NSString;
-@class SOGoMailFolder;
-
 @interface UIxMailEditor : UIxComponent
 {
   NSArray  *to;
@@ -61,7 +60,7 @@
   NSArray  *bcc;
   NSString *subject;
   NSString *text;
-  NSMutableArray  *fromEMails;
+  NSArray *fromEMails;
   NSString *from;
   SOGoMailFolder *sentFolder;
 
 
 @implementation UIxMailEditor
 
-static BOOL         keepMailTmpFile      = NO;
-static BOOL         showInternetMarker   = NO;
-static BOOL         useLocationBasedSentFolder = NO;
+static BOOL keepMailTmpFile = NO;
+static BOOL showInternetMarker = NO;
+static BOOL useLocationBasedSentFolder = NO;
 static NSDictionary *internetMailHeaders = nil;
-static NSArray      *infoKeys            = nil;
+static NSArray *infoKeys = nil;
 
 + (void) initialize
 {
   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
   
   infoKeys = [[NSArray alloc] initWithObjects:
-                               @"subject", @"text", @"to", @"cc", @"bcc", 
+                               @"subject", @"to", @"cc", @"bcc", 
                              @"from", @"replyTo",
                              nil];
   
@@ -113,104 +112,114 @@ static NSArray      *infoKeys            = nil;
 {
   [sentFolder release];
   [fromEMails release];
-  [from    release];
-  [text    release];
+  [from release];
+  [text release];
   [subject release];
-  [to      release];
-  [cc      release];
-  [bcc     release];
-  
-  [attachmentName  release];
+  [to release];
+  [cc release];
+  [bcc release];
+  [attachmentName release];
   [attachmentNames release];
   [super dealloc];
 }
 
 /* accessors */
 
-- (void) setFrom: (NSString *) _value
+- (void) setFrom: (NSString *) newFrom
 {
-  ASSIGNCOPY(from, _value);
+  ASSIGN (from, newFrom);
 }
 
 - (NSString *) from
 {
-  if (![from isNotEmpty])
-    return [[[self context] activeUser] primaryEmail];
+  NSDictionary *identity;
+
+  if (!from)
+    {
+      identity = [[context activeUser] primaryIdentity];
+      from = [identity keysWithFormat: @"%{fullName} <%{email}>"];
+    }
 
   return from;
 }
 
-- (void) setReplyTo: (NSString *) _ignore
-{
-}
+// - (void) setReplyTo: (NSString *) ignore
+// {
+// }
 
-- (NSString *) replyTo
-{
-  /* we are here for future extensibility */
-  return @"";
-}
+// - (NSString *) replyTo
+// {
+//   /* we are here for future extensibility */
+//   return @"";
+// }
 
-- (void) setSubject: (NSString *) _value
+- (void) setSubject: (NSString *) newSubject
 {
-  ASSIGNCOPY(subject, _value);
+  ASSIGN (subject, newSubject);
 }
 
 - (NSString *) subject
 {
-  return subject ? subject : @"";
+  return subject;
 }
 
-- (void) setText: (NSString *) _value
+- (void) setText: (NSString *) newText
 {
-  ASSIGNCOPY(text, _value);
+  ASSIGN (text, newText);
 }
 
 - (NSString *) text
 {
-  return [text isNotNull] ? text : @"";
+  return text;
 }
 
-- (void) setTo: (NSArray *)_value
+- (void) setTo: (NSArray *) newTo
 {
-  ASSIGNCOPY(to, _value);
+  if ([newTo isKindOfClass: [NSNull class]])
+    newTo = nil;
+
+  ASSIGN (to, newTo);
 }
 
 - (NSArray *) to
 {
-  return [to isNotNull] ? to : [NSArray array];
+  return to;
 }
 
-- (void) setCc: (NSArray *) _value
+- (void) setCc: (NSArray *) newCc
 {
-  ASSIGNCOPY(cc, _value);
+  if ([newCc isKindOfClass: [NSNull class]])
+    newCc = nil;
+
+  ASSIGN (cc, newCc);
 }
 
 - (NSArray *) cc
 {
-  return [cc isNotNull] ? cc : [NSArray array];
+  return cc;
 }
 
-- (void) setBcc: (NSArray *) _value
+- (void) setBcc: (NSArray *) newBcc
 {
-  ASSIGNCOPY(bcc, _value);
+  if ([newBcc isKindOfClass: [NSNull class]])
+    newBcc = nil;
+
+  ASSIGN (bcc, newBcc);
 }
 
 - (NSArray *) bcc
 {
-  return [bcc isNotNull] ? bcc : [NSArray array];
+  return bcc;
 }
 
 - (BOOL) hasOneOrMoreRecipients
 {
-  if ([[self to]  count] > 0) return YES;
-  if ([[self cc]  count] > 0) return YES;
-  if ([[self bcc] count] > 0) return YES;
-  return NO;
+  return (([to count] + [cc count] + [bcc count]) > 0);
 }
 
-- (void) setAttachmentName: (NSString *) _attachmentName
+- (void) setAttachmentName: (NSString *) newAttachmentName
 {
-  ASSIGN(attachmentName, _attachmentName);
+  ASSIGN (attachmentName, newAttachmentName);
 }
 
 - (NSString *) attachmentName
@@ -222,223 +231,41 @@ static NSArray      *infoKeys            = nil;
 
 - (NSArray *) fromEMails
 {
-  NSEnumerator *emails;
-  SOGoUser *activeUser;
-  NSString *cn, *fullMail, *email;
-  
+  NSArray *allIdentities;
+
   if (!fromEMails)
     { 
-      fromEMails = [NSMutableArray new];
-      activeUser = [context activeUser];
-      cn = [activeUser cn];
-      if ([cn length] == 0)
-       cn = nil;
-      emails = [[activeUser allEmails] objectEnumerator];
-      email = [emails nextObject];
-      while (email)
-       {
-         if (cn)
-           fullMail = [NSString stringWithFormat: @"%@ <%@>", cn, email];
-         else
-           fullMail = email;
-         [fromEMails addObject: fullMail];
-         email = [emails nextObject];
-       }
+      allIdentities = [[context activeUser] allIdentities];
+      fromEMails = [allIdentities keysWithFormat: @"%{fullName} <%{email}>"];
+      [fromEMails retain];
     }
 
   return fromEMails;
 }
 
-/* title */
-
-- (NSString *)panelTitle {
-  return [self labelForKey:@"Compose Mail"];
-}
-
 /* info loading */
 
-- (void)loadInfo:(NSDictionary *)_info {
+- (void) loadInfo: (NSDictionary *) _info
+{
   if (![_info isNotNull]) return;
   [self debugWithFormat:@"loading info ..."];
   [self takeValuesFromDictionary:_info];
 }
-- (NSDictionary *)storeInfo {
+
+- (NSDictionary *) storeInfo
+{
   [self debugWithFormat:@"storing info ..."];
   return [self valuesForKeys:infoKeys];
 }
 
 /* requests */
 
-- (BOOL) shouldTakeValuesFromRequest: (WORequest *) _rq
-                          inContext: (WOContext*) _c
+- (BOOL) shouldTakeValuesFromRequest: (WORequest *) request
+                          inContext: (WOContext*) localContext
 {
   return YES;
 }
 
-/* IMAP4 store */
-
-- (NSException *) patchFlagsInStore
-{
-  /*
-    Flags we should set:
-      if the draft is a reply   => [message markAnswered]
-      if the draft is a forward => [message addFlag:@"forwarded"]
-      
-    This is hard, we would need to find the original message in Cyrus.
-  */
-  return nil;
-}
-
-- (id) lookupSentFolderUsingAccount
-{
-  SOGoMailAccount *account;
-  SOGoMailFolder  *folder;
-  
-  if (sentFolder != nil)
-    return [sentFolder isNotNull] ? sentFolder : nil;;
-  
-  account = [[self clientObject] mailAccountFolder];
-  if ([account isKindOfClass:[NSException class]]) return account;
-  
-  folder = [account sentFolderInContext:[self context]];
-  if ([folder isKindOfClass:[NSException class]]) return folder;
-  return ((sentFolder = [folder retain]));
-}
-
-- (void) _presetFromBasedOnAccountsQueryParameter
-{
-  /* preset the from field to the primary identity of the given account */
-  /* Note: The compose action sets the 'accounts' query parameter */
-  NSString         *accountID;
-  SOGoMailAccounts *accounts;
-  SOGoMailAccount  *account;
-  SOGoMailIdentity *identity;
-
-  if (useLocationBasedSentFolder) /* from will be based on location */
-    return;
-
-  if ([from isNotEmpty]) /* a from is already set */
-    return;
-
-  accountID = [[[self context] request] formValueForKey:@"account"];
-  if (![accountID isNotEmpty])
-    return;
-
-  accounts = [[self clientObject] mailAccountsFolder];
-  if ([accounts isExceptionOrNull])
-    return; /* we don't treat this as an error but are tolerant */
-
-  account = [accounts lookupName:accountID inContext:[self context]
-                     acquire:NO];
-  if ([account isExceptionOrNull])
-    return; /* we don't treat this as an error but are tolerant */
-  
-  identity = [account valueForKey:@"preferredIdentity"];
-  if (![identity isNotNull]) {
-    [self warnWithFormat:@"Account has no preferred identity: %@", account];
-    return;
-  }
-  
-  [self setFrom: [identity email]];
-}
-
-- (SOGoMailIdentity *) selectedMailIdentity
-{
-  SOGoMailAccounts *accounts;
-  NSEnumerator     *e;
-  SOGoMailIdentity *identity;
-  
-  accounts = [[self clientObject] mailAccountsFolder];
-  if ([accounts isExceptionOrNull]) return (id)accounts;
-  
-  // TODO: This is still a hack because we detect the identity based on the
-  //       from. In Agenor all of the identities have unique emails, but this
-  //       is not required for SOGo.
-  
-  if ([[self from] length] == 0)
-    return nil;
-  
-  e = [[accounts fetchIdentitiesWithEmitterPermissions] objectEnumerator];
-  while ((identity = [e nextObject]) != nil) {
-    if ([[identity email] isEqualToString:[self from]])
-      return identity;
-  }
-  return nil;
-}
-
-- (id) lookupSentFolderUsingFrom
-{
-  // TODO: if we have the identity we could also support BCC
-  SOGoMailAccounts *accounts;
-  SOGoMailIdentity *identity;
-  SoSubContext *ctx;
-  NSString     *sentFolderName;
-  NSArray      *sentFolderPath;
-  NSException  *error = nil;
-  
-  if (sentFolder != nil)
-    return [sentFolder isNotNull] ? sentFolder : nil;;
-  
-  identity = [self selectedMailIdentity];
-  if ([identity isKindOfClass:[NSException class]]) return identity;
-  
-  if (![(sentFolderName = [identity sentFolderName]) isNotEmpty]) {
-    [self warnWithFormat:@"Identity has no sent folder name: %@", identity];
-    return nil;
-  }
-  
-  // TODO: fixme, we treat the foldername as a hardcoded path from SOGoAccounts
-  // TODO: escaping of foldernames with slashes
-  // TODO: maybe the SOGoMailIdentity should have an 'account-identifier'
-  //       which is used to lookup the account and _then_ perform an account
-  //       local folder lookup? => would not be possible to have identities
-  //       saving to different accounts.
-  sentFolderPath = [sentFolderName componentsSeparatedByString:@"/"];
-  
-  accounts = [[self clientObject] mailAccountsFolder];
-  if ([accounts isKindOfClass:[NSException class]]) return (id)accounts;
-  
-  ctx = [[SoSubContext alloc] initWithParentContext:[self context]];
-  
-  sentFolder = [[accounts traversePathArray:sentFolderPath
-                               inContext:ctx error:&error
-                               acquire:NO] retain];
-  [ctx release]; ctx = nil;
-  if (error != nil) {
-    [self errorWithFormat:@"Sent-Folder lookup for identity %@ failed: %@",
-           identity, sentFolderPath];
-    return error;
-  }
-  
-#if 0
-  [self logWithFormat:@"Sent-Folder: %@", sentFolderName];
-  [self logWithFormat:@"  object:    %@", sentFolder];
-#endif
-  return sentFolder;
-}
-
-- (NSException *) storeMailInSentFolder: (NSString *) _path
-{
-  SOGoMailFolder *folder;
-  NSData *data;
-  id result;
-  
-  folder = useLocationBasedSentFolder 
-    ? [self lookupSentFolderUsingAccount]
-    : [self lookupSentFolderUsingFrom];
-  if ([folder isKindOfClass:[NSException class]]) return (id)folder;
-  if (folder == nil) return nil;
-  
-  if ((data = [[NSData alloc] initWithContentsOfMappedFile:_path]) == nil) {
-    return [NSException exceptionWithHTTPStatus:500 /* server error */
-                       reason:@"could not find temporary draft file!"];
-  }
-  
-  result = [folder postData:data flags:@"seen"];
-  [data release]; data = nil;
-  return result;
-}
-
 /* actions */
 
 - (NSDictionary *) _scanAttachmentFilenamesInRequest: (id) httpBody
@@ -458,8 +285,8 @@ static NSArray      *infoKeys            = nil;
   for (count = 0; count < max; count++)
     {
       part = [parts objectAtIndex: count];
-      header = [part headerForKey: @"content-disposition"];
-      mimeType = [[part headerForKey: @"content-type"] stringValue];
+      header = (NGMimeContentDispositionHeaderField *) [part headerForKey: @"content-disposition"];
+      mimeType = [(NGMimeType *) [part headerForKey: @"content-type"] stringValue];
       attachment = [NSDictionary dictionaryWithObjectsAndKeys:
                                   [header filename], @"filename",
                                 mimeType, @"mime-type", nil];
@@ -506,26 +333,29 @@ static NSArray      *infoKeys            = nil;
   NSDictionary *info;
   NSException *error;
   BOOL success;
+  SOGoDraftObject *co;
+
+  co = [self clientObject];
+  [co fetchInfo];
 
   success = YES;
 
   if ([self _saveAttachments])
     {
       info = [self storeInfo];
-      if (info)
+      [co setHeaders: info];
+      [co setText: text];
+      error = [co storeInfo];
+      if (error)
        {
-         error = [[self clientObject] storeInfo:info];
-         if (error)
-           {
-             [self errorWithFormat:@"failed to store draft: %@", error];
-             // TODO: improve error handling
-             success = NO;
-           }
+         [self errorWithFormat: @"failed to store draft: %@", error];
+         // TODO: improve error handling
+         success = NO;
        }
     }
   else
     success = NO;
-  
+
   // TODO: wrap content
   
   return success;
@@ -543,13 +373,14 @@ static NSArray      *infoKeys            = nil;
 - (NSArray *) attachmentNames
 {
   NSArray *a;
-  
+
   if (attachmentNames != nil)
     return attachmentNames;
-  
+
   a = [[self clientObject] fetchAttachmentNames];
-  a = [a sortedArrayUsingSelector:@selector(compare:)];
+  a = [a sortedArrayUsingSelector: @selector (compare:)];
   attachmentNames = [a copy];
+
   return attachmentNames;
 }
 
@@ -560,126 +391,72 @@ static NSArray      *infoKeys            = nil;
 
 - (id) defaultAction
 {
-#if 0
-  [self logWithFormat:@"edit action, load content from: %@",
-         [self clientObject]];
-#endif
-  
-  [self loadInfo:[[self clientObject] fetchInfo]];
-  [self _presetFromBasedOnAccountsQueryParameter];
+  SOGoDraftObject *co;
+
+  co = [self clientObject];
+  [co fetchInfo];
+  [self loadInfo: [co headers]];
+  [self setText: [co text]];
+
   return self;
 }
 
-- (id) saveAction
+- (id <WOActionResults>) saveAction
 {
-  return [self _saveFormInfo] ? self : [self failedToSaveFormResponse];
+  id result;
+
+  if ([self _saveFormInfo])
+    {
+      result = [[self clientObject] save];
+      if (!result)
+       {
+         result = [context response];
+         [result setStatus: 204];
+       }
+    }
+  else
+    result = [self failedToSaveFormResponse];
+
+  return result;
 }
 
 - (NSException *) validateForSend
 {
-  // TODO: localize errors
-  
-  if (![self hasOneOrMoreRecipients]) {
-    return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                       reason:@"Please select a recipient!"];
-  }
-  if ([[self subject] length] == 0) {
-    return [NSException exceptionWithHTTPStatus:400 /* Bad Request */
-                       reason:@"Please set a subject!"];
-  }
+  NSException *error;
+
+  if (![self hasOneOrMoreRecipients])
+    error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+                        reason: @"Please select a recipient!"];
+  else if ([[self subject] length] == 0)
+    error = [NSException exceptionWithHTTPStatus: 400 /* Bad Request */
+                        reason: @"Please set a subject!"];
+  else
+    error = nil;
   
-  return nil;
+  return error;
 }
 
 - (id <WOActionResults>) sendAction
 {
-  NSException  *error;
-  NSString     *mailPath;
   id <WOActionResults> result;
 
   // TODO: need to validate whether we have a To etc
   
   /* first, save form data */
-  
-  if (![self _saveFormInfo])
-    return [self failedToSaveFormResponse];
-  
-  /* validate for send */
-  
-  if ((error = [self validateForSend]) != nil) {
-    id url;
-    
-    url = [[error reason] stringByEscapingURL];
-    url = [@"edit?error=" stringByAppendingString:url];
-    return [self redirectToLocation:url];
-  }
-  
-  /* setup some extra headers if required */
-  
-  /* save mail to file (so that we can upload the mail to Cyrus) */
-  // TODO: all this could be handled by the SOGoDraftObject?
-  
-  mailPath = [[self clientObject] saveMimeMessageToTemporaryFileWithHeaders: internetMailHeaders];
-
-  /* then, send mail */
-  
-  if ((error = [[self clientObject] sendMimeMessageAtPath:mailPath]) != nil) {
-    // TODO: improve error handling
-    [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
-    return error;
-  }
-  
-  /* patch flags in store for replies etc */
-  
-  if ((error = [self patchFlagsInStore]) != nil)
-     return error;
-  
-  /* finally store in Sent */
-  
-  if ((error = [self storeMailInSentFolder:mailPath]) != nil)
-    return error;
-  
-  /* delete temporary mail file */
-  
-  if (keepMailTmpFile)
-    [self warnWithFormat:@"keeping mail file: '%@'", mailPath];
-  else
-    [[NSFileManager defaultManager] removeFileAtPath:mailPath handler:nil];
-  mailPath = nil;
-  
-  /* delete draft */
-  
-  if ((error = [[self clientObject] delete]) != nil)
-    return error;
-
-  if ([[[[self context] request] formValueForKey: @"nojs"] intValue])
-    result = [self redirectToLocation: [self applicationPath]];
-  else
-    result = [self jsCloseWithRefreshMethod: nil];
+  result = [self validateForSend];
+  if (!result)
+    {
+      if ([self _saveFormInfo])
+       {
+         result = [[self clientObject] sendMail];
+         if (!result)
+           result = [self jsCloseWithRefreshMethod: nil];
+       }
+      else
+       result = [self failedToSaveFormResponse];
+    }
 
   return result;
 }
 
-- (id) deleteAction
-{
-  NSException *error;
-  id page;
-  
-  if ((error = [[self clientObject] delete]) != nil) {
-    /* Note: we ignore 404: those are drafts which were not yet saved */
-    if (![error httpStatus] == 404)
-      return error;
-  }
-  
-#if 1
-  page = [self pageWithName:@"UIxMailWindowCloser"];
-  [page takeValue:@"YES" forKey:@"refreshOpener"];
-  return page;
-#else
-  // TODO: if we just return nil, we produce a 500
-  return [NSException exceptionWithHTTPStatus:204 /* No Content */
-                     reason:@"object was deleted."];
-#endif
-}
-
 @end /* UIxMailEditor */
diff --git a/UI/MailerUI/UIxMailEditorAction.h b/UI/MailerUI/UIxMailEditorAction.h
deleted file mode 100644 (file)
index 9a9fa4f..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#ifndef __UIxMailEditorAction_H__
-#define __UIxMailEditorAction_H__
-
-#include <NGObjWeb/WODirectAction.h>
-
-/*
-  UIxMailEditorAction
-  
-  This action implements the backend for the various buttons which invoke the
-  mail editor. The mail editor itself only works on a SOGoDraftObject which
-  needs to be created in advance.
-*/
-
-@class NSException;
-@class WOResponse;
-@class SOGoDraftObject, SOGoDraftsFolder;
-
-@interface UIxMailEditorAction : WODirectAction
-{
-  SOGoDraftObject *newDraft;
-}
-
-/* errors */
-
-- (id)didNotFindDraftsError;
-- (id)couldNotCreateDraftError:(SOGoDraftsFolder *)_draftsFolder;
-- (id)didNotFindMailError;
-
-/* creating new draft object */
-
-- (NSException *)_setupNewDraft;
-- (WOResponse *)redirectToEditNewDraft;
-
-/* state */
-
-- (void)reset;
-
-@end
-
-#endif /* __UIxMailEditorAction_H__ */
diff --git a/UI/MailerUI/UIxMailEditorAction.m b/UI/MailerUI/UIxMailEditorAction.m
deleted file mode 100644 (file)
index 8fd8ed2..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <Foundation/NSDictionary.h>
-
-#import <NGObjWeb/WOContext.h>
-#import <NGObjWeb/WORequest.h>
-#import <NGObjWeb/WOResponse.h>
-#import <NGObjWeb/NSException+HTTP.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSException+misc.h>
-
-#import <SoObjects/SOGo/NSString+Utilities.h>
-#import <SoObjects/Mailer/SOGoDraftsFolder.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-#import <SoObjects/Mailer/SOGoMailAccount.h>
-#import <SoObjects/Mailer/SOGoMailObject.h>
-
-#import "UIxMailEditorAction.h"
-
-@implementation UIxMailEditorAction
-
-- (void)dealloc
-{
-  [self->newDraft release];
-  [super dealloc];
-}
-
-/* caches */
-
-- (void)reset {
-  [self->newDraft release]; self->newDraft = nil;
-}
-
-/* lookups */
-
-- (SOGoDraftsFolder *)draftsFolder {
-  /* 
-     Note: we cannot use acquisition to find the nearest drafts folder, because
-           the IMAP4 server might contains an own Drafts folder.
-  */
-//   SOGoDraftsFolder *drafts;
-  SOGoMailAccount *accountFolder;
-  
-  accountFolder = [[self clientObject] mailAccountFolder];
-
-  return [accountFolder
-          lookupName: [accountFolder draftsFolderNameInContext: context]
-          inContext: context acquire: NO];
-}
-
-/* errors */
-
-- (id)didNotFindDraftsError {
-  // TODO: make a nice error page
-  return [@"did not find drafts folder in object: "
-          stringByAppendingString:[[self clientObject] description]];
-}
-- (id)couldNotCreateDraftError:(SOGoDraftsFolder *)_draftsFolder {
-  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!"];
-}
-
-/* compose */
-
-- (id) composeAction
-{
-  SOGoDraftsFolder *drafts;
-  WOResponse *r;
-  NSString *urlBase, *url;
-  NSMutableDictionary *urlParams;
-  id parameter;
-  id returnValue;
-
-  drafts = [self draftsFolder];
-  if ([drafts isNotNull])
-    {
-      if ([drafts isKindOfClass: [NSException class]])
-       returnValue = drafts;
-      else
-       {
-         urlBase = [drafts newObjectBaseURLInContext: context];
-         if ([urlBase isNotNull])
-           {
-             urlParams = [NSMutableDictionary new];
-             [urlParams autorelease];
-
-             /* attach mail-account info */
-             parameter
-               = [[self clientObject] valueForKey: @"mailAccountFolder"];
-             if (parameter && ![parameter isExceptionOrNull])
-               [urlParams setObject: [parameter nameInContainer]
-                          forKey: @"account"];
-
-             parameter = [[self request] formValueForKey: @"mailto"];
-             if (parameter)
-               [urlParams setObject: parameter
-                          forKey: @"mailto"];
-
-             url = [urlBase composeURLWithAction: @"edit"
-                            parameters: urlParams
-                            andHash: NO];
-             /* perform redirect */
-             
-             [self debugWithFormat:@"compose on %@: %@", drafts, url];
-  
-             r = [context response];
-             [r setStatus: 302 /* move d */];
-             [r setHeader: url forKey: @"location"];
-             [self reset];
-
-             returnValue = r;
-           }
-         else
-           returnValue = [self couldNotCreateDraftError: drafts];
-       }
-    }
-  else
-    returnValue = [self didNotFindDraftsError];
-
-  return returnValue;
-}
-
-/* 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: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 debugWithFormat:@"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:context];
-  if (![url hasSuffix:@"/"]) url = [url stringByAppendingString:@"/"];
-  url = [url stringByAppendingString:@"edit"];
-  
-  // TODO: debug log
-  [self logWithFormat:@"compose on %@", url];
-  
-  r = [context response];
-  [r setStatus:302 /* moved */];
-  [r setHeader:url forKey:@"location"];
-  [self reset];
-  return r;
-}
-
-@end /* UIxMailEditorAction */
index df473df607ec3b1dcd51e008fde51f2aec104501..d3641d4a96207630ecc1da12c57007cf607d326a 100644 (file)
 @class WOResponse;
 
 @interface UIxMailFolderActions : WODirectAction
-{
-}
 
 - (WOResponse *) createFolderAction;
 - (WOResponse *) renameFolderAction;
 - (WOResponse *) deleteFolderAction;
+- (WOResponse *) expungeAction;
+- (WOResponse *) emptyTrashAction;
+- (WOResponse *) subscribeAction;
+- (WOResponse *) unsubscribeAction;
+- (WOResponse *) quotasAction;
 
 @end
 
index 3111e172b44d3bbd67e249e3b5669c4de55cdbf9..b4a184b847e69520e7a0af7c98fde8b73c5a286d 100644 (file)
@@ -58,7 +58,7 @@
       error = [connection createMailbox: folderName atURL: [co imap4URL]];
       if (error)
        {
-         [response setStatus: 403];
+         [response setStatus: 500];
          [response appendContentString: @"Unable to create folder."];
        }
       else
@@ -66,7 +66,7 @@
     }
   else
     {
-      [response setStatus: 403];
+      [response setStatus: 500];
       [response appendContentString: @"Missing 'name' parameter."];
     }
 
                          toURL: destURL];
       if (error)
        {
-         [response setStatus: 403];
+         [response setStatus: 500];
          [response appendContentString: @"Unable to rename folder."];
        }
       else
     }
   else
     {
-      [response setStatus: 403];
+      [response setStatus: 500];
       [response appendContentString: @"Missing 'name' parameter."];
     }
 
                      toURL: destURL];
   if (error)
     {
-      [response setStatus: 403];
+      [response setStatus: 500];
       [response appendContentString: @"Unable to move folder."];
     }
   else
   return response;
 }
 
+- (WOResponse *) expungeAction 
+{
+  NSException *error;
+  SOGoTrashFolder *co;
+  WOResponse *response;
+
+  co = [self clientObject];
+  response = [context response];
+
+  error = [co expunge];
+  if (error)
+    {
+      [response setStatus: 500];
+      [response appendContentString: @"Unable to expunge folder."];
+    }
+  else
+    {
+      [co flushMailCaches];
+      [response setStatus: 204];
+    }
+
+  return response;
+}
+
 - (WOResponse *) emptyTrashAction 
 {
   NSException *error;
     }
   if (error)
     {
-      [response setStatus: 403];
+      [response setStatus: 500];
       [response appendContentString: @"Unable to empty the trash folder."];
     }
   else
     }
   else
     {
-      [response setStatus: 403];
+      [response setStatus: 500];
       [response appendContentString: @"How did you end up here?"];
     }
 
diff --git a/UI/MailerUI/UIxMailForwardAction.m b/UI/MailerUI/UIxMailForwardAction.m
deleted file mode 100644 (file)
index ba59391..0000000
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <Foundation/NSDictionary.h>
-#import <Foundation/NSString.h>
-
-#import <NGExtensions/NSNull+misc.h>
-
-#import <SoObjects/Mailer/SOGoMailObject.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-
-#import "UIxMailEditorAction.h"
-
-@interface UIxMailForwardAction : UIxMailEditorAction
-@end
-
-
-@implementation UIxMailForwardAction
-
-- (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, *attachment;
-  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 = [newDraft storeInfo:info]) != nil)
-    return error;
-
-  /* attach message */
-  
-  // TODO: use subject for filename?
-//   error = [newDraft saveAttachment:content withName:@"forward.mail"];
-  attachment = [NSDictionary dictionaryWithObjectsAndKeys:
-                              @"forward.mail", @"filename",
-                            @"message/rfc822", @"mime-type",
-                            nil];
-  error = [newDraft saveAttachment: content
-                   withMetadata: attachment];
-  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 /* UIxMailForwardAction */
index 41ba101bbc588655e58c9831014509a1c6a08e8b..ae2758675fbcf2660386a2ec542f1ccff2d8c2a5 100644 (file)
@@ -567,30 +567,6 @@ static int attachmentFlagSize = 8096;
   return [self redirectToLocation:@"view"];
 }
 
-- (id) expungeAction 
-{
-  // TODO: we might want to flush the caches?
-  NSException *error;
-  id clientObject;
-
-  clientObject = [self clientObject];
-  if (clientObject)
-    {
-      error = [clientObject expunge];
-      if (!error)
-       {
-         if ([clientObject respondsToSelector: @selector(flushMailCaches)])
-           [clientObject flushMailCaches];
-         return [self redirectToLocation:@"view"];
-       }
-    }
-  else
-    error = [NSException exceptionWithHTTPStatus:404 /* Not Found */
-                        reason: @"did not find mail folder"];
-  
-  return error;
-}
-
 @end
 
 /* UIxMailListView */
index 2de7560993f74a070f3df1c09194e3db1a09713d..70bf0198c2a51893d0cf2abb573ab9262389fa0f 100644 (file)
@@ -1,4 +1,4 @@
-/* UIxMailMainFrame.h - this file is part of $PROJECT_NAME_HERE$
+/* UIxMailMainFrame.h - this file is part of SOGo
  *
  * Copyright (C) 2006 Inverse groupe conseil
  *
 #ifndef UIXMAILMAINFRAME_H
 #define UIXMAILMAINFRAME_H
 
-#import "../Common/UIxPageFrame.h"
+#import "../SOGoUI/UIxComponent.h"
 
-@interface UIxMailMainFrame : UIxPageFrame
-{
-  NSString *rootURL;
-  NSString *userRootURL;
-  struct {
-    int hideFolderTree:1;
-    int reserved:31;
-  } mmfFlags;
-}
-
-- (NSString *)rootURL;
-- (NSString *)userRootURL;
-- (NSString *)calendarRootURL;
+@interface UIxMailMainFrame : UIxComponent
 
 @end
 
index 95c64ca83b7378b6f1cee5a57fcea81c7e1c8549..9ad78fb1ed240a4b4e96bb6fbf8b108a417f7faa 100644 (file)
 
 #import <SoObjects/Mailer/SOGoMailObject.h>
 #import <SoObjects/Mailer/SOGoMailAccounts.h>
-#import <SoObjects/SOGo/NSString+Utilities.h>
-#import <SoObjects/SOGo/NSObject+Utilities.h>
+#import <SoObjects/SOGo/NSArray+Utilities.h>
+#import <SoObjects/SOGo/SOGoUser.h>
 #import <SOGoUI/UIxComponent.h>
 
 #import "UIxMailMainFrame.h"
 
 @implementation UIxMailMainFrame
 
-static NSString *treeRootClassName = nil;
-
-+ (void)initialize {
-  NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
-  
-  treeRootClassName = [[ud stringForKey:@"SOGoMailTreeRootClass"] copy];
-  if (treeRootClassName)
-    NSLog(@"Note: use class '%@' as root for mail tree.", treeRootClassName);
-  else
-    treeRootClassName = @"SOGoMailAccounts";
-}
-
-- (void)dealloc {
-  [self->rootURL     release];
-  [self->userRootURL release];
-  [super dealloc];
-}
-
 /* accessors */
 - (NSString *) mailAccounts
 {
-  SOGoMailAccounts *co;
+  NSArray *accounts, *accountNames;
 
-  co = [self clientObject];
+  accounts = [[context activeUser] mailAccounts];
+  accountNames = [accounts objectsForKey: @"name"];
 
-  return [[co fetchAllIdentities] jsonRepresentation];
+  return [accountNames jsonRepresentation];
 }
 
-- (NSString *)treeRootClassName {
-  return treeRootClassName;
-}
-
-- (void)setHideFolderTree:(BOOL)_flag {
-   self->mmfFlags.hideFolderTree = _flag ? 1 : 0;
-}
-- (BOOL)hideFolderTree {
-  return self->mmfFlags.hideFolderTree ? YES : NO;
-}
-
-- (NSString *) pageFormURL {
+- (NSString *) pageFormURL
+{
   NSString *u;
   NSRange  r;
   
@@ -117,136 +90,4 @@ static NSString *treeRootClassName = nil;
   return [u hasSuffix:@"/"] ? @"view" : @"#";
 }
 
-- (BOOL)showLinkBanner {
-  return YES;
-}
-
-- (NSString *)bannerToolbarStyle {
-  return nil;
-}
-
-- (NSString *)bannerConsumeStyle {
-  return nil;
-}
-
-/* URL generation */
-// TODO: I think all this should be done by the clientObject?!
-// TODO: is the stuff below necessary at all in the mailer frame?
-
-- (NSString *)rootURL {
-  WOContext *ctx;
-  NSArray   *traversalObjects;
-
-  if (self->rootURL != nil)
-    return self->rootURL;
-
-  ctx = [self context];
-  traversalObjects = [ctx objectTraversalStack];
-  self->rootURL = [[[traversalObjects objectAtIndex:0]
-                                      rootURLInContext:ctx]
-                                      copy];
-  return self->rootURL;
-}
-
-- (NSString *)userRootURL {
-  WOContext *ctx;
-  NSArray   *traversalObjects;
-
-  if (self->userRootURL)
-    return self->userRootURL;
-
-  ctx = [self context];
-  traversalObjects = [ctx objectTraversalStack];
-  self->userRootURL = [[[[traversalObjects objectAtIndex:1]
-                                           baseURLInContext:ctx]
-                                           stringByAppendingString:@"/"]
-                                           retain];
-  return self->userRootURL;
-}
-
-- (NSString *)calendarRootURL {
-  return [[self userRootURL] stringByAppendingString:@"Calendar/"];
-}
-
-- (NSString *)contactsRootURL {
-  return [[self userRootURL] stringByAppendingString:@"Contacts/"];
-}
-
-/* error handling */
-
-- (BOOL)hasErrorText {
-  return [[[[self context] request] formValueForKey:@"error"] length] > 0
-    ? YES : NO;
-}
-- (NSString *)errorText {
-  return [[[self context] request] formValueForKey:@"error"];
-}
-
-- (NSString *)errorAlertJavaScript {
-  NSString *errorText;
-  
-  if ([(errorText = [self errorText]) length] == 0)
-    return nil;
-  
-  // TODO: proper JavaScript escaping
-  errorText = [errorText stringByEscapingHTMLString];
-  errorText = [errorText stringByReplacingString:@"\"" withString:@"'"];
-  
-  return [NSString stringWithFormat:
-                    @"<script language=\"JavaScript\">"
-                    @"alert(\"%@\");"
-                    @"</script>", errorText];
-}
-
-/* FIXME: migrated methods which might not work yet... */
-// #warning check this
-// - (NSString *) mailFolderName
-// {
-//   NSMutableArray *mailboxes;
-//   SOGoMailObject *currentObject;
-
-//   mailboxes = [NSMutableArray new];
-//   [mailboxes autorelease];
-
-//   currentObject = [self clientObject];
-//   while (![currentObject isKindOfClass: [SOGoMailAccounts class]])
-//     {
-//       [mailboxes insertObject: [currentObject nameInContainer] atIndex: 0];
-//       currentObject = [currentObject container];
-//     }
-
-//   return [NSString stringWithFormat: @"/%@",
-//                    [mailboxes componentsJoinedByString: @"/"]];
-// }
-
-- (id) composeAction
-{
-  NSArray *c;
-  NSString *inbox, *url, *parameter;
-  NSMutableDictionary *urlParams;
-  id actionResult;
-
-  c = [[self clientObject] toManyRelationshipKeys];
-  if ([c count] > 0)
-    {
-      urlParams = [NSMutableDictionary new];
-      [urlParams autorelease];
-
-      parameter = [self queryParameterForKey: @"mailto"];
-      if (parameter)
-        [urlParams setObject: parameter
-                   forKey: @"mailto"];
-      inbox = [NSString stringWithFormat: @"%@/folderINBOX",
-                        [c objectAtIndex: 0]];
-      url = [inbox composeURLWithAction: @"compose"
-                   parameters: urlParams
-                   andHash: NO];
-      actionResult = [self redirectToLocation: url];
-    }
-  else
-    actionResult = self;
-
-  return actionResult;
-}
-
 @end /* UIxMailMainFrame */
diff --git a/UI/MailerUI/UIxMailReplyAction.m b/UI/MailerUI/UIxMailReplyAction.m
deleted file mode 100644 (file)
index ac3e13b..0000000
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
-  Copyright (C) 2004-2005 SKYRIX Software AG
-
-  This file is part of OpenGroupware.org.
-
-  OGo is free software; you can redistribute it and/or modify it under
-  the terms of the GNU Lesser General Public License as published by the
-  Free Software Foundation; either version 2, or (at your option) any
-  later version.
-
-  OGo is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or
-  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
-  License for more details.
-
-  You should have received a copy of the GNU Lesser General Public
-  License along with OGo; see the file COPYING.  If not, write to the
-  Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-  02111-1307, USA.
-*/
-
-#import <Foundation/NSArray.h>
-#import <Foundation/NSString.h>
-#import <NGExtensions/NSNull+misc.h>
-#import <NGExtensions/NSString+misc.h>
-#import <SoObjects/Mailer/SOGoMailObject.h>
-#import <SoObjects/Mailer/SOGoDraftObject.h>
-#import <NGImap4/NGImap4EnvelopeAddress.h>
-#import <NGImap4/NGImap4Envelope.h>
-
-#import "UIxMailEditorAction.h"
-
-@interface UIxMailReplyAction : UIxMailEditorAction
-@end
-
-@implementation UIxMailReplyAction
-
-- (BOOL)hasReplyPrefix:(NSString *)_subject {
-  static NSString *replyPrefixes[] = {
-    @"Re:", // regular
-    @"RE:", // Outlook v11 (English?)
-    @"AW:", // German Outlook v11
-    @"Re[", // numbered Re, eg "Re[2]:"
-    nil
-  };
-  unsigned i;
-  for (i = 0; replyPrefixes[i] != nil; i++) {
-    if ([_subject hasPrefix:replyPrefixes[i]])
-      return YES;
-  }
-  return NO;
-}
-
-- (NSString *)replySubject:(NSString *)_subject {
-  if (![_subject isNotNull] || [_subject length] == 0)
-    return _subject;
-  
-  if ([self hasReplyPrefix:_subject]) {
-    /* do not do: "Re: Re: Re: My Mail" - a single Re is sufficient ;-) */
-    return _subject;
-  }
-  
-  return [@"Re: " stringByAppendingString:_subject];
-}
-
-- (void)addEMailsOfAddresses:(NSArray *)_addrs toArray:(NSMutableArray *)_ma {
-  unsigned i, count;
-  
-  for (i = 0, count = [_addrs count]; i < count; i++)
-    [_ma addObject:[(NGImap4EnvelopeAddress *)[_addrs objectAtIndex:i] email]];
-}
-
-- (void)fillInReplyAddresses:(NSMutableDictionary *)_info
-  replyToAll:(BOOL)_replyToAll
-  envelope:(NGImap4Envelope *)_envelope
-{
-  /*
-    The rules as implemented by Thunderbird:
-    - if there is a 'reply-to' header, only include that (as TO)
-    - if we reply to all, all non-from addresses are added as CC
-    - the from is always the lone TO (except for reply-to)
-    
-    Note: we cannot check reply-to, because Cyrus even sets a reply-to in the
-          envelope if none is contained in the message itself! (bug or
-          feature?)
-    
-    TODO: what about sender (RFC 822 3.6.2)
-  */
-  NSMutableArray *to;
-  NSArray *addrs;
-  
-  to = [NSMutableArray arrayWithCapacity:2];
-
-  /* first check for "reply-to" */
-  
-  addrs = [_envelope replyTo];
-  if ([addrs count] == 0) {
-    /* no "reply-to", try "from" */
-    addrs = [_envelope from];
-  }
-  [self addEMailsOfAddresses:addrs toArray:to];
-  [_info setObject:to forKey:@"to"];
-  
-  /* CC processing if we reply-to-all: add all 'to' and 'cc'  */
-  
-  if (_replyToAll) {
-    to = [NSMutableArray arrayWithCapacity:8];
-    
-    [self addEMailsOfAddresses:[_envelope to] toArray:to];
-    [self addEMailsOfAddresses:[_envelope cc] toArray:to];
-    
-    [_info setObject:to forKey:@"cc"];
-  }
-}
-
-- (NSString *) contentForReplyOnParts: (NSDictionary *) _prts
-                                 keys: (NSArray *) _k
-{
-  static NSString *textPartSeparator = @"\n---\n";
-  NSMutableString *ms;
-  unsigned i, count;
-  
-  ms = [NSMutableString stringWithCapacity:16000];
-  for (i = 0, count = [_k count]; i < count; i++) {
-    NSString *k, *v;
-    
-    k = [_k objectAtIndex:i];
-   
-    // 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]]) {
-      [self logWithFormat:@"Note: cannot show part %@", k];
-      continue;
-    }
-    if ([v length] == 0)
-      continue;
-    
-    if (i != 0) [ms appendString:textPartSeparator];
-    [ms appendString:[v stringByApplyingMailQuoting]];
-  }
-  return ms;
-}
-
-- (NSString *)contentForReply {
-  NSArray      *keys, *partInfos;
-  NSDictionary *parts, *infos;
-
-  keys = [[self clientObject] plainTextContentFetchKeys];
-//   SOGoMailObject *co;
-
-//   co = [self clientObject];
-//   keys = [co plainTextContentFetchKeys];
-//   infos = [co fetchCoreInfos];
-//   partInfos = [infos objectForKey: keys];
-//   NSLog (@"infos: '%@'", infos);
-
-  if ([keys count] == 0)
-    return nil;
-  
-  if ([keys count] > 1) {
-    /* filter keys, only include top-level, or if none, the first */
-    NSMutableArray *topLevelKeys = nil;
-    unsigned i;
-    
-    for (i = 0; i < [keys count]; i++) {
-      NSRange r;
-      
-      r = [[keys objectAtIndex:i] rangeOfString:@"."];
-      if (r.length > 0)
-       continue;
-      
-      if (topLevelKeys == nil) 
-       topLevelKeys = [NSMutableArray arrayWithCapacity:4];
-      [topLevelKeys addObject:[keys objectAtIndex:i]];
-    }
-    
-    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]];
-    }
-  }
-  
-  parts = [[self clientObject] fetchPlainTextStrings:keys];
-  return [self contentForReplyOnParts:parts keys:keys];
-}
-
-- (id)replyToAll:(BOOL)_replyToAll {
-  NSMutableDictionary *info;
-  NSException *error;
-  id result;
-  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;
-  
-  /* fill draft info */
-  
-  info = [NSMutableDictionary dictionaryWithCapacity:16];
-  
-  [info setObject:[self replySubject:[[self clientObject] subject]]
-       forKey:@"subject"];
-  [self fillInReplyAddresses:info replyToAll:_replyToAll 
-       envelope:[[self clientObject] envelope]];
-  
-  /* fill in text content */
-  
-  if ((tmp = [self contentForReply]) != nil)
-    [info setObject:tmp forKey:@"text"];
-  
-  /* save draft info */
-
-  if ((error = [self->newDraft storeInfo:info]) != 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;
-}
-
-- (id)replyAction {
-  return [self replyToAll:NO];
-}
-- (id)replyallAction {
-  return [self replyToAll:YES];
-}
-
-@end /* UIxMailReplyAction */
index a8c01a0bc34fd63228a8f10dfdba693b7ff7fbfc..1eb40bc79b8ad0dc7744929a4e657daa63648e9f 100644 (file)
@@ -187,6 +187,11 @@ static NSString *mailETag = nil;
   return NO;
 }
 
+- (BOOL) mailIsDraft
+{
+  return [[self clientObject] isInDraftsFolder];
+}
+
 - (id) redirectToParentFolder
 {
   id url;
index 8a3b1331aef95b415f035f8deff45dfdaadea5a1..f974b07284cfa58df06eda0d753c29fbd4179ce9 100644 (file)
@@ -133,8 +133,8 @@ categories = {
         };
         expunge = {
            protectedBy = "View";
-           pageName    = "UIxMailListView";
-           actionName  = "expunge";
+           actionClass = "UIxMailFolderActions";
+           actionName = "emptyTrash";
         };
         createFolder = {
            protectedBy = "View";
@@ -151,11 +151,6 @@ categories = {
            actionClass = "UIxMailFolderActions";
            actionName = "deleteFolder";
         };
-        compose = {
-           protectedBy = "View";
-           actionClass = "UIxMailEditorAction";
-           actionName  = "compose";
-        };
         userRights = {
            protectedBy = "ReadAcls";
            pageName    = "UIxMailUserRightsEditor";
@@ -228,26 +223,22 @@ categories = {
         };
         edit = {
            protectedBy = "View";
-           pageName    = "UIxMailEditor";
-        };
-        compose = {
-           protectedBy = "View";
-           actionClass = "UIxMailEditorAction";
-           actionName  = "compose";
+           actionClass = "UIxMailActions";
+           actionName = "edit";
         };
         reply = {
            protectedBy = "View";
-           actionClass = "UIxMailReplyAction";
+           actionClass = "UIxMailActions";
            actionName  = "reply";
         };
         replyall = {
            protectedBy = "View";
-           actionClass = "UIxMailReplyAction";
-           actionName  = "replyall";
+           actionClass = "UIxMailActions";
+           actionName  = "replyToAll";
         };
         forward = {
            protectedBy = "View";
-           actionClass = "UIxMailForwardAction";
+           actionClass = "UIxMailActions";
            actionName  = "forward";
         };
       };
@@ -265,11 +256,6 @@ categories = {
            protectedBy = "View";
            pageName    = "UIxMailMainFrame";
         };
-        compose = {
-           protectedBy = "View";
-           pageName    = "UIxMailMainFrame";
-           actionName  = "compose";
-        };
         getMail = {
            protectedBy = "View";
            pageName    = "UIxMailAccountsView";
@@ -287,7 +273,7 @@ categories = {
       methods = {
         compose = {
            protectedBy = "View";
-           actionClass = "UIxMailEditorAction";
+           actionClass = "UIxMailAccountActions";
            actionName  = "compose";
         };
         mailboxes = {
@@ -331,11 +317,6 @@ categories = {
            protectedBy = "View";
            pageName    = "UIxMailListView";
         };
-        compose = {
-           protectedBy = "View";
-           actionClass = "UIxMailEditorAction";
-           actionName  = "compose";
-        };
       };
    };
 
@@ -347,10 +328,6 @@ categories = {
         };
       };
       methods = {
-        view = { /* somewhat hackish */
-            protectedBy = "View";
-           pageName    = "UIxMailView";
-        };
         edit = {
            protectedBy = "View";
            pageName    = "UIxMailEditor";
@@ -360,27 +337,6 @@ categories = {
            pageName    = "UIxMailEditor";
            actionName  = "save";
         };
-        delete = {
-           protectedBy = "View";
-           pageName    = "UIxMailEditor";
-           actionName  = "delete";
-        };
-
-        viewAttachments = {
-           protectedBy = "View";
-           pageName    = "UIxMailEditorAttach";
-           actionName  = "viewAttachments";
-        };
-        attach = {
-           protectedBy = "View";
-           pageName    = "UIxMailEditorAttach";
-           actionName  = "attach";
-        };
-        deleteAttachment = {
-           protectedBy = "View";
-           pageName    = "UIxMailEditorAttach";
-           actionName  = "deleteAttachment";
-        };
         send = {
            protectedBy = "View";
            pageName    = "UIxMailEditor";
index 6ea51ebe9cd19d2f9cf8e15477c809dee2cc7eb7..7d053e5b534d140ff959a533ce360d5adc97cbed 100644 (file)
@@ -18,8 +18,10 @@ MainUI_OBJC_FILES += \
 MainUI_RESOURCE_FILES +=       \
        Version                 \
        product.plist           \
+       AgenorProfile.sql \
+       AgenorProfile-oracle.sql \
        OCSFolderInfo.sql \
-       AgenorProfile.sql
+       OCSFolderInfo-oracle.sql \
 
 MainUI_LOCALIZED_RESOURCE_FILES += \
        Locale Localizable.strings
index aefddbdd26bdd99ee508eeddeb39d91af206d466..31b42ad6c997ceb0058b89e2e199cdb374719117 100644 (file)
   NGMimeMessage *message;
   NGMimeMultipartBody *body;
   SOGoUser *activeUser;
+  NSDictionary *identity;
+  NSString *from, *fullMail;
 
   activeUser = [context activeUser];
-  recipient = [[LDAPUserManager sharedUserManager] getFullEmailForUID: recipientUID];
+  identity = [activeUser primaryIdentity];
+  from = [identity objectForKey: @"email"];
+  fullMail = [NSString stringWithFormat: @"%@ <%@>",
+                      [identity objectForKey: @"fullName"], from];
+
+  recipient = [[LDAPUserManager sharedUserManager]
+               getFullEmailForUID: recipientUID];
 
   headerMap = [NGMutableHashMap hashMapWithCapacity: 5];
   [headerMap setObject: @"multipart/alternative" forKey: @"content-type"];
-  [headerMap setObject: [activeUser fullEmail] forKey: @"From"];
+  [headerMap setObject: fullMail forKey: @"From"];
   [headerMap setObject: recipient forKey: @"To"];
   date = [[NSCalendarDate date] rfc822DateString];
   [headerMap setObject: date forKey: @"Date"];
 
   [[SOGoMailer sharedMailer] sendMimePart: message
                             toRecipients: [NSArray arrayWithObject: recipient]
-                            sender: [activeUser primaryEmail]];
+                            sender: from];
 }
 
 @end
index fd5b0f2441a2d0ed3f86bd6f633dfa2acf6e00f5..62fb66fe12b92e3e0c5a74e7b0502a484f184373 100644 (file)
@@ -100,23 +100,20 @@ static BOOL shouldDisplayWeekend = NO;
 - (NSArray *) filterAppointments:(NSArray *) _apts
 {
   NSMutableArray *filtered;
-  unsigned i, count;
-  NSString *email;
-  NSDictionary *info;
-  NSArray *partmails;
-  unsigned p, pCount;
+  unsigned i, count, p, pCount;
+  NSString *email, *partmailsString, *state, *pEmail;
+  NSDictionary *info, *primaryIdentity;
+  NSArray *partmails, *partstates;
   BOOL shouldAdd;
-  NSString *partmailsString;
-  NSArray  *partstates;
-  NSString *state;
-  NSString *pEmail;
 
   if ([self shouldDisplayRejectedAppointments])
     return _apts;
   {
     count = [_apts count];
     filtered = [[[NSMutableArray alloc] initWithCapacity: count] autorelease];
-    email = [[context activeUser] primaryEmail];
+
+    primaryIdentity = [[context activeUser] primaryIdentity];
+    email = [primaryIdentity objectForKey: @"email"];
 
     for (i = 0; i < count; i++)
       {
index 08ef20929f68b0aa5af8a5d43c9af3f5c42b553b..cec6bbd1a6df956bff303a65c56be2623f22d863 100644 (file)
 {
   NSString *organizerEmail;
   SOGoUser *activeUser;
+  NSDictionary *primaryIdentity;
 
   organizerEmail = [[component organizer] email];
   if ([organizerEmail length] == 0)
        {
          ASSIGN (organizer, [iCalPerson elementWithTag: @"organizer"]);
          activeUser = [context activeUser];
+         primaryIdentity = [activeUser primaryIdentity];
          [organizer setCn: [activeUser cn]];
-         [organizer setEmail: [activeUser primaryEmail]];
+         [organizer setEmail: [primaryIdentity objectForKey: @"email"]];
          [component setOrganizer: organizer];
        }
     }
index 79df8ddf77b513d1272e13d051fe794d5f95593e..bbaa16612574fa7660c679d452076d594d7bb539 100644 (file)
@@ -29,7 +29,7 @@
         <var:foreach list="contactInfos" item="currentContact">
           <tr class="tableview"
             var:id="currentContact.c_name"
-            var:contactname="displayName"
+            var:contactname="currentContact.displayName"
             var:contactid="currentContact.c_uid"
             onclick="return onContactRowClick(event, this);"
             ondblclick="return onContactRowDblClick(event, this);"
index af0ff5b215f9403f933988e045f2fdc7528f7dab..229a502b1c39adbe12964d73d72efdee9bdd78a2 100644 (file)
@@ -1,58 +1,59 @@
 <?xml version='1.0' standalone='yes'?>
-  <var:component
-    xmlns="http://www.w3.org/1999/xhtml"
-    xmlns:var="http://www.skyrix.com/od/binding"
-    xmlns:const="http://www.skyrix.com/od/constant"
-    xmlns:uix="OGo:uix"
-    xmlns:rsrc="OGo:url"
-    xmlns:label="OGo:label"
-    className="UIxPageFrame"
-    title="panelTitle"
-    const:popup="YES">
-    <div class="menu" id="attachmentsMenu">
-      <ul>
-        <li><var:string label:value="Open"/></li>
-        <li><var:string label:value="Delete" /></li>
-        <li><var:string label:value="Select All" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Attach File(s)..." /></li>
-        <li><var:string label:value="Attach Web Page..." /></li>
-      </ul>
-    </div>
+<!DOCTYPE var:component>
+<var:component
+  xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:uix="OGo:uix"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label"
+  className="UIxPageFrame"
+  title="panelTitle"
+  const:popup="YES">
+  <div class="menu" id="attachmentsMenu">
+    <ul>
+      <li><var:string label:value="Open"/></li>
+      <li><var:string label:value="Delete" /></li>
+      <li><var:string label:value="Select All" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Attach File(s)..." /></li>
+      <li><var:string label:value="Attach Web Page..." /></li>
+    </ul>
+  </div>
 
-    <form name="pageform" enctype="multipart/form-data">
-      <div id="headerArea">
-        <div id="attachmentsArea">
-          <var:string label:value="Attachments:" />
-          <ul id="attachments">
-            <var:foreach list="attachmentNames" item="attachmentName"
-             ><img rsrc:img="attachment.gif"
-               /><var:string value="attachmentName"
-               /></var:foreach>
-         </ul>
-        </div>
-        <span class="headerField"><var:string label:value="From" />:</span>
-        <var:popup const:name="from"
-          list="fromEMails"
-          item="item"
-          selection="from"
-          /><br />
-        <div>
-          <var:component className="UIxMailToSelection"
-            to="to" cc="cc" bcc="bcc" />
-        </div>
-        <div class="addressListElement" id="subjectRow"
-          ><span class="headerField"><var:string label:value="Subject"
-              />:</span
-            ><span class="headerInput"
-            ><input name="subject"
-              type="text"
-              class="textField"
-              var:value="subject"
-              /></span></div>
+  <form name="pageform" enctype="multipart/form-data">
+    <div id="headerArea">
+      <div id="attachmentsArea">
+       <var:string label:value="Attachments:" />
+       <ul id="attachments">
+         <var:foreach list="attachmentNames" item="attachmentName"
+           ><img rsrc:img="attachment.gif"
+             /><var:string value="attachmentName"
+             /></var:foreach>
+       </ul>
+      </div>
+      <span class="headerField"><var:string label:value="From" />:</span>
+      <var:popup const:name="from"
+       list="fromEMails"
+       item="item"
+       selection="from"
+       /><br />
+      <div>
+       <var:component className="UIxMailToSelection"
+         to="to" cc="cc" bcc="bcc" />
       </div>
+      <div class="addressListElement" id="subjectRow"
+       ><span class="headerField"><var:string label:value="Subject"
+           />:</span
+         ><span class="headerInput"
+         ><input name="subject"
+           type="text"
+           class="textField"
+           var:value="subject"
+           /></span></div>
+    </div>
     <!-- separator line -->
-      <textarea name="text" var:value="text" />
+    <textarea name="text" rows="30" var:value="text" />
     <!-- img rsrc:src="tbird_073_compose.png" alt="screenshot" / -->
-    </form>
-  </var:component>
+  </form>
+</var:component>
index 47c56d98577fa55f7ddac947107db11b1f2103a9..e389db9c074c036c1838ad5ab5c8499fd652d8ed 100644 (file)
 <?xml version="1.0" standalone="yes"?>
-  <var:component xmlns="http://www.w3.org/1999/xhtml"
-    xmlns:var="http://www.skyrix.com/od/binding"
-    xmlns:const="http://www.skyrix.com/od/constant"
-    xmlns:rsrc="OGo:url"
-    xmlns:label="OGo:label"
-    className="UIxPageFrame"
-    title="title"
-    popup="isPopup"
-    >
-    <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
-    <script type="text/javascript" rsrc:src="MailerUI+dTree.js"><!-- space --></script>
-    <script type="text/javascript">
-      var mailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>'.evalJSON(true);
-    </script>
-    <div class="menu" id="accountIconMenu">
-      <ul>
-        <li><var:string label:value="Subscribe..." /></li>
-        <li><var:string label:value="Get Messages for Account" /></li>
-        <li><var:string label:value="New Folder..." /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Search Messages..." /></li>
-        <li><var:string label:value="Properties..." /></li>
-      </ul>
-    </div>
+<!DOCTYPE var:component>
+<var:component xmlns="http://www.w3.org/1999/xhtml"
+  xmlns:var="http://www.skyrix.com/od/binding"
+  xmlns:const="http://www.skyrix.com/od/constant"
+  xmlns:rsrc="OGo:url"
+  xmlns:label="OGo:label"
+  className="UIxPageFrame"
+  title="title"
+  >
+  <script type="text/javascript" rsrc:src="dtree.js"><!-- space --></script>
+  <script type="text/javascript" rsrc:src="MailerUI+dTree.js"><!-- space --></script>
+  <script type="text/javascript">
+    var mailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>'.evalJSON(true);
+  </script>
+  <div class="menu" id="accountIconMenu">
+    <ul>
+      <li><var:string label:value="Subscribe..." /></li>
+      <li><var:string label:value="Get Messages for Account" /></li>
+      <li><var:string label:value="New Folder..." /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Search Messages..." /></li>
+      <li><var:string label:value="Properties..." /></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="inboxIconMenu">
-      <ul>
-        <li><var:string label:value="Open in New Mail Window" /></li>
-        <li><var:string label:value="Copy Folder Location" /></li>
-        <li><var:string label:value="Subscribe..." /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Mark Folder Read..." /></li>
-        <li><var:string label:value="New Folder..." /></li>
-        <li><var:string label:value="Compact This Folder" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Search Messages..." /></li>
-        <li><var:string label:value="Sharing..." /></li>
-      </ul>
-    </div>
-      
-    <div class="menu" id="trashIconMenu">
-      <ul>
-        <li><var:string label:value="Open in New Mail Window" /></li>
-        <li><var:string label:value="Copy Folder Location" /></li>
-        <li><var:string label:value="Subscribe..." /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Mark Folder Read..." /></li>
-        <li><var:string label:value="New Subfolder..." /></li>
-        <li><var:string label:value="Compact This Folder" /></li>
-        <li><var:string label:value="Empty Trash" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Search Messages..." /></li>
-        <li><var:string label:value="Sharing..." /></li>
-      </ul>
-    </div>
+  <div class="menu" id="inboxIconMenu">
+    <ul>
+      <li><var:string label:value="Open in New Mail Window" /></li>
+      <li><var:string label:value="Copy Folder Location" /></li>
+      <li><var:string label:value="Subscribe..." /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Mark Folder Read..." /></li>
+      <li><var:string label:value="New Folder..." /></li>
+      <li><var:string label:value="Compact This Folder" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Search Messages..." /></li>
+      <li><var:string label:value="Sharing..." /></li>
+    </ul>
+  </div>
+  
+  <div class="menu" id="trashIconMenu">
+    <ul>
+      <li><var:string label:value="Open in New Mail Window" /></li>
+      <li><var:string label:value="Copy Folder Location" /></li>
+      <li><var:string label:value="Subscribe..." /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Mark Folder Read..." /></li>
+      <li><var:string label:value="New Subfolder..." /></li>
+      <li><var:string label:value="Compact This Folder" /></li>
+      <li><var:string label:value="Empty Trash" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Search Messages..." /></li>
+      <li><var:string label:value="Sharing..." /></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="mailboxIconMenu">
-      <ul>
-        <li><var:string label:value="Open in New Mail Window" /></li>
-        <li><var:string label:value="Copy Folder Location" /></li>
-        <li><var:string label:value="Subscribe..." /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Mark Folder Read..." /></li>
-        <li><var:string label:value="New Subfolder..." /></li>
-        <li><var:string label:value="Rename Folder..." /></li>
-        <li><var:string label:value="Compact This Folder" /></li>
-        <li><var:string label:value="Delete Folder" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Search Messages..." /></li>
-       <li><var:string label:value="Sharing..." /></li>
-      </ul>
-    </div>
-    
-    <div class="menu" id="addressMenu">
-      <ul>
-        <li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li>
-        <li id="compose_mailto"><var:string label:value="Compose Mail To"/></li>
-        <li id="create_filter"><var:string label:value="Create Filter From Message..."/></li>
-      </ul>
-    </div>
+  <div class="menu" id="mailboxIconMenu">
+    <ul>
+      <li><var:string label:value="Open in New Mail Window" /></li>
+      <li><var:string label:value="Copy Folder Location" /></li>
+      <li><var:string label:value="Subscribe..." /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Mark Folder Read..." /></li>
+      <li><var:string label:value="New Subfolder..." /></li>
+      <li><var:string label:value="Rename Folder..." /></li>
+      <li><var:string label:value="Compact This Folder" /></li>
+      <li><var:string label:value="Delete Folder" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Search Messages..." /></li>
+      <li><var:string label:value="Sharing..." /></li>
+    </ul>
+  </div>
+  
+  <div class="menu" id="addressMenu">
+    <ul>
+      <li id="add_to_addressbook"><var:string label:value="Add to Address Book..."/></li>
+      <li id="compose_mailto"><var:string label:value="Compose Mail To"/></li>
+      <li id="create_filter"><var:string label:value="Create Filter From Message..."/></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="messageListMenu">
-      <ul>
-        <li><var:string label:value="Open Message In New Window"/></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Reply to Sender Only"/></li>
-        <li><var:string label:value="Reply to All"/></li>
-        <li><var:string label:value="Forward"/></li>
-        <li><var:string label:value="Edit As New..."/></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Move To"/></li>
-        <li><var:string label:value="Copy To"/></li>
-        <li><var:string label:value="Label"/></li>
-        <li><var:string label:value="Mark"/></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Save As..."/></li>
-        <li><var:string label:value="View Message Source"/></li>
-        <li><var:string label:value="Print Preview"/></li>
-        <li><var:string label:value="Print..."/></li>
-        <li><var:string label:value="Delete Message"/></li>
-      </ul>
-    </div>
+  <div class="menu" id="messageListMenu">
+    <ul>
+      <li><var:string label:value="Open Message In New Window"/></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Reply to Sender Only"/></li>
+      <li><var:string label:value="Reply to All"/></li>
+      <li><var:string label:value="Forward"/></li>
+      <li><var:string label:value="Edit As New..."/></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Move To"/></li>
+      <li><var:string label:value="Copy To"/></li>
+      <li><var:string label:value="Label"/></li>
+      <li><var:string label:value="Mark"/></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Save As..."/></li>
+      <li><var:string label:value="View Message Source"/></li>
+      <li><var:string label:value="Print Preview"/></li>
+      <li><var:string label:value="Print..."/></li>
+      <li><var:string label:value="Delete Message"/></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="messageContentMenu">
-      <ul>
-        <li><var:string label:value="Reply to Sender Only"/></li>
-        <li><var:string label:value="Reply to All"/></li>
-        <li><var:string label:value="Forward"/></li>
-        <li><var:string label:value="Edit As New..."/></li>
-        <li><var:string label:value="Move To"/></li>
-        <li><var:string label:value="Copy To"/></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Label"/></li>
-        <li><var:string label:value="Mark"/></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Save As..."/></li>
-        <li><var:string label:value="View Message Source"/></li>
-        <li><var:string label:value="Print Preview"/></li>
-        <li><var:string label:value="Print..."/></li>
-        <li><var:string label:value="Delete Message"/></li>
-      </ul>
-    </div>
+  <div class="menu" id="messageContentMenu">
+    <ul>
+      <li><var:string label:value="Reply to Sender Only"/></li>
+      <li><var:string label:value="Reply to All"/></li>
+      <li><var:string label:value="Forward"/></li>
+      <li><var:string label:value="Edit As New..."/></li>
+      <li><var:string label:value="Move To"/></li>
+      <li><var:string label:value="Copy To"/></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Label"/></li>
+      <li><var:string label:value="Mark"/></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Save As..."/></li>
+      <li><var:string label:value="View Message Source"/></li>
+      <li><var:string label:value="Print Preview"/></li>
+      <li><var:string label:value="Print..."/></li>
+      <li><var:string label:value="Delete Message"/></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="label-menu">
-      <ul id="">
-        <li><var:string label:value="None" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Important" /></li>
-        <li><var:string label:value="Work" /></li>
-        <li><var:string label:value="Personal" /></li>
-        <li><var:string label:value="To Do" /></li>
-        <li><var:string label:value="Later" /></li>
-      </ul>
-    </div>
+  <div class="menu" id="label-menu">
+    <ul id="">
+      <li><var:string label:value="None" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Important" /></li>
+      <li><var:string label:value="Work" /></li>
+      <li><var:string label:value="Personal" /></li>
+      <li><var:string label:value="To Do" /></li>
+      <li><var:string label:value="Later" /></li>
+    </ul>
+  </div>
 
-    <div class="menu" id="mark-menu">
-      <ul id="">
-        <li><var:string label:value="As Read" /></li>
-        <li><var:string label:value="Thread As Read" /></li>
-        <li><var:string label:value="As Read By Date..." /></li>
-        <li><var:string label:value="All Read" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="Flag" /></li>
-        <li><!-- separator --></li>
-        <li><var:string label:value="As Junk" /></li>
-        <li><var:string label:value="As Not Junk" /></li>
-        <li><var:string label:value="Run Junk Mail Controls" /></li>
-      </ul>
-    </div>
+  <div class="menu" id="mark-menu">
+    <ul id="">
+      <li><var:string label:value="As Read" /></li>
+      <li><var:string label:value="Thread As Read" /></li>
+      <li><var:string label:value="As Read By Date..." /></li>
+      <li><var:string label:value="All Read" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="Flag" /></li>
+      <li><!-- separator --></li>
+      <li><var:string label:value="As Junk" /></li>
+      <li><var:string label:value="As Not Junk" /></li>
+      <li><var:string label:value="Run Junk Mail Controls" /></li>
+    </ul>
+  </div>
 
-    <div id="leftPanel">
-      <div class="titlediv"><var:string label:value="Folders" /></div>
-      <div id="folderTreeContent"><!-- space --></div>
-    </div>
+  <div id="leftPanel">
+    <div class="titlediv"><var:string label:value="Folders" /></div>
+    <div id="folderTreeContent"><!-- space --></div>
+  </div>
 
-    <div class="dragHandle" id="verticalDragHandle"><!-- space --></div>
+  <div class="dragHandle" id="verticalDragHandle"><!-- space --></div>
 
-    <div id="rightPanel">
-      <var:component className="UIxMailFilterPanel" qualifier="qualifier" />
-      <div id="mailboxContent"><!-- space --></div>
-      <div class="dragHandle" id="rightDragHandle"><!-- space --></div>
-      <div id="messageContent"><!-- space --></div>
-    </div>
+  <div id="rightPanel">
+    <var:component className="UIxMailFilterPanel" qualifier="qualifier" />
+    <div id="mailboxContent"><!-- space --></div>
+    <div class="dragHandle" id="rightDragHandle"><!-- space --></div>
+    <div id="messageContent"><!-- space --></div>
+  </div>
 
-    <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
-  </var:component>
+  <var:string value="errorAlertJavaScript" const:escapeHTML="NO" />
+</var:component>
index fb45e713760dcdd82e8e52efe96ade8ecaa0b43e..8ae114005d8d73de8a5e5745da0857bbd1d2f191 100644 (file)
@@ -5,6 +5,10 @@
     xmlns:uix="OGo:uix"
     xmlns:rsrc="OGo:url"
     xmlns:label="OGo:label">
+    <var:if var:condition="mailIsDraft"
+      ><input const:name="editDraftButton" const:id="editDraftButton"
+       type="button" class="button" label:value="Edit Draft..."
+       /></var:if>
     <table class="mailer_fieldtable">
       <tr class="mailer_fieldrow">
         <td class="mailer_fieldname" ><var:string label:value="Subject"/>:</td>
index b2b87f6ade148ce67cb892586246d2f0636c0072..736fa087e619514410729acf85e7e78238293340 100644 (file)
@@ -17,7 +17,7 @@
        <meta name="description" content="SOGo Web Interface" />
        <meta name="author" content="SKYRIX Software AG/Inverse groupe conseil" />
        <meta name="robots" content="stop" />
-       <link href="mailto:supper@inverse.ca" rev="made" />
+       <link href="mailto:support@inverse.ca" rev="made" />
        <link rel="shortcut icon" rsrc:href="lori_16x16.ico" type="image/x-icon" />
        <link type="text/css" rel="stylesheet" rsrc:href="generic.css" />
        <link type="text/css" rel="stylesheet" rsrc:href="dtree.css" />
index 6774403010c4da56aa313b4f664d468ba90a4bb2..ab4ab2a12232fdaed2da9363ccf80a8309edc757 100644 (file)
@@ -33,14 +33,14 @@ function validateEditorInput(sender) {
 function openContactsFolder(contactsFolder, reload, idx) {
   if ((contactsFolder && contactsFolder != currentContactFolder)
       || reload) {
-     currentContactFolder = contactsFolder; log("openContactsFolder " + contactsFolder);
+     currentContactFolder = contactsFolder;
      var url = URLForFolderID(currentContactFolder) +
        "/view?noframe=1";
 
      var searchValue = search["value"];
      if (searchValue && searchValue.length > 0)
        url += ("&search=" + search["criteria"]
-               + "&value=" + searchValue);
+               + "&value=" + escape(searchValue));
      var sortAttribute = sorting["attribute"];
      if (sortAttribute && sortAttribute.length > 0)
        url += ("&sort=" + sorting["attribute"]
@@ -139,43 +139,46 @@ function contactsListCallback(http) {
 
 function onContactFoldersContextMenu(event) {
   var menu = $("contactFoldersMenu");
-  Event.observe(menu, "hideMenu", onContactFoldersContextMenuHide, false);
+  //Event.observe(menu, "hideMenu", onContactFoldersContextMenuHide, false);
+  Event.observe(menu, "mousedown", onContactFoldersContextMenuHide, false);
   popupMenu(event, "contactFoldersMenu", this);
 
   var topNode = $("contactFolders");
   var selectedNodes = topNode.getSelectedRows();
   topNode.menuSelectedRows = selectedNodes;
   for (var i = 0; i < selectedNodes.length; i++)
-    selectedNodes[i].deselect();
+    $(selectedNodes[i]).deselect();
   topNode.menuSelectedEntry = this;
-  this.select();
+  $(this).select();
 }
 
 function onContactContextMenu(event, element) {
   var menu = $("contactMenu");
-  Event.observe(menu, "hideMenu", onContactContextMenuHide, false);
+  //Event.observe(menu, "hideMenu", onContactContextMenuHide, false);
+  Event.observe(menu, "mousedown", onContactContextMenuHide, false);
+  //document.documentElement.onclick = onContactContextMenuHide;
   popupMenu(event, "contactMenu", element);
 
   var topNode = $("contactsList");
   var selectedNodes = topNode.getSelectedRows();
   topNode.menuSelectedRows = selectedNodes;
   for (var i = 0; i < selectedNodes.length; i++)
-    selectedNodes[i].deselect();
+    $(selectedNodes[i]).deselect();
   topNode.menuSelectedEntry = element;
-  element.select();
+  $(element).select();
 }
 
 function onContactContextMenuHide(event) {
   var topNode = $("contactsList");
 
   if (topNode.menuSelectedEntry) {
-    topNode.menuSelectedEntry.deselect();
+    $(topNode.menuSelectedEntry).deselect();
     topNode.menuSelectedEntry = null;
   }
   if (topNode.menuSelectedRows) {
     var nodes = topNode.menuSelectedRows;
     for (var i = 0; i < nodes.length; i++)
-      nodes[i].select();
+      $(nodes[i]).select();
     topNode.menuSelectedRows = null;
   }
 }
@@ -437,7 +440,7 @@ function onFolderSelectionChange() {
    }
 }
 
-function refreshCurrentFolder() { log("refreshCurrentFolder");
+function refreshCurrentFolder() {
    openContactsFolder(currentContactFolder, true);
 }
 
index e4ecc8977ca65fe5973ca37ff0e1bdcccb6213a2..54535ed1ca610afaf80ee953be80e056f6d46e3d 100644 (file)
@@ -301,15 +301,21 @@ DIV.mailer_unreadicon a
 }
 
 /* fields (key/value UI), eg used in mail viewer */
+INPUT#editDraftButton
+{
+  position: absolute;
+  top: 2.5em;
+  right: 1em;
+}
 
 TABLE.mailer_fieldtable
 {
   top: 0px;
   left: 0px;
   padding-top: .5em;
+  padding-bottom: .5em;
   overflow-y: auto;
   overflow-x: hidden;
-  height: 7.5em;
   border-bottom: 1px solid #808080;
   background: #d4d0c8;
 }
index df32f1d746819e4a7373c0e46daa4961e91a8b81..15e4347131824d0b2c8624e453c2e7ddba47dde8 100644 (file)
@@ -6,6 +6,7 @@ var currentMessages = new Array();
 var maxCachedMessages = 20;
 var cachedMessages = new Array();
 var currentMailbox = null;
+var currentMailboxType = "";
 
 var usersRightsWindowHeight = 320;
 var usersRightsWindowWidth = 400;
@@ -31,12 +32,14 @@ function openMessageWindow(msguid, url) {
 }
 
 function onMessageDoubleClick(event) {
-  resetSelection(window);
-  var msguid = this.parentNode.id.substr(4);
-   
-  return openMessageWindow(msguid,
-                          ApplicationBaseURL + currentMailbox + "/"
-                          + msguid + "/popupview");
+  var action;
+
+  if (currentMailboxType == "draft")
+    action = "edit";
+  else
+    action = "popupview";
+
+  return openMessageWindowsForSelection(action, true);
 }
 
 function toggleMailSelect(sender) {
@@ -45,12 +48,6 @@ function toggleMailSelect(sender) {
   row.className = sender.checked ? "tableview_selected" : "tableview";
 }
 
-function clearSearch(sender) {
-  var searchField = window.$("search");
-  if (searchField) searchField.value="";
-  return true;
-}
-
 function openAddressbook(sender) {
   var urlstr;
    
@@ -146,19 +143,26 @@ function reopenToRemoveLocationBar() {
 
 /* mail list reply */
 
-function openMessageWindowsForSelection(action) {
+function openMessageWindowsForSelection(action, firstOnly) {
   if (document.body.hasClassName("popup"))
     win = openMessageWindow(window.messageId,
-                           window.messageURL + "/" + action /* url */);
+                           window.messageURL + "/" + action);
   else {
     var messageList = $("messageList");
     var rows = messageList.getSelectedRowsId();
-    var idset = "";
-    for (var i = 0; i < rows.length; i++)
-      win = openMessageWindow(rows[i].substr(4)        /* msguid */,
-                             ApplicationBaseURL + currentMailbox
-                             + "/" + rows[i].substr(4)
-                             + "/" + action /* url */);
+    if (rows.length > 0) {
+      if (firstOnly)
+       openMessageWindow(rows[0].substr(4),
+                         ApplicationBaseURL + currentMailbox
+                         + "/" + rows[0].substr(4)
+                         + "/" + action);
+      else
+       for (var i = 0; i < rows.length; i++)
+         openMessageWindow(rows[i].substr(4),
+                           ApplicationBaseURL + currentMailbox
+                           + "/" + rows[i].substr(4)
+                           + "/" + action);
+    }
   }
 
   return false;
@@ -348,13 +352,13 @@ function onMailboxTreeItemClick(event) {
   $("searchValue").value = "";
   initCriteria();
 
-  var datatype = this.parentNode.getAttribute("datatype");
-  if (datatype == "account" || datatype == "additional") {
+  currentMailboxType = this.parentNode.getAttribute("datatype");
+  if (currentMailboxType == "account" || currentMailboxType == "additional") {
     currentMailbox = mailbox;
     $("messageContent").innerHTML = "";
     var body = $("messageList").tBodies[0];
-    for (var i = body.rows.length - 1; i > 0; i--)
-      body.deleteRow(i);
+    for (var i = body.rows.length; i > 0; i--)
+      body.deleteRow(i-1);
   }
   else
     openMailbox(mailbox);
@@ -378,6 +382,22 @@ function refreshMailbox() {
   return false;
 }
 
+function onComposeMessage() {
+  var topWindow = getTopWindow();
+  if (topWindow)
+    topWindow.composeNewMessage();
+
+  return false;
+}
+
+function composeNewMessage() {
+  var account = currentMailbox.split("/")[1];
+  var url = ApplicationBaseURL + "/" + account + "/compose";
+  window.open(url, null,
+             "width=680,height=520,resizable=1,scrollbars=1,toolbar=0,"
+             + "location=0,directories=0,status=0,menubar=0,copyhistory=0");
+}
+
 function openMailbox(mailbox, reload, idx) {
   if (mailbox != currentMailbox || reload) {
     currentMailbox = mailbox;
@@ -393,7 +413,7 @@ function openMailbox(mailbox, reload, idx) {
     var searchValue = search["value"];
     if (searchValue && searchValue.length > 0)
       url += ("&search=" + search["criteria"]
-             + "&value=" + searchValue);
+             + "&value=" + escape(searchValue));
     var sortAttribute = sorting["attribute"];
     if (sortAttribute && sortAttribute.length > 0)
       url += ("&sort=" + sorting["attribute"]
@@ -675,12 +695,20 @@ function configureLinksInMessage() {
     else
       Event.observe(anchors[i], "click",
                    onMessageAnchorClick);
+
+  var editDraftButton = $("editDraftButton");
+  if (editDraftButton)
+    Event.observe(editDraftButton, "click", onMessageEditDraft);
 }
 
 function onMessageContentMenu(event) {
   popupMenu(event, 'messageContentMenu', this);
 }
 
+function onMessageEditDraft(event) {
+  return openMessageWindowsForSelection("edit", true);
+}
+
 function onEmailAddressClick(event) {
   popupMenu(event, 'addressMenu', this);
 }
@@ -1000,6 +1028,7 @@ function configureDragHandles() {
   var handle = $("verticalDragHandle");
   if (handle) {
     handle.addInterface(SOGoDragHandlesInterface);
+    handle.leftMargin = 1;
     handle.leftBlock=$("leftPanel");
     handle.rightBlock=$("rightPanel");
   }
@@ -1282,10 +1311,18 @@ function onMenuDeleteFolder(event) {
   }
 }
 
+function onMenuExpungeFolder(event) {
+  var folderID = document.menuTarget.getAttribute("dataname");
+  var urlstr = URLForFolderID(folderID) + "/expunge";
+
+  triggerAjaxRequest(urlstr, folderRefreshCallback, folderID);
+}
+
 function onMenuEmptyTrash(event) {
   var folderID = document.menuTarget.getAttribute("dataname");
   var urlstr = URLForFolderID(folderID) + "/emptyTrash";
-  triggerAjaxRequest(urlstr, folderOperationCallback);
+
+  triggerAjaxRequest(urlstr, folderRefreshCallback, folderID);
 }
 
 function folderOperationCallback(http) {
@@ -1296,21 +1333,34 @@ function folderOperationCallback(http) {
     window.alert(labels["Operation failed"].decodeEntities());
 }
 
+function folderRefreshCallback(http) {
+  if (http.readyState == 4
+      && http.status == 204) {
+    var oldMailbox = http.callbackData;
+    if (oldMailbox == currentMailbox)
+      refreshCurrentFolder();
+  }
+  else
+    window.alert(labels["Operation failed"].decodeEntities());
+}
+
 function getMenus() {
   var menus = {}
   menus["accountIconMenu"] = new Array(null, null, onMenuCreateFolder, null,
                                       null, null);
   menus["inboxIconMenu"] = new Array(null, null, null, "-", null,
-                                    onMenuCreateFolder, null, "-", null,
+                                    onMenuCreateFolder, onMenuExpungeFolder,
+                                    "-", null,
                                     onMenuSharing);
   menus["trashIconMenu"] = new Array(null, null, null, "-", null,
-                                    onMenuCreateFolder, null,
+                                    onMenuCreateFolder, onMenuExpungeFolder,
                                     onMenuEmptyTrash, "-", null,
                                     onMenuSharing);
   menus["mailboxIconMenu"] = new Array(null, null, null, "-", null,
                                       onMenuCreateFolder,
                                       onMenuRenameFolder,
-                                      null, onMenuDeleteFolder, "-", null,
+                                      onMenuExpungeFolder,
+                                      onMenuDeleteFolder, "-", null,
                                       onMenuSharing);
   menus["addressMenu"] = new Array(newContactFromEmail, newEmailTo, null);
   menus["messageListMenu"] = new Array(onMenuOpenMessage, "-",
index c3c6793d5c12a1537e8f8e2fc43acb69c4a6a6c4..d36a45e5b2d983b1c5a11d2377e11840e36734c3 100644 (file)
@@ -1,4 +1,6 @@
 var SOGoDragHandlesInterface = {
+  leftMargin: 180,
+  topMargin: 120,
   dhType: null,
   origX: -1,
   origLeft: -1,
@@ -11,9 +13,9 @@ var SOGoDragHandlesInterface = {
   rightBlock: null,
   upperBlock: null,
   lowerBlock: null,
-  startHandleDraggingBinded: null,
-  stopHandleDraggingBinded: null,
-  moveBinded: null,
+  startHandleDraggingBound: null,
+  stopHandleDraggingBound: null,
+  moveBound: null,
   bind: function () {
     this.startHandleDraggingBound = this.startHandleDragging.bindAsEventListener(this);
     Event.observe(this, "mousedown", this.startHandleDraggingBound, false);
@@ -27,24 +29,20 @@ var SOGoDragHandlesInterface = {
   startHandleDragging: function (event) {
     if (!this.dhType)
       this._determineType();
-    var targ;
-    if (!event)
-      var event = window.event;
-    if (event.target)
-      targ = event.target
-    else if (event.srcElement)
-      targ = event.srcElement
+    var targ = getTarget(event);
     if (targ.nodeType == 1) {
       if (this.dhType == 'horizontal') {
         this.origX = this.offsetLeft;
         this.origLeft = this.leftBlock.offsetWidth;
-        delta = 0;
+       delta = 0;
         this.origRight = this.rightBlock.offsetLeft - 5;
         document.body.setStyle({ cursor: "e-resize" });
       } else if (this.dhType == 'vertical') {
         this.origY = this.offsetTop;
         this.origUpper = this.upperBlock.offsetHeight;
-        delta = event.clientY - this.offsetTop - 5;
+       var pointY = Event.pointerY(event);
+       if (pointY <= this.topMargin) delta = this.topMargin;
+        else delta = pointY - this.offsetTop - 5;
         this.origLower = this.lowerBlock.offsetTop - 5;
         document.body.setStyle({ cursor: "n-resize" });
       }
@@ -62,51 +60,54 @@ var SOGoDragHandlesInterface = {
     if (!this.dhType)
       this._determineType();
     if (this.dhType == 'horizontal') {
-      var deltaX
-        = Math.floor(event.clientX - this.origX - (this.offsetWidth / 2));
-      this.rightBlock.setStyle({ left: (this.origRight + deltaX) + 'px' });
-      this.leftBlock.setStyle({ width: (this.origLeft + deltaX) + 'px' });
+      var pointerX = Event.pointerX(event);
+      if (pointerX <= this.leftMargin) {
+       this.rightBlock.setStyle({ left: (this.leftMargin) + 'px' });
+       this.leftBlock.setStyle({ width: (this.leftMargin) + 'px' });
+      }
+      else {
+       var deltaX = Math.floor(pointerX - this.origX - (this.offsetWidth / 2));
+       this.rightBlock.setStyle({ left: (this.origRight + deltaX) + 'px' });
+       this.leftBlock.setStyle({ width: (this.origLeft + deltaX) + 'px' });
+      }
     } else if (this.dhType == 'vertical') {
-      var deltaY
-        = Math.floor(event.clientY - this.origY - (this.offsetHeight / 2));
-      this.lowerBlock.setStyle({ top: (this.origLower + deltaY - delta) + 'px' });
-      this.upperBlock.setStyle({ height: (this.origUpper + deltaY - delta) + 'px' });
+      var pointerY = Event.pointerY(event);
+      if (pointerY <= this.topMargin) {
+       this.lowerBlock.setStyle({ top: (this.topMargin - delta) + 'px' });
+       this.upperBlock.setStyle({ height: (this.topMargin - delta) + 'px' });
+      }
+      else {
+        var deltaY = Math.floor(pointerY - this.origY - (this.offsetHeight / 2));
+       this.lowerBlock.setStyle({ top: (this.origLower + deltaY - delta) + 'px' });
+       this.upperBlock.setStyle({ height: (this.origUpper + deltaY - delta) + 'px' });
+      }
     }
     Event.stopObserving(document.body, "mouseup", this.stopHandleDraggingBound, true);
     Event.stopObserving(document.body, "mousemove", this.moveBound, true);
     
     document.body.setAttribute('style', '');
     
-    this.move(event);
-    event.cancelBubble = true;
-
-    return false;
+    Event.stop(event);
   },
   move: function (event) {
     if (!this.dhType)
       this._determineType();
     if (this.dhType == 'horizontal') {
+      var hX =  Event.pointerX(event);
       var width = this.offsetWidth;
-      var hX = event.clientX;
-      if (hX > -1) {
-        var newLeft = Math.floor(hX - (width / 2));
-        this.setStyle({ left: newLeft + 'px' });
-        event.cancelBubble = true;
-      
-        return false;
-      }
+      if (hX < this.leftMargin)
+       hX = this.leftMargin + Math.floor(width / 2);
+      var newLeft = Math.floor(hX - (width / 2));
+      this.setStyle({ left: newLeft + 'px' });
     } else if (this.dhType == 'vertical') {
       var height = this.offsetHeight;
-      var hY = event.clientY;
-      if (hY > -1) {
-        var newTop = Math.floor(hY - (height / 2))  - delta;
-        this.setStyle({ top: newTop + 'px' });
-        event.cancelBubble = true;
-
-        return false;
-      }
+      var hY = Event.pointerY(event);
+      if (hY < this.topMargin)
+       hY = this.topMargin + Math.floor(height / 2);
+      var newTop = Math.floor(hY - (height / 2))  - delta;
+      this.setStyle({ top: newTop + 'px' });
     }
+    Event.stop(event);
   },
   doubleClick: function (event) {
     if (!this.dhType)
index bf46fbd4df528b77b741acf65453ff67f46c9ccb..7eb2d2dcc46c15f41ac063038aece17a96e4a53f 100644 (file)
@@ -1427,21 +1427,6 @@ function onCalendarRemove(event) {
   preventDefault(event);
 }
 
-function configureSearchField() {
-   var searchValue = $("searchValue");
-
-   Event.observe(searchValue, "mousedown",
-                onSearchMouseDown.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "click",
-                popupSearchMenu.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "blur",
-                onSearchBlur.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "focus",
-                onSearchFocus.bindAsEventListener(searchValue));
-   Event.observe(searchValue, "keydown",
-                onSearchKeyDown.bindAsEventListener(searchValue));
-}
-
 function configureLists() {
    var list = $("tasksList");
    list.multiselect = true;
index a21531e7e849b0100401b392e69cafc8e653990a..e3c227b8d233c8c570e90467d1621dee7c7e86ef 100644 (file)
@@ -40,7 +40,7 @@ SPAN.headerInput INPUT
 
 DIV#subjectRow INPUT
 { background-image: none;
-  width: 38em;
+  width: 100%;
   padding-left: .5em; }
 
 div#compose_internetmarker
@@ -114,4 +114,5 @@ DIV.pageContent TEXTAREA
   left: 0em;
   right: 0em;
   bottom: 0em;
-  top: 17em; }
+  top: 17em;
+  width: 100%; }
index 4eb2c9a4d49255516a7b776d89d87c8180e3de35..a38d0088e6a07a26650365a1a13043ef87fbcf9e 100644 (file)
@@ -30,9 +30,8 @@ function addContact(tag, fullContactName, contactId, contactName, contactEmail)
     var stop = false;
     var counter = 0;
     var currentRow = $('row_' + counter);
-    while (currentRow
-           && !stop) {
-      var currentValue = currentRow.childNodesWithTag("span")[1].childNodesWithTag("input")[0].value;
+    while (currentRow && !stop) {
+      var currentValue = $(currentRow.childNodesWithTag("span")[1]).childNodesWithTag("input")[0].value;
       if (currentValue == neededOptionValue) {
         stop = true;
         insertContact($("addr_" + counter), contactName, contactEmail);
@@ -211,18 +210,16 @@ function createAttachment(node, list) {
 }
 
 function clickedEditorSave(sender) {
-   document.pageform.action = "save";
-   document.pageform.submit();
-   refreshOpener();
+  document.pageform.action = "save";
+  document.pageform.submit();
 
   return false;
 }
 
 function clickedEditorDelete(sender) {
-   document.pageform.action = "delete";
-   document.pageform.submit();
-   refreshOpener();
-   window.close();
+  document.pageform.action = "delete";
+  document.pageform.submit();
+  window.close();
 
   return false;
 }
@@ -231,9 +228,12 @@ function initMailEditor() {
   var list = $("attachments");
   $(list).attachMenu("attachmentsMenu");
   var elements = list.childNodesWithTag("li");
-  for (var i = 0; i < elements.length; i++)
+  for (var i = 0; i < elements.length; i++) {
     Event.observe(elements[i], "click",
                  onRowClick.bindAsEventListener(elements[i]));
+  }
+  onWindowResize(null);
+  Event.observe(window, "resize", onWindowResize);
 }
 
 function getMenus() {
@@ -264,4 +264,14 @@ function onSelectAllAttachments() {
     nodes[i].select();
 }
 
+function onWindowResize(event) {
+  var textarea = document.pageform.text;
+  var windowheight = (typeof self.innerHeight == "number" ? self.innerHeight : document.body.clientHeight);
+  var textareaoffset = textarea.offsetTop;
+  var rowheight = (Element.getHeight(textarea) / textarea.rows);
+
+  textarea.rows = Math.round((windowheight - textareaoffset) / rowheight);
+  log ("onWindowResize new number of rows = " + textarea.rows);
+}
+
 addEvent(window, 'load', initMailEditor);
index 14f04fecf5c981218517fda736958a9801e84ba1..98e8eb71b922ebf0ce672c661cab464d69af4733 100644 (file)
@@ -3,3 +3,12 @@ function onPrintCurrentMessage(event) {
 
   preventDefault(event);
 }
+
+function initMailerPopup(event) {
+  var headerTable = document.getElementsByClassName('mailer_fieldtable')[0];
+  var contentDiv = document.getElementsByClassName('mailer_mailcontent')[0];
+
+  contentDiv.setStyle({ 'top': (Element.getHeight(headerTable) + headerTable.offsetTop) + 'px' });
+}
+
+addEvent(window, 'load', initMailerPopup);
index 37a8d4c6e0fda60966374ad3ee33d6058f777748..42da85e11e052eead6ea7db6b1f1d60a6cae7440 100644 (file)
@@ -524,8 +524,8 @@ function popupMenu(event, menuId, target) {
       hideMenu(document.currentPopupMenu);
 
    var popup = $(menuId);
-   var menuTop = event.pageY;
-   var menuLeft = event.pageX;
+   var menuTop =  Event.pointerY(event);
+   var menuLeft = Event.pointerX(event);
    var heightDiff = (window.innerHeight
                     - (menuTop + popup.offsetHeight));
    if (heightDiff < 0)
@@ -541,6 +541,7 @@ function popupMenu(event, menuId, target) {
                    visibility: "visible" });
 
    document.currentPopupMenu = popup;
+
    Event.observe(document.body, "click", onBodyClickMenuHandler);
 
    preventDefault(event);
@@ -571,7 +572,7 @@ function onBodyClickMenuHandler(event) {
    preventDefault(event);
 }
 
-function hideMenu(menuNode) {
+function hideMenu(menuNode) { //log ("hideMenu");
   var onHide;
 
   if (menuNode.submenu) {
@@ -590,13 +591,17 @@ function hideMenu(menuNode) {
     menuNode.parentMenu = null;
   }
 
-  if (document.initEvent) {
-    var onhideEvent = document.createEvent("UIEvents");
-    onhideEvent.initEvent("hideMenu", false, true);
+  if (document.createEvent) {
+    var onhideEvent;
+    if (isSafari())
+      onhideEvent = document.createEvent("UIEvents");
+    else // Mozilla
+      onhideEvent = document.createEvent("Events");
+    onhideEvent.initEvent("mousedown", false, true);
     menuNode.dispatchEvent(onhideEvent);
   }
-  else if (document.createEventObject) {
-    // TODO: add support for IE
+  else if (document.createEventObject) { // IE
+    menuNode.fireEvent("onmousedown");
   }
 }
 
@@ -759,8 +764,10 @@ function checkDropDown(event) {
 /* search field */
 function popupSearchMenu(event) {
   var menuId = this.getAttribute("menuid");
-  relX = event.pageX - $(this).cascadeLeftOffset();
-  relY = event.pageY - $(this).cascadeTopOffset();
+  var offset = Position.cumulativeOffset(this);
+
+  relX = Event.pointerX(event) - offset[0];
+  relY = Event.pointerY(event) - offset[1];
 
   if (event.button == 0
       && relX < 24) {
@@ -771,8 +778,9 @@ function popupSearchMenu(event) {
       hideMenu(document.currentPopupMenu);
 
     var popup = $(menuId);
+    offset = Position.positionedOffset(this);
     popup.setStyle({ top: this.offsetHeight + "px",
-                    left: (this.offsetLeft + 3) + "px",
+                   left: (offset[0] + 3) + "px",
                            visibility: "visible" });
   
     document.currentPopupMenu = popup;
@@ -781,8 +789,8 @@ function popupSearchMenu(event) {
 }
 
 function setSearchCriteria(event) {
-  searchValue = $("searchValue");
-  searchCriteria = $("searchCriteria");
+  var searchValue = $("searchValue");
+  var searchCriteria = $("searchCriteria");
 
   searchValue.setAttribute("ghost-phrase", this.innerHTML);
   searchCriteria.value = this.getAttribute('id');
@@ -804,6 +812,8 @@ function onSearchChange() {
 function configureSearchField() {
    var searchValue = $("searchValue");
 
+   if (!searchValue) return;
+
    Event.observe(searchValue, "mousedown",
                 onSearchMouseDown.bindAsEventListener(searchValue));
    Event.observe(searchValue, "click",
@@ -818,8 +828,8 @@ function configureSearchField() {
 
 function onSearchMouseDown(event) {
    var superNode = this.parentNode.parentNode.parentNode;
-   relX = (event.pageX - superNode.offsetLeft - this.offsetLeft);
-   relY = (event.pageY - superNode.offsetTop - this.offsetTop);
+   relX = (Event.pointerX(event) - superNode.offsetLeft - this.offsetLeft);
+   relY = (Event.pointerY(event) - superNode.offsetTop - this.offsetTop);
 
    if (relY < 24) {
       event.cancelBubble = true;
@@ -840,12 +850,13 @@ function onSearchFocus() {
 }
 
 function onSearchBlur(event) {
-  var ghostPhrase = this.getAttribute("ghost-phrase");
-//   log ("search blur: '" + this.value + "'");
-  if (!this.value) {
+   var ghostPhrase = this.getAttribute("ghost-phrase");
+   //log ("search blur: '" + this.value + "'");
+   if (!this.value) {
     this.setAttribute("modified", "");
     this.setStyle({ color: "#aaa" });
     this.value = ghostPhrase;
+    refreshCurrentFolder();
   } else if (this.value == ghostPhrase) {
     this.setAttribute("modified", "");
     this.setStyle({ color: "#aaa" });
@@ -862,9 +873,12 @@ function onSearchKeyDown(event) {
   this.timer = setTimeout("onSearchFormSubmit()", 1000);
 }
 
-function onSearchFormSubmit(event) { log("generic.onSearchFormSubmit")
+function onSearchFormSubmit(event) {
    var searchValue = $("searchValue");
    var searchCriteria = $("searchCriteria");
+   var ghostPhrase = searchValue.getAttribute('ghost-phrase');
+
+   if (searchValue.value == ghostPhrase) return;
 
    search["criteria"] = searchCriteria.value;
    search["value"] = searchValue.value;
@@ -876,6 +890,8 @@ function initCriteria() {
   var searchCriteria = $("searchCriteria");
   var searchValue = $("searchValue");
  
+  if (!searchValue) return;
+
   var searchOptions = $("searchOptions").childNodesWithTag("li");
   if (searchOptions.length > 0) {
     var firstChild = searchOptions[0];
@@ -1035,7 +1051,7 @@ function initMenus() {
 function initMenu(menuDIV, callbacks) {
    var lis = $(menuDIV.childNodesWithTag("ul")[0]).childNodesWithTag("li");
    for (var j = 0; j < lis.length; j++) {
-      var node = lis[j];
+      var node = $(lis[j]);
       Event.observe(node, "mousedown", listRowMouseDownHandler, false);
       var callback = callbacks[j];
       if (callback) {
@@ -1228,9 +1244,9 @@ function onLoadHandler(event) {
    queryParameters = parseQueryParameters('' + window.location);
    if (!$(document.body).hasClassName("popup")) {
       initLogConsole();
-      initCriteria();
-      configureSearchField();
    }
+   initCriteria();
+   configureSearchField();
    initMenus();
    initTabs();
    configureDragHandles();