From 3c0bf460bd4c70e0f568fd2e19909057ed02a140 Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 27 May 2010 14:41:59 +0000 Subject: [PATCH] Live loading in the Webmail module! See ChangeLog. Monotone-Parent: c81c7151deb5466ad48ca5eb97d70f3b1172934c Monotone-Revision: 4c3c63649f1424cf932228d812cb2a38ae67b434 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2010-05-27T14:41:59 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 43 + UI/MailerUI/GNUmakefile | 2 +- ...UIxMailListView.h => UIxMailListActions.h} | 15 +- UI/MailerUI/UIxMailListActions.m | 665 +++++++++++++ UI/MailerUI/UIxMailListView.m | 874 ------------------ UI/MailerUI/UIxMailMainFrame.h | 6 + UI/MailerUI/UIxMailMainFrame.m | 192 +++- UI/MailerUI/product.plist | 28 +- UI/Templates/MailerUI/UIxMailListView.wox | 152 --- UI/Templates/MailerUI/UIxMailMainFrame.wox | 86 +- UI/WebServerResources/HTMLElement.js | 33 +- UI/WebServerResources/MailerUI.css | 250 +++-- UI/WebServerResources/MailerUI.js | 470 +++++----- UI/WebServerResources/SOGoDataTable.js | 260 ++++++ UI/WebServerResources/SOGoDragHandles.js | 5 +- UI/WebServerResources/SOGoMailDataSource.js | 167 ++++ UI/WebServerResources/SOGoResizableTable.js | 237 +++++ UI/WebServerResources/generic.css | 10 +- UI/WebServerResources/generic.js | 6 +- UI/WebServerResources/iefixes.css | 3 + 20 files changed, 2061 insertions(+), 1443 deletions(-) rename UI/MailerUI/{UIxMailListView.h => UIxMailListActions.h} (83%) create mode 100644 UI/MailerUI/UIxMailListActions.m delete mode 100644 UI/MailerUI/UIxMailListView.m delete mode 100644 UI/Templates/MailerUI/UIxMailListView.wox create mode 100644 UI/WebServerResources/SOGoDataTable.js create mode 100644 UI/WebServerResources/SOGoMailDataSource.js create mode 100644 UI/WebServerResources/SOGoResizableTable.js diff --git a/ChangeLog b/ChangeLog index f6fe4e772..f19f6ae56 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,46 @@ +2010-05-27 Francis Lachapelle + + * UI/MailerUI/UIxMailListActions.m: renamed from + UIxMailListview.m. Associated .wox was removed. + (-getSortedUIDsAction): new method that returns a JSON + array with all the messages UIDs of the mailbox, sorted as requested. + (-getHeadersAction): new method that returns a JSON dictionary of + the headers of requested messages UIDs. + + * UI/WebServerResources/SOGoDataTable.js: new interface to add + "live loading" to a table. + + * UI/WebServerResources/SOGoMailDataSource.js: new class that + dynamically fetches the message headers depending on the requests + from a SOGoDataTable. + + * UI/WebServerResources/SOGoResizableTable.js: new interface to + add resizable headers to a table. + + * UI/WebServerResources/MailerUI.js (initMailer): associates a DIV + with the new SOGoDataTable interface. + (openMailbox): uses the new SOGoMailDataSource class to fetch the + messages list. + (messageListCallback): new function called to populate a row from + the data received from the SOGoDataTable interface and + SOGoMailDataSource class. + (updateMessageListCounter): new function to update the message + counter that appears in the headers table. + (toggleAddressColumn): new + function to toggle the address column header content + (From/To). This column changes depending on the mailbox type + selected (draft, sent or other). + + * UI/WebServerResources/HTMLElement.js (refreshSelectionByIds): + new method to restore the selection based on the elements IDs instead + of the elements themselves. + + * UI/MailerUI/UIxMailMainFrame.m (-showToAddress, + -columnsMetaData, -columnsMetaData, -columnsDisplayOrder, + -columnsOrder, setCurrentColumn, -currentColumn, -columnTitle): + moved those methods from UIxMailListView, since the messages table + is now populated with a JSON representation of the data. + 2010-05-25 Wolfgang Sourdeau * SoObjects/Appointments/SOGoCalendarComponent.m diff --git a/UI/MailerUI/GNUmakefile b/UI/MailerUI/GNUmakefile index 2c2684c34..c3326bd12 100644 --- a/UI/MailerUI/GNUmakefile +++ b/UI/MailerUI/GNUmakefile @@ -17,7 +17,7 @@ MailerUI_OBJC_FILES += \ \ UIxMailMainFrame.m \ \ - UIxMailListView.m \ + UIxMailListActions.m \ UIxMailView.m \ UIxMailSourceView.m \ UIxMailPopupView.m \ diff --git a/UI/MailerUI/UIxMailListView.h b/UI/MailerUI/UIxMailListActions.h similarity index 83% rename from UI/MailerUI/UIxMailListView.h rename to UI/MailerUI/UIxMailListActions.h index 4c404531e..dbe4be6af 100644 --- a/UI/MailerUI/UIxMailListView.h +++ b/UI/MailerUI/UIxMailListActions.h @@ -19,8 +19,8 @@ 02111-1307, USA. */ -#ifndef UIXMAILLISTVIEW_H -#define UIXMAILLISTVIEW_H +#ifndef UIXMAILLISTACTIONS_H +#define UIXMAILLISTACTIONS_H #import @@ -28,24 +28,23 @@ @class EOQualifier; @class SOGoDateFormatter; -@interface UIxMailListView : UIxComponent +@interface UIxMailListActions : UIxComponent { NSArray *sortedUIDs; /* we always need to retrieve all anyway! */ NSArray *messages; - NSArray *columnsOrder; - unsigned firstMessageNumber; id message; - EOQualifier *qualifier; SOGoDateFormatter *dateFormatter; NSTimeZone *userTimeZone; int folderType; - NSDictionary *currentColumn; + int specificMessageNumber; } - (NSString *) defaultSortKey; - (NSString *) imap4SortKey; - (NSString *) imap4SortOrdering; +- (EOQualifier *) searchQualifier; +- (NSString *) msgLabels; @end -#endif /* UIXMAILLISTVIEW_H */ +#endif /* UIXMAILLISTACTIONS_H */ diff --git a/UI/MailerUI/UIxMailListActions.m b/UI/MailerUI/UIxMailListActions.m new file mode 100644 index 000000000..b732f9c40 --- /dev/null +++ b/UI/MailerUI/UIxMailListActions.m @@ -0,0 +1,665 @@ +/* + Copyright (C) 2004-2005 SKYRIX Software AG + Copyright (C) 2006-2010 Inverse inc. + + This file is part of SOGo + + SOGo 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. + + SOGo 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. +*/ + +/* + UIxMailListActions + + This component represent a list of mails and is attached to an SOGoMailFolder + object. +*/ + +#import +#import +#import +#import +#import +#import + +#import +#import +#import +#import +#import +#import +#import +#import + +#import + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#import "WOContext+UIxMailer.h" + +#import "UIxMailListActions.h" + +@implementation UIxMailListActions + +- (id) init +{ + SOGoUser *user; + + if ((self = [super init])) + { + user = [context activeUser]; + ASSIGN (dateFormatter, [user dateFormatterInContext: context]); + ASSIGN (userTimeZone, [[user userDefaults] timeZone]); + folderType = 0; + specificMessageNumber = 0; + } + + return self; +} + +- (void) dealloc +{ + [sortedUIDs release]; + [messages release]; + [message release]; + [dateFormatter release]; + [userTimeZone release]; + [super dealloc]; +} + +/* accessors */ + +- (void) setMessage: (id) _msg +{ + ASSIGN (message, _msg); +} + +- (id) message +{ + return message; +} + +- (NSString *) messageDate +{ + NSCalendarDate *messageDate; + + messageDate = [[message valueForKey: @"envelope"] date]; + [messageDate setTimeZone: userTimeZone]; + + return [dateFormatter formattedDateAndTime: messageDate]; +} + +- (NSString *) messageSize +{ + NSString *rc; + int size; + + size = [[message valueForKey: @"size"] intValue]; + if (size > 1024*1024) + rc = [NSString stringWithFormat: @"%.1f MB", (float) size/1024/1024]; + else if (size > 1024*100) + rc = [NSString stringWithFormat: @"%d KB", size/1024]; + else + rc = [NSString stringWithFormat: @"%.1f KB", (float) size/1024]; + + return rc; +} + +// +// Priorities are defined like this: +// +// X-Priority: 1 (Highest) +// X-Priority: 2 (High) +// X-Priority: 3 (Normal) +// X-Priority: 4 (Low) +// X-Priority: 5 (Lowest) +// +// Sometimes, the MUAs don't send over the string in () so we ignore it. +// +- (NSString *) messagePriority +{ + NSString *result; + NSData *data; + + data = [message objectForKey: @"header"]; + result = @""; + + if (data) + { + NSString *s; + + s = [[NSString alloc] initWithData: data + encoding: NSASCIIStringEncoding]; + + if (s) + { + NSRange r; + + [s autorelease]; + r = [s rangeOfString: @":"]; + + if (r.length) + { + s = [[s substringFromIndex: r.location+1] + stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"]; + else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"]; + else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"]; + else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"]; + } + } + } + + return result; +} + +- (NSString *) messageSubject +{ + id baseSubject; + NSString *subject; + + baseSubject = [[message valueForKey: @"envelope"] subject]; + subject = [baseSubject decodedHeader]; + if (![subject length]) + subject = [self labelForKey: @"Untitled"]; + + return subject; +} + +- (BOOL) showToAddress +{ + SOGoMailFolder *co; + + if (!folderType) + { + co = [self clientObject]; + if ([co isKindOfClass: [SOGoSentFolder class]] + || [co isKindOfClass: [SOGoDraftsFolder class]]) + folderType = 1; + else + folderType = -1; + } + + return (folderType == 1); +} + +/* title */ + +- (NSString *) objectTitle +{ + return [[self clientObject] nameInContainer]; +} + +- (NSString *) panelTitle +{ + NSString *s; + + s = [self labelForKey:@"View Mail Folder"]; + s = [s stringByAppendingString:@": "]; + s = [s stringByAppendingString:[self objectTitle]]; + return s; +} + +/* derived accessors */ + +- (BOOL) isMessageDeleted +{ + NSArray *flags; + + flags = [[self message] valueForKey:@"flags"]; + return [flags containsObject:@"deleted"]; +} + +- (BOOL) isMessageRead +{ + NSArray *flags; + + flags = [[self message] valueForKey:@"flags"]; + return [flags containsObject:@"seen"]; +} + +- (BOOL) isMessageFlagged +{ + NSArray *flags; + + flags = [[self message] valueForKey:@"flags"]; + return [flags containsObject:@"flagged"]; +} + +- (NSString *) messageUidString +{ + return [[[self message] valueForKey:@"uid"] stringValue]; +} + +- (NSString *) messageRowStyleClass +{ + NSArray *flags; + NSString *cellClass = @""; + + flags = [[self message] valueForKey:@"flags"]; + + if ([self isMessageDeleted]) + cellClass = [cellClass stringByAppendingString: @"mailer_listcell_deleted "]; + + if (![self isMessageRead]) + cellClass = [cellClass stringByAppendingString: @"mailer_unreadmail "]; + + if ([flags containsObject: @"answered"]) + { + if ([flags containsObject: @"$forwarded"]) + cellClass = [cellClass stringByAppendingString: @"mailer_forwardedrepliedmailsubject"]; + else + cellClass = [cellClass stringByAppendingString: @"mailer_repliedmailsubject"]; + } + else if ([flags containsObject: @"$forwarded"]) + cellClass = [cellClass stringByAppendingString: @"mailer_forwardedmailsubject"]; + else + cellClass = [cellClass stringByAppendingString: @"mailer_readmailsubject"]; + + return cellClass; +} + +- (BOOL) hasMessageAttachment +{ + NSArray *parts; + NSEnumerator *dispositions; + NSDictionary *currentDisp; + BOOL hasAttachment; + + hasAttachment = NO; + + parts = [[message objectForKey: @"body"] objectForKey: @"parts"]; + if ([parts count] > 1) + { + dispositions = [[parts objectsForKey: @"disposition" + notFoundMarker: nil] objectEnumerator]; + while (!hasAttachment + && (currentDisp = [dispositions nextObject])) + hasAttachment = ([[currentDisp objectForKey: @"type"] length]); + } + + return hasAttachment; +} + +/* fetching messages */ + +- (NSArray *) fetchKeys +{ + /* Note: see SOGoMailManager.m for allowed IMAP4 keys */ + static NSArray *keys = nil; + + if (!keys) + keys = [[NSArray alloc] initWithObjects: @"UID", + @"FLAGS", @"ENVELOPE", @"RFC822.SIZE", + @"BODYSTRUCTURE", @"BODY.PEEK[HEADER.FIELDS (X-PRIORITY)]", nil]; + return keys; +} + +- (NSString *) defaultSortKey +{ + return @"ARRIVAL"; +} + +- (NSString *) imap4SortKey +{ + NSString *sort; + + sort = [[context request] formValueForKey: @"sort"]; + + if (![sort length]) + sort = [self defaultSortKey]; + + return [sort uppercaseString]; +} + +- (NSString *) imap4SortOrdering +{ + NSString *sort, *ascending; + + sort = [self imap4SortKey]; + + ascending = [[context request] formValueForKey: @"asc"]; + if (![ascending boolValue]) + sort = [@"REVERSE " stringByAppendingString: sort]; + + return sort; +} + +- (EOQualifier *) searchQualifier +{ + NSString *criteria, *value; + EOQualifier *qualifier; + WORequest *request; + + request = [context request]; + criteria = [request formValueForKey: @"search"]; + value = [request formValueForKey: @"value"]; + qualifier = nil; + if ([value length]) + { + if ([criteria isEqualToString: @"subject"]) + qualifier = [EOQualifier qualifierWithQualifierFormat: + @"(subject doesContain: %@)", value]; + else if ([criteria isEqualToString: @"sender"]) + qualifier = [EOQualifier qualifierWithQualifierFormat: + @"(from doesContain: %@)", value]; + else if ([criteria isEqualToString: @"subject_or_sender"]) + qualifier = [EOQualifier qualifierWithQualifierFormat: + @"((subject doesContain: %@)" + @" OR (from doesContain: %@))", + value, value]; + else if ([criteria isEqualToString: @"to_or_cc"]) + qualifier = [EOQualifier qualifierWithQualifierFormat: + @"((to doesContain: %@)" + @" OR (cc doesContain: %@))", + value, value]; + else if ([criteria isEqualToString: @"entire_message"]) + qualifier = [EOQualifier qualifierWithQualifierFormat: + @"(body doesContain: %@)", value]; + } + + return qualifier; +} + +- (NSArray *) sortedUIDs +{ + EOQualifier *qualifier, *fetchQualifier, *notDeleted; + SOGoMailFolder *folder; + + if (!sortedUIDs) + { + notDeleted = [EOQualifier qualifierWithQualifierFormat: + @"(not (flags = %@))", + @"deleted"]; + qualifier = [self searchQualifier]; + if (qualifier) + { + fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: + notDeleted, qualifier, + nil]; + [fetchQualifier autorelease]; + } + else + fetchQualifier = notDeleted; + + folder = [self clientObject]; + sortedUIDs + = [folder fetchUIDsMatchingQualifier: fetchQualifier + sortOrdering: [self imap4SortOrdering]]; + [sortedUIDs retain]; + } + + return sortedUIDs; +} + +- (int) indexOfMessageUID: (int) messageNbr +{ + NSArray *messageNbrs; + int index; + + messageNbrs = [self sortedUIDs]; + index + = [messageNbrs indexOfObject: [NSNumber numberWithInt: messageNbr]]; +// if (index < 0) +// index = 0; + + return index; +} + +/* JavaScript */ + +- (NSString *) msgRowID +{ + return [@"row_" stringByAppendingString:[self messageUidString]]; +} + +- (NSString *) msgIconReadImgID +{ + return [@"readdiv_" stringByAppendingString:[self messageUidString]]; +} + +- (NSString *) msgIconUnreadImgID +{ + return [@"unreaddiv_" stringByAppendingString:[self messageUidString]]; +} + +/* error redirects */ + +/* +- (id) redirectToViewWithError: (id) _error +{ + // TODO: DUP in UIxMailAccountView + // TODO: improve, localize + // TODO: there is a bug in the treeview which preserves the current URL for + // the active object (displaying the error again) + id url; + + if (![_error isNotNull]) + return [self redirectToLocation:@"view"]; + + if ([_error isKindOfClass:[NSException class]]) + _error = [_error reason]; + else if ([_error isKindOfClass:[NSString class]]) + _error = [_error stringValue]; + + url = [_error stringByEscapingURL]; + url = [@"view?error=" stringByAppendingString:url]; + return [self redirectToLocation:url]; +} +*/ + +/* actions */ + +- (id) getMailAction +{ + // TODO: we might want to flush the caches? + id client; + + if ((client = [self clientObject]) == nil) { + return [NSException exceptionWithHTTPStatus:404 /* Not Found */ + reason:@"did not find mail folder"]; + } + + if (![client respondsToSelector:@selector(flushMailCaches) ]) + { + return [NSException exceptionWithHTTPStatus: 500 /* Server Error */ + reason: + @"invalid client object (does not support flush)"]; + } + + [client flushMailCaches]; + + return [self redirectToLocation:@"view"]; +} + +- (id ) getSortedUIDsAction +{ + NSArray *uids; + NSRange r; + WORequest *request; + WOResponse *response; + int firstUID, firstIndex, count; + + request = [context request]; + uids = [self sortedUIDs]; // retrieves the form parameters "sort" and "asc" + + if ([request formValueForKey: @"start"] != nil) + { + firstUID = [[request formValueForKey: @"start"] intValue]; + firstIndex = [self indexOfMessageUID: firstUID]; + if (firstIndex == NSNotFound) + return [NSException exceptionWithHTTPStatus: 404 + reason: @"Message not found"]; + } + else + firstIndex = -1; + + if ([request formValueForKey: @"count"] != nil) + { + count = [[request formValueForKey: @"count"] intValue]; + } + else + count = 0; + + if (firstIndex > -1) + { + if (count <= 0 || (count + firstIndex) > [uids count]) + count = [uids count] - firstIndex; + r = NSMakeRange(firstIndex, count); + uids = [uids subarrayWithRange: r]; + } + + response = [context response]; + [response setHeader: @"text/plain; charset=utf-8" + forKey: @"content-type"]; + [response appendContentString: [uids jsonRepresentation]]; + + return response; +} + +- (id ) getHeadersAction +{ + NSFormatter *addressFormatter; + NSArray *uids, *to; + NSDictionary *msgs; + NSMutableArray *headers; + NSMutableDictionary *msg; + NSEnumerator *msgsList; + NSString *msgIconStatus; + SOGoMailFolder *mailFolder; + WORequest *request; + WOResponse *response; + + request = [context request]; + + if ([request formValueForKey: @"uids"] == nil) + { + return [NSException exceptionWithHTTPStatus: 404 + reason: @"No UID specified"]; + } + + uids = [[request formValueForKey: @"uids"] componentsSeparatedByString: @","]; // Should we support ranges? ie "x-y" + headers = [NSMutableArray arrayWithCapacity: [uids count]]; + mailFolder = [self clientObject]; + addressFormatter = [context mailEnvelopeAddressFormatter]; + + // Fetch headers + msgs = (NSDictionary *)[mailFolder fetchUIDs: uids + parts: [self fetchKeys]]; + + msgsList = [[msgs objectForKey: @"fetch"] objectEnumerator]; + [self setMessage: [msgsList nextObject]]; + while (message) + { + msg = [NSMutableDictionary dictionaryWithCapacity: 11]; + + // Columns data + + to = [[message objectForKey: @"envelope"] to]; + if ([to count] > 0) + [msg setObject: [addressFormatter stringForArray: to] + forKey: @"To"]; + + if ([self hasMessageAttachment]) + [msg setObject: [NSString stringWithFormat: @"", [self urlForResourceFilename: @"title_attachment_14x14.png"]] + forKey: @"Attachment"]; + + if ([self isMessageFlagged]) + { + [msg setObject: [NSString stringWithFormat: @"", + [self urlForResourceFilename: @"flag.png"]] + forKey: @"Flagged"]; + } + else + { + [msg setObject: [NSString stringWithFormat: @"", + [self urlForResourceFilename: @"dot.png"]] + forKey: @"Flagged"]; + } + + [msg setObject: [NSString stringWithFormat: @"%@", + [self messageSubject]] + forKey: @"Subject"]; + + [msg setObject: [addressFormatter stringForArray: [[message objectForKey: @"envelope"] from]] forKey: @"From"]; + + if ([self isMessageRead]) + msgIconStatus = @"dot.png"; + else + msgIconStatus = @"icon_unread.gif"; + + [msg setObject: [self messageRowStyleClass] forKey: @"rowClasses"]; + [msg setObject: [NSString stringWithFormat: @"", + [self urlForResourceFilename: msgIconStatus], + [self labelForKey: @"Mark Unread"], + [self labelForKey: @"Mark Read"], + [self labelForKey: @"Mark Unread"], + [self msgIconReadImgID]] + forKey: @"Unread"]; + + [msg setObject: [self messagePriority] forKey: @"Priority"]; + + [msg setObject: [self messageDate] forKey: @"Date"]; + + [msg setObject: [self messageSize] forKey: @"Size"]; + + [msg setObject: [self msgLabels] forKey: @"labels"]; + + [msg setObject: [self msgRowID] forKey: @"rowID"]; + + [msg setObject: [message objectForKey: @"uid"] forKey: @"uid"]; + + [headers addObject: msg]; + + [self setMessage: [msgsList nextObject]]; + } + + response = [context response]; + [response setHeader: @"text/plain; charset=utf-8" + forKey: @"content-type"]; + [response appendContentString: [headers jsonRepresentation]]; + + return response; +} + +- (NSString *) msgLabels +{ + NSMutableArray *labels; + NSEnumerator *flags; + NSString *currentFlag; + + labels = [NSMutableArray array]; + + flags = [[message objectForKey: @"flags"] objectEnumerator]; + while ((currentFlag = [flags nextObject])) + if ([currentFlag hasPrefix: @"$label"]) + [labels addObject: [currentFlag substringFromIndex: 1]]; + + return [labels componentsJoinedByString: @" "]; +} + +@end + +/* UIxMailListActions */ diff --git a/UI/MailerUI/UIxMailListView.m b/UI/MailerUI/UIxMailListView.m deleted file mode 100644 index 404b25cc7..000000000 --- a/UI/MailerUI/UIxMailListView.m +++ /dev/null @@ -1,874 +0,0 @@ -/* - Copyright (C) 2004-2005 SKYRIX Software AG - Copyright (C) 2006-2009 Inverse inc. - - This file is part of SOGo - - SOGo 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. - - SOGo 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. -*/ - -/* - UIxMailListView - - This component represent a list of mails and is attached to an SOGoMailFolder - object. -*/ - -#import -#import -#import -#import -#import -#import - -#import -#import -#import -#import -#import -#import -#import - -#import - -#import -#import -#import -#import -#import -#import -#import -#import -#import - -#import "UIxMailListView.h" - -#define messagesPerPage 50 - -@implementation UIxMailListView - -- (id) init -{ - SOGoUser *user; - - if ((self = [super init])) - { - qualifier = nil; - user = [context activeUser]; - ASSIGN (dateFormatter, [user dateFormatterInContext: context]); - ASSIGN (userTimeZone, [[user userDefaults] timeZone]); - columnsOrder = nil; - folderType = 0; - currentColumn = nil; - } - - return self; -} - -- (void) dealloc -{ - [qualifier release]; - [sortedUIDs release]; - [messages release]; - [message release]; - [dateFormatter release]; - [userTimeZone release]; - [currentColumn release]; - [columnsOrder release]; - [super dealloc]; -} - -/* accessors */ - -- (void) setMessage: (id) _msg -{ - ASSIGN (message, _msg); -} - -- (id) message -{ - return message; -} - -- (NSString *) messageDate -{ - NSCalendarDate *messageDate; - - messageDate = [[message valueForKey: @"envelope"] date]; - [messageDate setTimeZone: userTimeZone]; - - return [dateFormatter formattedDateAndTime: messageDate]; -} - -- (NSString *) messageSize -{ - NSString *rc; - int size; - - size = [[message valueForKey: @"size"] intValue]; - if (size > 1024*1024) - rc = [NSString stringWithFormat: @"%.1f MB", (float) size/1024/1024]; - else if (size > 1024*100) - rc = [NSString stringWithFormat: @"%d KB", size/1024]; - else - rc = [NSString stringWithFormat: @"%.1f KB", (float) size/1024]; - - return rc; -} - -// -// Priorities are defined like this: -// -// X-Priority: 1 (Highest) -// X-Priority: 2 (High) -// X-Priority: 3 (Normal) -// X-Priority: 4 (Low) -// X-Priority: 5 (Lowest) -// -// Sometimes, the MUAs don't send over the string in () so we ignore it. -// -- (NSString *) messagePriority -{ - NSString *result; - NSData *data; - - data = [message objectForKey: @"header"]; - result = @""; - - if (data) - { - NSString *s; - - s = [[NSString alloc] initWithData: data - encoding: NSASCIIStringEncoding]; - - if (s) - { - NSRange r; - - [s autorelease]; - r = [s rangeOfString: @":"]; - - if (r.length) - { - s = [[s substringFromIndex: r.location+1] - stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - if ([s hasPrefix: @"1"]) result = [self labelForKey: @"highest"]; - else if ([s hasPrefix: @"2"]) result = [self labelForKey: @"high"]; - else if ([s hasPrefix: @"4"]) result = [self labelForKey: @"low"]; - else if ([s hasPrefix: @"5"]) result = [self labelForKey: @"lowest"]; - } - } - } - - return result; -} - -- (NSString *) messageSubject -{ - id baseSubject; - NSString *subject; - - baseSubject = [[message valueForKey: @"envelope"] subject]; - subject = [baseSubject decodedHeader]; - if (![subject length]) - subject = [self labelForKey: @"Untitled"]; - - return subject; -} - -- (BOOL) showToAddress -{ - SOGoMailFolder *co; - - if (!folderType) - { - co = [self clientObject]; - if ([co isKindOfClass: [SOGoSentFolder class]] - || [co isKindOfClass: [SOGoDraftsFolder class]]) - folderType = 1; - else - folderType = -1; - } - - return (folderType == 1); -} - -/* title */ - -- (NSString *) objectTitle -{ - return [[self clientObject] nameInContainer]; -} - -- (NSString *) panelTitle -{ - NSString *s; - - s = [self labelForKey:@"View Mail Folder"]; - s = [s stringByAppendingString:@": "]; - s = [s stringByAppendingString:[self objectTitle]]; - return s; -} - -/* derived accessors */ - -- (BOOL) isMessageDeleted -{ - NSArray *flags; - - flags = [[self message] valueForKey:@"flags"]; - return [flags containsObject:@"deleted"]; -} - -- (BOOL) isMessageRead -{ - NSArray *flags; - - flags = [[self message] valueForKey:@"flags"]; - return [flags containsObject:@"seen"]; -} - -- (BOOL) isMessageFlagged -{ - NSArray *flags; - - flags = [[self message] valueForKey:@"flags"]; - return [flags containsObject:@"flagged"]; -} - -- (NSString *) messageUidString -{ - return [[[self message] valueForKey:@"uid"] stringValue]; -} - -- (NSString *) messageRowStyleClass -{ - NSString *rowClass; - - rowClass = [self isMessageDeleted]? @"mailer_listcell_deleted" : @"mailer_listcell_regular"; - - if (![self isMessageRead]) - rowClass = [rowClass stringByAppendingString: @" mailer_unreadmail"]; - - return rowClass; -} - -- (NSString *) messageSubjectCellStyleClass -{ - NSArray *flags; - NSString *cellClass = @"messageSubjectColumn "; - - flags = [[self message] valueForKey:@"flags"]; - - if ([flags containsObject: @"answered"]) - { - if ([flags containsObject: @"$forwarded"]) - cellClass = [cellClass stringByAppendingString: @"mailer_forwardedrepliedmailsubject"]; - else - cellClass = [cellClass stringByAppendingString: @"mailer_repliedmailsubject"]; - } - else if ([flags containsObject: @"$forwarded"]) - cellClass = [cellClass stringByAppendingString: @"mailer_forwardedmailsubject"]; - else - cellClass = [cellClass stringByAppendingString: @"mailer_readmailsubject"]; - - return cellClass; -} - -- (BOOL) hasMessageAttachment -{ - NSArray *parts; - NSEnumerator *dispositions; - NSDictionary *currentDisp; - BOOL hasAttachment; - - hasAttachment = NO; - - parts = [[message objectForKey: @"body"] objectForKey: @"parts"]; - if ([parts count] > 1) - { - dispositions = [[parts objectsForKey: @"disposition" - notFoundMarker: nil] objectEnumerator]; - while (!hasAttachment - && (currentDisp = [dispositions nextObject])) - hasAttachment = ([[currentDisp objectForKey: @"type"] length]); - } - - return hasAttachment; -} - -/* fetching messages */ - -- (NSArray *) fetchKeys -{ - /* Note: see SOGoMailManager.m for allowed IMAP4 keys */ - static NSArray *keys = nil; - - if (!keys) - keys = [[NSArray alloc] initWithObjects: @"UID", - @"FLAGS", @"ENVELOPE", @"RFC822.SIZE", - @"BODYSTRUCTURE", @"BODY.PEEK[HEADER.FIELDS (X-PRIORITY)]", nil]; - return keys; -} - -- (NSString *) defaultSortKey -{ - return @"ARRIVAL"; -} - -- (NSString *) imap4SortKey -{ - NSString *sort; - - sort = [[context request] formValueForKey: @"sort"]; - - if (![sort length]) - sort = [self defaultSortKey]; - - return [sort uppercaseString]; -} - -- (NSString *) imap4SortOrdering -{ - NSString *sort, *ascending; - - sort = [self imap4SortKey]; - - ascending = [[context request] formValueForKey: @"asc"]; - if (![ascending boolValue]) - sort = [@"REVERSE " stringByAppendingString: sort]; - - return sort; -} - -- (NSRange) fetchRange -{ - if (firstMessageNumber == 0) - return NSMakeRange(0, messagesPerPage); - return NSMakeRange(firstMessageNumber - 1, messagesPerPage); -} - -- (NSArray *) sortedUIDs -{ - EOQualifier *fetchQualifier, *notDeleted; - - if (!sortedUIDs) - { - notDeleted = [EOQualifier qualifierWithQualifierFormat: - @"(not (flags = %@))", - @"deleted"]; - if (qualifier) - { - fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: - notDeleted, qualifier, - nil]; - [fetchQualifier autorelease]; - } - else - fetchQualifier = notDeleted; - - sortedUIDs - = [[self clientObject] fetchUIDsMatchingQualifier: fetchQualifier - sortOrdering: [self imap4SortOrdering]]; - [sortedUIDs retain]; - } - - return sortedUIDs; -} - -- (unsigned int) totalMessageCount -{ - return [sortedUIDs count]; -} - -- (BOOL) showsAllMessages -{ - return ([[self sortedUIDs] count] <= [self fetchRange].length) ? YES : NO; -} - -- (NSRange) fetchBlock -{ - NSRange r; - unsigned len; - NSArray *uids; - - r = [self fetchRange]; - uids = [self sortedUIDs]; - - /* only need to restrict if we have a lot */ - if ((len = [uids count]) <= r.length) { - r.location = 0; - r.length = len; - return r; - } - - if (len < r.location) { - // TODO: CHECK CONDITION (< vs <=) - /* out of range, recover at first block */ - r.location = 0; - return r; - } - - if (r.location + r.length > len) - r.length = len - r.location; - return r; -} - -- (unsigned int) firstMessageNumber -{ - return [self fetchBlock].location + 1; -} - -- (unsigned int) lastMessageNumber -{ - NSRange r; - - r = [self fetchBlock]; - return r.location + r.length; -} - -- (BOOL) hasPrevious -{ - return [self fetchBlock].location == 0 ? NO : YES; -} - -- (BOOL) hasNext -{ - NSRange r = [self fetchBlock]; - return r.location + r.length >= [[self sortedUIDs] count] ? NO : YES; -} - -- (unsigned int) nextFirstMessageNumber -{ - return [self firstMessageNumber] + [self fetchRange].length; -} - -- (unsigned int) lastFirstMessageNumber -{ - unsigned int max, modulo; - - if (!sortedUIDs) - [self sortedUIDs]; - - max = [sortedUIDs count]; - modulo = (max % messagesPerPage); - if (modulo == 0) - modulo = messagesPerPage; - - return (max + 1 - modulo); -} - -- (unsigned int) prevFirstMessageNumber -{ - NSRange r; - unsigned idx; - - idx = [self firstMessageNumber]; - r = [self fetchRange]; - if (idx > r.length) - return (idx - r.length); - return 1; -} - -- (NSArray *) messages -{ - NSMutableArray *unsortedMsgs; - NSMutableDictionary *map; - NSDictionary *msgs; - NSArray *uids; - - unsigned len, i, count; - NSRange r; - - if (!messages) - { - r = [self fetchBlock]; - uids = [self sortedUIDs]; - len = [uids count]; - - // only need to restrict if we have a lot - if (len > r.length) - { - uids = [uids subarrayWithRange: r]; - len = [uids count]; - } - - // Don't assume the IMAP server return the messages in the - // same order as the specified list of UIDs (specially true for - // dovecot). - msgs = (NSDictionary *) [[self clientObject] fetchUIDs: uids - parts: [self fetchKeys]]; - unsortedMsgs = [msgs objectForKey: @"fetch"]; - count = [unsortedMsgs count]; - - messages = [NSMutableArray arrayWithCapacity: count]; - - // We build our uid->message map from our FETCH response - map = [[NSMutableDictionary alloc] initWithCapacity: count]; - - for (i = 0; i < count; i++) - [map setObject: [unsortedMsgs objectAtIndex: i] - forKey: [[unsortedMsgs objectAtIndex: i] objectForKey: @"uid"]]; - - for (i = 0; i < len; i++) - { - [(NSMutableArray *)messages addObject: [map objectForKey: [uids objectAtIndex: i]]]; - } - - RELEASE(map); - RETAIN(messages); - } - - return messages; -} - -/* URL processing */ - -- (NSString *) messageViewTarget -{ - return [NSString stringWithFormat: @"SOGo_msg_%@", - [self messageUidString]]; -} - -- (NSString *) messageViewURL -{ - // TODO: noframe only when view-target is empty - // TODO: markread only if the message is unread - NSString *s; - - s = [[self messageUidString] stringByAppendingString:@"/view?noframe=1"]; - if (![self isMessageRead]) s = [s stringByAppendingString:@"&markread=1"]; - return s; -} -- (NSString *) markReadURL -{ - return [@"markMessageRead?uid=" stringByAppendingString: - [self messageUidString]]; -} -- (NSString *) markUnreadURL -{ - return [@"markMessageUnread?uid=" stringByAppendingString: - [self messageUidString]]; -} - -/* JavaScript */ - -- (NSString *)msgRowID -{ - return [@"row_" stringByAppendingString:[self messageUidString]]; -} - -- (NSString *)msgDivID -{ - return [@"div_" stringByAppendingString:[self messageUidString]]; -} - -- (NSString *)msgIconReadImgID -{ - return [@"readdiv_" stringByAppendingString:[self messageUidString]]; -} - -- (NSString *)msgIconUnreadImgID -{ - return [@"unreaddiv_" stringByAppendingString:[self messageUidString]]; -} - -/* error redirects */ - -- (id) redirectToViewWithError: (id) _error -{ - // TODO: DUP in UIxMailAccountView - // TODO: improve, localize - // TODO: there is a bug in the treeview which preserves the current URL for - // the active object (displaying the error again) - id url; - - if (![_error isNotNull]) - return [self redirectToLocation:@"view"]; - - if ([_error isKindOfClass:[NSException class]]) - _error = [_error reason]; - else if ([_error isKindOfClass:[NSString class]]) - _error = [_error stringValue]; - - url = [_error stringByEscapingURL]; - url = [@"view?error=" stringByAppendingString:url]; - return [self redirectToLocation:url]; -} - -/* actions */ - -- (int) firstMessageOfPageFor: (int) messageNbr -{ - NSArray *messageNbrs; - int nbrInArray; - int firstMessage; - - messageNbrs = [self sortedUIDs]; - nbrInArray - = [messageNbrs indexOfObject: [NSNumber numberWithInt: messageNbr]]; - if (nbrInArray > -1) - firstMessage = ((int) (nbrInArray / messagesPerPage) - * messagesPerPage) + 1; - else - firstMessage = 1; - - return firstMessage; -} - -- (void) _setQualifierForCriteria: (NSString *) criteria - andValue: (NSString *) value -{ - [qualifier release]; - - if ([criteria isEqualToString: @"subject"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(subject doesContain: %@)", value]; - else if ([criteria isEqualToString: @"sender"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(from doesContain: %@)", value]; - else if ([criteria isEqualToString: @"subject_or_sender"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"((subject doesContain: %@)" - @" OR (from doesContain: %@))", - value, value]; - else if ([criteria isEqualToString: @"to_or_cc"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"((to doesContain: %@)" - @" OR (cc doesContain: %@))", - value, value]; - else if ([criteria isEqualToString: @"entire_message"]) - qualifier = [EOQualifier qualifierWithQualifierFormat: - @"(body doesContain: %@)", value]; - else - qualifier = nil; - - [qualifier retain]; -} - -- (id) defaultAction -{ - WORequest *request; - NSString *specificMessage, *searchCriteria, *searchValue; - SOGoMailFolder *co; - - request = [context request]; - - co = [self clientObject]; - [co flushMailCaches]; - [co expungeLastMarkedFolder]; - - specificMessage = [request formValueForKey: @"pageforuid"]; - searchCriteria = [request formValueForKey: @"search"]; - searchValue = [request formValueForKey: @"value"]; - if ([searchValue length]) - [self _setQualifierForCriteria: searchCriteria - andValue: searchValue]; - - firstMessageNumber - = ((specificMessage) - ? [self firstMessageOfPageFor: [specificMessage intValue]] - : [[request formValueForKey:@"idx"] intValue]); - - return self; -} - -- (id) getMailAction -{ - // TODO: we might want to flush the caches? - id client; - - if ((client = [self clientObject]) == nil) { - return [NSException exceptionWithHTTPStatus:404 /* Not Found */ - reason:@"did not find mail folder"]; - } - - if (![client respondsToSelector:@selector(flushMailCaches) ]) - { - return [NSException exceptionWithHTTPStatus: 500 /* Server Error */ - reason: - @"invalid client object (does not support flush)"]; - } - - [client flushMailCaches]; - - return [self redirectToLocation:@"view"]; -} - -- (NSString *) msgLabels -{ - NSMutableArray *labels; - NSEnumerator *flags; - NSString *currentFlag; - - labels = [NSMutableArray array]; - - flags = [[message objectForKey: @"flags"] objectEnumerator]; - while ((currentFlag = [flags nextObject])) - if ([currentFlag hasPrefix: @"$label"]) - [labels addObject: [currentFlag substringFromIndex: 1]]; - - return [labels componentsJoinedByString: @" "]; -} - -- (NSDictionary *) columnsMetaData -{ - NSMutableDictionary *columnsMetaData; - NSArray *tmpColumns, *tmpKeys; - - columnsMetaData = [NSMutableDictionary dictionaryWithCapacity:8]; - - tmpKeys = [NSArray arrayWithObjects: @"headerClass", @"headerId", @"value", - nil]; - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell sortableTableHeader", - @"subjectHeader", @"Subject", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"Subject"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell messageFlagColumn", - @"invisibleHeader", @"Flagged", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"Flagged"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell messageFlagColumn", - @"attachmentHeader", @"Attachment", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: - tmpColumns - forKeys: tmpKeys] - forKey: @"Attachment"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell", @"messageFlagHeader", - @"Unread", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns forKeys: tmpKeys] forKey: @"Unread"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell sortableTableHeader", - @"toHeader", @"To", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns forKeys: tmpKeys] forKey: @"To"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell sortableTableHeader", - @"fromHeader", @"From", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"From"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell sortableTableHeader", - @"dateHeader", @"Date", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"Date"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell", @"priorityHeader", - @"Priority", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"Priority"]; - - tmpColumns - = [NSArray arrayWithObjects: @"tbtv_headercell sortableTableHeader", @"sizeHeader", - @"Size", nil]; - [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns - forKeys: tmpKeys] - forKey: @"Size"]; - - return columnsMetaData; -} - -- (NSArray *) columnsDisplayOrder -{ - NSMutableArray *finalOrder, *invalid; - NSArray *available; - NSDictionary *metaData; - SOGoUserDefaults *ud; - unsigned int i; - - if (!columnsOrder) - { - ud = [[context activeUser] userDefaults]; - columnsOrder = [ud mailListViewColumnsOrder]; - - metaData = [self columnsMetaData]; - - invalid = [columnsOrder mutableCopy]; - [invalid autorelease]; - available = [metaData allKeys]; - [invalid removeObjectsInArray: available]; - if ([invalid count] > 0) - { - [self errorWithFormat: @"those column names specified in" - @" SOGoMailListViewColumnsOrder are invalid: '%@'", - [invalid componentsJoinedByString: @"', '"]]; - [self errorWithFormat: @" falling back on hardcoded column order"]; - columnsOrder = available; - } - - finalOrder = [columnsOrder mutableCopy]; - [finalOrder autorelease]; - if ([self showToAddress]) - { - i = [finalOrder indexOfObject: @"From"]; - if (i != NSNotFound) - [finalOrder replaceObjectAtIndex: i withObject: @"To"]; - } - else - { - i = [finalOrder indexOfObject: @"To"]; - if (i != NSNotFound) - [finalOrder replaceObjectAtIndex: i withObject: @"From"]; - } - - columnsOrder = [[self columnsMetaData] objectsForKeys: finalOrder - notFoundMarker: @""]; - [columnsOrder retain]; - } - - return columnsOrder; -} - -- (NSString *) columnsDisplayCount -{ - return [NSString stringWithFormat: @"%d", [[self columnsDisplayOrder] count]]; -} - -- (void) setCurrentColumn: (NSDictionary *) newCurrentColumn -{ - ASSIGN (currentColumn, newCurrentColumn); -} - -- (NSDictionary *) currentColumn -{ - return currentColumn; -} - -- (NSString *) columnTitle -{ - return [self labelForKey: [currentColumn objectForKey: @"value"]]; -} - -@end - -/* UIxMailListView */ diff --git a/UI/MailerUI/UIxMailMainFrame.h b/UI/MailerUI/UIxMailMainFrame.h index 450c8d458..177e3fd5e 100644 --- a/UI/MailerUI/UIxMailMainFrame.h +++ b/UI/MailerUI/UIxMailMainFrame.h @@ -29,6 +29,10 @@ { SOGoUserSettings *us; NSMutableDictionary *moduleSettings; + + NSArray *columnsOrder; + int folderType; + NSDictionary *currentColumn; } - (WOResponse *) getFoldersStateAction; @@ -42,6 +46,8 @@ - (NSString *) formattedMailtoString: (NGVCard *) card; +- (NSArray *) columnsDisplayOrder; + @end #endif /* UIXMAILMAINFRAME_H */ diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index 1f6915209..5c3e33ced 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -1,5 +1,5 @@ /* - Copyright (C) 2007-2009 Inverse inc. + Copyright (C) 2007-2010 Inverse inc. Copyright (C) 2004-2005 SKYRIX Software AG This file is part of SOGo. @@ -30,6 +30,7 @@ #import #import #import +#import #import #import @@ -114,11 +115,8 @@ - (NSString *) defaultColumnsOrder { - SOGoDomainDefaults *dd; - - dd = [[context activeUser] domainDefaults]; - - return [[dd mailListViewColumnsOrder] jsonRepresentation]; + return [[[self columnsDisplayOrder] objectsForKey: @"value" + notFoundMarker: @""] jsonRepresentation]; } - (NSString *) pageFormURL @@ -402,4 +400,186 @@ return [super defaultAction]; } +/** + * + * methods from UIxMailListView + */ + +- (id) init +{ + if ((self = [super init])) + { + folderType = 0; + } + + return self; +} +- (BOOL) showToAddress +{ + SOGoMailFolder *co; + + if (!folderType) + { + co = [self clientObject]; + if ([co isKindOfClass: [SOGoSentFolder class]] + || [co isKindOfClass: [SOGoDraftsFolder class]]) + folderType = 1; + else + folderType = -1; + } + + return (folderType == 1); +} + +- (NSDictionary *) columnsMetaData +{ + NSMutableDictionary *columnsMetaData; + NSArray *tmpColumns, *tmpKeys; + + columnsMetaData = [NSMutableDictionary dictionaryWithCapacity: 8]; + + tmpKeys = [NSArray arrayWithObjects: @"headerClass", @"headerId", @"value", + nil]; + tmpColumns + = [NSArray arrayWithObjects: @"messageSubjectColumn tbtv_headercell sortableTableHeader resizable", + @"subjectHeader", @"Subject", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Subject"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageFlagColumn tbtv_headercell", + @"invisibleHeader", @"Flagged", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Flagged"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageFlagColumn tbtv_headercell", + @"attachmentHeader", @"Attachment", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Attachment"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageFlagColumn tbtv_headercell", @"messageFlagHeader", + @"Unread", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns forKeys: tmpKeys] + forKey: @"Unread"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageAddressHeader tbtv_headercell sortableTableHeader resizable", + @"toHeader", @"To", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns forKeys: tmpKeys] + forKey: @"To"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageAddressColumn tbtv_headercell sortableTableHeader resizable", + @"fromHeader", @"From", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"From"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageDateColumn tbtv_headercell sortableTableHeader resizable", + @"dateHeader", @"Date", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Date"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messagePriorityColumn tbtv_headercell resizable", @"priorityHeader", + @"Priority", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Priority"]; + + tmpColumns + = [NSArray arrayWithObjects: @"messageSizeColumn tbtv_headercell sortableTableHeader", @"sizeHeader", + @"Size", nil]; + [columnsMetaData setObject: [NSDictionary dictionaryWithObjects: tmpColumns + forKeys: tmpKeys] + forKey: @"Size"]; + + return columnsMetaData; +} + +- (NSArray *) columnsDisplayOrder +{ + NSMutableArray *finalOrder, *invalid; + NSArray *available; + NSDictionary *metaData; + SOGoUserDefaults *ud; + unsigned int i; + + if (!columnsOrder) + { + ud = [[context activeUser] userDefaults]; + columnsOrder = [ud mailListViewColumnsOrder]; + + metaData = [self columnsMetaData]; + + invalid = [columnsOrder mutableCopy]; + [invalid autorelease]; + available = [metaData allKeys]; + [invalid removeObjectsInArray: available]; + if ([invalid count] > 0) + { + [self errorWithFormat: @"those column names specified in" + @" SOGoMailListViewColumnsOrder are invalid: '%@'", + [invalid componentsJoinedByString: @"', '"]]; + [self errorWithFormat: @" falling back on hardcoded column order"]; + columnsOrder = available; + } + + finalOrder = [columnsOrder mutableCopy]; + [finalOrder autorelease]; + if ([self showToAddress]) + { + i = [finalOrder indexOfObject: @"From"]; + if (i != NSNotFound) + { + [finalOrder removeObject: @"To"]; + [finalOrder replaceObjectAtIndex: i withObject: @"To"]; + } + } + else + { + i = [finalOrder indexOfObject: @"To"]; + if (i != NSNotFound) + { + [finalOrder removeObject: @"From"]; + [finalOrder replaceObjectAtIndex: i withObject: @"From"]; + } + } + + columnsOrder = [[self columnsMetaData] objectsForKeys: finalOrder + notFoundMarker: @""]; + [columnsOrder retain]; + } + + return columnsOrder; +} + +- (NSString *) columnsDisplayCount +{ + return [NSString stringWithFormat: @"%d", [[self columnsDisplayOrder] count]]; +} + +- (void) setCurrentColumn: (NSDictionary *) newCurrentColumn +{ + ASSIGN (currentColumn, newCurrentColumn); +} + +- (NSDictionary *) currentColumn +{ + return currentColumn; +} + +- (NSString *) columnTitle +{ + return [self labelForKey: [currentColumn objectForKey: @"value"]]; +} + @end /* UIxMailMainFrame */ + diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 4faf62fbe..67d3abe96 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -77,9 +77,20 @@ }; }; methods = { - view = { + getMail = { protectedBy = "View"; - pageName = "UIxMailListView"; + pageName = "UIxMailListActions"; + actionName = "getMail"; + }; + uids = { + protectedBy = ""; + pageName = "UIxMailListActions"; + actionName = "getSortedUIDs"; + }; + headers = { + protectedBy = ""; + pageName = "UIxMailListActions"; + actionName = "getHeaders"; }; subscribe = { protectedBy = ""; @@ -96,19 +107,6 @@ actionClass = "UIxMailFolderActions"; actionName = "quotas"; }; - index = { - protectedBy = "View"; - pageName = "UIxMailListView"; - }; - GET = { /* hack to make it work as the default method */ - protectedBy = "View"; - pageName = "UIxMailListView"; - }; - getMail = { - protectedBy = "View"; - pageName = "UIxMailListView"; - actionName = "getMail"; - }; expunge = { protectedBy = "View"; actionClass = "UIxMailFolderActions"; diff --git a/UI/Templates/MailerUI/UIxMailListView.wox b/UI/Templates/MailerUI/UIxMailListView.wox deleted file mode 100644 index 8c3048fe2..000000000 --- a/UI/Templates/MailerUI/UIxMailListView.wox +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -
- | - | - - - 0 - - - - - - - - | - | - -
diff --git a/UI/Templates/MailerUI/UIxMailMainFrame.wox b/UI/Templates/MailerUI/UIxMailMainFrame.wox index ea70743d5..9314a36b4 100644 --- a/UI/Templates/MailerUI/UIxMailMainFrame.wox +++ b/UI/Templates/MailerUI/UIxMailMainFrame.wox @@ -7,7 +7,7 @@ xmlns:label="OGo:label" className="UIxPageFrame" title="title" - const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js"> + const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js">