Changeset View
Changeset View
Standalone View
Standalone View
plugins/kolab_addressbook/lib/rcube_carddav_contacts.php
- This file was copied from plugins/kolab_addressbook/lib/rcube_kolab_contacts.php.
<?php | <?php | ||||
/** | /** | ||||
* Backend class for a custom address book | * Backend class for a custom address book using CardDAV service. | ||||
* | * | ||||
* This part of the Roundcube+Kolab integration and connects the | * This part of the Roundcube+Kolab integration and connects the | ||||
* rcube_addressbook interface with the kolab_storage wrapper from libkolab | * rcube_addressbook interface with the kolab_storage_dav wrapper from libkolab | ||||
* | * | ||||
* @author Thomas Bruederli <bruederli@kolabsys.com> | * @author Thomas Bruederli <bruederli@kolabsys.com> | ||||
* @author Aleksander Machniak <machniak@kolabsys.com> | * @author Aleksander Machniak <machniak@apheleia-it.chm> | ||||
* | * | ||||
* Copyright (C) 2011, Kolab Systems AG <contact@kolabsys.com> | * Copyright (C) 2011-2022, Kolab Systems AG <contact@apheleia-it.ch> | ||||
* | * | ||||
* This program is free software: you can redistribute it and/or modify | * This program is free software: you can redistribute it and/or modify | ||||
* it under the terms of the GNU Affero General Public License as | * it under the terms of the GNU Affero General Public License as | ||||
* published by the Free Software Foundation, either version 3 of the | * published by the Free Software Foundation, either version 3 of the | ||||
* License, or (at your option) any later version. | * License, or (at your option) any later version. | ||||
* | * | ||||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | ||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
* GNU Affero General Public License for more details. | * GNU Affero General Public License for more details. | ||||
* | * | ||||
* You should have received a copy of the GNU Affero General Public License | * 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/>. | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
* | * | ||||
* @see rcube_addressbook | * @see rcube_addressbook | ||||
*/ | */ | ||||
class rcube_kolab_contacts extends rcube_addressbook | class rcube_carddav_contacts extends rcube_addressbook | ||||
{ | { | ||||
public $primary_key = 'ID'; | public $primary_key = 'ID'; | ||||
public $rights = 'lrs'; | public $rights = 'lrs'; | ||||
public $readonly = true; | public $readonly = true; | ||||
public $undelete = true; | public $undelete = false; | ||||
public $groups = true; | public $groups = false; // TODO | ||||
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), | |||||
'email' => array('subtypes' => array('home','work','other')), | |||||
'phone' => array(), | |||||
'address' => array('subtypes' => array('home','work','office')), | |||||
'website' => array('subtypes' => array('homepage','blog')), | |||||
'im' => array('subtypes' => null), | |||||
'gender' => array('limit' => 1), | |||||
'birthday' => array('limit' => 1), | |||||
'anniversary' => array('limit' => 1), | |||||
'profession' => array( | |||||
'type' => 'text', | |||||
'size' => 40, | |||||
'maxlength' => 80, | |||||
'limit' => 1, | |||||
'label' => 'kolab_addressbook.profession', | |||||
'category' => 'personal' | |||||
), | |||||
'manager' => array('limit' => null), | |||||
'assistant' => array('limit' => null), | |||||
'spouse' => array('limit' => 1), | |||||
'children' => array( | |||||
'type' => 'text', | |||||
'size' => 40, | |||||
'maxlength' => 80, | |||||
'limit' => null, | |||||
'label' => 'kolab_addressbook.children', | |||||
'category' => 'personal' | |||||
), | |||||
'freebusyurl' => array( | |||||
'type' => 'text', | |||||
'size' => 40, | |||||
'limit' => 1, | |||||
'label' => 'kolab_addressbook.freebusyurl' | |||||
), | |||||
'pgppublickey' => array( | |||||
'type' => 'textarea', | |||||
'size' => 70, | |||||
'rows' => 10, | |||||
'limit' => 1, | |||||
'label' => 'kolab_addressbook.pgppublickey' | |||||
), | |||||
'pkcs7publickey' => array( | |||||
'type' => 'textarea', | |||||
'size' => 70, | |||||
'rows' => 10, | |||||
'limit' => 1, | |||||
'label' => 'kolab_addressbook.pkcs7publickey' | |||||
), | |||||
'notes' => array('limit' => 1), | |||||
'photo' => array('limit' => 1), | |||||
// TODO: define more Kolab-specific fields such as: language, latitude, longitude, crypto settings | |||||
); | |||||
/** | public $coltypes = [ | ||||
* vCard additional fields mapping | 'name' => ['limit' => 1], | ||||
*/ | 'firstname' => ['limit' => 1], | ||||
public $vcard_map = array( | 'surname' => ['limit' => 1], | ||||
'profession' => 'X-PROFESSION', | 'middlename' => ['limit' => 1], | ||||
'officelocation' => 'X-OFFICE-LOCATION', | 'prefix' => ['limit' => 1], | ||||
'initials' => 'X-INITIALS', | 'suffix' => ['limit' => 1], | ||||
'children' => 'X-CHILDREN', | 'nickname' => ['limit' => 1], | ||||
'freebusyurl' => 'X-FREEBUSY-URL', | 'jobtitle' => ['limit' => 1], | ||||
'pgppublickey' => 'KEY', | 'organization' => ['limit' => 1], | ||||
); | 'department' => ['limit' => 1], | ||||
'email' => ['subtypes' => ['home','work','other']], | |||||
'phone' => [], | |||||
'address' => ['subtypes' => ['home','work','office']], | |||||
'website' => ['subtypes' => ['homepage','blog']], | |||||
'im' => ['subtypes' => null], | |||||
'gender' => ['limit' => 1], | |||||
'birthday' => ['limit' => 1], | |||||
'anniversary' => ['limit' => 1], | |||||
'manager' => ['limit' => null], | |||||
'assistant' => ['limit' => null], | |||||
'spouse' => ['limit' => 1], | |||||
'notes' => ['limit' => 1], | |||||
'photo' => ['limit' => 1], | |||||
]; | |||||
public $vcard_map = [ | |||||
// 'profession' => 'X-PROFESSION', | |||||
// 'officelocation' => 'X-OFFICE-LOCATION', | |||||
// 'initials' => 'X-INITIALS', | |||||
// 'children' => 'X-CHILDREN', | |||||
// 'freebusyurl' => 'X-FREEBUSY-URL', | |||||
// 'pgppublickey' => 'KEY', | |||||
'uid' => 'UID', | |||||
]; | |||||
/** | /** | ||||
* List of date type fields | * List of date type fields | ||||
*/ | */ | ||||
public $date_cols = array('birthday', 'anniversary'); | public $date_cols = ['birthday', 'anniversary']; | ||||
public $fulltext_cols = ['name', 'firstname', 'surname', 'middlename', 'email']; | |||||
private $gid; | private $gid; | ||||
private $storagefolder; | private $storage; | ||||
private $dataset; | private $dataset; | ||||
private $sortindex; | private $sortindex; | ||||
private $contacts; | private $contacts; | ||||
private $distlists; | private $distlists; | ||||
private $groupmembers; | private $groupmembers; | ||||
private $filter; | private $filter; | ||||
private $result; | private $result; | ||||
private $namespace; | private $namespace; | ||||
private $imap_folder = 'INBOX/Contacts'; | |||||
private $action; | private $action; | ||||
// list of fields used for searching in "All fields" mode | // list of fields used for searching in "All fields" mode | ||||
private $search_fields = array( | private $search_fields = [ | ||||
'name', | 'name', | ||||
'firstname', | 'firstname', | ||||
'surname', | 'surname', | ||||
'middlename', | 'middlename', | ||||
'prefix', | 'prefix', | ||||
'suffix', | 'suffix', | ||||
'nickname', | 'nickname', | ||||
'jobtitle', | 'jobtitle', | ||||
'organization', | 'organization', | ||||
'department', | 'department', | ||||
'email', | 'email', | ||||
'phone', | 'phone', | ||||
'address', | 'address', | ||||
'profession', | // 'profession', | ||||
'manager', | 'manager', | ||||
'assistant', | 'assistant', | ||||
'spouse', | 'spouse', | ||||
'children', | 'children', | ||||
'notes', | 'notes', | ||||
); | ]; | ||||
public function __construct($imap_folder = null) | /** | ||||
* Object constructor | |||||
*/ | |||||
public function __construct($dav_folder = null) | |||||
{ | { | ||||
if ($imap_folder) { | $this->storage = $dav_folder; | ||||
$this->imap_folder = $imap_folder; | $this->ready = !empty($this->storage); | ||||
} | |||||
// extend coltypes configuration | |||||
$format = kolab_format::factory('contact'); | |||||
$this->coltypes['phone']['subtypes'] = array_keys($format->phonetypes); | |||||
$this->coltypes['address']['subtypes'] = array_keys($format->addresstypes); | |||||
$rcube = rcube::get_instance(); | |||||
// set localized labels for proprietary cols | |||||
foreach ($this->coltypes as $col => $prop) { | |||||
if (is_string($prop['label'])) { | |||||
$this->coltypes[$col]['label'] = $rcube->gettext($prop['label']); | |||||
} | |||||
} | |||||
// fetch objects from the given IMAP folder | |||||
$this->storagefolder = kolab_storage::get_folder($this->imap_folder); | |||||
$this->ready = $this->storagefolder && !PEAR::isError($this->storagefolder); | |||||
// Set readonly and rights flags according to folder permissions | // Set readonly and rights flags according to folder permissions | ||||
if ($this->ready) { | if ($this->ready) { | ||||
if ($this->storagefolder->get_owner() == $_SESSION['username']) { | if ($this->storage->get_owner() == $_SESSION['username']) { | ||||
$this->readonly = false; | $this->readonly = false; | ||||
$this->rights = 'lrswikxtea'; | $this->rights = 'lrswikxtea'; | ||||
} | } | ||||
else { | else { | ||||
$rights = $this->storagefolder->get_myrights(); | $rights = $this->storage->get_myrights(); | ||||
if ($rights && !PEAR::isError($rights)) { | if ($rights && !PEAR::isError($rights)) { | ||||
$this->rights = $rights; | $this->rights = $rights; | ||||
if (strpos($rights, 'i') !== false && strpos($rights, 't') !== false) { | if (strpos($rights, 'i') !== false && strpos($rights, 't') !== false) { | ||||
$this->readonly = false; | $this->readonly = false; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
$this->action = rcube::get_instance()->action; | $this->action = rcube::get_instance()->action; | ||||
} | } | ||||
/** | /** | ||||
* Getter for the address book name to be displayed | * Getter for the address book name to be displayed | ||||
* | * | ||||
* @return string Name of this address book | * @return string Name of this address book | ||||
*/ | */ | ||||
public function get_name() | public function get_name() | ||||
{ | { | ||||
return $this->storagefolder->get_name(); | return $this->storage->get_name(); | ||||
} | } | ||||
/** | /** | ||||
* Wrapper for kolab_storage_folder::get_foldername() | * Wrapper for kolab_storage_folder::get_foldername() | ||||
*/ | */ | ||||
public function get_foldername() | public function get_foldername() | ||||
{ | { | ||||
return $this->storagefolder->get_foldername(); | return $this->storage->get_foldername(); | ||||
} | } | ||||
/** | /** | ||||
* Getter for the IMAP folder name | * Getter for the folder name | ||||
* | * | ||||
* @return string Name of the IMAP folder | * @return string Name of the folder | ||||
*/ | */ | ||||
public function get_realname() | public function get_realname() | ||||
{ | { | ||||
return $this->imap_folder; | return $this->get_name(); | ||||
} | } | ||||
/** | /** | ||||
* Getter for the name of the namespace to which the IMAP folder belongs | * Getter for the name of the namespace to which the IMAP folder belongs | ||||
* | * | ||||
* @return string Name of the namespace (personal, other, shared) | * @return string Name of the namespace (personal, other, shared) | ||||
*/ | */ | ||||
public function get_namespace() | public function get_namespace() | ||||
{ | { | ||||
if ($this->namespace === null && $this->ready) { | if ($this->namespace === null && $this->ready) { | ||||
$this->namespace = $this->storagefolder->get_namespace(); | $this->namespace = $this->storage->get_namespace(); | ||||
} | } | ||||
return $this->namespace; | return $this->namespace; | ||||
} | } | ||||
/** | /** | ||||
* Getter for parent folder path | * Getter for parent folder path | ||||
* | * | ||||
* @return string Full path to parent folder | * @return string Full path to parent folder | ||||
*/ | */ | ||||
public function get_parent() | public function get_parent() | ||||
{ | { | ||||
return $this->storagefolder->get_parent(); | return $this->storage->get_parent(); | ||||
} | } | ||||
/** | /** | ||||
* Check subscription status of this folder | * Check subscription status of this folder | ||||
* | * | ||||
* @return boolean True if subscribed, false if not | * @return boolean True if subscribed, false if not | ||||
*/ | */ | ||||
public function is_subscribed() | public function is_subscribed() | ||||
{ | { | ||||
return kolab_storage::folder_is_subscribed($this->imap_folder); | return true; | ||||
} | } | ||||
/** | /** | ||||
* Compose an URL for CardDAV access to this address book (if configured) | * Compose an URL for CardDAV access to this address book (if configured) | ||||
*/ | */ | ||||
public function get_carddav_url() | public function get_carddav_url() | ||||
{ | { | ||||
/* | |||||
$rcmail = rcmail::get_instance(); | $rcmail = rcmail::get_instance(); | ||||
if ($template = $rcmail->config->get('kolab_addressbook_carddav_url', null)) { | if ($template = $rcmail->config->get('kolab_addressbook_carddav_url', null)) { | ||||
return strtr($template, array( | return strtr($template, [ | ||||
'%h' => $_SERVER['HTTP_HOST'], | '%h' => $_SERVER['HTTP_HOST'], | ||||
'%u' => urlencode($rcmail->get_user_name()), | '%u' => urlencode($rcmail->get_user_name()), | ||||
'%i' => urlencode($this->storagefolder->get_uid()), | '%i' => urlencode($this->storage->get_uid()), | ||||
'%n' => urlencode($this->imap_folder), | '%n' => urlencode($this->imap_folder), | ||||
)); | ]); | ||||
} | } | ||||
*/ | |||||
return false; | return false; | ||||
} | } | ||||
/** | /** | ||||
* Setter for the current group | * Setter for the current group | ||||
*/ | */ | ||||
public function set_group($gid) | public function set_group($gid) | ||||
{ | { | ||||
Show All 25 Lines | */ | ||||
*/ | */ | ||||
public function reset() | public function reset() | ||||
{ | { | ||||
$this->result = null; | $this->result = null; | ||||
$this->filter = null; | $this->filter = null; | ||||
} | } | ||||
/** | /** | ||||
* List addressbook sources (folders) | |||||
*/ | |||||
public static function list_folders() | |||||
{ | |||||
$storage = self::get_storage(); | |||||
$sources = []; | |||||
// get all folders that have "contact" type | |||||
foreach ($storage->get_folders('contact') as $folder) { | |||||
$sources[$folder->id] = new rcube_carddav_contacts($folder); | |||||
} | |||||
return $sources; | |||||
} | |||||
/** | |||||
* Getter for the rcube_addressbook instance | |||||
* | |||||
* @param string $id Addressbook (folder) ID | |||||
* | |||||
* @return ?rcube_carddav_contacts | |||||
*/ | |||||
public static function get_address_book($id) | |||||
{ | |||||
$storage = self::get_storage(); | |||||
$folder = $storage->get_folder($id, 'contact'); | |||||
if ($folder) { | |||||
return new rcube_carddav_contacts($folder); | |||||
} | |||||
} | |||||
/** | |||||
* Initialize kolab_storage_dav instance | |||||
*/ | |||||
protected static function get_storage() | |||||
{ | |||||
$rcube = rcube::get_instance(); | |||||
$url = $rcube->config->get('kolab_addressbook_carddav_server', 'http://localhost'); | |||||
return new kolab_storage_dav($url); | |||||
} | |||||
/** | |||||
* List all active contact groups of this source | * List all active contact groups of this source | ||||
* | * | ||||
* @param string Optional search string to match group name | * @param string Optional search string to match group name | ||||
* @param int Search mode. Sum of self::SEARCH_* | * @param int Search mode. Sum of self::SEARCH_* | ||||
* | * | ||||
* @return array Indexed list of contact groups, each a hash array | * @return array Indexed list of contact groups, each a hash array | ||||
*/ | */ | ||||
function list_groups($search = null, $mode = 0) | function list_groups($search = null, $mode = 0) | ||||
{ | { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$groups = array(); | $groups = []; | ||||
foreach ((array)$this->distlists as $group) { | foreach ((array)$this->distlists as $group) { | ||||
if (!$search || strstr(mb_strtolower($group['name']), mb_strtolower($search))) { | if (!$search || strstr(mb_strtolower($group['name']), mb_strtolower($search))) { | ||||
$groups[$group['ID']] = array('ID' => $group['ID'], 'name' => $group['name']); | $groups[$group['ID']] = ['ID' => $group['ID'], 'name' => $group['name']]; | ||||
} | } | ||||
} | } | ||||
// sort groups by name | // sort groups by name | ||||
uasort($groups, function($a, $b) { return strcoll($a['name'], $b['name']); }); | uasort($groups, function($a, $b) { return strcoll($a['name'], $b['name']); }); | ||||
return array_values($groups); | return array_values($groups); | ||||
} | } | ||||
Show All 13 Lines | public function list_records($cols = null, $subset = 0, $nocount = false) | ||||
$fetch_all = false; | $fetch_all = false; | ||||
$fast_mode = !empty($cols) && is_array($cols); | $fast_mode = !empty($cols) && is_array($cols); | ||||
// list member of the selected group | // list member of the selected group | ||||
if ($this->gid) { | if ($this->gid) { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$this->sortindex = array(); | $this->sortindex = []; | ||||
$this->contacts = array(); | $this->contacts = []; | ||||
$local_sortindex = array(); | $local_sortindex = []; | ||||
$uids = array(); | $uids = []; | ||||
// get members with email specified | // get members with email specified | ||||
foreach ((array)$this->distlists[$this->gid]['member'] as $member) { | foreach ((array)$this->distlists[$this->gid]['member'] as $member) { | ||||
// skip member that don't match the search filter | // skip member that don't match the search filter | ||||
if (!empty($this->filter['ids']) && array_search($member['ID'], $this->filter['ids']) === false) { | if (!empty($this->filter['ids']) && array_search($member['ID'], $this->filter['ids']) === false) { | ||||
continue; | continue; | ||||
} | } | ||||
if (!empty($member['uid'])) { | if (!empty($member['uid'])) { | ||||
$uids[] = $member['uid']; | $uids[] = $member['uid']; | ||||
} | } | ||||
else if (!empty($member['email'])) { | else if (!empty($member['email'])) { | ||||
$this->contacts[$member['ID']] = $member; | $this->contacts[$member['ID']] = $member; | ||||
$local_sortindex[$member['ID']] = $this->_sort_string($member); | $local_sortindex[$member['ID']] = $this->_sort_string($member); | ||||
$fetch_all = true; | $fetch_all = true; | ||||
} | } | ||||
} | } | ||||
// get members by UID | // get members by UID | ||||
if (!empty($uids)) { | if (!empty($uids)) { | ||||
$this->_fetch_contacts($query = array(array('uid', '=', $uids)), $fetch_all ? false : count($uids), $fast_mode); | $this->_fetch_contacts($query = [['uid', '=', $uids]], $fetch_all ? false : count($uids), $fast_mode); | ||||
$this->sortindex = array_merge($this->sortindex, $local_sortindex); | $this->sortindex = array_merge($this->sortindex, $local_sortindex); | ||||
} | } | ||||
} | } | ||||
else if (is_array($this->filter['ids'])) { | else if (is_array($this->filter['ids'])) { | ||||
$ids = $this->filter['ids']; | $ids = $this->filter['ids']; | ||||
if (count($ids)) { | if (count($ids)) { | ||||
$uids = array_map(array($this, 'id2uid'), $this->filter['ids']); | $uids = array_map([$this, 'id2uid'], $this->filter['ids']); | ||||
$this->_fetch_contacts($query = array(array('uid', '=', $uids)), count($ids), $fast_mode); | $this->_fetch_contacts($query = [['uid', '=', $uids]], count($ids), $fast_mode); | ||||
} | } | ||||
} | } | ||||
else { | else { | ||||
$this->_fetch_contacts($query = 'contact', true, $fast_mode); | $this->_fetch_contacts($query = 'contact', true, $fast_mode); | ||||
} | } | ||||
if ($fetch_all) { | if ($fetch_all) { | ||||
// sort results (index only) | // sort results (index only) | ||||
Show All 13 Lines | public function list_records($cols = null, $subset = 0, $nocount = false) | ||||
} | } | ||||
} | } | ||||
else if (!empty($this->dataset)) { | else if (!empty($this->dataset)) { | ||||
// get all records count, skip the query if possible | // get all records count, skip the query if possible | ||||
if (!isset($query) || count($this->dataset) < $this->page_size) { | if (!isset($query) || count($this->dataset) < $this->page_size) { | ||||
$this->result->count = count($this->dataset) + $this->page_size * ($this->list_page - 1); | $this->result->count = count($this->dataset) + $this->page_size * ($this->list_page - 1); | ||||
} | } | ||||
else { | else { | ||||
$this->result->count = $this->storagefolder->count($query); | $this->result->count = $this->storage->count($query); | ||||
} | } | ||||
$start_row = $subset < 0 ? $this->page_size + $subset : 0; | $start_row = $subset < 0 ? $this->page_size + $subset : 0; | ||||
$last_row = min($subset != 0 ? $start_row + abs($subset) : $this->page_size, $this->result->count); | $last_row = min($subset != 0 ? $start_row + abs($subset) : $this->page_size, $this->result->count); | ||||
for ($i = $start_row; $i < $last_row; $i++) { | for ($i = $start_row; $i < $last_row; $i++) { | ||||
$this->result->add($this->_to_rcube_contact($this->dataset[$i])); | $this->result->add($this->_to_rcube_contact($this->dataset[$i])); | ||||
} | } | ||||
Show All 13 Lines | */ | ||||
* 2 - prefix (abc*) | * 2 - prefix (abc*) | ||||
* 4 - include groups (if supported) | * 4 - include groups (if supported) | ||||
* @param bool $select True if results are requested, False if count only | * @param bool $select True if results are requested, False if count only | ||||
* @param bool $nocount True to skip the count query (select only) | * @param bool $nocount True to skip the count query (select only) | ||||
* @param array $required List of fields that cannot be empty | * @param array $required List of fields that cannot be empty | ||||
* | * | ||||
* @return rcube_result_set List of contact records and 'count' value | * @return rcube_result_set List of contact records and 'count' value | ||||
*/ | */ | ||||
public function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array()) | public function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = []) | ||||
{ | { | ||||
// search by ID | // search by ID | ||||
if ($fields == $this->primary_key) { | if ($fields == $this->primary_key) { | ||||
$ids = !is_array($value) ? explode(',', $value) : $value; | $ids = !is_array($value) ? explode(',', $value) : $value; | ||||
$result = new rcube_result_set(); | $result = new rcube_result_set(); | ||||
foreach ($ids as $id) { | foreach ($ids as $id) { | ||||
if ($rec = $this->get_record($id, true)) { | if ($rec = $this->get_record($id, true)) { | ||||
$result->add($rec); | $result->add($rec); | ||||
$result->count++; | $result->count++; | ||||
} | } | ||||
} | } | ||||
return $result; | return $result; | ||||
} | } | ||||
else if ($fields == '*') { | else if ($fields == '*') { | ||||
$fields = $this->search_fields; | $fields = $this->search_fields; | ||||
} | } | ||||
if (!is_array($fields)) { | if (!is_array($fields)) { | ||||
$fields = array($fields); | $fields = [$fields]; | ||||
} | } | ||||
if (!is_array($required) && !empty($required)) { | if (!is_array($required) && !empty($required)) { | ||||
$required = array($required); | $required = [$required]; | ||||
} | } | ||||
// advanced search | // advanced search | ||||
if (is_array($value)) { | if (is_array($value)) { | ||||
$advanced = true; | $advanced = true; | ||||
$value = array_map('mb_strtolower', $value); | $value = array_map('mb_strtolower', $value); | ||||
} | } | ||||
else { | else { | ||||
$value = mb_strtolower($value); | $value = mb_strtolower($value); | ||||
} | } | ||||
$scount = count($fields); | $scount = count($fields); | ||||
// build key name regexp | // build key name regexp | ||||
$regexp = '/^(' . implode('|', $fields) . ')(?:.*)$/'; | $regexp = '/^(' . implode('|', $fields) . ')(?:.*)$/'; | ||||
// pass query to storage if only indexed cols are involved | // pass query to storage if only indexed cols are involved | ||||
// NOTE: this is only some rough pre-filtering but probably includes false positives | // NOTE: this is only some rough pre-filtering but probably includes false positives | ||||
$squery = $this->_search_query($fields, $value, $mode); | $squery = $this->_search_query($fields, $value, $mode); | ||||
// add magic selector to select contacts with birthday dates only | // add magic selector to select contacts with birthday dates only | ||||
if (in_array('birthday', $required)) { | if (in_array('birthday', $required)) { | ||||
$squery[] = array('tags', '=', 'x-has-birthday'); | $squery[] = ['tags', '=', 'x-has-birthday']; | ||||
} | } | ||||
$squery[] = array('type', '=', 'contact'); | $squery[] = ['type', '=', 'contact']; | ||||
// get all/matching records | // get all/matching records | ||||
$this->_fetch_contacts($squery); | $this->_fetch_contacts($squery); | ||||
// save searching conditions | // save searching conditions | ||||
$this->filter = array('fields' => $fields, 'value' => $value, 'mode' => $mode, 'ids' => array()); | $this->filter = ['fields' => $fields, 'value' => $value, 'mode' => $mode, 'ids' => []]; | ||||
// search by iterating over all records in dataset | // search by iterating over all records in dataset | ||||
foreach ($this->dataset as $record) { | foreach ($this->dataset as $record) { | ||||
$contact = $this->_to_rcube_contact($record); | $contact = $this->_to_rcube_contact($record); | ||||
$id = $contact['ID']; | $id = $contact['ID']; | ||||
// check if current contact has required values, otherwise skip it | // check if current contact has required values, otherwise skip it | ||||
if ($required) { | if ($required) { | ||||
foreach ($required as $f) { | foreach ($required as $f) { | ||||
// required field might be 'email', but contact might contain 'email:home' | // required field might be 'email', but contact might contain 'email:home' | ||||
if (!($v = rcube_addressbook::get_col_values($f, $contact, true)) || empty($v)) { | if (!($v = rcube_addressbook::get_col_values($f, $contact, true)) || empty($v)) { | ||||
continue 2; | continue 2; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
$found = array(); | $found = []; | ||||
$contents = ''; | $contents = ''; | ||||
foreach (preg_grep($regexp, array_keys($contact)) as $col) { | foreach (preg_grep($regexp, array_keys($contact)) as $col) { | ||||
$pos = strpos($col, ':'); | $pos = strpos($col, ':'); | ||||
$colname = $pos ? substr($col, 0, $pos) : $col; | $colname = $pos ? substr($col, 0, $pos) : $col; | ||||
foreach ((array)$contact[$col] as $val) { | foreach ((array)$contact[$col] as $val) { | ||||
if ($advanced) { | if ($advanced) { | ||||
$found[$colname] = $this->compare_search_value($colname, $val, $value[array_search($colname, $fields)], $mode); | $found[$colname] = $this->compare_search_value($colname, $val, $value[array_search($colname, $fields)], $mode); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | public function count() | ||||
if ($this->gid) { | if ($this->gid) { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$count = count($this->distlists[$this->gid]['member']); | $count = count($this->distlists[$this->gid]['member']); | ||||
} | } | ||||
else if (is_array($this->filter['ids'])) { | else if (is_array($this->filter['ids'])) { | ||||
$count = count($this->filter['ids']); | $count = count($this->filter['ids']); | ||||
} | } | ||||
else { | else { | ||||
$count = $this->storagefolder->count('contact'); | $count = $this->storage->count('contact'); | ||||
} | } | ||||
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size); | return new rcube_result_set($count, ($this->list_page-1) * $this->page_size); | ||||
} | } | ||||
/** | /** | ||||
* Return the last result set | * Return the last result set | ||||
* | * | ||||
Show All 18 Lines | public function get_record($id, $assoc = false) | ||||
$uid = $this->id2uid($id); | $uid = $this->id2uid($id); | ||||
$rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC); | $rev = rcube_utils::get_input_value('_rev', rcube_utils::INPUT_GPC); | ||||
if (strpos($uid, 'mailto:') === 0) { | if (strpos($uid, 'mailto:') === 0) { | ||||
$this->_fetch_groups(true); | $this->_fetch_groups(true); | ||||
$rec = $this->contacts[$id]; | $rec = $this->contacts[$id]; | ||||
$this->readonly = true; // set source to read-only | $this->readonly = true; // set source to read-only | ||||
} | } | ||||
/* | |||||
else if (!empty($rev)) { | else if (!empty($rev)) { | ||||
$rcmail = rcube::get_instance(); | $rcmail = rcube::get_instance(); | ||||
$plugin = $rcmail->plugins->get_plugin('kolab_addressbook'); | $plugin = $rcmail->plugins->get_plugin('kolab_addressbook'); | ||||
if ($plugin && ($object = $plugin->get_revision($id, kolab_storage::id_encode($this->imap_folder), $rev))) { | if ($plugin && ($object = $plugin->get_revision($id, kolab_storage::id_encode($this->imap_folder), $rev))) { | ||||
$rec = $this->_to_rcube_contact($object); | $rec = $this->_to_rcube_contact($object); | ||||
$rec['rev'] = $rev; | $rec['rev'] = $rev; | ||||
} | } | ||||
$this->readonly = true; // set source to read-only | $this->readonly = true; // set source to read-only | ||||
} | } | ||||
else if ($object = $this->storagefolder->get_object($uid)) { | */ | ||||
else if ($object = $this->storage->get_object($uid)) { | |||||
$rec = $this->_to_rcube_contact($object); | $rec = $this->_to_rcube_contact($object); | ||||
} | } | ||||
if ($rec) { | if ($rec) { | ||||
$this->result = new rcube_result_set(1); | $this->result = new rcube_result_set(1); | ||||
$this->result->add($rec); | $this->result->add($rec); | ||||
return $assoc ? $rec : $this->result; | return $assoc ? $rec : $this->result; | ||||
} | } | ||||
return false; | return false; | ||||
} | } | ||||
/** | /** | ||||
* Get group assignments of a specific contact record | * Get group assignments of a specific contact record | ||||
* | * | ||||
* @param mixed Record identifier | * @param mixed Record identifier | ||||
* | * | ||||
* @return array List of assigned groups as ID=>Name pairs | * @return array List of assigned groups as ID=>Name pairs | ||||
*/ | */ | ||||
public function get_record_groups($id) | public function get_record_groups($id) | ||||
{ | { | ||||
$out = array(); | $out = []; | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
if (!empty($this->groupmembers[$id])) { | if (!empty($this->groupmembers[$id])) { | ||||
foreach ((array) $this->groupmembers[$id] as $gid) { | foreach ((array) $this->groupmembers[$id] as $gid) { | ||||
if (!empty($this->distlists[$gid])) { | if (!empty($this->distlists[$gid])) { | ||||
$group = $this->distlists[$gid]; | $group = $this->distlists[$gid]; | ||||
$out[$gid] = $group['name']; | $out[$gid] = $group['name']; | ||||
} | } | ||||
Show All 27 Lines | public function insert($save_data, $check=false) | ||||
if (($res = $this->search('email', $email, true, false)) && $res->count) { | if (($res = $this->search('email', $email, true, false)) && $res->count) { | ||||
$existing = true; | $existing = true; | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
if (!$existing) { | if (!$existing) { | ||||
// remove existing id attributes (#1101) | // Unset contact ID (e.g. when copying/moving from another addressbook) | ||||
unset($save_data['ID'], $save_data['uid']); | unset($save_data['ID'], $save_data['uid']); | ||||
// generate new Kolab contact item | // generate new Kolab contact item | ||||
$object = $this->_from_rcube_contact($save_data); | $object = $this->_from_rcube_contact($save_data); | ||||
$saved = $this->storagefolder->save($object, 'contact'); | $saved = $this->storage->save($object, 'contact'); | ||||
if (!$saved) { | if (!$saved) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'type' => 'php', | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'file' => __FILE__, 'line' => __LINE__, | 'message' => "Error saving contact object to CardDAV server" | ||||
'message' => "Error saving contact object to Kolab server"), | ], | ||||
true, false); | true, false); | ||||
} | } | ||||
else { | else { | ||||
$insert_id = $this->uid2id($object['uid']); | $insert_id = $object['uid']; | ||||
} | } | ||||
} | } | ||||
return $insert_id; | return $insert_id; | ||||
} | } | ||||
/** | /** | ||||
* Update a specific contact record | * Update a specific contact record | ||||
* | * | ||||
* @param mixed Record identifier | * @param mixed Record identifier | ||||
* @param array Associative array with save data | * @param array Associative array with save data | ||||
* Keys: Field name with optional section in the form FIELD:SECTION | * 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 | * Values: Field value. Can be either a string or an array of strings for multiple values | ||||
* | * | ||||
* @return bool True on success, False on error | * @return bool True on success, False on error | ||||
*/ | */ | ||||
public function update($id, $save_data) | public function update($id, $save_data) | ||||
{ | { | ||||
$updated = false; | $updated = false; | ||||
if ($old = $this->storagefolder->get_object($this->id2uid($id))) { | if ($old = $this->storage->get_object($this->id2uid($id))) { | ||||
$object = $this->_from_rcube_contact($save_data, $old); | $object = $this->_from_rcube_contact($save_data, $old); | ||||
if (!$this->storagefolder->save($object, 'contact', $old['uid'])) { | if (!$this->storage->save($object, 'contact', $old['uid'])) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error saving contact object to Kolab server" | 'message' => "Error saving contact object to CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
} | } | ||||
else { | else { | ||||
$updated = true; | $updated = true; | ||||
// TODO: update data in groups this contact is member of | // TODO: update data in groups this contact is member of | ||||
} | } | ||||
} | } | ||||
return $updated; | return $updated; | ||||
} | } | ||||
/** | /** | ||||
* Mark one or more contact records as deleted | * Mark one or more contact records as deleted | ||||
* | * | ||||
* @param array Record identifiers | * @param array Record identifiers | ||||
* @param bool Remove record(s) irreversible (mark as deleted otherwise) | * @param bool Remove record(s) irreversible (mark as deleted otherwise) | ||||
* | * | ||||
* @return int Number of records deleted | * @return int Number of records deleted | ||||
*/ | */ | ||||
public function delete($ids, $force=true) | public function delete($ids, $force = true) | ||||
{ | { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
if (!is_array($ids)) { | if (!is_array($ids)) { | ||||
$ids = explode(',', $ids); | $ids = explode(',', $ids); | ||||
} | } | ||||
$count = 0; | $count = 0; | ||||
foreach ($ids as $id) { | foreach ($ids as $id) { | ||||
if ($uid = $this->id2uid($id)) { | if ($uid = $this->id2uid($id)) { | ||||
$is_mailto = strpos($uid, 'mailto:') === 0; | $is_mailto = strpos($uid, 'mailto:') === 0; | ||||
$deleted = $is_mailto || $this->storagefolder->delete($uid, $force); | $deleted = $is_mailto || $this->storage->delete($uid, $force); | ||||
if (!$deleted) { | if (!$deleted) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error deleting a contact object $uid from the Kolab server" | 'message' => "Error deleting a contact object $uid from the CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
} | } | ||||
else { | else { | ||||
// remove from distribution lists | // remove from distribution lists | ||||
foreach ((array) $this->groupmembers[$id] as $gid) { | foreach ((array) $this->groupmembers[$id] as $gid) { | ||||
if (!$is_mailto || $gid == $this->gid) { | if (!$is_mailto || $gid == $this->gid) { | ||||
$this->remove_from_group($gid, $id); | $this->remove_from_group($gid, $id); | ||||
Show All 22 Lines | */ | ||||
{ | { | ||||
if (!is_array($ids)) { | if (!is_array($ids)) { | ||||
$ids = explode(',', $ids); | $ids = explode(',', $ids); | ||||
} | } | ||||
$count = 0; | $count = 0; | ||||
foreach ($ids as $id) { | foreach ($ids as $id) { | ||||
$uid = $this->id2uid($id); | $uid = $this->id2uid($id); | ||||
if ($this->storagefolder->undelete($uid)) { | if ($this->storage->undelete($uid)) { | ||||
$count++; | $count++; | ||||
} | } | ||||
else { | else { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error undeleting a contact object $uid from the Kolab server" | 'message' => "Error undeleting a contact object $uid from the CardDav server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
} | } | ||||
} | } | ||||
return $count; | return $count; | ||||
} | } | ||||
/** | /** | ||||
* Remove all records from the database | * Remove all records from the database | ||||
* | * | ||||
* @param bool $with_groups Remove also groups | * @param bool $with_groups Remove also groups | ||||
*/ | */ | ||||
public function delete_all($with_groups = false) | public function delete_all($with_groups = false) | ||||
{ | { | ||||
if ($this->storagefolder->delete_all()) { | if ($this->storage->delete_all()) { | ||||
$this->contacts = array(); | $this->contacts = []; | ||||
$this->sortindex = array(); | $this->sortindex = []; | ||||
$this->dataset = null; | $this->dataset = null; | ||||
$this->result = null; | $this->result = null; | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Close connection to source | * Close connection to source | ||||
* Called on script shutdown | * Called on script shutdown | ||||
*/ | */ | ||||
public function close() | public function close() | ||||
{ | { | ||||
// NOP | |||||
} | } | ||||
/** | /** | ||||
* Create a contact group with the given name | * Create a contact group with the given name | ||||
* | * | ||||
* @param string The group name | * @param string The group name | ||||
* | * | ||||
* @return mixed False on error, array with record props in success | * @return mixed False on error, array with record props in success | ||||
*/ | */ | ||||
function create_group($name) | function create_group($name) | ||||
{ | { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$result = false; | $result = false; | ||||
$list = array( | $list = [ | ||||
'name' => $name, | 'name' => $name, | ||||
'member' => array(), | 'member' => [], | ||||
); | ]; | ||||
$saved = $this->storagefolder->save($list, 'distribution-list'); | $saved = $this->storage->save($list, 'distribution-list'); | ||||
if (!$saved) { | if (!$saved) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error saving distribution-list object to Kolab server" | 'message' => "Error saving distribution-list object to CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
return false; | return false; | ||||
} | } | ||||
else { | else { | ||||
$id = $this->uid2id($list['uid']); | $id = $this->uid2id($list['uid']); | ||||
$this->distlists[$id] = $list; | $this->distlists[$id] = $list; | ||||
$result = array('id' => $id, 'name' => $name); | $result = ['id' => $id, 'name' => $name]; | ||||
} | } | ||||
return $result; | return $result; | ||||
} | } | ||||
/** | /** | ||||
* Delete the given group and all linked group members | * Delete the given group and all linked group members | ||||
* | * | ||||
* @param string Group identifier | * @param string Group identifier | ||||
* | * | ||||
* @return bool True on success, false if no data was changed | * @return bool True on success, false if no data was changed | ||||
*/ | */ | ||||
function delete_group($gid) | function delete_group($gid) | ||||
{ | { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$result = false; | $result = false; | ||||
if ($list = $this->distlists[$gid]) { | if ($list = $this->distlists[$gid]) { | ||||
$deleted = $this->storagefolder->delete($list['uid']); | $deleted = $this->storage->delete($list['uid']); | ||||
} | } | ||||
if (!$deleted) { | if (!$deleted) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error deleting distribution-list object from the Kolab server" | 'message' => "Error deleting distribution-list object from the CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
} | } | ||||
else { | else { | ||||
$result = true; | $result = true; | ||||
} | } | ||||
return $result; | return $result; | ||||
Show All 10 Lines | */ | ||||
*/ | */ | ||||
function rename_group($gid, $newname, &$newid) | function rename_group($gid, $newname, &$newid) | ||||
{ | { | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
$list = $this->distlists[$gid]; | $list = $this->distlists[$gid]; | ||||
if ($newname != $list['name']) { | if ($newname != $list['name']) { | ||||
$list['name'] = $newname; | $list['name'] = $newname; | ||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']); | $saved = $this->storage->save($list, 'distribution-list', $list['uid']); | ||||
} | } | ||||
if (!$saved) { | if (!$saved) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error saving distribution-list object to Kolab server" | 'message' => "Error saving distribution-list object to CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
return false; | return false; | ||||
} | } | ||||
return $newname; | return $newname; | ||||
} | } | ||||
Show All 9 Lines | function add_to_group($gid, $ids) | ||||
if (!is_array($ids)) { | if (!is_array($ids)) { | ||||
$ids = explode(',', $ids); | $ids = explode(',', $ids); | ||||
} | } | ||||
$this->_fetch_groups(true); | $this->_fetch_groups(true); | ||||
$list = $this->distlists[$gid]; | $list = $this->distlists[$gid]; | ||||
$added = 0; | $added = 0; | ||||
$uids = array(); | $uids = []; | ||||
$exists = array(); | $exists = []; | ||||
foreach ((array)$list['member'] as $member) { | foreach ((array)$list['member'] as $member) { | ||||
$exists[] = $member['ID']; | $exists[] = $member['ID']; | ||||
} | } | ||||
// substract existing assignments from list | // substract existing assignments from list | ||||
$ids = array_unique(array_diff($ids, $exists)); | $ids = array_unique(array_diff($ids, $exists)); | ||||
// add mailto: members | // add mailto: members | ||||
foreach ($ids as $contact_id) { | foreach ($ids as $contact_id) { | ||||
$uid = $this->id2uid($contact_id); | $uid = $this->id2uid($contact_id); | ||||
if (strpos($uid, 'mailto:') === 0 && ($contact = $this->contacts[$contact_id])) { | if (strpos($uid, 'mailto:') === 0 && ($contact = $this->contacts[$contact_id])) { | ||||
$list['member'][] = array( | $list['member'][] = [ | ||||
'email' => $contact['email'], | 'email' => $contact['email'], | ||||
'name' => $contact['name'], | 'name' => $contact['name'], | ||||
); | ]; | ||||
$this->groupmembers[$contact_id][] = $gid; | $this->groupmembers[$contact_id][] = $gid; | ||||
$added++; | $added++; | ||||
} | } | ||||
else { | else { | ||||
$uids[$uid] = $contact_id; | $uids[$uid] = $contact_id; | ||||
} | } | ||||
} | } | ||||
// add members with UID | // add members with UID | ||||
if (!empty($uids)) { | if (!empty($uids)) { | ||||
foreach ($uids as $uid => $contact_id) { | foreach ($uids as $uid => $contact_id) { | ||||
$list['member'][] = array('uid' => $uid); | $list['member'][] = ['uid' => $uid]; | ||||
$this->groupmembers[$contact_id][] = $gid; | $this->groupmembers[$contact_id][] = $gid; | ||||
$added++; | $added++; | ||||
} | } | ||||
} | } | ||||
if ($added) { | if ($added) { | ||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']); | $saved = $this->storage->save($list, 'distribution-list', $list['uid']); | ||||
} | } | ||||
else { | else { | ||||
$saved = true; | $saved = true; | ||||
} | } | ||||
if (!$saved) { | if (!$saved) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error saving distribution-list to Kolab server" | 'message' => "Error saving distribution-list to CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
$added = false; | $added = false; | ||||
$this->set_error(self::ERROR_SAVING, 'errorsaving'); | $this->set_error(self::ERROR_SAVING, 'errorsaving'); | ||||
} | } | ||||
else { | else { | ||||
$this->distlists[$gid] = $list; | $this->distlists[$gid] = $list; | ||||
} | } | ||||
return $added; | return $added; | ||||
} | } | ||||
/** | /** | ||||
* Remove the given contact records from a certain group | * Remove the given contact records from a certain group | ||||
* | * | ||||
* @param string Group identifier | * @param string Group identifier | ||||
* @param array List of contact identifiers to be removed | * @param array List of contact identifiers to be removed | ||||
* @return int Number of deleted group members | * | ||||
* @return bool | |||||
*/ | */ | ||||
function remove_from_group($gid, $ids) | function remove_from_group($gid, $ids) | ||||
{ | { | ||||
if (!is_array($ids)) { | if (!is_array($ids)) { | ||||
$ids = explode(',', $ids); | $ids = explode(',', $ids); | ||||
} | } | ||||
$this->_fetch_groups(); | $this->_fetch_groups(); | ||||
if (!($list = $this->distlists[$gid])) { | if (!($list = $this->distlists[$gid])) { | ||||
return false; | return false; | ||||
} | } | ||||
$new_member = array(); | $new_member = []; | ||||
foreach ((array)$list['member'] as $member) { | foreach ((array) $list['member'] as $member) { | ||||
if (!in_array($member['ID'], $ids)) { | if (!in_array($member['ID'], $ids)) { | ||||
$new_member[] = $member; | $new_member[] = $member; | ||||
} | } | ||||
} | } | ||||
// write distribution list back to server | // write distribution list back to server | ||||
$list['member'] = $new_member; | $list['member'] = $new_member; | ||||
$saved = $this->storagefolder->save($list, 'distribution-list', $list['uid']); | $saved = $this->storage->save($list, 'distribution-list', $list['uid']); | ||||
if (!$saved) { | if (!$saved) { | ||||
rcube::raise_error(array( | rcube::raise_error([ | ||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, | ||||
'message' => "Error saving distribution-list object to Kolab server" | 'message' => "Error saving distribution-list object to CardDAV server" | ||||
), | ], | ||||
true, false | true, false | ||||
); | ); | ||||
} | } | ||||
else { | else { | ||||
// remove group assigments in local cache | // remove group assigments in local cache | ||||
foreach ($ids as $id) { | foreach ($ids as $id) { | ||||
$j = array_search($gid, $this->groupmembers[$id]); | $j = array_search($gid, $this->groupmembers[$id]); | ||||
unset($this->groupmembers[$id][$j]); | unset($this->groupmembers[$id][$j]); | ||||
Show All 32 Lines | public function validate(&$save_data, $autofix = false) | ||||
} | } | ||||
return $valid; | return $valid; | ||||
} | } | ||||
/** | /** | ||||
* Query storage layer and store records in private member var | * Query storage layer and store records in private member var | ||||
*/ | */ | ||||
private function _fetch_contacts($query = array(), $limit = false, $fast_mode = false) | private function _fetch_contacts($query = [], $limit = false, $fast_mode = false) | ||||
{ | { | ||||
if (!isset($this->dataset) || !empty($query)) { | if (!isset($this->dataset) || !empty($query)) { | ||||
if ($limit) { | if ($limit) { | ||||
$size = is_int($limit) && $limit < $this->page_size ? $limit : $this->page_size; | $size = is_int($limit) && $limit < $this->page_size ? $limit : $this->page_size; | ||||
$this->storagefolder->set_order_and_limit($this->_sort_columns(), $size, ($this->list_page-1) * $this->page_size); | $this->storage->set_order_and_limit($this->_sort_columns(), $size, ($this->list_page-1) * $this->page_size); | ||||
} | } | ||||
$this->sortindex = array(); | $this->sortindex = []; | ||||
$this->dataset = $this->storagefolder->select($query, $fast_mode); | $this->dataset = $this->storage->select($query, $fast_mode); | ||||
foreach ($this->dataset as $idx => $record) { | foreach ($this->dataset as $idx => $record) { | ||||
$contact = $this->_to_rcube_contact($record); | $contact = $this->_to_rcube_contact($record); | ||||
$this->sortindex[$idx] = $this->_sort_string($contact); | $this->sortindex[$idx] = $this->_sort_string($contact); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
Show All 16 Lines | private function _sort_string($rec) | ||||
break; | break; | ||||
default: | default: | ||||
$str = $rec[$this->sort_col]; | $str = $rec[$this->sort_col]; | ||||
break; | break; | ||||
} | } | ||||
$str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email']; | $str .= is_array($rec['email']) ? $rec['email'][0] : $rec['email']; | ||||
return mb_strtolower($str); | return mb_strtolower($str); | ||||
} | } | ||||
/** | /** | ||||
* Return the cache table columns to order by | * Return the cache table columns to order by | ||||
*/ | */ | ||||
private function _sort_columns() | private function _sort_columns() | ||||
{ | { | ||||
$sortcols = array(); | $sortcols = []; | ||||
switch ($this->sort_col) { | switch ($this->sort_col) { | ||||
case 'name': | case 'name': | ||||
$sortcols[] = 'name'; | $sortcols[] = 'name'; | ||||
case 'firstname': | case 'firstname': | ||||
$sortcols[] = 'firstname'; | $sortcols[] = 'firstname'; | ||||
break; | break; | ||||
case 'surname': | case 'surname': | ||||
$sortcols[] = 'surname'; | $sortcols[] = 'surname'; | ||||
break; | break; | ||||
} | } | ||||
$sortcols[] = 'email'; | $sortcols[] = 'email'; | ||||
return $sortcols; | return $sortcols; | ||||
} | } | ||||
/** | /** | ||||
* Read distribution-lists AKA groups from server | * Read distribution-lists AKA groups from server | ||||
*/ | */ | ||||
private function _fetch_groups($with_contacts = false) | private function _fetch_groups($with_contacts = false) | ||||
{ | { | ||||
return; // TODO | |||||
if (!isset($this->distlists)) { | if (!isset($this->distlists)) { | ||||
$this->distlists = $this->groupmembers = array(); | $this->distlists = $this->groupmembers = []; | ||||
foreach ($this->storagefolder->select('distribution-list', true) as $record) { | foreach ($this->storage->select('distribution-list', true) as $record) { | ||||
$record['ID'] = $this->uid2id($record['uid']); | $record['ID'] = $this->uid2id($record['uid']); | ||||
foreach ((array)$record['member'] as $i => $member) { | foreach ((array)$record['member'] as $i => $member) { | ||||
$mid = $this->uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']); | $mid = $this->uid2id($member['uid'] ? $member['uid'] : 'mailto:' . $member['email']); | ||||
$record['member'][$i]['ID'] = $mid; | $record['member'][$i]['ID'] = $mid; | ||||
$record['member'][$i]['readonly'] = empty($member['uid']); | $record['member'][$i]['readonly'] = empty($member['uid']); | ||||
$this->groupmembers[$mid][] = $record['ID']; | $this->groupmembers[$mid][] = $record['ID']; | ||||
if ($with_contacts && empty($member['uid'])) { | if ($with_contacts && empty($member['uid'])) { | ||||
Show All 21 Lines | public function id2uid($id) | ||||
return base64_decode(str_pad(strtr($id, '-_', '+/'), strlen($id) % 4, '=', STR_PAD_RIGHT)); | return base64_decode(str_pad(strtr($id, '-_', '+/'), strlen($id) % 4, '=', STR_PAD_RIGHT)); | ||||
} | } | ||||
/** | /** | ||||
* Build SQL query for fulltext matches | * Build SQL query for fulltext matches | ||||
*/ | */ | ||||
private function _search_query($fields, $value, $mode) | private function _search_query($fields, $value, $mode) | ||||
{ | { | ||||
$query = array(); | $query = []; | ||||
$cols = array(); | $cols = []; | ||||
// $fulltext_cols might contain composite field names e.g. 'email:address' while $fields not | $cols = array_intersect($fields, $this->fulltext_cols); | ||||
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)) { | if (count($cols)) { | ||||
if ($mode & rcube_addressbook::SEARCH_STRICT) { | if ($mode & rcube_addressbook::SEARCH_STRICT) { | ||||
$prefix = '^'; $suffix = '$'; | $prefix = '^'; $suffix = '$'; | ||||
} | } | ||||
else if ($mode & rcube_addressbook::SEARCH_PREFIX) { | else if ($mode & rcube_addressbook::SEARCH_PREFIX) { | ||||
$prefix = '^'; $suffix = ''; | $prefix = '^'; $suffix = ''; | ||||
} | } | ||||
else { | else { | ||||
$prefix = ''; $suffix = ''; | $prefix = ''; $suffix = ''; | ||||
} | } | ||||
$search_string = is_array($value) ? join(' ', $value) : $value; | $search_string = is_array($value) ? join(' ', $value) : $value; | ||||
foreach (rcube_utils::normalize_string($search_string, true) as $word) { | foreach (rcube_utils::normalize_string($search_string, true) as $word) { | ||||
$query[] = array('words', 'LIKE', $prefix . $word . $suffix); | $query[] = ['words', 'LIKE', $prefix . $word . $suffix]; | ||||
} | } | ||||
} | } | ||||
return $query; | return $query; | ||||
} | } | ||||
/** | /** | ||||
* Map fields from internal Kolab_Format to Roundcube contact format | * Map fields from internal Kolab_Format to Roundcube contact format | ||||
*/ | */ | ||||
private function _to_rcube_contact($record) | private function _to_rcube_contact($record) | ||||
{ | { | ||||
$record['ID'] = $this->uid2id($record['uid']); | $record['ID'] = $this->uid2id($record['uid']); | ||||
// 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]; | |||||
} | |||||
} | |||||
} | |||||
if (is_array($record['address'])) { | |||||
$addresses = $record['address']; | |||||
unset($record['address']); | |||||
foreach ($addresses as $i => $adr) { | |||||
$key = 'address' . ($adr['type'] ? ':' . $adr['type'] : ''); | |||||
$record[$key][] = array( | |||||
'street' => $adr['street'], | |||||
'locality' => $adr['locality'], | |||||
'zipcode' => $adr['code'], | |||||
'region' => $adr['region'], | |||||
'country' => $adr['country'], | |||||
); | |||||
} | |||||
} | |||||
// photo is stored as separate attachment | |||||
if ($record['photo'] && strlen($record['photo']) < 255 && !empty($record['_attachments'][$record['photo']])) { | |||||
$att = $record['_attachments'][$record['photo']]; | |||||
// only fetch photo content if requested | |||||
if ($this->action == 'photo') { | |||||
if (!empty($att['content'])) { | |||||
$record['photo'] = $att['content']; | |||||
} | |||||
else { | |||||
$record['photo'] = $this->storagefolder->get_attachment($record['uid'], $att['id']); | |||||
} | |||||
} | |||||
} | |||||
// truncate publickey value for display | |||||
if (!empty($record['pgppublickey']) && $this->action == 'show') { | |||||
$record['pgppublickey'] = substr($record['pgppublickey'], 0, 140) . '...'; | |||||
} | |||||
// remove empty fields | // remove empty fields | ||||
$record = array_filter($record); | $record = array_filter($record); | ||||
// remove kolab_storage internal data | // Set _type for proper icon on the list | ||||
unset($record['_msguid'], $record['_formatobj'], $record['_mailbox'], $record['_type'], $record['_size']); | $record['_type'] = 'person'; | ||||
return $record; | return $record; | ||||
} | } | ||||
/** | /** | ||||
* Map fields from Roundcube format to internal kolab_format_contact properties | * Map fields from Roundcube format to internal kolab_format_contact properties | ||||
*/ | */ | ||||
private function _from_rcube_contact($contact, $old = array()) | private function _from_rcube_contact($contact, $old = []) | ||||
{ | { | ||||
if (!$contact['uid'] && $contact['ID']) { | if (empty($contact['uid']) && !empty($contact['ID'])) { | ||||
$contact['uid'] = $this->id2uid($contact['ID']); | $contact['uid'] = $this->id2uid($contact['ID']); | ||||
} | } | ||||
else if (!$contact['uid'] && $old['uid']) { | else if (empty($contact['uid']) && !empty($old['uid'])) { | ||||
$contact['uid'] = $old['uid']; | $contact['uid'] = $old['uid']; | ||||
} | } | ||||
else if (empty($contact['uid'])) { | |||||
$contact['im'] = array_filter($this->get_col_values('im', $contact, true)); | $rcube = rcube::get_instance(); | ||||
$contact['uid'] = strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($rcube->user->get_username()), 0, 16)); | |||||
// convert email, website, phone values | |||||
foreach (array('email'=>'address', 'website'=>'url', 'phone'=>'number') as $col => $propname) { | |||||
$col_values = $this->get_col_values($col, $contact); | |||||
$contact[$col] = array(); | |||||
foreach ($col_values as $type => $values) { | |||||
foreach ((array)$values as $val) { | |||||
if (!empty($val)) { | |||||
$contact[$col][] = array($propname => $val, 'type' => $type); | |||||
} | |||||
} | |||||
unset($contact[$col.':'.$type]); | |||||
} | |||||
} | |||||
$addresses = array(); | |||||
foreach ($this->get_col_values('address', $contact) as $type => $values) { | |||||
foreach ((array)$values as $adr) { | |||||
// skip empty address | |||||
$adr = array_filter($adr); | |||||
if (empty($adr)) { | |||||
continue; | |||||
} | |||||
$addresses[] = array( | |||||
'type' => $type, | |||||
'street' => $adr['street'], | |||||
'locality' => $adr['locality'], | |||||
'code' => $adr['zipcode'], | |||||
'region' => $adr['region'], | |||||
'country' => $adr['country'], | |||||
); | |||||
} | |||||
unset($contact['address:'.$type]); | |||||
} | |||||
$contact['address'] = $addresses; | |||||
// categories are not supported in the web client but should be preserved (#2608) | |||||
$contact['categories'] = $old['categories']; | |||||
// copy meta data (starting with _) from old object | |||||
foreach ((array)$old as $key => $val) { | |||||
if (!isset($contact[$key]) && $key[0] == '_') { | |||||
$contact[$key] = $val; | |||||
} | |||||
} | |||||
// 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])); | |||||
} | |||||
} | } | ||||
// When importing contacts 'vcard' data is added, we don't need it (Bug #1711) | // When importing contacts 'vcard' data might be added, we don't need it (Bug #1711) | ||||
unset($contact['vcard']); | unset($contact['vcard']); | ||||
// add empty values for some fields which can be removed in the UI | return $contact; | ||||
return array_filter($contact) + array( | |||||
'nickname' => '', | |||||
'birthday' => '', | |||||
'anniversary' => '', | |||||
'freebusyurl' => '', | |||||
'photo' => $contact['photo'] | |||||
); | |||||
} | } | ||||
} | } |