Support contact+group searches in all relevant places (T1360)

Before the search worked only in Addressbook, not also in Compose.
The point of the change is also to align group searches with contact searches
in that it now uses the same set of attributes. Previously groups
in Compose were searched by name only.

Conflicts:

	program/lib/Roundcube/rcube_addressbook.php
	program/lib/Roundcube/rcube_contacts.php
	program/steps/mail/search_contacts.inc
pull/6833/head
Aleksander Machniak 8 years ago
parent 3f10f9a2e6
commit 93cb7b1fea

@ -25,14 +25,20 @@
*/ */
abstract class rcube_addressbook abstract class rcube_addressbook
{ {
/** constants for error reporting **/ // constants for error reporting
const ERROR_READ_ONLY = 1; const ERROR_READ_ONLY = 1;
const ERROR_NO_CONNECTION = 2; const ERROR_NO_CONNECTION = 2;
const ERROR_VALIDATE = 3; const ERROR_VALIDATE = 3;
const ERROR_SAVING = 4; const ERROR_SAVING = 4;
const ERROR_SEARCH = 5; const ERROR_SEARCH = 5;
/** public properties (mandatory) */ // search modes
const SEARCH_ALL = 0;
const SEARCH_STRICT = 1;
const SEARCH_PREFIX = 2;
const SEARCH_GROUPS = 4;
// public properties (mandatory)
public $primary_key; public $primary_key;
public $groups = false; public $groups = false;
public $export_groups = true; public $export_groups = true;
@ -98,13 +104,11 @@ abstract class rcube_addressbook
* *
* @param array List of fields to search in * @param array List of fields to search in
* @param string Search value * @param string Search value
* @param int Matching mode: * @param int Search mode. Sum of self::SEARCH_*.
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* @param boolean True if results are requested, False if count only * @param boolean True if results are requested, False if count only
* @param boolean True to skip the count query (select only) * @param boolean True to skip the count query (select only)
* @param array List of fields that cannot be empty * @param array List of fields that cannot be empty
*
* @return object rcube_result_set List of contact records and 'count' value * @return object rcube_result_set List of contact records and 'count' value
*/ */
abstract function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array()); abstract function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array());
@ -330,10 +334,7 @@ abstract class rcube_addressbook
* List all active contact groups of this source * List all active contact groups of this source
* *
* @param string Optional search string to match group name * @param string Optional search string to match group name
* @param int Matching mode: * @param int Search mode. Sum of self::SEARCH_*
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* *
* @return array Indexed list of contact groups, each a hash array * @return array Indexed list of contact groups, each a hash array
*/ */
@ -665,16 +666,14 @@ abstract class rcube_addressbook
// composite field, e.g. address // composite field, e.g. address
foreach ((array)$value as $val) { foreach ((array)$value as $val) {
$val = mb_strtolower($val); $val = mb_strtolower($val);
switch ($mode) {
case 1:
$got = ($val == $search);
break;
case 2: if ($mode & self::SEARCH_STRICT) {
$got = ($val == $search);
}
else if ($mode & self::SEARCH_PREFIX) {
$got = ($search == substr($val, 0, strlen($search))); $got = ($search == substr($val, 0, strlen($search)));
break; }
else {
default:
$got = (strpos($val, $search) !== false); $got = (strpos($val, $search) !== false);
} }

@ -136,11 +136,8 @@ class rcube_contacts extends rcube_addressbook
/** /**
* List all active contact groups of this source * List all active contact groups of this source
* *
* @param string Search string to match group name * @param string $search Search string to match group name
* @param int Matching mode: * @param int $mode Matching mode. Sum of rcube_addressbook::SEARCH_*
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* *
* @return array Indexed list of contact groups, each a hash array * @return array Indexed list of contact groups, each a hash array
*/ */
@ -148,18 +145,18 @@ class rcube_contacts extends rcube_addressbook
{ {
$results = array(); $results = array();
if (!$this->groups) if (!$this->groups) {
return $results; return $results;
}
if ($search) { if ($search) {
switch (intval($mode)) { if ($mode & rcube_addressbook::SEARCH_STRICT) {
case 1:
$sql_filter = $this->db->ilike('name', $search); $sql_filter = $this->db->ilike('name', $search);
break; }
case 2: else if ($mode & rcube_addressbook::SEARCH_PREFIX) {
$sql_filter = $this->db->ilike('name', $search . '%'); $sql_filter = $this->db->ilike('name', $search . '%');
break; }
default: else {
$sql_filter = $this->db->ilike('name', '%' . $search . '%'); $sql_filter = $this->db->ilike('name', '%' . $search . '%');
} }
@ -290,10 +287,7 @@ class rcube_contacts extends rcube_addressbook
* *
* @param mixed $fields The field name of array of field names to search in * @param mixed $fields The field name of array of field names to search in
* @param mixed $value Search value (or array of values when $fields is array) * @param mixed $value Search value (or array of values when $fields is array)
* @param int $mode Matching mode: * @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* @param boolean $select True if results are requested, False if count only * @param boolean $select True if results are requested, False if count only
* @param boolean $nocount True to skip the count query (select only) * @param boolean $nocount True to skip the count query (select only)
* @param array $required List of fields that cannot be empty * @param array $required List of fields that cannot be empty

@ -719,10 +719,7 @@ class rcube_ldap extends rcube_addressbook
* *
* @param mixed $fields The field name of array of field names to search in * @param mixed $fields The field name of array of field names to search in
* @param mixed $value Search value (or array of values when $fields is array) * @param mixed $value Search value (or array of values when $fields is array)
* @param int $mode Matching mode: * @param int $mode Matching mode. Sum of rcube_addressbook::SEARCH_*
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* @param boolean $select True if results are requested, False if count only * @param boolean $select True if results are requested, False if count only
* @param boolean $nocount (Not used) * @param boolean $nocount (Not used)
* @param array $required List of fields that cannot be empty * @param array $required List of fields that cannot be empty
@ -753,7 +750,7 @@ class rcube_ldap extends rcube_addressbook
if ($this->prop['vlv_search'] && $this->ready && join(',', (array)$fields) == join(',', $list_fields)) { if ($this->prop['vlv_search'] && $this->ready && join(',', (array)$fields) == join(',', $list_fields)) {
$this->result = new rcube_result_set(0); $this->result = new rcube_result_set(0);
$this->ldap->config_set('fuzzy_search', intval($this->prop['fuzzy_search'] && $mode != 1)); $this->ldap->config_set('fuzzy_search', intval($this->prop['fuzzy_search'] && !($mode & rcube_addressbook::SEARCH_STRICT)));
$ldap_data = $this->ldap->search($this->base_dn, $this->prop['filter'], $this->prop['scope'], $this->prop['attributes'], $ldap_data = $this->ldap->search($this->base_dn, $this->prop['filter'], $this->prop['scope'], $this->prop['attributes'],
array('search' => $value /*, 'sort' => $this->prop['sort'] */)); array('search' => $value /*, 'sort' => $this->prop['sort'] */));
if ($ldap_data === false) { if ($ldap_data === false) {
@ -785,9 +782,9 @@ class rcube_ldap extends rcube_addressbook
// set wildcards // set wildcards
$wp = $ws = ''; $wp = $ws = '';
if (!empty($this->prop['fuzzy_search']) && $mode != 1) { if (!empty($this->prop['fuzzy_search']) && !($mode & rcube_addressbook::SEARCH_STRICT)) {
$ws = '*'; $ws = '*';
if (!$mode) { if (!($mode & rcube_addressbook::SEARCH_PREFIX)) {
$wp = '*'; $wp = '*';
} }
} }
@ -853,10 +850,12 @@ class rcube_ldap extends rcube_addressbook
// avoid double-wildcard if $value is empty // avoid double-wildcard if $value is empty
$filter = preg_replace('/\*+/', '*', $filter); $filter = preg_replace('/\*+/', '*', $filter);
if ($mode & rcube_addressbook::SEARCH_GROUPS) {
$filter = 'e:' . $filter;
}
// set filter string and execute search // set filter string and execute search
// @FIXME: we need a better way to detect/define when groups are allowed in the result $this->set_search_set($filter);
$prefix = empty($required) ? 'e:' : '';
$this->set_search_set($prefix . $filter);
if ($select) if ($select)
$this->list_records(); $this->list_records();
@ -1669,10 +1668,7 @@ class rcube_ldap extends rcube_addressbook
* List all active contact groups of this source * List all active contact groups of this source
* *
* @param string Optional search string to match group name * @param string Optional search string to match group name
* @param int Matching mode: * @param int Matching mode. Sum of rcube_addressbook::SEARCH_*
* 0 - partial (*abc*),
* 1 - strict (=),
* 2 - prefix (abc*)
* *
* @return array Indexed list of contact groups, each a hash array * @return array Indexed list of contact groups, each a hash array
*/ */
@ -1764,9 +1760,11 @@ class rcube_ldap extends rcube_addressbook
if ($search !== null) { if ($search !== null) {
// set wildcards // set wildcards
$wp = $ws = ''; $wp = $ws = '';
if (!empty($this->prop['fuzzy_search']) && $mode != 1) { if (!empty($this->prop['fuzzy_search']) && !($mode & rcube_addressbook::SEARCH_STRICT)) {
$ws = '*'; $ws = '*';
$wp = !$mode ? '*' : ''; if (!($mode & rcube_addressbook::SEARCH_PREFIX)) {
$wp = '*';
}
} }
$filter = "(&$filter($name_attr=$wp" . rcube_ldap_generic::quote_string($search) . "$ws))"; $filter = "(&$filter($name_attr=$wp" . rcube_ldap_generic::quote_string($search) . "$ws))";
$props['search'] = $wp . $search . $ws; $props['search'] = $wp . $search . $ws;

@ -139,6 +139,7 @@ function rcmail_contact_search()
// Values matching mode // Values matching mode
$mode = (int) $RCMAIL->config->get('addressbook_search_mode'); $mode = (int) $RCMAIL->config->get('addressbook_search_mode');
$mode |= rcube_addressbook::SEARCH_GROUPS;
// get sources list // get sources list
$sources = $RCMAIL->get_address_sources(); $sources = $RCMAIL->get_address_sources();

@ -63,6 +63,7 @@ if (!empty($book_types) && strlen($search)) {
$sort_keys = array(); $sort_keys = array();
$books_num = count($book_types); $books_num = count($book_types);
$search_lc = mb_strtolower($search); $search_lc = mb_strtolower($search);
$mode |= rcube_addressbook::SEARCH_GROUPS;
foreach ($book_types as $id) { foreach ($book_types as $id) {
$abook = $RCMAIL->get_address_book($id); $abook = $RCMAIL->get_address_book($id);

@ -117,7 +117,6 @@ else if (!empty($result) && $result->count > 0) {
} }
} }
// update env // update env
$OUTPUT->set_env('contactdata', $jsresult); $OUTPUT->set_env('contactdata', $jsresult);
$OUTPUT->set_env('pagecount', ceil($result->count / $page_size)); $OUTPUT->set_env('pagecount', ceil($result->count / $page_size));

@ -28,6 +28,7 @@ $page_size = $RCMAIL->config->get('addressbook_pagesize', $RCMAIL->config->g
$records = array(); $records = array();
$search_set = array(); $search_set = array();
$jsresult = array(); $jsresult = array();
$search_mode |= rcube_addressbook::SEARCH_GROUPS;
foreach ($sources as $s) { foreach ($sources as $s) {
$source = $RCMAIL->get_address_book($s['id']); $source = $RCMAIL->get_address_book($s['id']);
@ -76,6 +77,8 @@ if (!empty($result) && $result->count > 0) {
// create javascript list // create javascript list
while ($row = $result->next()) { while ($row = $result->next()) {
$name = rcube_addressbook::compose_list_name($row); $name = rcube_addressbook::compose_list_name($row);
$classname = $row['_type'] == 'group' ? 'group' : 'person';
$keyname = $row['_type'] == 'group' ? 'contactgroup' : 'contact';
// add record for every email address of the contact // add record for every email address of the contact
// (same as in list_contacts.inc) // (same as in list_contacts.inc)
@ -86,9 +89,9 @@ if (!empty($result) && $result->count > 0) {
$title = rcube_addressbook::compose_search_name($row, $email, $name); $title = rcube_addressbook::compose_search_name($row, $email, $name);
$OUTPUT->command('add_contact_row', $row_id, array( $OUTPUT->command('add_contact_row', $row_id, array(
'contact' => html::a(array('title' => $title), rcube::Q($name ? $name : $email) . $keyname => html::a(array('title' => $title), rcube::Q($name ? $name : $email) .
($name && count($emails) > 1 ? ' ' . html::span('email', rcube::Q($email)) : '') ($name && count($emails) > 1 ? ' ' . html::span('email', rcube::Q($email)) : '')
)), 'person'); )), $classname);
} }
} }

Loading…
Cancel
Save