CardDAV support
This commit is contained in:
parent
f53ff8edec
commit
0159e3c115
9 changed files with 268 additions and 142 deletions
|
@ -1,5 +1,10 @@
|
|||
<?php
|
||||
|
||||
// Backend type (kolab, carddav)
|
||||
$config['kolab_addressbook_driver'] = "kolab";
|
||||
|
||||
// CalDAV server location (required when kolab_addressbook_driver = carddav)
|
||||
$config['kolab_addressbook_carddav_server'] = "http://localhost";
|
||||
|
||||
// This option allows to set addressbooks priority or to disable some
|
||||
// of them. Disabled addressbooks will be not shown in the UI. Default: 0.
|
||||
|
@ -15,7 +20,8 @@ $config['kolab_addressbook_prio'] = 0;
|
|||
// %u - Current webmail user name
|
||||
// %n - Folder name
|
||||
// %i - Folder UUID
|
||||
// $config['kolab_addressbook_carddav_url'] = 'http://%h/iRony/addressbooks/%u/%i';
|
||||
// For example: 'http://%h/iRony/addressbooks/%u/%i'
|
||||
$config['kolab_addressbook_carddav_url'] = null;
|
||||
|
||||
// Name of LDAP addressbook (a key in ldap_public configuration array) for which
|
||||
// the CardDAV URI will be displayed if kolab_addressbook_carddav_url is set.
|
||||
|
@ -31,5 +37,3 @@ $config['kolab_addressbook_prio'] = 0;
|
|||
// ignore these properties and allow modifications which then result in sync errors because the server
|
||||
// denies such updates.
|
||||
$config['kolab_addressbook_carddav_ldap'] = '';
|
||||
|
||||
?>
|
||||
|
|
|
@ -31,12 +31,13 @@ class kolab_addressbook extends rcube_plugin
|
|||
{
|
||||
public $task = '?(?!logout).*';
|
||||
|
||||
public $driver;
|
||||
public $bonnie_api = false;
|
||||
|
||||
private $sources;
|
||||
private $folders;
|
||||
private $rc;
|
||||
private $ui;
|
||||
|
||||
public $bonnie_api = false;
|
||||
private $driver_class;
|
||||
|
||||
const GLOBAL_FIRST = 0;
|
||||
const PERSONAL_FIRST = 1;
|
||||
|
@ -53,8 +54,11 @@ class kolab_addressbook extends rcube_plugin
|
|||
// load required plugin
|
||||
$this->require_plugin('libkolab');
|
||||
|
||||
$driver = $this->rc->config->get('kolab_addressbook_driver') ?: 'kolab';
|
||||
require_once(dirname(__FILE__) . '/lib/rcube_' . $driver . '_contacts.php');
|
||||
$this->load_config();
|
||||
|
||||
$this->driver = $this->rc->config->get('kolab_addressbook_driver') ?: 'kolab';
|
||||
$this->driver_class = 'rcube_' . $this->driver . '_contacts';
|
||||
require_once(dirname(__FILE__) . '/lib/' . $this->driver_class . '.php');
|
||||
|
||||
// register hooks
|
||||
$this->add_hook('addressbooks_list', array($this, 'address_sources'));
|
||||
|
@ -82,7 +86,6 @@ class kolab_addressbook extends rcube_plugin
|
|||
|
||||
// Load UI elements
|
||||
if ($this->api->output->type == 'html') {
|
||||
$this->load_config();
|
||||
require_once($this->home . '/lib/kolab_addressbook_ui.php');
|
||||
$this->ui = new kolab_addressbook_ui($this);
|
||||
|
||||
|
@ -104,9 +107,11 @@ class kolab_addressbook extends rcube_plugin
|
|||
$this->add_hook('preferences_save', array($this, 'prefs_save'));
|
||||
}
|
||||
|
||||
$this->add_hook('folder_delete', array($this, 'prefs_folder_delete'));
|
||||
$this->add_hook('folder_rename', array($this, 'prefs_folder_rename'));
|
||||
$this->add_hook('folder_update', array($this, 'prefs_folder_update'));
|
||||
if ($this->driver == 'kolab') {
|
||||
$this->add_hook('folder_delete', array($this, 'prefs_folder_delete'));
|
||||
$this->add_hook('folder_rename', array($this, 'prefs_folder_rename'));
|
||||
$this->add_hook('folder_update', array($this, 'prefs_folder_update'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,10 +217,22 @@ class kolab_addressbook extends rcube_plugin
|
|||
$out .= $this->addressbook_list_item($id, $source, $jsdata) . '</li>';
|
||||
}
|
||||
|
||||
$filter = function($source) { return !empty($source['kolab']) && empty($source['hidden']); };
|
||||
$folders = array_filter($sources, $filter);
|
||||
|
||||
// render a hierarchical list of kolab contact folders
|
||||
kolab_storage::folder_hierarchy($this->folders, $tree);
|
||||
if ($tree && !empty($tree->children)) {
|
||||
$out .= $this->folder_tree_html($tree, $sources, $jsdata);
|
||||
// TODO: Move this to the drivers
|
||||
if ($this->driver == 'kolab') {
|
||||
kolab_storage::folder_hierarchy($folders, $tree);
|
||||
if ($tree && !empty($tree->children)) {
|
||||
$out .= $this->folder_tree_html($tree, $sources, $jsdata);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($folders as $j => $source) {
|
||||
$id = strval(strlen($source['id']) ? $source['id'] : $j);
|
||||
$out .= $this->addressbook_list_item($id, $source, $jsdata) . '</li>';
|
||||
}
|
||||
}
|
||||
|
||||
$this->rc->output->set_env('contactgroups', array_filter($jsdata, function($src){ return $src['type'] == 'group'; }));
|
||||
|
@ -303,7 +320,7 @@ class kolab_addressbook extends rcube_plugin
|
|||
), $name)
|
||||
);
|
||||
|
||||
if (isset($source['subscribed'])) {
|
||||
if ($this->driver == 'kolab' && isset($source['subscribed'])) {
|
||||
$inner .= html::span(array(
|
||||
'class' => 'subscribed',
|
||||
'title' => $this->gettext('foldersubscribe'),
|
||||
|
@ -396,7 +413,6 @@ class kolab_addressbook extends rcube_plugin
|
|||
return $args;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Getter for the rcube_addressbook instance
|
||||
*
|
||||
|
@ -407,16 +423,8 @@ class kolab_addressbook extends rcube_plugin
|
|||
public function get_address_book($p)
|
||||
{
|
||||
if ($p['id']) {
|
||||
$id = kolab_storage::id_decode($p['id']);
|
||||
$folder = kolab_storage::get_folder($id);
|
||||
|
||||
// try with unencoded (old-style) identifier
|
||||
if ((!$folder || $folder->type != 'contact') && $id != $p['id']) {
|
||||
$folder = kolab_storage::get_folder($p['id']);
|
||||
}
|
||||
|
||||
if ($folder && $folder->type == 'contact') {
|
||||
$p['instance'] = new rcube_kolab_contacts($folder->name);
|
||||
if ($source = $this->driver_class::get_address_book($p['id'])) {
|
||||
$p['instance'] = $source;
|
||||
|
||||
// flag source as writeable if 'i' right is given
|
||||
if ($p['writeable'] && $this->rc->action == 'save' && strpos($p['instance']->rights, 'i') !== false) {
|
||||
|
@ -431,57 +439,33 @@ class kolab_addressbook extends rcube_plugin
|
|||
return $p;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List addressbook sources list
|
||||
*/
|
||||
private function _list_sources()
|
||||
{
|
||||
// already read sources
|
||||
if (isset($this->sources))
|
||||
if (isset($this->sources)) {
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
kolab_storage::$encode_ids = true;
|
||||
$this->sources = array();
|
||||
$this->folders = array();
|
||||
$this->sources = [];
|
||||
|
||||
$abook_prio = $this->addressbook_prio();
|
||||
|
||||
// Personal address source(s) disabled?
|
||||
if ($abook_prio == self::GLOBAL_ONLY) {
|
||||
if ($abook_prio == kolab_addressbook::GLOBAL_ONLY) {
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
// get all folders that have "contact" type
|
||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('contact'));
|
||||
|
||||
if (PEAR::isError($folders)) {
|
||||
rcube::raise_error(array(
|
||||
'code' => 600, 'type' => 'php',
|
||||
'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Failed to list contact folders from Kolab server:" . $folders->getMessage()),
|
||||
true, false);
|
||||
}
|
||||
else {
|
||||
// we need at least one folder to prevent from errors in Roundcube core
|
||||
// when there's also no sql nor ldap addressbook (Bug #2086)
|
||||
if (empty($folders)) {
|
||||
if ($folder = kolab_storage::create_default_folder('contact')) {
|
||||
$folders = array(new kolab_storage_folder($folder, 'contact'));
|
||||
}
|
||||
}
|
||||
|
||||
// convert to UTF8 and sort
|
||||
foreach ($folders as $folder) {
|
||||
// create instance of rcube_contacts
|
||||
$abook_id = $folder->id;
|
||||
$abook = new rcube_kolab_contacts($folder->name);
|
||||
$this->sources[$abook_id] = $abook;
|
||||
$this->folders[$abook_id] = $folder;
|
||||
}
|
||||
foreach ($this->driver_class::list_folders() as $id => $source) {
|
||||
$this->sources[$id] = $source;
|
||||
}
|
||||
|
||||
return $this->sources;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Plugin hook called before rendering the contact form or detail view
|
||||
*
|
||||
|
@ -801,17 +785,17 @@ class kolab_addressbook extends rcube_plugin
|
|||
*/
|
||||
private function _sort_form_fields($contents, $source)
|
||||
{
|
||||
$block = array();
|
||||
$block = [];
|
||||
|
||||
foreach (array_keys($source->coltypes) as $col) {
|
||||
if (isset($contents[$col]))
|
||||
$block[$col] = $contents[$col];
|
||||
}
|
||||
foreach (array_keys($source->coltypes) as $col) {
|
||||
if (isset($contents[$col])) {
|
||||
$block[$col] = $contents[$col];
|
||||
}
|
||||
}
|
||||
|
||||
return $block;
|
||||
return $block;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handler for user preferences form (preferences_list hook)
|
||||
*
|
||||
|
@ -959,9 +943,9 @@ class kolab_addressbook extends rcube_plugin
|
|||
*/
|
||||
public function book_search()
|
||||
{
|
||||
$results = array();
|
||||
$query = rcube_utils::get_input_value('q', rcube_utils::INPUT_GPC);
|
||||
$source = rcube_utils::get_input_value('source', rcube_utils::INPUT_GPC);
|
||||
$results = [];
|
||||
$query = rcube_utils::get_input_value('q', rcube_utils::INPUT_GPC);
|
||||
$source = rcube_utils::get_input_value('source', rcube_utils::INPUT_GPC);
|
||||
|
||||
kolab_storage::$encode_ids = true;
|
||||
$search_more_results = false;
|
||||
|
@ -1109,12 +1093,6 @@ class kolab_addressbook extends rcube_plugin
|
|||
*/
|
||||
private function addressbook_prio()
|
||||
{
|
||||
// Load configuration
|
||||
if (!$this->config_loaded) {
|
||||
$this->load_config();
|
||||
$this->config_loaded = true;
|
||||
}
|
||||
|
||||
$abook_prio = (int) $this->rc->config->get('kolab_addressbook_prio');
|
||||
|
||||
// Make sure any global addressbooks are defined
|
||||
|
|
|
@ -54,6 +54,10 @@ class kolab_addressbook_ui
|
|||
// Include stylesheet (for directorylist)
|
||||
$this->plugin->include_stylesheet($this->plugin->local_skin_path().'/kolab_addressbook.css');
|
||||
|
||||
if ($this->plugin->driver != 'kolab') {
|
||||
return;
|
||||
}
|
||||
|
||||
// include kolab folderlist widget if available
|
||||
if (in_array('libkolab', $this->plugin->api->loaded_plugins())) {
|
||||
$this->plugin->api->include_script('libkolab/libkolab.js');
|
||||
|
@ -66,20 +70,20 @@ class kolab_addressbook_ui
|
|||
$idx = 0;
|
||||
|
||||
if ($dav_url = $this->rc->config->get('kolab_addressbook_carddav_url')) {
|
||||
$options[] = 'book-showurl';
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_url', true);
|
||||
$options[] = 'book-showurl';
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_url', true);
|
||||
|
||||
// set CardDAV URI for specified ldap addressbook
|
||||
if ($ldap_abook = $this->rc->config->get('kolab_addressbook_carddav_ldap')) {
|
||||
$dav_ldap_url = strtr($dav_url, array(
|
||||
'%h' => $_SERVER['HTTP_HOST'],
|
||||
'%u' => urlencode($this->rc->get_user_name()),
|
||||
'%i' => 'ldap-directory',
|
||||
'%n' => '',
|
||||
));
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_ldap', $ldap_abook);
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_ldap_url', $dav_ldap_url);
|
||||
}
|
||||
// set CardDAV URI for specified ldap addressbook
|
||||
if ($ldap_abook = $this->rc->config->get('kolab_addressbook_carddav_ldap')) {
|
||||
$dav_ldap_url = strtr($dav_url, array(
|
||||
'%h' => $_SERVER['HTTP_HOST'],
|
||||
'%u' => urlencode($this->rc->get_user_name()),
|
||||
'%i' => 'ldap-directory',
|
||||
'%n' => '',
|
||||
));
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_ldap', $ldap_abook);
|
||||
$this->rc->output->set_env('kolab_addressbook_carddav_ldap_url', $dav_ldap_url);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($options as $command) {
|
||||
|
|
|
@ -312,6 +312,64 @@ class rcube_kolab_contacts extends rcube_addressbook
|
|||
$this->filter = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List addressbook sources (folders)
|
||||
*/
|
||||
public static function list_folders()
|
||||
{
|
||||
kolab_storage::$encode_ids = true;
|
||||
|
||||
// get all folders that have "contact" type
|
||||
$folders = kolab_storage::sort_folders(kolab_storage::get_folders('contact'));
|
||||
|
||||
if (PEAR::isError($folders)) {
|
||||
rcube::raise_error([
|
||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||
'message' => "Failed to list contact folders from Kolab server:" . $folders->getMessage()
|
||||
],
|
||||
true, false);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// we need at least one folder to prevent from errors in Roundcube core
|
||||
// when there's also no sql nor ldap addressbook (Bug #2086)
|
||||
if (empty($folders)) {
|
||||
if ($folder = kolab_storage::create_default_folder('contact')) {
|
||||
$folders = [new kolab_storage_folder($folder, 'contact')];
|
||||
}
|
||||
}
|
||||
|
||||
$sources = [];
|
||||
foreach ($folders as $folder) {
|
||||
$sources[$folder->id] = new rcube_kolab_contacts($folder->name);
|
||||
}
|
||||
|
||||
return $sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the rcube_addressbook instance
|
||||
*
|
||||
* @param string $id Addressbook (folder) ID
|
||||
*
|
||||
* @return ?rcube_kolab_contacts
|
||||
*/
|
||||
public static function get_address_book($id)
|
||||
{
|
||||
$folderId = kolab_storage::id_decode($id);
|
||||
$folder = kolab_storage::get_folder($folderId);
|
||||
|
||||
// try with unencoded (old-style) identifier
|
||||
if ((!$folder || $folder->type != 'contact') && $folderId != $id) {
|
||||
$folder = kolab_storage::get_folder($id);
|
||||
}
|
||||
|
||||
if ($folder && $folder->type == 'contact') {
|
||||
return new rcube_kolab_contacts($folder->name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List all active contact groups of this source
|
||||
*
|
||||
|
|
|
@ -73,7 +73,6 @@ class kolab_dav_client
|
|||
}
|
||||
|
||||
try {
|
||||
|
||||
$request = $this->initRequest($this->url . $path, $method, $request_config);
|
||||
|
||||
$request->setAuth($this->user, $this->password);
|
||||
|
@ -135,7 +134,8 @@ class kolab_dav_client
|
|||
. '</d:prop>'
|
||||
. '</d:propfind>';
|
||||
|
||||
$response = $this->request('/' . $roots[$component], 'PROPFIND', $body);
|
||||
// Note: Cyrus CardDAV service requires Depth:1 (CalDAV works without it)
|
||||
$response = $this->request('/' . $roots[$component], 'PROPFIND', $body, ['Depth' => 1, 'Prefer' => 'return-minimal']);
|
||||
|
||||
$elements = $response->getElementsByTagName('response');
|
||||
|
||||
|
@ -150,10 +150,22 @@ class kolab_dav_client
|
|||
$principal_href = substr($principal_href, strlen($path));
|
||||
}
|
||||
|
||||
$homes = [
|
||||
'VEVENT' => 'calendar-home-set',
|
||||
'VTODO' => 'calendar-home-set',
|
||||
'VCARD' => 'addressbook-home-set',
|
||||
];
|
||||
|
||||
$ns = [
|
||||
'VEVENT' => 'caldav',
|
||||
'VTODO' => 'caldav',
|
||||
'VCARD' => 'carddav',
|
||||
];
|
||||
|
||||
$body = '<?xml version="1.0" encoding="utf-8"?>'
|
||||
. '<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">'
|
||||
. '<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:' . $ns[$component] . '">'
|
||||
. '<d:prop>'
|
||||
. '<c:calendar-home-set />'
|
||||
. '<c:' . $homes[$component] . ' />'
|
||||
. '</d:prop>'
|
||||
. '</d:propfind>';
|
||||
|
||||
|
@ -178,28 +190,41 @@ class kolab_dav_client
|
|||
$root_href = '/' . $roots[$component] . '/' . rawurlencode($this->user);
|
||||
}
|
||||
|
||||
if ($component == 'VCARD') {
|
||||
$add_ns = '';
|
||||
$add_props = '';
|
||||
}
|
||||
else {
|
||||
$add_ns = ' xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:a="http://apple.com/ns/ical/"';
|
||||
$add_props = '<c:supported-calendar-component-set /><a:calendar-color />';
|
||||
}
|
||||
|
||||
$body = '<?xml version="1.0" encoding="utf-8"?>'
|
||||
. '<d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav" xmlns:a="http://apple.com/ns/ical/">'
|
||||
. '<d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/"' . $add_ns . '>'
|
||||
. '<d:prop>'
|
||||
. '<d:resourcetype />'
|
||||
. '<d:displayname />'
|
||||
// . '<d:sync-token />'
|
||||
. '<cs:getctag />'
|
||||
. '<c:supported-calendar-component-set />'
|
||||
. '<a:calendar-color />'
|
||||
. $add_props
|
||||
. '</d:prop>'
|
||||
. '</d:propfind>';
|
||||
|
||||
$response = $this->request($root_href, 'PROPFIND', $body);
|
||||
// Note: Cyrus CardDAV service requires Depth:1 (CalDAV works without it)
|
||||
$response = $this->request($root_href, 'PROPFIND', $body, ['Depth' => 1, 'Prefer' => 'return-minimal']);
|
||||
|
||||
if (empty($response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$folders = [];
|
||||
|
||||
foreach ($response->getElementsByTagName('response') as $element) {
|
||||
$folder = $this->getFolderPropertiesFromResponse($element);
|
||||
if ($folder['type'] === $component) {
|
||||
|
||||
// Note: Addressbooks don't have 'type' specified
|
||||
if (($component == 'VCARD' && in_array('addressbook', $folder['resource_type']))
|
||||
|| $folder['type'] === $component
|
||||
) {
|
||||
$folders[] = $folder;
|
||||
}
|
||||
}
|
||||
|
@ -210,9 +235,17 @@ class kolab_dav_client
|
|||
/**
|
||||
* Create a DAV object in a folder
|
||||
*/
|
||||
public function create($location, $content)
|
||||
public function create($location, $content, $component = 'VEVENT')
|
||||
{
|
||||
$response = $this->request($location, 'PUT', $content, ['Content-Type' => 'text/calendar; charset=utf-8']);
|
||||
$ctype = [
|
||||
'VEVENT' => 'text/calendar',
|
||||
'VTODO' => 'text/calendar',
|
||||
'VCARD' => 'text/vcard',
|
||||
];
|
||||
|
||||
$headers = ['Content-Type' => $ctype[$component] . '; charset=utf-8'];
|
||||
|
||||
$response = $this->request($location, 'PUT', $content, $headers);
|
||||
|
||||
if ($response !== false) {
|
||||
$etag = $this->responseHeaders['etag'];
|
||||
|
@ -230,9 +263,9 @@ class kolab_dav_client
|
|||
/**
|
||||
* Update a DAV object in a folder
|
||||
*/
|
||||
public function update($location, $content)
|
||||
public function update($location, $content, $component = 'VEVENT')
|
||||
{
|
||||
return $this->create($location, $content);
|
||||
return $this->create($location, $content, $component);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -100,14 +100,18 @@ class kolab_storage_dav
|
|||
/**
|
||||
* Getter for a specific storage folder
|
||||
*
|
||||
* @param string Folder to access
|
||||
* @param string Expected folder type
|
||||
* @param string $id Folder to access
|
||||
* @param string $type Expected folder type
|
||||
*
|
||||
* @return object kolab_storage_folder The folder object
|
||||
* @return ?object kolab_storage_folder The folder object
|
||||
*/
|
||||
public function get_folder($folder, $type = null)
|
||||
public function get_folder($id, $type = null)
|
||||
{
|
||||
// TODO
|
||||
foreach ($this->get_folders($type) as $folder) {
|
||||
if ($folder->id == $id) {
|
||||
return $folder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -319,8 +319,8 @@ class kolab_storage_dav_cache extends kolab_storage_cache
|
|||
|
||||
$sql_data = $this->_serialize($object);
|
||||
$sql_data['folder_id'] = $this->folder_id;
|
||||
$sql_data['uid'] = $object['uid'];
|
||||
$sql_data['etag'] = $object['etag'];
|
||||
$sql_data['uid'] = rcube_charset::clean($object['uid']);
|
||||
$sql_data['etag'] = rcube_charset::clean($object['etag']);
|
||||
|
||||
$args = [];
|
||||
$cols = ['folder_id', 'uid', 'etag', 'changed', 'data', 'tags', 'words'];
|
||||
|
@ -524,8 +524,15 @@ class kolab_storage_dav_cache extends kolab_storage_cache
|
|||
// In Oracle we can't put long data inline, others we don't support yet
|
||||
if (strpos($this->db->db_provider, 'mysql') !== 0) {
|
||||
$extra_args = [];
|
||||
$params = [$this->folder_id, $object['uid'], $object['etag'], $sql_data['changed'],
|
||||
$sql_data['data'], $sql_data['tags'], $sql_data['words']];
|
||||
$params = [
|
||||
$this->folder_id,
|
||||
rcube_charset::clean($object['uid']),
|
||||
rcube_charset::clean($object['etag']),
|
||||
$sql_data['changed'],
|
||||
$sql_data['data'],
|
||||
$sql_data['tags'],
|
||||
$sql_data['words']
|
||||
];
|
||||
|
||||
foreach ($this->extra_cols as $col) {
|
||||
$params[] = $sql_data[$col];
|
||||
|
@ -552,8 +559,8 @@ class kolab_storage_dav_cache extends kolab_storage_cache
|
|||
|
||||
$values = array(
|
||||
$this->db->quote($this->folder_id),
|
||||
$this->db->quote($object['uid']),
|
||||
$this->db->quote($object['etag']),
|
||||
$this->db->quote(rcube_charset::clean($object['uid'])),
|
||||
$this->db->quote(rcube_charset::clean($object['etag'])),
|
||||
$this->db->now(),
|
||||
$this->db->quote($sql_data['changed']),
|
||||
$this->db->quote($sql_data['data']),
|
||||
|
|
|
@ -21,12 +21,12 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class kolab_storage_cache_contact extends kolab_storage_cache
|
||||
class kolab_storage_dav_cache_contact extends kolab_storage_dav_cache
|
||||
{
|
||||
protected $extra_cols_max = 255;
|
||||
protected $extra_cols = ['type', 'name', 'firstname', 'surname', 'email'];
|
||||
protected $data_props = ['type', 'name', 'firstname', 'middlename', 'prefix', 'suffix', 'surname', 'email', 'organization', 'member'];
|
||||
protected $fulltext_cols = ['name', 'firstname', 'surname', 'middlename', 'email:address'];
|
||||
protected $fulltext_cols = ['name', 'firstname', 'surname', 'middlename', 'email'];
|
||||
|
||||
/**
|
||||
* Helper method to convert the given Kolab object into a dataset to be written to cache
|
||||
|
@ -36,20 +36,20 @@ class kolab_storage_cache_contact extends kolab_storage_cache
|
|||
protected function _serialize($object)
|
||||
{
|
||||
$sql_data = parent::_serialize($object);
|
||||
$sql_data['type'] = $object['_type'];
|
||||
$sql_data['type'] = $object['_type'] ?: 'contact';
|
||||
|
||||
// columns for sorting
|
||||
$sql_data['name'] = rcube_charset::clean($object['name'] . $object['prefix']);
|
||||
$sql_data['firstname'] = rcube_charset::clean($object['firstname'] . $object['middlename'] . $object['surname']);
|
||||
$sql_data['surname'] = rcube_charset::clean($object['surname'] . $object['firstname'] . $object['middlename']);
|
||||
$sql_data['email'] = rcube_charset::clean(is_array($object['email']) ? $object['email'][0] : $object['email']);
|
||||
$sql_data['email'] = '';
|
||||
|
||||
if (is_array($sql_data['email'])) {
|
||||
$sql_data['email'] = $sql_data['email']['address'];
|
||||
}
|
||||
// avoid value being null
|
||||
if (empty($sql_data['email'])) {
|
||||
$sql_data['email'] = '';
|
||||
foreach ($object as $colname => $value) {
|
||||
list($col, $field) = explode(':', $colname);
|
||||
if ($col == 'email' && !empty($value)) {
|
||||
$sql_data['email'] = is_array($value) ? $value[0] : $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// use organization if name is empty
|
||||
|
@ -78,21 +78,18 @@ class kolab_storage_cache_contact extends kolab_storage_cache
|
|||
public function get_words($object)
|
||||
{
|
||||
$data = '';
|
||||
foreach ($this->fulltext_cols as $colname) {
|
||||
|
||||
foreach ($object as $colname => $value) {
|
||||
list($col, $field) = explode(':', $colname);
|
||||
|
||||
if ($field) {
|
||||
$a = [];
|
||||
foreach ((array)$object[$col] as $attr)
|
||||
$a[] = $attr[$field];
|
||||
$val = join(' ', $a);
|
||||
}
|
||||
else {
|
||||
$val = is_array($object[$col]) ? join(' ', $object[$col]) : $object[$col];
|
||||
$val = '';
|
||||
if (in_array($col, $this->fulltext_cols)) {
|
||||
$val = is_array($value) ? join(' ', $value) : $value;
|
||||
}
|
||||
|
||||
if (strlen($val))
|
||||
if (strlen($val)) {
|
||||
$data .= $val . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
return array_unique(rcube_utils::normalize_string($data, true));
|
||||
|
|
|
@ -32,12 +32,9 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
|||
public function __construct($dav, $attributes, $type_annotation = '')
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
$this->href = $this->attributes['href'];
|
||||
|
||||
// Here we assume the last element of the folder path is the folder ID
|
||||
// if that's not the case, we should consider generating an ID
|
||||
$href = explode('/', unslashify($this->href));
|
||||
$this->id = $href[count($href) - 1];
|
||||
$this->href = $this->attributes['href'];
|
||||
$this->id = md5($this->href);
|
||||
$this->dav = $dav;
|
||||
$this->valid = true;
|
||||
|
||||
|
@ -410,8 +407,9 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
|||
|
||||
// generate and save object message
|
||||
if ($content = $this->to_dav($object)) {
|
||||
$method = $uid ? 'update' : 'create';
|
||||
$result = $this->dav->{$method}($this->object_location($object['uid']), $content);
|
||||
$method = $uid ? 'update' : 'create';
|
||||
$dav_type = $this->get_dav_type();
|
||||
$result = $this->dav->{$method}($this->object_location($object['uid']), $content, $dav_type);
|
||||
|
||||
// Note: $result can be NULL if the request was successful, but ETag wasn't returned
|
||||
if ($result !== false) {
|
||||
|
@ -473,10 +471,24 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
|||
|
||||
$result = $events[0];
|
||||
}
|
||||
else if ($this->type == 'contact') {
|
||||
if (stripos($object['data'], 'BEGIN:VCARD') !== 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$vcard = new rcube_vcard($object['data'], RCUBE_CHARSET, false);
|
||||
|
||||
if (!empty($vcard->displayname) || !empty($vcard->surname) || !empty($vcard->firstname) || !empty($vcard->email)) {
|
||||
$result = $vcard->get_assoc();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$result['etag'] = $object['etag'];
|
||||
$result['href'] = $object['href'];
|
||||
$result['uid'] = $object['uid'] ?: $result['uid'];
|
||||
$result['uid'] = $object['uid'] ?: $result['uid'];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
@ -496,6 +508,35 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
|||
|
||||
$result = $ical->export([$object]);
|
||||
}
|
||||
else if ($this->type == 'contact') {
|
||||
// copy values into vcard object
|
||||
$vcard = new rcube_vcard('', RCUBE_CHARSET, false, ['uid' => 'UID']);
|
||||
|
||||
$vcard->set('groups', null);
|
||||
|
||||
foreach ($object as $key => $values) {
|
||||
list($field, $section) = rcube_utils::explode(':', $key);
|
||||
|
||||
// avoid casting DateTime objects to array
|
||||
if (is_object($values) && is_a($values, 'DateTime')) {
|
||||
$values = [$values];
|
||||
}
|
||||
|
||||
foreach ((array) $values as $value) {
|
||||
if (isset($value)) {
|
||||
$vcard->set($field, $value, $section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = $vcard->export(false);
|
||||
}
|
||||
|
||||
if ($result) {
|
||||
// The content must be UTF-8, otherwise if we try to fetch the object
|
||||
// from server XML parsing would fail.
|
||||
$result = rcube_charset::clean($result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue