2011-05-21 12:25:39 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Backend class for a custom address book
|
|
|
|
*
|
|
|
|
* This part of the Roundcube+Kolab integration and connects the
|
2012-03-07 11:02:51 +01:00
|
|
|
* rcube_addressbook interface with the kolab_storage wrapper from libkolab
|
2011-05-21 12:25:39 +02:00
|
|
|
*
|
2011-10-27 10:20:46 +02:00
|
|
|
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
|
|
|
* @author Aleksander Machniak <machniak@kolabsys.com>
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as
|
|
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
|
|
* License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
2011-05-21 12:25:39 +02:00
|
|
|
* @see rcube_addressbook
|
|
|
|
*/
|
|
|
|
class rcube_kolab_contacts extends rcube_addressbook
|
|
|
|
{
|
|
|
|
public $primary_key = 'ID';
|
2011-06-24 20:13:43 +02:00
|
|
|
public $readonly = true;
|
|
|
|
public $editable = false;
|
2011-07-08 17:39:03 +02:00
|
|
|
public $undelete = true;
|
2011-07-05 11:41:22 +02:00
|
|
|
public $groups = true;
|
2011-05-21 12:25:39 +02:00
|
|
|
public $coltypes = array(
|
|
|
|
'name' => array('limit' => 1),
|
|
|
|
'firstname' => array('limit' => 1),
|
|
|
|
'surname' => array('limit' => 1),
|
|
|
|
'middlename' => array('limit' => 1),
|
|
|
|
'prefix' => array('limit' => 1),
|
|
|
|
'suffix' => array('limit' => 1),
|
|
|
|
'nickname' => array('limit' => 1),
|
|
|
|
'jobtitle' => array('limit' => 1),
|
|
|
|
'organization' => array('limit' => 1),
|
|
|
|
'department' => array('limit' => 1),
|
2013-03-21 10:02:35 +01:00
|
|
|
'email' => array('subtypes' => array('home','work','other')),
|
2011-05-21 12:25:39 +02:00
|
|
|
'phone' => array(),
|
2012-03-21 16:15:53 +01:00
|
|
|
'address' => array('subtypes' => array('home','work','office')),
|
2012-04-21 18:23:11 +02:00
|
|
|
'website' => array('subtypes' => array('homepage','blog')),
|
2012-03-07 11:02:51 +01:00
|
|
|
'im' => array('subtypes' => null),
|
2011-05-21 12:25:39 +02:00
|
|
|
'gender' => array('limit' => 1),
|
|
|
|
'birthday' => array('limit' => 1),
|
|
|
|
'anniversary' => array('limit' => 1),
|
2011-08-12 17:35:21 +02:00
|
|
|
'profession' => array('type' => 'text', 'size' => 40, 'maxlength' => 80, 'limit' => 1,
|
2011-06-03 15:28:42 +02:00
|
|
|
'label' => 'kolab_addressbook.profession', 'category' => 'personal'),
|
2012-03-20 23:51:43 +01:00
|
|
|
'manager' => array('limit' => null),
|
|
|
|
'assistant' => array('limit' => null),
|
2011-05-21 12:25:39 +02:00
|
|
|
'spouse' => array('limit' => 1),
|
2012-03-20 23:51:43 +01:00
|
|
|
'children' => array('type' => 'text', 'size' => 40, 'maxlength' => 80, 'limit' => null,
|
2011-06-03 15:28:42 +02:00
|
|
|
'label' => 'kolab_addressbook.children', 'category' => 'personal'),
|
|
|
|
'freebusyurl' => array('type' => 'text', 'size' => 40, 'limit' => 1,
|
|
|
|
'label' => 'kolab_addressbook.freebusyurl'),
|
2012-03-21 16:15:53 +01:00
|
|
|
'pgppublickey' => array('type' => 'textarea', 'size' => 70, 'rows' => 10, 'limit' => 1,
|
|
|
|
'label' => 'kolab_addressbook.pgppublickey'),
|
2012-04-25 14:12:09 +02:00
|
|
|
'pkcs7publickey' => array('type' => 'textarea', 'size' => 70, 'rows' => 10, 'limit' => 1,
|
|
|
|
'label' => 'kolab_addressbook.pkcs7publickey'),
|
2013-02-20 09:36:04 +01:00
|
|
|
'notes' => array('limit' => 1),
|
|
|
|
'photo' => array('limit' => 1),
|
2012-04-25 14:12:09 +02:00
|
|
|
// TODO: define more Kolab-specific fields such as: language, latitude, longitude, crypto settings
|
2011-05-21 12:25:39 +02:00
|
|
|
);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-08-03 11:22:12 +02:00
|
|
|
/**
|
|
|
|
* vCard additional fields mapping
|
|
|
|
*/
|
|
|
|
public $vcard_map = array(
|
|
|
|
'profession' => 'X-PROFESSION',
|
|
|
|
'officelocation' => 'X-OFFICE-LOCATION',
|
|
|
|
'initials' => 'X-INITIALS',
|
|
|
|
'children' => 'X-CHILDREN',
|
|
|
|
'freebusyurl' => 'X-FREEBUSY-URL',
|
|
|
|
'pgppublickey' => 'KEY',
|
|
|
|
);
|
|
|
|
|
2013-01-07 15:31:08 +01:00
|
|
|
/**
|
|
|
|
* List of date type fields
|
|
|
|
*/
|
|
|
|
public $date_cols = array('birthday', 'anniversary');
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
private $gid;
|
2011-07-04 14:57:00 +02:00
|
|
|
private $storagefolder;
|
2014-02-06 17:33:05 +01:00
|
|
|
private $dataset;
|
|
|
|
private $sortindex;
|
2011-05-21 12:25:39 +02:00
|
|
|
private $contacts;
|
|
|
|
private $distlists;
|
|
|
|
private $groupmembers;
|
|
|
|
private $filter;
|
|
|
|
private $result;
|
2011-06-24 20:13:43 +02:00
|
|
|
private $namespace;
|
2011-05-21 12:25:39 +02:00
|
|
|
private $imap_folder = 'INBOX/Contacts';
|
2012-03-21 16:15:53 +01:00
|
|
|
private $action;
|
2011-05-21 12:25:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
public function __construct($imap_folder = null)
|
|
|
|
{
|
2012-01-23 10:16:30 +01:00
|
|
|
if ($imap_folder) {
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->imap_folder = $imap_folder;
|
2012-01-23 10:16:30 +01:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// extend coltypes configuration
|
2012-03-07 11:02:51 +01:00
|
|
|
$format = kolab_format::factory('contact');
|
|
|
|
$this->coltypes['phone']['subtypes'] = array_keys($format->phonetypes);
|
|
|
|
$this->coltypes['address']['subtypes'] = array_keys($format->addresstypes);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// set localized labels for proprietary cols
|
|
|
|
foreach ($this->coltypes as $col => $prop) {
|
|
|
|
if (is_string($prop['label']))
|
|
|
|
$this->coltypes[$col]['label'] = rcube_label($prop['label']);
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// fetch objects from the given IMAP folder
|
2012-03-07 11:02:51 +01:00
|
|
|
$this->storagefolder = kolab_storage::get_folder($this->imap_folder);
|
2012-03-22 20:22:26 +01:00
|
|
|
$this->ready = $this->storagefolder && !PEAR::isError($this->storagefolder);
|
2011-06-24 20:13:43 +02:00
|
|
|
|
|
|
|
// Set readonly and editable flags according to folder permissions
|
|
|
|
if ($this->ready) {
|
2012-03-07 11:02:51 +01:00
|
|
|
if ($this->storagefolder->get_owner() == $_SESSION['username']) {
|
2011-06-24 20:13:43 +02:00
|
|
|
$this->editable = true;
|
|
|
|
$this->readonly = false;
|
|
|
|
}
|
|
|
|
else {
|
2012-04-21 18:23:11 +02:00
|
|
|
$rights = $this->storagefolder->get_myrights();
|
2011-07-29 15:08:00 +02:00
|
|
|
if (!PEAR::isError($rights)) {
|
|
|
|
if (strpos($rights, 'i') !== false)
|
2011-07-25 12:33:44 +02:00
|
|
|
$this->readonly = false;
|
2011-07-29 15:08:00 +02:00
|
|
|
if (strpos($rights, 'a') !== false || strpos($rights, 'x') !== false)
|
2011-07-25 12:33:44 +02:00
|
|
|
$this->editable = true;
|
|
|
|
}
|
2011-06-24 20:13:43 +02:00
|
|
|
}
|
|
|
|
}
|
2012-03-21 16:15:53 +01:00
|
|
|
|
2012-10-17 11:54:25 +02:00
|
|
|
$this->action = rcube::get_instance()->action;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter for the address book name to be displayed
|
|
|
|
*
|
|
|
|
* @return string Name of this address book
|
|
|
|
*/
|
|
|
|
public function get_name()
|
|
|
|
{
|
2012-03-07 11:02:51 +01:00
|
|
|
$folder = kolab_storage::object_name($this->imap_folder, $this->namespace);
|
2011-06-24 20:13:43 +02:00
|
|
|
return $folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter for the IMAP folder name
|
|
|
|
*
|
|
|
|
* @return string Name of the IMAP folder
|
|
|
|
*/
|
|
|
|
public function get_realname()
|
|
|
|
{
|
|
|
|
return $this->imap_folder;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter for the name of the namespace to which the IMAP folder belongs
|
|
|
|
*
|
|
|
|
* @return string Name of the namespace (personal, other, shared)
|
|
|
|
*/
|
|
|
|
public function get_namespace()
|
|
|
|
{
|
2012-03-22 20:22:26 +01:00
|
|
|
if ($this->namespace === null && $this->ready) {
|
2012-03-07 11:02:51 +01:00
|
|
|
$this->namespace = $this->storagefolder->get_namespace();
|
2011-06-24 20:13:43 +02:00
|
|
|
}
|
|
|
|
|
2011-06-28 10:02:33 +02:00
|
|
|
return $this->namespace;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2013-10-03 12:44:41 +02:00
|
|
|
/**
|
|
|
|
* Compose an URL for CardDAV access to this address book (if configured)
|
|
|
|
*/
|
|
|
|
public function get_carddav_url()
|
|
|
|
{
|
|
|
|
$url = null;
|
|
|
|
$rcmail = rcmail::get_instance();
|
|
|
|
if ($template = $rcmail->config->get('kolab_addressbook_carddav_url', null)) {
|
|
|
|
return strtr($template, array(
|
|
|
|
'%h' => $_SERVER['HTTP_HOST'],
|
|
|
|
'%u' => urlencode($rcmail->get_user_name()),
|
|
|
|
'%i' => urlencode($this->storagefolder->get_uid()),
|
|
|
|
'%n' => urlencode($this->imap_folder),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Setter for the current group
|
|
|
|
*/
|
|
|
|
public function set_group($gid)
|
|
|
|
{
|
|
|
|
$this->gid = $gid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save a search string for future listings
|
|
|
|
*
|
|
|
|
* @param mixed Search params to use in listing method, obtained by get_search_set()
|
|
|
|
*/
|
|
|
|
public function set_search_set($filter)
|
|
|
|
{
|
|
|
|
$this->filter = $filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Getter for saved search properties
|
|
|
|
*
|
|
|
|
* @return mixed Search properties used by this class
|
|
|
|
*/
|
|
|
|
public function get_search_set()
|
|
|
|
{
|
|
|
|
return $this->filter;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset saved results and search parameters
|
|
|
|
*/
|
|
|
|
public function reset()
|
|
|
|
{
|
|
|
|
$this->result = null;
|
|
|
|
$this->filter = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List all active contact groups of this source
|
|
|
|
*
|
|
|
|
* @param string Optional search string to match group name
|
|
|
|
* @return array Indexed list of contact groups, each a hash array
|
|
|
|
*/
|
|
|
|
function list_groups($search = null)
|
|
|
|
{
|
|
|
|
$this->_fetch_groups();
|
|
|
|
$groups = array();
|
2013-07-03 12:16:13 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ((array)$this->distlists as $group) {
|
2012-03-07 11:02:51 +01:00
|
|
|
if (!$search || strstr(strtolower($group['name']), strtolower($search)))
|
|
|
|
$groups[$group['name']] = array('ID' => $group['ID'], 'name' => $group['name']);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-07-05 20:11:19 +02:00
|
|
|
|
|
|
|
// sort groups
|
|
|
|
ksort($groups, SORT_LOCALE_STRING);
|
|
|
|
|
|
|
|
return array_values($groups);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* List the current set of contact records
|
|
|
|
*
|
2013-05-08 14:22:18 +02:00
|
|
|
* @param array List of cols to show
|
|
|
|
* @param int Only return this number of records, use negative values for tail
|
2014-02-06 17:33:05 +01:00
|
|
|
* @param boolean True to skip the count query (select only)
|
2013-05-07 12:16:11 +02:00
|
|
|
*
|
2011-05-21 12:25:39 +02:00
|
|
|
* @return array Indexed list of contact records, each a hash array
|
|
|
|
*/
|
2014-02-06 17:33:05 +01:00
|
|
|
public function list_records($cols = null, $subset = 0, $nocount = false)
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
2013-07-01 11:18:43 +02:00
|
|
|
$this->result = new rcube_result_set(0, ($this->list_page-1) * $this->page_size);
|
2012-03-07 11:02:51 +01:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// list member of the selected group
|
|
|
|
if ($this->gid) {
|
2012-03-08 21:23:55 +01:00
|
|
|
$this->_fetch_groups();
|
2013-07-03 12:16:13 +02:00
|
|
|
|
2014-02-06 17:33:05 +01:00
|
|
|
$this->sortindex = array();
|
|
|
|
$this->contacts = array();
|
|
|
|
$local_sortindex = array();
|
|
|
|
$uids = array();
|
2013-07-03 12:16:13 +02:00
|
|
|
|
|
|
|
// get members with email specified
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ((array)$this->distlists[$this->gid]['member'] as $member) {
|
|
|
|
// skip member that don't match the search filter
|
2013-07-03 12:16:13 +02:00
|
|
|
if (!empty($this->filter['ids']) && array_search($member['ID'], $this->filter['ids']) === false) {
|
2011-05-21 12:25:39 +02:00
|
|
|
continue;
|
2013-07-03 12:16:13 +02:00
|
|
|
}
|
2011-08-02 13:18:48 +02:00
|
|
|
|
2013-07-03 12:16:13 +02:00
|
|
|
if (!empty($member['uid'])) {
|
|
|
|
$uids[] = $member['uid'];
|
|
|
|
}
|
2014-02-06 17:33:05 +01:00
|
|
|
else if (!empty($member['email'])) {
|
|
|
|
$this->contacts[$member['ID']] = $member;
|
|
|
|
$local_sortindex[$member['ID']] = $this->_sort_string($member);
|
|
|
|
}
|
2013-07-03 12:16:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// get members by UID
|
|
|
|
if (!empty($uids)) {
|
2014-02-06 17:33:05 +01:00
|
|
|
$this->_fetch_contacts(array(array('uid', '=', $uids)));
|
|
|
|
$this->sortindex = array_merge($this->sortindex, $local_sortindex);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
2012-05-16 10:49:53 +02:00
|
|
|
else if (is_array($this->filter['ids'])) {
|
|
|
|
$ids = $this->filter['ids'];
|
2013-09-09 12:40:00 +02:00
|
|
|
if (count($ids)) {
|
2013-07-01 11:18:43 +02:00
|
|
|
$uids = array_map(array($this, 'id2uid'), $this->filter['ids']);
|
|
|
|
$this->_fetch_contacts(array(array('uid', '=', $uids)));
|
|
|
|
}
|
2012-05-16 10:49:53 +02:00
|
|
|
}
|
2012-03-07 11:02:51 +01:00
|
|
|
else {
|
|
|
|
$this->_fetch_contacts();
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2014-02-06 17:33:05 +01:00
|
|
|
// sort results (index only)
|
|
|
|
asort($this->sortindex, SORT_LOCALE_STRING);
|
|
|
|
$ids = array_keys($this->sortindex);
|
2011-07-28 11:41:44 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// fill contact data into the current result set
|
2014-02-06 17:33:05 +01:00
|
|
|
$this->result->count = count($ids);
|
2011-05-21 12:25:39 +02:00
|
|
|
$start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first;
|
2014-02-06 17:33:05 +01:00
|
|
|
$last_row = min($subset != 0 ? $start_row + abs($subset) : $this->result->first + $this->page_size, $this->result->count);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
for ($i = $start_row; $i < $last_row; $i++) {
|
2014-02-06 17:33:05 +01:00
|
|
|
if (array_key_exists($i, $ids)) {
|
|
|
|
$idx = $ids[$i];
|
|
|
|
$this->result->add($this->contacts[$idx] ?: $this->_to_rcube_contact($this->dataset[$idx]));
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $this->result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Search records
|
|
|
|
*
|
2011-06-03 15:28:42 +02:00
|
|
|
* @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)
|
2011-11-11 14:50:08 +01:00
|
|
|
* @param int $mode Matching mode:
|
|
|
|
* 0 - partial (*abc*),
|
|
|
|
* 1 - strict (=),
|
|
|
|
* 2 - prefix (abc*)
|
2011-06-03 15:28:42 +02:00
|
|
|
* @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
|
|
|
|
*
|
2011-05-21 12:25:39 +02:00
|
|
|
* @return object rcube_result_set List of contact records and 'count' value
|
|
|
|
*/
|
2011-11-11 14:50:08 +01:00
|
|
|
public function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array())
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
|
|
|
// search by ID
|
|
|
|
if ($fields == $this->primary_key) {
|
2011-06-13 15:53:16 +02:00
|
|
|
$ids = !is_array($value) ? explode(',', $value) : $value;
|
|
|
|
$result = new rcube_result_set();
|
|
|
|
|
|
|
|
foreach ($ids as $id) {
|
|
|
|
if ($rec = $this->get_record($id, true)) {
|
|
|
|
$result->add($rec);
|
|
|
|
$result->count++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $result;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-05-31 12:04:08 +02:00
|
|
|
else if ($fields == '*') {
|
|
|
|
$fields = array_keys($this->coltypes);
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
if (!is_array($fields))
|
|
|
|
$fields = array($fields);
|
|
|
|
if (!is_array($required) && !empty($required))
|
|
|
|
$required = array($required);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-06-03 15:28:42 +02:00
|
|
|
// advanced search
|
|
|
|
if (is_array($value)) {
|
|
|
|
$advanced = true;
|
|
|
|
$value = array_map('mb_strtolower', $value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
$value = mb_strtolower($value);
|
|
|
|
|
|
|
|
$scount = count($fields);
|
|
|
|
// build key name regexp
|
|
|
|
$regexp = '/^(' . implode($fields, '|') . ')(?:.*)$/';
|
|
|
|
|
2012-05-16 10:10:51 +02:00
|
|
|
// pass query to storage if only indexed cols are involved
|
|
|
|
// NOTE: this is only some rough pre-filtering but probably includes false positives
|
2013-08-07 12:30:54 +02:00
|
|
|
$squery = $this->_search_query($fields, $value, $mode);
|
2012-05-16 10:10:51 +02:00
|
|
|
|
2014-01-28 15:51:09 +01:00
|
|
|
// add magic selector to select contacts with birthday dates only
|
|
|
|
if (in_array('birthday', $required)) {
|
|
|
|
$squery[] = array('tags', '=', 'x-has-birthday');
|
|
|
|
}
|
|
|
|
|
2012-05-16 10:10:51 +02:00
|
|
|
// get all/matching records
|
|
|
|
$this->_fetch_contacts($squery);
|
|
|
|
|
2011-06-03 15:28:42 +02:00
|
|
|
// save searching conditions
|
2011-11-11 14:50:08 +01:00
|
|
|
$this->filter = array('fields' => $fields, 'value' => $value, 'mode' => $mode, 'ids' => array());
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2014-02-06 17:33:05 +01:00
|
|
|
// search by iterating over all records in dataset
|
|
|
|
foreach ($this->dataset as $i => $record) {
|
|
|
|
$contact = $this->_to_rcube_contact($record);
|
|
|
|
$id = $contact['ID'];
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// check if current contact has required values, otherwise skip it
|
|
|
|
if ($required) {
|
2013-08-07 12:30:54 +02:00
|
|
|
foreach ($required as $f) {
|
|
|
|
// required field might be 'email', but contact might contain 'email:home'
|
|
|
|
if (!($v = rcube_addressbook::get_col_values($f, $contact, true)) || empty($v)) {
|
2011-05-21 12:25:39 +02:00
|
|
|
continue 2;
|
2013-08-07 12:30:54 +02:00
|
|
|
}
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-06-03 15:28:42 +02:00
|
|
|
$found = array();
|
|
|
|
foreach (preg_grep($regexp, array_keys($contact)) as $col) {
|
2013-01-07 15:31:08 +01:00
|
|
|
$pos = strpos($col, ':');
|
|
|
|
$colname = $pos ? substr($col, 0, $pos) : $col;
|
|
|
|
$search = $advanced ? $value[array_search($colname, $fields)] : $value;
|
2011-06-03 15:28:42 +02:00
|
|
|
|
|
|
|
foreach ((array)$contact[$col] as $val) {
|
2013-01-07 15:31:08 +01:00
|
|
|
if ($this->compare_search_value($colname, $val, $search, $mode)) {
|
2011-06-03 15:28:42 +02:00
|
|
|
if (!$advanced) {
|
|
|
|
$this->filter['ids'][] = $id;
|
|
|
|
break 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$found[$colname] = true;
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-03 15:28:42 +02:00
|
|
|
|
|
|
|
if (count($found) >= $scount) // && $advanced
|
|
|
|
$this->filter['ids'][] = $id;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2013-09-09 12:40:00 +02:00
|
|
|
// dummy result with contacts count
|
|
|
|
if (!$select) {
|
|
|
|
return new rcube_result_set(count($this->filter['ids']), ($this->list_page-1) * $this->page_size);
|
|
|
|
}
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// list records (now limited by $this->filter)
|
|
|
|
return $this->list_records();
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Refresh saved search results after data has changed
|
|
|
|
*/
|
|
|
|
public function refresh_search()
|
|
|
|
{
|
|
|
|
if ($this->filter)
|
2011-11-11 14:50:08 +01:00
|
|
|
$this->search($this->filter['fields'], $this->filter['value'], $this->filter['mode']);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $this->get_search_set();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Count number of available contacts in database
|
|
|
|
*
|
|
|
|
* @return rcube_result_set Result set with values for 'count' and 'first'
|
|
|
|
*/
|
|
|
|
public function count()
|
|
|
|
{
|
2012-03-07 11:02:51 +01:00
|
|
|
if ($this->gid) {
|
|
|
|
$this->_fetch_groups();
|
|
|
|
$count = count($this->distlists[$this->gid]['member']);
|
|
|
|
}
|
|
|
|
else if (is_array($this->filter['ids'])) {
|
|
|
|
$count = count($this->filter['ids']);
|
|
|
|
}
|
|
|
|
else {
|
2013-10-15 12:36:10 +02:00
|
|
|
$count = $this->storagefolder->count('contact');
|
2012-03-07 11:02:51 +01:00
|
|
|
}
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the last result set
|
|
|
|
*
|
|
|
|
* @return rcube_result_set Current result set or NULL if nothing selected yet
|
|
|
|
*/
|
|
|
|
public function get_result()
|
|
|
|
{
|
|
|
|
return $this->result;
|
|
|
|
}
|
|
|
|
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Get a specific contact record
|
|
|
|
*
|
|
|
|
* @param mixed record identifier(s)
|
|
|
|
* @param boolean True to return record as associative array, otherwise a result set is returned
|
|
|
|
* @return mixed Result object with all record fields or False if not found
|
|
|
|
*/
|
|
|
|
public function get_record($id, $assoc=false)
|
|
|
|
{
|
2012-03-20 23:51:43 +01:00
|
|
|
$rec = null;
|
2013-07-01 11:18:43 +02:00
|
|
|
$uid = $this->id2uid($id);
|
2012-03-20 23:51:43 +01:00
|
|
|
if (strpos($uid, 'mailto:') === 0) {
|
|
|
|
$this->_fetch_groups(true);
|
|
|
|
$rec = $this->contacts[$id];
|
|
|
|
$this->readonly = true; // set source to read-only
|
|
|
|
}
|
|
|
|
else if ($object = $this->storagefolder->get_object($uid)) {
|
2012-03-07 11:02:51 +01:00
|
|
|
$rec = $this->_to_rcube_contact($object);
|
2012-03-20 23:51:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($rec) {
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->result = new rcube_result_set(1);
|
2012-03-07 11:02:51 +01:00
|
|
|
$this->result->add($rec);
|
|
|
|
return $assoc ? $rec : $this->result;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get group assignments of a specific contact record
|
|
|
|
*
|
|
|
|
* @param mixed Record identifier
|
|
|
|
* @return array List of assigned groups as ID=>Name pairs
|
|
|
|
*/
|
|
|
|
public function get_record_groups($id)
|
|
|
|
{
|
|
|
|
$out = array();
|
|
|
|
$this->_fetch_groups();
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ((array)$this->groupmembers[$id] as $gid) {
|
|
|
|
if ($group = $this->distlists[$gid])
|
2012-03-07 11:02:51 +01:00
|
|
|
$out[$gid] = $group['name'];
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $out;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new contact record
|
|
|
|
*
|
|
|
|
* @param array Assoziative array with save data
|
|
|
|
* Keys: Field name with optional section in the form FIELD:SECTION
|
|
|
|
* Values: Field value. Can be either a string or an array of strings for multiple values
|
|
|
|
* @param boolean True to check for duplicates first
|
|
|
|
* @return mixed The created record ID on success, False on error
|
|
|
|
*/
|
|
|
|
public function insert($save_data, $check=false)
|
|
|
|
{
|
|
|
|
if (!is_array($save_data))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$insert_id = $existing = false;
|
|
|
|
|
|
|
|
// check for existing records by e-mail comparison
|
|
|
|
if ($check) {
|
|
|
|
foreach ($this->get_col_values('email', $save_data, true) as $email) {
|
|
|
|
if (($res = $this->search('email', $email, true, false)) && $res->count) {
|
|
|
|
$existing = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
if (!$existing) {
|
2012-10-17 11:27:54 +02:00
|
|
|
// remove existing id attributes (#1101)
|
|
|
|
unset($save_data['ID'], $save_data['uid']);
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// generate new Kolab contact item
|
|
|
|
$object = $this->_from_rcube_contact($save_data);
|
2012-12-31 17:41:30 +01:00
|
|
|
$saved = $this->storagefolder->save($object, 'contact');
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$saved) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
2011-05-21 12:25:39 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2012-03-08 21:23:55 +01:00
|
|
|
'message' => "Error saving contact object to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
else {
|
2014-02-06 17:33:05 +01:00
|
|
|
$insert_id = $this->uid2id($object['uid']);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $insert_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update a specific contact record
|
|
|
|
*
|
|
|
|
* @param mixed Record identifier
|
|
|
|
* @param array Assoziative array with save data
|
|
|
|
* Keys: Field name with optional section in the form FIELD:SECTION
|
|
|
|
* Values: Field value. Can be either a string or an array of strings for multiple values
|
|
|
|
* @return boolean True on success, False on error
|
|
|
|
*/
|
|
|
|
public function update($id, $save_data)
|
|
|
|
{
|
|
|
|
$updated = false;
|
2013-07-01 11:18:43 +02:00
|
|
|
if ($old = $this->storagefolder->get_object($this->id2uid($id))) {
|
2012-03-14 18:51:38 +01:00
|
|
|
$object = $this->_from_rcube_contact($save_data, $old);
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2012-03-21 11:10:17 +01:00
|
|
|
if (!$this->storagefolder->save($object, 'contact', $old['uid'])) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
2011-05-21 12:25:39 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2012-03-07 11:02:51 +01:00
|
|
|
'message' => "Error saving contact object to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$updated = true;
|
2012-12-31 17:41:30 +01:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
// TODO: update data in groups this contact is member of
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $updated;
|
|
|
|
}
|
|
|
|
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Mark one or more contact records as deleted
|
|
|
|
*
|
2011-07-08 17:39:03 +02:00
|
|
|
* @param array Record identifiers
|
|
|
|
* @param boolean Remove record(s) irreversible (mark as deleted otherwise)
|
|
|
|
*
|
|
|
|
* @return int Number of records deleted
|
2011-05-21 12:25:39 +02:00
|
|
|
*/
|
2011-07-08 17:39:03 +02:00
|
|
|
public function delete($ids, $force=true)
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
|
|
|
$this->_fetch_groups();
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
if (!is_array($ids))
|
|
|
|
$ids = explode(',', $ids);
|
|
|
|
|
|
|
|
$count = 0;
|
|
|
|
foreach ($ids as $id) {
|
2013-07-01 11:18:43 +02:00
|
|
|
if ($uid = $this->id2uid($id)) {
|
2012-03-20 23:51:43 +01:00
|
|
|
$is_mailto = strpos($uid, 'mailto:') === 0;
|
|
|
|
$deleted = $is_mailto || $this->storagefolder->delete($uid, $force);
|
2011-07-08 11:38:26 +02:00
|
|
|
|
2012-03-07 11:02:51 +01:00
|
|
|
if (!$deleted) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
2011-07-08 11:38:26 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2012-03-20 23:51:43 +01:00
|
|
|
'message' => "Error deleting a contact object $uid from the Kolab server"),
|
2011-07-08 11:38:26 +02:00
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// remove from distribution lists
|
2012-03-20 23:51:43 +01:00
|
|
|
foreach ((array)$this->groupmembers[$id] as $gid) {
|
|
|
|
if (!$is_mailto || $gid == $this->gid)
|
|
|
|
$this->remove_from_group($gid, $id);
|
|
|
|
}
|
2011-07-07 14:01:25 +02:00
|
|
|
|
2011-07-08 11:38:26 +02:00
|
|
|
// clear internal cache
|
2014-02-06 17:33:05 +01:00
|
|
|
unset($this->groupmembers[$id]);
|
2011-07-08 11:38:26 +02:00
|
|
|
$count++;
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $count;
|
|
|
|
}
|
|
|
|
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-07-08 17:39:03 +02:00
|
|
|
/**
|
|
|
|
* Undelete one or more contact records.
|
|
|
|
* Only possible just after delete (see 2nd argument of delete() method).
|
|
|
|
*
|
|
|
|
* @param array Record identifiers
|
|
|
|
*
|
|
|
|
* @return int Number of records restored
|
|
|
|
*/
|
|
|
|
public function undelete($ids)
|
|
|
|
{
|
|
|
|
if (!is_array($ids))
|
|
|
|
$ids = explode(',', $ids);
|
|
|
|
|
2012-03-14 13:57:02 +01:00
|
|
|
$count = 0;
|
|
|
|
foreach ($ids as $id) {
|
2013-07-01 11:18:43 +02:00
|
|
|
$uid = $this->id2uid($id);
|
2012-03-14 13:57:02 +01:00
|
|
|
if ($this->storagefolder->undelete($uid)) {
|
|
|
|
$count++;
|
2011-07-08 17:39:03 +02:00
|
|
|
}
|
|
|
|
else {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
2011-07-08 17:39:03 +02:00
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
2012-03-14 13:57:02 +01:00
|
|
|
'message' => "Error undeleting a contact object $uid from the Kolab server"),
|
2011-07-08 17:39:03 +02:00
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
}
|
2012-03-14 13:57:02 +01:00
|
|
|
|
|
|
|
return $count;
|
2011-07-08 17:39:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Remove all records from the database
|
|
|
|
*/
|
|
|
|
public function delete_all()
|
|
|
|
{
|
2012-03-07 11:02:51 +01:00
|
|
|
if ($this->storagefolder->delete_all()) {
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->contacts = array();
|
2014-02-06 17:33:05 +01:00
|
|
|
$this->sortindex = array();
|
|
|
|
$this->dataset = null;
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->result = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Close connection to source
|
|
|
|
* Called on script shutdown
|
|
|
|
*/
|
|
|
|
public function close()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a contact group with the given name
|
|
|
|
*
|
|
|
|
* @param string The group name
|
|
|
|
* @return mixed False on error, array with record props in success
|
|
|
|
*/
|
|
|
|
function create_group($name)
|
|
|
|
{
|
|
|
|
$this->_fetch_groups();
|
|
|
|
$result = false;
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
$list = array(
|
2012-03-07 11:02:51 +01:00
|
|
|
'name' => $name,
|
2011-05-21 12:25:39 +02:00
|
|
|
'member' => array(),
|
|
|
|
);
|
2012-03-07 11:02:51 +01:00
|
|
|
$saved = $this->storagefolder->save($list, 'distribution-list');
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$saved) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
|
|
|
'message' => "Error saving distribution-list object to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else {
|
2013-07-01 11:18:43 +02:00
|
|
|
$id = $this->uid2id($list['uid']);
|
2011-11-04 09:35:59 +01:00
|
|
|
$this->distlists[$id] = $list;
|
2011-07-08 11:38:26 +02:00
|
|
|
$result = array('id' => $id, 'name' => $name);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete the given group and all linked group members
|
|
|
|
*
|
|
|
|
* @param string Group identifier
|
|
|
|
* @return boolean True on success, false if no data was changed
|
|
|
|
*/
|
|
|
|
function delete_group($gid)
|
|
|
|
{
|
|
|
|
$this->_fetch_groups();
|
|
|
|
$result = false;
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-11-06 11:46:15 +01:00
|
|
|
if ($list = $this->distlists[$gid]) {
|
2012-03-07 11:02:51 +01:00
|
|
|
$deleted = $this->storagefolder->delete($list['uid']);
|
2012-11-06 11:46:15 +01:00
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$deleted) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
|
|
|
'message' => "Error deleting distribution-list object from the Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
}
|
2012-11-06 11:46:15 +01:00
|
|
|
else {
|
2011-05-21 12:25:39 +02:00
|
|
|
$result = true;
|
2012-11-06 11:46:15 +01:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rename a specific contact group
|
|
|
|
*
|
|
|
|
* @param string Group identifier
|
|
|
|
* @param string New name to set for this group
|
|
|
|
* @return boolean New name on success, false if no data was changed
|
|
|
|
*/
|
|
|
|
function rename_group($gid, $newname)
|
|
|
|
{
|
|
|
|
$this->_fetch_groups();
|
|
|
|
$list = $this->distlists[$gid];
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-03-07 11:02:51 +01:00
|
|
|
if ($newname != $list['name']) {
|
|
|
|
$list['name'] = $newname;
|
|
|
|
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$saved) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
|
|
|
'message' => "Error saving distribution-list object to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $newname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the given contact records the a certain group
|
|
|
|
*
|
|
|
|
* @param string Group identifier
|
|
|
|
* @param array List of contact identifiers to be added
|
|
|
|
* @return int Number of contacts added
|
|
|
|
*/
|
|
|
|
function add_to_group($gid, $ids)
|
|
|
|
{
|
2013-07-03 12:16:13 +02:00
|
|
|
if (!is_array($ids)) {
|
2011-05-21 12:25:39 +02:00
|
|
|
$ids = explode(',', $ids);
|
2013-07-03 12:16:13 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-03-20 23:51:43 +01:00
|
|
|
$this->_fetch_groups(true);
|
2013-07-03 12:16:13 +02:00
|
|
|
|
|
|
|
$list = $this->distlists[$gid];
|
|
|
|
$added = 0;
|
|
|
|
$uids = array();
|
|
|
|
$exists = array();
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2013-05-07 12:16:11 +02:00
|
|
|
foreach ((array)$list['member'] as $member) {
|
2011-05-21 12:25:39 +02:00
|
|
|
$exists[] = $member['ID'];
|
2013-05-07 12:16:11 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// substract existing assignments from list
|
2013-07-03 12:16:13 +02:00
|
|
|
$ids = array_unique(array_diff($ids, $exists));
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2013-07-03 12:16:13 +02:00
|
|
|
// add mailto: members
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ($ids as $contact_id) {
|
2013-07-01 11:18:43 +02:00
|
|
|
$uid = $this->id2uid($contact_id);
|
2013-07-03 12:16:13 +02:00
|
|
|
if (strpos($uid, 'mailto:') === 0 && ($contact = $this->contacts[$contact_id])) {
|
2012-03-20 23:51:43 +01:00
|
|
|
$list['member'][] = array(
|
2013-07-03 12:16:13 +02:00
|
|
|
'email' => $contact['email'],
|
|
|
|
'name' => $contact['name'],
|
2012-03-20 23:51:43 +01:00
|
|
|
);
|
|
|
|
$this->groupmembers[$contact_id][] = $gid;
|
|
|
|
$added++;
|
|
|
|
}
|
2013-07-03 12:16:13 +02:00
|
|
|
else {
|
|
|
|
$uids[$uid] = $contact_id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// add members with UID
|
|
|
|
if (!empty($uids)) {
|
|
|
|
foreach ($uids as $uid => $contact_id) {
|
|
|
|
$list['member'][] = array('uid' => $uid);
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->groupmembers[$contact_id][] = $gid;
|
|
|
|
$added++;
|
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
if ($added)
|
2012-03-07 11:02:51 +01:00
|
|
|
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
2012-05-30 14:46:57 +02:00
|
|
|
else
|
|
|
|
$saved = true;
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$saved) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
|
|
|
'message' => "Error saving distribution-list to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
$added = false;
|
2012-05-30 14:46:57 +02:00
|
|
|
$this->set_error(self::ERROR_SAVING, 'errorsaving');
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
$this->distlists[$gid] = $list;
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
return $added;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove the given contact records from a certain group
|
|
|
|
*
|
|
|
|
* @param string Group identifier
|
|
|
|
* @param array List of contact identifiers to be removed
|
|
|
|
* @return int Number of deleted group members
|
|
|
|
*/
|
|
|
|
function remove_from_group($gid, $ids)
|
|
|
|
{
|
|
|
|
if (!is_array($ids))
|
|
|
|
$ids = explode(',', $ids);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->_fetch_groups();
|
|
|
|
if (!($list = $this->distlists[$gid]))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
$new_member = array();
|
|
|
|
foreach ((array)$list['member'] as $member) {
|
|
|
|
if (!in_array($member['ID'], $ids))
|
|
|
|
$new_member[] = $member;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write distribution list back to server
|
|
|
|
$list['member'] = $new_member;
|
2012-03-07 11:02:51 +01:00
|
|
|
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']);
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-03-08 21:23:55 +01:00
|
|
|
if (!$saved) {
|
2012-11-06 11:46:15 +01:00
|
|
|
rcube::raise_error(array(
|
|
|
|
'code' => 600, 'type' => 'php',
|
|
|
|
'file' => __FILE__, 'line' => __LINE__,
|
|
|
|
'message' => "Error saving distribution-list object to Kolab server"),
|
2011-05-21 12:25:39 +02:00
|
|
|
true, false);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// remove group assigments in local cache
|
|
|
|
foreach ($ids as $id) {
|
|
|
|
$j = array_search($gid, $this->groupmembers[$id]);
|
|
|
|
unset($this->groupmembers[$id][$j]);
|
|
|
|
}
|
|
|
|
$this->distlists[$gid] = $list;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-07-24 10:56:47 +02:00
|
|
|
/**
|
|
|
|
* Check the given data before saving.
|
|
|
|
* If input not valid, the message to display can be fetched using get_error()
|
|
|
|
*
|
|
|
|
* @param array Associative array with contact data to save
|
|
|
|
*
|
|
|
|
* @return boolean True if input is valid, False if not.
|
|
|
|
*/
|
2013-09-10 15:25:15 +02:00
|
|
|
public function validate(&$save_data)
|
2011-07-24 10:56:47 +02:00
|
|
|
{
|
|
|
|
// validate e-mail addresses
|
|
|
|
$valid = parent::validate($save_data);
|
|
|
|
|
|
|
|
// require at least one e-mail address (syntax check is already done)
|
|
|
|
if ($valid) {
|
|
|
|
if (!strlen($save_data['name'])
|
|
|
|
&& !array_filter($this->get_col_values('email', $save_data, true))
|
|
|
|
) {
|
|
|
|
$this->set_error('warning', 'kolab_addressbook.noemailnamewarning');
|
|
|
|
$valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $valid;
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
|
|
|
|
/**
|
2012-05-16 10:10:51 +02:00
|
|
|
* Query storage layer and store records in private member var
|
2011-05-21 12:25:39 +02:00
|
|
|
*/
|
2012-05-16 10:10:51 +02:00
|
|
|
private function _fetch_contacts($query = array())
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
2014-02-06 17:33:05 +01:00
|
|
|
if (!isset($this->dataset) || !empty($query)) {
|
|
|
|
$this->sortindex = array();
|
|
|
|
$this->dataset = $this->storagefolder->select($query);
|
|
|
|
foreach ($this->dataset as $idx => $record) {
|
2011-07-20 15:54:04 +02:00
|
|
|
$contact = $this->_to_rcube_contact($record);
|
2014-02-06 17:33:05 +01:00
|
|
|
$this->sortindex[$idx] = $this->_sort_string($contact);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-02-06 17:33:05 +01:00
|
|
|
* Extract a string for sorting from the given contact record
|
2011-05-21 12:25:39 +02:00
|
|
|
*/
|
2014-02-06 17:33:05 +01:00
|
|
|
private function _sort_string($rec)
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
2014-02-06 17:33:05 +01:00
|
|
|
$str = '';
|
2012-02-11 14:32:10 +01:00
|
|
|
|
|
|
|
switch ($this->sort_col) {
|
|
|
|
case 'name':
|
2014-02-06 17:33:05 +01:00
|
|
|
$str = $rec['name'] . $rec['prefix'];
|
2012-02-11 14:32:10 +01:00
|
|
|
case 'firstname':
|
2014-02-06 17:33:05 +01:00
|
|
|
$str .= $rec['firstname'] . $rec['middlename'] . $rec['surname'];
|
2012-02-11 14:32:10 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'surname':
|
2014-02-06 17:33:05 +01:00
|
|
|
$str = $rec['surname'] . $rec['firstname'] . $rec['middlename'];
|
2012-02-11 14:32:10 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2014-02-06 17:33:05 +01:00
|
|
|
$str = $rec[$this->sort_col];
|
2012-02-11 14:32:10 +01:00
|
|
|
break;
|
2012-02-08 15:09:55 +01:00
|
|
|
}
|
|
|
|
|
2014-02-06 17:33:05 +01:00
|
|
|
$str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email'];
|
|
|
|
return mb_strtolower($str);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Read distribution-lists AKA groups from server
|
|
|
|
*/
|
2012-03-20 23:51:43 +01:00
|
|
|
private function _fetch_groups($with_contacts = false)
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
|
|
|
if (!isset($this->distlists)) {
|
|
|
|
$this->distlists = $this->groupmembers = array();
|
2014-02-06 17:33:05 +01:00
|
|
|
foreach ($this->storagefolder->get_objects('distribution-list') as $record) {
|
2013-07-01 11:18:43 +02:00
|
|
|
$record['ID'] = $this->uid2id($record['uid']);
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ((array)$record['member'] as $i => $member) {
|
2013-07-01 11:18:43 +02:00
|
|
|
$mid = $this->uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']);
|
2011-05-21 12:25:39 +02:00
|
|
|
$record['member'][$i]['ID'] = $mid;
|
2012-03-20 23:51:43 +01:00
|
|
|
$record['member'][$i]['readonly'] = empty($member['uid']);
|
2011-05-21 12:25:39 +02:00
|
|
|
$this->groupmembers[$mid][] = $record['ID'];
|
2012-03-20 23:51:43 +01:00
|
|
|
|
|
|
|
if ($with_contacts && empty($member['uid']))
|
|
|
|
$this->contacts[$mid] = $record['member'][$i];
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
$this->distlists[$record['ID']] = $record;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2012-03-07 11:02:51 +01:00
|
|
|
/**
|
|
|
|
* Encode object UID into a safe identifier
|
|
|
|
*/
|
2013-07-01 11:18:43 +02:00
|
|
|
public function uid2id($uid)
|
2012-03-07 11:02:51 +01:00
|
|
|
{
|
|
|
|
return rtrim(strtr(base64_encode($uid), '+/', '-_'), '=');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert Roundcube object identifier back into the original UID
|
|
|
|
*/
|
2013-07-01 11:18:43 +02:00
|
|
|
public function id2uid($id)
|
2012-03-07 11:02:51 +01:00
|
|
|
{
|
|
|
|
return base64_decode(str_pad(strtr($id, '-_', '+/'), strlen($id) % 4, '=', STR_PAD_RIGHT));
|
|
|
|
}
|
|
|
|
|
2013-08-07 12:30:54 +02:00
|
|
|
/**
|
|
|
|
* Build SQL query for fulltext matches
|
|
|
|
*/
|
|
|
|
private function _search_query($fields, $value, $mode)
|
|
|
|
{
|
|
|
|
$query = array();
|
|
|
|
$cols = array();
|
|
|
|
|
|
|
|
// $fulltext_cols might contain composite field names e.g. 'email:address' while $fields not
|
|
|
|
foreach (kolab_format_contact::$fulltext_cols as $col) {
|
|
|
|
if ($pos = strpos($col, ':')) {
|
|
|
|
$col = substr($col, 0, $pos);
|
|
|
|
}
|
|
|
|
if (in_array($col, $fields)) {
|
|
|
|
$cols[] = $col;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($cols) == count($fields)) {
|
|
|
|
switch ($mode) {
|
|
|
|
case 1: $prefix = '^'; $suffix = '$'; break; // strict
|
|
|
|
case 2: $prefix = '^'; $suffix = ''; break; // prefix
|
|
|
|
default: $prefix = ''; $suffix = ''; break; // substring
|
|
|
|
}
|
|
|
|
|
|
|
|
$search_string = is_array($value) ? join(' ', $value) : $value;
|
|
|
|
foreach (rcube_utils::normalize_string($search_string, true) as $word) {
|
|
|
|
$query[] = array('words', 'LIKE', $prefix . $word . $suffix);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $query;
|
|
|
|
}
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
/**
|
|
|
|
* Map fields from internal Kolab_Format to Roundcube contact format
|
|
|
|
*/
|
|
|
|
private function _to_rcube_contact($record)
|
|
|
|
{
|
2013-07-01 11:18:43 +02:00
|
|
|
$record['ID'] = $this->uid2id($record['uid']);
|
2012-03-07 11:02:51 +01:00
|
|
|
|
2013-03-21 10:02:35 +01:00
|
|
|
// convert email, website, phone values
|
|
|
|
foreach (array('email'=>'address', 'website'=>'url', 'phone'=>'number') as $col => $propname) {
|
|
|
|
if (is_array($record[$col])) {
|
|
|
|
$values = $record[$col];
|
|
|
|
unset($record[$col]);
|
|
|
|
foreach ((array)$values as $i => $val) {
|
|
|
|
$key = $col . ($val['type'] ? ':' . $val['type'] : '');
|
|
|
|
$record[$key][] = $val[$propname];
|
|
|
|
}
|
2012-04-21 18:23:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
if (is_array($record['address'])) {
|
2012-03-07 11:02:51 +01:00
|
|
|
$addresses = $record['address'];
|
|
|
|
unset($record['address']);
|
|
|
|
foreach ($addresses as $i => $adr) {
|
|
|
|
$key = 'address' . ($adr['type'] ? ':' . $adr['type'] : '');
|
|
|
|
$record[$key][] = array(
|
|
|
|
'street' => $adr['street'],
|
2011-05-21 12:25:39 +02:00
|
|
|
'locality' => $adr['locality'],
|
2012-03-07 11:02:51 +01:00
|
|
|
'zipcode' => $adr['code'],
|
|
|
|
'region' => $adr['region'],
|
|
|
|
'country' => $adr['country'],
|
2011-05-21 12:25:39 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// photo is stored as separate attachment
|
2012-03-08 10:21:21 +01:00
|
|
|
if ($record['photo'] && strlen($record['photo']) < 255 && ($att = $record['_attachments'][$record['photo']])) {
|
|
|
|
// only fetch photo content if requested
|
2012-03-21 16:15:53 +01:00
|
|
|
if ($this->action == 'photo')
|
2012-04-25 19:26:40 +02:00
|
|
|
$record['photo'] = $att['content'] ? $att['content'] : $this->storagefolder->get_attachment($record['uid'], $att['id']);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2012-03-21 16:15:53 +01:00
|
|
|
// truncate publickey value for display
|
|
|
|
if ($record['pgppublickey'] && $this->action == 'show')
|
|
|
|
$record['pgppublickey'] = substr($record['pgppublickey'], 0, 140) . '...';
|
|
|
|
|
2011-05-21 12:25:39 +02:00
|
|
|
// remove empty fields
|
2013-04-18 15:45:00 +02:00
|
|
|
$record = array_filter($record);
|
|
|
|
|
|
|
|
// remove kolab_storage internal data
|
|
|
|
unset($record['_msguid'], $record['_formatobj'], $record['_mailbox'], $record['_type'], $record['_size']);
|
|
|
|
|
|
|
|
return $record;
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
2011-07-24 10:56:47 +02:00
|
|
|
/**
|
2012-03-14 18:51:38 +01:00
|
|
|
* Map fields from Roundcube format to internal kolab_format_contact properties
|
2011-07-24 10:56:47 +02:00
|
|
|
*/
|
2012-03-14 18:51:38 +01:00
|
|
|
private function _from_rcube_contact($contact, $old = array())
|
2011-05-21 12:25:39 +02:00
|
|
|
{
|
2012-03-07 11:02:51 +01:00
|
|
|
if (!$contact['uid'] && $contact['ID'])
|
2013-07-01 11:18:43 +02:00
|
|
|
$contact['uid'] = $this->id2uid($contact['ID']);
|
2012-03-21 11:10:17 +01:00
|
|
|
else if (!$contact['uid'] && $old['uid'])
|
|
|
|
$contact['uid'] = $old['uid'];
|
2011-05-21 12:25:39 +02:00
|
|
|
|
2013-07-01 12:04:56 +02:00
|
|
|
$contact['im'] = array_filter($this->get_col_values('im', $contact, true));
|
2012-09-20 09:39:15 +02:00
|
|
|
|
2013-03-21 10:02:35 +01:00
|
|
|
// convert email, website, phone values
|
|
|
|
foreach (array('email'=>'address', 'website'=>'url', 'phone'=>'number') as $col => $propname) {
|
2013-08-07 12:34:37 +02:00
|
|
|
$col_values = $this->get_col_values($col, $contact);
|
2013-03-21 10:02:35 +01:00
|
|
|
$contact[$col] = array();
|
2013-08-07 12:34:37 +02:00
|
|
|
foreach ($col_values as $type => $values) {
|
2013-03-21 10:02:35 +01:00
|
|
|
foreach ((array)$values as $val) {
|
|
|
|
if (!empty($val)) {
|
|
|
|
$contact[$col][] = array($propname => $val, 'type' => $type);
|
|
|
|
}
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2013-03-21 10:02:35 +01:00
|
|
|
unset($contact[$col.':'.$type]);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-21 10:02:35 +01:00
|
|
|
$addresses = array();
|
2011-05-21 12:25:39 +02:00
|
|
|
foreach ($this->get_col_values('address', $contact) as $type => $values) {
|
|
|
|
foreach ((array)$values as $adr) {
|
2011-08-03 11:55:36 +02:00
|
|
|
// skip empty address
|
|
|
|
$adr = array_filter($adr);
|
|
|
|
if (empty($adr))
|
|
|
|
continue;
|
|
|
|
|
2012-03-07 11:02:51 +01:00
|
|
|
$addresses[] = array(
|
|
|
|
'type' => $type,
|
|
|
|
'street' => $adr['street'],
|
|
|
|
'locality' => $adr['locality'],
|
|
|
|
'code' => $adr['zipcode'],
|
|
|
|
'region' => $adr['region'],
|
|
|
|
'country' => $adr['country'],
|
|
|
|
);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2012-03-07 11:02:51 +01:00
|
|
|
|
|
|
|
unset($contact['address:'.$type]);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
2012-09-20 09:39:15 +02:00
|
|
|
|
2012-03-07 11:02:51 +01:00
|
|
|
$contact['address'] = $addresses;
|
2011-06-02 09:45:04 +02:00
|
|
|
|
2013-11-27 13:58:07 +01:00
|
|
|
// categories are not supported in the web client but should be preserved (#2608)
|
|
|
|
$contact['categories'] = $old['categories'];
|
|
|
|
|
2012-03-14 18:51:38 +01:00
|
|
|
// copy meta data (starting with _) from old object
|
|
|
|
foreach ((array)$old as $key => $val) {
|
|
|
|
if (!isset($contact[$key]) && $key[0] == '_')
|
|
|
|
$contact[$key] = $val;
|
|
|
|
}
|
|
|
|
|
2012-12-31 17:41:30 +01:00
|
|
|
// convert one-item-array elements into string element
|
|
|
|
// this is needed e.g. to properly import birthday field
|
|
|
|
foreach ($this->coltypes as $type => $col_def) {
|
|
|
|
if ($col_def['limit'] == 1 && is_array($contact[$type])) {
|
|
|
|
$contact[$type] = array_shift(array_filter($contact[$type]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-12 19:05:45 +02:00
|
|
|
// When importing contacts 'vcard' data is added, we don't need it (Bug #1711)
|
|
|
|
unset($contact['vcard']);
|
|
|
|
|
2012-03-14 18:51:38 +01:00
|
|
|
// add empty values for some fields which can be removed in the UI
|
2012-11-13 17:45:53 +01:00
|
|
|
return array_filter($contact) + array('nickname' => '', 'birthday' => '', 'anniversary' => '', 'freebusyurl' => '', 'photo' => $contact['photo']);
|
2011-05-21 12:25:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|