Changeset 4834 in subversion


Ignore:
Timestamp:
Jun 3, 2011 7:03:13 AM (2 years ago)
Author:
alec
Message:
  • Added addressbook advanced search
Location:
trunk/roundcubemail
Files:
1 added
15 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r4832 r4834  
    22=========================== 
    33 
     4- Added addressbook advanced search 
    45- Add popup with basic fields selection for addressbook search 
    56- Case-insensitive matching in autocompletion (#1487933) 
  • trunk/roundcubemail/program/include/rcube_contacts.php

    r4823 r4834  
    233233     * Search contacts 
    234234     * 
    235      * @param array   List of fields to search in 
    236      * @param string  Search value 
    237      * @param boolean True for strict (=), False for partial (LIKE) matching 
    238      * @param boolean True if results are requested, False if count only 
    239      * @param boolean True to skip the count query (select only) 
    240      * @param array   List of fields that cannot be empty 
     235     * @param mixed   $fields   The field name of array of field names to search in 
     236     * @param mixed   $value    Search value (or array of values when $fields is array) 
     237     * @param boolean $strict   True for strict (=), False for partial (LIKE) matching 
     238     * @param boolean $select   True if results are requested, False if count only 
     239     * @param boolean $nocount  True to skip the count query (select only) 
     240     * @param array   $required List of fields that cannot be empty 
     241     * 
    241242     * @return object rcube_result_set Contact records and 'count' value 
    242243     */ 
     
    250251        $where = $and_where = array(); 
    251252 
    252         foreach ($fields as $col) { 
     253        foreach ($fields as $idx => $col) { 
     254            // direct ID search 
    253255            if ($col == 'ID' || $col == $this->primary_key) { 
    254256                $ids     = !is_array($value) ? explode(',', $value) : $value; 
    255257                $ids     = $this->db->array2list($ids, 'integer'); 
    256258                $where[] = 'c.' . $this->primary_key.' IN ('.$ids.')'; 
     259                continue; 
    257260            } 
     261            // fulltext search in all fields 
    258262            else if ($col == '*') { 
    259263                $words = array(); 
    260                 foreach(explode(" ", self::normalize_string($value)) as $word) 
     264                foreach (explode(" ", self::normalize_string($value)) as $word) 
    261265                    $words[] = $this->db->ilike('words', '%'.$word.'%'); 
    262266                $where[] = '(' . join(' AND ', $words) . ')'; 
    263267            } 
    264             else if ($strict) { 
    265                 $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($value); 
    266             } 
    267             else if (in_array($col, $this->table_cols)) { 
    268                 $where[] = $this->db->ilike($col, '%'.$value.'%'); 
     268            else { 
     269                $val = is_array($value) ? $value[$idx] : $value; 
     270                // table column 
     271                if (in_array($col, $this->table_cols)) { 
     272                    if ($strict) { 
     273                        $where[] = $this->db->quoteIdentifier($col).' = '.$this->db->quote($val); 
     274                    } 
     275                    else { 
     276                        $where[] = $this->db->ilike($col, '%'.$val.'%'); 
     277                    } 
     278                } 
     279                // vCard field 
     280                else { 
     281                    if (in_array($col, $this->fulltext_cols)) { 
     282                        foreach (explode(" ", self::normalize_string($val)) as $word) 
     283                            $words[] = $this->db->ilike('words', '%'.$word.'%'); 
     284                        $where[] = '(' . join(' AND ', $words) . ')'; 
     285                    } 
     286                    if (is_array($value)) 
     287                        $post_search[$col] = $strict ? $val : mb_strtolower($val); 
     288                } 
    269289            } 
    270290        } 
     
    274294        } 
    275295 
    276         if (!empty($where)) 
    277             $where = join(' OR ', $where); 
     296        if (!empty($where)) { 
     297            // use AND operator for advanced searches 
     298            $where = join(is_array($value) ? ' AND ' : ' OR ', $where); 
     299        } 
    278300 
    279301        if (!empty($and_where)) 
    280302            $where = ($where ? "($where) AND " : '') . join(' AND ', $and_where); 
     303 
     304        // Post-searching in vCard data fields 
     305        // we will search in all records and then build a where clause for their IDs 
     306        if (!empty($post_search)) { 
     307            $ids = array(0); 
     308            // build key name regexp 
     309            $regexp = '/^(' . implode(array_keys($post_search), '|') . ')(:.*?)$/'; 
     310            // use initial WHERE clause, to limit records number if possible 
     311            if (!empty($where)) 
     312                $this->set_search_set($where); 
     313 
     314            // count result pages 
     315            $cnt   = $this->count(); 
     316            $pages = ceil($cnt / $this->page_size); 
     317            $scnt  = count($post_search); 
     318 
     319            // get (paged) result 
     320            for ($i=0; $i<$pages; $i++) { 
     321                $this->list_records(null, $i, true); 
     322                while ($row = $this->result->next()) { 
     323                    $id = $row[$this->primary_key]; 
     324                    $found = 0; 
     325                    foreach (preg_grep($regexp, array_keys($row)) as $col) { 
     326                        $pos     = strpos($col, ':'); 
     327                        $colname = $pos ? substr($col, 0, $pos) : $col; 
     328                        $search  = $post_search[$colname]; 
     329                        foreach ((array)$row[$col] as $value) { 
     330                            // composite field, e.g. address 
     331                            if (is_array($value)) { 
     332                                $value = implode($value); 
     333                            } 
     334                            if (($strict && $value == $search) 
     335                                || (!$strict && strpos(mb_strtolower($value), $search) !== false) 
     336                            ) { 
     337                                $found++; 
     338                                break; 
     339                            } 
     340                        } 
     341                    } 
     342                    // all fields match 
     343                    if ($found == $scnt) { 
     344                        $ids[] = $id; 
     345                    } 
     346                } 
     347            } 
     348 
     349            // build WHERE clause 
     350            $ids = $this->db->array2list($ids, 'integer'); 
     351            $where = 'c.' . $this->primary_key.' IN ('.$ids.')'; 
     352            unset($this->cache['count']); 
     353        } 
    281354 
    282355        if (!empty($where)) { 
     
    288361        } 
    289362 
    290         return $this->result;  
     363        return $this->result; 
    291364    } 
    292365 
  • trunk/roundcubemail/program/include/rcube_ldap.php

    r4823 r4834  
    452452    * Search contacts 
    453453    * 
    454     * @param array   List of fields to search in 
    455     * @param string  Search value 
    456     * @param boolean True for strict, False for partial (fuzzy) matching 
    457     * @param boolean True if results are requested, False if count only 
    458     * @param boolean (Not used) 
    459     * @param array   List of fields that cannot be empty 
     454    * @param mixed   $fields   The field name of array of field names to search in 
     455    * @param mixed   $value    Search value (or array of values when $fields is array) 
     456    * @param boolean $strict   True for strict, False for partial (fuzzy) matching 
     457    * @param boolean $select   True if results are requested, False if count only 
     458    * @param boolean $nocount  (Not used) 
     459    * @param array   $required List of fields that cannot be empty 
     460    * 
    460461    * @return array  Indexed list of contact records and 'count' value 
    461462    */ 
     
    478479        } 
    479480 
    480         $filter = '(|'; 
    481         $wc = !$strict && $this->prop['fuzzy_search'] ? '*' : ''; 
     481        // use AND operator for advanced searches 
     482        $filter = is_array($value) ? '(&' : '(|'; 
     483        $wc     = !$strict && $this->prop['fuzzy_search'] ? '*' : ''; 
     484 
    482485        if ($fields == '*') 
    483486        { 
     
    491494            if (is_array($this->prop['search_fields'])) 
    492495            { 
    493                 foreach ($this->prop['search_fields'] as $k => $field) 
     496                foreach ($this->prop['search_fields'] as $field) { 
    494497                    $filter .= "($field=$wc" . $this->_quote_string($value) . "$wc)"; 
     498                } 
    495499            } 
    496500        } 
    497501        else 
    498502        { 
    499             foreach ((array)$fields as $field) 
    500                 if ($f = $this->_map_field($field)) 
    501                     $filter .= "($f=$wc" . $this->_quote_string($value) . "$wc)"; 
     503            foreach ((array)$fields as $idx => $field) { 
     504                $val = is_array($value) ? $value[$idx] : $value; 
     505                if ($f = $this->_map_field($field)) { 
     506                    $filter .= "($f=$wc" . $this->_quote_string($val) . "$wc)"; 
     507                } 
     508            } 
    502509        } 
    503510        $filter .= ')'; 
  • trunk/roundcubemail/program/include/rcube_result_set.php

    r4410 r4834  
    4545        $this->records[] = $rec; 
    4646    } 
    47    
     47 
    4848    function iterate() 
    4949    { 
    5050        return $this->records[$this->current++]; 
    5151    } 
    52    
     52 
    5353    function first() 
    5454    { 
     
    5656        return $this->records[$this->current++]; 
    5757    } 
    58    
     58 
    5959    // alias for iterate() 
    6060    function next() 
     
    6262        return $this->iterate(); 
    6363    } 
    64    
     64 
    6565    function seek($i) 
    6666    { 
    6767        $this->current = $i; 
    6868    } 
    69    
     69 
    7070} 
  • trunk/roundcubemail/program/js/app.js

    r4826 r4834  
    327327        } 
    328328 
    329         if ((this.env.action == 'add' || this.env.action == 'edit') && this.gui_objects.editform) { 
     329        if (this.gui_objects.editform) { 
    330330          this.enable_command('save', true); 
    331           this.init_contact_form(); 
    332         } 
    333         else if (this.gui_objects.qsearchbox) { 
     331          if (this.env.action == 'add' || this.env.action == 'edit') 
     332              this.init_contact_form(); 
     333        } 
     334        if (this.gui_objects.qsearchbox) { 
    334335          this.enable_command('search', 'reset-search', 'moveto', true); 
    335336          $(this.gui_objects.qsearchbox).select(); 
     
    339340          this.enable_command('export', true); 
    340341 
    341         this.enable_command('list', 'listgroup', true); 
     342        this.enable_command('list', 'listgroup', 'advanced-search', true); 
    342343        break; 
    343344 
     
    588589      // common commands used in multiple tasks 
    589590      case 'show': 
    590         if (this.task=='mail') { 
     591        if (this.task == 'mail') { 
    591592          var uid = this.get_single_uid(); 
    592593          if (uid && (!this.env.uid || uid != this.env.uid)) { 
     
    597598          } 
    598599        } 
    599         else if (this.task=='addressbook') { 
     600        else if (this.task == 'addressbook') { 
    600601          var cid = props ? props : this.get_single_cid(); 
    601           if (cid && !(this.env.action=='show' && cid==this.env.cid)) 
     602          if (cid && !(this.env.action == 'show' && cid == this.env.cid)) 
    602603            this.load_contact(cid, 'show'); 
    603604        } 
     
    605606 
    606607      case 'add': 
    607         if (this.task=='addressbook') 
     608        if (this.task == 'addressbook') 
    608609          this.load_contact(0, 'add'); 
    609         else if (this.task=='settings') { 
     610        else if (this.task == 'settings') { 
    610611          this.identity_list.clear_selection(); 
    611612          this.load_identity(0, 'add-identity'); 
     
    626627 
    627628      case 'save': 
    628         if (this.gui_objects.editform) { 
    629           var input_pagesize = $("input[name='_pagesize']"); 
    630           var input_name  = $("input[name='_name']"); 
    631           var input_email = $("input[name='_email']"); 
    632  
     629        var input, form = this.gui_objects.editform; 
     630        if (form) { 
     631          // adv. search 
     632          if (this.env.action == 'search') { 
     633          } 
    633634          // user prefs 
    634           if (input_pagesize.length && isNaN(parseInt(input_pagesize.val()))) { 
     635          else if ((input = $("input[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) { 
    635636            alert(this.get_label('nopagesizewarning')); 
    636             input_pagesize.focus(); 
     637            input.focus(); 
    637638            break; 
    638639          } 
    639640          // contacts/identities 
    640641          else { 
    641             if (input_name.length && input_name.val() == '') { 
     642            if ((input = $("input[name='_name']", form)) &&input.length && input.val() == '') { 
    642643              alert(this.get_label('nonamewarning')); 
    643               input_name.focus(); 
     644              input.focus(); 
    644645              break; 
    645646            } 
    646             else if (this.task == 'settings' && input_email.length && (this.env.identities_level % 2) == 0 && !rcube_check_email(input_email.val())) { 
     647            else if (this.task == 'settings' && (this.env.identities_level % 2) == 0  && 
     648              (input = $("input[name='_email']", form)) && input.length&& !rcube_check_email(input.val()) 
     649            ) { 
    647650              alert(this.get_label('noemailwarning')); 
    648               input_email.focus(); 
     651              input.focus(); 
    649652              break; 
    650653            } 
     
    654657          } 
    655658 
    656           this.gui_objects.editform.submit(); 
     659          form.submit(); 
    657660        } 
    658661        break; 
     
    33493352          mods = mods[mbox] ? mods[mbox] : mods['*']; 
    33503353      } else if (this.contact_list) { 
    3351         this.contact_list.clear(true); 
    3352         this.show_contentframe(false); 
     3354        this.list_contacts_clear(); 
    33533355      } 
    33543356 
     
    37163718  { 
    37173719    // clear message list first 
     3720    this.list_contacts_clear(); 
     3721 
     3722    // send request to server 
     3723    var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : ''), 
     3724      lock = this.set_busy(true, 'loading'); 
     3725 
     3726    this.env.source = src; 
     3727    this.env.group = group; 
     3728 
     3729    if (group) 
     3730      url += '&_gid='+group; 
     3731 
     3732    // also send search request to get the right messages  
     3733    if (this.env.search_request)  
     3734      url += '&_search='+this.env.search_request; 
     3735 
     3736    this.http_request('list', url, lock); 
     3737  }; 
     3738 
     3739  this.list_contacts_clear = function() 
     3740  { 
    37183741    this.contact_list.clear(true); 
    37193742    this.show_contentframe(false); 
    37203743    this.enable_command('delete', 'compose', false); 
    3721  
    3722     // send request to server 
    3723     var url = (src ? '_source='+urlencode(src) : '') + (page ? (src?'&':'') + '_page='+page : ''), 
    3724       lock = this.set_busy(true, 'loading'); 
    3725  
    3726     this.env.source = src; 
    3727     this.env.group = group; 
    3728  
    3729     if (group) 
    3730       url += '&_gid='+group; 
    3731  
    3732     // also send search request to get the right messages  
    3733     if (this.env.search_request)  
    3734       url += '&_search='+this.env.search_request; 
    3735  
    3736     this.http_request('list', url, lock); 
    37373744  }; 
    37383745 
     
    40784085      var lastelem = $('.ff_'+col), 
    40794086        appendcontainer = $('#contactsection'+section+' .contactcontroller'+col); 
    4080        
     4087 
    40814088      if (!appendcontainer.length) 
    40824089        appendcontainer = $('<fieldset>').addClass('contactfieldgroup contactcontroller'+col).insertAfter($('#contactsection'+section+' .contactfieldgroup').last()); 
     
    40874094          cell = $('<div>').addClass('contactfieldcontent data'), 
    40884095          label = $('<div>').addClass('contactfieldlabel label'); 
    4089            
     4096 
    40904097        if (colprop.subtypes_select) 
    40914098          label.html(colprop.subtypes_select); 
     
    41214128            .attr('name', '_'+col+name_suffix) 
    41224129            .appendTo(cell); 
    4123            
     4130 
    41244131          var options = input.attr('options'); 
    41254132          options[options.length] = new Option('---', ''); 
     
    41354142            .click(function(){ ref.delete_edit_field(this); return false }) 
    41364143            .appendTo(cell); 
    4137            
     4144 
    41384145          row.append(label).append(cell).appendTo(appendcontainer.show()); 
    41394146          input.first().focus(); 
    4140            
     4147 
    41414148          // disable option if limit reached 
    41424149          if (!colprop.count) colprop.count = 0; 
     
    41544161      fieldset = $(elem).parents('fieldset.contactfieldgroup'), 
    41554162      addmenu = fieldset.parent().find('select.addfieldmenu'); 
    4156      
     4163 
    41574164    // just clear input but don't hide the last field 
    41584165    if (--colprop.count <= 0 && colprop.visible) 
     
    41644171        fieldset.hide(); 
    41654172    } 
    4166      
     4173 
    41674174    // enable option in add-field selector or insert it if necessary 
    41684175    if (addmenu.length) { 
     
    42124219    this.enable_command('upload-photo', this.env.coltypes.photo ? true : false); 
    42134220    this.enable_command('delete-photo', this.env.coltypes.photo && id != '-del-'); 
     4221  }; 
     4222 
     4223  // load advanced search page 
     4224  this.advanced_search = function() 
     4225  { 
     4226    var add_url = '&_form=1', target = window; 
     4227 
     4228    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { 
     4229      add_url += '&_framed=1'; 
     4230      target = window.frames[this.env.contentframe]; 
     4231      this.contact_list.clear_selection(); 
     4232    } 
     4233    else if (framed) 
     4234      return false; 
     4235 
     4236    this.location_href(this.env.comm_path+'&_action=search'+add_url 
     4237      +'&_source='+urlencode(this.env.source) 
     4238      +(this.env.group ? '&_gid='+urlencode(this.env.group) : ''), target); 
     4239 
     4240    return true; 
    42144241  }; 
    42154242 
  • trunk/roundcubemail/program/localization/en_US/labels.inc

    r4823 r4834  
    273273$labels['spouse'] = 'Spouse'; 
    274274$labels['allfields'] = 'All fields'; 
     275$labels['search'] = 'Search'; 
     276$labels['advsearch'] = 'Advanced Search'; 
     277$labels['other'] = 'Other'; 
    275278 
    276279$labels['typehome']   = 'Home'; 
     
    285288$labels['typevideo']  = 'Video'; 
    286289$labels['typeassistant']  = 'Assistant'; 
     290$labels['typehomepage']  = 'Home page'; 
    287291 
    288292$labels['addfield'] = 'Add field...'; 
  • trunk/roundcubemail/program/localization/pl_PL/labels.inc

    r4823 r4834  
    448448$labels['typevideo'] = 'Wideo'; 
    449449$labels['typeassistant'] = 'Asystent'; 
     450$labels['typehomepage'] = 'Strona domowa'; 
    450451$labels['addfield'] = 'Dodaj pole...'; 
    451452$labels['personalinfo'] = 'Informacje osobiste'; 
     
    460461$labels['spellcheckbeforesend'] = 'Przed wysłaniem wiadomości sprawdzaj pisownię'; 
    461462$labels['allfields'] = 'Wszystkie pola'; 
     463$labels['search'] = 'Szukaj'; 
     464$labels['advsearch'] = 'Zaawansowane wyszukiwanie'; 
     465$labels['other'] = 'Inne'; 
    462466 
    463467?> 
  • trunk/roundcubemail/program/steps/addressbook/func.inc

    r4823 r4834  
    6363// general definition of contact coltypes 
    6464$CONTACT_COLTYPES = array( 
    65   'name'         => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name')), 
    66   'firstname'    => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('firstname')), 
    67   'surname'      => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('surname')), 
    68   'middlename'   => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('middlename')), 
    69   'prefix'       => array('type' => 'text', 'size' => 8,  'limit' => 1, 'label' => rcube_label('nameprefix')), 
    70   'suffix'       => array('type' => 'text', 'size' => 8,  'limit' => 1, 'label' => rcube_label('namesuffix')), 
    71   'nickname'     => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('nickname')), 
    72   'jobtitle'     => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('jobtitle')), 
    73   'organization' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('organization')), 
    74   'department'   => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('department')), 
    75   'gender'       => array('type' => 'select', 'limit' => 1, 'label' => rcube_label('gender'), 'options' => array('male' => rcube_label('male'), 'female' => rcube_label('female'))), 
    76   'maidenname'   => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('maidenname')), 
    77   'email'        => array('type' => 'text', 'size' => 40, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other')), 
    78   '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')), 
     65  'name'         => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('name'), 'category' => 'main'), 
     66  'firstname'    => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('firstname'), 'category' => 'main'), 
     67  'surname'      => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('surname'), 'category' => 'main'), 
     68  'email'        => array('type' => 'text', 'size' => 40, 'label' => rcube_label('email'), 'subtypes' => array('home','work','other'), 'category' => 'main'), 
     69  'middlename'   => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('middlename'), 'category' => 'main'), 
     70  'prefix'       => array('type' => 'text', 'size' => 8,  'limit' => 1, 'label' => rcube_label('nameprefix'), 'category' => 'main'), 
     71  'suffix'       => array('type' => 'text', 'size' => 8,  'limit' => 1, 'label' => rcube_label('namesuffix'), 'category' => 'main'), 
     72  'nickname'     => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('nickname'), 'category' => 'main'), 
     73  'jobtitle'     => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('jobtitle'), 'category' => 'main'), 
     74  'organization' => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('organization'), 'category' => 'main'), 
     75  'department'   => array('type' => 'text', 'size' => 19, 'limit' => 1, 'label' => rcube_label('department'), 'category' => 'main'), 
     76  'gender'       => array('type' => 'select', 'limit' => 1, 'label' => rcube_label('gender'), 'options' => array('male' => rcube_label('male'), 'female' => rcube_label('female')), 'category' => 'personal'), 
     77  'maidenname'   => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('maidenname'), 'category' => 'personal'), 
     78  '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'), 
    7979  'address'      => array('type' => 'composite', 'label' => rcube_label('address'), 'subtypes' => array('home','work','other'), 'childs' => array( 
    80     'street'     => array('type' => 'text', 'size' => 40, 'label' => rcube_label('street')), 
    81     'locality'   => array('type' => 'text', 'size' => 28, 'label' => rcube_label('locality')), 
    82     'zipcode'    => array('type' => 'text', 'size' => 8, 'label' => rcube_label('zipcode')), 
    83     'region'     => array('type' => 'text', 'size' => 12, 'label' => rcube_label('region')), 
    84     'country'    => array('type' => 'text', 'size' => 40, 'label' => rcube_label('country')), 
    85   )), 
    86   'birthday'     => array('type' => 'date', 'size' => 12, 'label' => rcube_label('birthday'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), 
    87   'anniversary'  => array('type' => 'date', 'size' => 12, 'label' => rcube_label('anniversary'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col'), 
    88   'website'      => array('type' => 'text', 'size' => 40, 'label' => rcube_label('website'), 'subtypes' => array('homepage','work','blog','other')), 
    89   'im'           => array('type' => 'text', 'size' => 40, 'label' => rcube_label('instantmessenger'), 'subtypes' => array('aim','icq','msn','yahoo','jabber','skype','other')), 
     80    'street'     => array('type' => 'text', 'size' => 40, 'label' => rcube_label('street'), 'category' => 'main'), 
     81    'locality'   => array('type' => 'text', 'size' => 28, 'label' => rcube_label('locality'), 'category' => 'main'), 
     82    'zipcode'    => array('type' => 'text', 'size' => 8, 'label' => rcube_label('zipcode'), 'category' => 'main'), 
     83    'region'     => array('type' => 'text', 'size' => 12, 'label' => rcube_label('region'), 'category' => 'main'), 
     84    'country'    => array('type' => 'text', 'size' => 40, 'label' => rcube_label('country'), 'category' => 'main'), 
     85  ), 'category' => 'main'), 
     86  'birthday'     => array('type' => 'date', 'size' => 12, 'label' => rcube_label('birthday'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col', 'category' => 'personal'), 
     87  'anniversary'  => array('type' => 'date', 'size' => 12, 'label' => rcube_label('anniversary'), 'limit' => 1, 'render_func' => 'rcmail_format_date_col', 'category' => 'personal'), 
     88  'website'      => array('type' => 'text', 'size' => 40, 'label' => rcube_label('website'), 'subtypes' => array('homepage','work','blog','other'), 'category' => 'main'), 
     89  'im'           => array('type' => 'text', 'size' => 40, 'label' => rcube_label('instantmessenger'), 'subtypes' => array('aim','icq','msn','yahoo','jabber','skype','other'), 'category' => 'main'), 
    9090  'notes'        => array('type' => 'textarea', 'size' => 40, 'rows' => 15, 'label' => rcube_label('notes'), 'limit' => 1), 
    91   'photo'        => array('type' => 'image', 'limit' => 1), 
    92   'assistant'    => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('assistant')), 
    93   'manager'      => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('manager')), 
    94   'spouse'       => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('spouse')), 
     91  'photo'        => array('type' => 'image', 'limit' => 1, 'category' => 'main'), 
     92  'assistant'    => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('assistant'), 'category' => 'personal'), 
     93  'manager'      => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('manager'), 'category' => 'personal'), 
     94  'spouse'       => array('type' => 'text', 'size' => 40, 'limit' => 1, 'label' => rcube_label('spouse'), 'category' => 'personal'), 
    9595  // TODO: define fields for vcards like GEO, KEY 
    9696); 
     
    226226    // define list of cols to be displayed 
    227227    $a_show_cols = array('name'); 
    228    
     228 
    229229    while ($row = $result->next()) { 
    230230        $a_row_cols = array(); 
    231      
     231 
    232232        // format each col 
    233233        foreach ($a_show_cols as $col) 
     
    325325    // get default coltypes 
    326326    $coltypes = $GLOBALS['CONTACT_COLTYPES']; 
    327     $coltype_lables = array(); 
     327    $coltype_labels = array(); 
    328328     
    329329    foreach ($coltypes as $col => $prop) { 
     
    336336        if ($prop['childs']) { 
    337337            foreach ($prop['childs'] as $childcol => $cp) 
    338                 $coltype_lables[$childcol] = array('label' => $cp['label']); 
     338                $coltype_labels[$childcol] = array('label' => $cp['label']); 
    339339        } 
    340340    } 
     
    549549 
    550550    if ($edit_mode) { 
    551       $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_lables); 
     551      $RCMAIL->output->set_env('coltypes', $coltypes + $coltype_labels); 
    552552      $RCMAIL->output->set_env('delbutton', $del_button); 
    553553      $RCMAIL->output->add_label('delete'); 
  • trunk/roundcubemail/program/steps/addressbook/list.inc

    r4424 r4834  
    2929// create javascript list 
    3030rcmail_js_contacts_list($result); 
    31    
     31 
    3232// send response 
    3333$OUTPUT->send(); 
  • trunk/roundcubemail/program/steps/addressbook/search.inc

    r4823 r4834  
    77 | This file is part of the Roundcube Webmail client                     | 
    88 | Copyright (C) 2005-2011, The Roundcube Dev Team                       | 
     9 | Copyright (C) 2011, Kolab Systems AG                                  | 
    910 | Licensed under the GNU GPL                                            | 
    1011 |                                                                       | 
    1112 | PURPOSE:                                                              | 
    12  |   Search step for address book contacts                               | 
     13 |   Search action (and form) for address book contacts                  | 
    1314 |                                                                       | 
    1415 +-----------------------------------------------------------------------+ 
    1516 | Author: Thomas Bruederli <roundcube@gmail.com>                        | 
     17 | Author: Aleksander Machniak <machniak@kolabsys.com>                   | 
    1618 +-----------------------------------------------------------------------+ 
    1719 
     
    2022*/ 
    2123 
    22 $CONTACTS->set_page(1); 
    23 $_SESSION['page'] = 1; 
    24  
    25 // get input 
    26 $search = trim(get_input_value('_q', RCUBE_INPUT_GET, true)); 
    27 $fields = explode(',', get_input_value('_headers', RCUBE_INPUT_GET)); 
    28  
    29 if (empty($fields)) { 
    30     $fields = $SEARCH_MODS_DEFAULT; 
     24if (!isset($_GET['_form'])) { 
     25    rcmail_contact_search(); 
    3126} 
    3227 
    33 $search_request = md5('addr'.$search.implode($fields, ',')); 
     28$OUTPUT->add_handler('searchform', 'rcmail_contact_search_form'); 
     29$OUTPUT->send('contactsearch'); 
    3430 
    35 // update search_mods setting 
    36 $search_mods = array_fill_keys($fields, 1); 
    37 $RCMAIL->user->save_prefs(array('addressbook_search_mods' => $search_mods)); 
    3831 
    39 if ($fields['all'] || count($fields) == count($SEARCH_MODS_DEFAULT)) { 
    40     $fields = '*'; 
     32function rcmail_contact_search() 
     33{ 
     34    global $RCMAIL, $OUTPUT, $CONTACTS, $CONTACT_COLTYPES, $SEARCH_MODS_DEFAULT; 
     35 
     36    $adv = isset($_POST['_adv']); 
     37 
     38    // get fields/values from advanced search form 
     39    if ($adv) { 
     40        foreach ($CONTACT_COLTYPES as $col => $colprop) { 
     41            $s = trim(get_input_value('_'.$col, RCUBE_INPUT_POST, true)); 
     42            if (strlen($s)) { 
     43                $search[] = $s; 
     44                $fields[] = $col; 
     45            } 
     46        } 
     47 
     48        if (empty($fields)) { 
     49            // do nothing, show the form again 
     50            return; 
     51        } 
     52    } 
     53    // quick-search 
     54    else { 
     55        $search = trim(get_input_value('_q', RCUBE_INPUT_GET, true)); 
     56        $fields = explode(',', get_input_value('_headers', RCUBE_INPUT_GET)); 
     57 
     58        if (empty($fields)) { 
     59            $fields = $SEARCH_MODS_DEFAULT; 
     60        } 
     61 
     62        // update search_mods setting 
     63        $old_mods = $RCMAIL->config->get('addressbook_search_mods'); 
     64        $search_mods = array_fill_keys($fields, 1); 
     65        if ($old_mods != $search_mods) { 
     66            $RCMAIL->user->save_prefs(array('addressbook_search_mods' => $search_mods)); 
     67        } 
     68 
     69        if ($fields['*'] || count($fields) == count($SEARCH_MODS_DEFAULT)) { 
     70            $fields = '*'; 
     71        } 
     72    } 
     73 
     74    // search request ID 
     75    $search_request = md5('addr'.implode($fields, ',') 
     76        .(is_array($search) ? implode($search, ',') : $search)); 
     77 
     78    // reset page 
     79    $CONTACTS->set_page(1); 
     80    $_SESSION['page'] = 1; 
     81 
     82    // get contacts for this user 
     83    $result = $CONTACTS->search($fields, $search); 
     84 
     85    // save search settings in session 
     86    $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); 
     87 
     88    if ($adv) 
     89        $OUTPUT->command('list_contacts_clear'); 
     90 
     91    if ($result->count > 0) { 
     92        // create javascript list 
     93        rcmail_js_contacts_list($result); 
     94    } 
     95    else { 
     96        $OUTPUT->show_message('nocontactsfound', 'notice'); 
     97    } 
     98 
     99    // update message count display 
     100    $OUTPUT->command('set_env', 'search_request', $search_request); 
     101    $OUTPUT->command('set_env', 'pagecount', ceil($result->count / $CONTACTS->page_size)); 
     102    $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text()); 
     103 
     104    // send response 
     105    $OUTPUT->send($adv ? 'iframe' : null); 
    41106} 
    42107 
    43 // get contacts for this user 
    44 $result = $CONTACTS->search($fields, $search); 
     108function rcmail_contact_search_form($attrib) 
     109{ 
     110    global $RCMAIL, $CONTACTS, $CONTACT_COLTYPES; 
    45111 
    46 // save search settings in session 
    47 $_SESSION['search'][$search_request] = $CONTACTS->get_search_set(); 
     112    $i_size = !empty($attrib['size']) ? $attrib['size'] : 30; 
    48113 
    49 if ($result->count > 0) { 
    50     // create javascript list 
    51     rcmail_js_contacts_list($result); 
     114    $form = array( 
     115        'main' => array( 
     116            'name'    => rcube_label('contactproperties'), 
     117            'content' => array( 
     118            ), 
     119        ), 
     120        'personal' => array( 
     121            'name'    => rcube_label('personalinfo'), 
     122            'content' => array( 
     123            ), 
     124        ), 
     125        'other' => array( 
     126            'name'    => rcube_label('other'), 
     127            'content' => array( 
     128            ), 
     129        ), 
     130    ); 
     131 
     132    foreach ($CONTACT_COLTYPES as $col => $colprop) 
     133    { 
     134        if ($colprop['type'] != 'image' && !$colprop['nosearch']) 
     135        { 
     136            $ftype    = $colprop['type'] == 'select' ? 'select' : 'text'; 
     137            $label    = isset($colprop['label']) ? $colprop['label'] : rcube_label($col); 
     138            $category = $colprop['category'] ? $colprop['category'] : 'other'; 
     139 
     140            if ($ftype == 'text') 
     141                $colprop['size'] = $i_size; 
     142 
     143            $content  = html::div('row', html::div('contactfieldlabel label', Q($label)) 
     144                . html::div('contactfieldcontent', rcmail_get_edit_field($col, '', $colprop, $ftype))); 
     145 
     146            $form[$category]['content'][] = $content; 
     147        } 
     148    } 
     149 
     150    $hiddenfields = new html_hiddenfield(array( 
     151        'name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); 
     152    $hiddenfields->add(array('name' => '_gid', 'value' => $CONTACTS->group_id)); 
     153    $hiddenfields->add(array('name' => '_adv', 'value' => 1)); 
     154 
     155    $out = $RCMAIL->output->request_form(array( 
     156        'name' => 'form', 'method' => 'post', 
     157        'task' => $RCMAIL->task, 'action' => 'search', 
     158        'noclose' => true) + $attrib, $hiddenfields->show()); 
     159 
     160    $RCMAIL->output->add_gui_object('editform', $attrib['id']); 
     161 
     162    unset($attrib['name']); 
     163    unset($attrib['id']); 
     164 
     165    foreach ($form as $f) { 
     166        if (!empty($f['content'])) { 
     167            $content = html::div('contactfieldgroup', join("\n", $f['content'])); 
     168 
     169            $out .= html::tag('fieldset', $attrib, 
     170                html::tag('legend', null, Q($f['name'])) 
     171                . $content) . "\n"; 
     172        } 
     173    } 
     174 
     175    return $out . '</form>'; 
    52176} 
    53 else { 
    54   $OUTPUT->show_message('nocontactsfound', 'notice'); 
    55 } 
    56  
    57 // update message count display 
    58 $OUTPUT->set_env('search_request', $search_request); 
    59 $OUTPUT->set_env('pagecount', ceil($result->count / $CONTACTS->page_size)); 
    60 $OUTPUT->command('set_rowcount', rcmail_get_rowcount_text()); 
    61  
    62 // send response 
    63 $OUTPUT->send(); 
  • trunk/roundcubemail/skins/default/addressbook.css

    r4528 r4834  
    7373} 
    7474 
     75#abooktoolbar a.search { 
     76  background-position: -170px 0; 
     77} 
     78 
     79#abooktoolbar a.searchSel { 
     80  background-position: -170px -32px; 
     81} 
     82 
    7583#abookcountbar 
    7684{ 
     
    209217} 
    210218 
    211 #contact-details table td.title 
    212 { 
    213   font-weight: bold; 
    214   text-align: right; 
    215 } 
    216  
    217219#contacttabs 
    218220{ 
     
    336338        top: 0; 
    337339        left: 2px; 
    338         width: 90px; 
     340        width: 110px; 
    339341        white-space: nowrap; 
    340342        overflow: hidden; 
     
    356358.contactfieldgroup .contactfieldcontent 
    357359{ 
    358         padding-left: 100px; 
     360        padding-left: 120px; 
    359361        min-height: 1em; 
    360362        line-height: 1.3em; 
  • trunk/roundcubemail/skins/default/templates/addressbook.html

    r4823 r4834  
    2626<roundcube:button command="import" type="link" class="buttonPas import" classAct="button import" classSel="button importSel" title="importcontacts" content=" " /> 
    2727<roundcube:button command="export" type="link" class="buttonPas export" classAct="button export" classSel="button exportSel" title="exportvcards" content=" " /> 
     28<roundcube:button command="advanced-search" type="link" class="buttonPas search" classAct="button search" classSel="button searchSel" title="advsearch" content=" " /> 
    2829<roundcube:container name="toolbar" id="abooktoolbar" /> 
    2930</div> 
     
    4243    <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> 
    4344    <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> 
    44 <!-- 
    45     <li class="separator_below"> 
    46     <li><roundcube:button command="advsearch" type="link" label="advsearch" style="padding-left: 0" classAct="active" /></li> 
    47 --> 
    4845  </ul> 
    4946</div> 
  • trunk/roundcubemail/skins/default/templates/contactadd.html

    r4424 r4834  
    2020  <roundcube:object name="contactedithead" id="contacthead" size="16" form="editform" /> 
    2121  <div style="clear:both"></div> 
    22    
    2322  <div id="contacttabs"> 
    2423    <roundcube:object name="contacteditform" size="40" textareacols="60" deleteIcon="/images/icons/delete.png" form="editform" /> 
Note: See TracChangeset for help on using the changeset viewer.