From bd82526e41e54fa001c851d48b4e7823aa5e7bcd Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 22 Feb 2015 10:43:48 +0100 Subject: Fix missing or not up-to-date CATEGORIES entry in vCard export (#1490277) --- program/lib/Roundcube/rcube_contacts.php | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'program/lib/Roundcube/rcube_contacts.php') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 6ac9fd5de..475f15696 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -714,6 +714,11 @@ class rcube_contacts extends rcube_addressbook // copy values into vcard object $vcard = new rcube_vcard($record['vcard'] ? $record['vcard'] : $save_data['vcard'], RCUBE_CHARSET, false, $this->vcard_fieldmap); $vcard->reset(); + + // don't store groups in vCard (#1490277) + $vcard->set('groups', null); + unset($save_data['groups']); + foreach ($save_data as $key => $values) { list($field, $section) = explode(':', $key); $fulltext = in_array($field, $this->fulltext_cols); -- cgit v1.2.3 From fd259bed691c35ff87211552c8b280db4fc1460a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 15:52:14 +0100 Subject: Adapt fulltext search in local address book to ignore words order --- program/lib/Roundcube/rcube_contacts.php | 80 ++++++++++++++++---------------- 1 file changed, 39 insertions(+), 41 deletions(-) (limited to 'program/lib/Roundcube/rcube_contacts.php') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 475f15696..8ba3e6372 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -320,28 +320,8 @@ class rcube_contacts extends rcube_addressbook $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; continue; } - // fulltext search in all fields - else if ($col == '*') { - $words = array(); - foreach (explode($WS, rcube_utils::normalize_string($value)) as $word) { - switch ($mode) { - case 1: // strict - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word . $WS . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word) . ')'; - break; - case 2: // prefix - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', '%' . $WS . $word . '%') . ')'; - break; - default: // partial - $words[] = $this->db->ilike('words', '%' . $word . '%'); - } - } - $where[] = '(' . join(' AND ', $words) . ')'; - } - else { - $val = is_array($value) ? $value[$idx] : $value; + else if (is_array($value)) { + $val = $value[$idx]; // table column if (in_array($col, $this->table_cols)) { switch ($mode) { @@ -362,27 +342,18 @@ class rcube_contacts extends rcube_addressbook // vCard field else { if (in_array($col, $this->fulltext_cols)) { - foreach (rcube_utils::normalize_string($val, true) as $word) { - switch ($mode) { - case 1: // strict - $words[] = '(' . $this->db->ilike('words', $word . $WS . '%') - . ' OR ' . $this->db->ilike('words', '%' . $AS . $word . $WS .'%') - . ' OR ' . $this->db->ilike('words', '%' . $AS . $word) . ')'; - break; - case 2: // prefix - $words[] = '(' . $this->db->ilike('words', $word . '%') - . ' OR ' . $this->db->ilike('words', $AS . $word . '%') . ')'; - break; - default: // partial - $words[] = $this->db->ilike('words', '%' . $word . '%'); - } - } - $where[] = '(' . join(' AND ', $words) . ')'; + $where[] = $this->fulltext_sql_where($val, $mode, 'words'); } - if (is_array($value)) - $post_search[$col] = mb_strtolower($val); + $post_search[$col] = mb_strtolower($val); } } + // fulltext search in all fields + else if ($col == '*') { + $where[] = $this->fulltext_sql_where($value, $mode, 'words'); + } + else { + $where[] = $this->fulltext_sql_where($value, $mode, $col, 'OR'); + } } foreach (array_intersect($required, $this->table_cols) as $col) { @@ -391,7 +362,7 @@ class rcube_contacts extends rcube_addressbook if (!empty($where)) { // use AND operator for advanced searches - $where = join(is_array($value) ? ' AND ' : ' OR ', $where); + $where = join(is_array($value) || $fields[0] != '*' ? ' AND ' : ' OR ', $where); } if (!empty($and_where)) @@ -460,6 +431,33 @@ class rcube_contacts extends rcube_addressbook return $this->result; } + /** + * Helper method to compose SQL where statements for fulltext searching + */ + private function fulltext_sql_where($value, $mode, $col = 'words', $bool = 'AND') + { + $WS = ' '; + $AS = $col == 'words' ? $WS : self::SEPARATOR; + + $where = array(); + foreach (rcube_utils::normalize_string($value, true) as $word) { + switch ($mode) { + case 1: // strict + $where[] = '(' . $this->db->ilike($col, $word . '%') + . ' OR ' . $this->db->ilike($col, '%' . $WS . $word . $WS . '%') + . ' OR ' . $this->db->ilike($col, '%' . $WS . $word) . ')'; + break; + case 2: // prefix + $where[] = '(' . $this->db->ilike($col, $word . '%') + . ' OR ' . $this->db->ilike($col, '%' . $AS . $word . '%') . ')'; + break; + default: // partial + $where[] = $this->db->ilike($col, '%' . $word . '%'); + } + } + + return '(' . join(" $bool ", $where) . ')'; + } /** * Count number of available contacts in database -- cgit v1.2.3 From 3bd027726114a4f66dfcf5cbe069d50b9485f010 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 16:28:10 +0100 Subject: Fix full-text searching in a given list of fields --- program/lib/Roundcube/rcube_contacts.php | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) (limited to 'program/lib/Roundcube/rcube_contacts.php') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 8ba3e6372..5cd96b2dd 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -302,8 +302,6 @@ class rcube_contacts extends rcube_addressbook */ function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array()) { - if (!is_array($fields)) - $fields = array($fields); if (!is_array($required) && !empty($required)) $required = array($required); @@ -312,16 +310,19 @@ class rcube_contacts extends rcube_addressbook $WS = ' '; $AS = self::SEPARATOR; - foreach ($fields as $idx => $col) { - // direct ID search - if ($col == 'ID' || $col == $this->primary_key) { - $ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value; - $ids = $this->db->array2list($ids, 'integer'); - $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; - continue; - } - else if (is_array($value)) { + // direct ID search + if ($fields == 'ID' || $fields == $this->primary_key) { + $ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value; + $ids = $this->db->array2list($ids, 'integer'); + $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; + } + else if (is_array($value)) { + foreach ((array)$fields as $idx => $col) { $val = $value[$idx]; + + if (!strlen($val)) + continue; + // table column if (in_array($col, $this->table_cols)) { switch ($mode) { @@ -347,12 +348,19 @@ class rcube_contacts extends rcube_addressbook $post_search[$col] = mb_strtolower($val); } } - // fulltext search in all fields - else if ($col == '*') { - $where[] = $this->fulltext_sql_where($value, $mode, 'words'); - } - else { - $where[] = $this->fulltext_sql_where($value, $mode, $col, 'OR'); + } + // fulltext search in all fields + else if ($fields == '*') { + $where[] = $this->fulltext_sql_where($value, $mode, 'words'); + } + else { + // require each word in to be present in one of the fields + foreach (rcube_utils::normalize_string($value, true) as $word) { + $groups = array(); + foreach ((array)$fields as $idx => $col) { + $groups[] = $this->fulltext_sql_where($word, $mode, $col); + } + $where[] = '(' . join(' OR ', $groups) . ')'; } } @@ -362,7 +370,7 @@ class rcube_contacts extends rcube_addressbook if (!empty($where)) { // use AND operator for advanced searches - $where = join(is_array($value) || $fields[0] != '*' ? ' AND ' : ' OR ', $where); + $where = join(" AND ", $where); } if (!empty($and_where)) -- cgit v1.2.3 From 8e333bcb662b6464f8a6c873cfb8825ac0a11943 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Tue, 3 Mar 2015 16:35:01 +0100 Subject: Only normalize search term when searching in 'words' column --- program/lib/Roundcube/rcube_contacts.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'program/lib/Roundcube/rcube_contacts.php') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 5cd96b2dd..6438c431a 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -355,7 +355,7 @@ class rcube_contacts extends rcube_addressbook } else { // require each word in to be present in one of the fields - foreach (rcube_utils::normalize_string($value, true) as $word) { + foreach (rcube_utils::tokenize_string($value, 1) as $word) { $groups = array(); foreach ((array)$fields as $idx => $col) { $groups[] = $this->fulltext_sql_where($word, $mode, $col); @@ -446,9 +446,10 @@ class rcube_contacts extends rcube_addressbook { $WS = ' '; $AS = $col == 'words' ? $WS : self::SEPARATOR; + $words = $col == 'words' ? rcube_utils::normalize_string($value, true) : array($value); $where = array(); - foreach (rcube_utils::normalize_string($value, true) as $word) { + foreach ($words as $word) { switch ($mode) { case 1: // strict $where[] = '(' . $this->db->ilike($col, $word . '%') -- cgit v1.2.3 From aafc050f550eec7557d9e66ee90cc8bd7b5536da Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 9 Mar 2015 09:12:34 +0100 Subject: Fix rcube_contacts::search() calls with empty search string but $required argument --- program/lib/Roundcube/rcube_contacts.php | 44 +++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 12 deletions(-) (limited to 'program/lib/Roundcube/rcube_contacts.php') diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 6438c431a..23e0b710c 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -305,7 +305,7 @@ class rcube_contacts extends rcube_addressbook if (!is_array($required) && !empty($required)) $required = array($required); - $where = $and_where = array(); + $where = $and_where = $post_search = array(); $mode = intval($mode); $WS = ' '; $AS = self::SEPARATOR; @@ -367,6 +367,7 @@ class rcube_contacts extends rcube_addressbook foreach (array_intersect($required, $this->table_cols) as $col) { $and_where[] = $this->db->quote_identifier($col).' <> '.$this->db->quote(''); } + $required = array_diff($required, $this->table_cols); if (!empty($where)) { // use AND operator for advanced searches @@ -378,7 +379,7 @@ class rcube_contacts extends rcube_addressbook // Post-searching in vCard data fields // we will search in all records and then build a where clause for their IDs - if (!empty($post_search)) { + if (!empty($post_search) || !empty($required)) { $ids = array(0); // build key name regexp $regexp = '/^(' . implode(array_keys($post_search), '|') . ')(?:.*)$/'; @@ -387,7 +388,7 @@ class rcube_contacts extends rcube_addressbook $this->set_search_set($where); // count result pages - $cnt = $this->count(); + $cnt = $this->count()->count; $pages = ceil($cnt / $this->page_size); $scnt = count($post_search); @@ -397,14 +398,33 @@ class rcube_contacts extends rcube_addressbook while ($row = $this->result->next()) { $id = $row[$this->primary_key]; $found = array(); - foreach (preg_grep($regexp, array_keys($row)) as $col) { - $pos = strpos($col, ':'); - $colname = $pos ? substr($col, 0, $pos) : $col; - $search = $post_search[$colname]; - foreach ((array)$row[$col] as $value) { - if ($this->compare_search_value($colname, $value, $search, $mode)) { - $found[$colname] = true; - break 2; + if (!empty($post_search)) { + foreach (preg_grep($regexp, array_keys($row)) as $col) { + $pos = strpos($col, ':'); + $colname = $pos ? substr($col, 0, $pos) : $col; + $search = $post_search[$colname]; + foreach ((array)$row[$col] as $value) { + if ($this->compare_search_value($colname, $value, $search, $mode)) { + $found[$colname] = true; + break 2; + } + } + } + } + // check if required fields are present + if (!empty($required)) { + foreach ($required as $req) { + $hit = false; + foreach ($row as $c => $values) { + if ($c === $req || strpos($c, $req.':') === 0) { + if ((is_string($row[$c]) && strlen($row[$c])) || !empty($row[$c])) { + $hit = true; + break; + } + } + } + if (!$hit) { + continue 2; } } } @@ -465,7 +485,7 @@ class rcube_contacts extends rcube_addressbook } } - return '(' . join(" $bool ", $where) . ')'; + return count($where) ? '(' . join(" $bool ", $where) . ')' : ''; } /** -- cgit v1.2.3