diff --git a/UI/MailerUI/GNUmakefile b/UI/MailerUI/GNUmakefile index e354d983f..851eb9abf 100644 --- a/UI/MailerUI/GNUmakefile +++ b/UI/MailerUI/GNUmakefile @@ -23,6 +23,7 @@ MailerUI_OBJC_FILES += \ UIxMailPopupView.m \ UIxMailMoveToPopUp.m \ UIxMailFilterPanel.m \ + UIxMailSearch.m \ \ UIxMailAccountActions.m \ UIxMailFolderActions.m \ diff --git a/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar b/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar index 1c9d1968d..c536a49fc 100644 --- a/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar +++ b/UI/MailerUI/Toolbars/SOGoMailFolder.toolbar @@ -63,5 +63,12 @@ image = "tb-mail-print-flat-24x24.png"; label = "Print"; tooltip = "Print this message"; }, + + { link = "#"; + onclick = "return onSearchMail(event);"; + cssClass = ""; + image = "search-messages.png"; + label = "Search"; + tooltip = "Search inbox"; } ) ) diff --git a/UI/MailerUI/UIxMailListActions.m b/UI/MailerUI/UIxMailListActions.m index 33585e932..5ae707e6c 100644 --- a/UI/MailerUI/UIxMailListActions.m +++ b/UI/MailerUI/UIxMailListActions.m @@ -325,24 +325,33 @@ - (NSString *) imap4SortKey { NSString *sort; + NSDictionary *urlParams, *sortingAttributes; + WORequest *request; - sort = [[context request] formValueForKey: @"sort"]; + request = [context request]; + urlParams = [request contentAsString] objectFromJSONString]; + sortingAttributes = [urlParams objectForKey:@"sortingAttributes"]; + sort = [sortingAttributes objectForKey:@"sort"]; return [sort uppercaseString]; } - (NSString *) imap4SortOrdering { - NSString *sort, *ascending; + NSString *sort; NSString *module; NSMutableDictionary *moduleSettings; + NSDictionary *urlParams, *sortingAttributes; + WORequest *request; BOOL asc; SOGoUser *activeUser; SOGoUserSettings *us; sort = [self imap4SortKey]; - ascending = [[context request] formValueForKey: @"asc"]; - asc = [ascending boolValue]; + request = [context request]; + urlParams = [[request contentAsString] objectFromJSONString]; + sortingAttributes = [urlParams objectForKey:@"sortingAttributes"]; + asc = [[sortingAttributes objectForKey:@"asc"] boolValue]; activeUser = [context activeUser]; module = @"Mail"; @@ -393,68 +402,71 @@ - (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]; - } + EOQualifier *qualifier, *searchQualifier; + WORequest *request; + NSDictionary *filters, *sortingAttributes, *content; + NSString *searchBy, *searchArgument, *searchInput, *searchString, *match; + NSMutableArray *searchArray; + int nbFilters, i; + request = [context request]; + content = [[request contentAsString] objectFromJSONString]; + qualifier = nil; + searchString = nil; + nbFilters = 0; + searchArray = [[NSMutableArray alloc] init]; + + if ([content objectForKey:@"filters"]) + { + filters = [content objectForKey:@"filters"]; + sortingAttributes = [content objectForKey:@"sortingAttributes"]; + nbFilters = [filters count]; + match = [NSString stringWithString:[sortingAttributes objectForKey:@"match"]]; // AND, OR + + for (i = 0; i < nbFilters; i++) + { + searchBy = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchBy"]]; + searchArgument = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchArgument"]]; + searchInput = [NSString stringWithString:[[filters objectAtIndex:i] objectForKey:@"searchInput"]]; + + searchString = [NSString stringWithFormat:@"(%@ %@: '%@')", searchBy, searchArgument, searchInput]; + searchQualifier = [EOQualifier qualifierWithQualifierFormat:searchString]; + [searchArray addObject:searchQualifier]; + } + if ([match isEqualToString:@"OR"]) + qualifier = [[EOOrQualifier alloc] initWithQualifierArray: searchArray]; + else + qualifier = [[EOAndQualifier alloc] initWithQualifierArray: searchArray]; + + [searchArray release]; + [qualifier autorelease]; + } return qualifier; } - (NSArray *) getSortedUIDsInFolder: (SOGoMailFolder *) mailFolder { EOQualifier *qualifier, *fetchQualifier, *notDeleted; - + if (!sortedUIDs) { - notDeleted = [EOQualifier qualifierWithQualifierFormat: - @"(not (flags = %@))", - @"deleted"]; + notDeleted = [EOQualifier qualifierWithQualifierFormat: @"(not (flags = %@))", @"deleted"]; qualifier = [self searchQualifier]; if (qualifier) - { - fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: - notDeleted, qualifier, - nil]; - [fetchQualifier autorelease]; - } + { + fetchQualifier = [[EOAndQualifier alloc] initWithQualifiers: notDeleted, qualifier, nil]; + [fetchQualifier autorelease]; + } else - fetchQualifier = notDeleted; - - sortedUIDs - = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier - sortOrdering: [self imap4SortOrdering] - threaded: sortByThread]; - + fetchQualifier = notDeleted; + + sortedUIDs = [mailFolder fetchUIDsMatchingQualifier: fetchQualifier + sortOrdering: [self imap4SortOrdering] + threaded: sortByThread]; + [sortedUIDs retain]; } - + return sortedUIDs; } @@ -681,6 +693,7 @@ response = [context response]; [response setHeader: @"text/plain; charset=utf-8" forKey: @"content-type"]; + folder = [self clientObject]; noHeaders = [request formValueForKey: @"no_headers"]; diff --git a/UI/MailerUI/UIxMailMainFrame.m b/UI/MailerUI/UIxMailMainFrame.m index d87fa20af..d94f90aae 100644 --- a/UI/MailerUI/UIxMailMainFrame.m +++ b/UI/MailerUI/UIxMailMainFrame.m @@ -119,6 +119,17 @@ return [names jsonRepresentation]; } +- (NSString *) userNames +{ + NSArray *accounts, *userNames; + + accounts = [[self clientObject] mailAccounts]; + userNames = [accounts objectsForKey: @"userName" notFoundMarker: nil]; + + return [userNames jsonRepresentation]; + +} + - (NSString *) pageFormURL { NSString *u; diff --git a/UI/MailerUI/UIxMailSearch.h b/UI/MailerUI/UIxMailSearch.h new file mode 100644 index 000000000..8eb7b03ba --- /dev/null +++ b/UI/MailerUI/UIxMailSearch.h @@ -0,0 +1,29 @@ +/* UIxMailSearch.h - this file is part of SOGo + * + * Copyright (C) 2006-2014 Inverse inc. + * + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +@interface UIxMailSearch : UIxComponent +{ + id item; + +} +@end \ No newline at end of file diff --git a/UI/MailerUI/UIxMailSearch.m b/UI/MailerUI/UIxMailSearch.m new file mode 100644 index 000000000..3de88e00d --- /dev/null +++ b/UI/MailerUI/UIxMailSearch.m @@ -0,0 +1,118 @@ +/* UIxMailSearch.m - this file is part of SOGo + * + * Copyright (C) 2006-2014 Inverse inc. + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This file is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#import +#import +#import + +#import +#import +#import + +#import + +@implementation UIxMailSearch + +- (id) init +{ + item = nil; + + return self; +} + +- (void) dealloc +{ + [item release]; +} + +- (void) setItem: (NSString *) newItem +{ + ASSIGN(item, newItem); +} + +- (NSString *) item +{ + return item; +} + +- (NSArray *) mailAccountsList +{ + SOGoMailAccount *co, *accountFolder; + SOGoMailAccounts *accountsFolder; + SOGoUserFolder *userFolder; + NSString *userName, *option, *lookup; + NSArray *folders; + NSMutableArray *mailboxes; + NSDictionary *mailAccount; + int nbMailboxes, nbMailAccounts, i, j; + + + userFolder = [[context activeUser] homeFolderInContext: context]; + accountsFolder = [userFolder lookupName: @"Mail" inContext: context acquire: NO]; + nbMailAccounts = [[accountsFolder mailAccounts] count]; + + mailboxes = [[NSMutableArray alloc] init]; + for (i = 0; i < nbMailAccounts; i++) + { + mailAccount = [[[accountsFolder mailAccounts] objectAtIndex:i] objectForKey:@"name"]; // Keys on this account = (name, port, encryption, mailboxes, serverName, identities, userName) + userName = [[[accountsFolder mailAccounts] objectAtIndex:i] objectForKey:@"userName"]; + lookup = [NSString stringWithFormat:@"%i", i]; + accountFolder = [accountsFolder lookupName:lookup inContext: context acquire: NO]; + folders = [accountFolder allFoldersMetadata]; + nbMailboxes = [folders count]; + [mailboxes addObject:mailAccount]; + for (j = 0; j < nbMailboxes; j++) + { + option = [NSString stringWithFormat:@"%@%@", userName, [[folders objectAtIndex:j] objectForKey:@"displayName"]]; + [mailboxes addObject:option]; + } + } + return mailboxes; + [mailboxes release]; +} + +// +// The objective here is to return the parent view layout and select the print +// layout corresponding. Default print view: list view +/* +- (NSString *) mailAccountSelected +{ + SOGoUser *activeUser; + NSString *parentView; + + activeUser = [context activeUser]; + us = [activeUser userSettings]; + parentView = [[us objectForKey:@"Calendar"] objectForKey:@"View" ]; + + if ([parentView isEqualToString:@"dayview"]) + return @"Daily"; + + else if ([parentView isEqualToString:@"weekview"]) + return @"Weekly"; + + else if ([parentView isEqualToString:@"multicolumndayview"]) + return @"Multi-Columns"; + + else + return @"LIST"; +} +*/ + +@end \ No newline at end of file diff --git a/UI/MailerUI/product.plist b/UI/MailerUI/product.plist index 8b9ed30cb..4956cf286 100644 --- a/UI/MailerUI/product.plist +++ b/UI/MailerUI/product.plist @@ -327,6 +327,10 @@ pageName = "UIxMailMainFrame"; actionName = "saveColumnsState"; }; + search = { + protectedBy = "View"; + pageName = "UIxMailSearch"; + }; }; }; diff --git a/UI/Templates/MailerUI/UIxMailMainFrame.wox b/UI/Templates/MailerUI/UIxMailMainFrame.wox index 6e83548a6..b796d8ad3 100644 --- a/UI/Templates/MailerUI/UIxMailMainFrame.wox +++ b/UI/Templates/MailerUI/UIxMailMainFrame.wox @@ -9,9 +9,11 @@ title="title" const:userDefaultsKeys="SOGoMailMessageCheck,SOGoMailSortByThreads,SOGoMailListViewColumnsOrder,SOGoMailDisplayRemoteInlineImages" const:userSettingsKeys="Mail" - const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js,jquery-ui.js"> + const:jsFiles="dtree.js,MailerUIdTree.js,SOGoAutoCompletion.js,SOGoResizableTable.js,SOGoMailDataSource.js,SOGoDataTable.js,jquery-ui.js, UIxMailSearch.js" + const:cssFiles="UIxMailSearch.css"> diff --git a/UI/Templates/MailerUI/UIxMailSearch.wox b/UI/Templates/MailerUI/UIxMailSearch.wox new file mode 100644 index 000000000..2e77b13e3 --- /dev/null +++ b/UI/Templates/MailerUI/UIxMailSearch.wox @@ -0,0 +1,112 @@ + +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + +
+
+ +
+
+
+ + + + +
+
+ + + +
+
+ + + +
+ + + + + + + + + + + + + + + +
+ + + + + +
+
+ + + + +
+
diff --git a/UI/WebServerResources/MailerUI.js b/UI/WebServerResources/MailerUI.js index 2df20dd8b..e08440f38 100644 --- a/UI/WebServerResources/MailerUI.js +++ b/UI/WebServerResources/MailerUI.js @@ -432,6 +432,38 @@ function onDocumentKeydown(event) { } } +/* Search mail, call the template and open inside a dialog windoĆ’w */ +function onSearchMail(event) { + if ($("searchMailView")) { + $("searchMailView").style.display = "block"; + $("bgDialogDiv").style.display = "block"; + initSearchMailView(); + } + else { + var urlstr = ApplicationBaseURL + "/search"; + + // Return the template for the searchMail feature + triggerAjaxRequest(urlstr, displaySearchMailCallback); + } +} + +function displaySearchMailCallback(http) { + if (http.readyState == 4 && http.status == 200) { + var fields = createElement("div", null); // (tagName, id, classes, attributes, htmlAttributes, parentNode) + var title = _("Search mail"); + var id = _("searchMailView"); + fields.innerHTML = http.responseText; + + dialog = createDialog(id, title, null, fields, "searchMail"); // (id, title, legend, content, positionClass) + document.body.appendChild(dialog); + + if (Prototype.Browser.IE) + jQuery('#bgDialogDiv').css('opacity', 0.4); + jQuery(dialog).fadeIn('fast'); + initSearchMailView(); + } +} + /* bulk delete of messages */ function deleteSelectedMessages(sender) { @@ -780,7 +812,7 @@ function composeNewMessage() { function openMailbox(mailbox, reload) { if (mailbox != Mailer.currentMailbox || reload) { var url = ApplicationBaseURL + encodeURI(mailbox.unescapeHTML()); - var urlParams = new Hash(); + var urlParams = {}; if (!reload) { var messageContent = $("messageContent"); @@ -791,13 +823,28 @@ function openMailbox(mailbox, reload) { var searchValue = search["mail"]["value"]; if (searchValue && searchValue.length > 0) { - urlParams.set("search", search["mail"]["criteria"]); - urlParams.set("value", escape(searchValue.utf8encode())); + var searchCriteria = []; + if (search["mail"]["criteria"] == "subject") + searchCriteria.push("subject"); + else if (search["mail"]["criteria"] == "sender") + searchCriteria.push("from"); + else if (search["mail"]["criteria"] == "subject_or_sender") + searchCriteria.push("subject", "from"); + else if (search["mail"]["criteria"] == "to_or_cc") + searchCriteria.push("to", "cc"); + else if (search["mail"]["criteria"] == "entire_message") + searchCriteria.push("body"); + + var filters = []; + for (i = 0; i < searchCriteria.length; i++) + filters.push({"searchBy": searchCriteria[i], "searchArgument": "doesContain", "searchInput": searchValue}); + + urlParams.filters = filters; } var sortAttribute = sorting["attribute"]; if (sortAttribute && sortAttribute.length > 0) { - urlParams.set("sort", sorting["attribute"]); - urlParams.set("asc", sorting["ascending"]); + var sortingAttributes = {"sort":sorting["attribute"], "asc":sorting["ascending"], "match":"OR"}; + urlParams.sortingAttributes = sortingAttributes; var sortHeader = $(sorting["attribute"] + "Header"); if (sortHeader) { @@ -816,18 +863,15 @@ function openMailbox(mailbox, reload) { var messageList = $("messageListBody").down('TBODY'); var key = mailbox; - if (urlParams.keys().length > 0) { - var p = urlParams.keys().collect(function(key) { return key + "=" + urlParams.get(key); }).join("&"); - key += "?" + p; - } if (reload) { // Don't change data source, only query UIDs from server and refresh // the view. Cases that end up here: // - performed a search // - clicked on Get Mail button - urlParams.set("no_headers", "1"); - Mailer.dataTable.load(urlParams); + urlParams.sortingAttributes.no_headers= "1"; + var content = Object.toJSON(urlParams); + Mailer.dataTable.load(content); Mailer.dataTable.refresh(); } else { @@ -843,7 +887,8 @@ function openMailbox(mailbox, reload) { } else // Fetch UIDs and headers from server - dataSource.load(urlParams); + var content = Object.toJSON(urlParams); + dataSource.load(content); // Cache data source Mailer.dataSources.set(key, dataSource); // Update unseen count @@ -851,8 +896,9 @@ function openMailbox(mailbox, reload) { } else { // Data source is cached, query only UIDs from server - urlParams.set("no_headers", "1"); - dataSource.load(urlParams); + urlParams.sortingAttributes.no_headers= "1"; + var content = Object.toJSON(urlParams); + dataSource.load(content); } // Associate data source with data table and render the view Mailer.dataTable.setSource(dataSource); diff --git a/UI/WebServerResources/SOGoDataTable.js b/UI/WebServerResources/SOGoDataTable.js index fed13db47..ee04b19f7 100644 --- a/UI/WebServerResources/SOGoDataTable.js +++ b/UI/WebServerResources/SOGoDataTable.js @@ -118,8 +118,7 @@ var SOGoDataTableInterface = { 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()); + this.dataSource.load(urlParams); }, visibleRowCount: function() { diff --git a/UI/WebServerResources/SOGoMailDataSource.js b/UI/WebServerResources/SOGoMailDataSource.js index c587b2982..7826ffe32 100644 --- a/UI/WebServerResources/SOGoMailDataSource.js +++ b/UI/WebServerResources/SOGoMailDataSource.js @@ -72,22 +72,13 @@ SOGoMailDataSource = Class.create({ // log ("MailDataSource.init() " + this.uids.length + " UIDs, " + this.cache.keys().length + " headers"); }, - load: function(urlParams) { - var params; + load: function(content) { this.loaded = false; - if (urlParams.keys().length > 0) { - params = urlParams.keys().collect(function(key) { return key + "=" + urlParams.get(key); }).join("&"); - } - else - params = ""; - this.id = this.url + "?" + params; - -// log ("MailDataSource.load() " + params); triggerAjaxRequest(this.url + "/uids", this._loadCallback.bind(this), null, - params, - { "Content-type": "application/x-www-form-urlencoded" }); + content, + { "content-type": "application/json" }); }, _loadCallback: function(http) { diff --git a/UI/WebServerResources/UIxMailSearch.css b/UI/WebServerResources/UIxMailSearch.css new file mode 100644 index 000000000..80969374d --- /dev/null +++ b/UI/WebServerResources/UIxMailSearch.css @@ -0,0 +1,95 @@ + +/*************** Table adjustment *****************/ + +TABLE#searchMailHeader, TABLE#searchFiltersList, TABLE#searchMailFooter +{ width: 100%; } + +TABLE#searchFiltersList, TABLE#searchMailFooter +{ + border: 1px solid #909090; + margin-top: 1em; + padding:2px; + border-radius: 3px; +} + +TABLE#searchFiltersList +{ + display:block; + max-height:100px; + overflow:auto; + width:672px; +} + +TABLE#searchMailFooter +{ + height:141px; + min-height: 110px; +} + +.buttonsCell +{ + width:1%; +} + +.inputsCell +{ + width:45%; +} + +.filterRow +{ + cellspacing=0; +} + +.scrollbar +{ + overflow-y:auto; + height:106px; + width:676px; + position:absolute; +} + +/*************** Table adjustment : search results *****************/ +.resultsRow { + cursor:pointer; +} + +/*************** Button adjustment *****************/ + +#headerButtons +{ + width:27%; +} + +#searchButton, #cancelButton +{ + margin-top:0; +} + +.searchByList, .searchArgumentsList, .searchInput +{ + width:100%; +} + +DIV#optionsButtons +{ float:left; } + +.button +{ + font-style:normal; +} + +#changeHeader +{ width:15px} + +DIV.bottomToolbar { + margin:0; + position:relative; + border-radius:5px; + width:40px; +} + +/*************** Lists *****************/ +.hidden +{ display:none; } + diff --git a/UI/WebServerResources/UIxMailSearch.js b/UI/WebServerResources/UIxMailSearch.js new file mode 100644 index 000000000..e9c3245e0 --- /dev/null +++ b/UI/WebServerResources/UIxMailSearch.js @@ -0,0 +1,368 @@ +/* -*- Mode: js2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +var searchParams = { + searchLocation: "", + subfolder: true, + filterMatching: "AND", + filters: [] +}; + +// This variable allowed the user to stop the ongoing search +var stopOngoingSearch = false; + +/************ Search mail header ************/ + +function onSearchClick() { +// This function updates the searchParams + var filterRows = $$(".filterRow"); + var searchButton = $("searchButton").down().innerText; + var mailAccountsList = $("mailAccountsList").options; + + if (searchButton == _("Search")) { + searchParams.filters = []; + stopOngoingSearch = false; + + // Get the mailboxe(s) + for (i = 0; i < mailAccountsList.length ; i++) { + if (mailAccountsList[i].selected) { + searchParams.searchLocation = mailAccountsList[i].innerText; + break; + } + } + + for (i = 0; i < filterRows.length; i++){ + // Get the information from every filter row before triggering the AJAX call + var filter = {}; + var searchByOptions = filterRows[i].down(".searchByList").options; + var searchArgumentsOptions = filterRows[i].down(".searchArgumentsList").options; + var searchInput = filterRows[i].down(".searchInput"); + + // Get the searchBy + for (j = 0; j < searchByOptions.length ; j++) { + if (searchByOptions[j].selected) { + filter.searchBy = searchByOptions[j].innerText; + break; + } + } + + // Get the searchArgument + for (j = 0; j < searchArgumentsOptions.length ; j++) { + if (searchArgumentsOptions[j].selected) { + filter.searchArgument = searchArgumentsOptions[j].innerText; + if (filter.searchArgument == "contains") + filter.searchArgument = "doesContain"; + else + filter.searchArgument = "NOT doesContain"; + break; + } + } + + // Get the input text + filter.searchInput = searchInput.getValue(); + + // Add the filter inside the searchParams.filters if the input is not empty + if (!filter.searchInput.empty()) + searchParams.filters.push(filter); + } + // Send the request only if there is at least one filter + if (searchParams.filters.length > 0) { + $("searchButton").down().innerText = _("Stop"); + searchMails(); + } + // TODO - give the user a warning or a notice that it needs at least one filter + } + else { + stopOngoingSearch = true; + $("searchButton").down().innerText = _("Search"); + } +} + +function searchMails() { + // Variables for the subfolders search + var optionsList = $("mailAccountsList").options; + var nbOptions = optionsList.length; + var selectedIndex = optionsList.selectedIndex; + + var mailAccountIndex = mailAccounts.indexOf(searchParams.searchLocation); + if (mailAccountIndex != -1) { + var accountNumber = "/" + mailAccountIndex; + var folderName = accountNumber + "/folderINBOX"; + var accountUser = userNames[mailAccountIndex]; + var folderPath = accountUser; + } + else { + var searchLocation = searchParams.searchLocation.split("/"); + var accountUser = searchLocation[0]; + var accountNumber = "/" + userNames.indexOf(accountUser); + + var position = searchLocation.length; + var folderName = accountNumber + "/folder" + searchLocation[1]; + for (i = 2; i < position; i++) + folderName += accountNumber + "/folder" + searchLocation[i]; + + var folderPath = optionsList[selectedIndex].innerText; + + } + + var subfolders = []; + if (searchParams.subfolder == true) { + for (i = 0; i < nbOptions; i++) { + if ((optionsList[i].innerText.search(folderPath) != -1) && (i != selectedIndex)) { + var splitArray = optionsList[i].innerText.split("/"); + // Remove the user information since it is not required + splitArray.splice(0, 1); + var subfolder = []; + var level = splitArray.length; + for(j = 0; j < level; j++) { + subfolder += "/folder" + splitArray[j]; + } + subfolders.push(accountNumber + subfolder); + } + } + } + + var urlstr = (ApplicationBaseURL + folderName + "/uids"); + + var callbackData = {"folderName" : folderName, "subfolders" : subfolders, "newSearch" : true}; + var object = {"filters":searchParams.filters, "sortingAttributes":{"match":searchParams.filterMatching}}; + var content = Object.toJSON(object); + document.searchMailsAjaxRequest = triggerAjaxRequest(urlstr, searchMailsCallback, callbackData, content, {"content-type": "application/json"}); + +} + +function searchMailsCallback(http) { + if (http.readyState == 4 && http.status == 200 && !stopOngoingSearch) { + var response = http.responseText.evalJSON(); + var table = $("searchMailFooter").down("tbody"); + + // Erase all previous entries before proceeding with the current request + if (http.callbackData.newSearch) { + var oldEntries = table.rows; + var count = oldEntries.length - 1; + for (x = count; x >= 0; x--) + oldEntries[x].remove(); + } + + // ["To", "Attachment", "Flagged", "Subject", "From", "Unread", "Priority", "Date", "Size", "rowClasses", "labels", "rowID", "uid"] + if (response.headers.length > 1) { + if ($("noSearchResults")) + $("noSearchResults").remove(); + + for (i = 1; i < response.headers.length; i++) { // Starts at 1 because the position 0 in the array are the headers of the table + var row = table.insertRow(i - 1); // This is the reason why row inserting starts at i - 1 + Element.addClassName(row, "resultsRow"); + row.writeAttribute("uid", response.headers[i][12]); + row.writeAttribute("folderName", http.callbackData.folderName); + + var cell1 = row.insertCell(0); + cell1.innerHTML = response.headers[i][3]; + + var cell2 = row.insertCell(1); + cell2.innerHTML = response.headers[i][4]; + + var cell3 = row.insertCell(2); + cell3.innerHTML = response.headers[i][7]; + + var cell4 = row.insertCell(3); + cell4.innerHTML = response.headers[i][12]; + } + + } + else if (http.callbackData.newSearch) { + if (!table.down("tr")) { + var row = table.insertRow(0); + var cell = row.insertCell(0); + var element = document.createElement("span"); + + cell.writeAttribute("id", "noSearchResults"); + cell.writeAttribute("colspan", "4"); + element.innerText = _("No matches found"); + cell.appendChild(element); + } + } + + if (http.callbackData.subfolders.length > 0) { + var folderName = http.callbackData.subfolders[0]; + var subfolders = http.callbackData.subfolders; + subfolders.splice(0, 1); + + var urlstr = (ApplicationBaseURL + folderName + "/uids"); + var callbackData = {"folderName" : folderName, "subfolders" : subfolders, "newSearch" : false}; + + // TODO - need to add these following contents ; asc, no-headers, sort + var object = {"filters":searchParams.filters, "sortingAttributes":{"match":searchParams.filterMatching}}; + var content = Object.toJSON(object); + document.searchMailsAjaxRequest = triggerAjaxRequest(urlstr, searchMailsCallback, callbackData, content, {"content-type": "application/json"}); + } + else { + $("searchButton").down().innerText = _("Search"); + } + + } +} + +function onCancelClick() { + disposeDialog(); + $("searchMailView").remove(); +} + +function onSearchSubfoldersCheck(event) { + searchParams.subfolder = (event.checked ? true : false); +} + +function onMatchFilters(event) { + searchParams.filterMatching = ((event.getAttribute("id") == "matchAllFilters") ? "AND" : "OR"); +} + +/**** Search mail body ****/ + +function onAddFilter() { + var table = $("searchFiltersList").down("tbody"); + var searchByList = $("searchByList").getElementsByTagName("li"); + var stringArgumentsList = $("stringArgumentsList").getElementsByTagName("li"); + + var rowCount = table.rows.length; + var row = table.insertRow(rowCount); + Element.addClassName(row, "filterRow"); + + var cell1 = row.insertCell(0); + var element1 = document.createElement("select"); + Element.addClassName(element1, "searchByList"); + element1.writeAttribute("id", "searchByListRow" + rowCount); + for (i = 0; i < searchByList.length; i++) { + var option = document.createElement("option"); + option.writeAttribute("value", i); + option.innerHTML = searchByList[i].innerText; + element1.appendChild(option); + } + cell1.appendChild(element1); + + var cell2 = row.insertCell(1); + var element2 = document.createElement("select"); + Element.addClassName(element2, "searchArgumentsList"); + element2.writeAttribute("id", "searchArgumentsListRow" + rowCount); + for (i = 0; i < stringArgumentsList.length; i++) { + var option = document.createElement("option"); + option.writeAttribute("value", i); + option.innerHTML = stringArgumentsList[i].innerText; + element2.appendChild(option); + } + cell2.appendChild(element2); + + var cell3 = row.insertCell(2); + Element.addClassName(cell3, "inputsCell"); + var element3 = document.createElement("input"); + Element.addClassName(element3, "searchInput"); + element3.writeAttribute("type", "text"); + element3.writeAttribute("name", "searchInput"); + element3.writeAttribute("value", ""); + element3.writeAttribute("id", "searchInputRow" + rowCount); + cell3.appendChild(element3); + + var cell4 = row.insertCell(3); + Element.addClassName(cell4, "buttonsCell"); + var element4 = document.createElement("a"); + var element5 = document.createElement("a"); + var buttonsDiv = document.createElement("div"); + var spanAddFilter = document.createElement("span"); + var imageAddFilter = document.createElement("img"); + var spanRemoveFilter = document.createElement("span"); + var imageRemoveFilter = document.createElement("img"); + Element.addClassName(element4, "addFilterButton"); + Element.addClassName(buttonsDiv, "bottomToolbar"); + element4.writeAttribute("name", "addFilter"); + element4.writeAttribute("id", "addFilterButtonRow" + rowCount); + element4.writeAttribute("onclick", "onAddFilter(this)"); + imageAddFilter.writeAttribute("src", "/SOGo.woa/WebServerResources/add-icon.png"); + spanAddFilter.appendChild(imageAddFilter); + element4.appendChild(spanAddFilter); + buttonsDiv.appendChild(element4); + + Element.addClassName(element5, "removeFilterButton"); + element5.writeAttribute("name", "removeFilter"); + element5.writeAttribute("id", "removeFilterButtonRow" + rowCount); + element5.writeAttribute("onclick", "onRemoveFilter(this)"); + imageRemoveFilter.writeAttribute("src", "/SOGo.woa/WebServerResources/remove-icon.png"); + spanRemoveFilter.appendChild(imageRemoveFilter); + element5.appendChild(spanRemoveFilter); + buttonsDiv.appendChild(element5); + cell4.appendChild(buttonsDiv); + +} + +function onRemoveFilter(event) { + var rows = $("searchFiltersList").down("tbody").getElementsByTagName("tr"); + var currentRow = event.up(".filterRow"); + + if(rows.length > 1) + currentRow.remove(); +} + +/**** Search mail Footer ****/ + +function onResultSelectionChange(event) { + var table = $("searchMailFooter").down("tbody"); + + if (event && (event.target.innerText != _("No matches found"))) { + var node = getTarget(event); + + if (node.tagName == "SPAN") + node = node.parentNode; + + // Update rows selection + onRowClick(event, node); + } +} + +/**** Search mail optionsButtons ****/ + +function onOpenClick(event) { +// This function is linked with the openButton and the doubleClick on a message + var selectedRow = $("searchMailFooter").down("._selected"); + var msguid = selectedRow.getAttribute("uid"); + var folderName = selectedRow.getAttribute("folderName"); + + var url = "/SOGo/so/sogo1/Mail" + folderName + "/" + msguid + "/popupview"; + if (selectedRow) { + openMessageWindow(msguid, url); + } +} + +function onDeleteClick(event) { + console.debug("deleteButton"); +} + +function onResizeClick() { + var resizeAttrribute = $("resizeButton").getAttribute("name"); + if (resizeAttrribute == "resizeUp") { + $("searchFiltersList").style.display = "none"; + $("searchMailFooter").style.height = "300px"; + $("resultsTable").style.height = "265px"; + $("resizeUp").style.display = "none"; + $("resizeDown").style.display = "block"; + $("resizeButton").writeAttribute("name", "resizeDown"); + } + else { + $("searchFiltersList").style.display = "block"; + $("searchMailFooter").style.height = "141px"; + $("resultsTable").style.height = "106px"; + $("resizeUp").style.display = "block"; + $("resizeDown").style.display = "none"; + $("resizeButton").writeAttribute("name", "resizeUp"); + } + +} + +/*************** Init ********************/ + +function initSearchMailView () { + + // Add one filterRow + onAddFilter(); + + // Observers : Event.on(element, eventName[, selector], callback) + $("searchMailFooter").down("tbody").on("mousedown", "tr", onResultSelectionChange); + $("searchMailFooter").down("tbody").on("dblclick", "tr", onOpenClick); + + +} \ No newline at end of file diff --git a/UI/WebServerResources/generic.css b/UI/WebServerResources/generic.css index 64dd2dd35..736d556d4 100644 --- a/UI/WebServerResources/generic.css +++ b/UI/WebServerResources/generic.css @@ -658,6 +658,19 @@ DIV.dialog > DIV top: 7px; } +DIV.dialog.searchMail { + position: relative; + margin: 0px; + padding: 0px; + opacity: 1; + width: 700px; + margin: 100px auto; +} + +DIV.dialog.searchMail > DIV { + padding: 10px; +} + DIV.dialog.none { position: relative; margin: 0px; diff --git a/UI/WebServerResources/generic.js b/UI/WebServerResources/generic.js index 1c45ddd15..f51d0b0a4 100644 --- a/UI/WebServerResources/generic.js +++ b/UI/WebServerResources/generic.js @@ -1998,7 +1998,7 @@ function createDialog(id, title, legend, content, positionClass) { var newDialog = createElement("div", id, ["dialog", positionClass]); newDialog.setStyle({"display": "none"}); - if (positionClass == "none") { + if (positionClass == "none" || positionClass == "searchMail") { var bgDiv = $("bgDialogDiv"); if (bgDiv) { bgDiv.show();