Mercurial > hg > rc1
changeset 34:50ac5484d514
one fix to distro
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/.gitignore Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,2 @@ +*.swp +config.inc.php
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/README.md Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,77 @@ + +Advanced Search Plugin for Roundcube +==================================== + +## Getting It + +You can download direct from GitHub or consider using +the [plugin repository for Roundcube](http://plugins.roundcube.net/) + +## Usage + +After install, 'Advanced search' will show up under the 'more' menu. + +Please use the _'stable'_ brach for deployment. + +Advantages: + +* This version should be tested and bug-free +* It uses minified versions of the JavaScript + +## Requirements +Version 2.0.0 requires Roundcube 0.9.4 or later + +## License + +This plugin is released under the GNU General Public License Version 3 +or later (http://www.gnu.org/licenses/gpl.html). + +Even if skins might contain some programming work, they are not considered +as a linked part of the plugin and therefore skins DO NOT fall under the +provisions of the GPL license. See the README file located in the core skins +folder for details on the skin license. + +## Download + +### GIT : +* Clone the GitHub repository to 'advanced_search': + + > git clone git://github.com/GMS-SA/roundcube-advanced-search.git advanced_search + +* Change to the 'stable' branch: + + > cd advanced_search + > git checkout -b stable origin/stable + +### ZIP : +* Swap branches to 'stable' +* Click on the 'ZIP' download icon +* Rename the unziped directory 'advanced_search' + +## Install + +* Place the 'advanced_search' plugin folder into the plugins directory of Roundcube. +* If using git and not wanting all the '.git' repository data in your live webmail: + + > cd advanced_search + > git archive --format=tar --prefix=advanced_search/ stable | tar -x -C /path/to/roundcube/plugins/ + + This will give you a git-free copy of the stable branch. +* Add advanced_search to $rcmail_config['plugins'] in your Roundcube config + +* To override defaults, copy the config-default.inc.php file to config.inc.php and modify + +## Upgrade +If upgrading from 1.2.0 or lower, you *must* review the config file. + +## Configuration + +* Available search criterias +* Targeted roundcube menu for the advanced search + +## Credits + +* Wilwert Claude +* Ludovicy Steve +* Moules Chris +* [Global Media Systems](http://www.gms.lu)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/advanced_search.js Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,726 @@ +(function($) { + /** + * The fontend scripts for an advanced search. + * + * @version 2.1.6 + * @licence GNU GPLv3+ + * @author Wilwert Claude + * @author Ludovicy Steve + * @author Chris Moules + * @website http://www.gms.lu + */ + + $.stack = { + /** + * This object is used to buffer all the server side information which doesn't change. So the script + * doesn't have to send an ajax-request for every new added row. + * + * @name stack + * @namespace + */ + date_criteria: {}, + flag_criteria: {}, + email_criteria: {}, + row: null, + messages: null + }; + + var search_loading = ''; + + $(document).on("change", "#button_display_option", function(e) { + var img = $('img', $(this).closest('p')); + var src = img.attr('src'); + if(this.value == 'messagemenu') { + src = src.replace('menu_location_b.jpg', 'menu_location_a.jpg'); + } else { + src = src.replace('menu_location_a.jpg', 'menu_location_b.jpg'); + } + img.attr('src', src); + }); + + $(document).on("change", "#_show_message_mbox_info, #_show_message_label_header", function(e) { + var img = $('img', $(this).closest('p')); + if($(this).is(':checked')) { + img.removeClass('disabled'); + } else { + img.addClass('disabled'); + } + }); + + /** + * The callback function of the initial dialog call. It creates the dialog and buffers the serverside + * informations into an object. + * + * @param {object} r The serverside informations + */ + rcmail.addEventListener('plugin.show', function(r) { + $.stack.date_criteria = r.date_criteria; + $.stack.flag_criteria = r.flag_criteria; + $.stack.email_criteria = r.email_criteria; + $.stack.row = r.row; + $.stack.html = r.html; + + var $html = $(r.html); + var saved_searches_label = rcmail.gettext('saved_searches', 'advanced_search'); + var saved_searches = '<span class="saved_searches"> <label for="select_saved_search">' + saved_searches_label + ': <select name="select_saved_search" id="select_saved_search"><option value=""></option></select></label></span>'; + title = $('<div>' + r.title + saved_searches + '<div>'); + var saved_searches_select = $('[name=select_saved_search]', title); + if (r.saved_searches.length) { + var i; + for (i in r.saved_searches) { + saved_searches_select.append('<option value="' + r.saved_searches[i] + '">' + r.saved_searches[i] + '</option>'); + } + } + $html.dialog({ + width: 640, + height: 300, + resizable: true, + draggable: true, + title: title, + dialogClass: "advanced_search_dialog", + close: function() { + $('body').css('overflow', 'auto'); + }, + create: function() { + $('body').css('overflow', 'hidden'); + } + }); + + saved_searches_select.bind("blur click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select", function(e) { + e.stopPropagation(); + }); + + saved_searches_select.bind("mouseenter", function(e) { + saved_searches_select.focus(); + }); + + saved_searches_select.bind('change', function(e) { + var search_name = $(this).val(); + if (search_name == "") { + $('#adsearch-popup').html($.stack.html); + } else { + rcmail.http_request('plugin.get_saved_search', { search_name : search_name }); + $('[name=delete]', '#adsearch-popup').show(); + } + }); + + }); + + rcmail.addEventListener('plugin.load_saved_search', function(search) { + var $form = $("#adsearch-popup form"), + $tr = $('tr', $('tbody', $form)).not(':first').not(':last'), + $last = $('tr:last', $('tbody', $form)); + saved_search = search.search, + data = []; + $tr.remove(); + $("[name=folder]", $form).val(search.folder); + $("[name=subfolder]", $form).prop('checked', search.sub_folders == "true"); + $('span.sub-folders', $form).css('display', search.folder == 'all' ? 'none' : 'inline'); + + var i = 0; + for (i; i < saved_search.length; i++) { + var row; + if (i == 0) { + row = $('<tr>' + $("tr:eq(1)", $.stack.html).html() + '</tr>'); + } else { + row = $($.stack.row); + } + $("[name=method]", row).val(saved_search[i].method); + $("[name=filter]", row).val(saved_search[i].filter); + $("[name=not]", row).prop('checked', saved_search[i]['not'] == "true"); + $("[name=filter-exclude]", row).prop('checked', saved_search[i]['excluded'] == "true"); + $last.before(row); + $("[name=filter]", row).trigger("change"); + $("[name=filter-val]", row).val(saved_search[i]['filter-val']); + } + }); +//messagelistcontainer table thead + rcmail.addEventListener('plugin.advanced_search_add_header', function(evt) { + if($("#messagelistcontainer #rcavbox1").length == 0) { + var Mbox = rcmail.gettext('mbox', 'advanced_search'); + $("#messagelistcontainer table.fixedcopy thead tr:first").append('<td class="mbox" id="rcavbox1"><span class="mbox">' + Mbox + '</span></td>'); + $("#messagelistcontainer table#messagelist thead tr:first").append('<td class="mbox" id="rcavbox2"><span class="mbox">' + Mbox + '</span></td>'); + } + }); + + rcmail.addEventListener('plugin.advanced_search_del_header', function(evt) { + $("#messagelistcontainer #rcavbox1").remove(); + $("#messagelistcontainer #rcavbox2").remove(); + }); + + rcube_webmail.prototype.advanced_search_add_mbox = function (mbox, count, showMbox) { + if (!this.gui_objects.messagelist || !this.message_list) { + return false; + } + + var colspan = showMbox == true ? 9 : 8; + $(rcmail.message_list.list).append('<tr class="aslabel_mbox"><td><span class="aslabel_found">' + count + '</span></td><td colspan="' + colspan + '">' + mbox + '</td></tr>'); + } + + rcube_webmail.prototype.advanced_search_active = function(param) { + var page = param.replace('_page=', ''); + rcmail.http_request('plugin.trigger_search_pagination', { _page : page }); + } + + /** + * Builds the search to send to the server + */ + var get_search_data = function() + { + var $form = $("#adsearch-popup form"), + $tr = $('tr', $('tbody', $form)).not(':first').not(':last'), + data = []; + + if ($tr.length) { + $tr.each(function() { + var item = {not: $('input[name=not]', $(this)).prop('checked'), + excluded: $('input[name=filter-exclude]', $(this)).prop('checked'), + filter: $('option:selected', $('select[name=filter]', $(this))).val(), + 'filter-val': $('input[name=filter-val]', $(this)).val()}; + + if ($('select[name=method]', $(this)).length) { + item.method = $('option:selected', $('select[name=method]', $(this))).val(); + } + + data.push(item); + }); + } + + return data; + } + + /** + * The onclick event handler for the search button. This generates the search query and sends them + * to the server. It also stores the wrapped set of the old rows into an object for later cleanup. + * + * @param {object} e The event element + */ + $(document).on("click", 'input[name=search]', function(e) { + e.preventDefault(); + + rcmail.clear_message_list(); + + $.stack.messages = $('tr', $('tbody', '#messagelist')); + + var $form = $("#adsearch-popup form"); + search_loading = rcmail.set_busy(true, 'loading'); + rcmail.http_request('plugin.trigger_search', + {search: get_search_data(), + current_folder: rcmail.env.mailbox, + folder: $('select[name=folder]', $form).val(), + sub_folders: $('input[name=subfolder]', $form).prop('checked')}); + + $("#adsearch-popup").closest('div.ui-dialog-content').dialog('close'); + }); + + /** + * The onclick event handler of the "reset search" button, which resets the advanced search + * back to its initial state. + * + * @param {object} e The event element + */ + $(document).on("click", 'input[name=reset]', function(e) { + e.preventDefault(); + $('#adsearch-popup').html($.stack.html); + $('[name=select_saved_search]').val(""); + }); + + /** + * The onclick event handler for the "add" button. This adds one new row to the query dialog + * + * @param {object} e The event element + */ + $(document).on("click", 'button[name=add]', function(e) { + e.preventDefault(); + + $(this).closest('tr').after($.stack.row); + }); + + /** + * The onclick event handler for the "delete" button. This removes the containing row from + * the query dialog + * + * @param {object} e The event element + */ + $(document).on("click", 'button[name=delete]', function(e) { + e.preventDefault(); + + $(this).closest('tr').remove(); + }); + + /** + * The change event handler for the filter selector. + * Make the input field context relevent. + * + * @param {object} e The event element + */ + $(document).on("click", 'select[name=filter]', function(e) { + var $row_input = $(this).nextUntil('tr', 'input[name=filter-val]'), + old_avs_type = $row_input.data("avs_type"); + + if ($.inArray($(this).val(), $.stack.date_criteria) >= 0) { + if(old_avs_type !== "date") { + $row_input.val(''); + $row_input.datepicker({dateFormat: rcmail.env.date_format}); + } + + $row_input.data("avs_type", "date"); + } else if ($.inArray($(this).val(), $.stack.email_criteria) >= 0) { + if(old_avs_type !== "email") { + rcmail.init_address_input_events($row_input, ""); + rcmail.addEventListener('autocomplete_insert', function(e){ + e.field.value = e.insert.replace(/.*<(\S*?@\S*?)>.*/, "$1"); + }); + } + + $row_input.data("avs_type", "email"); + } else if ($.inArray($(this).val(), $.stack.flag_criteria) >= 0) { + if (old_avs_type !== "flag_criteria") { + $row_input.val(''); + $row_input.hide(); + } + + $row_input.data("avs_type", "flag_criteria"); + } else { + $row_input.data("avs_type", "none"); + } + + switch (old_avs_type) { + case "date": + if (($row_input.data("avs_type") !== "date") && $row_input.hasClass("hasDatepicker")) { + $row_input.datepicker("destroy"); + } + break; + case "email": + if (($row_input.data("avs_type") !== "email")) { + $row_input.removeAttr("autocomplete"); + $row_input.unbind('keydown'); + $row_input.unbind('keypress'); + } + break; + case "flag_criteria": + if (($row_input.data("avs_type") !== "flag_criteria") && !$row_input.is(":visible")) { + $row_input.show(); + } + break; + } + }); + + /** + * The change event handler for the folder select box. It makes the subfolder checkbox invisible + * when selecting the "all folders" option + * + * @param {object} e The event element + */ + $(document).on("click", 'select[name=folder]', function(e) { + $('span.sub-folders', $(this).closest('form')).css('display', $(this).val() == 'all' ? 'none' : 'inline'); + }); + + /** + * The onclick event handler for the menu entry of the advanced search. This makes the initial call + * of the advanced search and fires a query to the server to get every important information. + * + * @param {object} e The event element + */ + $(document).on("click", 'a.icon.advanced-search, a.button.advanced-search', function(e) { + e.preventDefault(); + + if (!$('#adsearch-popup').length) { + rcmail.http_request('plugin.display_advanced_search'); + } else { + $("#adsearch-popup").closest('div.ui-dialog-content').dialog('open'); + } + }); + + /** + * Stop propagation of keydown and keypress events. + * This should stop these events being processed by other listeners in the mailbox. + * + * @param {object} e The event element + */ + $(document).on("keydown keypress", "#adsearch-popup", function(e) { + e.stopPropagation(); + }); + + $(document).on("click", "#adsearch-popup input.delete_search", function(e) { + e.stopPropagation(); + e.preventDefault(); + var search_name = $("[name=select_saved_search]").val(); + var txt = {}; + txt['cancel'] = rcmail.get_label('advanced_search.cancel'); + txt['delete'] = rcmail.get_label('advanced_search.delete'); + $( "<p><strong>" + search_name + "</strong></p>" ).dialog({ + resizable: true, + height:180, + modal: true, + title: rcmail.gettext('advanced_search.deletesearch'), + buttons: [ + { + text: txt['delete'], + click: function() { + rcmail.http_request('plugin.delete_search', {search_name: search_name}); + $("[value=" + search_name + "]", "[name=select_saved_search]").remove(); + $("[name=select_saved_search]").val("").trigger("change"); + $( this ).dialog( "close" ); + }, + }, + { + text: txt['cancel'], + click: function() { + $( this ).dialog( "close" ); + } + }] + + }); + }); + + $(document).on("click", "#save_the_search", function(e) { + e.stopPropagation(); + e.preventDefault(); + var labelName = rcmail.gettext('name', 'advanced_search'); + var labelSave = rcmail.gettext('save', 'advanced_search'); + var labelCancel = rcmail.gettext('cancel', 'advanced_search'); + var save_search = '<table>' + + ' <tr><td>' + labelName + ' </td><td><input type="text" name="search_name" /></td></tr>' + + ' <tr><td></td><td><input type="submit" class="button mainaction" value="' + labelSave + '" /> <input type="reset" class="button reset" value="' + labelCancel + '" /></td></tr>' + + '</table>'; + save_search = $(save_search); + $("[name=search_name]", save_search).val($("[name=select_saved_search]").val()); + + save_search.dialog({ + title: rcmail.gettext('advanced_search.save_the_search'), + dialogClass: 'saveTheSearch', + close: function() { + $(this).remove(); + }, + width: $("#adsearch-popup").width(), + height: $("#adsearch-popup").height(), + modal: true + }); + $(".mainaction", save_search).bind('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + var search_name = $("[name=search_name]", save_search).val(); + var $form = $("#adsearch-popup form"); + rcmail.http_request('plugin.save_search', + {search: get_search_data(), + search_name: search_name, + folder: $('select[name=folder]', $form).val(), + sub_folders: $('input[name=subfolder]', $form).prop('checked')}); + var isNewSearch = true; + $("[name=select_saved_search] option").each(function(e) { + if ($(this).attr("value") == search_name) { + isNewSearch = false; + } + }); + if (isNewSearch) { + $("[name=select_saved_search]").append('<option value="' + search_name + '">' + search_name + '</option>'); + $("[name=select_saved_search]").val(search_name); + } + save_search.dialog('close'); + }); + + $(".reset", save_search).bind('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + save_search.dialog('close'); + }); + + }); + + var advanced_search_redirect_draft_messages = function(check) { + if (rcmail.env.search_request == "advanced_search_active") { + if (rcmail.task == 'mail') { + uid = rcmail.get_single_uid(); + if (uid && (!rcmail.env.uid || uid != rcmail.env.uid || check)) { + if ((rcmail.env.mailbox == rcmail.env.drafts_mailbox) || check) { + url = { + _mbox: this.env.mailbox, + _search: 'advanced_search_active' + }; + url[this.env.mailbox == this.env.drafts_mailbox ? '_draft_uid' : '_uid'] = uid; + this.goto_url('compose', url, true); + } + } + } + return true; + } + return false; + } + + var advanced_search_perform_action = function(props, action) { + + var raw_selection = rcmail.message_list.get_selection(); + var md5_folders = rcmail.env.as_md5_folders; + var i; + var selections = {}; + for (i in raw_selection) { + raw_selection[i]; + var parts = raw_selection[i].split('__MB__'); + var mid = parts[0]; + var md5Mbox = parts[1]; + var mbox = md5_folders[md5Mbox]; + if (!selections[mbox]) { + selections[mbox] = []; + } + selections[mbox].push(mid); + } + + if (i != undefined) { + // show wait message + if (rcmail.env.action == 'show') { + lock = rcmail.set_busy(true, 'movingmessage'); + } else { + rcmail.show_contentframe(false); + } + // Hide message command buttons until a message is selected + rcmail.enable_command(rcmail.env.message_commands, false); + var j; + for (j in selections) { + rcmail.select_folder(j, '', true); + rcmail.env.mailbox = j; + var uids = selections[j].join(','); + var lock = false, + post_data = rcmail.selection_post_data({ + _target_mbox: props.id, + _uid: uids + }); + rcmail._with_selected_messages(action, post_data, lock); + } + // Make sure we have no selection + rcmail.env.uid = undefined; + rcmail.message_list.selection = []; + } + + } + + var advanced_search_check_multi_mbox = function () { + var raw_selection = rcmail.message_list.get_selection(); + var md5_folders = rcmail.env.as_md5_folders; + var i; + var mcount = 0; + var selections = {}; + for (i in raw_selection) { + raw_selection[i]; + var parts = raw_selection[i].split('__MB__'); + var mid = parts[0]; + var md5Mbox = parts[1]; + var mbox = md5_folders[md5Mbox]; + if (!selections[mbox]) { + selections[mbox] = []; + mcount++; + } + selections[mbox].push(mid); + } + + return { + isMulti: mcount > 1, + selections: selections + }; + } + + /** + * The roundcube init funtion, which registers and enables the advanced search command. + */ + rcmail.addEventListener('init', function() { + rcmail.register_command('plugin.advanced_search', true, true); + rcmail.enable_command('plugin.advanced_search', true); + + rcmail.addEventListener('plugin.search_complete', function(r) { + rcmail.set_busy(false, 'loading', search_loading); + /* Start registering event listeners for handling drag/drop, marking, flagging etc... */ + rcmail.addEventListener('beforeedit', function (command) { + rcmail.env.framed = true; + if (advanced_search_redirect_draft_messages(true)) { + return false; + } + }); + + rcmail.message_list.addEventListener('dblclick', function (o) { + advanced_search_redirect_draft_messages(); + }); + + rcmail.message_list.addEventListener('select', function (list) { + if (rcmail.env.search_request == "advanced_search_active") { + if (list.selection.length == 1) { + var parts = list.selection[0].split('__MB__'); + var mid = parts[0]; + var md5Mbox = parts[1]; + var mbox = rcmail.env.as_md5_folders[md5Mbox]; + rcmail.env.uid = mid; + if (rcmail.env.mailbox != mbox) { + var ex = []; + li = rcmail.get_folder_li(mbox, '', true); + parent = $(li).parents(".mailbox"); + parent.each(function () { + div = $(this.getElementsByTagName('div')[0]); + a = $(this.getElementsByTagName('a')[0]); + if (div.hasClass('collapsed')) { + ex.push($(a).attr("rel")); + } + }); + for (var i = ex.length - 1; i >= 0; i--) { + rcmail.command('collapse-folder', ex[i]); + } + rcmail.select_folder(mbox, '', true); + rcmail.env.mailbox = mbox; + } + return false; + } + } + }); + + rcmail.addEventListener('beforemoveto', function (props) { + if (rcmail.env.search_request == 'advanced_search_active') { + advanced_search_perform_action(props, 'moveto'); + + return false; + } + }); + + rcmail.addEventListener('beforedelete', function (props) { + if (rcmail.env.search_request == 'advanced_search_active') { + advanced_search_perform_action(props, 'delete'); + + return false; + } + }); + + rcmail.addEventListener('beforemark', function (flag) { + if (rcmail.env.search_request == 'advanced_search_active') { + var res = advanced_search_check_multi_mbox(); + //Update on server + var i; + var sel = res.selections; + for (i in sel) { + var uids = sel[i].join(','); + var lock = false; + var post_data = { + _uid: uids, + _flag: flag, + _mbox: i, + _remote: 1 + }; + rcmail.http_post('mark', post_data, lock); + } + var raw_selection = rcmail.message_list.get_selection(); + for(i in raw_selection) { + var key = raw_selection[i]; + switch (flag) { + case 'read': + rcmail.message_list.rows[key].unread = 0; + break; + case 'unread': + rcmail.message_list.rows[key].unread = 1; + break; + case 'flagged': + rcmail.message_list.rows[key].flagged = 1; + break; + case 'unflagged': + rcmail.message_list.rows[key].flagged = 0; + break; + } + } + //Refresh ui + var messages = []; + var selections = rcmail.message_list.get_selection(); + var j; + for (j in selections) { + messages.push('#rcmrow' + selections[j]); + } + var selector = messages.join(', '); + var selection = $(selector); + switch (flag) { + case 'read': + selection.removeClass('unread'); + break; + case 'unread': + selection.addClass('unread'); + break; + case 'flagged': + selection.addClass('flagged'); + $("td.flag span", selection).removeClass('unflagged').addClass('flagged'); + break; + case 'unflagged': + selection.removeClass('flagged'); + $("td.flag span", selection).removeClass('flagged').addClass('unflagged'); + break; + default: + break; + } + return false; + } + }); + + rcmail.addEventListener('beforeforward', function (props) { + if (rcmail.env.search_request == 'advanced_search_active' && rcmail.message_list.selection.length > 1) { + var res = advanced_search_check_multi_mbox(); + if (res.isMulti == true) { + //Selections from more then one folder + return false; + } else { + //Only one folder, redirecting + var i, url, sel = res.selections; + for (i in sel) { + url = '&_forward_uid=' + sel[i].join(',') + '&_mbox=' + i; + } + url += '&_attachment=1&_action=compose'; + window.location = location.pathname + rcmail.env.comm_path + url; + + return false; + } + } + }); + + rcmail.addEventListener('beforeforward-attachment', function (props) { + if (rcmail.env.search_request == 'advanced_search_active' && rcmail.message_list.selection.length > 1) { + var res = advanced_search_check_multi_mbox(); + if (res.isMulti == true) { + //Selections from more then one folder + return false; + } else { + //Only one folder, redirecting + var i, url, sel = res.selections; + for (i in sel) { + url = '&_forward_uid=' + sel[i].join(',') + '&_mbox=' + i; + } + url += '&_attachment=1&_action=compose'; + window.location = location.pathname + rcmail.env.comm_path + url; + + return false; + } + } + }); + + rcmail.addEventListener('beforetoggle_flag', function (props) { + if (rcmail.env.search_request == 'advanced_search_active') { + var flag = $(props).hasClass('unflagged') ? 'flagged' : 'unflagged'; + var tr = $(props).closest('tr'); + var id = tr.attr('id').replace('rcmrow', ''); + var parts = id.split('__MB__'); + var lock = false; + var mbox = rcmail.env.as_md5_folders[parts[1]]; + var post_data = { + _uid: parts[0], + _flag: flag, + _mbox: mbox, + _remote: 1 + }; + rcmail.http_post('mark', post_data, lock); + if (flag == 'flagged') { + tr.addClass('flagged'); + $("td.flag span", tr).removeClass('unflagged').addClass('flagged'); + rcmail.message_list.rows[id].flagged = 1; + } else { + tr.removeClass('flagged'); + $("td.flag span", tr).removeClass('flagged').addClass('unflagged'); + rcmail.message_list.rows[id].flagged = 0; + } + return false; + } + }); + /* End registering event listeners */ + }); + + }); +})(jQuery);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/advanced_search.min.js Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,1 @@ +(function($){$.stack={date_criteria:{},flag_criteria:{},email_criteria:{},row:null,messages:null};var search_loading="";$(document).on("change","#button_display_option",function(e){var img=$("img",$(this).closest("p"));var src=img.attr("src");if(this.value=="messagemenu"){src=src.replace("menu_location_b.jpg","menu_location_a.jpg")}else{src=src.replace("menu_location_a.jpg","menu_location_b.jpg")}img.attr("src",src)});$(document).on("change","#_show_message_mbox_info, #_show_message_label_header",function(e){var img=$("img",$(this).closest("p"));if($(this).is(":checked")){img.removeClass("disabled")}else{img.addClass("disabled")}});rcmail.addEventListener("plugin.show",function(r){$.stack.date_criteria=r.date_criteria;$.stack.flag_criteria=r.flag_criteria;$.stack.email_criteria=r.email_criteria;$.stack.row=r.row;$.stack.html=r.html;var $html=$(r.html);var saved_searches_label=rcmail.gettext("saved_searches","advanced_search");var saved_searches='<span class="saved_searches"> <label for="select_saved_search">'+saved_searches_label+': <select name="select_saved_search" id="select_saved_search"><option value=""></option></select></label></span>';title=$("<div>"+r.title+saved_searches+"<div>");var saved_searches_select=$("[name=select_saved_search]",title);if(r.saved_searches.length){var i;for(i in r.saved_searches){saved_searches_select.append('<option value="'+r.saved_searches[i]+'">'+r.saved_searches[i]+"</option>")}}$html.dialog({width:640,height:300,resizable:true,draggable:true,title:title,dialogClass:"advanced_search_dialog",close:function(){$("body").css("overflow","auto")},create:function(){$("body").css("overflow","hidden")}});saved_searches_select.bind("blur click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select",function(e){e.stopPropagation()});saved_searches_select.bind("mouseenter",function(e){saved_searches_select.focus()});saved_searches_select.bind("change",function(e){var search_name=$(this).val();if(search_name==""){$("#adsearch-popup").html($.stack.html)}else{rcmail.http_request("plugin.get_saved_search",{search_name:search_name});$("[name=delete]","#adsearch-popup").show()}})});rcmail.addEventListener("plugin.load_saved_search",function(search){var $form=$("#adsearch-popup form"),$tr=$("tr",$("tbody",$form)).not(":first").not(":last"),$last=$("tr:last",$("tbody",$form));saved_search=search.search,data=[];$tr.remove();$("[name=folder]",$form).val(search.folder);$("[name=subfolder]",$form).prop("checked",search.sub_folders=="true");$("span.sub-folders",$form).css("display",search.folder=="all"?"none":"inline");var i=0;for(i;i<saved_search.length;i++){var row;if(i==0){row=$("<tr>"+$("tr:eq(1)",$.stack.html).html()+"</tr>")}else{row=$($.stack.row)}$("[name=method]",row).val(saved_search[i].method);$("[name=filter]",row).val(saved_search[i].filter);$("[name=not]",row).prop("checked",saved_search[i]["not"]=="true");$("[name=filter-exclude]",row).prop("checked",saved_search[i]["excluded"]=="true");$last.before(row);$("[name=filter]",row).trigger("change");$("[name=filter-val]",row).val(saved_search[i]["filter-val"])}});rcmail.addEventListener("plugin.advanced_search_add_header",function(evt){if($("#messagelistcontainer #rcavbox1").length==0){var Mbox=rcmail.gettext("mbox","advanced_search");$("#messagelistcontainer table.fixedcopy thead tr:first").append('<td class="mbox" id="rcavbox1"><span class="mbox">'+Mbox+"</span></td>");$("#messagelistcontainer table#messagelist thead tr:first").append('<td class="mbox" id="rcavbox2"><span class="mbox">'+Mbox+"</span></td>")}});rcmail.addEventListener("plugin.advanced_search_del_header",function(evt){$("#messagelistcontainer #rcavbox1").remove();$("#messagelistcontainer #rcavbox2").remove()});rcube_webmail.prototype.advanced_search_add_mbox=function(mbox,count,showMbox){if(!this.gui_objects.messagelist||!this.message_list){return false}var colspan=showMbox==true?9:8;$(rcmail.message_list.list).append('<tr class="aslabel_mbox"><td><span class="aslabel_found">'+count+'</span></td><td colspan="'+colspan+'">'+mbox+"</td></tr>")};rcube_webmail.prototype.advanced_search_active=function(param){var page=param.replace("_page=","");rcmail.http_request("plugin.trigger_search_pagination",{_page:page})};var get_search_data=function(){var $form=$("#adsearch-popup form"),$tr=$("tr",$("tbody",$form)).not(":first").not(":last"),data=[];if($tr.length){$tr.each(function(){var item={not:$("input[name=not]",$(this)).prop("checked"),excluded:$("input[name=filter-exclude]",$(this)).prop("checked"),filter:$("option:selected",$("select[name=filter]",$(this))).val(),"filter-val":$("input[name=filter-val]",$(this)).val()};if($("select[name=method]",$(this)).length){item.method=$("option:selected",$("select[name=method]",$(this))).val()}data.push(item)})}return data};$(document).on("click","input[name=search]",function(e){e.preventDefault();rcmail.clear_message_list();$.stack.messages=$("tr",$("tbody","#messagelist"));var $form=$("#adsearch-popup form");search_loading=rcmail.set_busy(true,"loading");rcmail.http_request("plugin.trigger_search",{search:get_search_data(),current_folder:rcmail.env.mailbox,folder:$("select[name=folder]",$form).val(),sub_folders:$("input[name=subfolder]",$form).prop("checked")});$("#adsearch-popup").closest("div.ui-dialog-content").dialog("close")});$(document).on("click","input[name=reset]",function(e){e.preventDefault();$("#adsearch-popup").html($.stack.html);$("[name=select_saved_search]").val("")});$(document).on("click","button[name=add]",function(e){e.preventDefault();$(this).closest("tr").after($.stack.row)});$(document).on("click","button[name=delete]",function(e){e.preventDefault();$(this).closest("tr").remove()});$(document).on("click","select[name=filter]",function(e){var $row_input=$(this).nextUntil("tr","input[name=filter-val]"),old_avs_type=$row_input.data("avs_type");if($.inArray($(this).val(),$.stack.date_criteria)>=0){if(old_avs_type!=="date"){$row_input.val("");$row_input.datepicker({dateFormat:rcmail.env.date_format})}$row_input.data("avs_type","date")}else if($.inArray($(this).val(),$.stack.email_criteria)>=0){if(old_avs_type!=="email"){rcmail.init_address_input_events($row_input,"");rcmail.addEventListener("autocomplete_insert",function(e){e.field.value=e.insert.replace(/.*<(\S*?@\S*?)>.*/,"$1")})}$row_input.data("avs_type","email")}else if($.inArray($(this).val(),$.stack.flag_criteria)>=0){if(old_avs_type!=="flag_criteria"){$row_input.val("");$row_input.hide()}$row_input.data("avs_type","flag_criteria")}else{$row_input.data("avs_type","none")}switch(old_avs_type){case"date":if($row_input.data("avs_type")!=="date"&&$row_input.hasClass("hasDatepicker")){$row_input.datepicker("destroy")}break;case"email":if($row_input.data("avs_type")!=="email"){$row_input.removeAttr("autocomplete");$row_input.unbind("keydown");$row_input.unbind("keypress")}break;case"flag_criteria":if($row_input.data("avs_type")!=="flag_criteria"&&!$row_input.is(":visible")){$row_input.show()}break}});$(document).on("click","select[name=folder]",function(e){$("span.sub-folders",$(this).closest("form")).css("display",$(this).val()=="all"?"none":"inline")});$(document).on("click","a.icon.advanced-search, a.button.advanced-search",function(e){e.preventDefault();if(!$("#adsearch-popup").length){rcmail.http_request("plugin.display_advanced_search")}else{$("#adsearch-popup").closest("div.ui-dialog-content").dialog("open")}});$(document).on("keydown keypress","#adsearch-popup",function(e){e.stopPropagation()});$(document).on("click","#adsearch-popup input.delete_search",function(e){e.stopPropagation();e.preventDefault();var search_name=$("[name=select_saved_search]").val();var txt={};txt["cancel"]=rcmail.get_label("advanced_search.cancel");txt["delete"]=rcmail.get_label("advanced_search.delete");$("<p><strong>"+search_name+"</strong></p>").dialog({resizable:true,height:180,modal:true,title:rcmail.gettext("advanced_search.deletesearch"),buttons:[{text:txt["delete"],click:function(){rcmail.http_request("plugin.delete_search",{search_name:search_name});$("[value="+search_name+"]","[name=select_saved_search]").remove();$("[name=select_saved_search]").val("").trigger("change");$(this).dialog("close")}},{text:txt["cancel"],click:function(){$(this).dialog("close")}}]})});$(document).on("click","#save_the_search",function(e){e.stopPropagation();e.preventDefault();var labelName=rcmail.gettext("name","advanced_search");var labelSave=rcmail.gettext("save","advanced_search");var labelCancel=rcmail.gettext("cancel","advanced_search");var save_search="<table>"+" <tr><td>"+labelName+' </td><td><input type="text" name="search_name" /></td></tr>'+' <tr><td></td><td><input type="submit" class="button mainaction" value="'+labelSave+'" /> <input type="reset" class="button reset" value="'+labelCancel+'" /></td></tr>'+"</table>";save_search=$(save_search);$("[name=search_name]",save_search).val($("[name=select_saved_search]").val());save_search.dialog({title:rcmail.gettext("advanced_search.save_the_search"),dialogClass:"saveTheSearch",close:function(){$(this).remove()},width:$("#adsearch-popup").width(),height:$("#adsearch-popup").height(),modal:true});$(".mainaction",save_search).bind("click",function(e){e.preventDefault();e.stopPropagation();var search_name=$("[name=search_name]",save_search).val();var $form=$("#adsearch-popup form");rcmail.http_request("plugin.save_search",{search:get_search_data(),search_name:search_name,folder:$("select[name=folder]",$form).val(),sub_folders:$("input[name=subfolder]",$form).prop("checked")});var isNewSearch=true;$("[name=select_saved_search] option").each(function(e){if($(this).attr("value")==search_name){isNewSearch=false}});if(isNewSearch){$("[name=select_saved_search]").append('<option value="'+search_name+'">'+search_name+"</option>");$("[name=select_saved_search]").val(search_name)}save_search.dialog("close")});$(".reset",save_search).bind("click",function(e){e.preventDefault();e.stopPropagation();save_search.dialog("close")})});var advanced_search_redirect_draft_messages=function(check){if(rcmail.env.search_request=="advanced_search_active"){if(rcmail.task=="mail"){uid=rcmail.get_single_uid();if(uid&&(!rcmail.env.uid||uid!=rcmail.env.uid||check)){if(rcmail.env.mailbox==rcmail.env.drafts_mailbox||check){url={_mbox:this.env.mailbox,_search:"advanced_search_active"};url[this.env.mailbox==this.env.drafts_mailbox?"_draft_uid":"_uid"]=uid;this.goto_url("compose",url,true)}}}return true}return false};var advanced_search_perform_action=function(props,action){var raw_selection=rcmail.message_list.get_selection();var md5_folders=rcmail.env.as_md5_folders;var i;var selections={};for(i in raw_selection){raw_selection[i];var parts=raw_selection[i].split("__MB__");var mid=parts[0];var md5Mbox=parts[1];var mbox=md5_folders[md5Mbox];if(!selections[mbox]){selections[mbox]=[]}selections[mbox].push(mid)}if(i!=undefined){if(rcmail.env.action=="show"){lock=rcmail.set_busy(true,"movingmessage")}else{rcmail.show_contentframe(false)}rcmail.enable_command(rcmail.env.message_commands,false);var j;for(j in selections){rcmail.select_folder(j,"",true);rcmail.env.mailbox=j;var uids=selections[j].join(",");var lock=false,post_data=rcmail.selection_post_data({_target_mbox:props.id,_uid:uids});rcmail._with_selected_messages(action,post_data,lock)}rcmail.env.uid=undefined;rcmail.message_list.selection=[]}};var advanced_search_check_multi_mbox=function(){var raw_selection=rcmail.message_list.get_selection();var md5_folders=rcmail.env.as_md5_folders;var i;var mcount=0;var selections={};for(i in raw_selection){raw_selection[i];var parts=raw_selection[i].split("__MB__");var mid=parts[0];var md5Mbox=parts[1];var mbox=md5_folders[md5Mbox];if(!selections[mbox]){selections[mbox]=[];mcount++}selections[mbox].push(mid)}return{isMulti:mcount>1,selections:selections}};rcmail.addEventListener("init",function(){rcmail.register_command("plugin.advanced_search",true,true);rcmail.enable_command("plugin.advanced_search",true);rcmail.addEventListener("plugin.search_complete",function(r){rcmail.set_busy(false,"loading",search_loading);rcmail.addEventListener("beforeedit",function(command){rcmail.env.framed=true;if(advanced_search_redirect_draft_messages(true)){return false}});rcmail.message_list.addEventListener("dblclick",function(o){advanced_search_redirect_draft_messages()});rcmail.message_list.addEventListener("select",function(list){if(rcmail.env.search_request=="advanced_search_active"){if(list.selection.length==1){var parts=list.selection[0].split("__MB__");var mid=parts[0];var md5Mbox=parts[1];var mbox=rcmail.env.as_md5_folders[md5Mbox];rcmail.env.uid=mid;if(rcmail.env.mailbox!=mbox){var ex=[];li=rcmail.get_folder_li(mbox,"",true);parent=$(li).parents(".mailbox");parent.each(function(){div=$(this.getElementsByTagName("div")[0]);a=$(this.getElementsByTagName("a")[0]);if(div.hasClass("collapsed")){ex.push($(a).attr("rel"))}});for(var i=ex.length-1;i>=0;i--){rcmail.command("collapse-folder",ex[i])}rcmail.select_folder(mbox,"",true);rcmail.env.mailbox=mbox}return false}}});rcmail.addEventListener("beforemoveto",function(props){if(rcmail.env.search_request=="advanced_search_active"){advanced_search_perform_action(props,"moveto");return false}});rcmail.addEventListener("beforedelete",function(props){if(rcmail.env.search_request=="advanced_search_active"){advanced_search_perform_action(props,"delete");return false}});rcmail.addEventListener("beforemark",function(flag){if(rcmail.env.search_request=="advanced_search_active"){var res=advanced_search_check_multi_mbox();var i;var sel=res.selections;for(i in sel){var uids=sel[i].join(",");var lock=false;var post_data={_uid:uids,_flag:flag,_mbox:i,_remote:1};rcmail.http_post("mark",post_data,lock)}var raw_selection=rcmail.message_list.get_selection();for(i in raw_selection){var key=raw_selection[i];switch(flag){case"read":rcmail.message_list.rows[key].unread=0;break;case"unread":rcmail.message_list.rows[key].unread=1;break;case"flagged":rcmail.message_list.rows[key].flagged=1;break;case"unflagged":rcmail.message_list.rows[key].flagged=0;break}}var messages=[];var selections=rcmail.message_list.get_selection();var j;for(j in selections){messages.push("#rcmrow"+selections[j])}var selector=messages.join(", ");var selection=$(selector);switch(flag){case"read":selection.removeClass("unread");break;case"unread":selection.addClass("unread");break;case"flagged":selection.addClass("flagged");$("td.flag span",selection).removeClass("unflagged").addClass("flagged");break;case"unflagged":selection.removeClass("flagged");$("td.flag span",selection).removeClass("flagged").addClass("unflagged");break;default:break}return false}});rcmail.addEventListener("beforeforward",function(props){if(rcmail.env.search_request=="advanced_search_active"&&rcmail.message_list.selection.length>1){var res=advanced_search_check_multi_mbox();if(res.isMulti==true){return false}else{var i,url,sel=res.selections;for(i in sel){url="&_forward_uid="+sel[i].join(",")+"&_mbox="+i}url+="&_attachment=1&_action=compose";window.location=location.pathname+rcmail.env.comm_path+url;return false}}});rcmail.addEventListener("beforeforward-attachment",function(props){if(rcmail.env.search_request=="advanced_search_active"&&rcmail.message_list.selection.length>1){var res=advanced_search_check_multi_mbox();if(res.isMulti==true){return false}else{var i,url,sel=res.selections;for(i in sel){url="&_forward_uid="+sel[i].join(",")+"&_mbox="+i}url+="&_attachment=1&_action=compose";window.location=location.pathname+rcmail.env.comm_path+url;return false}}});rcmail.addEventListener("beforetoggle_flag",function(props){if(rcmail.env.search_request=="advanced_search_active"){var flag=$(props).hasClass("unflagged")?"flagged":"unflagged";var tr=$(props).closest("tr");var id=tr.attr("id").replace("rcmrow","");var parts=id.split("__MB__");var lock=false;var mbox=rcmail.env.as_md5_folders[parts[1]];var post_data={_uid:parts[0],_flag:flag,_mbox:mbox,_remote:1};rcmail.http_post("mark",post_data,lock);if(flag=="flagged"){tr.addClass("flagged");$("td.flag span",tr).removeClass("unflagged").addClass("flagged");rcmail.message_list.rows[id].flagged=1}else{tr.removeClass("flagged");$("td.flag span",tr).removeClass("flagged").addClass("unflagged");rcmail.message_list.rows[id].flagged=0}return false}})})})})(jQuery); \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/advanced_search.php Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,1088 @@ +<?php +/** + * Processing an advanced search over an E-Mail Account + * + * @version 2.1.5 + * @licence GNU GPLv3+ + * @author Wilwert Claude + * @author Ludovicy Steve + * @author Chris Moules + * @website http://www.gms.lu + * + * Updated by Alex S Grebenschikov (www.poralix.com) + * to make it compatible with RoundCube 1.3.0 + * Last modified: Tue Aug 1 06:41:46 CEST 2017 + * + */ + +class advanced_search extends rcube_plugin +{ + + /** + * Instance of rcmail + * + * @var object + * @access private + */ + private $rc; + + public $task = 'mail|settings'; + + /** + * Plugin config + * + * @var array + * @access private + */ + private $config; + + /** + * Localization strings + * + * @var array + * @access private + */ + private $i18n_strings = array(); + + /** + * Initialisation of the plugin + * + * @access public + * @return null + */ + public function init() + { + $this->rc = rcmail::get_instance(); + $this->load_config("config-default.inc.php"); + $this->load_config(); + $this->config = $this->rc->config->get('advanced_search_plugin'); + $this->register_action('plugin.display_advanced_search', array($this, 'display_advanced_search')); + $this->register_action('plugin.trigger_search', array($this, 'trigger_search')); + $this->register_action('plugin.trigger_search_pagination', array($this, 'trigger_search_pagination')); + $this->register_action('plugin.save_search', array($this, 'save_search')); + $this->register_action('plugin.delete_search', array($this, 'delete_search')); + $this->register_action('plugin.get_saved_search', array($this, 'get_saved_search')); + + $this->skin = $this->rc->config->get('skin'); + $this->add_texts('localization', true); + $this->populate_i18n(); + if(isset($this->config['criteria'])) { + foreach($this->config['criteria'] as $key => $translation) { + $this->config['criteria'][$key] = $this->gettext($key); + } + } + $this->include_script('advanced_search.js'); + + if ($this->rc->task == 'mail') { + $file = 'skins/' . $this->skin . '/advanced_search.css'; + + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + + if (empty($this->rc->action)) { + $this->add_menu_entry(); + } + } elseif ($this->rc->task == 'settings') { + $this->add_hook('preferences_list', array($this, 'preferences_list')); + $this->add_hook('preferences_save', array($this, 'preferences_save')); + $this->add_hook('preferences_sections_list', array($this, 'preferences_section')); + $file = 'skins/' . $this->skin . '/advanced_search.css'; + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + } + + $this->add_hook('startup', array($this, 'startup')); + } + + + public function startup($args) + { + $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GET); + if (!isset($search)) { + $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_POST); + } + $rsearch = $search == 'advanced_search_active'; + $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GET); + $draft_uid = rcube_utils::get_input_value('_draft_uid', rcube_utils::INPUT_GET); + $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GET); + $page = rcube_utils::get_input_value('_page', rcube_utils::INPUT_GET); + $sort = rcube_utils::get_input_value('_sort', rcube_utils::INPUT_GET); + + if (!empty($uid)) { + $parts = explode('__MB__', $uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + if (!empty($draft_uid)) { + $parts = explode('__MB__', $draft_uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + + if ($search == 'advanced_search_active') { + if ($args['action'] == 'show' && !empty($uid)) { + $parts = explode('__MB__', $uid); + $uid = $parts[0]; + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_uid' => $uid)); + } + if ($args['action'] == 'compose') { + $draft_uid = rcube_utils::get_input_value('_draft_uid', rcube_utils::INPUT_GET); + $parts = explode('__MB__', $draft_uid); + $draft_uid = $parts[0]; + if (!empty($draft_uid)) { + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_draft_uid' => $draft_uid)); + } + } + if ($args['action'] == 'list' && $rsearch) { + $this->rc->output->command('advanced_search_active', '_page=' . $page . '&_sort=' . $sort); + $this->rc->output->send(); + $args['abort'] = true; + } + if ($args['action'] == 'mark') { + $flag = rcube_utils::get_input_value('_flag', rcube_utils::INPUT_POST); + $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); + + $post_str = '_flag=' . $flag . '&_uid=' . $uid; + if ($quiet = rcube_utils::get_input_value('_quiet', rcube_utils::INPUT_POST)) { + $post_str .= '&_quiet=' . $quiet; + } + if ($from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST)) { + $post_str .= '&_from=' . $from; + } + if ($count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST)) { + $post_str .= '&_count=' . $count; + } + if ($ruid = rcube_utils::get_input_value('_ruid', rcube_utils::INPUT_POST)) { + $post_str .= '&_ruid=' . $ruid; + } + $this->rc->output->command('label_mark', $post_str); + $this->rc->output->send(); + $args['abort'] = true; + } + + } else { + if ($args['action'] != 'plugin.get_saved_search' && $args['action'] != 'plugin.save_search' && $args['action'] != 'plugin.delete_search') { + $this->rc->output->command('plugin.advanced_search_del_header', array()); + } + } + } + + /** + * This function populates an array with localization texts. + * This is needed as ew are using a lot of localizations from core. + * The core localizations are not avalable directly in JS + * + * @access private + * @return null + */ + private function populate_i18n() + { + $core = array('advsearch', 'search', 'resetsearch', 'addfield', 'delete', 'cancel'); + + foreach ($core as $label) { + $this->i18n_strings[$label] = $this->rc->gettext($label); + } + + $local = array('in', 'and', 'or', 'not', 'where', 'exclude', 'andsubfolders', 'allfolders', 'save_the_search', 'has_been_saved', 'deletesearch', 'has_been_deleted'); + + foreach ($local as $label) { + $this->i18n_strings[$label] = $this->gettext($label); + } + } + + /** + * This adds a button into the configured menu to use the advanced search + * + * @access public + * @return null + */ + public function add_menu_entry() + { + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + if ($target_menu != 'toolbar') { + $this->api->add_content( + html::tag( + 'li', + null, + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'label' => 'advsearch', + 'type' => 'link', + 'classact' => 'icon advanced-search active', + 'class' => 'icon advanced-search', + 'innerclass' => 'icon advanced-search' + ) + ) + ), + $target_menu + ); + } else { + $this->api->add_content( + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'title' => 'advsearch', + 'label' => 'search', + 'type' => 'link', + 'classact' => 'button advanced-search active', + 'class' => 'button advanced-search', + 'innerclass' => 'button advanced-search', + ) + ), + $target_menu + ); + } + } + + /** + * This function quotes some specific values based on their data type + * + * @param mixed $input The value to get quoted + * @access public + * @return The quoted value + */ + public function quote($value) + { + if (getType($value) == 'string') { + if (!preg_match('/"/', $value)) { + $value = preg_replace('/^"/', '', $value); + $value = preg_replace('/"$/', '', $value); + $value = preg_replace('/"/', '\\"', $value); + } + + $value = '"' . $value . '"'; + } + + return $value; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access private + * @return string or int + */ + private function process_search_part($search_part) + { + $command_str = ''; + $flag = false; + + // Check for valid input + if (!array_key_exists($search_part['filter'], $this->config['criteria'])) { + $this->rc->output->show_message($this->gettext('internalerror'), 'error'); + + return 0; + } + if (in_array($search_part['filter'], $this->config['flag_criteria'])) { + $flag = true; + } + if (!$flag && !(isset($search_part['filter-val']) && $search_part['filter-val'] != '')) { + return 1; + } + + // Negate part + if ($search_part['not'] == 'true') { + $command_str .= 'NOT '; + } + + $command_str .= $search_part['filter']; + + if (!$flag) { + if (in_array($search_part['filter'], $this->config['date_criteria'])) { + // Take date format from user environment + $date_format = $this->rc->config->get('date_format', 'Y-m-d'); + // Try to use PHP5.2+ DateTime but fallback to ugly old method + if (class_exists('DateTime')) { + $date = DateTime::createFromFormat($date_format, $search_part['filter-val']); + $command_str .= ' ' . $this->quote($date->format("d-M-Y")); + } else { + $date_format = preg_replace('/(\w)/', '%$1', $date_format); + $date_array = strptime($search_part['filter-val'], $date_format); + $unix_ts = mktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'], $date_array['tm_mon']+1, $date_array['tm_mday'], $date_array['tm_year']+1900); + $command_str .= ' ' . $this->quote(date("d-M-Y", $unix_ts)); + } + } elseif (in_array($search_part['filter'], $this->config['email_criteria'])) { + // Strip possible ',' added by autocomplete + $command_str .= ' ' . $this->quote(trim($search_part['filter-val'], " \t,")); + } else { + // Don't try to use a value for a binary flag object + $command_str .= ' ' . $this->quote($search_part['filter-val']); + } + } + + return $command_str; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access public + * @return The final search command + */ + public function get_search_query($input) + { + $command = array(); + + foreach ($input as $search_part) { + // Skip excluded parts + if ($search_part['excluded'] == 'true') { + continue; + } + if (! $part_command = $this->process_search_part($search_part)) { + return 0; + } + // Skip invalid parts + if ($part_command === 1) { + continue; + } + + $command[] = array('method' => isset($search_part['method']) ? $search_part['method'] : 'and', + 'command' => $part_command); + } + + $command_string = $this->build_search_string($command); + + return $command_string; + } + + /** + * This function converts the preconfigured query parts (as array) into an IMAP compatible string + * + * @param array $command_array An array containing the advanced search criteria + * @access public + * @return The command string + */ + private function build_search_string($command_array) + { + $command = array(); + $paranthesis = 0; + $prev_method = null; + $next_method = null; + $cnt = count($command_array); + + foreach ($command_array as $k => $v) { + $part = ''; + $next_method = 'unknown'; + + // Lookup next method + if ($k < $cnt-1) { + $next_method = $command_array[$k+1]['method']; + } + + // If previous option was OR, close any open brakets + if ($paranthesis > 0 && $prev_method == 'or' && $v['method'] != 'or') { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + // If there are two consecutive ORs, add brakets + // If the next option is a new OR, add the prefix here + // If the next option is _not_ an OR, and the current option is AND, prefix ALL + if ($next_method == 'or') { + if ($v['method'] == 'or') { + $part .= ' ('; + $paranthesis++; + } + $part .= 'OR '; + } elseif ($v['method'] == 'and') { + $part .= 'ALL '; + } + + $part .= $v['command']; + + // If this is the end of the query, and we have open brakets, close them + if ($k == $cnt-1 && $paranthesis > 0) { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + $prev_method = $v['method']; + $command[] = $part; + } + + $command = implode(' ', $command); + + return $command; + } + + /** + * This functions sends the initial data to the client side where a form (in dialog) is built for the advanced search + * + * @access public + * @return null + */ + public function display_advanced_search() + { + $ret = array('html' => $this->generate_searchbox(), + 'row' => $this->add_row(), + 'saved_searches' => $this->get_saved_search_names(), + 'title' => $this->i18n_strings['advsearch'], + 'date_criteria' => $this->config['date_criteria'], + 'flag_criteria' => $this->config['flag_criteria'], + 'email_criteria' => $this->config['email_criteria']); + + $this->rc->output->command('plugin.show', $ret); + } + + public function generate_searchbox() + { + $search_button = new html_inputfield(array('type' => 'submit', 'name' => 'search', 'class' => 'button mainaction', 'value' => $this->i18n_strings['search'])); + $reset_button = new html_inputfield(array('type' => 'reset', 'name' => 'reset', 'class' => 'button reset', 'value' => $this->i18n_strings['resetsearch'])); + $save_button = html::tag('input', array('type' => 'submit', 'name' => 'save_the_search', id=> 'save_the_search', 'class' => 'button save_search', 'value' => $this->i18n_strings['save_the_search'])); + $delete_button = new html_inputfield(array('type' => 'submit', 'name' => 'delete', 'style' => 'display: none;', 'class' => 'button delete_search', 'value' => $this->i18n_strings['deletesearch'])); + + $layout_table = new html_table(); + $layout_table->add(null, $search_button->show()); + $folderConfig = array('name' => 'folder'); + $layout_table->add( + null, + $this->i18n_strings['in'] . ': ' . + $this->folder_selector($folderConfig)->show($this->rc->storage->get_folder()) . + html::span( + array('class' => 'sub-folders'), + $this->i18n_strings['andsubfolders'] . ': ' . + html::tag( + 'input', + array('type' => 'checkbox', 'name' => 'subfolder'), + null + ) + ) . + $this->i19n_strings['where'] + ); + $first_row = $this->add_row(true); + $layout_table->add_row(); + $layout_table->add(array('class' => 'adv-search-and-or'), null); + $layout_table->add(null, $first_row); + $layout_table->add_row(); + $layout_table->add(null, $search_button->show()); + $layout_table->add(null, $save_button . ' ' . $reset_button->show() . ' ' . $delete_button->show()); + + return html::tag( + 'div', + array('id' => 'adsearch-popup'), + html::tag( + 'form', + array('method' => 'post', 'action' => '#'), + $layout_table->show() + ) + ); + } + + /** + * This function is used to render the html of the advanced search form and also + * the later following rows are created by this function + * + * @param array $folders Array of folders + * @param boolean $first True if form gets created, False to create a new row + * @access public + * @return string The final html + */ + public function add_row($first = false) + { + $row_html = ''; + $optgroups = ''; + + $criteria = $this->config['criteria']; + $all_criteria = array( + $this->gettext('Common') => $this->config['prefered_criteria'], + $this->gettext('Addresses') => $this->config['email_criteria'], + $this->gettext('Dates') => $this->config['date_criteria'], + $this->gettext('Flags') => $this->config['flag_criteria'], + $this->gettext('Other') => $this->config['other_criteria'], + ); + + foreach ($all_criteria as $label => $specific_criteria) { + $options = ''; + + foreach ($specific_criteria as $value) { + $options .= html::tag('option', array('value' => $value), $criteria[$value]); + } + + $optgroups .= html::tag('optgroup', array('label' => $label), $options); + } + + $tmp = html::tag('select', array('name' => 'filter'), $optgroups); + $tmp .= $this->i18n_strings['not'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'not'), null); + $tmp .= html::tag('input', array('type' => 'text', 'name' => 'filter-val')); + $tmp .= $this->i18n_strings['exclude'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'filter-exclude'), null); + $tmp .= html::tag('button', array('name' => 'add', 'class' => 'add'), $this->i18n_strings['addfield']); + + if ($first) { + $row_html = $tmp; + } else { + $and_or_select = new html_select(array('name' => 'method')); + $and_or_select->add($this->i18n_strings['and'], 'and'); + $and_or_select->add($this->i18n_strings['or'], 'or'); + $tmp .= html::tag('button', array('name' => 'delete', 'class' => 'delete'), $this->i18n_strings['delete']); + $row_html = html::tag( + 'tr', + null, + html::tag( + 'td', + array('class' => 'adv-search-and-or'), + $and_or_select->show() + ) . + html::tag( + 'td', + null, + $tmp + ) + ); + } + + return $row_html; + } + + /** + * Return folders list as html_select object + * + * This is a copy of the core function and adapted to fit + * the needs of the advanced_search function + * + * @param array $p Named parameters + * + * @return html_select HTML drop-down object + */ + public function folder_selector($p = array()) + { + $p += array('maxlength' => 100, 'realnames' => false, 'is_escaped' => true); + $a_mailboxes = array(); + $storage = $this->rc->get_storage(); + + $list = $storage->list_folders_subscribed(); + $delimiter = $storage->get_hierarchy_delimiter(); + + foreach ($list as $folder) { + $this->rc->build_folder_tree($a_mailboxes, $folder, $delimiter); + } + + $select = new html_select($p); + $select->add($this->i18n_strings['allfolders'], "all"); + + $this->rc->render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); + + return $select; + } + + public function trigger_search_pagination($param) + { + $_GET['search'] = $_SESSION['av_search']; + $_GET['folder'] = $_SESSION['av_folder']; + $_GET['sub_folders'] = $_SESSION['av_sub_folders']; + $this->trigger_search(true); + } + + /** + * Here is where the actual query is fired to the imap server and the result is evaluated and sent back to the client side + * + * @access public + * @return null + */ + public function trigger_search($inPagination = false) + { + $search = rcube_utils::get_input_value('search', rcube_utils::INPUT_GPC); + // reset list_page and old search results + $this->rc->storage->set_page(1); + $this->rc->storage->set_search_set(null); + $page = rcube_utils::get_input_value('_page', rcube_utils::INPUT_GPC); + $page = $page ? $page : 1; + $pagesize = $this->rc->storage->get_pagesize(); + + if (!empty($search)) { + $mbox = rcube_utils::get_input_value('folder', rcube_utils::INPUT_GPC); + $imap_charset = RCMAIL_CHARSET; + $sort_column = rcmail_sort_column(); + $search_str = $this->get_search_query($search); + $sub_folders = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC) == 'true'; + $folders = array(); + $result_h = array(); + $count = 0; + $new_id = 1; + $current_mbox = $this->rc->storage->get_folder(); + $uid_list = array(); + //Store information in session for pagination + $_SESSION['av_search'] = $search; + $_SESSION['av_folder'] = $mbox; + $_SESSION['av_sub_folders'] = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC); + $nosub = $sub_folders; + $folders = $this->rc->get_storage()->list_folders_subscribed(); + if (empty($folders) || ($sub_folders === false && $mbox !== 'all')) { + $folders = array($mbox); + } elseif ($mbox !== 'all') { + if ($sub_folders === false) { + $folders = array($mbox); + } else { + $folders = $this->rc->get_storage()->list_folders_subscribed_direct($mbox); + } + } + $md5folders = array(); + foreach ($folders as $folder) { + $md5folders[md5($folder)] = $folder; + } + $this->rc->output->set_env('as_md5_folders', $md5folders); + + if ($search_str) { + $res = $this->perform_search($search_str, $folders, $page); + $count = $res['count']; + } + + + if ($count > 0) { + $_SESSION['advanced_search']['uid_list'] = $uid_list; + if ($search_str && $inPagination == false) { + $this->rc->output->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); + } + } elseif ($err_code = $this->rc->storage->get_error_code()) { + rcmail_display_server_error(); + } else { + $this->rc->output->show_message('searchnomatch', 'notice'); + } + + $current_folder = rcube_utils::get_input_value('current_folder', rcube_utils::INPUT_GPC); + + $this->rc->output->set_env('search_request', 'advanced_search_active'); + $this->rc->output->set_env('messagecount', $count); + $this->rc->output->set_env('pagecount', ceil($count / $pagesize)); + $this->rc->output->set_env('exists', $this->rc->storage->count($current_folder, 'EXISTS')); + $this->rc->output->command('set_rowcount', rcmail_get_messagecount_text($count, $page)); + $this->rc->output->command('plugin.search_complete'); + $this->rc->output->send(); + } + } + + /** + * return javascript commands to add rows to the message list + */ + public function rcmail_js_message_list($a_headers, $insert_top = false, $a_show_cols = null, $avmbox = false, $avcols = array(), $showMboxColumn = false) + { + global $CONFIG, $RCMAIL, $OUTPUT; + $uid_mboxes = array(); + + if (empty($a_show_cols)) { + if (!empty($_SESSION['list_attrib']['columns'])) { + $a_show_cols = $_SESSION['list_attrib']['columns']; + } else { + $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject'); + } + } else { + if (!is_array($a_show_cols)) { + $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols)); + } + $head_replace = true; + } + + $mbox = $RCMAIL->storage->get_folder(); + + // make sure 'threads' and 'subject' columns are present + if (!in_array('subject', $a_show_cols)) { + array_unshift($a_show_cols, 'subject'); + } + if (!in_array('threads', $a_show_cols)) { + array_unshift($a_show_cols, 'threads'); + } + $_SESSION['list_attrib']['columns'] = $a_show_cols; + + // Make sure there are no duplicated columns (#1486999) + $a_show_cols = array_merge($a_show_cols, $avcols); + $a_show_cols = array_unique($a_show_cols); + + // Plugins may set header's list_cols/list_flags and other rcube_message_header variables + // and list columns + $plugin = $RCMAIL->plugins->exec_hook( + 'messages_list', + array('messages' => $a_headers, 'cols' => $a_show_cols) + ); + $a_show_cols = $plugin['cols']; + $a_headers = $plugin['messages']; + $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : null; + + // get name of smart From/To column in folder context + if (($f = array_search('fromto', $a_show_cols)) !== false) { + $smart_col = rcmail_message_list_smart_column_name(); + } + if ($this->coltypesSet == false) { + $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead, $smart_col); + if ($showMboxColumn === true) { + $OUTPUT->command('plugin.advanced_search_add_header', array()); + } + $this->coltypesSet = true; + } + + if (empty($a_headers)) { + return; + } + + // remove 'threads', 'attachment', 'flag', 'status' columns, we don't need them here + foreach (array('threads', 'attachment', 'flag', 'status', 'priority') as $col) { + if (($key = array_search($col, $a_show_cols)) !== false) { + unset($a_show_cols[$key]); + } + } + + // loop through message headers + foreach ($a_headers as $n => $header) { + if (empty($header)) { + continue; + } + $a_msg_cols = array(); + $a_msg_flags = array(); + // format each col; similar as in rcmail_message_list() + foreach ($a_show_cols as $col) { + $col_name = $col == 'fromto' ? $smart_col : $col; + + if (in_array($col_name, array('from', 'to', 'cc', 'replyto'))) { + $cont = rcmail_address_string($header->$col_name, 3, false, null, $header->charset); + } elseif ($col == 'subject') { + $cont = trim(rcube_mime::decode_header($header->$col, $header->charset)); + if (!$cont) { + $cont = $this->gettext('nosubject'); + } + $cont = rcube::Q($cont); + } elseif ($col == 'size') { + $cont = $this->rc->show_bytes($header->$col); + } elseif ($col == 'date') { + $cont = rcube_utils::format_datestr($header->date,$date_format); + } else { + $cont = rcube::Q($header->$col); + } + $a_msg_cols[$col] = $cont; + } + + $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags)); + if ($header->depth) { + $a_msg_flags['depth'] = $header->depth; + } elseif ($header->has_children) { + $roots[] = $header->uid; + } + if ($header->parent_uid) { + $a_msg_flags['parent_uid'] = $header->parent_uid; + } + if ($header->has_children) { + $a_msg_flags['has_children'] = $header->has_children; + } + if ($header->unread_children) { + $a_msg_flags['unread_children'] = $header->unread_children; + } + if ($header->others['list-post']) { + $a_msg_flags['ml'] = 1; + } + if ($header->priority) { + $a_msg_flags['prio'] = (int) $header->priority; + } + $a_msg_flags['ctype'] = rcube::Q($header->ctype); + $a_msg_flags['mbox'] = $mbox; + if (!empty($header->list_flags) && is_array($header->list_flags)) { + $a_msg_flags = array_merge($a_msg_flags, $header->list_flags); + } + if (!empty($header->list_cols) && is_array($header->list_cols)) { + $a_msg_cols = array_merge($a_msg_cols, $header->list_cols); + } + if ($showMboxColumn === true) { + $a_msg_flags['avmbox'] = $avmbox; + } + + $OUTPUT->command( + 'add_message_row', + $header->uid . '__MB__' . md5($mbox), + $a_msg_cols, + $a_msg_flags, + $insert_top + ); + $id = $header->uid . '__MB__' . md5($mbox); + $uid_mboxes[$id] = array('uid' => $header->uid, 'mbox' => $mbox, 'md5mbox' => md5($mbox)); + } + + if ($RCMAIL->storage->get_threading()) { + $OUTPUT->command('init_threads', (array) $roots, $mbox); + } + + return $uid_mboxes; + } + + + private function do_pagination($folders, $onPage) + { + $perPage = $this->rc->storage->get_pagesize(); + $from = $perPage * $onPage - $perPage + 1; + $to = $from + $perPage - 1; + $got = 0; + $pos = 0; + $cbox = ""; + $boxStart = 0; + $boxStop = 0; + $fetch = array(); + foreach ($folders as $box => $num) { + $i = $num; + if ($box != $cbox) { + $boxStart = 0; + $boxStop = 0; + $cbox = $box; + } + while ($i--) { + $pos++; + $boxStart++; + if ($pos >= $from && $pos <= $to) { + if (!isset($fetch[$box])) { + $fetch[$box] = array("from" => $boxStart); + } + $fetch[$box]['to'] = $boxStart; + $got++; + } + } + if ($got >= $perPage) { + break; + } + } + + return $fetch; + } + + /** + * Save advanced search preferences + * + * @access public + */ + public function preferences_save($args) + { + if ($args['section'] != 'advancedsearch') { + return; + } + $rcmail = rcmail::get_instance(); + + $displayOptions = array(); + $displayOptions['_show_message_label_header'] = rcube_utils::get_input_value('_show_message_label_header', rcube_utils::INPUT_POST) == 1 ? true : false; + $displayOptions['_show_message_mbox_info'] = rcube_utils::get_input_value('_show_message_mbox_info', rcube_utils::INPUT_POST) == 1 ? true : false; + $displayOptions['target_menu'] = rcube_utils::get_input_value('button_display_option', rcube_utils::INPUT_POST); + + $args['prefs']['advanced_search_display_options'] = $displayOptions; + + return($args); + } + + /** + * Add a section advanced search to the preferences section list + * + * @access public + */ + public function preferences_section($args) + { + $args['list']['advancedsearch'] = array( + 'id' => 'advancedsearch', + 'section' => rcube::Q($this->gettext('advancedsearch')) + ); + + return($args); + } + + /** + * Display advanced search configuration in user preferences tab + * + * @access public + */ + public function preferences_list($args) + { + if ($args['section'] == 'advancedsearch') { + + $this->rc = rcmail::get_instance(); + $args['blocks']['label_display_options'] = array('options' => array(), 'name' => rcube::Q($this->gettext('label_display_options'))); + + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + $options = ''; + $optarg = array('value' => 'messagemenu'); + if ($target_menu == 'messagemenu') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_a.jpg'; + } + $options .= html::tag('option', $optarg, rcube::Q($this->gettext('display_in_messagemenu'))); + $optarg = array('value' => 'toolbar'); + if ($target_menu == 'toolbar') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_b.jpg'; + } + $options .= html::tag('option', $optarg,rcube::Q($this->gettext('display_in_toolbar'))); + $select = html::tag('select', array('name' => 'button_display_option', 'id' => 'button_display_option'), $options); + + $label1 = html::label('_show_message_label_header', rcube::Q($this->gettext('mailbox_headers_in_results'))); + $label2 = html::label('_show_message_mbox_info', rcube::Q($this->gettext('mailbox_info_in_results'))); + $label3 = html::label('button_display_option', rcube::Q($this->gettext('show_advanced_search'))); + + $arg1 = array('name' => '_show_message_label_header', 'id' => '_show_message_label_header', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $arg1['checked'] = 'checked'; + $img1class = 'enabled'; + } else { + $img1class = 'disabled'; + } + $check1 = html::tag('input', $arg1); + $arg2 = array('name' => '_show_message_mbox_info', 'id' => '_show_message_mbox_info', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] === true) { + $arg2['checked'] = 'checked'; + $img2class = 'enabled'; + } else { + $img2class = 'disabled'; + } + + $img1 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_row.jpg'), 'class' => $img1class)); + $img2 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_col.jpg'), 'class' => $img2class)); + $img3 = html::img(array('src' => $this->url('skins/larry/images/' . $target_image))); + + $check2 = html::tag('input', $arg2); + $args['blocks']['label_display_options']['options'][0] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check1 . ' ' . $label1 . '</span> ' . $img1 . '</p>'); + $args['blocks']['label_display_options']['options'][1] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check2 . ' ' . $label2 . '</span> ' . $img2 . '</p>'); + $args['blocks']['label_display_options']['options'][2] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $label3 . ' ' . $select . '</span> ' . $img3 . '</p>'); + } + + return($args); + } + + private function perform_search($search_string, $folders, $page = 1) + { + // Search all folders and build a final set + if ($folders[0] == 'all' || empty($folders)) { + $folders_search = $this->rc->imap->list_folders_subscribed(); + } else { + $folders_search = $folders; + } + $count = 0; + $folder_count = array(); + foreach ($folders_search as $mbox) { + $this->rc->storage->set_folder($mbox); + $this->rc->storage->search($mbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $result = array(); + $fcount = $this->rc->storage->count($mbox, 'ALL', !empty($_REQUEST['_refresh'])); + $count += $fcount; + $folder_count[$mbox] = $fcount; + } + foreach ($folder_count as $k => $v) { + if ($v == 0) { + unset($folder_count[$k]); + } + } + $fetch = $this->do_pagination($folder_count, $page); + $mails = array(); + $currentMailbox = ""; + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $showMboxColumn = isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] ? true : false; + $uid_mboxes = array(); + foreach ($fetch as $mailbox => $data) { + if ($currentMailbox != $mailbox) { + $currentMailbox = $mailbox; + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $this->rc->output->command('advanced_search_add_mbox', $mailbox, $folder_count[$mailbox], $showMboxColumn); + } + } + $uid_mboxes = array_merge($uid_mboxes, $this->getMails($mailbox, $data, $search_string, $showMboxColumn)); + } + + return array('result' => array(), 'count' => $count, 'uid_mboxes' => $uid_mboxes); + } + + private function getMails($mailbox, $data, $search_string, $showMboxColumn) + { + $pageSize = $this->rc->storage->get_pagesize(); + $msgNum = $data['from']; + $startPage = ceil($msgNum/$pageSize); + $msgMod = $msgNum % $pageSize; + $multiPage = "false"; + $firstArrayElement = $msgMod == 0 ? ($pageSize-1) : ($msgMod-1); + $quantity = $data['to'] - $data['from']; + if ($data['from'] + $quantity > $pageSize) { + $multiPage = "true"; + } + $this->rc->storage->set_folder($mailbox); + $this->rc->storage->search($mailbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $messages = $this->rc->storage->list_messages('', $startPage); + if ($multiPage) { + $messages = array_merge($messages, $this->rc->storage->list_messages('', $startPage+1)); + } + //FIRST: 0 QUANTITY: 2 + $sliceTo = $quantity + 1; + $mslice = array_slice($messages, $firstArrayElement, $sliceTo, true); + $messages = $mslice; + $avbox = array(); + $showAvmbox = false; + foreach ($messages as $set_flag) { + $set_flag->flags['skip_mbox_check'] = true; + if ($showMboxColumn === true) { + $set_flag->avmbox = $mailbox; + $avbox[] = 'avmbox'; + $showAvmbox = true; + } + } + $uid_mboxes = $this->rcmail_js_message_list($messages, false, null, $showAvmbox, $avbox, $showMboxColumn); + + return $uid_mboxes; + } + + public function save_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + if ($search_name) { + $search = array(); + $search['search'] = rcube_utils::get_input_value('search', rcube_utils::INPUT_GPC); + $search['search_name'] = $search_name; + $search['folder'] = rcube_utils::get_input_value('folder', rcube_utils::INPUT_GPC); + $search['sub_folders'] = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $prefs['advanced_search'][$search_name] = $search; + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_saved'], 'confirmation'); + } + } + + public function delete_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + if ($search_name) { + $prefs = (array)$this->rc->user->get_prefs(); + unset($prefs['advanced_search'][$search_name]); + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_deleted'], 'notice'); + } + } + + public function get_saved_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + + $search = isset($prefs['advanced_search'][$search_name]) ? $prefs['advanced_search'][$search_name] : false; + $this->rc->output->command('plugin.load_saved_search', $search); + $this->rc->output->send(); + } + + private function get_saved_search_names() + { + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $names = array(); + foreach($prefs['advanced_search'] as $name => $search) { + $names[] = $name; + } + + return $names; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/advanced_search.php.orig Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,1083 @@ +<?php +/** + * Processing an advanced search over an E-Mail Account + * + * @version 2.1.6 + * @licence GNU GPLv3+ + * @author Wilwert Claude + * @author Ludovicy Steve + * @author Chris Moules + * @website http://www.gms.lu + */ + +class advanced_search extends rcube_plugin +{ + + /** + * Instance of rcmail + * + * @var object + * @access private + */ + private $rc; + + public $task = 'mail|settings'; + + /** + * Plugin config + * + * @var array + * @access private + */ + private $config; + + /** + * Localization strings + * + * @var array + * @access private + */ + private $i18n_strings = array(); + + /** + * Initialisation of the plugin + * + * @access public + * @return null + */ + public function init() + { + $this->rc = rcmail::get_instance(); + $this->load_config("config-default.inc.php"); + $this->load_config(); + $this->config = $this->rc->config->get('advanced_search_plugin'); + $this->register_action('plugin.display_advanced_search', array($this, 'display_advanced_search')); + $this->register_action('plugin.trigger_search', array($this, 'trigger_search')); + $this->register_action('plugin.trigger_search_pagination', array($this, 'trigger_search_pagination')); + $this->register_action('plugin.save_search', array($this, 'save_search')); + $this->register_action('plugin.delete_search', array($this, 'delete_search')); + $this->register_action('plugin.get_saved_search', array($this, 'get_saved_search')); + + $this->skin = $this->rc->config->get('skin'); + $this->add_texts('localization', true); + $this->populate_i18n(); + if(isset($this->config['criteria'])) { + foreach($this->config['criteria'] as $key => $translation) { + $this->config['criteria'][$key] = $this->gettext($key); + } + } + $this->include_script('advanced_search.min.js'); + + if ($this->rc->task == 'mail') { + $file = 'skins/' . $this->skin . '/advanced_search.css'; + + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + + if (empty($this->rc->action)) { + $this->add_menu_entry(); + } + } elseif ($this->rc->task == 'settings') { + $this->add_hook('preferences_list', array($this, 'preferences_list')); + $this->add_hook('preferences_save', array($this, 'preferences_save')); + $this->add_hook('preferences_sections_list', array($this, 'preferences_section')); + $file = 'skins/' . $this->skin . '/advanced_search.css'; + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + } + + $this->add_hook('startup', array($this, 'startup')); + } + + + public function startup($args) + { + $search = get_input_value('_search', RCUBE_INPUT_GET); + if (!isset($search)) { + $search = get_input_value('_search', RCUBE_INPUT_POST); + } + $rsearch = $search == 'advanced_search_active'; + $uid = get_input_value('_uid', RCUBE_INPUT_GET); + $draft_uid = get_input_value('_draft_uid', RCUBE_INPUT_GET); + $mbox = get_input_value('_mbox', RCUBE_INPUT_GET); + $page = get_input_value('_page', RCUBE_INPUT_GET); + $sort = get_input_value('_sort', RCUBE_INPUT_GET); + + if (!empty($uid)) { + $parts = explode('__MB__', $uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + if (!empty($draft_uid)) { + $parts = explode('__MB__', $draft_uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + + if ($search == 'advanced_search_active') { + if ($args['action'] == 'show' && !empty($uid)) { + $parts = explode('__MB__', $uid); + $uid = $parts[0]; + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_uid' => $uid)); + } + if ($args['action'] == 'compose') { + $draft_uid = get_input_value('_draft_uid', RCUBE_INPUT_GET); + $parts = explode('__MB__', $draft_uid); + $draft_uid = $parts[0]; + if (!empty($draft_uid)) { + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_draft_uid' => $draft_uid)); + } + } + if ($args['action'] == 'list' && $rsearch) { + $this->rc->output->command('advanced_search_active', '_page=' . $page . '&_sort=' . $sort); + $this->rc->output->send(); + $args['abort'] = true; + } + if ($args['action'] == 'mark') { + $flag = get_input_value('_flag', RCUBE_INPUT_POST); + $uid = get_input_value('_uid', RCUBE_INPUT_POST); + + $post_str = '_flag=' . $flag . '&_uid=' . $uid; + if ($quiet = get_input_value('_quiet', RCUBE_INPUT_POST)) { + $post_str .= '&_quiet=' . $quiet; + } + if ($from = get_input_value('_from', RCUBE_INPUT_POST)) { + $post_str .= '&_from=' . $from; + } + if ($count = get_input_value('_count', RCUBE_INPUT_POST)) { + $post_str .= '&_count=' . $count; + } + if ($ruid = get_input_value('_ruid', RCUBE_INPUT_POST)) { + $post_str .= '&_ruid=' . $ruid; + } + $this->rc->output->command('label_mark', $post_str); + $this->rc->output->send(); + $args['abort'] = true; + } + + } else { + if ($args['action'] != 'plugin.get_saved_search' && $args['action'] != 'plugin.save_search' && $args['action'] != 'plugin.delete_search') { + $this->rc->output->command('plugin.advanced_search_del_header', array()); + } + } + } + + /** + * This function populates an array with localization texts. + * This is needed as ew are using a lot of localizations from core. + * The core localizations are not avalable directly in JS + * + * @access private + * @return null + */ + private function populate_i18n() + { + $core = array('advsearch', 'search', 'resetsearch', 'addfield', 'delete', 'cancel'); + + foreach ($core as $label) { + $this->i18n_strings[$label] = $this->rc->gettext($label); + } + + $local = array('in', 'and', 'or', 'not', 'where', 'exclude', 'andsubfolders', 'allfolders', 'save_the_search', 'has_been_saved', 'deletesearch', 'has_been_deleted'); + + foreach ($local as $label) { + $this->i18n_strings[$label] = $this->gettext($label); + } + } + + /** + * This adds a button into the configured menu to use the advanced search + * + * @access public + * @return null + */ + public function add_menu_entry() + { + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + if ($target_menu != 'toolbar') { + $this->api->add_content( + html::tag( + 'li', + null, + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'label' => 'advsearch', + 'type' => 'link', + 'classact' => 'icon advanced-search active', + 'class' => 'icon advanced-search', + 'innerclass' => 'icon advanced-search' + ) + ) + ), + $target_menu + ); + } else { + $this->api->add_content( + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'title' => 'advsearch', + 'label' => 'search', + 'type' => 'link', + 'classact' => 'button advanced-search active', + 'class' => 'button advanced-search', + 'innerclass' => 'button advanced-search', + ) + ), + $target_menu + ); + } + } + + /** + * This function quotes some specific values based on their data type + * + * @param mixed $input The value to get quoted + * @access public + * @return The quoted value + */ + public function quote($value) + { + if (getType($value) == 'string') { + if (!preg_match('/"/', $value)) { + $value = preg_replace('/^"/', '', $value); + $value = preg_replace('/"$/', '', $value); + $value = preg_replace('/"/', '\\"', $value); + } + + $value = '"' . $value . '"'; + } + + return $value; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access private + * @return string or int + */ + private function process_search_part($search_part) + { + $command_str = ''; + $flag = false; + + // Check for valid input + if (!array_key_exists($search_part['filter'], $this->config['criteria'])) { + $this->rc->output->show_message($this->gettext('internalerror'), 'error'); + + return 0; + } + if (in_array($search_part['filter'], $this->config['flag_criteria'])) { + $flag = true; + } + if (!$flag && !(isset($search_part['filter-val']) && $search_part['filter-val'] != '')) { + return 1; + } + + // Negate part + if ($search_part['not'] == 'true') { + $command_str .= 'NOT '; + } + + $command_str .= $search_part['filter']; + + if (!$flag) { + if (in_array($search_part['filter'], $this->config['date_criteria'])) { + // Take date format from user environment + $date_format = $this->rc->config->get('date_format', 'Y-m-d'); + // Try to use PHP5.2+ DateTime but fallback to ugly old method + if (class_exists('DateTime')) { + $date = DateTime::createFromFormat($date_format, $search_part['filter-val']); + $command_str .= ' ' . $this->quote($date->format("d-M-Y")); + } else { + $date_format = preg_replace('/(\w)/', '%$1', $date_format); + $date_array = strptime($search_part['filter-val'], $date_format); + $unix_ts = mktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'], $date_array['tm_mon']+1, $date_array['tm_mday'], $date_array['tm_year']+1900); + $command_str .= ' ' . $this->quote(date("d-M-Y", $unix_ts)); + } + } elseif (in_array($search_part['filter'], $this->config['email_criteria'])) { + // Strip possible ',' added by autocomplete + $command_str .= ' ' . $this->quote(trim($search_part['filter-val'], " \t,")); + } else { + // Don't try to use a value for a binary flag object + $command_str .= ' ' . $this->quote($search_part['filter-val']); + } + } + + return $command_str; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access public + * @return The final search command + */ + public function get_search_query($input) + { + $command = array(); + + foreach ($input as $search_part) { + // Skip excluded parts + if ($search_part['excluded'] == 'true') { + continue; + } + if (! $part_command = $this->process_search_part($search_part)) { + return 0; + } + // Skip invalid parts + if ($part_command === 1) { + continue; + } + + $command[] = array('method' => isset($search_part['method']) ? $search_part['method'] : 'and', + 'command' => $part_command); + } + + $command_string = $this->build_search_string($command); + + return $command_string; + } + + /** + * This function converts the preconfigured query parts (as array) into an IMAP compatible string + * + * @param array $command_array An array containing the advanced search criteria + * @access public + * @return The command string + */ + private function build_search_string($command_array) + { + $command = array(); + $paranthesis = 0; + $prev_method = null; + $next_method = null; + $cnt = count($command_array); + + foreach ($command_array as $k => $v) { + $part = ''; + $next_method = 'unknown'; + + // Lookup next method + if ($k < $cnt-1) { + $next_method = $command_array[$k+1]['method']; + } + + // If previous option was OR, close any open brakets + if ($paranthesis > 0 && $prev_method == 'or' && $v['method'] != 'or') { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + // If there are two consecutive ORs, add brakets + // If the next option is a new OR, add the prefix here + // If the next option is _not_ an OR, and the current option is AND, prefix ALL + if ($next_method == 'or') { + if ($v['method'] == 'or') { + $part .= ' ('; + $paranthesis++; + } + $part .= 'OR '; + } elseif ($v['method'] == 'and') { + $part .= 'ALL '; + } + + $part .= $v['command']; + + // If this is the end of the query, and we have open brakets, close them + if ($k == $cnt-1 && $paranthesis > 0) { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + $prev_method = $v['method']; + $command[] = $part; + } + + $command = implode(' ', $command); + + return $command; + } + + /** + * This functions sends the initial data to the client side where a form (in dialog) is built for the advanced search + * + * @access public + * @return null + */ + public function display_advanced_search() + { + $ret = array('html' => $this->generate_searchbox(), + 'row' => $this->add_row(), + 'saved_searches' => $this->get_saved_search_names(), + 'title' => $this->i18n_strings['advsearch'], + 'date_criteria' => $this->config['date_criteria'], + 'flag_criteria' => $this->config['flag_criteria'], + 'email_criteria' => $this->config['email_criteria']); + + $this->rc->output->command('plugin.show', $ret); + } + + public function generate_searchbox() + { + $search_button = new html_inputfield(array('type' => 'submit', 'name' => 'search', 'class' => 'button mainaction', 'value' => $this->i18n_strings['search'])); + $reset_button = new html_inputfield(array('type' => 'reset', 'name' => 'reset', 'class' => 'button reset', 'value' => $this->i18n_strings['resetsearch'])); + $save_button = html::tag('input', array('type' => 'submit', 'name' => 'save_the_search', id=> 'save_the_search', 'class' => 'button save_search', 'value' => $this->i18n_strings['save_the_search'])); + $delete_button = new html_inputfield(array('type' => 'submit', 'name' => 'delete', 'style' => 'display: none;', 'class' => 'button delete_search', 'value' => $this->i18n_strings['deletesearch'])); + + $layout_table = new html_table(); + $layout_table->add(null, $search_button->show()); + $folderConfig = array('name' => 'folder'); + $layout_table->add( + null, + $this->i18n_strings['in'] . ': ' . + $this->folder_selector($folderConfig)->show($this->rc->storage->get_folder()) . + html::span( + array('class' => 'sub-folders'), + $this->i18n_strings['andsubfolders'] . ': ' . + html::tag( + 'input', + array('type' => 'checkbox', 'name' => 'subfolder'), + null + ) + ) . + $this->i19n_strings['where'] + ); + $first_row = $this->add_row(true); + $layout_table->add_row(); + $layout_table->add(array('class' => 'adv-search-and-or'), null); + $layout_table->add(null, $first_row); + $layout_table->add_row(); + $layout_table->add(null, $search_button->show()); + $layout_table->add(null, $save_button . ' ' . $reset_button->show() . ' ' . $delete_button->show()); + + return html::tag( + 'div', + array('id' => 'adsearch-popup'), + html::tag( + 'form', + array('method' => 'post', 'action' => '#'), + $layout_table->show() + ) + ); + } + + /** + * This function is used to render the html of the advanced search form and also + * the later following rows are created by this function + * + * @param array $folders Array of folders + * @param boolean $first True if form gets created, False to create a new row + * @access public + * @return string The final html + */ + public function add_row($first = false) + { + $row_html = ''; + $optgroups = ''; + + $criteria = $this->config['criteria']; + $all_criteria = array( + $this->gettext('Common') => $this->config['prefered_criteria'], + $this->gettext('Addresses') => $this->config['email_criteria'], + $this->gettext('Dates') => $this->config['date_criteria'], + $this->gettext('Flags') => $this->config['flag_criteria'], + $this->gettext('Other') => $this->config['other_criteria'], + ); + + foreach ($all_criteria as $label => $specific_criteria) { + $options = ''; + + foreach ($specific_criteria as $value) { + $options .= html::tag('option', array('value' => $value), $criteria[$value]); + } + + $optgroups .= html::tag('optgroup', array('label' => $label), $options); + } + + $tmp = html::tag('select', array('name' => 'filter'), $optgroups); + $tmp .= $this->i18n_strings['not'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'not'), null); + $tmp .= html::tag('input', array('type' => 'text', 'name' => 'filter-val')); + $tmp .= $this->i18n_strings['exclude'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'filter-exclude'), null); + $tmp .= html::tag('button', array('name' => 'add', 'class' => 'add'), $this->i18n_strings['addfield']); + + if ($first) { + $row_html = $tmp; + } else { + $and_or_select = new html_select(array('name' => 'method')); + $and_or_select->add($this->i18n_strings['and'], 'and'); + $and_or_select->add($this->i18n_strings['or'], 'or'); + $tmp .= html::tag('button', array('name' => 'delete', 'class' => 'delete'), $this->i18n_strings['delete']); + $row_html = html::tag( + 'tr', + null, + html::tag( + 'td', + array('class' => 'adv-search-and-or'), + $and_or_select->show() + ) . + html::tag( + 'td', + null, + $tmp + ) + ); + } + + return $row_html; + } + + /** + * Return folders list as html_select object + * + * This is a copy of the core function and adapted to fit + * the needs of the advanced_search function + * + * @param array $p Named parameters + * + * @return html_select HTML drop-down object + */ + public function folder_selector($p = array()) + { + $p += array('maxlength' => 100, 'realnames' => false, 'is_escaped' => true); + $a_mailboxes = array(); + $storage = $this->rc->get_storage(); + + $list = $storage->list_folders_subscribed(); + $delimiter = $storage->get_hierarchy_delimiter(); + + foreach ($list as $folder) { + $this->rc->build_folder_tree($a_mailboxes, $folder, $delimiter); + } + + $select = new html_select($p); + $select->add($this->i18n_strings['allfolders'], "all"); + + $this->rc->render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); + + return $select; + } + + public function trigger_search_pagination($param) + { + $_GET['search'] = $_SESSION['av_search']; + $_GET['folder'] = $_SESSION['av_folder']; + $_GET['sub_folders'] = $_SESSION['av_sub_folders']; + $this->trigger_search(true); + } + + /** + * Here is where the actual query is fired to the imap server and the result is evaluated and sent back to the client side + * + * @access public + * @return null + */ + public function trigger_search($inPagination = false) + { + $search = get_input_value('search', RCUBE_INPUT_GPC); + // reset list_page and old search results + $this->rc->storage->set_page(1); + $this->rc->storage->set_search_set(null); + $page = get_input_value('_page', RCUBE_INPUT_GPC); + $page = $page ? $page : 1; + $pagesize = $this->rc->storage->get_pagesize(); + + if (!empty($search)) { + $mbox = get_input_value('folder', RCUBE_INPUT_GPC); + $imap_charset = RCMAIL_CHARSET; + $sort_column = rcmail_sort_column(); + $search_str = $this->get_search_query($search); + $sub_folders = get_input_value('sub_folders', RCUBE_INPUT_GPC) == 'true'; + $folders = array(); + $result_h = array(); + $count = 0; + $new_id = 1; + $current_mbox = $this->rc->storage->get_folder(); + $uid_list = array(); + //Store information in session for pagination + $_SESSION['av_search'] = $search; + $_SESSION['av_folder'] = $mbox; + $_SESSION['av_sub_folders'] = get_input_value('sub_folders', RCUBE_INPUT_GPC); + $nosub = $sub_folders; + $folders = $this->rc->get_storage()->list_folders_subscribed(); + if (empty($folders) || ($sub_folders === false && $mbox !== 'all')) { + $folders = array($mbox); + } elseif ($mbox !== 'all') { + if ($sub_folders === false) { + $folders = array($mbox); + } else { + $folders = $this->rc->get_storage()->list_folders_subscribed_direct($mbox); + } + } + $md5folders = array(); + foreach ($folders as $folder) { + $md5folders[md5($folder)] = $folder; + } + $this->rc->output->set_env('as_md5_folders', $md5folders); + + if ($search_str) { + $res = $this->perform_search($search_str, $folders, $page); + $count = $res['count']; + } + + + if ($count > 0) { + $_SESSION['advanced_search']['uid_list'] = $uid_list; + if ($search_str && $inPagination == false) { + $this->rc->output->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); + } + } elseif ($err_code = $this->rc->storage->get_error_code()) { + rcmail_display_server_error(); + } else { + $this->rc->output->show_message('searchnomatch', 'notice'); + } + + $current_folder = get_input_value('current_folder', RCUBE_INPUT_GPC); + + $this->rc->output->set_env('search_request', 'advanced_search_active'); + $this->rc->output->set_env('messagecount', $count); + $this->rc->output->set_env('pagecount', ceil($count / $pagesize)); + $this->rc->output->set_env('exists', $this->rc->storage->count($current_folder, 'EXISTS')); + $this->rc->output->command('set_rowcount', rcmail_get_messagecount_text($count, $page)); + $this->rc->output->command('plugin.search_complete'); + $this->rc->output->send(); + } + } + + /** + * return javascript commands to add rows to the message list + */ + public function rcmail_js_message_list($a_headers, $insert_top = false, $a_show_cols = null, $avmbox = false, $avcols = array(), $showMboxColumn = false) + { + global $CONFIG, $RCMAIL, $OUTPUT; + $uid_mboxes = array(); + + if (empty($a_show_cols)) { + if (!empty($_SESSION['list_attrib']['columns'])) { + $a_show_cols = $_SESSION['list_attrib']['columns']; + } else { + $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject'); + } + } else { + if (!is_array($a_show_cols)) { + $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols)); + } + $head_replace = true; + } + + $mbox = $RCMAIL->storage->get_folder(); + + // make sure 'threads' and 'subject' columns are present + if (!in_array('subject', $a_show_cols)) { + array_unshift($a_show_cols, 'subject'); + } + if (!in_array('threads', $a_show_cols)) { + array_unshift($a_show_cols, 'threads'); + } + $_SESSION['list_attrib']['columns'] = $a_show_cols; + + // Make sure there are no duplicated columns (#1486999) + $a_show_cols = array_merge($a_show_cols, $avcols); + $a_show_cols = array_unique($a_show_cols); + + // Plugins may set header's list_cols/list_flags and other rcube_message_header variables + // and list columns + $plugin = $RCMAIL->plugins->exec_hook( + 'messages_list', + array('messages' => $a_headers, 'cols' => $a_show_cols) + ); + $a_show_cols = $plugin['cols']; + $a_headers = $plugin['messages']; + $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : null; + + // get name of smart From/To column in folder context + if (($f = array_search('fromto', $a_show_cols)) !== false) { + $smart_col = rcmail_message_list_smart_column_name(); + } + if ($this->coltypesSet == false) { + $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead, $smart_col); + if ($showMboxColumn === true) { + $OUTPUT->command('plugin.advanced_search_add_header', array()); + } + $this->coltypesSet = true; + } + + if (empty($a_headers)) { + return; + } + + // remove 'threads', 'attachment', 'flag', 'status' columns, we don't need them here + foreach (array('threads', 'attachment', 'flag', 'status', 'priority') as $col) { + if (($key = array_search($col, $a_show_cols)) !== false) { + unset($a_show_cols[$key]); + } + } + + // loop through message headers + foreach ($a_headers as $n => $header) { + if (empty($header)) { + continue; + } + $a_msg_cols = array(); + $a_msg_flags = array(); + // format each col; similar as in rcmail_message_list() + foreach ($a_show_cols as $col) { + $col_name = $col == 'fromto' ? $smart_col : $col; + + if (in_array($col_name, array('from', 'to', 'cc', 'replyto'))) { + $cont = rcmail_address_string($header->$col_name, 3, false, null, $header->charset); + } elseif ($col == 'subject') { + $cont = trim(rcube_mime::decode_header($header->$col, $header->charset)); + if (!$cont) { + $cont = rcube_label('nosubject'); + } + $cont = Q($cont); + } elseif ($col == 'size') { + $cont = show_bytes($header->$col); + } elseif ($col == 'date') { + $cont = format_date($header->date); + } else { + $cont = Q($header->$col); + } + $a_msg_cols[$col] = $cont; + } + + $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags)); + if ($header->depth) { + $a_msg_flags['depth'] = $header->depth; + } elseif ($header->has_children) { + $roots[] = $header->uid; + } + if ($header->parent_uid) { + $a_msg_flags['parent_uid'] = $header->parent_uid; + } + if ($header->has_children) { + $a_msg_flags['has_children'] = $header->has_children; + } + if ($header->unread_children) { + $a_msg_flags['unread_children'] = $header->unread_children; + } + if ($header->others['list-post']) { + $a_msg_flags['ml'] = 1; + } + if ($header->priority) { + $a_msg_flags['prio'] = (int) $header->priority; + } + $a_msg_flags['ctype'] = Q($header->ctype); + $a_msg_flags['mbox'] = $mbox; + if (!empty($header->list_flags) && is_array($header->list_flags)) { + $a_msg_flags = array_merge($a_msg_flags, $header->list_flags); + } + if (!empty($header->list_cols) && is_array($header->list_cols)) { + $a_msg_cols = array_merge($a_msg_cols, $header->list_cols); + } + if ($showMboxColumn === true) { + $a_msg_flags['avmbox'] = $avmbox; + } + + $OUTPUT->command( + 'add_message_row', + $header->uid . '__MB__' . md5($mbox), + $a_msg_cols, + $a_msg_flags, + $insert_top + ); + $id = $header->uid . '__MB__' . md5($mbox); + $uid_mboxes[$id] = array('uid' => $header->uid, 'mbox' => $mbox, 'md5mbox' => md5($mbox)); + } + + if ($RCMAIL->storage->get_threading()) { + $OUTPUT->command('init_threads', (array) $roots, $mbox); + } + + return $uid_mboxes; + } + + + private function do_pagination($folders, $onPage) + { + $perPage = $this->rc->storage->get_pagesize(); + $from = $perPage * $onPage - $perPage + 1; + $to = $from + $perPage - 1; + $got = 0; + $pos = 0; + $cbox = ""; + $boxStart = 0; + $boxStop = 0; + $fetch = array(); + foreach ($folders as $box => $num) { + $i = $num; + if ($box != $cbox) { + $boxStart = 0; + $boxStop = 0; + $cbox = $box; + } + while ($i--) { + $pos++; + $boxStart++; + if ($pos >= $from && $pos <= $to) { + if (!isset($fetch[$box])) { + $fetch[$box] = array("from" => $boxStart); + } + $fetch[$box]['to'] = $boxStart; + $got++; + } + } + if ($got >= $perPage) { + break; + } + } + + return $fetch; + } + + /** + * Save advanced search preferences + * + * @access public + */ + public function preferences_save($args) + { + if ($args['section'] != 'advancedsearch') { + return; + } + $rcmail = rcmail::get_instance(); + + $displayOptions = array(); + $displayOptions['_show_message_label_header'] = get_input_value('_show_message_label_header', RCUBE_INPUT_POST) == 1 ? true : false; + $displayOptions['_show_message_mbox_info'] = get_input_value('_show_message_mbox_info', RCUBE_INPUT_POST) == 1 ? true : false; + $displayOptions['target_menu'] = get_input_value('button_display_option', RCUBE_INPUT_POST); + + $args['prefs']['advanced_search_display_options'] = $displayOptions; + + return($args); + } + + /** + * Add a section advanced search to the preferences section list + * + * @access public + */ + public function preferences_section($args) + { + $args['list']['advancedsearch'] = array( + 'id' => 'advancedsearch', + 'section' => Q($this->gettext('advancedsearch')) + ); + + return($args); + } + + /** + * Display advanced search configuration in user preferences tab + * + * @access public + */ + public function preferences_list($args) + { + if ($args['section'] == 'advancedsearch') { + + $this->rc = rcmail::get_instance(); + $args['blocks']['label_display_options'] = array('options' => array(), 'name' => Q($this->gettext('label_display_options'))); + + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + $options = ''; + $optarg = array('value' => 'messagemenu'); + if ($target_menu == 'messagemenu') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_a.jpg'; + } + $options .= html::tag('option', $optarg, Q($this->gettext('display_in_messagemenu'))); + $optarg = array('value' => 'toolbar'); + if ($target_menu == 'toolbar') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_b.jpg'; + } + $options .= html::tag('option', $optarg, Q($this->gettext('display_in_toolbar'))); + $select = html::tag('select', array('name' => 'button_display_option', 'id' => 'button_display_option'), $options); + + $label1 = html::label('_show_message_label_header', Q($this->gettext('mailbox_headers_in_results'))); + $label2 = html::label('_show_message_mbox_info', Q($this->gettext('mailbox_info_in_results'))); + $label3 = html::label('button_display_option', Q($this->gettext('show_advanced_search'))); + + $arg1 = array('name' => '_show_message_label_header', 'id' => '_show_message_label_header', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $arg1['checked'] = 'checked'; + $img1class = 'enabled'; + } else { + $img1class = 'disabled'; + } + $check1 = html::tag('input', $arg1); + $arg2 = array('name' => '_show_message_mbox_info', 'id' => '_show_message_mbox_info', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] === true) { + $arg2['checked'] = 'checked'; + $img2class = 'enabled'; + } else { + $img2class = 'disabled'; + } + + $img1 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_row.jpg'), 'class' => $img1class)); + $img2 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_col.jpg'), 'class' => $img2class)); + $img3 = html::img(array('src' => $this->url('skins/larry/images/' . $target_image))); + + $check2 = html::tag('input', $arg2); + $args['blocks']['label_display_options']['options'][0] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check1 . ' ' . $label1 . '</span> ' . $img1 . '</p>'); + $args['blocks']['label_display_options']['options'][1] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check2 . ' ' . $label2 . '</span> ' . $img2 . '</p>'); + $args['blocks']['label_display_options']['options'][2] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $label3 . ' ' . $select . '</span> ' . $img3 . '</p>'); + } + + return($args); + } + + private function perform_search($search_string, $folders, $page = 1) + { + // Search all folders and build a final set + if ($folders[0] == 'all' || empty($folders)) { + $folders_search = $this->rc->imap->list_folders_subscribed(); + } else { + $folders_search = $folders; + } + $count = 0; + $folder_count = array(); + foreach ($folders_search as $mbox) { + $this->rc->storage->set_folder($mbox); + $this->rc->storage->search($mbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $result = array(); + $fcount = $this->rc->storage->count($mbox, 'ALL', !empty($_REQUEST['_refresh'])); + $count += $fcount; + $folder_count[$mbox] = $fcount; + } + foreach ($folder_count as $k => $v) { + if ($v == 0) { + unset($folder_count[$k]); + } + } + $fetch = $this->do_pagination($folder_count, $page); + $mails = array(); + $currentMailbox = ""; + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $showMboxColumn = isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] ? true : false; + $uid_mboxes = array(); + foreach ($fetch as $mailbox => $data) { + if ($currentMailbox != $mailbox) { + $currentMailbox = $mailbox; + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $this->rc->output->command('advanced_search_add_mbox', $mailbox, $folder_count[$mailbox], $showMboxColumn); + } + } + $uid_mboxes = array_merge($uid_mboxes, $this->getMails($mailbox, $data, $search_string, $showMboxColumn)); + } + + return array('result' => array(), 'count' => $count, 'uid_mboxes' => $uid_mboxes); + } + + private function getMails($mailbox, $data, $search_string, $showMboxColumn) + { + $pageSize = $this->rc->storage->get_pagesize(); + $msgNum = $data['from']; + $startPage = ceil($msgNum/$pageSize); + $msgMod = $msgNum % $pageSize; + $multiPage = "false"; + $firstArrayElement = $msgMod == 0 ? ($pageSize-1) : ($msgMod-1); + $quantity = $data['to'] - $data['from']; + if ($data['from'] + $quantity > $pageSize) { + $multiPage = "true"; + } + $this->rc->storage->set_folder($mailbox); + $this->rc->storage->search($mailbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $messages = $this->rc->storage->list_messages('', $startPage); + if ($multiPage) { + $messages = array_merge($messages, $this->rc->storage->list_messages('', $startPage+1)); + } + //FIRST: 0 QUANTITY: 2 + $sliceTo = $quantity + 1; + $mslice = array_slice($messages, $firstArrayElement, $sliceTo, true); + $messages = $mslice; + $avbox = array(); + $showAvmbox = false; + foreach ($messages as $set_flag) { + $set_flag->flags['skip_mbox_check'] = true; + if ($showMboxColumn === true) { + $set_flag->avmbox = $mailbox; + $avbox[] = 'avmbox'; + $showAvmbox = true; + } + } + $uid_mboxes = $this->rcmail_js_message_list($messages, false, null, $showAvmbox, $avbox, $showMboxColumn); + + return $uid_mboxes; + } + + public function save_search() + { + $search_name = get_input_value('search_name', RCUBE_INPUT_GPC); + if ($search_name) { + $search = array(); + $search['search'] = get_input_value('search', RCUBE_INPUT_GPC); + $search['search_name'] = $search_name; + $search['folder'] = get_input_value('folder', RCUBE_INPUT_GPC); + $search['sub_folders'] = get_input_value('sub_folders', RCUBE_INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $prefs['advanced_search'][$search_name] = $search; + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_saved'], 'confirmation'); + } + } + + public function delete_search() + { + $search_name = get_input_value('search_name', RCUBE_INPUT_GPC); + if ($search_name) { + $prefs = (array)$this->rc->user->get_prefs(); + unset($prefs['advanced_search'][$search_name]); + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_deleted'], 'notice'); + } + } + + public function get_saved_search() + { + $search_name = get_input_value('search_name', RCUBE_INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + + $search = isset($prefs['advanced_search'][$search_name]) ? $prefs['advanced_search'][$search_name] : false; + $this->rc->output->command('plugin.load_saved_search', $search); + $this->rc->output->send(); + } + + private function get_saved_search_names() + { + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $names = array(); + foreach($prefs['advanced_search'] as $name => $search) { + $names[] = $name; + } + + return $names; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/advanced_search.php~ Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,1088 @@ +<?php +/** + * Processing an advanced search over an E-Mail Account + * + * @version 2.1.5 + * @licence GNU GPLv3+ + * @author Wilwert Claude + * @author Ludovicy Steve + * @author Chris Moules + * @website http://www.gms.lu + * + * Updated by Alex S Grebenschikov (www.poralix.com) + * to make it compatible with RoundCube 1.3.0 + * Last modified: Tue Aug 1 06:41:46 CEST 2017 + * + */ + +class advanced_search extends rcube_plugin +{ + + /** + * Instance of rcmail + * + * @var object + * @access private + */ + private $rc; + + public $task = 'mail|settings'; + + /** + * Plugin config + * + * @var array + * @access private + */ + private $config; + + /** + * Localization strings + * + * @var array + * @access private + */ + private $i18n_strings = array(); + + /** + * Initialisation of the plugin + * + * @access public + * @return null + */ + public function init() + { + $this->rc = rcmail::get_instance(); + $this->load_config("config-default.inc.php"); + $this->load_config(); + $this->config = $this->rc->config->get('advanced_search_plugin'); + $this->register_action('plugin.display_advanced_search', array($this, 'display_advanced_search')); + $this->register_action('plugin.trigger_search', array($this, 'trigger_search')); + $this->register_action('plugin.trigger_search_pagination', array($this, 'trigger_search_pagination')); + $this->register_action('plugin.save_search', array($this, 'save_search')); + $this->register_action('plugin.delete_search', array($this, 'delete_search')); + $this->register_action('plugin.get_saved_search', array($this, 'get_saved_search')); + + $this->skin = $this->rc->config->get('skin'); + $this->add_texts('localization', true); + $this->populate_i18n(); + if(isset($this->config['criteria'])) { + foreach($this->config['criteria'] as $key => $translation) { + $this->config['criteria'][$key] = $this->gettext($key); + } + } + $this->include_script('advanced_search.js'); + + if ($this->rc->task == 'mail') { + $file = 'skins/' . $this->skin . '/advanced_search.css'; + + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + + if (empty($this->rc->action)) { + $this->add_menu_entry(); + } + } elseif ($this->rc->task == 'settings') { + $this->add_hook('preferences_list', array($this, 'preferences_list')); + $this->add_hook('preferences_save', array($this, 'preferences_save')); + $this->add_hook('preferences_sections_list', array($this, 'preferences_section')); + $file = 'skins/' . $this->skin . '/advanced_search.css'; + if (file_exists($this->home . '/' . $file)) { + $this->include_stylesheet($file); + } else { + $this->include_stylesheet('skins/default/advanced_search.css'); + } + } + + $this->add_hook('startup', array($this, 'startup')); + } + + + public function startup($args) + { + $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_GET); + if (!isset($search)) { + $search = rcube_utils::get_input_value('_search', rcube_utils::INPUT_POST); + } + $rsearch = $search == 'advanced_search_active'; + $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GET); + $draft_uid = rcube_utils::get_input_value('_draft_uid', rcube_utils::INPUT_GET); + $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GET); + $page = rcube_utils::get_input_value('_page', rcube_utils::INPUT_GET); + $sort = rcube_utils::get_input_value('_sort', rcube_utils::INPUT_GET); + + if (!empty($uid)) { + $parts = explode('__MB__', $uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + if (!empty($draft_uid)) { + $parts = explode('__MB__', $draft_uid); + if (count($parts) == 2) { + $search = 'advanced_search_active'; + } + } + + if ($search == 'advanced_search_active') { + if ($args['action'] == 'show' && !empty($uid)) { + $parts = explode('__MB__', $uid); + $uid = $parts[0]; + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_uid' => $uid)); + } + if ($args['action'] == 'compose') { + $draft_uid = rcube_utils::get_input_value('_draft_uid', rcube_utils::INPUT_GET); + $parts = explode('__MB__', $draft_uid); + $draft_uid = $parts[0]; + if (!empty($draft_uid)) { + $this->rc->output->redirect(array('_task' => 'mail', '_action' => $args['action'], '_mbox' => $mbox, '_draft_uid' => $draft_uid)); + } + } + if ($args['action'] == 'list' && $rsearch) { + $this->rc->output->command('advanced_search_active', '_page=' . $page . '&_sort=' . $sort); + $this->rc->output->send(); + $args['abort'] = true; + } + if ($args['action'] == 'mark') { + $flag = rcube_utils::get_input_value('_flag', rcube_utils::INPUT_POST); + $uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST); + + $post_str = '_flag=' . $flag . '&_uid=' . $uid; + if ($quiet = rcube_utils::get_input_value('_quiet', rcube_utils::INPUT_POST)) { + $post_str .= '&_quiet=' . $quiet; + } + if ($from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST)) { + $post_str .= '&_from=' . $from; + } + if ($count = rcube_utils::get_input_value('_count', rcube_utils::INPUT_POST)) { + $post_str .= '&_count=' . $count; + } + if ($ruid = rcube_utils::get_input_value('_ruid', rcube_utils::INPUT_POST)) { + $post_str .= '&_ruid=' . $ruid; + } + $this->rc->output->command('label_mark', $post_str); + $this->rc->output->send(); + $args['abort'] = true; + } + + } else { + if ($args['action'] != 'plugin.get_saved_search' && $args['action'] != 'plugin.save_search' && $args['action'] != 'plugin.delete_search') { + $this->rc->output->command('plugin.advanced_search_del_header', array()); + } + } + } + + /** + * This function populates an array with localization texts. + * This is needed as ew are using a lot of localizations from core. + * The core localizations are not avalable directly in JS + * + * @access private + * @return null + */ + private function populate_i18n() + { + $core = array('advsearch', 'search', 'resetsearch', 'addfield', 'delete', 'cancel'); + + foreach ($core as $label) { + $this->i18n_strings[$label] = $this->rc->gettext($label); + } + + $local = array('in', 'and', 'or', 'not', 'where', 'exclude', 'andsubfolders', 'allfolders', 'save_the_search', 'has_been_saved', 'deletesearch', 'has_been_deleted'); + + foreach ($local as $label) { + $this->i18n_strings[$label] = $this->gettext($label); + } + } + + /** + * This adds a button into the configured menu to use the advanced search + * + * @access public + * @return null + */ + public function add_menu_entry() + { + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + if ($target_menu != 'toolbar') { + $this->api->add_content( + html::tag( + 'li', + null, + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'label' => 'advsearch', + 'type' => 'link', + 'classact' => 'icon advanced-search active', + 'class' => 'icon advanced-search', + 'innerclass' => 'icon advanced-search' + ) + ) + ), + $target_menu + ); + } else { + $this->api->add_content( + $this->api->output->button( + array( + 'command' => 'plugin.advanced_search', + 'title' => 'advsearch', + 'label' => 'search', + 'type' => 'link', + 'classact' => 'button advanced-search active', + 'class' => 'button advanced-search', + 'innerclass' => 'button advanced-search', + ) + ), + $target_menu + ); + } + } + + /** + * This function quotes some specific values based on their data type + * + * @param mixed $input The value to get quoted + * @access public + * @return The quoted value + */ + public function quote($value) + { + if (getType($value) == 'string') { + if (!preg_match('/"/', $value)) { + $value = preg_replace('/^"/', '', $value); + $value = preg_replace('/"$/', '', $value); + $value = preg_replace('/"/', '\\"', $value); + } + + $value = '"' . $value . '"'; + } + + return $value; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access private + * @return string or int + */ + private function process_search_part($search_part) + { + $command_str = ''; + $flag = false; + + // Check for valid input + if (!array_key_exists($search_part['filter'], $this->config['criteria'])) { + $this->rc->output->show_message($this->gettext('internalerror'), 'error'); + + return 0; + } + if (in_array($search_part['filter'], $this->config['flag_criteria'])) { + $flag = true; + } + if (!$flag && !(isset($search_part['filter-val']) && $search_part['filter-val'] != '')) { + return 1; + } + + // Negate part + if ($search_part['not'] == 'true') { + $command_str .= 'NOT '; + } + + $command_str .= $search_part['filter']; + + if (!$flag) { + if (in_array($search_part['filter'], $this->config['date_criteria'])) { + // Take date format from user environment + $date_format = $this->rc->config->get('date_format', 'Y-m-d'); + // Try to use PHP5.2+ DateTime but fallback to ugly old method + if (class_exists('DateTime')) { + $date = DateTime::createFromFormat($date_format, $search_part['filter-val']); + $command_str .= ' ' . $this->quote($date->format("d-M-Y")); + } else { + $date_format = preg_replace('/(\w)/', '%$1', $date_format); + $date_array = strptime($search_part['filter-val'], $date_format); + $unix_ts = mktime($date_array['tm_hour'], $date_array['tm_min'], $date_array['tm_sec'], $date_array['tm_mon']+1, $date_array['tm_mday'], $date_array['tm_year']+1900); + $command_str .= ' ' . $this->quote(date("d-M-Y", $unix_ts)); + } + } elseif (in_array($search_part['filter'], $this->config['email_criteria'])) { + // Strip possible ',' added by autocomplete + $command_str .= ' ' . $this->quote(trim($search_part['filter-val'], " \t,")); + } else { + // Don't try to use a value for a binary flag object + $command_str .= ' ' . $this->quote($search_part['filter-val']); + } + } + + return $command_str; + } + + /** + * This function generates the IMAP compatible search query based on the request data (by javascript) + * + * @param array $input The raw criteria data sent by javascript + * @access public + * @return The final search command + */ + public function get_search_query($input) + { + $command = array(); + + foreach ($input as $search_part) { + // Skip excluded parts + if ($search_part['excluded'] == 'true') { + continue; + } + if (! $part_command = $this->process_search_part($search_part)) { + return 0; + } + // Skip invalid parts + if ($part_command === 1) { + continue; + } + + $command[] = array('method' => isset($search_part['method']) ? $search_part['method'] : 'and', + 'command' => $part_command); + } + + $command_string = $this->build_search_string($command); + + return $command_string; + } + + /** + * This function converts the preconfigured query parts (as array) into an IMAP compatible string + * + * @param array $command_array An array containing the advanced search criteria + * @access public + * @return The command string + */ + private function build_search_string($command_array) + { + $command = array(); + $paranthesis = 0; + $prev_method = null; + $next_method = null; + $cnt = count($command_array); + + foreach ($command_array as $k => $v) { + $part = ''; + $next_method = 'unknown'; + + // Lookup next method + if ($k < $cnt-1) { + $next_method = $command_array[$k+1]['method']; + } + + // If previous option was OR, close any open brakets + if ($paranthesis > 0 && $prev_method == 'or' && $v['method'] != 'or') { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + // If there are two consecutive ORs, add brakets + // If the next option is a new OR, add the prefix here + // If the next option is _not_ an OR, and the current option is AND, prefix ALL + if ($next_method == 'or') { + if ($v['method'] == 'or') { + $part .= ' ('; + $paranthesis++; + } + $part .= 'OR '; + } elseif ($v['method'] == 'and') { + $part .= 'ALL '; + } + + $part .= $v['command']; + + // If this is the end of the query, and we have open brakets, close them + if ($k == $cnt-1 && $paranthesis > 0) { + for (; $paranthesis > 0; $paranthesis--) { + $part .= ')'; + } + } + + $prev_method = $v['method']; + $command[] = $part; + } + + $command = implode(' ', $command); + + return $command; + } + + /** + * This functions sends the initial data to the client side where a form (in dialog) is built for the advanced search + * + * @access public + * @return null + */ + public function display_advanced_search() + { + $ret = array('html' => $this->generate_searchbox(), + 'row' => $this->add_row(), + 'saved_searches' => $this->get_saved_search_names(), + 'title' => $this->i18n_strings['advsearch'], + 'date_criteria' => $this->config['date_criteria'], + 'flag_criteria' => $this->config['flag_criteria'], + 'email_criteria' => $this->config['email_criteria']); + + $this->rc->output->command('plugin.show', $ret); + } + + public function generate_searchbox() + { + $search_button = new html_inputfield(array('type' => 'submit', 'name' => 'search', 'class' => 'button mainaction', 'value' => $this->i18n_strings['search'])); + $reset_button = new html_inputfield(array('type' => 'reset', 'name' => 'reset', 'class' => 'button reset', 'value' => $this->i18n_strings['resetsearch'])); + $save_button = html::tag('input', array('type' => 'submit', 'name' => 'save_the_search', id=> 'save_the_search', 'class' => 'button save_search', 'value' => $this->i18n_strings['save_the_search'])); + $delete_button = new html_inputfield(array('type' => 'submit', 'name' => 'delete', 'style' => 'display: none;', 'class' => 'button delete_search', 'value' => $this->i18n_strings['deletesearch'])); + + $layout_table = new html_table(); + $layout_table->add(null, $search_button->show()); + $folderConfig = array('name' => 'folder'); + $layout_table->add( + null, + $this->i18n_strings['in'] . ': ' . + $this->folder_selector($folderConfig)->show($this->rc->storage->get_folder()) . + html::span( + array('class' => 'sub-folders'), + $this->i18n_strings['andsubfolders'] . ': ' . + html::tag( + 'input', + array('type' => 'checkbox', 'name' => 'subfolder'), + null + ) + ) . + $this->i19n_strings['where'] + ); + $first_row = $this->add_row(true); + $layout_table->add_row(); + $layout_table->add(array('class' => 'adv-search-and-or'), null); + $layout_table->add(null, $first_row); + $layout_table->add_row(); + $layout_table->add(null, $search_button->show()); + $layout_table->add(null, $save_button . ' ' . $reset_button->show() . ' ' . $delete_button->show()); + + return html::tag( + 'div', + array('id' => 'adsearch-popup'), + html::tag( + 'form', + array('method' => 'post', 'action' => '#'), + $layout_table->show() + ) + ); + } + + /** + * This function is used to render the html of the advanced search form and also + * the later following rows are created by this function + * + * @param array $folders Array of folders + * @param boolean $first True if form gets created, False to create a new row + * @access public + * @return string The final html + */ + public function add_row($first = false) + { + $row_html = ''; + $optgroups = ''; + + $criteria = $this->config['criteria']; + $all_criteria = array( + $this->gettext('Common') => $this->config['prefered_criteria'], + $this->gettext('Addresses') => $this->config['email_criteria'], + $this->gettext('Dates') => $this->config['date_criteria'], + $this->gettext('Flags') => $this->config['flag_criteria'], + $this->gettext('Other') => $this->config['other_criteria'], + ); + + foreach ($all_criteria as $label => $specific_criteria) { + $options = ''; + + foreach ($specific_criteria as $value) { + $options .= html::tag('option', array('value' => $value), $criteria[$value]); + } + + $optgroups .= html::tag('optgroup', array('label' => $label), $options); + } + + $tmp = html::tag('select', array('name' => 'filter'), $optgroups); + $tmp .= $this->i18n_strings['not'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'not'), null); + $tmp .= html::tag('input', array('type' => 'text', 'name' => 'filter-val')); + $tmp .= $this->i18n_strings['exclude'] . ':' . html::tag('input', array('type' => 'checkbox', 'name' => 'filter-exclude'), null); + $tmp .= html::tag('button', array('name' => 'add', 'class' => 'add'), $this->i18n_strings['addfield']); + + if ($first) { + $row_html = $tmp; + } else { + $and_or_select = new html_select(array('name' => 'method')); + $and_or_select->add($this->i18n_strings['and'], 'and'); + $and_or_select->add($this->i18n_strings['or'], 'or'); + $tmp .= html::tag('button', array('name' => 'delete', 'class' => 'delete'), $this->i18n_strings['delete']); + $row_html = html::tag( + 'tr', + null, + html::tag( + 'td', + array('class' => 'adv-search-and-or'), + $and_or_select->show() + ) . + html::tag( + 'td', + null, + $tmp + ) + ); + } + + return $row_html; + } + + /** + * Return folders list as html_select object + * + * This is a copy of the core function and adapted to fit + * the needs of the advanced_search function + * + * @param array $p Named parameters + * + * @return html_select HTML drop-down object + */ + public function folder_selector($p = array()) + { + $p += array('maxlength' => 100, 'realnames' => false, 'is_escaped' => true); + $a_mailboxes = array(); + $storage = $this->rc->get_storage(); + + $list = $storage->list_folders_subscribed(); + $delimiter = $storage->get_hierarchy_delimiter(); + + foreach ($list as $folder) { + $this->rc->build_folder_tree($a_mailboxes, $folder, $delimiter); + } + + $select = new html_select($p); + $select->add($this->i18n_strings['allfolders'], "all"); + + $this->rc->render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); + + return $select; + } + + public function trigger_search_pagination($param) + { + $_GET['search'] = $_SESSION['av_search']; + $_GET['folder'] = $_SESSION['av_folder']; + $_GET['sub_folders'] = $_SESSION['av_sub_folders']; + $this->trigger_search(true); + } + + /** + * Here is where the actual query is fired to the imap server and the result is evaluated and sent back to the client side + * + * @access public + * @return null + */ + public function trigger_search($inPagination = false) + { + $search = rcube_utils::get_input_value('search', rcube_utils::INPUT_GPC); + // reset list_page and old search results + $this->rc->storage->set_page(1); + $this->rc->storage->set_search_set(null); + $page = rcube_utils::get_input_value('_page', rcube_utils::INPUT_GPC); + $page = $page ? $page : 1; + $pagesize = $this->rc->storage->get_pagesize(); + + if (!empty($search)) { + $mbox = rcube_utils::get_input_value('folder', rcube_utils::INPUT_GPC); + $imap_charset = RCMAIL_CHARSET; + $sort_column = rcmail_sort_column(); + $search_str = $this->get_search_query($search); + $sub_folders = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC) == 'true'; + $folders = array(); + $result_h = array(); + $count = 0; + $new_id = 1; + $current_mbox = $this->rc->storage->get_folder(); + $uid_list = array(); + //Store information in session for pagination + $_SESSION['av_search'] = $search; + $_SESSION['av_folder'] = $mbox; + $_SESSION['av_sub_folders'] = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC); + $nosub = $sub_folders; + $folders = $this->rc->get_storage()->list_folders_subscribed(); + if (empty($folders) || ($sub_folders === false && $mbox !== 'all')) { + $folders = array($mbox); + } elseif ($mbox !== 'all') { + if ($sub_folders === false) { + $folders = array($mbox); + } else { + $folders = $this->rc->get_storage()->list_folders_subscribed_direct($mbox); + } + } + $md5folders = array(); + foreach ($folders as $folder) { + $md5folders[md5($folder)] = $folder; + } + $this->rc->output->set_env('as_md5_folders', $md5folders); + + if ($search_str) { + $res = $this->perform_search($search_str, $folders, $page); + $count = $res['count']; + } + + + if ($count > 0) { + $_SESSION['advanced_search']['uid_list'] = $uid_list; + if ($search_str && $inPagination == false) { + $this->rc->output->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); + } + } elseif ($err_code = $this->rc->storage->get_error_code()) { + rcmail_display_server_error(); + } else { + $this->rc->output->show_message('searchnomatch', 'notice'); + } + + $current_folder = rcube_utils::get_input_value('current_folder', rcube_utils::INPUT_GPC); + + $this->rc->output->set_env('search_request', 'advanced_search_active'); + $this->rc->output->set_env('messagecount', $count); + $this->rc->output->set_env('pagecount', ceil($count / $pagesize)); + $this->rc->output->set_env('exists', $this->rc->storage->count($current_folder, 'EXISTS')); + $this->rc->output->command('set_rowcount', rcmail_get_messagecount_text($count, $page)); + $this->rc->output->command('plugin.search_complete'); + $this->rc->output->send(); + } + } + + /** + * return javascript commands to add rows to the message list + */ + public function rcmail_js_message_list($a_headers, $insert_top = false, $a_show_cols = null, $avmbox = false, $avcols = array(), $showMboxColumn = false) + { + global $CONFIG, $RCMAIL, $OUTPUT; + $uid_mboxes = array(); + + if (empty($a_show_cols)) { + if (!empty($_SESSION['list_attrib']['columns'])) { + $a_show_cols = $_SESSION['list_attrib']['columns']; + } else { + $a_show_cols = is_array($CONFIG['list_cols']) ? $CONFIG['list_cols'] : array('subject'); + } + } else { + if (!is_array($a_show_cols)) { + $a_show_cols = preg_split('/[\s,;]+/', strip_quotes($a_show_cols)); + } + $head_replace = true; + } + + $mbox = $RCMAIL->storage->get_folder(); + + // make sure 'threads' and 'subject' columns are present + if (!in_array('subject', $a_show_cols)) { + array_unshift($a_show_cols, 'subject'); + } + if (!in_array('threads', $a_show_cols)) { + array_unshift($a_show_cols, 'threads'); + } + $_SESSION['list_attrib']['columns'] = $a_show_cols; + + // Make sure there are no duplicated columns (#1486999) + $a_show_cols = array_merge($a_show_cols, $avcols); + $a_show_cols = array_unique($a_show_cols); + + // Plugins may set header's list_cols/list_flags and other rcube_message_header variables + // and list columns + $plugin = $RCMAIL->plugins->exec_hook( + 'messages_list', + array('messages' => $a_headers, 'cols' => $a_show_cols) + ); + $a_show_cols = $plugin['cols']; + $a_headers = $plugin['messages']; + $thead = $head_replace ? rcmail_message_list_head($_SESSION['list_attrib'], $a_show_cols) : null; + + // get name of smart From/To column in folder context + if (($f = array_search('fromto', $a_show_cols)) !== false) { + $smart_col = rcmail_message_list_smart_column_name(); + } + if ($this->coltypesSet == false) { + $OUTPUT->command('set_message_coltypes', $a_show_cols, $thead, $smart_col); + if ($showMboxColumn === true) { + $OUTPUT->command('plugin.advanced_search_add_header', array()); + } + $this->coltypesSet = true; + } + + if (empty($a_headers)) { + return; + } + + // remove 'threads', 'attachment', 'flag', 'status' columns, we don't need them here + foreach (array('threads', 'attachment', 'flag', 'status', 'priority') as $col) { + if (($key = array_search($col, $a_show_cols)) !== false) { + unset($a_show_cols[$key]); + } + } + + // loop through message headers + foreach ($a_headers as $n => $header) { + if (empty($header)) { + continue; + } + $a_msg_cols = array(); + $a_msg_flags = array(); + // format each col; similar as in rcmail_message_list() + foreach ($a_show_cols as $col) { + $col_name = $col == 'fromto' ? $smart_col : $col; + + if (in_array($col_name, array('from', 'to', 'cc', 'replyto'))) { + $cont = rcmail_address_string($header->$col_name, 3, false, null, $header->charset); + } elseif ($col == 'subject') { + $cont = trim(rcube_mime::decode_header($header->$col, $header->charset)); + if (!$cont) { + $cont = rcube_label('nosubject'); + } + $cont = rcube::Q($cont); + } elseif ($col == 'size') { + $cont = $this->rc->show_bytes($header->$col); + } elseif ($col == 'date') { + $cont = rcube_utils::format_datestr($header->date,$date_format); + } else { + $cont = rcube::Q($header->$col); + } + $a_msg_cols[$col] = $cont; + } + + $a_msg_flags = array_change_key_case(array_map('intval', (array) $header->flags)); + if ($header->depth) { + $a_msg_flags['depth'] = $header->depth; + } elseif ($header->has_children) { + $roots[] = $header->uid; + } + if ($header->parent_uid) { + $a_msg_flags['parent_uid'] = $header->parent_uid; + } + if ($header->has_children) { + $a_msg_flags['has_children'] = $header->has_children; + } + if ($header->unread_children) { + $a_msg_flags['unread_children'] = $header->unread_children; + } + if ($header->others['list-post']) { + $a_msg_flags['ml'] = 1; + } + if ($header->priority) { + $a_msg_flags['prio'] = (int) $header->priority; + } + $a_msg_flags['ctype'] = rcube::Q($header->ctype); + $a_msg_flags['mbox'] = $mbox; + if (!empty($header->list_flags) && is_array($header->list_flags)) { + $a_msg_flags = array_merge($a_msg_flags, $header->list_flags); + } + if (!empty($header->list_cols) && is_array($header->list_cols)) { + $a_msg_cols = array_merge($a_msg_cols, $header->list_cols); + } + if ($showMboxColumn === true) { + $a_msg_flags['avmbox'] = $avmbox; + } + + $OUTPUT->command( + 'add_message_row', + $header->uid . '__MB__' . md5($mbox), + $a_msg_cols, + $a_msg_flags, + $insert_top + ); + $id = $header->uid . '__MB__' . md5($mbox); + $uid_mboxes[$id] = array('uid' => $header->uid, 'mbox' => $mbox, 'md5mbox' => md5($mbox)); + } + + if ($RCMAIL->storage->get_threading()) { + $OUTPUT->command('init_threads', (array) $roots, $mbox); + } + + return $uid_mboxes; + } + + + private function do_pagination($folders, $onPage) + { + $perPage = $this->rc->storage->get_pagesize(); + $from = $perPage * $onPage - $perPage + 1; + $to = $from + $perPage - 1; + $got = 0; + $pos = 0; + $cbox = ""; + $boxStart = 0; + $boxStop = 0; + $fetch = array(); + foreach ($folders as $box => $num) { + $i = $num; + if ($box != $cbox) { + $boxStart = 0; + $boxStop = 0; + $cbox = $box; + } + while ($i--) { + $pos++; + $boxStart++; + if ($pos >= $from && $pos <= $to) { + if (!isset($fetch[$box])) { + $fetch[$box] = array("from" => $boxStart); + } + $fetch[$box]['to'] = $boxStart; + $got++; + } + } + if ($got >= $perPage) { + break; + } + } + + return $fetch; + } + + /** + * Save advanced search preferences + * + * @access public + */ + public function preferences_save($args) + { + if ($args['section'] != 'advancedsearch') { + return; + } + $rcmail = rcmail::get_instance(); + + $displayOptions = array(); + $displayOptions['_show_message_label_header'] = rcube_utils::get_input_value('_show_message_label_header', rcube_utils::INPUT_POST) == 1 ? true : false; + $displayOptions['_show_message_mbox_info'] = rcube_utils::get_input_value('_show_message_mbox_info', rcube_utils::INPUT_POST) == 1 ? true : false; + $displayOptions['target_menu'] = rcube_utils::get_input_value('button_display_option', rcube_utils::INPUT_POST); + + $args['prefs']['advanced_search_display_options'] = $displayOptions; + + return($args); + } + + /** + * Add a section advanced search to the preferences section list + * + * @access public + */ + public function preferences_section($args) + { + $args['list']['advancedsearch'] = array( + 'id' => 'advancedsearch', + 'section' => rcube::Q($this->gettext('advancedsearch')) + ); + + return($args); + } + + /** + * Display advanced search configuration in user preferences tab + * + * @access public + */ + public function preferences_list($args) + { + if ($args['section'] == 'advancedsearch') { + + $this->rc = rcmail::get_instance(); + $args['blocks']['label_display_options'] = array('options' => array(), 'name' => rcube::Q($this->gettext('label_display_options'))); + + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $target_menu = (isset($displayOptions['target_menu']) && !empty($displayOptions['target_menu'])) ? $displayOptions['target_menu'] : $this->config['target_menu']; + $options = ''; + $optarg = array('value' => 'messagemenu'); + if ($target_menu == 'messagemenu') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_a.jpg'; + } + $options .= html::tag('option', $optarg, rcube::Q($this->gettext('display_in_messagemenu'))); + $optarg = array('value' => 'toolbar'); + if ($target_menu == 'toolbar') { + $optarg['selected'] = 'selected'; + $target_image = 'menu_location_b.jpg'; + } + $options .= html::tag('option', $optarg,rcube::Q($this->gettext('display_in_toolbar'))); + $select = html::tag('select', array('name' => 'button_display_option', 'id' => 'button_display_option'), $options); + + $label1 = html::label('_show_message_label_header', rcube::Q($this->gettext('mailbox_headers_in_results'))); + $label2 = html::label('_show_message_mbox_info', rcube::Q($this->gettext('mailbox_info_in_results'))); + $label3 = html::label('button_display_option', rcube::Q($this->gettext('show_advanced_search'))); + + $arg1 = array('name' => '_show_message_label_header', 'id' => '_show_message_label_header', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $arg1['checked'] = 'checked'; + $img1class = 'enabled'; + } else { + $img1class = 'disabled'; + } + $check1 = html::tag('input', $arg1); + $arg2 = array('name' => '_show_message_mbox_info', 'id' => '_show_message_mbox_info', 'type' => 'checkbox', 'title' => "", 'class' => 'watermark linput', 'value' => 1); + if (isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] === true) { + $arg2['checked'] = 'checked'; + $img2class = 'enabled'; + } else { + $img2class = 'disabled'; + } + + $img1 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_row.jpg'), 'class' => $img1class)); + $img2 = html::img(array('src' => $this->url('skins/larry/images/show_mbox_col.jpg'), 'class' => $img2class)); + $img3 = html::img(array('src' => $this->url('skins/larry/images/' . $target_image))); + + $check2 = html::tag('input', $arg2); + $args['blocks']['label_display_options']['options'][0] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check1 . ' ' . $label1 . '</span> ' . $img1 . '</p>'); + $args['blocks']['label_display_options']['options'][1] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $check2 . ' ' . $label2 . '</span> ' . $img2 . '</p>'); + $args['blocks']['label_display_options']['options'][2] = array('title' => '', 'content' => '<p class="avsearchpref"><span>' . $label3 . ' ' . $select . '</span> ' . $img3 . '</p>'); + } + + return($args); + } + + private function perform_search($search_string, $folders, $page = 1) + { + // Search all folders and build a final set + if ($folders[0] == 'all' || empty($folders)) { + $folders_search = $this->rc->imap->list_folders_subscribed(); + } else { + $folders_search = $folders; + } + $count = 0; + $folder_count = array(); + foreach ($folders_search as $mbox) { + $this->rc->storage->set_folder($mbox); + $this->rc->storage->search($mbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $result = array(); + $fcount = $this->rc->storage->count($mbox, 'ALL', !empty($_REQUEST['_refresh'])); + $count += $fcount; + $folder_count[$mbox] = $fcount; + } + foreach ($folder_count as $k => $v) { + if ($v == 0) { + unset($folder_count[$k]); + } + } + $fetch = $this->do_pagination($folder_count, $page); + $mails = array(); + $currentMailbox = ""; + $displayOptions = $this->rc->config->get('advanced_search_display_options', array()); + $showMboxColumn = isset($displayOptions['_show_message_mbox_info']) && $displayOptions['_show_message_mbox_info'] ? true : false; + $uid_mboxes = array(); + foreach ($fetch as $mailbox => $data) { + if ($currentMailbox != $mailbox) { + $currentMailbox = $mailbox; + if (isset($displayOptions['_show_message_label_header']) && $displayOptions['_show_message_label_header'] === true) { + $this->rc->output->command('advanced_search_add_mbox', $mailbox, $folder_count[$mailbox], $showMboxColumn); + } + } + $uid_mboxes = array_merge($uid_mboxes, $this->getMails($mailbox, $data, $search_string, $showMboxColumn)); + } + + return array('result' => array(), 'count' => $count, 'uid_mboxes' => $uid_mboxes); + } + + private function getMails($mailbox, $data, $search_string, $showMboxColumn) + { + $pageSize = $this->rc->storage->get_pagesize(); + $msgNum = $data['from']; + $startPage = ceil($msgNum/$pageSize); + $msgMod = $msgNum % $pageSize; + $multiPage = "false"; + $firstArrayElement = $msgMod == 0 ? ($pageSize-1) : ($msgMod-1); + $quantity = $data['to'] - $data['from']; + if ($data['from'] + $quantity > $pageSize) { + $multiPage = "true"; + } + $this->rc->storage->set_folder($mailbox); + $this->rc->storage->search($mailbox, $search_string, RCMAIL_CHARSET, $_SESSION['sort_col']); + $messages = $this->rc->storage->list_messages('', $startPage); + if ($multiPage) { + $messages = array_merge($messages, $this->rc->storage->list_messages('', $startPage+1)); + } + //FIRST: 0 QUANTITY: 2 + $sliceTo = $quantity + 1; + $mslice = array_slice($messages, $firstArrayElement, $sliceTo, true); + $messages = $mslice; + $avbox = array(); + $showAvmbox = false; + foreach ($messages as $set_flag) { + $set_flag->flags['skip_mbox_check'] = true; + if ($showMboxColumn === true) { + $set_flag->avmbox = $mailbox; + $avbox[] = 'avmbox'; + $showAvmbox = true; + } + } + $uid_mboxes = $this->rcmail_js_message_list($messages, false, null, $showAvmbox, $avbox, $showMboxColumn); + + return $uid_mboxes; + } + + public function save_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + if ($search_name) { + $search = array(); + $search['search'] = rcube_utils::get_input_value('search', rcube_utils::INPUT_GPC); + $search['search_name'] = $search_name; + $search['folder'] = rcube_utils::get_input_value('folder', rcube_utils::INPUT_GPC); + $search['sub_folders'] = rcube_utils::get_input_value('sub_folders', rcube_utils::INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $prefs['advanced_search'][$search_name] = $search; + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_saved'], 'confirmation'); + } + } + + public function delete_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + if ($search_name) { + $prefs = (array)$this->rc->user->get_prefs(); + unset($prefs['advanced_search'][$search_name]); + $this->rc->user->save_prefs(array('advanced_search' => $prefs['advanced_search'])); + $this->rc->output->show_message('"<i>' . $search_name . '</i>" ' . $this->i18n_strings['has_been_deleted'], 'notice'); + } + } + + public function get_saved_search() + { + $search_name = rcube_utils::get_input_value('search_name', rcube_utils::INPUT_GPC); + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + + $search = isset($prefs['advanced_search'][$search_name]) ? $prefs['advanced_search'][$search_name] : false; + $this->rc->output->command('plugin.load_saved_search', $search); + $this->rc->output->send(); + } + + private function get_saved_search_names() + { + $prefs = (array)$this->rc->user->get_prefs(); + if (!isset($prefs['advanced_search'])) { + $prefs['advanced_search'] = array(); + } + $names = array(); + foreach($prefs['advanced_search'] as $name => $search) { + $names[] = $name; + } + + return $names; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/composer.json Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,23 @@ +{ + "name": "gms-sa/advanced-search", + "type": "roundcube-plugin", + "description": "Advanced search allowing multiple search criteria and folder recursion", + "keywords": ["mail","search","advanced"], + "homepage": "http://github.com/GMS-SA/roundcube-advanced-search/tree/stable", + "license": "GPL-3.0", + "repositories": [ + { + "type": "composer", + "url": "http://plugins.roundcube.net" + } + ], + "require": { + "roundcube/plugin-installer": ">=0.1.2" + }, + "extra": { + "roundcube": { + "min-version": "0.9.4", + "max-version": "1.0.9999" + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/config-default.inc.php Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,45 @@ +<?php + +$rcmail_config['advanced_search_plugin'] = array( + // The menu where to place the advanced search button by default + 'target_menu' => 'messagemenu', + + // Every criteria which takes a email as argument + 'email_criteria' => array('HEADER FROM', 'HEADER TO', 'CC', 'BCC'), + + // Every criteria which takes a date as argument + 'date_criteria' => array('BEFORE', 'ON', 'SINCE', 'SENTBEFORE', 'SENTON', 'SENTSINCE'), + + // Every criteria which doesn't take an argument + 'flag_criteria' => array('ANSWERED', 'DELETED', 'DRAFT', 'FLAGGED', 'SEEN'), + + // Prefered criteria to show on the top of lists + 'prefered_criteria' => array('SUBJECT', 'BODY', 'HEADER FROM', 'HEADER TO', 'SENTSINCE', 'LARGER'), + + // Other criteria, anything not in the above lists, except 'prefered_criteria' + 'other_criteria' => array('SUBJECT', 'BODY', 'KEYWORD', 'LARGER', 'SMALLER'), + + // All filter criteria + 'criteria' => array( + 'ANSWERED' => 'Answered', + 'BCC' => 'Bcc', + 'BEFORE' => 'Before', + 'CC' => 'Cc', + 'DELETED' => 'Deleted', + 'DRAFT' => 'Draft', + 'FLAGGED' => 'Flagged', + 'KEYWORD' => 'Keyword', + 'LARGER' => 'Larger Than', + 'BODY' => 'Message Body', + 'ON' => 'On', + 'SEEN' => 'Read', + 'SENTBEFORE' => 'Sent Before', + 'HEADER FROM' => 'From', + 'SENTON' => 'Sent On', + 'SENTSINCE' => 'Sent Since', + 'HEADER TO' => 'To', + 'SINCE' => 'Since', + 'SMALLER' => 'Smaller Than', + 'SUBJECT' => 'Subject Contains' + ) +);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/de_DE.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,51 @@ +<?php + $labels = array(); + $labels['in'] = 'In'; + $labels['and'] = 'Und'; + $labels['or'] = 'Oder'; + $labels['not'] = 'Nicht'; + $labels['where'] = 'Wo'; + $labels['exclude'] = 'ausschließen'; + $labels['andsubfolders'] = 'mit Unterordnern'; + $labels['allfolders'] = 'Alle Ordner'; + $labels['advancedsearch'] = 'Erweiterte Suche'; + $labels['label_display_options'] = 'Anzeigeoptionen'; + $labels['mailbox_headers_in_results'] = 'Ordnername als Reihe anzeigen'; + $labels['mailbox_info_in_results'] = 'Ordnername als Kolonne anzeigen'; + $labels['display_in_messagemenu'] = 'im Nachrichtenmenü anzeigen'; + $labels['display_in_toolbar'] = 'als Button in der Toolbar anzeigen'; + $labels['show_advanced_search'] = 'Erweiterte Suche'; + $labels['saved_searches'] = 'Gespeicherte Suche'; + $labels['save_the_search'] = 'Diese Suche speichern'; + $labels['has_been_saved'] = 'wurde gespeichert'; + $labels['has_been_deleted'] = 'wurde gelöscht'; + $labels['cancel'] = 'Abbrechen'; + $labels['delete'] = 'Löschen'; + $labels['Common'] = 'Häufig genutzt'; + $labels['Addresses'] = 'Adressen'; + $labels['Dates'] = 'Daten'; + $labels['Flags'] = 'Markierungen'; + $labels['Other'] = 'Andere'; + $labels['ANSWERED'] = 'Beantwortet'; + $labels['BCC'] = 'Bcc'; + $labels['BEFORE'] = 'Vor'; + $labels['CC'] = 'Cc'; + $labels['DELETED'] = 'Gelöscht'; + $labels['DRAFT'] = 'Entwurf'; + $labels['FLAGGED'] = 'Markiert'; + $labels['KEYWORD'] = 'Schlüsselwort'; + $labels['LARGER'] = 'Größer als'; + $labels['BODY'] = 'Nachrichtentext'; + $labels['ON'] = 'Am'; + $labels['SEEN'] = 'Gelesen'; + $labels['SENTBEFORE'] = 'Gesendet vor'; + $labels['HEADER FROM'] = 'Von'; + $labels['SENTON'] = 'Gesendet am'; + $labels['SENTSINCE'] = 'Gesendet seit'; + $labels['HEADER TO'] = 'An'; + $labels['SINCE'] = 'Seit'; + $labels['SMALLER'] = 'Kleiner als'; + $labels['SUBJECT'] = 'Betreff enthält'; + $labels['name'] = 'Name'; + $labels['save'] = 'Speichern'; + $labels['mbox'] = 'Ordner'; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/en_US.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,52 @@ +<?php + $labels = array(); + $labels['in'] = 'In'; + $labels['and'] = 'And'; + $labels['or'] = 'Or'; + $labels['not'] = 'Not'; + $labels['where'] = 'Where'; + $labels['exclude'] = 'Exclude'; + $labels['andsubfolders'] = 'and subfolders'; + $labels['allfolders'] = 'All Folders'; + $labels['advancedsearch'] = 'Advanced search'; + $labels['label_display_options'] = 'Display options in search results'; + $labels['mailbox_headers_in_results'] = 'Show mailbox title as row'; + $labels['mailbox_info_in_results'] = 'Show mailbox title as column'; + $labels['display_in_messagemenu'] = 'in message menu'; + $labels['display_in_toolbar'] = 'as button in toolbar'; + $labels['show_advanced_search'] = 'Show advanced search'; + $labels['saved_searches'] = 'Saved searches'; + $labels['save_the_search'] = 'Save this search'; + $labels['has_been_saved'] = 'has been saved'; + $labels['deletesearch'] = 'Delete search'; + $labels['has_been_deleted'] = 'has been deleted'; + $labels['cancel'] = 'Cancel'; + $labels['delete'] = 'Delete'; + $labels['Common'] = 'Common'; + $labels['Addresses'] = 'Addresses'; + $labels['Dates'] = 'Dates'; + $labels['Flags'] = 'Flags'; + $labels['Other'] = 'Other'; + $labels['ANSWERED'] = 'Answered'; + $labels['BCC'] = 'Bcc'; + $labels['BEFORE'] = 'Before'; + $labels['CC'] = 'Cc'; + $labels['DELETED'] = 'Deleted'; + $labels['DRAFT'] = 'Draft'; + $labels['FLAGGED'] = 'Flagged'; + $labels['KEYWORD'] = 'Keyword'; + $labels['LARGER'] = 'Larger Than'; + $labels['BODY'] = 'Message Body'; + $labels['ON'] = 'On'; + $labels['SEEN'] = 'Read'; + $labels['SENTBEFORE'] = 'Sent Before'; + $labels['HEADER FROM'] = 'From'; + $labels['SENTON'] = 'Sent On'; + $labels['SENTSINCE'] = 'Sent Since'; + $labels['HEADER TO'] = 'To'; + $labels['SINCE'] = 'Since'; + $labels['SMALLER'] = 'Smaller Than'; + $labels['SUBJECT'] = 'Subject Contains'; + $labels['name'] = 'Name'; + $labels['save'] = 'Save'; + $labels['mbox'] = 'Folder'; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/fr_FR.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,51 @@ +<?php + $labels = array(); + $labels['in'] = 'Dans'; + $labels['and'] = 'Et'; + $labels['or'] = 'Ou'; + $labels['not'] = 'Pas'; + $labels['where'] = 'Ou'; + $labels['exclude'] = 'Exclure'; + $labels['andsubfolders'] = 'et sous-dossiers'; + $labels['allfolders'] = 'Tous les dossiers'; + $labels['advancedsearch'] = 'Recherche avancée'; + $labels['label_display_options'] = 'Options d\'affichage'; + $labels['mailbox_headers_in_results'] = 'Afficher le nom du dossier en tant que ligne'; + $labels['mailbox_info_in_results'] = 'Afficher le nom du dossier en tant que colonne'; + $labels['display_in_messagemenu'] = 'dans le menu des messages'; + $labels['display_in_toolbar'] = 'en tant que bouton dans la Toolbar'; + $labels['show_advanced_search'] = 'Afficher la recherche avancée'; + $labels['saved_searches'] = 'Recherches sauvegardées'; + $labels['save_the_search'] = 'Sauvegarder cette recherche'; + $labels['has_been_saved'] = 'a été sauvegardée'; + $labels['has_been_deleted'] = 'viens d\'être effacé'; + $labels['cancel'] = 'Annuler'; + $labels['delete'] = 'Supprimer'; + $labels['Common'] = 'Populaire'; + $labels['Addresses'] = 'Adresses'; + $labels['Dates'] = 'Dates'; + $labels['Flags'] = 'Marqueurs'; + $labels['Other'] = 'Autres'; + $labels['ANSWERED'] = 'Répondu'; + $labels['BCC'] = 'Copie cachée à'; + $labels['BEFORE'] = 'Datant d\'avant le'; + $labels['CC'] = 'Copie à'; + $labels['DELETED'] = 'Supprimé'; + $labels['DRAFT'] = 'Brouillon'; + $labels['FLAGGED'] = 'Marqué'; + $labels['KEYWORD'] = 'Mot-clé'; + $labels['LARGER'] = 'Plus gros que'; + $labels['BODY'] = 'Corps du message'; + $labels['ON'] = 'Datant du'; + $labels['SEEN'] = 'Lu'; + $labels['SENTBEFORE'] = 'Envoyé avant le'; + $labels['HEADER FROM'] = 'Envoyé par'; + $labels['SENTON'] = 'Envoyé le'; + $labels['SENTSINCE'] = 'Envoyé après le'; + $labels['HEADER TO'] = 'À'; + $labels['SINCE'] = 'Datant d\'après le'; + $labels['SMALLER'] = 'Plus petit que'; + $labels['SUBJECT'] = 'Objet contenant'; + $labels['name'] = 'Nom'; + $labels['save'] = 'Sauvegarder'; + $labels['mbox'] = 'Dossier'; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/it_IT.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,53 @@ +<?php + $labels = array(); + $labels['in'] = 'In'; + $labels['and'] = 'E'; + $labels['or'] = 'O'; + $labels['not'] = 'Non'; + $labels['where'] = 'Dove'; + $labels['exclude'] = 'Escludi'; + $labels['andsubfolders'] = 'e sottocartelle'; + $labels['allfolders'] = 'Tutte le Cartelle'; + $labels['advancedsearch'] = 'Ricerca Avanzata'; + $labels['label_display_options'] = 'Mostra le opzioni nei risultati'; + $labels['mailbox_headers_in_results'] = 'Mostra il titolo della mailbox come riga'; + $labels['mailbox_info_in_results'] = 'Mostra il titolo della mailbox come colonna'; + $labels['display_in_messagemenu'] = 'nel menu del messaggio'; + $labels['display_in_toolbar'] = 'come bottone nella toolbar'; + $labels['show_advanced_search'] = 'Mostra la ricerca avanzata'; + $labels['saved_searches'] = 'Ricerche salvate'; + $labels['save_the_search'] = 'Salva questa ricerca'; + $labels['has_been_saved'] = 'è stata salvata'; + $labels['deletesearch'] = 'Cancella la ricerca'; + $labels['has_been_deleted'] = 'è stata cancellata'; + $labels['cancel'] = 'Annulla'; + $labels['delete'] = 'Cancella'; + $labels['Common'] = 'Comuni'; + $labels['Addresses'] = 'Indirizzi'; + $labels['Dates'] = 'Date'; + $labels['Flags'] = 'Etichette'; + $labels['Other'] = 'Altro'; + $labels['ANSWERED'] = 'Risposto'; + $labels['BCC'] = 'Bcc'; + $labels['BEFORE'] = 'Prima'; + $labels['CC'] = 'Cc'; + $labels['DELETED'] = 'Cancellata'; + $labels['DRAFT'] = 'Bozza'; + $labels['FLAGGED'] = 'Etichettata'; + $labels['KEYWORD'] = 'Parola chiave'; + $labels['LARGER'] = 'Più grande di'; + $labels['BODY'] = 'Corpo del Messaggio'; + $labels['ON'] = 'Il'; + $labels['SEEN'] = 'Letta'; + $labels['SENTBEFORE'] = 'Inviata Prima'; + $labels['HEADER FROM'] = 'Da'; + $labels['SENTON'] = 'Inviata Il'; + $labels['SENTSINCE'] = 'Inviata Dal'; + $labels['HEADER TO'] = 'A'; + $labels['SINCE'] = 'Dal'; + $labels['SMALLER'] = 'Più piccola di'; + $labels['SUBJECT'] = 'Soggetto Contiene'; + $labels['name'] = 'Nome'; + $labels['save'] = 'Salva'; + $labels['mbox'] = 'Cartella'; +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/pl_PL.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,52 @@ +<?php +$labels = array(); +$labels['in'] = 'w'; +$labels['and'] = 'i'; +$labels['or'] = 'lub'; +$labels['not'] = 'nie'; +$labels['where'] = 'gdzie'; +$labels['exclude'] = 'wyklucz'; +$labels['andsubfolders'] = 'i podfolderach'; +$labels['allfolders'] = 'Wszystkie foldery'; +$labels['advancedsearch'] = 'Wyszukiwanie zaawansowane'; +$labels['label_display_options'] = 'Opcje w zaawansowanych wynikach wyszukiwania'; +$labels['mailbox_headers_in_results'] = 'Wyświetl nazwę folderu w rzędzie'; +$labels['mailbox_info_in_results'] = 'Wyświetl nazwę folderu w kolumnie'; +$labels['display_in_messagemenu'] = 'w menu wiadomości'; +$labels['display_in_toolbar'] = 'jako przycisk'; +$labels['show_advanced_search'] = 'Pokaż zaawansowane wyszukiwanie'; +$labels['saved_searches'] = 'Zachowane wyszukiwania'; +$labels['save_the_search'] = 'Zachowaj to wyszukiwanie'; +$labels['has_been_saved'] = 'zostało zachowane'; +$labels['deletesearch'] = 'Usuń wyszukiwanie'; +$labels['has_been_deleted'] = 'zostało usunięte'; +$labels['cancel'] = 'Anuluj'; +$labels['delete'] = 'Usuń'; +$labels['Common'] = 'Zwykłe'; +$labels['Addresses'] = 'Adresy'; +$labels['Dates'] = 'Daty'; +$labels['Flags'] = 'Flagi'; +$labels['Other'] = 'Inne'; +$labels['ANSWERED'] = 'Odpowiedziano'; +$labels['BCC'] = 'Bcc'; +$labels['BEFORE'] = 'Przed datą'; +$labels['CC'] = 'Cc'; +$labels['DELETED'] = 'Skasowana'; +$labels['DRAFT'] = 'Kopia robocza'; +$labels['FLAGGED'] = 'Oznaczona'; +$labels['KEYWORD'] = 'Słowo kluczowe'; +$labels['LARGER'] = 'Większe od'; +$labels['BODY'] = 'Treść wiadomości'; +$labels['ON'] = 'W dacie'; +$labels['SEEN'] = 'Przeczytana'; +$labels['SENTBEFORE'] = 'Wysłany przed datą'; +$labels['HEADER FROM'] = 'Od'; +$labels['SENTON'] = 'Wysłany w dacie'; +$labels['SENTSINCE'] = 'Wysłane od daty'; +$labels['HEADER TO'] = 'Do'; +$labels['SINCE'] = 'Od daty'; +$labels['SMALLER'] = 'Mniejsze od'; +$labels['SUBJECT'] = 'Temat zawiera'; +$labels['name'] = 'Nazwa'; +$labels['save'] = 'Zachowaj'; +$labels['mbox'] = 'Folder'; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/localization/ru_RU.inc Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,52 @@ +<?php + $labels = array(); + $labels['in'] = 'В'; + $labels['and'] = 'И'; + $labels['or'] = 'Или'; + $labels['not'] = 'Не'; + $labels['where'] = 'Где'; + $labels['exclude'] = 'Исключить'; + $labels['andsubfolders'] = 'и подпапки'; + $labels['allfolders'] = 'Все папки'; + $labels['advancedsearch'] = 'Расширенный поиск'; + $labels['label_display_options'] = 'Отображение резултатов поиска'; + $labels['mailbox_headers_in_results'] = 'Название папки в строке'; + $labels['mailbox_info_in_results'] = 'Название папки в колонке'; + $labels['display_in_messagemenu'] = 'В меню сообщений'; + $labels['display_in_toolbar'] = 'Кнопка на панели'; + $labels['show_advanced_search'] = 'Показывать расширенный поиск'; + $labels['saved_searches'] = 'Сохраненные'; + $labels['save_the_search'] = 'Сохранить этот поиск'; + $labels['has_been_saved'] = 'был сохранен'; + $labels['deletesearch'] = 'Удалить поиск'; + $labels['has_been_deleted'] = 'был удален'; + $labels['cancel'] = 'Отменить'; + $labels['delete'] = 'Удалить'; + $labels['Common'] = 'Общее'; + $labels['Addresses'] = 'Адреса'; + $labels['Dates'] = 'Дата'; + $labels['Flags'] = 'Метки'; + $labels['Other'] = 'Другое'; + $labels['ANSWERED'] = 'С ответом'; + $labels['BCC'] = 'Скрытая копия'; + $labels['BEFORE'] = 'Получено до'; + $labels['CC'] = 'Копия'; + $labels['DELETED'] = 'Удаленные'; + $labels['DRAFT'] = 'Черновики'; + $labels['FLAGGED'] = 'Помеченные'; + $labels['KEYWORD'] = 'Метка'; + $labels['LARGER'] = 'Размер больше'; + $labels['BODY'] = 'Тело письма'; + $labels['ON'] = 'Дата получения'; + $labels['SEEN'] = 'Прочитанные'; + $labels['SENTBEFORE'] = 'Отправлено до'; + $labels['HEADER FROM'] = 'От'; + $labels['SENTON'] = 'Дата отправки'; + $labels['SENTSINCE'] = 'Отправлено после'; + $labels['HEADER TO'] = 'Кому'; + $labels['SINCE'] = 'Получено после'; + $labels['SMALLER'] = 'Размер меньше'; + $labels['SUBJECT'] = 'Тема'; + $labels['name'] = 'Название'; + $labels['save'] = 'Сохранить'; + $labels['mbox'] = 'Ящик'; \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/skins/classic/advanced_search.css Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,34 @@ +#messagetoolbar > a.advanced-search span.advanced-search { + display: none; +} + +.advanced_search_dialog .ui-dialog-title { + width: 100%; +} + +.advanced_search_dialog .ui-dialog-title span.saved_searches { + border: 1px solid lightgray; + float: right; + display: block; + padding-right: 10px; + padding-left: 10px; + padding-bottom: 3px; + padding-top: 3px; + border-radius: 4px; + margin-right: 20px; +} + +#rcavbox1, #rcavbox2 { + width: 30%; +} + +#messagetoolbar a.advanced-search { + background: url(../../../../skins/classic/images/abook_toolbar.png) 0 0 no-repeat transparent; + background-position: -168px 0px; +} + +#messagemenu a.advanced-search { + color: #a0a0a0; + text-decoration: none; + background: url(../../../../skins/classic/images/icons/glass.png) no-repeat 7px 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/advanced_search/skins/larry/advanced_search.css Sun May 27 16:53:56 2018 -0400 @@ -0,0 +1,218 @@ +a.icon.advanced-search span.advanced-search { + background-image: url(images/icon-loupe.png) !important; + background-position: 0 !important; +} + +a.button.advanced-search { + text-align: center; + font-size: 10px; + color: #555; + min-width: 50px; + max-width: 75px; + height: 13px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + padding: 28px 2px 0 2px; + text-shadow: 0px 1px 1px #eee; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; + background: url(../../../../skins/larry/images/buttons.png?v=abf1.36693) -100px 0 no-repeat transparent; + border: 0; + border-radius: 0; + background-position: center -970px; +} + +.records-table tbody .aslabel_mbox td { + border-top: 1px solid #fff; + border-bottom: 1px solid #bbd3da; + cursor: default; + font-weight: normal; + background-color: #d9ecf4; + color: #376572; + text-shadow: 0px 1px 1px #fff; + text-decoration: none; + font-weight: bold; +} + +.records-table tbody .aslabel_mbox .aslabel_found { + top: 3px; + right: 6px; + min-width: 1.8em; + padding: 2px 4px; + background: #82acb5; + background: -moz-linear-gradient(top, #82acb5 0%, #6a939f 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#82acb5), color-stop(100%,#6a939f)); + background: -o-linear-gradient(top, #82acb5 0%, #6a939f 100%); + background: -ms-linear-gradient(top, #82acb5 0%, #6a939f 100%); + background: linear-gradient(top, #82acb5 0%, #6a939f 100%); + box-shadow: inset 0 1px 1px 0 #536d72; + -o-box-shadow: inset 0 1px 1px 0 #536d72; + -webkit-box-shadow: inset 0 1px 1px 0 #536d72; + -moz-box-shadow: inset 0 1px 1px 0 #536d72; + border-radius: 9px; + color: #fff; + text-align: center; + font-weight: bold; + text-shadow: none; + display: block; +} + +#adsearch-popup button.add, #adsearch-popup button.delete{ + display: inline-block; + margin: 0 2px; + padding: 2px 5px; + color: #525252; + text-shadow: 0px 1px 1px #fff; + border: 1px solid #c0c0c0; + border-radius: 4px; + background: #f7f7f7; + background: -moz-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f9f9f9), color-stop(100%,#e6e6e6)); + background: -o-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); + background: -ms-linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); + background: linear-gradient(top, #f9f9f9 0%, #e6e6e6 100%); + box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3); + -o-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3); + -webkit-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3); + -moz-box-shadow: 0 1px 1px 0 rgba(140, 140, 140, 0.3); + text-decoration: none; + outline: none; +} + +#adv-search tbody tr { + padding: 4px 10px; + background: #eee; + border-bottom: 2px solid #fff; +} + +#adv-search, #adv-search tr { + width: 100%; +} + +td.adv-search-and-or { + width: 80px; + text-align: center; +} + +#adsearch-popup .mainaction { + height: 24px; + padding-left: 10px; + padding-right: 10px; + margin-right: 10px; +} + +#rcavbox1, #rcavbox2 { + width: 30%; +} + +#rcmrowadvancedsearch td.section { + background-position: 6px -1650px !important; +} + +#rcmrowadvancedsearch.selected td.section { + background-position: 6px -1674px !important; +} + +p.avsearchpref span { + min-width: 280px; + display: block; + max-width: 280px; + float: left; + vertical-align: middle; +} + +p.avsearchpref img { + border-radius: 10px; +} + +p.avsearchpref img.disabled { + -webkit-filter: grayscale(100%); + -moz-filter: grayscale(100%); + filter: grayscale(100%); +} + +button.save_search { + color: #ffffff; + background-color: #2f96b4; + border-radius: 4px; + height: 20px; + padding-left: 10px; + padding-right: 10px; + margin-right: 10px; +} + +input.save_search { + padding: 0 10px 0 10px; + height: 20px; + background: #20b8fb; + border: none; + font-weight: bold; + color: #fff; + text-shadow: none; + background: #6193DF; + background: -moz-linear-gradient(top, #6193DF 0%, #2B84C0 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6193DF), color-stop(100%,#2B84C0)); + background: -o-linear-gradient(top, #6193DF 0%, #2B84C0 100%); + background: -ms-linear-gradient(top, #6193DF 0%, #2B84C0 100%); + background: linear-gradient(top, #6193DF 0%, #2B84C0 100%); +} + +input.save_search:active { + background: #81C0EB; + background: -moz-linear-gradient(top, #6193DF 0%, #81C0EB 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#6193DF), color-stop(100%,#81C0EB)); + background: -o-linear-gradient(top, #6193DF 0%, #81C0EB 100%); + background: -ms-linear-gradient(top, #6193DF 0%, #81C0EB 100%); + background: linear-gradient(top, #6193DF 0%, #81C0EB 100%); +} + +input.delete_search { + padding: 0 10px 0 10px; + height: 19px; + background: #20b8fb; + border: none; + font-weight: bold; + color: #fff; + text-shadow: none; + background: #85190A; + background: -moz-linear-gradient(top, #85190A 0%, #CA361A 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#85190A), color-stop(100%,#CA361A)); + background: -o-linear-gradient(top, #85190A 0%, #CA361A 100%); + background: -ms-linear-gradient(top, #85190A 0%, #CA361A 100%); + background: linear-gradient(top, #85190A 0%, #CA361A 100%); +} + +input.delete_search:active { + background: #85190A; + background: -moz-linear-gradient(top, #85190A 0%, #F09F98 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#85190A), color-stop(100%,#F09F98)); + background: -o-linear-gradient(top, #85190A 0%, #F09F98 100%); + background: -ms-linear-gradient(top, #85190A 0%, #F09F98 100%); + background: linear-gradient(top, #85190A 0%, #F09F98 100%); +} + + +.advanced_search_dialog .ui-dialog-title { + width: 100%; +} + +.advanced_search_dialog .ui-dialog-title span.saved_searches { + border: 1px solid lightgray; + float: right; + display: block; + padding-right: 10px; + -moz-box-shadow: inset 0 0 5px #888; + -webkit-box-shadow: inset 0 0 5px#888; + box-shadow: inner 0 0 5px #888; + padding-left: 10px; + padding-bottom: 3px; + padding-top: 3px; + border-radius: 4px; +} + +#adsearch-popup input[name="filter-val"] { + margin-right: 4px; +}