From 34f2904f0d5aab6b3caf39f38ecc13b2871ff99c Mon Sep 17 00:00:00 2001 From: Francis Lachapelle Date: Thu, 7 Jul 2011 13:58:50 +0000 Subject: [PATCH] See ChangeLog Monotone-Parent: 73de56ded7f5b5ba79857c706ecef4d70ac3113b Monotone-Revision: 5c6353cb6270d51a457d46a8fe98dadae4f37193 Monotone-Author: flachapelle@inverse.ca Monotone-Date: 2011-07-07T13:58:50 Monotone-Branch: ca.inverse.sogo --- ChangeLog | 18 ++++++++++++++ SoObjects/Mailer/SOGoMailFolder.m | 27 +++++++++++++++------ UI/MailerUI/UIxMailAccountActions.m | 2 -- UI/MailerUI/UIxMailFolderActions.m | 14 +++++++++-- UI/MailerUI/UIxMailListActions.m | 24 +++++++++++++++--- UI/MailerUI/UIxMailMainFrame.m | 17 ++++++------- UI/WebServerResources/MailerUI.js | 26 ++++++++++++-------- UI/WebServerResources/SOGoDataTable.js | 1 + UI/WebServerResources/SOGoMailDataSource.js | 19 +++++++++------ 9 files changed, 106 insertions(+), 42 deletions(-) diff --git a/ChangeLog b/ChangeLog index bbd945f6a..05319c3d7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2011-07-08 Francis Lachapelle + + * UI/MailerUI/UIxMailListActions.m (-getUIDsAndHeadersInFolder) + (-getSortedUIDsAction): both methods now return the inbox quota + along with the other data. + + * UI/MailerUI/UIxMailFolderActions.m (-batchDeleteAction): return + the inbox quota when the messages were successfully deleted. + + * SoObjects/Mailer/SOGoMailFolder.m + (-deleteUIDs:useTrashFolder:inContext:): when not using the trash folder + or when deleting a message within the trash folder, we now expunge the + folder immediately. + + * UI/WebServerResources/SOGoMailDataSource.js (init): quotas + information can now be passed as argument. It will be passed back + to the method "updateQuotas" from MailerUI.js. + 2011-07-08 Wolfgang Sourdeau * OpenChange/MAPIStoreContext.m (-openDir:): implemented. diff --git a/SoObjects/Mailer/SOGoMailFolder.m b/SoObjects/Mailer/SOGoMailFolder.m index e393bd9ac..7b96c8683 100644 --- a/SoObjects/Mailer/SOGoMailFolder.m +++ b/SoObjects/Mailer/SOGoMailFolder.m @@ -296,8 +296,12 @@ static NSString *defaultUserID = @"anyone"; // If we are deleting messages within the Trash folder itself, we // do not, of course, try to move messages to the Trash folder. - if (![folderName isEqualToString: [self relativeImap4Name]]) - { + if ([folderName isEqualToString: [self relativeImap4Name]]) + { + withTrash = NO; + } + else + { // If our Trash folder doesn't exist when we try to copy messages // to it, we create it. result = [[client status: folderName flags: [NSArray arrayWithObject: @"UIDVALIDITY"]] @@ -331,11 +335,20 @@ static NSString *defaultUserID = @"anyone"; objectForKey: @"result"]; if ([result boolValue]) { - [self markForExpunge]; - if (trashFolder) - [trashFolder flushMailCaches]; - error = nil; - } + if (withTrash) + { + [self markForExpunge]; + if (trashFolder) + [trashFolder flushMailCaches]; + error = nil; + } + else + { + // When not using a trash folder, expunge the current folder + // immediately + error = [self expunge]; + } + } else error = [NSException exceptionWithHTTPStatus:500 diff --git a/UI/MailerUI/UIxMailAccountActions.m b/UI/MailerUI/UIxMailAccountActions.m index 7668dcfa1..35677d068 100644 --- a/UI/MailerUI/UIxMailAccountActions.m +++ b/UI/MailerUI/UIxMailAccountActions.m @@ -181,13 +181,11 @@ NSArray *folders; NSDictionary *data; WOResponse *response; - id inboxQuota; co = [self clientObject]; rawFolders = [[co allFolderPaths] objectEnumerator]; folders = [self _jsonFolders: rawFolders]; - inboxQuota = nil; // The parameters order is important here, as if the server doesn't support // quota, inboxQuota will be nil and it'll terminate the list of objects/keys. diff --git a/UI/MailerUI/UIxMailFolderActions.m b/UI/MailerUI/UIxMailFolderActions.m index 3858d98e3..693a255c0 100644 --- a/UI/MailerUI/UIxMailFolderActions.m +++ b/UI/MailerUI/UIxMailFolderActions.m @@ -229,9 +229,11 @@ - (WOResponse *) batchDeleteAction { SOGoMailFolder *co; + SOGoMailAccount *account; WOResponse *response; NSArray *uids; NSString *value; + NSDictionary *data; BOOL withoutTrash; co = [self clientObject]; @@ -244,7 +246,12 @@ uids = [value componentsSeparatedByString: @","]; response = (WOResponse *) [co deleteUIDs: uids useTrashFolder: !withoutTrash inContext: context]; if (!response) - response = [self responseWith204]; + { + account = [co mailAccountFolder]; + data = [NSDictionary dictionaryWithObjectsAndKeys: [account getInboxQuota], @"quotas", nil]; + response = [self responseWithStatus: 200 + andString: [data jsonRepresentation]]; + } } else { @@ -520,6 +527,8 @@ if (!error) { [co flushMailCaches]; + + // Delete folders within the trash connection = [co imap4Connection]; subfolders = [[co allFolderURLs] objectEnumerator]; while ((currentURL = [subfolders nextObject])) @@ -585,6 +594,7 @@ EOQualifier *searchQualifier; NSArray *searchResult; NSDictionary *imapResult; +// NSMutableDictionary *data; NGImap4Connection *connection; NGImap4Client *client; int unseen; @@ -606,7 +616,7 @@ } else unseen = 0; - + return [NSDictionary dictionaryWithObject: [NSNumber numberWithInt: unseen] forKey: @"unseen"]; diff --git a/UI/MailerUI/UIxMailListActions.m b/UI/MailerUI/UIxMailListActions.m index a8faf93c0..4a9d6dd88 100644 --- a/UI/MailerUI/UIxMailListActions.m +++ b/UI/MailerUI/UIxMailListActions.m @@ -48,6 +48,7 @@ #import #import +#import #import #import #import @@ -347,14 +348,12 @@ BOOL asc; SOGoUser *activeUser; SOGoUserSettings *us; - SOGoMailAccounts *clientObject; sort = [self imap4SortKey]; ascending = [[context request] formValueForKey: @"asc"]; asc = [ascending boolValue]; activeUser = [context activeUser]; - clientObject = [self clientObject]; module = @"Mail"; us = [activeUser userSettings]; moduleSettings = [us objectForKey: module]; @@ -630,6 +629,8 @@ NSDictionary *data; NSRange r; int count; + SOGoMailAccount *account; + id quota; uids = [self getSortedUIDsInFolder: mailFolder]; // retrieves the form parameters "sort" and "asc" @@ -649,9 +650,14 @@ sortByThread = NO; } + // We also return the inbox quota + account = [mailFolder mailAccountFolder]; + quota = [account getInboxQuota]; + data = [NSDictionary dictionaryWithObjectsAndKeys: uids, @"uids", headers, @"headers", - [NSNumber numberWithBool: sortByThread], @"threaded", nil]; + [NSNumber numberWithBool: sortByThread], @"threaded", + quota, @"quotas", nil]; return data; } @@ -663,7 +669,9 @@ NSDictionary *data; NSArray *uids, *threadedUids; NSString *noHeaders; + SOGoMailAccount *account; SOGoMailFolder *folder; + id quota; WORequest *request; WOResponse *response; @@ -672,9 +680,11 @@ [response setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"]; folder = [self clientObject]; + // TODO: we might want to flush the caches? //[folder flushMailCaches]; [folder expungeLastMarkedFolder]; + noHeaders = [request formValueForKey: @"no_headers"]; if ([noHeaders length]) { @@ -687,8 +697,14 @@ else sortByThread = NO; } + + // We also return the inbox quota + account = [folder mailAccountFolder]; + quota = [account getInboxQuota]; + data = [NSDictionary dictionaryWithObjectsAndKeys: uids, @"uids", - [NSNumber numberWithBool: sortByThread], @"threaded", nil]; + [NSNumber numberWithBool: sortByThread], @"threaded", + quota, @"quotas", nil]; } else data = [self getUIDsAndHeadersInFolder: folder]; diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index ba19d4a07..ba0297bee 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -162,14 +162,12 @@ SOGoMailAccount *account; SOGoMailFolder *inbox; NSDictionary *data; - SOGoUser *activeUser; UIxMailListActions *actions; [self _setupContext]; #warning this code is dirty: we should not invoke UIxMailListActions from here! actions = [[[UIxMailListActions new] initWithRequest: [context request]] autorelease]; - activeUser = [context activeUser]; accounts = [self clientObject]; account = [accounts lookupName: @"0" inContext: context acquire: NO]; @@ -472,13 +470,6 @@ 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: @"messageThreadColumn tbtv_headercell", @"invisibleHeader", @"Thread", nil]; @@ -486,6 +477,13 @@ forKeys: tmpKeys] forKey: @"Thread"]; + 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]; @@ -582,7 +580,6 @@ if (i == NSNotFound) [finalOrder insertObject: @"Thread" atIndex: 0]; } - if ([self showToAddress]) { i = [finalOrder indexOfObject: @"From"]; diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index 75b811e20..51f3ee1b6 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -315,8 +315,8 @@ function mailListToggleMessagesFlagged(row) { } } if (selectedRowsId.length > 0) { - var firstTd = row.childElements().first(); - var img = firstTd.childElements().first(); + var td = row.down("td.messageFlagColumn"); + var img = td.childElements().first(); var action = "markMessageFlagged"; var flagged = true; @@ -476,15 +476,16 @@ function deleteSelectedMessages(sender) { var nextUid = nextRow.id.substr(4); var nextIndex = Mailer.dataTable.dataSource.indexOf(nextUid); Mailer.dataTable.dataSource.uids[nextIndex][2] = 1; // mark it as "first" - Mailer.dataTable.invalidate(nextUid, true); + Mailer.dataTable.dataSource.invalidate(nextUid); // next refresh will reload headers for row } if (nextRow.id.startsWith('row_')) { Mailer.currentMessages[Mailer.currentMailbox] = nextRow.id.substr(4); nextRow.selectElement(); - if (loadMessage(Mailer.currentMessages[Mailer.currentMailbox]) && !refreshFolder) + if (loadMessage(Mailer.currentMessages[Mailer.currentMailbox]) && !refreshFolder) { // Seen state has changed - Mailer.dataTable.invalidate(Mailer.currentMessages[Mailer.currentMailbox], true); + Mailer.dataTable.dataSource.invalidate(Mailer.currentMessages[Mailer.currentMailbox]); refreshFolder = true; + } } } else { @@ -496,7 +497,8 @@ function deleteSelectedMessages(sender) { lastClickedRow = nextRow.rowIndex; lastClickedRowId = nextRow.id; } - deleteCachedMailboxByType("trash"); + if (Mailer.currentMailboxType != "trash") + deleteCachedMailboxByType("trash"); } else { Mailer.dataTable.remove(uid); @@ -522,8 +524,11 @@ function deleteSelectedMessages(sender) { } function deleteSelectedMessagesCallback(http) { - if (isHttpStatus204(http.status)) { + if (http.status == 200) { var data = http.callbackData; + var rdata = http.responseText.evalJSON(true); + if (rdata.quotas && data["mailbox"].startsWith('/0/')) + updateQuotas(rdata.quotas); if (data["refreshUnseenCount"]) // TODO : the unseen count should be returned when calling the batchDelete remote action, // in order to avoid this extra AJAX call. @@ -539,7 +544,8 @@ function deleteSelectedMessagesCallback(http) { } else { var html = new Element('div').update(http.responseText); - log ("Messages deletion failed (" + http.status + ") : " + html.down('p').innerHTML); + log ("Messages deletion failed (" + http.status + ") : "); + log (html.down('p').innerHTML); showAlertDialog(_("Operation failed")); refreshCurrentFolder(); } @@ -1637,7 +1643,7 @@ function loadMessageCallback(http) { // Warning: If the user can't set the read/unread flag, it won't // be reflected in the view unless we force the refresh. if (http.callbackData.seenStateHasChanged) - Mailer.dataTable.invalidate(msguid, true); + Mailer.dataTable.dataSource.invalidate(msguid); } var cachedMessage = new Array(); cachedMessage['idx'] = Mailer.currentMailbox + '/' + msguid; @@ -2068,7 +2074,7 @@ function updateQuotas(quotas) { if (quotas) Mailer.quotas = quotas; if (Mailer.quotas && parseInt(Mailer.quotas.maxQuota) > 0) { - log ("updating quotas"); + log ("updating quotas " + Mailer.quotas.usedSpace + "/" + Mailer.quotas.maxQuota); var treeContent = $("folderTreeContent"); var tree = $("mailboxTree"); var quotaDiv = $("quotaIndicator"); diff --git a/UI/WebServerResources/SOGoDataTable.js b/UI/WebServerResources/SOGoDataTable.js index 447dca812..669fea09a 100644 --- a/UI/WebServerResources/SOGoDataTable.js +++ b/UI/WebServerResources/SOGoDataTable.js @@ -292,6 +292,7 @@ var SOGoDataTableInterface = { invalidate: function(uid, withoutRefresh) { // Refetch the data for uid. Only refresh the data table if // necessary. +// log ("DataTable.invalidate(" + uid + ", with" + (withoutRefresh?"out":"") + " refresh)"); var index = this.dataSource.invalidate(uid); this.currentRenderID = index + "-" + 1; this.dataSource.getData(this.currentRenderID, diff --git a/UI/WebServerResources/SOGoMailDataSource.js b/UI/WebServerResources/SOGoMailDataSource.js index 686fe8794..0bcfae667 100644 --- a/UI/WebServerResources/SOGoMailDataSource.js +++ b/UI/WebServerResources/SOGoMailDataSource.js @@ -35,6 +35,7 @@ SOGoMailDataSource = Class.create({ }, remove: function(uid) { +// log ("MailDataSource.remove(" + uid + ")"); var index = this.invalidate(uid); if (index >= 0) { this.uids.splice(index, 1); @@ -43,7 +44,7 @@ SOGoMailDataSource = Class.create({ return index; }, - init: function(uids, threaded, headers) { + init: function(uids, threaded, headers, quotas) { this.uids = uids; if (typeof threaded != "undefined") { this.threaded = threaded; @@ -52,6 +53,9 @@ SOGoMailDataSource = Class.create({ } // log ("MailDataSource.init() " + this.uids.length + " uids loaded"); + if (quotas && Object.isFunction(updateQuotas)) + updateQuotas(quotas); + if (headers) { var keys = headers[0]; for (var i = 1; i < headers.length; i++) { @@ -89,7 +93,7 @@ SOGoMailDataSource = Class.create({ if (http.responseText.length > 0) { var data = http.responseText.evalJSON(true); if (data.uids) - this.init(data.uids, data.threaded, data.headers); + this.init(data.uids, data.threaded, data.headers, data.quotas); else this.init(data); if (this.delayedGetData) { @@ -146,7 +150,8 @@ SOGoMailDataSource = Class.create({ for (i = 0, j = start; j < end; j++) { var uid = this.threaded? this.uids[j][0] : this.uids[j]; if (!this.cache.get(uid)) { - missingUids[i] = uid; +// log ("MailDataSource._getData missing headers of uid " + uid + " at index " + j + (this.threaded? " (":" (non-") + "threaded)"); + missingUids[i] = uid; i++; } } @@ -168,9 +173,9 @@ SOGoMailDataSource = Class.create({ if (this.ajaxGetData) { this.ajaxGetData.aborted = true; this.ajaxGetData.abort(); -// log ("MailDataSource._getData() aborted previous AJAX request"); +// log ("MailDataSource._getRemoteData() aborted previous AJAX request"); } -// log ("MailDataSource._getData() fetching headers of " + urlParams); +// log ("MailDataSource._getRemoteData() fetching headers of " + urlParams); this.ajaxGetData = triggerAjaxRequest(this.url + "/headers", this._getRemoteDataCallback.bind(this), callbackData, @@ -191,7 +196,7 @@ SOGoMailDataSource = Class.create({ header[keys[j]] = headers[i][j]; this.cache.set(header["uid"], header); } - + if (data["callbackFunction"]) this._returnData(data["callbackFunction"], data["id"], data["start"], data["end"]); } @@ -211,7 +216,7 @@ SOGoMailDataSource = Class.create({ // Add thread-related data if (parseInt(this.uids[i][2]) > 0) data[j]['Thread'] = ' '; //''; - else + else if (data[j]['Thread']) delete data[j]['Thread']; if (parseInt(this.uids[i][1]) > -1) data[j]['ThreadLevel'] = this.uids[i][1];