mirror of
https://github.com/inverse-inc/sogo.git
synced 2026-05-08 13:05:28 +00:00
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
This commit is contained in:
@@ -1,3 +1,46 @@
|
||||
2010-05-27 Francis Lachapelle <flachapelle@inverse.ca>
|
||||
|
||||
* 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 <wsourdeau@inverse.ca>
|
||||
|
||||
* SoObjects/Appointments/SOGoCalendarComponent.m
|
||||
|
||||
@@ -17,7 +17,7 @@ MailerUI_OBJC_FILES += \
|
||||
\
|
||||
UIxMailMainFrame.m \
|
||||
\
|
||||
UIxMailListView.m \
|
||||
UIxMailListActions.m \
|
||||
UIxMailView.m \
|
||||
UIxMailSourceView.m \
|
||||
UIxMailPopupView.m \
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef UIXMAILLISTVIEW_H
|
||||
#define UIXMAILLISTVIEW_H
|
||||
#ifndef UIXMAILLISTACTIONS_H
|
||||
#define UIXMAILLISTACTIONS_H
|
||||
|
||||
#import <SOGoUI/UIxComponent.h>
|
||||
|
||||
@@ -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 */
|
||||
@@ -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 <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSCharacterSet.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/SoObject+SoDAV.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGImap4/NGImap4Envelope.h>
|
||||
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
#import <Mailer/SOGoDraftsFolder.h>
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <Mailer/SOGoSentFolder.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/NSDictionary+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#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 <WOActionResults>) 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 <WOActionResults>) 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: @"<img src=\"%@\"/>", [self urlForResourceFilename: @"title_attachment_14x14.png"]]
|
||||
forKey: @"Attachment"];
|
||||
|
||||
if ([self isMessageFlagged])
|
||||
{
|
||||
[msg setObject: [NSString stringWithFormat: @"<img src=\"%@\" class=\"messageIsFlagged\">",
|
||||
[self urlForResourceFilename: @"flag.png"]]
|
||||
forKey: @"Flagged"];
|
||||
}
|
||||
else
|
||||
{
|
||||
[msg setObject: [NSString stringWithFormat: @"<img src=\"%@\">",
|
||||
[self urlForResourceFilename: @"dot.png"]]
|
||||
forKey: @"Flagged"];
|
||||
}
|
||||
|
||||
[msg setObject: [NSString stringWithFormat: @"<span>%@</span>",
|
||||
[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: @"<img src=\"%@\" class=\"mailerReadIcon\" title=\"%@\" title-markread=\"%@\" title-markunread=\"%@\" id=\"%@\"/>",
|
||||
[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 */
|
||||
@@ -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 <Foundation/NSCalendarDate.h>
|
||||
#import <Foundation/NSCharacterSet.h>
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSEnumerator.h>
|
||||
#import <Foundation/NSTimeZone.h>
|
||||
#import <Foundation/NSValue.h>
|
||||
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/SoObject+SoDAV.h>
|
||||
#import <NGObjWeb/NSException+HTTP.h>
|
||||
#import <NGExtensions/NSNull+misc.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
|
||||
#import <EOControl/EOQualifier.h>
|
||||
|
||||
#import <Mailer/NSString+Mail.h>
|
||||
#import <Mailer/SOGoDraftsFolder.h>
|
||||
#import <Mailer/SOGoMailFolder.h>
|
||||
#import <Mailer/SOGoMailObject.h>
|
||||
#import <Mailer/SOGoSentFolder.h>
|
||||
#import <SOGo/NSArray+Utilities.h>
|
||||
#import <SOGo/SOGoDateFormatter.h>
|
||||
#import <SOGo/SOGoUser.h>
|
||||
#import <SOGo/SOGoUserDefaults.h>
|
||||
|
||||
#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 */
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 <NGObjWeb/WORequest.h>
|
||||
#import <NGObjWeb/WOResponse.h>
|
||||
#import <NGObjWeb/SoComponent.h>
|
||||
#import <NGExtensions/NSObject+Logs.h>
|
||||
#import <NGExtensions/NSString+misc.h>
|
||||
|
||||
#import <Contacts/SOGoContactObject.h>
|
||||
@@ -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 */
|
||||
|
||||
|
||||
+13
-15
@@ -77,9 +77,20 @@
|
||||
};
|
||||
};
|
||||
methods = {
|
||||
view = {
|
||||
getMail = {
|
||||
protectedBy = "View";
|
||||
pageName = "UIxMailListView";
|
||||
pageName = "UIxMailListActions";
|
||||
actionName = "getMail";
|
||||
};
|
||||
uids = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "UIxMailListActions";
|
||||
actionName = "getSortedUIDs";
|
||||
};
|
||||
headers = {
|
||||
protectedBy = "<public>";
|
||||
pageName = "UIxMailListActions";
|
||||
actionName = "getHeaders";
|
||||
};
|
||||
subscribe = {
|
||||
protectedBy = "<public>";
|
||||
@@ -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";
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
<?xml version='1.0' standalone='yes'?>
|
||||
<!DOCTYPE table>
|
||||
<table id="messageList" cellspacing="0"
|
||||
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">
|
||||
<thead>
|
||||
<tr class="tableview"
|
||||
><var:foreach list="columnsDisplayOrder" item="currentColumn">
|
||||
<td var:class="currentColumn.headerClass" var:id="currentColumn.headerId">
|
||||
<var:if condition="currentColumn.value" const:value="Flagged">
|
||||
<img rsrc:src="empty.gif" width="100%" height="100%" const:title="" />
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Attachment">
|
||||
<img rsrc:src="title_attachment_14x14.png" label:title="$currentColumn.value" width="14"
|
||||
height="14"/>
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Unread">
|
||||
<img rsrc:src="title_read_14x14.png" label:title="$currentColumn.value" />
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Flagged" const:negate="YES">
|
||||
<var:if condition="currentColumn.value" const:value="Attachment" const:negate="YES">
|
||||
<var:if condition="currentColumn.value" const:value="Unread" const:negate="YES">
|
||||
<var:string var:value="columnTitle" />
|
||||
</var:if>
|
||||
</var:if>
|
||||
</var:if>
|
||||
</td>
|
||||
</var:foreach>
|
||||
</tr>
|
||||
<tr id="messageCountHeader" class="tableview"
|
||||
><td var:colspan="columnsDisplayCount" class="tbtv_navcell"
|
||||
><var:if condition="hasPrevious">
|
||||
<a href="#"
|
||||
idx="1"><var:string label:value="first"/></a> |
|
||||
<a href="#"
|
||||
var:idx="prevFirstMessageNumber"
|
||||
><var:string label:value="previous"/></a> |
|
||||
</var:if>
|
||||
<var:if condition="lastMessageNumber" const:negate="YES">
|
||||
0 <var:string label:value="message"/>
|
||||
</var:if>
|
||||
<var:if condition="lastMessageNumber" const:negate="0">
|
||||
<var:string value="firstMessageNumber" />
|
||||
<var:string label:value="msgnumber_to" />
|
||||
<var:string value="lastMessageNumber" />
|
||||
<var:string label:value="msgnumber_of" />
|
||||
<var:string value="sortedUIDs.count" />
|
||||
<var:if condition="hasNext"
|
||||
>| <a href="#"
|
||||
var:idx="nextFirstMessageNumber"
|
||||
><var:string label:value="next" /></a>
|
||||
| <a href="#"
|
||||
var:idx="lastFirstMessageNumber"
|
||||
><var:string label:value="last" /></a>
|
||||
</var:if>
|
||||
</var:if
|
||||
></td
|
||||
></tr
|
||||
>
|
||||
</thead>
|
||||
<tbody>
|
||||
<var:foreach list="messages" item="message">
|
||||
<tr var:class="messageRowStyleClass" var:id="msgRowID" var:labels="msgLabels"
|
||||
><var:foreach list="columnsDisplayOrder" item="currentColumn"
|
||||
><var:if condition="currentColumn.value" const:value="Flagged"
|
||||
><td class="messageFlagColumn messageFlag"
|
||||
><var:if condition="isMessageFlagged"
|
||||
><img rsrc:src="flag.png" const:class="messageIsFlagged"
|
||||
/></var:if
|
||||
><var:if condition="isMessageFlagged" const:negate="YES"
|
||||
><img rsrc:src="dot.png"
|
||||
/></var:if
|
||||
></td
|
||||
></var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Attachment"
|
||||
><td class="messageFlagColumn"
|
||||
><var:if condition="hasMessageAttachment"
|
||||
><img rsrc:src="title_attachment_14x14.png"
|
||||
/></var:if
|
||||
></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Subject"
|
||||
><td var:class="messageSubjectCellStyleClass" var:id="msgDivID"
|
||||
><var:string value="messageSubject"
|
||||
/></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="From"
|
||||
><td class="messageAddressColumn"
|
||||
><var:if condition="showToAddress" const:negate="YES"
|
||||
><var:string value="message.envelope.from" formatter="context.mailEnvelopeAddressFormatter"
|
||||
/></var:if
|
||||
><var:if condition="showToAddress"
|
||||
><var:string value="message.envelope.to" formatter="context.mailEnvelopeAddressFormatter"
|
||||
/></var:if
|
||||
></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="To"
|
||||
><td class="messageAddressColumn"
|
||||
><var:if condition="showToAddress" const:negate="YES"
|
||||
><var:string value="message.envelope.from" formatter="context.mailEnvelopeAddressFormatter"
|
||||
/></var:if
|
||||
><var:if condition="showToAddress"
|
||||
><var:string value="message.envelope.to" formatter="context.mailEnvelopeAddressFormatter"
|
||||
/></var:if
|
||||
></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Unread"
|
||||
><td class="messageFlagColumn"
|
||||
><var:if condition="isMessageRead"
|
||||
><img rsrc:src="dot.png"
|
||||
class="mailerReadIcon"
|
||||
label:title="Mark Unread"
|
||||
label:title-markread="Mark Read"
|
||||
label:title-markunread="Mark Unread"
|
||||
var:id="msgIconReadImgID"
|
||||
/></var:if
|
||||
><var:if condition="isMessageRead" const:negate="YES"
|
||||
><img rsrc:src="icon_unread.gif"
|
||||
class="mailerUnreadIcon"
|
||||
label:title="Mark Read"
|
||||
label:title-markread="Mark Read"
|
||||
label:title-markunread="Mark Unread"
|
||||
var:id="msgIconUnreadImgID"
|
||||
/></var:if
|
||||
></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Date"
|
||||
><td class="messageDateColumn"
|
||||
><var:string value="messageDate"
|
||||
/><entity name="nbsp"
|
||||
/></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Priority"
|
||||
><td class="messagePriorityColumn"
|
||||
><var:string value="messagePriority"
|
||||
/><entity name="nbsp"
|
||||
/></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Size"
|
||||
><td class="messagePriorityColumn"
|
||||
><var:string value="messageSize"
|
||||
/><entity name="nbsp"
|
||||
/></td
|
||||
></var:if>
|
||||
</var:foreach>
|
||||
</tr>
|
||||
</var:foreach>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -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">
|
||||
<script type="text/javascript">
|
||||
var textMailAccounts = '<var:string value="mailAccounts" const:escapeHTML="NO"/>';
|
||||
var textDefaultColumnsOrder = '<var:string value="defaultColumnsOrder" const:escapeHTML="NO"/>';
|
||||
@@ -215,7 +215,89 @@
|
||||
|
||||
<div id="rightPanel">
|
||||
<var:component className="UIxMailFilterPanel" qualifier="qualifier" />
|
||||
<div id="mailboxContent"><!-- space --></div>
|
||||
|
||||
<div id="mailboxContent">
|
||||
|
||||
<table id="messageListHeader" class="messageList" cellspacing="0">
|
||||
<thead>
|
||||
<tr class="tableview"
|
||||
><var:foreach list="columnsDisplayOrder" item="currentColumn">
|
||||
<th var:class="currentColumn.headerClass" var:id="currentColumn.headerId">
|
||||
<var:if condition="currentColumn.value" const:value="Flagged">
|
||||
<img rsrc:src="empty.gif" width="100%" height="100%" const:title="" />
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Attachment">
|
||||
<img rsrc:src="title_attachment_14x14.png" label:title="$currentColumn.value" width="14"
|
||||
height="14"/>
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Unread">
|
||||
<img rsrc:src="title_read_14x14.png" label:title="$currentColumn.value" />
|
||||
</var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Flagged" const:negate="YES">
|
||||
<var:if condition="currentColumn.value" const:value="Attachment" const:negate="YES">
|
||||
<var:if condition="currentColumn.value" const:value="Unread" const:negate="YES">
|
||||
<var:string var:value="columnTitle" />
|
||||
</var:if>
|
||||
</var:if>
|
||||
</var:if>
|
||||
</th>
|
||||
</var:foreach>
|
||||
</tr>
|
||||
<tr id="messageCountHeader" class="tableview"
|
||||
><th var:colspan="columnsDisplayCount" class="tbtv_navcell"
|
||||
><!-- empty --></th
|
||||
></tr
|
||||
>
|
||||
</thead>
|
||||
</table>
|
||||
|
||||
<div id="mailboxList">
|
||||
<table id="messageListBody" class="messageList" cellspacing="0">
|
||||
<tbody>
|
||||
<tr const:style="display: none;"
|
||||
><var:foreach list="columnsDisplayOrder" item="currentColumn"
|
||||
><var:if condition="currentColumn.value" const:value="Flagged"
|
||||
><td class="messageFlagColumn messageFlag"
|
||||
><!-- flagged --></td
|
||||
></var:if>
|
||||
<var:if condition="currentColumn.value" const:value="Attachment"
|
||||
><td class="messageFlagColumn"
|
||||
><!-- attachment --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Subject"
|
||||
><td class="messageSubjectColumn"
|
||||
><!-- subject --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="From"
|
||||
><td class="messageAddressColumn"
|
||||
><!-- from --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="To"
|
||||
><td class="messageAddressColumn"
|
||||
><!-- to --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Unread"
|
||||
><td class="messageFlagColumn"
|
||||
><!-- unread --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Date"
|
||||
><td class="messageDateColumn"
|
||||
><!-- date --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Priority"
|
||||
><td class="messagePriorityColumn"
|
||||
><!-- priority --></td
|
||||
></var:if
|
||||
><var:if condition="currentColumn.value" const:value="Size"
|
||||
><td class="messageSizeColumn"
|
||||
><!-- size --></td
|
||||
></var:if>
|
||||
</var:foreach>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="dragHandle" id="rightDragHandle"><!-- space --></div>
|
||||
<div id="messageContent"><!-- space --></div>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,7 @@ Element.addMethods({
|
||||
if (element.bind)
|
||||
element.bind();
|
||||
},
|
||||
|
||||
|
||||
childNodesWithTag: function(element, tagName) {
|
||||
element = $(element);
|
||||
|
||||
@@ -165,15 +165,18 @@ Element.addMethods({
|
||||
element.addClassName('_selected');
|
||||
|
||||
var parent = element.up();
|
||||
if (!parent.selectedElements)
|
||||
if (!parent.selectedElements) {
|
||||
// Selected nodes are kept in a array at the
|
||||
// container level.
|
||||
parent.selectedElements = new Array();
|
||||
parent.selectedIds = new Array();
|
||||
}
|
||||
for (var i = 0; i < parent.selectedElements.length; i++)
|
||||
if (parent.selectedElements[i] == element) return;
|
||||
parent.selectedElements.push(element); // use index instead ?
|
||||
parent.selectedIds.push(element.id);
|
||||
},
|
||||
|
||||
|
||||
selectRange: function(element, startIndex, endIndex) {
|
||||
element = $(element);
|
||||
var s;
|
||||
@@ -202,10 +205,11 @@ Element.addMethods({
|
||||
deselect: function(element) {
|
||||
element = $(element);
|
||||
element.removeClassName('_selected');
|
||||
|
||||
var parent = element.up();
|
||||
if (parent && parent.selectedElements)
|
||||
if (parent && parent.selectedElements) {
|
||||
parent.selectedElements = parent.selectedElements.without(element);
|
||||
parent.selectedIds = parent.selectedIds.without(element.id);
|
||||
}
|
||||
},
|
||||
|
||||
deselectAll: function(element) {
|
||||
@@ -215,6 +219,25 @@ Element.addMethods({
|
||||
element.selectedElements[i].removeClassName('_selected');
|
||||
element.selectedElements = null;
|
||||
}
|
||||
if (element.selectedIds) {
|
||||
for (var i = 0; i < element.selectedIds.length; i++) {
|
||||
var e = element.down('#' + element.selectedIds[i]);
|
||||
if (e && e.hasClassName('_selected'))
|
||||
e.removeClassName('_selected');
|
||||
}
|
||||
element.selectedIds = null;
|
||||
}
|
||||
},
|
||||
|
||||
refreshSelectionByIds: function(element) {
|
||||
element = $(element);
|
||||
if (element.selectedIds) {
|
||||
for (var i = 0; i < element.selectedIds.length; i++) {
|
||||
var e = element.down('#'+element.selectedIds[i]);
|
||||
if (e && !e.hasClassName('_selected'))
|
||||
e.addClassName('_selected');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setCaretTo: function(element, pos) {
|
||||
|
||||
+119
-131
@@ -18,7 +18,6 @@
|
||||
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
||||
02111-1307, USA.
|
||||
*/
|
||||
/* TODO: is the section below used in the mailer? */
|
||||
|
||||
DIV#leftPanel
|
||||
{
|
||||
@@ -53,7 +52,10 @@ DIV#mailboxContent
|
||||
right: 0px;
|
||||
height: 15.5em;
|
||||
border-left: 1px solid #9B9B9B;
|
||||
overflow: auto;
|
||||
overflow: hidden; }
|
||||
|
||||
DIV#mailboxList
|
||||
{ overflow: auto;
|
||||
overflow-x: hidden; }
|
||||
|
||||
DIV#messageContent
|
||||
@@ -225,81 +227,160 @@ DIV.dTreeNode SPAN.unseen
|
||||
|
||||
/* mail tableview */
|
||||
|
||||
TD.mailer_readmailsubject
|
||||
{
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
/* messages table with fixed headers */
|
||||
|
||||
TR.mailer_unreadmail TD
|
||||
{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
TABLE.messageList
|
||||
{ width: 100%;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none; }
|
||||
|
||||
TABLE.messageList TH,
|
||||
TABLE.messageList TD
|
||||
{ height: 20px;
|
||||
min-height: 20px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding: 2px 3px;
|
||||
min-width: 22px;
|
||||
margin: 0;
|
||||
white-space: nowrap; }
|
||||
|
||||
TR#messageCountHeader TH
|
||||
{ border-top: 0px; }
|
||||
|
||||
TABLE.messageList TD
|
||||
{ border-right: 1px solid #fff; }
|
||||
|
||||
TABLE.messageList .messageFlagColumn
|
||||
{ width: 22px;
|
||||
max-width: 22px;
|
||||
text-align: center; }
|
||||
|
||||
TABLE.messageList .messageSubjectColumn
|
||||
{ max-width: 30%;
|
||||
width: 30%; }
|
||||
|
||||
TABLE.messageList .messageSubjectColumn SPAN
|
||||
{ padding-left: 20px; }
|
||||
|
||||
TABLE.messageList .messageAddressColumn
|
||||
{ max-width: 18%;
|
||||
width: 18%; }
|
||||
|
||||
TABLE.messageList .messageDateColumn
|
||||
{ max-width: 22%;
|
||||
width: 22%; }
|
||||
|
||||
TABLE.messageList .messagePriorityColumn
|
||||
{ width: 60px;
|
||||
max-width: 60px; }
|
||||
|
||||
TABLE.messageList .messageSizeColumn
|
||||
{ min-width: 40px; }
|
||||
|
||||
TR#rowTop TD
|
||||
{ height: 0;
|
||||
min-height: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0; }
|
||||
|
||||
TABLE.messageList TR._selected TD
|
||||
{ background-color: #9ABCD8;
|
||||
color: #fff; }
|
||||
|
||||
TABLE.messageList TR._deleted TD
|
||||
{ text-decoration: line-through; }
|
||||
|
||||
TABLE.messageList TR[labels~="label5"] TD
|
||||
{ color: #9c309c; }
|
||||
|
||||
TABLE.messageList TR[labels~="label5"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #9c309c; }
|
||||
|
||||
TABLE.messageList TR[labels~="label4"] TD
|
||||
{ color: #3130ff; }
|
||||
|
||||
TABLE.messageList TR[labels~="label4"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #3130ff; }
|
||||
|
||||
TABLE.messageList TR[labels~="label3"] TD
|
||||
{ color: #009a00; }
|
||||
|
||||
TABLE.messageList TR[labels~="label3"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #009a00; }
|
||||
|
||||
TABLE.messageList TR[labels~="label2"] TD
|
||||
{ color: #ff9a00; }
|
||||
|
||||
TABLE.messageList TR[labels~="label2"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #ff9a00; }
|
||||
|
||||
TABLE.messageList TR[labels~="label1"] TD
|
||||
{ color: #f00; }
|
||||
|
||||
TABLE.messageList TR[labels~="label1"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #f00; }
|
||||
|
||||
TABLE.messageList TR.mailer_unreadmail TD
|
||||
{ font-weight: bold !important; }
|
||||
|
||||
TR.mailer_unreadmail TD.messageSubjectColumn
|
||||
{
|
||||
background-image: url(icon-new.png);
|
||||
{ background-image: url(icon-new.png);
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0px 0px !important;
|
||||
padding-left: 20px !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
background-position: 0px 3px !important;
|
||||
font-weight: bold !important; }
|
||||
|
||||
TD.mailer_repliedmailsubject
|
||||
{
|
||||
background-image: url(icon-replied.png) !important;
|
||||
TR.mailer_repliedmailsubject TD.messageSubjectColumn
|
||||
{ background-image: url(icon-replied.png) !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0px 0px !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
background-position: 0px 0px !important; }
|
||||
|
||||
TD.mailer_forwardedmailsubject
|
||||
{
|
||||
background-image: url(icon-forwarded.png) !important;
|
||||
{ background-image: url(icon-forwarded.png) !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0px 0px !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
background-position: 0px 0px !important; }
|
||||
|
||||
TD.mailer_forwardedrepliedmailsubject
|
||||
{
|
||||
background-image: url(icon-forwarded-replied.png) !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0px 0px !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
background-position: 0px 0px !important; }
|
||||
|
||||
TD.mailer_deletedmailsubject
|
||||
{
|
||||
background-image: url(icon-deleted.png) !important;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: 0px 0px !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
background-position: 0px 0px !important; }
|
||||
|
||||
TD.mailer_readmailsubject a
|
||||
TD.mailer_readmailsubject A
|
||||
{
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
TD.mailer_unreadmailsubject a
|
||||
TD.mailer_unreadmailsubject A
|
||||
{
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
tr.mailer_listcell_deleted td
|
||||
TR.mailer_listcell_deleted TD
|
||||
{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
tr.mailer_listcell_regular td a
|
||||
TR.mailer_listcell_regular TD A
|
||||
{
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* fields (key/value UI), eg used in mail viewer */
|
||||
/* mail viewer */
|
||||
INPUT#editDraftButton
|
||||
{
|
||||
position: absolute;
|
||||
@@ -547,99 +628,6 @@ TABLE#addr_table
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
TABLE#messageList
|
||||
{ width: 100%;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none; }
|
||||
|
||||
TABLE#messageList TD,
|
||||
TABLE#messageList TH
|
||||
{ height: 20px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap; }
|
||||
|
||||
TABLE#messageList TH
|
||||
{ white-space: pre; }
|
||||
|
||||
TABLE#messageList TBODY TD
|
||||
{ white-space: pre; }
|
||||
|
||||
TD#messageFlagHeader,
|
||||
TABLE#messageList TD.messageFlagColumn
|
||||
{ width: 22px;
|
||||
text-align: center; }
|
||||
|
||||
TABLE#messageList TD.messageFlagColumn IMG
|
||||
{ width: 14px;
|
||||
height: 14px; }
|
||||
|
||||
TD#subjectHeader,
|
||||
TABLE#messageList TD.mailer_readmailsubject
|
||||
{ /*width: 40%;*/
|
||||
min-width: 35%; }
|
||||
|
||||
TD#fromHeader,
|
||||
TABLE#messageList TD.messageAddressColumn
|
||||
{ /*width: 35%;*/
|
||||
min-width: 30%;
|
||||
overflow: hidden; }
|
||||
|
||||
TD#dateHeader
|
||||
{ /*width: 25%;*/
|
||||
overflow: hidden; }
|
||||
|
||||
TD#priorityHeader,
|
||||
TD#sizeHeader
|
||||
{ /*width: 25%;*/
|
||||
width: 7%;
|
||||
overflow: hidden; }
|
||||
|
||||
TABLE#messageList TR._selected TD
|
||||
{
|
||||
background-color: #9ABCD8;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
TABLE#messageList TR._deleted TD
|
||||
{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
TABLE#messageList TR[labels~="label5"] TD
|
||||
{ color: #9c309c; }
|
||||
|
||||
TABLE#messageList TR[labels~="label5"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #9c309c; }
|
||||
|
||||
TABLE#messageList TR[labels~="label4"] TD
|
||||
{ color: #3130ff; }
|
||||
|
||||
TABLE#messageList TR[labels~="label4"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #3130ff; }
|
||||
|
||||
TABLE#messageList TR[labels~="label3"] TD
|
||||
{ color: #009a00; }
|
||||
|
||||
TABLE#messageList TR[labels~="label3"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #009a00; }
|
||||
|
||||
TABLE#messageList TR[labels~="label2"] TD
|
||||
{ color: #ff9a00; }
|
||||
|
||||
TABLE#messageList TR[labels~="label2"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #ff9a00; }
|
||||
|
||||
TABLE#messageList TR[labels~="label1"] TD
|
||||
{ color: #f00; }
|
||||
|
||||
TABLE#messageList TR[labels~="label1"]._selected TD
|
||||
{ color: #fff;
|
||||
background-color: #f00; }
|
||||
|
||||
/* quota indicator */
|
||||
DIV.quota
|
||||
{ border-bottom: 1px solid #ccc;
|
||||
|
||||
+230
-240
@@ -10,14 +10,6 @@ if (typeof textMailAccounts != 'undefined') {
|
||||
else
|
||||
mailAccounts = new Array();
|
||||
}
|
||||
|
||||
var defaultColumnsOrder;
|
||||
if (typeof textDefaultColumnsOrder != 'undefined') {
|
||||
if (textDefaultColumnsOrder.length > 0)
|
||||
defaultColumnsOrder = textDefaultColumnsOrder.evalJSON(true);
|
||||
else
|
||||
defaultColumnsOrder = new Array();
|
||||
}
|
||||
|
||||
var Mailer = {
|
||||
currentMailbox: null,
|
||||
@@ -27,7 +19,9 @@ var Mailer = {
|
||||
cachedMessages: new Array(),
|
||||
foldersStateTimer: false,
|
||||
popups: new Array(),
|
||||
quotas: null
|
||||
quotas: null,
|
||||
|
||||
dataTable: null
|
||||
};
|
||||
|
||||
var usersRightsWindowHeight = 320;
|
||||
@@ -113,8 +107,8 @@ function flagMailInWindow (win, msguid, flagged) {
|
||||
var row = win.$("row_" + msguid);
|
||||
|
||||
if (row) {
|
||||
var col = row.select("TD.messageFlag").first();
|
||||
var img = col.select("img").first();
|
||||
var col = row.down("TD.messageFlag");
|
||||
var img = col.down("img");
|
||||
if (flagged) {
|
||||
img.setAttribute("src", ResourcesURL + "/flag.png");
|
||||
img.addClassName("messageIsFlagged");
|
||||
@@ -128,36 +122,34 @@ function flagMailInWindow (win, msguid, flagged) {
|
||||
|
||||
function markMailInWindow(win, msguid, markread) {
|
||||
var row = win.$("row_" + msguid);
|
||||
var subjectCell = win.$("div_" + msguid);
|
||||
var unseenCount = 0;
|
||||
|
||||
if (row && subjectCell) {
|
||||
if (row) {
|
||||
if (markread) {
|
||||
if (row.hasClassName("mailer_unreadmail")) {
|
||||
row.removeClassName("mailer_unreadmail");
|
||||
subjectCell.addClassName("mailer_readmailsubject");
|
||||
var img = win.$("unreaddiv_" + msguid);
|
||||
var img = win.$("readdiv_" + msguid);
|
||||
if (img) {
|
||||
img.removeClassName("mailerUnreadIcon");
|
||||
img.addClassName("mailerReadIcon");
|
||||
img.setAttribute("id", "readdiv_" + msguid);
|
||||
img.setAttribute("src", ResourcesURL + "/dot.png");
|
||||
var title = img.getAttribute("title-markunread");
|
||||
if (title)
|
||||
img.setAttribute("title", title);
|
||||
}
|
||||
else {
|
||||
log ("No IMG found for " + msguid);
|
||||
}
|
||||
unseenCount = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!row.hasClassName("mailer_unreadmail")) {
|
||||
row.addClassName("mailer_unreadmail");
|
||||
subjectCell.removeClassName('mailer_readmailsubject');
|
||||
var img = win.$("readdiv_" + msguid);
|
||||
if (img) {
|
||||
img.removeClassName("mailerReadIcon");
|
||||
img.addClassName("mailerUnreadIcon");
|
||||
img.setAttribute("id", "unreaddiv_" + msguid);
|
||||
img.setAttribute("src", ResourcesURL + "/icon_unread.gif");
|
||||
var title = img.getAttribute("title-markread");
|
||||
if (title)
|
||||
@@ -169,7 +161,7 @@ function markMailInWindow(win, msguid, markread) {
|
||||
}
|
||||
|
||||
if (unseenCount != 0) {
|
||||
/* Update unseen count only if it's the inbox */
|
||||
// Update unseen count only if it's the inbox
|
||||
for (var i = 0; i < mailboxTree.aNodes.length; i++)
|
||||
if (mailboxTree.aNodes[i].datatype == "inbox") break;
|
||||
if (i != mailboxTree.aNodes.length && Mailer.currentMailbox == mailboxTree.aNodes[i].dataname)
|
||||
@@ -194,7 +186,7 @@ function openMessageWindowsForSelection(action, firstOnly) {
|
||||
window.location.href = parts.join("/");
|
||||
}
|
||||
else {
|
||||
var messageList = $("messageList");
|
||||
var messageList = $("messageListBody");
|
||||
var rows = messageList.getSelectedRowsId();
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
@@ -236,7 +228,9 @@ function mailListMarkMessage(event) {
|
||||
}
|
||||
|
||||
function mailListMarkMessageCallback(http) {
|
||||
if (isHttpStatus204(http.status)) {
|
||||
if (isHttpStatus204(http.status)
|
||||
|| http.status == 304) { // In some cases, Safari returns a 304 even
|
||||
// though SOGo returns a 204!
|
||||
var data = http.callbackData;
|
||||
markMailInWindow(data["window"], data["msguid"], data["markread"]);
|
||||
}
|
||||
@@ -264,6 +258,7 @@ function mailListFlagMessageToggle (e) {
|
||||
|
||||
triggerAjaxRequest(url, mailListFlagMessageToggleCallback, data);
|
||||
}
|
||||
|
||||
function mailListFlagMessageToggleCallback (http) {
|
||||
if (isHttpStatus204(http.status)) {
|
||||
var data = http.callbackData;
|
||||
@@ -326,7 +321,7 @@ function onDocumentKeydown(event) {
|
||||
nextRow = row.next("tr");
|
||||
else
|
||||
nextRow = row.previous("tr");
|
||||
if (nextRow) {
|
||||
if (nextRow && nextRow.id != 'rowTop' && nextRow.id != 'rowBottom') {
|
||||
Mailer.currentMessages[Mailer.currentMailbox] = nextRow.getAttribute("id").substr(4);
|
||||
row.up().deselectAll();
|
||||
|
||||
@@ -355,10 +350,11 @@ function onDocumentKeydown(event) {
|
||||
/* bulk delete of messages */
|
||||
|
||||
function deleteSelectedMessages(sender) {
|
||||
var messageList = $("messageList").down("TBODY");
|
||||
var messageList = $("messageListBody").down("TBODY");
|
||||
var rows = messageList.getSelectedNodes();
|
||||
var uids = new Array(); // message IDs
|
||||
var paths = new Array(); // row IDs
|
||||
var unseenCount = 0;
|
||||
|
||||
if (rows.length > 0) {
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
@@ -368,6 +364,17 @@ function deleteSelectedMessages(sender) {
|
||||
rows[i].hide();
|
||||
uids.push(uid);
|
||||
paths.push(path);
|
||||
if (rows[i].hasClassName("mailer_unreadmail"))
|
||||
unseenCount--;
|
||||
}
|
||||
messageList.deselectAll();
|
||||
updateMessageListCounter(0 - rows.length, true);
|
||||
if (unseenCount < 0) {
|
||||
// Update unseen count only if it's the inbox
|
||||
for (var i = 0; i < mailboxTree.aNodes.length; i++)
|
||||
if (mailboxTree.aNodes[i].datatype == "inbox") break;
|
||||
if (i != mailboxTree.aNodes.length && Mailer.currentMailbox == mailboxTree.aNodes[i].dataname)
|
||||
updateStatusFolders(unseenCount, true);
|
||||
}
|
||||
var url = ApplicationBaseURL + encodeURI(Mailer.currentMailbox) + "/batchDelete";
|
||||
var parameters = "uid=" + uids.join(",");
|
||||
@@ -391,31 +398,35 @@ function deleteSelectedMessagesCallback(http) {
|
||||
var div = $('messageContent');
|
||||
if (Mailer.currentMessages[Mailer.currentMailbox] == data["id"][i]) {
|
||||
div.update();
|
||||
Mailer.currentMessages[Mailer.currentMailbox] = null;
|
||||
Mailer.currentMessages[Mailer.currentMailbox] = null;
|
||||
}
|
||||
var row = $("row_" + data["id"][i]);
|
||||
if (deleteMessageRequestCount == 0) {
|
||||
var nextRow = row.next("tr");
|
||||
if (!nextRow)
|
||||
nextRow = row.previous("tr");
|
||||
// row.addClassName("deleted"); // when we'll offer "mark as deleted"
|
||||
if (nextRow) {
|
||||
Mailer.currentMessages[Mailer.currentMailbox] = nextRow.getAttribute("id").substr(4);
|
||||
nextRow.selectElement();
|
||||
loadMessage(Mailer.currentMessages[Mailer.currentMailbox]);
|
||||
var row = $("row_" + data["id"][i]);
|
||||
if (row) {
|
||||
var nextRow = row.next("tr");
|
||||
if (!nextRow.id.startsWith('row_'))
|
||||
nextRow = row.previous("tr");
|
||||
// row.addClassName("deleted"); // when we'll offer "mark as deleted"
|
||||
if (nextRow.id.startsWith('row_')) {
|
||||
Mailer.currentMessages[Mailer.currentMailbox] = nextRow.getAttribute("id").substr(4);
|
||||
nextRow.selectElement();
|
||||
loadMessage(Mailer.currentMessages[Mailer.currentMailbox]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
div.update();
|
||||
}
|
||||
refreshCurrentFolder();
|
||||
Mailer.dataTable.remove(data["id"][i]);
|
||||
Mailer.dataTable.render();
|
||||
}
|
||||
else {
|
||||
Mailer.dataTable.remove(data["id"][i]);
|
||||
}
|
||||
row.parentNode.removeChild(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log ("deleteSelectedMessagesCallback: problem during ajax request " + http.status);
|
||||
window.alert(getLabel("Operation failed"));
|
||||
refreshCurrentFolder();
|
||||
}
|
||||
}
|
||||
@@ -485,15 +496,33 @@ function onMailboxTreeItemClick(event) {
|
||||
if (head.rows[1])
|
||||
head.rows[1].firstChild.update();
|
||||
}
|
||||
else
|
||||
else {
|
||||
var datatype = this.parentNode.getAttribute("datatype");
|
||||
if (datatype == 'draft' || datatype == 'sent')
|
||||
toggleAddressColumn("from", "to");
|
||||
else
|
||||
toggleAddressColumn("to", "from");
|
||||
|
||||
openMailbox(mailbox);
|
||||
}
|
||||
|
||||
Event.stop(event);
|
||||
}
|
||||
|
||||
function toggleAddressColumn(search, replace) {
|
||||
var header = $(search + "Header");
|
||||
if (header) {
|
||||
header.id = replace + "Header";
|
||||
header.update(getLabel(replace.capitalize()));
|
||||
var i = UserDefaults["SOGoMailListViewColumnsOrder"].indexOf(search.capitalize());
|
||||
if (i >= 0)
|
||||
UserDefaults["SOGoMailListViewColumnsOrder"][i] = replace.capitalize();
|
||||
}
|
||||
}
|
||||
|
||||
function onMailboxMenuMove(event) {
|
||||
var targetMailbox;
|
||||
var messageList = $("messageList").down("TBODY");
|
||||
var messageList = $("messageListBody").down("TBODY");
|
||||
var rows = messageList.getSelectedNodes();
|
||||
var uids = new Array(); // message IDs
|
||||
var paths = new Array(); // row IDs
|
||||
@@ -534,7 +563,7 @@ function onMailboxMenuMove(event) {
|
||||
}
|
||||
|
||||
function onMailboxMenuCopy(event) {
|
||||
var messageList = $("messageList").down("TBODY");
|
||||
var messageList = $("messageListBody").down("TBODY");
|
||||
var rows = messageList.getSelectedNodes();
|
||||
var uids = new Array(); // message IDs
|
||||
var paths = new Array(); // row IDs
|
||||
@@ -590,138 +619,34 @@ function composeNewMessage() {
|
||||
}
|
||||
}
|
||||
|
||||
function openMailbox(mailbox, reload, idx, updateStatus) {
|
||||
function openMailbox(mailbox, reload, updateStatus) {
|
||||
if (mailbox != Mailer.currentMailbox || reload) {
|
||||
Mailer.currentMailbox = mailbox;
|
||||
var url = ApplicationBaseURL + encodeURI(mailbox) + "/view?noframe=1";
|
||||
|
||||
if (!reload || idx) {
|
||||
var url = ApplicationBaseURL + encodeURI(mailbox);
|
||||
var urlParams = new Hash();
|
||||
|
||||
if (!reload) {
|
||||
var messageContent = $("messageContent");
|
||||
messageContent.update();
|
||||
$("messageCountHeader").down().update();
|
||||
lastClickedRow = -1; // from generic.js
|
||||
}
|
||||
|
||||
var currentMessage;
|
||||
|
||||
if (!idx) {
|
||||
currentMessage = Mailer.currentMessages[mailbox];
|
||||
if (currentMessage) {
|
||||
url += '&pageforuid=' + currentMessage;
|
||||
if (!reload)
|
||||
loadMessage(currentMessage);
|
||||
}
|
||||
}
|
||||
|
||||
var searchValue = search["value"];
|
||||
if (searchValue && searchValue.length > 0)
|
||||
url += ("&search=" + search["criteria"]
|
||||
+ "&value=" + escape(searchValue.utf8encode()));
|
||||
if (searchValue && searchValue.length > 0) {
|
||||
urlParams.set("search", search["criteria"]);
|
||||
urlParams.set("value", escape(searchValue.utf8encode()));
|
||||
}
|
||||
var sortAttribute = sorting["attribute"];
|
||||
if (sortAttribute && sortAttribute.length > 0)
|
||||
url += ("&sort=" + sorting["attribute"]
|
||||
+ "&asc=" + sorting["ascending"]);
|
||||
if (idx)
|
||||
url += "&idx=" + idx;
|
||||
|
||||
if (document.messageListAjaxRequest) {
|
||||
document.messageListAjaxRequest.aborted = true;
|
||||
document.messageListAjaxRequest.abort();
|
||||
}
|
||||
|
||||
var mailboxContent = $("mailboxContent");
|
||||
if (mailboxContent.getStyle('visibility') == "hidden") {
|
||||
mailboxContent.setStyle({ visibility: "visible" });
|
||||
var rightDragHandle = $("rightDragHandle");
|
||||
rightDragHandle.setStyle({ visibility: "visible" });
|
||||
messageContent.setStyle({ top: (rightDragHandle.offsetTop
|
||||
+ rightDragHandle.offsetHeight
|
||||
+ 'px') });
|
||||
}
|
||||
document.messageListAjaxRequest
|
||||
= triggerAjaxRequest(url, messageListCallback,
|
||||
currentMessage);
|
||||
|
||||
if (updateStatus != false)
|
||||
getStatusFolders();
|
||||
}
|
||||
}
|
||||
|
||||
function openMailboxAtIndex(event) {
|
||||
openMailbox(Mailer.currentMailbox, true, this.getAttribute("idx"));
|
||||
|
||||
Event.stop(event);
|
||||
}
|
||||
|
||||
function messageListCallback(http) {
|
||||
var div = $('mailboxContent');
|
||||
var table = $('messageList');
|
||||
|
||||
var columnsOrder = UserDefaults["SOGoMailListViewColumnsOrder"];
|
||||
if ( typeof(columnsOrder) == "undefined" ) {
|
||||
columnsOrder = defaultColumnsOrder;
|
||||
}
|
||||
var addrIndex = 3;
|
||||
for(var i=0; i<columnsOrder.length; i++) {
|
||||
if (columnsOrder[i] == "From" || columnsOrder[i] == "To") {
|
||||
addrIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (http.status == 200) {
|
||||
document.messageListAjaxRequest = null;
|
||||
|
||||
if (table) {
|
||||
// Update table
|
||||
var thead = table.tHead;
|
||||
var addressHeaderCell = thead.rows[0].cells[addrIndex];
|
||||
var tbody = table.tBodies[0];
|
||||
var tmp = document.createElement('div');
|
||||
$(tmp).update(http.responseText);
|
||||
|
||||
var newRows = tmp.firstChild.tHead.rows;
|
||||
thead.rows[1].parentNode.replaceChild(newRows[1], thead.rows[1]);
|
||||
addressHeaderCell.replaceChild(newRows[0].cells[addrIndex].lastChild,
|
||||
addressHeaderCell.lastChild);
|
||||
addressHeaderCell.setAttribute("id", newRows[0].cells[addrIndex].getAttribute("id"));
|
||||
table.replaceChild(tmp.firstChild.tBodies[0], tbody);
|
||||
configureMessageListEvents(table);
|
||||
}
|
||||
else {
|
||||
// Add table
|
||||
div.update(http.responseText);
|
||||
table = $("messageList");
|
||||
configureMessageListEvents(table);
|
||||
TableKit.Resizable.init(table, {'trueResize' : true, 'keepWidth' : true});
|
||||
configureDraggables();
|
||||
}
|
||||
configureMessageListBodyEvents(table);
|
||||
|
||||
var selected = http.callbackData;
|
||||
if (selected) {
|
||||
var row = $("row_" + selected);
|
||||
if (row) {
|
||||
row.selectElement();
|
||||
lastClickedRow = row.rowIndex - $(row).up('table').down('thead').getElementsByTagName('tr').length;
|
||||
var rowPosition = row.rowIndex * row.getHeight();
|
||||
if (rowPosition < div.scrollTop
|
||||
|| rowPosition > div.scrollTop + div.getHeight ())
|
||||
div.scrollTop = rowPosition; // scroll to selected message
|
||||
}
|
||||
else
|
||||
$("messageContent").update();
|
||||
}
|
||||
else
|
||||
div.scrollTop = 0;
|
||||
|
||||
if (sorting["attribute"] && sorting["attribute"].length > 0) {
|
||||
if (sortAttribute && sortAttribute.length > 0) {
|
||||
urlParams.set("sort", sorting["attribute"]);
|
||||
urlParams.set("asc", sorting["ascending"]);
|
||||
|
||||
var sortHeader = $(sorting["attribute"] + "Header");
|
||||
|
||||
if (sortHeader) {
|
||||
var sortImages = $(table.tHead).select(".sortImage");
|
||||
var sortImages = sortHeader.up('THEAD').select(".sortImage");
|
||||
$(sortImages).each(function(item) {
|
||||
item.remove();
|
||||
});
|
||||
|
||||
var sortImage = createElement("img", "messageSortImage", "sortImage");
|
||||
sortHeader.insertBefore(sortImage, sortHeader.firstChild);
|
||||
if (sorting["ascending"])
|
||||
@@ -730,13 +655,73 @@ function messageListCallback(http) {
|
||||
sortImage.src = ResourcesURL + "/arrow-up.png";
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : refresh mailbox without removing all rows.
|
||||
var messageList = $("messageListBody").down('TBODY');
|
||||
var dataSource = new SOGoMailDataSource(Mailer.dataTable, url);
|
||||
Mailer.dataTable.setSource('SOGoMailDataSource', url, urlParams);
|
||||
messageList.deselectAll();
|
||||
Mailer.dataTable.render();
|
||||
configureDraggables();
|
||||
Mailer.currentMailbox = mailbox;
|
||||
|
||||
/*
|
||||
// TODO : restore previously selected message.
|
||||
var currentMessage = Mailer.currentMessages[mailbox];
|
||||
if (currentMessage) {
|
||||
Mailer.dataTable.render(currentMessage);
|
||||
if (!reload)
|
||||
loadMessage(currentMessage);
|
||||
}
|
||||
*/
|
||||
|
||||
if (updateStatus != false)
|
||||
getStatusFolders();
|
||||
}
|
||||
else {
|
||||
var data = http.responseText;
|
||||
var msg = data.replace(/^(.*\n)*.*<p>((.*\n)*.*)<\/p>(.*\n)*.*$/, "$2");
|
||||
log("messageListCallback: problem during ajax request (readyState = " + http.readyState + ", status = " + http.status + ", response = " + msg + ")");
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from SOGoDataTable.render()
|
||||
*/
|
||||
function messageListCallback(row, data, isNew) {
|
||||
if (isNew) {
|
||||
row.observe("mousedown", onRowClick);
|
||||
row.observe("selectstart", listRowMouseDownHandler);
|
||||
row.observe("contextmenu", onMessageContextMenu);
|
||||
}
|
||||
|
||||
row.className = data['rowClasses'];
|
||||
row.id = data['rowID'];
|
||||
row.writeAttribute('labels', (data['labels']?data['labels']:""));
|
||||
|
||||
var columnsOrder = UserDefaults["SOGoMailListViewColumnsOrder"];
|
||||
// if (typeof columnsOrder == "undefined") {
|
||||
// columnsOrder = defaultColumnsOrder;
|
||||
// }
|
||||
|
||||
var cells;
|
||||
if (Prototype.Browser.IE)
|
||||
cells = row.childNodes;
|
||||
else
|
||||
cells = row.cells;
|
||||
|
||||
for (var j = 0; j < cells.length; j++) {
|
||||
var cell = $(cells[j]);
|
||||
var cellType = columnsOrder[j];
|
||||
|
||||
if (data[cellType]) cell.update(data[cellType]);
|
||||
|
||||
cell.observe("mousedown", listRowMouseDownHandler);
|
||||
if (cellType == "Subject" || cellType == "From" || cellType == "To" || cellType == "Date")
|
||||
cell.observe("dblclick", onMessageDoubleClick.bindAsEventListener(cell));
|
||||
else if (cellType == "Unread") {
|
||||
var img = cell.down('img');
|
||||
if (img)
|
||||
img.observe("click", mailListMarkMessage.bindAsEventListener(img));
|
||||
}
|
||||
else if (cellType == 'Flagged' && isNew)
|
||||
cell.observe("click", mailListFlagMessageToggle.bindAsEventListener(cell));
|
||||
}
|
||||
initFlagIcons ();
|
||||
}
|
||||
|
||||
function getStatusFolders() {
|
||||
@@ -784,9 +769,28 @@ function updateStatusFolders(count, isDelta) {
|
||||
}
|
||||
}
|
||||
|
||||
function updateMessageListCounter(count, isDelta) {
|
||||
var cell = $("messageCountHeader").down();
|
||||
|
||||
if (isDelta) {
|
||||
var value = parseInt(cell.innerHTML);
|
||||
count += value;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
cell.update(count + " " + getLabel("messages"));
|
||||
else
|
||||
cell.update(getLabel("No message"));
|
||||
}
|
||||
|
||||
function onMessageListRender(event) {
|
||||
// Event is fired from SOGoDataTable.
|
||||
updateMessageListCounter(event.memo, false);
|
||||
}
|
||||
|
||||
function onMessageContextMenu(event) {
|
||||
var menu = $('messageListMenu');
|
||||
var topNode = $('messageList');
|
||||
var topNode = $('messageListBody');
|
||||
var selectedNodes = topNode.getSelectedRows();
|
||||
|
||||
menu.observe("hideMenu", onMessageContextMenuHide);
|
||||
@@ -1409,7 +1413,7 @@ function onMenuForwardMessage(event) {
|
||||
}
|
||||
|
||||
function onMenuViewMessageSource(event) {
|
||||
var messageList = $("messageList");
|
||||
var messageList = $("messageListBody");
|
||||
var rows = messageList.getSelectedRowsId();
|
||||
|
||||
if (rows.length > 0) {
|
||||
@@ -1482,9 +1486,9 @@ function expandUpperTree(node) {
|
||||
}
|
||||
|
||||
function onHeaderClick(event) {
|
||||
if (TableKit.Resizable._onHandle)
|
||||
if (SOGoResizableTable._onHandle)
|
||||
return;
|
||||
|
||||
|
||||
var headerId = this.getAttribute("id");
|
||||
var newSortAttribute;
|
||||
if (headerId == "subjectHeader")
|
||||
@@ -1515,63 +1519,16 @@ function refreshCurrentFolder() {
|
||||
openMailbox(Mailer.currentMailbox, true);
|
||||
}
|
||||
|
||||
/* a model for a futur refactoring of the sortable table headers mechanism */
|
||||
function configureMessageListEvents(table) {
|
||||
if (table) {
|
||||
table.multiselect = true;
|
||||
// Each body row can load a message
|
||||
table.observe("mousedown", onMessageSelectionChange);
|
||||
function configureMessageListEvents(headerTable, dataTable) {
|
||||
if (headerTable)
|
||||
// Sortable columns
|
||||
configureSortableTableHeaders(table);
|
||||
}
|
||||
}
|
||||
|
||||
function configureMessageListBodyEvents(table) {
|
||||
if (table) {
|
||||
// Page navigation
|
||||
var cell = table.tHead.rows[1].cells[0];
|
||||
if ($(cell).hasClassName("tbtv_navcell")) {
|
||||
var anchors = $(cell).childNodesWithTag("a");
|
||||
for (var i = 0; i < anchors.length; i++)
|
||||
$(anchors[i]).observe("click", openMailboxAtIndex);
|
||||
}
|
||||
|
||||
rows = table.tBodies[0].rows;
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
var row = $(rows[i]);
|
||||
row.observe("mousedown", onRowClick);
|
||||
row.observe("selectstart", listRowMouseDownHandler);
|
||||
row.observe("contextmenu", onMessageContextMenu);
|
||||
|
||||
//row.dndTypes = function() { return new Array("mailRow"); };
|
||||
//row.dndGhost = messageListGhost;
|
||||
//row.dndDataForType = messageListData;
|
||||
//document.DNDManager.registerSource(row);
|
||||
// Correspondances index <> nom de la colonne
|
||||
// 0 => Invisible
|
||||
// 1 => Attachment
|
||||
// 2 => Subject
|
||||
// 3 => From
|
||||
// 4 => Unread
|
||||
// 5 => Date
|
||||
// 6 => Priority
|
||||
var columnsOrder = UserDefaults["SOGoMailListViewColumnsOrder"];
|
||||
if ( typeof(columnsOrder) == "undefined" ) {
|
||||
columnsOrder = defaultColumnsOrder;
|
||||
}
|
||||
for (var j = 0; j < row.cells.length; j++) {
|
||||
var cell = $(row.cells[j]);
|
||||
var cellType = columnsOrder[j];
|
||||
cell.observe("mousedown", listRowMouseDownHandler);
|
||||
if (cellType == "Subject" || cellType == "From" || cellType == "To" || cellType == "Date")
|
||||
cell.observe("dblclick", onMessageDoubleClick.bindAsEventListener(cell));
|
||||
else if (cellType == "Unread") {
|
||||
var img = $(cell.childNodesWithTag("img")[0]);
|
||||
if (img)
|
||||
img.observe("click", mailListMarkMessage.bindAsEventListener(img));
|
||||
}
|
||||
}
|
||||
}
|
||||
configureSortableTableHeaders(headerTable);
|
||||
|
||||
if (dataTable) {
|
||||
dataTable.multiselect = true;
|
||||
// Each body row can load a message
|
||||
dataTable.observe("mousedown",
|
||||
onMessageSelectionChange.bindAsEventListener(dataTable));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1589,9 +1546,15 @@ function configureDragHandles() {
|
||||
handle.addInterface(SOGoDragHandlesInterface);
|
||||
handle.upperBlock=$("mailboxContent");
|
||||
handle.lowerBlock=$("messageContent");
|
||||
handle.upperBlock.observe("handle:resize", onMessageListResize);
|
||||
}
|
||||
}
|
||||
|
||||
function onMessageListResize(event) {
|
||||
var h = $("mailboxContent").getHeight() - $("messageListHeader").getHeight();
|
||||
$("mailboxList").setStyle({'height': h + 'px'});
|
||||
}
|
||||
|
||||
function onWindowResize(event) {
|
||||
var handle = $("verticalDragHandle");
|
||||
if (handle)
|
||||
@@ -1641,15 +1604,36 @@ function openInbox(node) {
|
||||
mailboxTree.o(1);
|
||||
}
|
||||
|
||||
function initFlagIcons () {
|
||||
var icons = $$("TABLE#messageList TBODY TR.mailer_listcell_regular TD.messageFlag");
|
||||
for (var i = 0; i < icons.length; i++)
|
||||
icons[i].onclick = mailListFlagMessageToggle;
|
||||
}
|
||||
|
||||
function initMailer(event) {
|
||||
// Default sort options
|
||||
sorting["attribute"] = "date";
|
||||
sorting["ascending"] = false;
|
||||
|
||||
// Define columns order
|
||||
if (typeof UserDefaults["SOGoMailListViewColumnsOrder"] == "undefined") {
|
||||
var defaultColumnsOrder;
|
||||
if (typeof textDefaultColumnsOrder != 'undefined') {
|
||||
if (textDefaultColumnsOrder.length > 0)
|
||||
defaultColumnsOrder = textDefaultColumnsOrder.evalJSON(true);
|
||||
else
|
||||
defaultColumnsOrder = new Array();
|
||||
}
|
||||
UserDefaults["SOGoMailListViewColumnsOrder"] = defaultColumnsOrder;
|
||||
}
|
||||
|
||||
if (!$(document.body).hasClassName("popup")) {
|
||||
//initDnd();
|
||||
|
||||
Mailer.dataTable = $("mailboxList");
|
||||
Mailer.dataTable.addInterface(SOGoDataTableInterface);
|
||||
Mailer.dataTable.setRowRenderCallback(messageListCallback);
|
||||
Mailer.dataTable.observe("datatable:rendered", onMessageListRender);
|
||||
|
||||
var messageListHeader = $("messageListHeader");
|
||||
messageListHeader.addInterface(SOGoResizableTableInterface);
|
||||
|
||||
configureMessageListEvents($("messageListHeader"), $("messageListBody"));
|
||||
|
||||
initMailboxTree();
|
||||
initMessageCheckTimer();
|
||||
|
||||
@@ -1668,12 +1652,9 @@ function initMailer(event) {
|
||||
Event.observe(window, "beforeunload", onUnload);
|
||||
}
|
||||
|
||||
onMessageListResize();
|
||||
onWindowResize.defer();
|
||||
Event.observe(window, "resize", onWindowResize);
|
||||
|
||||
// Default sort options
|
||||
sorting["attribute"] = "date";
|
||||
sorting["ascending"] = false;
|
||||
}
|
||||
|
||||
function initMessageCheckTimer() {
|
||||
@@ -2188,8 +2169,17 @@ function folderRefreshCallback(http) {
|
||||
&& isHttpStatus204(http.status)) {
|
||||
var oldMailbox = http.callbackData.mailbox;
|
||||
if (http.callbackData.refresh
|
||||
&& oldMailbox == Mailer.currentMailbox)
|
||||
refreshCurrentFolder();
|
||||
&& oldMailbox == Mailer.currentMailbox) {
|
||||
if (http.callbackData.id) {
|
||||
var s = http.callbackData.id + "";
|
||||
var uids = s.split(",");
|
||||
for (var i = 0; i < uids.length; i++)
|
||||
Mailer.dataTable.remove(uids[i]);
|
||||
Mailer.dataTable.render();
|
||||
}
|
||||
else
|
||||
refreshCurrentFolder();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (http.callbackData.id) {
|
||||
@@ -2237,7 +2227,7 @@ function messageFlagCallback(http) {
|
||||
}
|
||||
|
||||
function onLabelMenuPrepareVisibility() {
|
||||
var messageList = $("messageList");
|
||||
var messageList = $("messageListBody");
|
||||
var flags = {};
|
||||
|
||||
if (messageList) {
|
||||
@@ -2266,7 +2256,7 @@ function onLabelMenuPrepareVisibility() {
|
||||
}
|
||||
|
||||
function saveAs(event) {
|
||||
var messageList = $("messageList").down("TBODY");
|
||||
var messageList = $("messageListBody").down("TBODY");
|
||||
var rows = messageList.getSelectedNodes();
|
||||
var uids = new Array(); // message IDs
|
||||
var paths = new Array(); // row IDs
|
||||
@@ -2434,7 +2424,7 @@ function configureDraggables () {
|
||||
mainElement.hide();
|
||||
|
||||
new Draggable ("dragDropVisual",
|
||||
{ handle: "messageList",
|
||||
{ handle: "messageListBody",
|
||||
onStart: startDragging,
|
||||
onEnd: stopDragging,
|
||||
onDrag: whileDragging,
|
||||
@@ -2455,11 +2445,11 @@ function configureDroppables () {
|
||||
|
||||
function startDragging (itm, e) {
|
||||
var target = Event.element(e);
|
||||
if (target.up().up().tagName != "TBODY")
|
||||
if (target.up('TBODY') == undefined)
|
||||
return false;
|
||||
|
||||
var handle = $("dragDropVisual");
|
||||
var count = $('messageList').getSelectedRowsId().length;
|
||||
var count = $("messageListBody").getSelectedRowsId().length;
|
||||
|
||||
handle.update (count);
|
||||
if (e.shiftKey)
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Data table interface to be added to a DIV (this!)
|
||||
*
|
||||
* Available events:
|
||||
* datatable:rendered -- fired once the view rendering is completed
|
||||
*
|
||||
*/
|
||||
var SOGoDataTableInterface = {
|
||||
|
||||
// Object variables initialized with "bind"
|
||||
columnsCount: null,
|
||||
rowModel: null,
|
||||
rowHeight: 0,
|
||||
body: null,
|
||||
|
||||
// Object variables
|
||||
dataSource: null,
|
||||
rowTop: null,
|
||||
rowBottom: null,
|
||||
renderedIndex: -1,
|
||||
renderedCount: 0,
|
||||
rowRenderCallback: null,
|
||||
|
||||
// Constants
|
||||
overflow: 30, // must be lower than the overflow of the data source class
|
||||
renderDelay: 0.2, // delay (in seconds) before which the table is rendered upon scrolling
|
||||
|
||||
bind: function() {
|
||||
this.observe("scroll" , this.render.bind(this));
|
||||
|
||||
this.body = this.down("tbody");
|
||||
this.rowModel = this.body.down("tr");
|
||||
|
||||
// Since we use the fixed table layout, the first row must have the
|
||||
// proper CSS classes that will define the columns width.
|
||||
this.rowTop = new Element('tr', {'id': 'rowTop'});//.update(new Element('td'));
|
||||
this.body.insertBefore(this.rowTop, this.rowModel); // IE requires the element to be inside the DOM before appending new children
|
||||
var cells = this.rowModel.select('TD');
|
||||
for (var i = 0; i < cells.length; i++) {
|
||||
var cell = cells[i];
|
||||
var td = new Element('td', {'class': cell.className});
|
||||
this.rowTop.appendChild(td);
|
||||
}
|
||||
|
||||
this.rowBottom = new Element('tr', {'id': 'rowBottom'}).update(new Element('td'));
|
||||
this.body.insertBefore(this.rowBottom, this.rowModel);
|
||||
|
||||
this.columnsCount = this.rowModel.select("td").length;
|
||||
this.rowHeight = this.rowModel.getHeight();
|
||||
},
|
||||
|
||||
setRowRenderCallback: function(callbackFunction) {
|
||||
// Each time a row is created or updated with new data, this callback
|
||||
// function will be called.
|
||||
this.rowRenderCallback = callbackFunction;
|
||||
},
|
||||
|
||||
setSource: function(dataSourceClass, url, params) {
|
||||
// log ("DataTable.setSource() " + url);
|
||||
if (this.dataSource) this.dataSource.destroy();
|
||||
this._emptyTable();
|
||||
this.dataSource = new window[dataSourceClass](this, url);
|
||||
this.load(params);
|
||||
},
|
||||
|
||||
load: function(urlParams) {
|
||||
if (!this.dataSource) return;
|
||||
// log ("DataTable.load() with parameters [" + urlParams.keys().join(' ') + "]");
|
||||
if (Object.isHash(urlParams) && urlParams.keys().length > 0) this.dataSource.load(urlParams);
|
||||
else this.dataSource.load(new Hash());
|
||||
},
|
||||
|
||||
visibleRowCount: function() {
|
||||
var divHeight = this.getHeight();
|
||||
var visibleRowCount = Math.ceil(divHeight/this.rowHeight);
|
||||
|
||||
return visibleRowCount;
|
||||
},
|
||||
|
||||
firstVisibleRowIndex: function() {
|
||||
var firstRowIndex = Math.floor(this.scrollTop/this.rowHeight);
|
||||
|
||||
return firstRowIndex;
|
||||
},
|
||||
|
||||
render: function(uid) {
|
||||
var index = this.firstVisibleRowIndex();
|
||||
var count = this.visibleRowCount();
|
||||
|
||||
// Overflow the query to the maximum defined in the class variable overflow
|
||||
var start = index - (this.overflow/2);
|
||||
if (start < 0) start = 0;
|
||||
var end = index + count + this.overflow - (index - start);
|
||||
// log ("DataTable.getData() from " + index + " to " + (index + count) + " boosted from " + start + " to " + end);
|
||||
|
||||
// Don't overflow above the maximum number of entries from the data source
|
||||
if (this.dataSource.uids && this.dataSource.uids.length < end) end = this.dataSource.uids.length;
|
||||
|
||||
index = start;
|
||||
count = end - start;
|
||||
|
||||
this.currentRenderID = index + "-" + count;
|
||||
// Query the data source only if at least one row is not loaded
|
||||
if (this.renderedIndex < 0 ||
|
||||
this.renderedIndex > index ||
|
||||
this.renderedCount < count ||
|
||||
(index + count) > (this.renderedIndex + this.renderedCount)) {
|
||||
this.dataSource.getData(this.currentRenderID,
|
||||
index,
|
||||
count,
|
||||
this._render.bind(this),
|
||||
this.renderDelay);
|
||||
}
|
||||
},
|
||||
|
||||
_render: function(renderID, start, max, data) {
|
||||
if (this.currentRenderID != renderID) {
|
||||
// log ("DataTable._render() ignore render for " + renderID + " (current is " + this.currentRenderID + ")");
|
||||
return;
|
||||
}
|
||||
// log("DataTable._render() for " + data.length + " uids (from " + start + ")");
|
||||
|
||||
var h, i, j;
|
||||
var rows = this.body.select("tr");
|
||||
var scroll;
|
||||
|
||||
scroll = this.scrollTop;
|
||||
lastClickedRow = -1; // defined in generic.js
|
||||
|
||||
h = start * this.rowHeight;
|
||||
if (Prototype.Browser.IE)
|
||||
this.rowTop.setStyle({ 'height': h + 'px', 'line-height': h + 'px' });
|
||||
this.rowTop.firstChild.setStyle({ 'height': h + 'px', 'line-height': h + 'px' });
|
||||
|
||||
h = (max - start - data.length) * this.rowHeight;
|
||||
if (Prototype.Browser.IE)
|
||||
this.rowBottom.setStyle({ 'height': h + 'px', 'line-height': h + 'px' });
|
||||
this.rowBottom.firstChild.setStyle({ 'height': h + 'px', 'line-height': h + 'px' });
|
||||
|
||||
if (this.renderedIndex < 0) {
|
||||
this.renderedIndex = 0;
|
||||
this.renderedCount = 0;
|
||||
}
|
||||
|
||||
if (start > (this.renderedIndex + this.renderedCount) ||
|
||||
start + data.length < this.renderedIndex) {
|
||||
// No reusable row in the viewport;
|
||||
// refresh the complete view port
|
||||
|
||||
for (i = 0, j = start;
|
||||
i < this.renderedCount && i < data.length;
|
||||
i++, j++) {
|
||||
// Render all existing rows with new data
|
||||
var row = rows[i+1]; // must skip the first row (this.rowTop)
|
||||
row.removeClassName('_selected');
|
||||
this.rowRenderCallback(row, data[i], false);
|
||||
}
|
||||
|
||||
for (i = this.renderedCount;
|
||||
i < data.length;
|
||||
i++, j++) {
|
||||
// Add new rows, if necessary
|
||||
var row = this.rowModel.cloneNode(true);
|
||||
this.rowRenderCallback(row, data[i], true);
|
||||
row.show();
|
||||
this.body.insertBefore(row, this.rowBottom);
|
||||
}
|
||||
|
||||
for (i = this.renderedCount;
|
||||
i > data.length;
|
||||
i--) {
|
||||
// Delete extra rows, if necessary
|
||||
this.body.removeChild(rows[i]);
|
||||
}
|
||||
}
|
||||
else if (start >= this.renderedIndex) {
|
||||
// Scrolling down
|
||||
|
||||
// Delete top rows
|
||||
for (i = start; i > this.renderedIndex; i--) {
|
||||
this.body.removeChild(rows[i - this.renderedIndex]);
|
||||
}
|
||||
|
||||
// Add bottom rows
|
||||
for (j = this.renderedIndex + this.renderedCount - start, i = this.renderedIndex + this.renderedCount;
|
||||
j < data.length;
|
||||
j++, i++) {
|
||||
var row = this.rowModel.cloneNode(true);
|
||||
this.rowRenderCallback(row, data[j], true);
|
||||
row.show();
|
||||
this.body.insertBefore(row, this.rowBottom);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Scrolling up
|
||||
|
||||
// Delete bottom rows
|
||||
for (i = this.renderedIndex + this.renderedCount, j = this.renderedCount;
|
||||
i > (start + data.length);
|
||||
i--, j--) {
|
||||
this.body.removeChild(rows[j]);
|
||||
}
|
||||
|
||||
// Add top rows
|
||||
for (i = 0, j = start;
|
||||
j < this.renderedIndex;
|
||||
i++, j++) {
|
||||
var row = this.rowModel.cloneNode(true);
|
||||
this.rowRenderCallback(row, data[i], true);
|
||||
row.show();
|
||||
this.body.insertBefore(row, rows[1]);
|
||||
}
|
||||
}
|
||||
|
||||
this.body.refreshSelectionByIds();
|
||||
// log ("DataTable._render() top gap/bottom gap/total rows = " + this.rowTop.getStyle('height') + "/" + this.rowBottom.getStyle('height') + "/" + this.body.select("tr").length + " (height = " + this.down("table").getHeight() + "px)");
|
||||
|
||||
// Save current rendered view index and count
|
||||
this.renderedIndex = start;
|
||||
this.renderedCount = data.length;
|
||||
|
||||
// Restore scroll position (necessary in certain cases)
|
||||
this.scrollTop = scroll;
|
||||
|
||||
Event.fire(this, "datatable:rendered", max);
|
||||
},
|
||||
|
||||
remove: function(uid) {
|
||||
var rows = this.body.select("TR#row_" + uid);
|
||||
if (rows.length == 1) {
|
||||
var row = rows.first();
|
||||
row.parentNode.removeChild(row);
|
||||
var index = this.dataSource.invalidate(uid);
|
||||
// log ("DataTable.remove(" + uid + ")");
|
||||
if (this.renderedIndex < index &&
|
||||
(this.renderedIndex + this.renderedCount) > index) {
|
||||
this.renderedCount--;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_emptyTable: function() {
|
||||
var rows = this.body.select("tr");
|
||||
var currentCount = rows.length;
|
||||
|
||||
for (var i = currentCount - 1; i >= 0; i--) {
|
||||
if (rows[i] != this.rowModel &&
|
||||
rows[i] != this.rowTop &&
|
||||
rows[i] != this.rowBottom)
|
||||
this.body.removeChild(rows[i]);
|
||||
}
|
||||
|
||||
this.renderedIndex = -1;
|
||||
this.renderedCount = 0;
|
||||
this.rowTop.firstChild.setStyle({ 'height': '0px', 'line-height': '0px' });
|
||||
this.rowBottom.firstChild.setStyle({ 'height': '0px', 'line-height': '0px' });
|
||||
}
|
||||
};
|
||||
@@ -120,10 +120,11 @@ var SOGoDragHandlesInterface = {
|
||||
deltaY = Math.floor(pointerY - this.origY - (this.offsetHeight / 2));
|
||||
this.lowerBlock.setStyle({ top: (this.origLower + deltaY - this.delta) + 'px' });
|
||||
this.upperBlock.setStyle({ height: (this.origUpper + deltaY - this.delta) + 'px' });
|
||||
|
||||
//this.lowerBlock.fire("handle:resize");
|
||||
this.upperBlock.fire("handle:resize");
|
||||
this.saveDragHandleState(this.dhType, parseInt(this.lowerBlock.getStyle("top")));
|
||||
}
|
||||
if (Prototype.Browser.IE)
|
||||
if (Prototype.Browser.IE)
|
||||
Event.stopObserving(document.body, "mouseup", this.stopHandleDraggingBound);
|
||||
else
|
||||
Event.stopObserving(window, "mouseup", this.stopHandleDraggingBound);
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
SOGoMailDataSource = Class.create({
|
||||
|
||||
initialize: function(dataTable, url) {
|
||||
// Instance variables
|
||||
this.dataTable = dataTable;
|
||||
this.url = url;
|
||||
|
||||
this.uids = new Array();
|
||||
this.cache = new Hash();
|
||||
|
||||
this.loaded = false;
|
||||
this.delayedGetData = false;
|
||||
this.ajaxGetData = false;
|
||||
|
||||
// Constants
|
||||
this.overflow = 60;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.uids.clear();
|
||||
var keys = this.cache.keys();
|
||||
for (var i = 0; i < keys.length; i++)
|
||||
this.cache.unset(keys[i]);
|
||||
},
|
||||
|
||||
invalidate: function(uid) {
|
||||
this.cache.unset(uid);
|
||||
var index = this.uids.indexOf(parseInt(uid));
|
||||
log ("MailDataSource.invalidate(" + uid + ") at index " + index);
|
||||
if (index >= 0) {
|
||||
this.uids.splice(index, 1);
|
||||
}
|
||||
|
||||
return index;
|
||||
},
|
||||
|
||||
load: function(urlParams) {
|
||||
var params;
|
||||
this.loaded = false;
|
||||
if (urlParams.keys().length > 0) {
|
||||
params = urlParams.keys().collect(function(key) { return key + "=" + urlParams.get(key); }).join("&");
|
||||
}
|
||||
else
|
||||
params = "";
|
||||
|
||||
// log ("MailDataSource.load() " + params);
|
||||
triggerAjaxRequest(this.url + "/uids",
|
||||
this._loadCallback.bind(this),
|
||||
null,
|
||||
params,
|
||||
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||
},
|
||||
|
||||
_loadCallback: function(http) {
|
||||
if (http.status == 200) {
|
||||
if (http.responseText.length > 0) {
|
||||
this.uids = $A(http.responseText.evalJSON(true));
|
||||
log ("MailDataSource._loadCallback() " + this.uids.length + " uids");
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert("SOGoMailDataSource._loadCallback Error " + http.status + ": " + http.responseText);
|
||||
}
|
||||
},
|
||||
|
||||
getData: function(id, index, count, callbackFunction, delay) {
|
||||
if (this.loaded == false) {
|
||||
// UIDs are not yet loaded -- delay the call to the current function
|
||||
// log ("MailDataSource.getData() delaying data fetching while waiting for UIDs");
|
||||
if (this.delayedGetData) window.clearTimeout(this.delayedGetData);
|
||||
this.delayedGetData = this.getData.bind(this, id, index, count, callbackFunction, delay).delay(0.3);
|
||||
return;
|
||||
}
|
||||
if (this.delayed_getData) window.clearTimeout(this.delayed_getData);
|
||||
this.delayed_getData = this._getData.bind(this,
|
||||
id,
|
||||
index,
|
||||
count,
|
||||
callbackFunction
|
||||
).delay(delay);
|
||||
},
|
||||
|
||||
_getData: function(id, index, count, callbackFunction) {
|
||||
var start, end;
|
||||
var i, j;
|
||||
var missingUids = new Array();
|
||||
|
||||
// Compute last index depending on number of UIDs
|
||||
start = index - (this.overflow/2);
|
||||
if (start < 0) start = 0;
|
||||
end = index + count + this.overflow - (index - start);
|
||||
if (end > this.uids.length) {
|
||||
start -= end - this.uids.length;
|
||||
end = this.uids.length;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
log ("MailDataSource._getData() from " + index + " to " + (index + count) + " boosted from " + start + " to " + end);
|
||||
|
||||
for (i = 0, j = start; j < end; j++) {
|
||||
if (!this.cache.get(this.uids[j])) {
|
||||
missingUids[i] = this.uids[j];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.delayed_getRemoteData) window.clearTimeout(this.delayed_getRemoteData);
|
||||
if (missingUids.length > 0) {
|
||||
var params = "uids=" + missingUids.join(",");
|
||||
this.delayed_getRemoteData = this._getRemoteData.bind(this,
|
||||
{ callbackFunction: callbackFunction,
|
||||
start: start, end: end,
|
||||
id: id },
|
||||
params).delay(0.5);
|
||||
}
|
||||
else
|
||||
this._returnData(callbackFunction, id, start, end);
|
||||
},
|
||||
|
||||
_getRemoteData: function(callbackData, urlParams) {
|
||||
if (this.ajaxGetData) {
|
||||
this.ajaxGetData.aborted = true;
|
||||
this.ajaxGetData.abort();
|
||||
// log ("MailDataSource._getData() aborted previous AJAX request");
|
||||
}
|
||||
// log ("MailDataSource._getData() fetching headers of " + urlParams);
|
||||
this.ajaxGetData = triggerAjaxRequest(this.url + "/headers",
|
||||
this._getRemoteDataCallback.bind(this),
|
||||
callbackData,
|
||||
urlParams,
|
||||
{ "Content-type": "application/x-www-form-urlencoded" });
|
||||
},
|
||||
|
||||
_getRemoteDataCallback: function(http) {
|
||||
if (http.status == 200) {
|
||||
if (http.responseText.length > 0) {
|
||||
// We receives an array of hashes
|
||||
var headers = $A(http.responseText.evalJSON(true));
|
||||
var data = http.callbackData;
|
||||
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
this.cache.set(headers[i]["uid"], headers[i]);
|
||||
}
|
||||
|
||||
this._returnData(data["callbackFunction"], data["id"], data["start"], data["end"]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
alert("SOGoMailDataSource._getRemoteDataCallback Error " + http.status + ": " + http.responseText);
|
||||
}
|
||||
},
|
||||
|
||||
_returnData: function(callbackFunction, id, start, end) {
|
||||
var i, j;
|
||||
var data = new Array();
|
||||
for (i = start, j = 0; i < end; i++, j++) {
|
||||
data[j] = this.cache.get(this.uids[i]);
|
||||
}
|
||||
callbackFunction(id, start, this.uids.length, data);
|
||||
},
|
||||
|
||||
indexOf: function(uid) {
|
||||
this.uids.indexOf(uid + "");
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,237 @@
|
||||
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
|
||||
/*
|
||||
* Resizable table interface to be added to a TABLE (this!)
|
||||
*
|
||||
* Columns with the class resizable will be .. resizable.
|
||||
*
|
||||
*/
|
||||
var SOGoResizableTableInterface = {
|
||||
|
||||
delayedResize: null,
|
||||
|
||||
bind: function() {
|
||||
var i;
|
||||
var cells = $(this).down('tr').childElements();
|
||||
for (i = 0; i < cells.length; i++) {
|
||||
var cell = cells[i];
|
||||
if (Prototype.Browser.IE)
|
||||
cell.observe("selectstart", Event.stop);
|
||||
if (cell.hasClassName('resizable')) {
|
||||
Event.observe(cell, 'mouseover', SOGoResizableTable.initDetect);
|
||||
Event.observe(cell, 'mouseout', SOGoResizableTable.killDetect);
|
||||
}
|
||||
SOGoResizableTable._resize(this, $(cell), i, null, cell.getWidth());
|
||||
}
|
||||
Event.observe(window, "resize", this.restore.bind(this));
|
||||
},
|
||||
|
||||
restore: function(e) {
|
||||
// Only resize the columns after a certain delay, otherwise it slow
|
||||
// down the interface.
|
||||
if (this.delayedResize) window.clearTimeout(this.delayedResize);
|
||||
this.delayedResize = this._restore.bind(this).delay(0.2);
|
||||
},
|
||||
|
||||
_restore: function() {
|
||||
if (Prototype.Browser.IE)
|
||||
while (SOGoResizableTable._stylesheet.styleSheet.rules.length)
|
||||
SOGoResizableTable._stylesheet.styleSheet.removeRule();
|
||||
else
|
||||
while (SOGoResizableTable._stylesheet.firstChild)
|
||||
SOGoResizableTable._stylesheet.removeChild(SOGoResizableTable._stylesheet.firstChild);
|
||||
|
||||
// TODO : widths ratios should be computed and columns restored accordingly.
|
||||
var cells = $(this).down('tr').childElements();
|
||||
for (i = 0; i < cells.length; i++) {
|
||||
var cell = cells[i];
|
||||
SOGoResizableTable._resize(this, $(cell), i, null, cell.getWidth());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SOGoResizableTable = {
|
||||
|
||||
_onHandle: false,
|
||||
_cell: null,
|
||||
_tbl: null,
|
||||
_handle: null,
|
||||
_stylesheet: null,
|
||||
|
||||
resize: function(table, index, w) {
|
||||
var cell;
|
||||
if (typeof index === 'number') {
|
||||
if (!table || (table.tagName && table.tagName !== "TABLE")) { return; }
|
||||
table = $(table);
|
||||
index = Math.min(table.rows[0].cells.length, index);
|
||||
index = Math.max(1, index);
|
||||
index -= 1;
|
||||
cell = $(table.rows[0].cells[index]);
|
||||
}
|
||||
else {
|
||||
cell = $(index);
|
||||
table = table ? $(table) : cell.up('table');
|
||||
index = SOGoResizableTable.getCellIndex(cell);
|
||||
}
|
||||
|
||||
var cells = table.down('tr').childElements();
|
||||
var nextResizableCell = null;
|
||||
for (var i = index + 1; i < cells.length; i++) {
|
||||
var c = cells[i];
|
||||
if (c.hasClassName('resizable')) {
|
||||
nextResizableCell = c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var delta = SOGoResizableTable._resize(table, cell, index, nextResizableCell, w, false);
|
||||
if (delta != 0 && nextResizableCell != null) {
|
||||
var w = nextResizableCell.getWidth() - delta;
|
||||
SOGoResizableTable._resize(table, nextResizableCell, i, null, w, true);
|
||||
}
|
||||
},
|
||||
|
||||
_resize: function(table, cell, index, nextResizableCell, w, isAdjustment) {
|
||||
var pad = 0;
|
||||
if (!Prototype.Browser.WebKit) {
|
||||
pad = parseInt(cell.getStyle('paddingLeft'),10) + parseInt(cell.getStyle('paddingRight'),10);
|
||||
pad += parseInt(cell.getStyle('borderLeftWidth'),10) + parseInt(cell.getStyle('borderRightWidth'),10);
|
||||
}
|
||||
|
||||
var cells = table.down('tr').childElements();
|
||||
if ((index + 1) == cells.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!isAdjustment && cell.getWidth() < w) {
|
||||
if (nextResizableCell == null && (index + 2) == cells.length)
|
||||
// The next cell is the last cell; respect its minimum width
|
||||
// event if it's not resizable.
|
||||
nextResizableCell = cells[index + 1];
|
||||
if (nextResizableCell != null) {
|
||||
// Respect the minimum width of the next resizable cell.
|
||||
var max = cells[index].getWidth()
|
||||
+ nextResizableCell.getWidth()
|
||||
- parseInt(nextResizableCell.getStyle('minWidth'))
|
||||
- pad;
|
||||
w = Math.min(max, w);
|
||||
}
|
||||
}
|
||||
|
||||
// Respect the minimum width of the cell.
|
||||
w = Math.max(w - pad, parseInt(cell.getStyle('minWidth')));
|
||||
|
||||
var delta = w - cell.getWidth() + pad;
|
||||
|
||||
var cssSelector = ' TABLE.' + $w(table.className).first() + ' .' + $w(cell.className).first();
|
||||
|
||||
if (SOGoResizableTable._stylesheet == null) {
|
||||
SOGoResizableTable._stylesheet = document.createElement("style");
|
||||
SOGoResizableTable._stylesheet.type = "text/css";
|
||||
document.getElementsByTagName("head")[0].appendChild(SOGoResizableTable._stylesheet);
|
||||
}
|
||||
|
||||
if (SOGoResizableTable._stylesheet.styleSheet && SOGoResizableTable._stylesheet.styleSheet.addRule) {
|
||||
// IE
|
||||
SOGoResizableTable._stylesheet.styleSheet.addRule(cssSelector,
|
||||
' { width: ' + w + 'px; max-width: ' + w + 'px; }');
|
||||
}
|
||||
else {
|
||||
// Mozilla + Safari
|
||||
SOGoResizableTable._stylesheet.appendChild(document.createTextNode(cssSelector +
|
||||
' { width: ' + w + 'px; max-width: ' + w + 'px; }'));
|
||||
}
|
||||
|
||||
return delta;
|
||||
},
|
||||
|
||||
initDetect: function(e) {
|
||||
var cell = Event.element(e);
|
||||
if (cell.tagName != "TH") { return; }
|
||||
Event.observe(cell, 'mousemove', SOGoResizableTable.detectHandle);
|
||||
Event.observe(cell, 'mousedown', SOGoResizableTable.startResize);
|
||||
},
|
||||
|
||||
detectHandle: function(e) {
|
||||
var cell = Event.element(e);
|
||||
if (SOGoResizableTable.pointerPos(cell, Event.pointerX(e), Event.pointerY(e))) {
|
||||
cell.addClassName('resize-handle-active');
|
||||
SOGoResizableTable._onHandle = true;
|
||||
}
|
||||
else {
|
||||
cell.removeClassName('resize-handle-active');
|
||||
SOGoResizableTable._onHandle = false;
|
||||
}
|
||||
},
|
||||
|
||||
killDetect: function(e) {
|
||||
SOGoResizableTable._onHandle = false;
|
||||
var cell = Event.element(e);
|
||||
Event.stopObserving(cell, 'mousemove', SOGoResizableTable.detectHandle);
|
||||
Event.stopObserving(cell, 'mousedown', SOGoResizableTable.startResize);
|
||||
cell.removeClassName('resize-handle-active');
|
||||
},
|
||||
|
||||
startResize: function(e) {
|
||||
if (!SOGoResizableTable._onHandle) { return; }
|
||||
var cell = Event.element(e);
|
||||
Event.stopObserving(cell, 'mousemove', SOGoResizableTable.detectHandle);
|
||||
Event.stopObserving(cell, 'mousedown', SOGoResizableTable.startResize);
|
||||
Event.stopObserving(cell, 'mouseout', SOGoResizableTable.killDetect);
|
||||
SOGoResizableTable._cell = cell;
|
||||
var table = cell.up('table');
|
||||
SOGoResizableTable._tbl = table;
|
||||
SOGoResizableTable._handle = $(document.createElement('div')).addClassName('resize-handle').setStyle({
|
||||
'top' : table.cumulativeOffset()[1] + 'px',
|
||||
'left' : Event.pointerX(e) + 'px',
|
||||
'height' : table.getHeight() + 'px',
|
||||
'max-height' : table.getHeight() + 'px'
|
||||
});
|
||||
document.body.appendChild(SOGoResizableTable._handle);
|
||||
|
||||
Event.observe(document, 'mousemove', SOGoResizableTable.drag);
|
||||
Event.observe(document, 'mouseup', SOGoResizableTable.endResize);
|
||||
Event.stop(e);
|
||||
},
|
||||
|
||||
endResize: function(e) {
|
||||
var cell = SOGoResizableTable._cell;
|
||||
if (!cell) { return; }
|
||||
SOGoResizableTable.resize(null, cell, (Event.pointerX(e) - cell.cumulativeOffset()[0]));
|
||||
Event.stopObserving(document, 'mousemove', SOGoResizableTable.drag);
|
||||
Event.stopObserving(document, 'mouseup', SOGoResizableTable.endResize);
|
||||
$$('div.resize-handle').each(function(elm){
|
||||
document.body.removeChild(elm);
|
||||
});
|
||||
Event.observe(cell, 'mouseout', SOGoResizableTable.killDetect);
|
||||
SOGoResizableTable._tbl = SOGoResizableTable._handle = SOGoResizableTable._cell = null;
|
||||
Event.stop(e);
|
||||
},
|
||||
|
||||
drag: function(e) {
|
||||
e = $(e);
|
||||
if (SOGoResizableTable._handle === null) {
|
||||
try {
|
||||
SOGoResizableTable.resize(SOGoResizableTable._tbl, SOGoResizableTable._cell, (Event.pointerX(e) - SOGoResizableTable._cell.cumulativeOffset()[0]));
|
||||
}
|
||||
catch(e) {}
|
||||
}
|
||||
else {
|
||||
SOGoResizableTable._handle.setStyle({'left' : Event.pointerX(e) + 'px'});
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
pointerPos: function(element, x, y) {
|
||||
var offset = $(element).cumulativeOffset();
|
||||
return (y >= offset[1] &&
|
||||
y < offset[1] + element.offsetHeight &&
|
||||
x >= offset[0] + element.offsetWidth - 5 &&
|
||||
x < offset[0] + element.offsetWidth);
|
||||
},
|
||||
|
||||
getCellIndex : function(cell) {
|
||||
return $A(cell.parentNode.cells).indexOf(cell);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -409,7 +409,7 @@ td img.tbtv_sortcell
|
||||
height: 12px; }
|
||||
|
||||
TD.subjectCell,
|
||||
td.tbtv_subject_headercell
|
||||
TD.tbtv_subject_headercell
|
||||
{ overflow: hidden; }
|
||||
|
||||
/* drag handles */
|
||||
@@ -530,9 +530,10 @@ DIV.dTreeNode SPAN._dragOver
|
||||
INPUT.checkBox
|
||||
{ vertical-align: middle; }
|
||||
|
||||
/* tablekit resizable columns */
|
||||
/* resizable columns */
|
||||
|
||||
TABLE TD.resize-handle-active
|
||||
TABLE TD.resize-handle-active,
|
||||
TABLE TH.resize-handle-active
|
||||
{ cursor: e-resize; }
|
||||
|
||||
DIV.resize-handle
|
||||
@@ -541,8 +542,7 @@ DIV.resize-handle
|
||||
border-right: 1px solid #fff;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
max-height: 2em; } /* will be set in JavaScript when setting drag handles */
|
||||
left: 0px; }
|
||||
|
||||
@media print
|
||||
{
|
||||
|
||||
@@ -466,7 +466,10 @@ function onRowClick(event) {
|
||||
node = node.parentNode; // select TR
|
||||
}
|
||||
if (node.tagName == 'TR') {
|
||||
rowIndex = node.rowIndex - $(node).up('table').down('thead').getElementsByTagName('tr').length;
|
||||
var head = $(node).up('table').down('thead');
|
||||
rowIndex = node.rowIndex;
|
||||
if (head)
|
||||
rowIndex -= head.getElementsByTagName('tr').length;
|
||||
}
|
||||
else if (node.tagName == 'LI') {
|
||||
// Find index of clicked row
|
||||
@@ -573,7 +576,6 @@ function popupMenu(event, menuId, target) {
|
||||
|
||||
$(document.body).observe("click", onBodyClickMenuHandler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function getParentMenu(node) {
|
||||
|
||||
@@ -54,6 +54,9 @@ IMG.dragMessage
|
||||
TD.mailer_fieldname
|
||||
{ width: 8em; }
|
||||
|
||||
TABLE.messageList TD
|
||||
{ white-space: pre; }
|
||||
|
||||
/* ContactsUI */
|
||||
|
||||
/*DIV#contactFoldersList SPAN.toolbarButton
|
||||
|
||||
Reference in New Issue
Block a user