source: github/program/include/rcube_addressbook.php @ 041c93c

HEADdev-browser-capabilitiespdo
Last change on this file since 041c93c was 041c93c, checked in by Aleksander Machniak <alec@…>, 12 months ago

Removed $Id$

  • Property mode set to 100644
File size: 16.1 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/rcube_addressbook.php                                 |
6 |                                                                       |
7 | This file is part of the Roundcube Webmail client                     |
8 | Copyright (C) 2006-2012, The Roundcube Dev Team                       |
9 |                                                                       |
10 | Licensed under the GNU General Public License version 3 or            |
11 | any later version with exceptions for skins & plugins.                |
12 | See the README file for a full license statement.                     |
13 |                                                                       |
14 | PURPOSE:                                                              |
15 |   Interface to the local address book database                        |
16 |                                                                       |
17 +-----------------------------------------------------------------------+
18 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
19 +-----------------------------------------------------------------------+
20*/
21
22
23/**
24 * Abstract skeleton of an address book/repository
25 *
26 * @package Addressbook
27 */
28abstract class rcube_addressbook
29{
30    /** constants for error reporting **/
31    const ERROR_READ_ONLY = 1;
32    const ERROR_NO_CONNECTION = 2;
33    const ERROR_VALIDATE = 3;
34    const ERROR_SAVING = 4;
35    const ERROR_SEARCH = 5;
36
37    /** public properties (mandatory) */
38    public $primary_key;
39    public $groups = false;
40    public $readonly = true;
41    public $searchonly = false;
42    public $undelete = false;
43    public $ready = false;
44    public $group_id = null;
45    public $list_page = 1;
46    public $page_size = 10;
47    public $sort_col = 'name';
48    public $sort_order = 'ASC';
49    public $coltypes = array('name' => array('limit'=>1), 'firstname' => array('limit'=>1), 'surname' => array('limit'=>1), 'email' => array('limit'=>1));
50
51    protected $error;
52
53    /**
54     * Returns addressbook name (e.g. for addressbooks listing)
55     */
56    abstract function get_name();
57
58    /**
59     * Save a search string for future listings
60     *
61     * @param mixed Search params to use in listing method, obtained by get_search_set()
62     */
63    abstract function set_search_set($filter);
64
65    /**
66     * Getter for saved search properties
67     *
68     * @return mixed Search properties used by this class
69     */
70    abstract function get_search_set();
71
72    /**
73     * Reset saved results and search parameters
74     */
75    abstract function reset();
76
77    /**
78     * Refresh saved search set after data has changed
79     *
80     * @return mixed New search set
81     */
82    function refresh_search()
83    {
84        return $this->get_search_set();
85    }
86
87    /**
88     * List the current set of contact records
89     *
90     * @param  array  List of cols to show
91     * @param  int    Only return this number of records, use negative values for tail
92     * @return array  Indexed list of contact records, each a hash array
93     */
94    abstract function list_records($cols=null, $subset=0);
95
96    /**
97     * Search records
98     *
99     * @param array   List of fields to search in
100     * @param string  Search value
101     * @param int     Matching mode:
102     *                0 - partial (*abc*),
103     *                1 - strict (=),
104     *                2 - prefix (abc*)
105     * @param boolean True if results are requested, False if count only
106     * @param boolean True to skip the count query (select only)
107     * @param array   List of fields that cannot be empty
108     * @return object rcube_result_set List of contact records and 'count' value
109     */
110    abstract function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array());
111
112    /**
113     * Count number of available contacts in database
114     *
115     * @return rcube_result_set Result set with values for 'count' and 'first'
116     */
117    abstract function count();
118
119    /**
120     * Return the last result set
121     *
122     * @return rcube_result_set Current result set or NULL if nothing selected yet
123     */
124    abstract function get_result();
125
126    /**
127     * Get a specific contact record
128     *
129     * @param mixed record identifier(s)
130     * @param boolean True to return record as associative array, otherwise a result set is returned
131     *
132     * @return mixed Result object with all record fields or False if not found
133     */
134    abstract function get_record($id, $assoc=false);
135
136    /**
137     * Returns the last error occured (e.g. when updating/inserting failed)
138     *
139     * @return array Hash array with the following fields: type, message
140     */
141    function get_error()
142    {
143      return $this->error;
144    }
145
146    /**
147     * Setter for errors for internal use
148     *
149     * @param int Error type (one of this class' error constants)
150     * @param string Error message (name of a text label)
151     */
152    protected function set_error($type, $message)
153    {
154      $this->error = array('type' => $type, 'message' => $message);
155    }
156
157    /**
158     * Close connection to source
159     * Called on script shutdown
160     */
161    function close() { }
162
163    /**
164     * Set internal list page
165     *
166     * @param  number  Page number to list
167     * @access public
168     */
169    function set_page($page)
170    {
171        $this->list_page = (int)$page;
172    }
173
174    /**
175     * Set internal page size
176     *
177     * @param  number  Number of messages to display on one page
178     * @access public
179     */
180    function set_pagesize($size)
181    {
182        $this->page_size = (int)$size;
183    }
184
185    /**
186     * Set internal sort settings
187     *
188     * @param string $sort_col Sort column
189     * @param string $sort_order Sort order
190     */
191    function set_sort_order($sort_col, $sort_order = null)
192    {
193        if ($sort_col != null && ($this->coltypes[$sort_col] || in_array($sort_col, $this->coltypes))) {
194            $this->sort_col = $sort_col;
195        }
196        if ($sort_order != null) {
197            $this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
198        }
199    }
200
201    /**
202     * Check the given data before saving.
203     * If input isn't valid, the message to display can be fetched using get_error()
204     *
205     * @param array Assoziative array with data to save
206     * @param boolean Attempt to fix/complete record automatically
207     * @return boolean True if input is valid, False if not.
208     */
209    public function validate(&$save_data, $autofix = false)
210    {
211        $rcmail = rcmail::get_instance();
212
213        // check validity of email addresses
214        foreach ($this->get_col_values('email', $save_data, true) as $email) {
215            if (strlen($email)) {
216                if (!rcube_utils::check_email(rcube_utils::idn_to_ascii($email))) {
217                    $error = $rcmail->gettext(array('name' => 'emailformaterror', 'vars' => array('email' => $email)));
218                    $this->set_error(self::ERROR_VALIDATE, $error);
219                    return false;
220                }
221            }
222        }
223
224        return true;
225    }
226
227
228    /**
229     * Create a new contact record
230     *
231     * @param array Assoziative array with save data
232     *  Keys:   Field name with optional section in the form FIELD:SECTION
233     *  Values: Field value. Can be either a string or an array of strings for multiple values
234     * @param boolean True to check for duplicates first
235     * @return mixed The created record ID on success, False on error
236     */
237    function insert($save_data, $check=false)
238    {
239        /* empty for read-only address books */
240    }
241
242    /**
243     * Create new contact records for every item in the record set
244     *
245     * @param object rcube_result_set Recordset to insert
246     * @param boolean True to check for duplicates first
247     * @return array List of created record IDs
248     */
249    function insertMultiple($recset, $check=false)
250    {
251        $ids = array();
252        if (is_object($recset) && is_a($recset, rcube_result_set)) {
253            while ($row = $recset->next()) {
254                if ($insert = $this->insert($row, $check))
255                    $ids[] = $insert;
256            }
257        }
258        return $ids;
259    }
260
261    /**
262     * Update a specific contact record
263     *
264     * @param mixed Record identifier
265     * @param array Assoziative array with save data
266     *  Keys:   Field name with optional section in the form FIELD:SECTION
267     *  Values: Field value. Can be either a string or an array of strings for multiple values
268     * @return boolean True on success, False on error
269     */
270    function update($id, $save_cols)
271    {
272        /* empty for read-only address books */
273    }
274
275    /**
276     * Mark one or more contact records as deleted
277     *
278     * @param array  Record identifiers
279     * @param bool   Remove records irreversible (see self::undelete)
280     */
281    function delete($ids, $force=true)
282    {
283        /* empty for read-only address books */
284    }
285
286    /**
287     * Unmark delete flag on contact record(s)
288     *
289     * @param array  Record identifiers
290     */
291    function undelete($ids)
292    {
293        /* empty for read-only address books */
294    }
295
296    /**
297     * Mark all records in database as deleted
298     */
299    function delete_all()
300    {
301        /* empty for read-only address books */
302    }
303
304    /**
305     * Setter for the current group
306     * (empty, has to be re-implemented by extending class)
307     */
308    function set_group($gid) { }
309
310    /**
311     * List all active contact groups of this source
312     *
313     * @param string  Optional search string to match group name
314     * @return array  Indexed list of contact groups, each a hash array
315     */
316    function list_groups($search = null)
317    {
318        /* empty for address books don't supporting groups */
319        return array();
320    }
321
322    /**
323     * Get group properties such as name and email address(es)
324     *
325     * @param string Group identifier
326     * @return array Group properties as hash array
327     */
328    function get_group($group_id)
329    {
330        /* empty for address books don't supporting groups */
331        return null;
332    }
333
334    /**
335     * Create a contact group with the given name
336     *
337     * @param string The group name
338     * @return mixed False on error, array with record props in success
339     */
340    function create_group($name)
341    {
342        /* empty for address books don't supporting groups */
343        return false;
344    }
345
346    /**
347     * Delete the given group and all linked group members
348     *
349     * @param string Group identifier
350     * @return boolean True on success, false if no data was changed
351     */
352    function delete_group($gid)
353    {
354        /* empty for address books don't supporting groups */
355        return false;
356    }
357
358    /**
359     * Rename a specific contact group
360     *
361     * @param string Group identifier
362     * @param string New name to set for this group
363     * @param string New group identifier (if changed, otherwise don't set)
364     * @return boolean New name on success, false if no data was changed
365     */
366    function rename_group($gid, $newname, &$newid)
367    {
368        /* empty for address books don't supporting groups */
369        return false;
370    }
371
372    /**
373     * Add the given contact records the a certain group
374     *
375     * @param string  Group identifier
376     * @param array   List of contact identifiers to be added
377     * @return int    Number of contacts added
378     */
379    function add_to_group($group_id, $ids)
380    {
381        /* empty for address books don't supporting groups */
382        return 0;
383    }
384
385    /**
386     * Remove the given contact records from a certain group
387     *
388     * @param string  Group identifier
389     * @param array   List of contact identifiers to be removed
390     * @return int    Number of deleted group members
391     */
392    function remove_from_group($group_id, $ids)
393    {
394        /* empty for address books don't supporting groups */
395        return 0;
396    }
397
398    /**
399     * Get group assignments of a specific contact record
400     *
401     * @param mixed Record identifier
402     *
403     * @return array List of assigned groups as ID=>Name pairs
404     * @since 0.5-beta
405     */
406    function get_record_groups($id)
407    {
408        /* empty for address books don't supporting groups */
409        return array();
410    }
411
412
413    /**
414     * Utility function to return all values of a certain data column
415     * either as flat list or grouped by subtype
416     *
417     * @param string Col name
418     * @param array  Record data array as used for saving
419     * @param boolean True to return one array with all values, False for hash array with values grouped by type
420     * @return array List of column values
421     */
422    function get_col_values($col, $data, $flat = false)
423    {
424        $out = array();
425        foreach ($data as $c => $values) {
426            if ($c === $col || strpos($c, $col.':') === 0) {
427                if ($flat) {
428                    $out = array_merge($out, (array)$values);
429                }
430                else {
431                    list($f, $type) = explode(':', $c);
432                    $out[$type] = array_merge((array)$out[$type], (array)$values);
433                }
434            }
435        }
436
437        return $out;
438    }
439
440
441    /**
442     * Normalize the given string for fulltext search.
443     * Currently only optimized for Latin-1 characters; to be extended
444     *
445     * @param string Input string (UTF-8)
446     * @return string Normalized string
447     * @deprecated since 0.9-beta
448     */
449    protected static function normalize_string($str)
450    {
451        return rcbe_utils::normalize_string($str);
452    }
453
454    /**
455     * Compose a valid display name from the given structured contact data
456     *
457     * @param array  Hash array with contact data as key-value pairs
458     * @param bool   Don't attempt to extract components from the email address
459     *
460     * @return string Display name
461     */
462    public static function compose_display_name($contact, $full_email = false)
463    {
464        $contact = rcmail::get_instance()->plugins->exec_hook('contact_displayname', $contact);
465        $fn = $contact['name'];
466
467        if (!$fn)  // default display name composition according to vcard standard
468            $fn = join(' ', array_filter(array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix'])));
469
470        // use email address part for name
471        $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email'];
472
473        if ($email && (empty($fn) || $fn == $email)) {
474            // return full email
475            if ($full_email)
476                return $email;
477
478            list($emailname) = explode('@', $email);
479            if (preg_match('/(.*)[\.\-\_](.*)/', $emailname, $match))
480                $fn = trim(ucfirst($match[1]).' '.ucfirst($match[2]));
481            else
482                $fn = ucfirst($emailname);
483        }
484
485        return $fn;
486    }
487
488
489    /**
490     * Compose the name to display in the contacts list for the given contact record.
491     * This respects the settings parameter how to list conacts.
492     *
493     * @param array  Hash array with contact data as key-value pairs
494     * @return string List name
495     */
496    public static function compose_list_name($contact)
497    {
498        static $compose_mode;
499
500        if (!isset($compose_mode))  // cache this
501            $compose_mode = rcmail::get_instance()->config->get('addressbook_name_listing', 0);
502
503        if ($compose_mode == 3)
504            $fn = join(' ', array($contact['surname'] . ',', $contact['firstname'], $contact['middlename']));
505        else if ($compose_mode == 2)
506            $fn = join(' ', array($contact['surname'], $contact['firstname'], $contact['middlename']));
507        else if ($compose_mode == 1)
508            $fn = join(' ', array($contact['firstname'], $contact['middlename'], $contact['surname']));
509        else
510            $fn = !empty($contact['name']) ? $contact['name'] : join(' ', array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix']));
511
512        $fn = trim($fn, ', ');
513
514        // fallback to display name
515        if (empty($fn) && $contact['name'])
516            $fn = $contact['name'];
517
518        // fallback to email address
519        $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email'];
520        if (empty($fn) && $email)
521            return $email;
522
523        return $fn;
524    }
525
526}
527
Note: See TracBrowser for help on using the repository browser.