diff options
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | config/main.inc.php.dist | 3 | ||||
-rw-r--r-- | program/include/rcube_contacts.php | 13 | ||||
-rw-r--r-- | program/include/rcube_ldap.php | 15 | ||||
-rw-r--r-- | program/js/app.js | 26 | ||||
-rw-r--r-- | program/localization/en_US/labels.inc | 1 | ||||
-rw-r--r-- | program/localization/pl_PL/labels.inc | 1 | ||||
-rw-r--r-- | program/steps/addressbook/func.inc | 6 | ||||
-rw-r--r-- | program/steps/addressbook/search.inc | 31 | ||||
-rw-r--r-- | skins/default/common.css | 16 | ||||
-rw-r--r-- | skins/default/functions.js | 70 | ||||
-rw-r--r-- | skins/default/mail.css | 15 | ||||
-rw-r--r-- | skins/default/templates/addressbook.html | 16 |
13 files changed, 149 insertions, 65 deletions
@@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add popup with basic fields selection for addressbook search - Case-insensitive matching in autocompletion (#1487933) - Added option to force spellchecking before sending a message (#1485458) - Fix handling of "<" character in contact data, search fields and folder names (#1487864) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 447575fce..bcbd98c95 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -674,6 +674,9 @@ $rcmail_config['force_7bit'] = false; // Please note that folder names should to be in sync with $rcmail_config['default_imap_folders'] $rcmail_config['search_mods'] = null; // Example: array('*' => array('subject'=>1, 'from'=>1), 'Sent' => array('subject'=>1, 'to'=>1)); +// Defaults of the addressbook search field configuration. +$rcmail_config['addressbook_search_mods'] = null; // Example: array('name'=>1, 'firstname'=>1, 'surname'=>1, 'email'=>1, '*'=>1); + // 'Delete always' // This setting reflects if mail should be always deleted // when moving to Trash fails. This is necessary in some setups diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php index 687b4b111..7c142f545 100644 --- a/program/include/rcube_contacts.php +++ b/program/include/rcube_contacts.php @@ -192,7 +192,7 @@ class rcube_contacts extends rcube_addressbook // determine whether we have to parse the vcard or if only db cols are requested $read_vcard = !$cols || count(array_intersect($cols, $this->table_cols)) < count($cols); - + while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) { $sql_arr['ID'] = $sql_arr[$this->primary_key]; @@ -255,17 +255,18 @@ class rcube_contacts extends rcube_addressbook $ids = $this->db->array2list($ids, 'integer'); $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; } - else if ($strict) { - $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($value); - } else if ($col == '*') { $words = array(); foreach(explode(" ", self::normalize_string($value)) as $word) $words[] = $this->db->ilike('words', '%'.$word.'%'); $where[] = '(' . join(' AND ', $words) . ')'; - } - else + } + else if ($strict) { + $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($value); + } + else if (in_array($col, $this->table_cols)) { $where[] = $this->db->ilike($col, '%'.$value.'%'); + } } foreach ($required as $col) { diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index f166fd23a..7f0ea84fa 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -479,21 +479,20 @@ class rcube_ldap extends rcube_addressbook $filter = '(|'; $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : ''; - if ($fields != '*') + if ($fields == '*') { // search_fields are required for fulltext search - if (!$this->prop['search_fields']) + if (empty($this->prop['search_fields'])) { $this->set_error(self::ERROR_SEARCH, 'nofulltextsearch'); $this->result = new rcube_result_set(); return $this->result; } - } - - if (is_array($this->prop['search_fields'])) - { - foreach ($this->prop['search_fields'] as $k => $field) - $filter .= "($field=$wc" . $this->_quote_string($value) . "$wc)"; + if (is_array($this->prop['search_fields'])) + { + foreach ($this->prop['search_fields'] as $k => $field) + $filter .= "($field=$wc" . $this->_quote_string($value) . "$wc)"; + } } else { diff --git a/program/js/app.js b/program/js/app.js index 32d7a6332..db79772a1 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -3332,31 +3332,33 @@ function rcube_webmail() this.qsearch = function(value) { if (value != '') { - var addurl = ''; + var n, addurl = '', mods_arr = [], + mods = this.env.search_mods, + mbox = this.env.mailbox, + lock = this.set_busy(true, 'searching'); + if (this.message_list) { this.clear_message_list(); - if (this.env.search_mods) { - var mods = this.env.search_mods[this.env.mailbox] ? this.env.search_mods[this.env.mailbox] : this.env.search_mods['*']; - if (mods) { - var head_arr = []; - for (var n in mods) - head_arr.push(n); - addurl += '&_headers='+head_arr.join(','); - } - } + if (mods) + mods = mods[mbox] ? mods[mbox] : mods['*']; } else if (this.contact_list) { this.contact_list.clear(true); this.show_contentframe(false); } + if (mods) { + for (n in mods) + mods_arr.push(n); + addurl += '&_headers='+mods_arr.join(','); + } + if (this.gui_objects.search_filter) addurl += '&_filter=' + this.gui_objects.search_filter.value; // reset vars this.env.current_page = 1; - var lock = this.set_busy(true, 'searching'); this.http_request('search', '_q='+urlencode(value) - + (this.env.mailbox ? '&_mbox='+urlencode(this.env.mailbox) : '') + + (mbox ? '&_mbox='+urlencode(mbox) : '') + (this.env.source ? '&_source='+urlencode(this.env.source) : '') + (this.env.group ? '&_gid='+urlencode(this.env.group) : '') + (addurl ? addurl : ''), lock); diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 45da21cb4..c04531cf5 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -271,6 +271,7 @@ $labels['female'] = 'female'; $labels['manager'] = 'Manager'; $labels['assistant'] = 'Assistant'; $labels['spouse'] = 'Spouse'; +$labels['allfields'] = 'All fields'; $labels['typehome'] = 'Home'; $labels['typework'] = 'Work'; diff --git a/program/localization/pl_PL/labels.inc b/program/localization/pl_PL/labels.inc index 637768e4d..8c4e910cd 100644 --- a/program/localization/pl_PL/labels.inc +++ b/program/localization/pl_PL/labels.inc @@ -408,5 +408,6 @@ $labels['otherfolder'] = 'Folder innego użytkownika'; $labels['sharedfolder'] = 'Folder współdzielony'; $labels['defaultaddressbook'] = 'Nowe kontakty dodawaj do wybranej książki adresowej'; $labels['spellcheckbeforesend'] = 'Przed wysłaniem wiadomości sprawdzaj pisownię'; +$labels['allfields'] = 'Wszystkie pola'; ?> diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 8b4a9f10f..545f140bf 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -19,6 +19,8 @@ */ +$SEARCH_MODS_DEFAULT = array('name'=>1, 'firstname'=>1, 'surname'=>1, 'email'=>1, '*'=>1); + // add list of address sources to client env $js_list = $RCMAIL->get_address_sources(); @@ -39,7 +41,7 @@ if (!empty($_GET['_page'])) $CONTACTS->set_page(($_SESSION['page'] = intval($_GET['_page']))); else $CONTACTS->set_page(isset($_SESSION['page']) ?$_SESSION['page'] : 1); - + if (!empty($_REQUEST['_gid'])) $CONTACTS->set_group(get_input_value('_gid', RCUBE_INPUT_GPC)); @@ -51,6 +53,8 @@ if (!empty($_REQUEST['_search']) && isset($_SESSION['search'][$_REQUEST['_search $OUTPUT->set_env('source', $source ? $source : '0'); $OUTPUT->set_env('readonly', $CONTACTS->readonly, false); if (!$OUTPUT->ajax_call) { + $search_mods = $RCMAIL->config->get('addressbook_search_mods', $SEARCH_MODS_DEFAULT); + $OUTPUT->set_env('search_mods', $search_mods); $OUTPUT->set_env('address_sources', $js_list); $OUTPUT->set_pagetitle(rcube_label('addressbook')); } diff --git a/program/steps/addressbook/search.inc b/program/steps/addressbook/search.inc index 8d25a8fbc..fe7099fac 100644 --- a/program/steps/addressbook/search.inc +++ b/program/steps/addressbook/search.inc @@ -22,22 +22,35 @@ $CONTACTS->set_page(1); $_SESSION['page'] = 1; +// get input $search = trim(get_input_value('_q', RCUBE_INPUT_GET, true)); -$search_request = md5('addr'.$search); +$fields = explode(',', get_input_value('_headers', RCUBE_INPUT_GET)); + +if (empty($fields)) { + $fields = $SEARCH_MODS_DEFAULT; +} + +$search_request = md5('addr'.$search.implode($fields, ',')); + +// update search_mods setting +$search_mods = array_fill_keys($fields, 1); +$RCMAIL->user->save_prefs(array('addressbook_search_mods' => $search_mods)); + +if ($fields['all'] || count($fields) == count($SEARCH_MODS_DEFAULT)) { + $fields = '*'; +} // get contacts for this user -$result = $CONTACTS->search('*', $search); +$result = $CONTACTS->search($fields, $search); // save search settings in session $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); -if ($result->count > 0) -{ - // create javascript list - rcmail_js_contacts_list($result); +if ($result->count > 0) { + // create javascript list + rcmail_js_contacts_list($result); } -else -{ +else { $OUTPUT->show_message('nocontactsfound', 'notice'); } @@ -45,6 +58,6 @@ else $OUTPUT->set_env('search_request', $search_request); $OUTPUT->set_env('pagecount', ceil($result->count / $CONTACTS->page_size)); $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text()); - + // send response $OUTPUT->send(); diff --git a/skins/default/common.css b/skins/default/common.css index 675a80cc0..a76220b53 100644 --- a/skins/default/common.css +++ b/skins/default/common.css @@ -617,7 +617,6 @@ table.records-table tr.unfocused td border: none; } - /***** roundcube webmail pre-defined classes *****/ #rcmversion @@ -815,6 +814,21 @@ ul.toolbarmenu li.separator_above padding-top: 2px; } +#searchmenu +{ + width: 160px; +} + +#searchmenu ul.toolbarmenu +{ + margin: 0; +} + +#searchmenu ul.toolbarmenu li +{ + margin: 1px 4px 1px; +} + /***** tabbed interface elements *****/ diff --git a/skins/default/functions.js b/skins/default/functions.js index 9057d97c4..603c703b2 100644 --- a/skins/default/functions.js +++ b/skins/default/functions.js @@ -186,9 +186,25 @@ searchmenu: function(show) .find(':checked').prop('checked', false); if (rcmail.env.search_mods) { - var search_mods = rcmail.env.search_mods[rcmail.env.mailbox] ? rcmail.env.search_mods[rcmail.env.mailbox] : rcmail.env.search_mods['*']; - for (var n in search_mods) - $('#s_mod_' + n).prop('checked', true); + var n, mbox = rcmail.env.mailbox, mods = rcmail.env.search_mods; + + if (rcmail.env.task != 'addressbook') { + mods = mods[mbox] ? mods[mbox] : mods['*']; + + for (n in mods) + $('#s_mod_' + n).prop('checked', true); + } + else { + if (mods['*']) + $('input:checkbox[name="s_mods[]"]').map(function() { + this.checked = true; + this.disabled = this.value != '*'; + }); + else { + for (n in mods) + $('#s_mod_' + n).prop('checked', true); + } + } } } obj[show?'show':'hide'](); @@ -196,16 +212,46 @@ searchmenu: function(show) set_searchmod: function(elem) { - if (!rcmail.env.search_mods) - rcmail.env.search_mods = {}; - - if (!rcmail.env.search_mods[rcmail.env.mailbox]) - rcmail.env.search_mods[rcmail.env.mailbox] = rcube_clone_object(rcmail.env.search_mods['*']); + var task = rcmail.env.task, + mods = rcmail.env.search_mods, + mbox = rcmail.env.mailbox; + + if (!mods) + mods = {}; + + if (task == 'mail') { + if (!mods[mbox]) + mods[mbox] = rcube_clone_object(mods['*']); + if (!elem.checked) + delete(mods[mbox][elem.value]); + else + mods[mbox][elem.value] = 1; + } + else { //addressbook + if (!elem.checked) + delete(mods[elem.value]); + else + mods[elem.value] = 1; + + // mark all fields + if (elem.value == '*') { + $('input:checkbox[name="s_mods[]"]').map(function() { + if (this == elem) + return; + + if (elem.checked) { + mods[this.value] = 1; + this.checked = true; + this.disabled = true; + } + else { + this.disabled = false; + } + }); + } + } - if (!elem.checked) - delete(rcmail.env.search_mods[rcmail.env.mailbox][elem.value]); - else - rcmail.env.search_mods[rcmail.env.mailbox][elem.value] = elem.value; + rcmail.env.search_mods = mods; }, listmenu: function(show) diff --git a/skins/default/mail.css b/skins/default/mail.css index a96dd48d5..a2400ec0c 100644 --- a/skins/default/mail.css +++ b/skins/default/mail.css @@ -154,21 +154,6 @@ padding-left: 2px; } -#searchmenu -{ - width: 160px; -} - -#searchmenu ul.toolbarmenu -{ - margin: 0; -} - -#searchmenu ul.toolbarmenu li -{ - margin: 1px 4px 1px; -} - #messagemenu li a.active:hover, #markmessagemenu li a.active:hover { diff --git a/skins/default/templates/addressbook.html b/skins/default/templates/addressbook.html index 668b309a7..97cd13c2f 100644 --- a/skins/default/templates/addressbook.html +++ b/skins/default/templates/addressbook.html @@ -29,11 +29,25 @@ </div> <div id="quicksearchbar"> -<roundcube:button name="searchmenulink" id="searchmenulink" image="/images/icons/glass.png" /> +<roundcube:button name="searchmenulink" id="searchmenulink" image="/images/icons/glass_roll.png" onclick="rcmail_ui.show_popup('searchmenu');return false" title="searchmod" /> <roundcube:object name="searchform" id="quicksearchbox" /> <roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" /> </div> +<div id="searchmenu" class="popupmenu"> + <ul class="toolbarmenu"> + <li><input type="checkbox" name="s_mods[]" value="name" id="s_mod_name" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_name"><roundcube:label name="name" /></label></li> + <li><input type="checkbox" name="s_mods[]" value="firstname" id="s_mod_firstname" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_firstname"><roundcube:label name="firstname" /></label></li> + <li><input type="checkbox" name="s_mods[]" value="surname" id="s_mod_surname" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_surname"><roundcube:label name="surname" /></label></li> + <li><input type="checkbox" name="s_mods[]" value="email" id="s_mod_email" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_email"><roundcube:label name="email" /></label></li> + <li><input type="checkbox" name="s_mods[]" value="*" id="s_mod_all" onclick="rcmail_ui.set_searchmod(this)" /><label for="s_mod_all"><roundcube:label name="allfields" /></label></li> +<!-- + <li class="separator_below"> + <li><roundcube:button command="advsearch" type="link" label="advsearch" style="padding-left: 0" classAct="active" /></li> +--> + </ul> +</div> + <div id="directorylistbox"> <div id="directorylist-title" class="boxtitle"><roundcube:label name="groups" /></div> <div class="boxlistcontent"> |