From e9a9f2f6c52e41f3e85fc3ab0ee93afecd080892 Mon Sep 17 00:00:00 2001 From: alecpl Date: Fri, 3 Jun 2011 11:03:13 +0000 Subject: [PATCH] - Added addressbook advanced search --- CHANGELOG | 1 + program/include/rcube_contacts.php | 105 ++++++++++-- program/include/rcube_ldap.php | 31 ++-- program/include/rcube_result_set.php | 10 +- program/js/app.js | 93 +++++++---- program/localization/en_US/labels.inc | 4 + program/localization/pl_PL/labels.inc | 4 + program/steps/addressbook/func.inc | 66 ++++---- program/steps/addressbook/list.inc | 2 +- program/steps/addressbook/search.inc | 177 +++++++++++++++++---- skins/default/addressbook.css | 18 ++- skins/default/images/abook_toolbar.gif | Bin 6087 -> 5753 bytes skins/default/images/abook_toolbar.png | Bin 13586 -> 16395 bytes skins/default/templates/addressbook.html | 5 +- skins/default/templates/contactadd.html | 1 - skins/default/templates/contactsearch.html | 18 +++ 16 files changed, 390 insertions(+), 145 deletions(-) create mode 100644 skins/default/templates/contactsearch.html diff --git a/CHANGELOG b/CHANGELOG index 426d3c957..b7f16896c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Added addressbook advanced search - 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) diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php index 7c142f545..5e7165edd 100644 --- a/program/include/rcube_contacts.php +++ b/program/include/rcube_contacts.php @@ -232,12 +232,13 @@ class rcube_contacts extends rcube_addressbook /** * Search contacts * - * @param array List of fields to search in - * @param string Search value - * @param boolean True for strict (=), False for partial (LIKE) matching - * @param boolean True if results are requested, False if count only - * @param boolean True to skip the count query (select only) - * @param array List of fields that cannot be empty + * @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 boolean $strict True for strict (=), False for partial (LIKE) matching + * @param boolean $select True if results are requested, False if count only + * @param boolean $nocount True to skip the count query (select only) + * @param array $required List of fields that cannot be empty + * * @return object rcube_result_set Contact records and 'count' value */ function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array()) @@ -249,23 +250,42 @@ class rcube_contacts extends rcube_addressbook $where = $and_where = array(); - foreach ($fields as $col) { + foreach ($fields as $idx => $col) { + // direct ID search if ($col == 'ID' || $col == $this->primary_key) { $ids = !is_array($value) ? explode(',', $value) : $value; $ids = $this->db->array2list($ids, 'integer'); $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; + continue; } + // fulltext search in all fields else if ($col == '*') { $words = array(); - foreach(explode(" ", self::normalize_string($value)) as $word) + foreach (explode(" ", self::normalize_string($value)) as $word) $words[] = $this->db->ilike('words', '%'.$word.'%'); $where[] = '(' . join(' AND ', $words) . ')'; } - 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.'%'); + else { + $val = is_array($value) ? $value[$idx] : $value; + // table column + if (in_array($col, $this->table_cols)) { + if ($strict) { + $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($val); + } + else { + $where[] = $this->db->ilike($col, '%'.$val.'%'); + } + } + // vCard field + else { + if (in_array($col, $this->fulltext_cols)) { + foreach (explode(" ", self::normalize_string($val)) as $word) + $words[] = $this->db->ilike('words', '%'.$word.'%'); + $where[] = '(' . join(' AND ', $words) . ')'; + } + if (is_array($value)) + $post_search[$col] = $strict ? $val : mb_strtolower($val); + } } } @@ -273,12 +293,65 @@ class rcube_contacts extends rcube_addressbook $and_where[] = $this->db->quoteIdentifier($col).' <> '.$this->db->quote(''); } - if (!empty($where)) - $where = join(' OR ', $where); + if (!empty($where)) { + // use AND operator for advanced searches + $where = join(is_array($value) ? ' AND ' : ' OR ', $where); + } if (!empty($and_where)) $where = ($where ? "($where) AND " : '') . join(' AND ', $and_where); + // 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)) { + $ids = array(0); + // build key name regexp + $regexp = '/^(' . implode(array_keys($post_search), '|') . ')(:.*?)$/'; + // use initial WHERE clause, to limit records number if possible + if (!empty($where)) + $this->set_search_set($where); + + // count result pages + $cnt = $this->count(); + $pages = ceil($cnt / $this->page_size); + $scnt = count($post_search); + + // get (paged) result + for ($i=0; $i<$pages; $i++) { + $this->list_records(null, $i, true); + while ($row = $this->result->next()) { + $id = $row[$this->primary_key]; + $found = 0; + 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) { + // composite field, e.g. address + if (is_array($value)) { + $value = implode($value); + } + if (($strict && $value == $search) + || (!$strict && strpos(mb_strtolower($value), $search) !== false) + ) { + $found++; + break; + } + } + } + // all fields match + if ($found == $scnt) { + $ids[] = $id; + } + } + } + + // build WHERE clause + $ids = $this->db->array2list($ids, 'integer'); + $where = 'c.' . $this->primary_key.' IN ('.$ids.')'; + unset($this->cache['count']); + } + if (!empty($where)) { $this->set_search_set($where); if ($select) @@ -287,7 +360,7 @@ class rcube_contacts extends rcube_addressbook $this->result = $this->count(); } - return $this->result; + return $this->result; } diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index 7f0ea84fa..0c865a9c7 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -451,12 +451,13 @@ class rcube_ldap extends rcube_addressbook /** * Search contacts * - * @param array List of fields to search in - * @param string Search value - * @param boolean True for strict, False for partial (fuzzy) matching - * @param boolean True if results are requested, False if count only - * @param boolean (Not used) - * @param array List of fields that cannot be empty + * @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 boolean $strict True for strict, False for partial (fuzzy) matching + * @param boolean $select True if results are requested, False if count only + * @param boolean $nocount (Not used) + * @param array $required List of fields that cannot be empty + * * @return array Indexed list of contact records and 'count' value */ function search($fields, $value, $strict=false, $select=true, $nocount=false, $required=array()) @@ -477,8 +478,10 @@ class rcube_ldap extends rcube_addressbook return $result; } - $filter = '(|'; - $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : ''; + // use AND operator for advanced searches + $filter = is_array($value) ? '(&' : '(|'; + $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : ''; + if ($fields == '*') { // search_fields are required for fulltext search @@ -490,15 +493,19 @@ class rcube_ldap extends rcube_addressbook } if (is_array($this->prop['search_fields'])) { - foreach ($this->prop['search_fields'] as $k => $field) + foreach ($this->prop['search_fields'] as $field) { $filter .= "($field=$wc" . $this->_quote_string($value) . "$wc)"; + } } } else { - foreach ((array)$fields as $field) - if ($f = $this->_map_field($field)) - $filter .= "($f=$wc" . $this->_quote_string($value) . "$wc)"; + foreach ((array)$fields as $idx => $field) { + $val = is_array($value) ? $value[$idx] : $value; + if ($f = $this->_map_field($field)) { + $filter .= "($f=$wc" . $this->_quote_string($val) . "$wc)"; + } + } } $filter .= ')'; diff --git a/program/include/rcube_result_set.php b/program/include/rcube_result_set.php index 1739cacff..10361609f 100644 --- a/program/include/rcube_result_set.php +++ b/program/include/rcube_result_set.php @@ -44,27 +44,27 @@ class rcube_result_set { $this->records[] = $rec; } - + function iterate() { return $this->records[$this->current++]; } - + function first() { $this->current = 0; return $this->records[$this->current++]; } - + // alias for iterate() function next() { return $this->iterate(); } - + function seek($i) { $this->current = $i; } - + } diff --git a/program/js/app.js b/program/js/app.js index 405a12fc8..48f058eaf 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -326,11 +326,12 @@ function rcube_webmail() } } - if ((this.env.action == 'add' || this.env.action == 'edit') && this.gui_objects.editform) { + if (this.gui_objects.editform) { this.enable_command('save', true); - this.init_contact_form(); + if (this.env.action == 'add' || this.env.action == 'edit') + this.init_contact_form(); } - else if (this.gui_objects.qsearchbox) { + if (this.gui_objects.qsearchbox) { this.enable_command('search', 'reset-search', 'moveto', true); $(this.gui_objects.qsearchbox).select(); } @@ -338,7 +339,7 @@ function rcube_webmail() if (this.contact_list && this.contact_list.rowcount > 0) this.enable_command('export', true); - this.enable_command('list', 'listgroup', true); + this.enable_command('list', 'listgroup', 'advanced-search', true); break; @@ -587,7 +588,7 @@ function rcube_webmail() // common commands used in multiple tasks case 'show': - if (this.task=='mail') { + if (this.task == 'mail') { var uid = this.get_single_uid(); if (uid && (!this.env.uid || uid != this.env.uid)) { if (this.env.mailbox == this.env.drafts_mailbox) @@ -596,17 +597,17 @@ function rcube_webmail() this.show_message(uid); } } - else if (this.task=='addressbook') { + else if (this.task == 'addressbook') { var cid = props ? props : this.get_single_cid(); - if (cid && !(this.env.action=='show' && cid==this.env.cid)) + if (cid && !(this.env.action == 'show' && cid == this.env.cid)) this.load_contact(cid, 'show'); } break; case 'add': - if (this.task=='addressbook') + if (this.task == 'addressbook') this.load_contact(0, 'add'); - else if (this.task=='settings') { + else if (this.task == 'settings') { this.identity_list.clear_selection(); this.load_identity(0, 'add-identity'); } @@ -625,27 +626,29 @@ function rcube_webmail() break; case 'save': - if (this.gui_objects.editform) { - var input_pagesize = $("input[name='_pagesize']"); - var input_name = $("input[name='_name']"); - var input_email = $("input[name='_email']"); - + var input, form = this.gui_objects.editform; + if (form) { + // adv. search + if (this.env.action == 'search') { + } // user prefs - if (input_pagesize.length && isNaN(parseInt(input_pagesize.val()))) { + else if ((input = $("input[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) { alert(this.get_label('nopagesizewarning')); - input_pagesize.focus(); + input.focus(); break; } // contacts/identities else { - if (input_name.length && input_name.val() == '') { + if ((input = $("input[name='_name']", form)) &&input.length && input.val() == '') { alert(this.get_label('nonamewarning')); - input_name.focus(); + input.focus(); break; } - else if (this.task == 'settings' && input_email.length && (this.env.identities_level % 2) == 0 && !rcube_check_email(input_email.val())) { + else if (this.task == 'settings' && (this.env.identities_level % 2) == 0 && + (input = $("input[name='_email']", form)) && input.length&& !rcube_check_email(input.val()) + ) { alert(this.get_label('noemailwarning')); - input_email.focus(); + input.focus(); break; } @@ -653,7 +656,7 @@ function rcube_webmail() $('input.placeholder').each(function(){ if (this.value == this._placeholder) this.value = ''; }); } - this.gui_objects.editform.submit(); + form.submit(); } break; @@ -3348,8 +3351,7 @@ function rcube_webmail() if (mods) mods = mods[mbox] ? mods[mbox] : mods['*']; } else if (this.contact_list) { - this.contact_list.clear(true); - this.show_contentframe(false); + this.list_contacts_clear(); } if (mods) { @@ -3715,9 +3717,7 @@ function rcube_webmail() this.list_contacts_remote = function(src, group, page) { // clear message list first - this.contact_list.clear(true); - this.show_contentframe(false); - this.enable_command('delete', 'compose', false); + this.list_contacts_clear(); // send request to server var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : ''), @@ -3736,6 +3736,13 @@ function rcube_webmail() this.http_request('list', url, lock); }; + this.list_contacts_clear = function() + { + this.contact_list.clear(true); + this.show_contentframe(false); + this.enable_command('delete', 'compose', false); + }; + // load contact record this.load_contact = function(cid, action, framed) { @@ -4077,7 +4084,7 @@ function rcube_webmail() else { var lastelem = $('.ff_'+col), appendcontainer = $('#contactsection'+section+' .contactcontroller'+col); - + if (!appendcontainer.length) appendcontainer = $('
').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last()); @@ -4086,7 +4093,7 @@ function rcube_webmail() row = $('
').addClass('row'), cell = $('
').addClass('contactfieldcontent data'), label = $('
').addClass('contactfieldlabel label'); - + if (colprop.subtypes_select) label.html(colprop.subtypes_select); else @@ -4120,7 +4127,7 @@ function rcube_webmail() .addClass('ff_'+col) .attr('name', '_'+col+name_suffix) .appendTo(cell); - + var options = input.attr('options'); options[options.length] = new Option('---', ''); if (colprop.options) @@ -4134,10 +4141,10 @@ function rcube_webmail() .html(this.env.delbutton) .click(function(){ ref.delete_edit_field(this); return false }) .appendTo(cell); - + row.append(label).append(cell).appendTo(appendcontainer.show()); input.first().focus(); - + // disable option if limit reached if (!colprop.count) colprop.count = 0; if (++colprop.count == colprop.limit && colprop.limit) @@ -4153,7 +4160,7 @@ function rcube_webmail() colprop = this.env.coltypes[col], fieldset = $(elem).parents('fieldset.contactfieldgroup'), addmenu = fieldset.parent().find('select.addfieldmenu'); - + // just clear input but don't hide the last field if (--colprop.count <= 0 && colprop.visible) $(elem).parent().children('input').val('').blur(); @@ -4163,7 +4170,7 @@ function rcube_webmail() if (!fieldset.children('div.row').length) fieldset.hide(); } - + // enable option in add-field selector or insert it if necessary if (addmenu.length) { var option = addmenu.children('option[value="'+col+'"]'); @@ -4213,6 +4220,26 @@ function rcube_webmail() this.enable_command('delete-photo', this.env.coltypes.photo && id != '-del-'); }; + // load advanced search page + this.advanced_search = function() + { + var add_url = '&_form=1', target = window; + + if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { + add_url += '&_framed=1'; + target = window.frames[this.env.contentframe]; + this.contact_list.clear_selection(); + } + else if (framed) + return false; + + this.location_href(this.env.comm_path+'&_action=search'+add_url + +'&_source='+urlencode(this.env.source) + +(this.env.group ? '&_gid='+urlencode(this.env.group) : ''), target); + + return true; + }; + /*********************************************************/ /********* user settings methods *********/ diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index c04531cf5..fa0fab581 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -272,6 +272,9 @@ $labels['manager'] = 'Manager'; $labels['assistant'] = 'Assistant'; $labels['spouse'] = 'Spouse'; $labels['allfields'] = 'All fields'; +$labels['search'] = 'Search'; +$labels['advsearch'] = 'Advanced Search'; +$labels['other'] = 'Other'; $labels['typehome'] = 'Home'; $labels['typework'] = 'Work'; @@ -284,6 +287,7 @@ $labels['typecar'] = 'Car'; $labels['typepager'] = 'Pager'; $labels['typevideo'] = 'Video'; $labels['typeassistant'] = 'Assistant'; +$labels['typehomepage'] = 'Home page'; $labels['addfield'] = 'Add field...'; $labels['addcontact'] = 'Add new contact'; diff --git a/program/localization/pl_PL/labels.inc b/program/localization/pl_PL/labels.inc index 8c4e910cd..cb89440a5 100644 --- a/program/localization/pl_PL/labels.inc +++ b/program/localization/pl_PL/labels.inc @@ -398,6 +398,7 @@ $labels['typecar'] = 'Samochód'; $labels['typepager'] = 'Pager'; $labels['typevideo'] = 'Wideo'; $labels['typeassistant'] = 'Asystent'; +$labels['typehomepage'] = 'Strona domowa'; $labels['addfield'] = 'Dodaj pole...'; $labels['personalinfo'] = 'Informacje osobiste'; $labels['addphoto'] = 'Dodaj'; @@ -409,5 +410,8 @@ $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'; +$labels['search'] = 'Szukaj'; +$labels['advsearch'] = 'Zaawansowane wyszukiwanie'; +$labels['other'] = 'Inne'; ?> diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 545f140bf..df86fced2 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -62,36 +62,36 @@ if (!$OUTPUT->ajax_call) { // general definition of contact coltypes $CONTACT_COLTYPES = array( - 'name' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name')), - 'firstname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('firstname')), - 'surname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('surname')), - 'middlename' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('middlename')), - 'prefix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('nameprefix')), - 'suffix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('namesuffix')), - 'nickname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('nickname')), - 'jobtitle' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('jobtitle')), - 'organization' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('organization')), - 'department' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('department')), - 'gender' => array('type' => 'select', 'limit' => 1, 'label' => rcube_label('gender'), 'options' => array('male' => rcube_label('male'), 'female' => rcube_label('female'))), - 'maidenname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('maidenname')), - 'email' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other')), - 'phone' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('phone'), 'subtypes' => array('home','home2','work','work2','mobile','main','homefax','workfax','car','pager','video','assistant','other')), + 'name' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'), + 'firstname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('firstname'), 'category' => 'main'), + 'surname' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('surname'), 'category' => 'main'), + 'email' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'), + 'middlename' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('middlename'), 'category' => 'main'), + 'prefix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('nameprefix'), 'category' => 'main'), + 'suffix' => array('type' => 'text', 'size' => 8, 'limit' => 1, 'label' => rcube_label('namesuffix'), 'category' => 'main'), + 'nickname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('nickname'), 'category' => 'main'), + 'jobtitle' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('jobtitle'), 'category' => 'main'), + 'organization' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('organization'), 'category' => 'main'), + 'department' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('department'), 'category' => 'main'), + 'gender' => array('type' => 'select', 'limit' => 1, 'label' => rcube_label('gender'), 'options' => array('male' => rcube_label('male'), 'female' => rcube_label('female')), 'category' => 'personal'), + 'maidenname' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('maidenname'), 'category' => 'personal'), + 'phone' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('phone'), 'subtypes' => array('home','home2','work','work2','mobile','main','homefax','workfax','car','pager','video','assistant','other'), 'category' => 'main'), 'address' => array('type' => 'composite', 'label' => rcube_label('address'), 'subtypes' => array('home','work','other'), 'childs' => array( - 'street' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('street')), - 'locality' => array('type' => 'text', 'size' => 28, 'label' => rcube_label('locality')), - 'zipcode' => array('type' => 'text', 'size' => 8, 'label' => rcube_label('zipcode')), - 'region' => array('type' => 'text', 'size' => 12, 'label' => rcube_label('region')), - 'country' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('country')), - )), - 'birthday' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('birthday'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), - 'anniversary' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('anniversary'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), - 'website' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('website'), 'subtypes' => array('homepage','work','blog','other')), - 'im' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('instantmessenger'), 'subtypes' => array('aim','icq','msn','yahoo','jabber','skype','other')), + 'street' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('street'), 'category' => 'main'), + 'locality' => array('type' => 'text', 'size' => 28, 'label' => rcube_label('locality'), 'category' => 'main'), + 'zipcode' => array('type' => 'text', 'size' => 8, 'label' => rcube_label('zipcode'), 'category' => 'main'), + 'region' => array('type' => 'text', 'size' => 12, 'label' => rcube_label('region'), 'category' => 'main'), + 'country' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('country'), 'category' => 'main'), + ), 'category' => 'main'), + 'birthday' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('birthday'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col', 'category' => 'personal'), + 'anniversary' => array('type' => 'date', 'size' => 12, 'label' => rcube_label('anniversary'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col', 'category' => 'personal'), + 'website' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('website'), 'subtypes' => array('homepage','work','blog','other'), 'category' => 'main'), + 'im' => array('type' => 'text', 'size' => 40, 'label' => rcube_label('instantmessenger'), 'subtypes' => array('aim','icq','msn','yahoo','jabber','skype','other'), 'category' => 'main'), 'notes' => array('type' => 'textarea', 'size' => 40, 'rows' => 15, 'label' => rcube_label('notes'), 'limit' => 1), - 'photo' => array('type' => 'image', 'limit' => 1), - 'assistant' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('assistant')), - 'manager' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('manager')), - 'spouse' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('spouse')), + 'photo' => array('type' => 'image', 'limit' => 1, 'category' => 'main'), + 'assistant' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('assistant'), 'category' => 'personal'), + 'manager' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('manager'), 'category' => 'personal'), + 'spouse' => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('spouse'), 'category' => 'personal'), // TODO: define fields for vcards like GEO, KEY ); @@ -225,10 +225,10 @@ function rcmail_js_contacts_list($result, $prefix='') // define list of cols to be displayed $a_show_cols = array('name'); - + while ($row = $result->next()) { $a_row_cols = array(); - + // format each col foreach ($a_show_cols as $col) $a_row_cols[$col] = Q($row[$col]); @@ -324,7 +324,7 @@ function rcmail_contact_form($form, $record, $attrib = null) // get default coltypes $coltypes = $GLOBALS['CONTACT_COLTYPES']; - $coltype_lables = array(); + $coltype_labels = array(); foreach ($coltypes as $col => $prop) { if ($prop['subtypes']) { @@ -335,7 +335,7 @@ function rcmail_contact_form($form, $record, $attrib = null) } if ($prop['childs']) { foreach ($prop['childs'] as $childcol => $cp) - $coltype_lables[$childcol] = array('label' => $cp['label']); + $coltype_labels[$childcol] = array('label' => $cp['label']); } } @@ -548,7 +548,7 @@ function rcmail_contact_form($form, $record, $attrib = null) } if ($edit_mode) { - $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_lables); + $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_labels); $RCMAIL->output->set_env('delbutton', $del_button); $RCMAIL->output->add_label('delete'); } diff --git a/program/steps/addressbook/list.inc b/program/steps/addressbook/list.inc index 234e1a633..0eb4b806a 100644 --- a/program/steps/addressbook/list.inc +++ b/program/steps/addressbook/list.inc @@ -28,7 +28,7 @@ $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text($rowcount)); // create javascript list rcmail_js_contacts_list($result); - + // send response $OUTPUT->send(); diff --git a/program/steps/addressbook/search.inc b/program/steps/addressbook/search.inc index fe7099fac..fff6bd66c 100644 --- a/program/steps/addressbook/search.inc +++ b/program/steps/addressbook/search.inc @@ -6,58 +6,171 @@ | | | This file is part of the Roundcube Webmail client | | Copyright (C) 2005-2011, The Roundcube Dev Team | + | Copyright (C) 2011, Kolab Systems AG | | Licensed under the GNU GPL | | | | PURPOSE: | - | Search step for address book contacts | + | Search action (and form) for address book contacts | | | +-----------------------------------------------------------------------+ | Author: Thomas Bruederli | + | Author: Aleksander Machniak | +-----------------------------------------------------------------------+ $Id: search.inc 456 2007-01-10 12:34:33Z thomasb $ */ -$CONTACTS->set_page(1); -$_SESSION['page'] = 1; +if (!isset($_GET['_form'])) { + rcmail_contact_search(); +} -// get input -$search = trim(get_input_value('_q', RCUBE_INPUT_GET, true)); -$fields = explode(',', get_input_value('_headers', RCUBE_INPUT_GET)); +$OUTPUT->add_handler('searchform', 'rcmail_contact_search_form'); +$OUTPUT->send('contactsearch'); -if (empty($fields)) { - $fields = $SEARCH_MODS_DEFAULT; -} -$search_request = md5('addr'.$search.implode($fields, ',')); +function rcmail_contact_search() +{ + global $RCMAIL, $OUTPUT, $CONTACTS, $CONTACT_COLTYPES, $SEARCH_MODS_DEFAULT; -// update search_mods setting -$search_mods = array_fill_keys($fields, 1); -$RCMAIL->user->save_prefs(array('addressbook_search_mods' => $search_mods)); + $adv = isset($_POST['_adv']); -if ($fields['all'] || count($fields) == count($SEARCH_MODS_DEFAULT)) { - $fields = '*'; -} + // get fields/values from advanced search form + if ($adv) { + foreach ($CONTACT_COLTYPES as $col => $colprop) { + $s = trim(get_input_value('_'.$col, RCUBE_INPUT_POST, true)); + if (strlen($s)) { + $search[] = $s; + $fields[] = $col; + } + } -// get contacts for this user -$result = $CONTACTS->search($fields, $search); + if (empty($fields)) { + // do nothing, show the form again + return; + } + } + // quick-search + else { + $search = trim(get_input_value('_q', RCUBE_INPUT_GET, true)); + $fields = explode(',', get_input_value('_headers', RCUBE_INPUT_GET)); -// save search settings in session -$_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); + if (empty($fields)) { + $fields = $SEARCH_MODS_DEFAULT; + } -if ($result->count > 0) { - // create javascript list - rcmail_js_contacts_list($result); -} -else { - $OUTPUT->show_message('nocontactsfound', 'notice'); + // update search_mods setting + $old_mods = $RCMAIL->config->get('addressbook_search_mods'); + $search_mods = array_fill_keys($fields, 1); + if ($old_mods != $search_mods) { + $RCMAIL->user->save_prefs(array('addressbook_search_mods' => $search_mods)); + } + + if ($fields['*'] || count($fields) == count($SEARCH_MODS_DEFAULT)) { + $fields = '*'; + } + } + + // search request ID + $search_request = md5('addr'.implode($fields, ',') + .(is_array($search) ? implode($search, ',') : $search)); + + // reset page + $CONTACTS->set_page(1); + $_SESSION['page'] = 1; + + // get contacts for this user + $result = $CONTACTS->search($fields, $search); + + // save search settings in session + $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); + + if ($adv) + $OUTPUT->command('list_contacts_clear'); + + if ($result->count > 0) { + // create javascript list + rcmail_js_contacts_list($result); + } + else { + $OUTPUT->show_message('nocontactsfound', 'notice'); + } + + // update message count display + $OUTPUT->command('set_env', 'search_request', $search_request); + $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONTACTS->page_size)); + $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text()); + + // send response + $OUTPUT->send($adv ? 'iframe' : null); } -// update message count display -$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()); +function rcmail_contact_search_form($attrib) +{ + global $RCMAIL, $CONTACTS, $CONTACT_COLTYPES; -// send response -$OUTPUT->send(); + $i_size = !empty($attrib['size']) ? $attrib['size'] : 30; + + $form = array( + 'main' => array( + 'name' => rcube_label('contactproperties'), + 'content' => array( + ), + ), + 'personal' => array( + 'name' => rcube_label('personalinfo'), + 'content' => array( + ), + ), + 'other' => array( + 'name' => rcube_label('other'), + 'content' => array( + ), + ), + ); + + foreach ($CONTACT_COLTYPES as $col => $colprop) + { + if ($colprop['type'] != 'image' && !$colprop['nosearch']) + { + $ftype = $colprop['type'] == 'select' ? 'select' : 'text'; + $label = isset($colprop['label']) ? $colprop['label'] : rcube_label($col); + $category = $colprop['category'] ? $colprop['category'] : 'other'; + + if ($ftype == 'text') + $colprop['size'] = $i_size; + + $content = html::div('row', html::div('contactfieldlabel label', Q($label)) + . html::div('contactfieldcontent', rcmail_get_edit_field($col, '', $colprop, $ftype))); + + $form[$category]['content'][] = $content; + } + } + + $hiddenfields = new html_hiddenfield(array( + 'name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); + $hiddenfields->add(array('name' => '_gid', 'value' => $CONTACTS->group_id)); + $hiddenfields->add(array('name' => '_adv', 'value' => 1)); + + $out = $RCMAIL->output->request_form(array( + 'name' => 'form', 'method' => 'post', + 'task' => $RCMAIL->task, 'action' => 'search', + 'noclose' => true) + $attrib, $hiddenfields->show()); + + $RCMAIL->output->add_gui_object('editform', $attrib['id']); + + unset($attrib['name']); + unset($attrib['id']); + + foreach ($form as $f) { + if (!empty($f['content'])) { + $content = html::div('contactfieldgroup', join("\n", $f['content'])); + + $out .= html::tag('fieldset', $attrib, + html::tag('legend', null, Q($f['name'])) + . $content) . "\n"; + } + } + + return $out . ''; +} diff --git a/skins/default/addressbook.css b/skins/default/addressbook.css index 06808cd76..18939b9fe 100644 --- a/skins/default/addressbook.css +++ b/skins/default/addressbook.css @@ -72,6 +72,14 @@ background-position: -162px 0; } +#abooktoolbar a.search { + background-position: -170px 0; +} + +#abooktoolbar a.searchSel { + background-position: -170px -32px; +} + #abookcountbar { margin-top: 4px; @@ -208,12 +216,6 @@ body.iframe, border: none; } -#contact-details table td.title -{ - font-weight: bold; - text-align: right; -} - #contacttabs { position: relative; @@ -335,7 +337,7 @@ fieldset.contactfieldgroup legend position: absolute; top: 0; left: 2px; - width: 90px; + width: 110px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -355,7 +357,7 @@ fieldset.contactfieldgroup legend .contactfieldgroup .contactfieldcontent { - padding-left: 100px; + padding-left: 120px; min-height: 1em; line-height: 1.3em; } diff --git a/skins/default/images/abook_toolbar.gif b/skins/default/images/abook_toolbar.gif index 1de95da76ae92eef7b592242ace2847983831352..2e8f4e2596645b4d584258ed63f9da3089e308f6 100644 GIT binary patch literal 5753 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 literal 6087 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 Knlyj_xBmnBQJHA~ diff --git a/skins/default/images/abook_toolbar.png b/skins/default/images/abook_toolbar.png index c761fbc9d96de756b60d723b74940ba436f7c586..feb95c026cc24656da50f9cfc5fa4823f7d7beb2 100644 GIT binary patch literal 16395 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

KG{BWk5nd%BzY%Of|C3%cLJ(aNsmRF$W?KswCU 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  +

@@ -41,10 +42,6 @@
  • -
    diff --git a/skins/default/templates/contactadd.html b/skins/default/templates/contactadd.html index b5fd05609..6e0737020 100644 --- a/skins/default/templates/contactadd.html +++ b/skins/default/templates/contactadd.html @@ -19,7 +19,6 @@
    -
    diff --git a/skins/default/templates/contactsearch.html b/skins/default/templates/contactsearch.html new file mode 100644 index 000000000..23cbec4cb --- /dev/null +++ b/skins/default/templates/contactsearch.html @@ -0,0 +1,18 @@ + + + +<roundcube:object name="pagetitle" /> + + + + + +
    +
    + +

    +
    + + + +