Contact groups
This commit is contained in:
parent
b6cc8c0715
commit
5fbfa61a4b
4 changed files with 164 additions and 47 deletions
|
@ -32,7 +32,7 @@ class carddav_contacts extends rcube_addressbook
|
||||||
public $rights = 'lrs';
|
public $rights = 'lrs';
|
||||||
public $readonly = true;
|
public $readonly = true;
|
||||||
public $undelete = false;
|
public $undelete = false;
|
||||||
public $groups = false; // TODO
|
public $groups = true;
|
||||||
|
|
||||||
public $coltypes = [
|
public $coltypes = [
|
||||||
'name' => ['limit' => 1],
|
'name' => ['limit' => 1],
|
||||||
|
@ -108,7 +108,7 @@ class carddav_contacts extends rcube_addressbook
|
||||||
'manager',
|
'manager',
|
||||||
'assistant',
|
'assistant',
|
||||||
'spouse',
|
'spouse',
|
||||||
'children',
|
// 'children',
|
||||||
'notes',
|
'notes',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -810,30 +810,34 @@ class carddav_contacts extends rcube_addressbook
|
||||||
function create_group($name)
|
function create_group($name)
|
||||||
{
|
{
|
||||||
$this->_fetch_groups();
|
$this->_fetch_groups();
|
||||||
$result = false;
|
|
||||||
|
$rcube = rcube::get_instance();
|
||||||
|
|
||||||
$list = [
|
$list = [
|
||||||
'name' => $name,
|
'uid' => strtoupper(md5(time() . uniqid(rand())) . '-' . substr(md5($rcube->user->get_username()), 0, 16)),
|
||||||
|
'name' => $name,
|
||||||
|
'kind' => 'group',
|
||||||
'member' => [],
|
'member' => [],
|
||||||
];
|
];
|
||||||
$saved = $this->storage->save($list, 'distribution-list');
|
|
||||||
|
$saved = $this->storage->save($list, 'contact');
|
||||||
|
|
||||||
if (!$saved) {
|
if (!$saved) {
|
||||||
rcube::raise_error([
|
rcube::raise_error([
|
||||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||||
'message' => "Error saving distribution-list object to CardDAV server"
|
'message' => "Error saving a contact group to CardDAV server"
|
||||||
],
|
],
|
||||||
true, false
|
true, false
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$id = $this->uid2id($list['uid']);
|
|
||||||
$this->distlists[$id] = $list;
|
|
||||||
$result = ['id' => $id, 'name' => $name];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
$id = $this->uid2id($list['uid']);
|
||||||
|
|
||||||
|
$this->distlists[$id] = $list;
|
||||||
|
|
||||||
|
return ['id' => $id, 'name' => $name];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -846,25 +850,25 @@ class carddav_contacts extends rcube_addressbook
|
||||||
function delete_group($gid)
|
function delete_group($gid)
|
||||||
{
|
{
|
||||||
$this->_fetch_groups();
|
$this->_fetch_groups();
|
||||||
$result = false;
|
|
||||||
|
|
||||||
if ($list = $this->distlists[$gid]) {
|
$list = $this->distlists[$gid];
|
||||||
$deleted = $this->storage->delete($list['uid']);
|
|
||||||
|
if (!$list) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$deleted = $this->storage->delete($list['uid']);
|
||||||
|
|
||||||
if (!$deleted) {
|
if (!$deleted) {
|
||||||
rcube::raise_error([
|
rcube::raise_error([
|
||||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||||
'message' => "Error deleting distribution-list object from the CardDAV server"
|
'message' => "Error deleting a contact group from the CardDAV server"
|
||||||
],
|
],
|
||||||
true, false
|
true, false
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
$result = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
return $deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -874,25 +878,33 @@ class carddav_contacts extends rcube_addressbook
|
||||||
* @param string New name to set for this group
|
* @param string New name to set for this group
|
||||||
* @param string New group identifier (if changed, otherwise don't set)
|
* @param string New group identifier (if changed, otherwise don't set)
|
||||||
*
|
*
|
||||||
* @return bool New name on success, false if no data was changed
|
* @return string|false New name on success, false if no data was changed
|
||||||
*/
|
*/
|
||||||
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 (!$list) {
|
||||||
$list['name'] = $newname;
|
return false;
|
||||||
$saved = $this->storage->save($list, 'distribution-list', $list['uid']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($newname === $list['name']) {
|
||||||
|
return $newname;
|
||||||
|
}
|
||||||
|
|
||||||
|
$list['name'] = $newname;
|
||||||
|
$saved = $this->storage->save($list, 'contact', $list['uid']);
|
||||||
|
|
||||||
if (!$saved) {
|
if (!$saved) {
|
||||||
rcube::raise_error([
|
rcube::raise_error([
|
||||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||||
'message' => "Error saving distribution-list object to CardDAV server"
|
'message' => "Error saving a contact group to CardDAV server"
|
||||||
],
|
],
|
||||||
true, false
|
true, false
|
||||||
);
|
);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,7 +916,8 @@ class carddav_contacts extends rcube_addressbook
|
||||||
*
|
*
|
||||||
* @param string Group identifier
|
* @param string Group identifier
|
||||||
* @param array List of contact identifiers to be added
|
* @param array List of contact identifiers to be added
|
||||||
* @return int Number of contacts added
|
*
|
||||||
|
* @return int Number of contacts added
|
||||||
*/
|
*/
|
||||||
function add_to_group($gid, $ids)
|
function add_to_group($gid, $ids)
|
||||||
{
|
{
|
||||||
|
@ -914,12 +927,17 @@ class carddav_contacts extends rcube_addressbook
|
||||||
|
|
||||||
$this->_fetch_groups(true);
|
$this->_fetch_groups(true);
|
||||||
|
|
||||||
$list = $this->distlists[$gid];
|
$list = $this->distlists[$gid];
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
$added = 0;
|
$added = 0;
|
||||||
$uids = [];
|
$uids = [];
|
||||||
$exists = [];
|
$exists = [];
|
||||||
|
|
||||||
foreach ((array)$list['member'] as $member) {
|
foreach ((array) $list['member'] as $member) {
|
||||||
$exists[] = $member['ID'];
|
$exists[] = $member['ID'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -952,7 +970,7 @@ class carddav_contacts extends rcube_addressbook
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($added) {
|
if ($added) {
|
||||||
$saved = $this->storage->save($list, 'distribution-list', $list['uid']);
|
$saved = $this->storage->save($list, 'contact', $list['uid']);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$saved = true;
|
$saved = true;
|
||||||
|
@ -961,7 +979,7 @@ class carddav_contacts extends rcube_addressbook
|
||||||
if (!$saved) {
|
if (!$saved) {
|
||||||
rcube::raise_error([
|
rcube::raise_error([
|
||||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||||
'message' => "Error saving distribution-list to CardDAV server"
|
'message' => "Error saving a contact-group to CardDAV server"
|
||||||
],
|
],
|
||||||
true, false
|
true, false
|
||||||
);
|
);
|
||||||
|
@ -986,13 +1004,16 @@ class carddav_contacts extends rcube_addressbook
|
||||||
*/
|
*/
|
||||||
function remove_from_group($gid, $ids)
|
function remove_from_group($gid, $ids)
|
||||||
{
|
{
|
||||||
if (!is_array($ids)) {
|
$this->_fetch_groups();
|
||||||
$ids = explode(',', $ids);
|
|
||||||
|
$list = $this->distlists[$gid];
|
||||||
|
|
||||||
|
if (!$list) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->_fetch_groups();
|
if (!is_array($ids)) {
|
||||||
if (!($list = $this->distlists[$gid])) {
|
$ids = explode(',', $ids);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_member = [];
|
$new_member = [];
|
||||||
|
@ -1004,12 +1025,12 @@ class carddav_contacts extends rcube_addressbook
|
||||||
|
|
||||||
// write distribution list back to server
|
// write distribution list back to server
|
||||||
$list['member'] = $new_member;
|
$list['member'] = $new_member;
|
||||||
$saved = $this->storage->save($list, 'distribution-list', $list['uid']);
|
$saved = $this->storage->save($list, 'contact', $list['uid']);
|
||||||
|
|
||||||
if (!$saved) {
|
if (!$saved) {
|
||||||
rcube::raise_error([
|
rcube::raise_error([
|
||||||
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
|
||||||
'message' => "Error saving distribution-list object to CardDAV server"
|
'message' => "Error saving a contact group to CardDAV server"
|
||||||
],
|
],
|
||||||
true, false
|
true, false
|
||||||
);
|
);
|
||||||
|
@ -1020,7 +1041,9 @@ class carddav_contacts extends rcube_addressbook
|
||||||
$j = array_search($gid, $this->groupmembers[$id]);
|
$j = array_search($gid, $this->groupmembers[$id]);
|
||||||
unset($this->groupmembers[$id][$j]);
|
unset($this->groupmembers[$id][$j]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->distlists[$gid] = $list;
|
$this->distlists[$gid] = $list;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1130,15 +1153,17 @@ class carddav_contacts extends rcube_addressbook
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read distribution-lists AKA groups from server
|
* Read contact 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 = [];
|
$this->distlists = $this->groupmembers = [];
|
||||||
foreach ($this->storage->select('distribution-list', true) as $record) {
|
|
||||||
|
// Set order (and LIMIT to skip the count(*) select)
|
||||||
|
$this->storage->set_order_and_limit(['name'], 200, 0);
|
||||||
|
|
||||||
|
foreach ($this->storage->select('group', 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']);
|
||||||
|
@ -1150,8 +1175,11 @@ class carddav_contacts extends rcube_addressbook
|
||||||
$this->contacts[$mid] = $record['member'][$i];
|
$this->contacts[$mid] = $record['member'][$i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->distlists[$record['ID']] = $record;
|
$this->distlists[$record['ID']] = $record;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->storage->set_order_and_limit($this->_sort_columns(), null, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ class kolab_storage_dav
|
||||||
/**
|
/**
|
||||||
* Get a list of storage folders for the given data type
|
* Get a list of storage folders for the given data type
|
||||||
*
|
*
|
||||||
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
|
* @param string Data type to list folders for (contact,event,task,note)
|
||||||
*
|
*
|
||||||
* @return array List of kolab_storage_dav_folder objects
|
* @return array List of kolab_storage_dav_folder objects
|
||||||
*/
|
*/
|
||||||
|
@ -87,7 +87,7 @@ class kolab_storage_dav
|
||||||
/**
|
/**
|
||||||
* Getter for the storage folder for the given type
|
* Getter for the storage folder for the given type
|
||||||
*
|
*
|
||||||
* @param string Data type to list folders for (contact,distribution-list,event,task,note)
|
* @param string Data type to list folders for (contact,event,task,note)
|
||||||
*
|
*
|
||||||
* @return object kolab_storage_dav_folder The folder object
|
* @return object kolab_storage_dav_folder The folder object
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -38,6 +38,10 @@ class kolab_storage_dav_cache_contact extends kolab_storage_dav_cache
|
||||||
$sql_data = parent::_serialize($object);
|
$sql_data = parent::_serialize($object);
|
||||||
$sql_data['type'] = $object['_type'] ?: 'contact';
|
$sql_data['type'] = $object['_type'] ?: 'contact';
|
||||||
|
|
||||||
|
if ($sql_data['type'] == 'group' || (!empty($object['kind']) && $object['kind'] == 'group')) {
|
||||||
|
$sql_data['type'] = 'group';
|
||||||
|
}
|
||||||
|
|
||||||
// columns for sorting
|
// columns for sorting
|
||||||
$sql_data['name'] = rcube_charset::clean($object['name'] . $object['prefix']);
|
$sql_data['name'] = rcube_charset::clean($object['name'] . $object['prefix']);
|
||||||
$sql_data['firstname'] = rcube_charset::clean($object['firstname'] . $object['middlename'] . $object['surname']);
|
$sql_data['firstname'] = rcube_charset::clean($object['firstname'] . $object['middlename'] . $object['surname']);
|
||||||
|
|
|
@ -483,10 +483,52 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$vcard = new rcube_vcard($object['data'], RCUBE_CHARSET, false);
|
// vCard properties not supported by rcube_vcard
|
||||||
|
$map = [
|
||||||
|
'uid' => 'UID',
|
||||||
|
'kind' => 'KIND',
|
||||||
|
'member' => 'MEMBER',
|
||||||
|
'x-kind' => 'X-ADDRESSBOOKSERVER-KIND',
|
||||||
|
'x-member' => 'X-ADDRESSBOOKSERVER-MEMBER',
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: We should probably use Sabre/Vobject to parse the vCard
|
||||||
|
|
||||||
|
$vcard = new rcube_vcard($object['data'], RCUBE_CHARSET, false, $map);
|
||||||
|
|
||||||
if (!empty($vcard->displayname) || !empty($vcard->surname) || !empty($vcard->firstname) || !empty($vcard->email)) {
|
if (!empty($vcard->displayname) || !empty($vcard->surname) || !empty($vcard->firstname) || !empty($vcard->email)) {
|
||||||
$result = $vcard->get_assoc();
|
$result = $vcard->get_assoc();
|
||||||
|
|
||||||
|
// Contact groups
|
||||||
|
if (!empty($result['x-kind']) && implode($result['x-kind']) == 'group') {
|
||||||
|
$result['_type'] = 'group';
|
||||||
|
$members = isset($result['x-member']) ? $result['x-member'] : [];
|
||||||
|
unset($result['x-kind'], $result['x-member']);
|
||||||
|
}
|
||||||
|
else if (!empty($result['kind']) && implode($result['kind']) == 'group') {
|
||||||
|
$result['_type'] = 'group';
|
||||||
|
$members = isset($result['member']) ? $result['member'] : [];
|
||||||
|
unset($result['kind'], $result['member']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($members)) {
|
||||||
|
$result['member'] = [];
|
||||||
|
foreach ($members as $member) {
|
||||||
|
if (strpos($member, 'urn:uuid:') === 0) {
|
||||||
|
$result['member'][] = ['uid' => substr($member, 9)];
|
||||||
|
}
|
||||||
|
else if (strpos($member, 'mailto:') === 0) {
|
||||||
|
$member = reset(rcube_mime::decode_address_list(urldecode(substr($member, 7))));
|
||||||
|
if (!empty($member['mailto'])) {
|
||||||
|
$result['member'][] = ['email' => $member['mailto'], 'name' => $member['name']];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($result['uid'])) {
|
||||||
|
$result['uid'] = preg_replace('/^urn:uuid:/', '', implode($result['uid']));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -517,9 +559,17 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
||||||
}
|
}
|
||||||
else if ($this->type == 'contact') {
|
else if ($this->type == 'contact') {
|
||||||
// copy values into vcard object
|
// copy values into vcard object
|
||||||
$vcard = new rcube_vcard('', RCUBE_CHARSET, false, ['uid' => 'UID']);
|
// TODO: We should probably use Sabre/Vobject to create the vCard
|
||||||
|
|
||||||
$vcard->set('groups', null);
|
// vCard properties not supported by rcube_vcard
|
||||||
|
$map = ['uid' => 'UID', 'kind' => 'KIND'];
|
||||||
|
$vcard = new rcube_vcard('', RCUBE_CHARSET, false, $map);
|
||||||
|
|
||||||
|
if ((!empty($object['_type']) && $object['_type'] == 'group')
|
||||||
|
|| (!empty($object['type']) && $object['type'] == 'group')
|
||||||
|
) {
|
||||||
|
$object['kind'] = 'group';
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($object as $key => $values) {
|
foreach ($object as $key => $values) {
|
||||||
list($field, $section) = rcube_utils::explode(':', $key);
|
list($field, $section) = rcube_utils::explode(':', $key);
|
||||||
|
@ -537,6 +587,41 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $vcard->export(false);
|
$result = $vcard->export(false);
|
||||||
|
|
||||||
|
if (!empty($object['kind']) && $object['kind'] == 'group') {
|
||||||
|
$members = '';
|
||||||
|
foreach ((array) $object['member'] as $member) {
|
||||||
|
$value = null;
|
||||||
|
if (!empty($member['uid'])) {
|
||||||
|
$value = 'urn:uuid:' . $member['uid'];
|
||||||
|
}
|
||||||
|
else if (!empty($member['email']) && !empty($member['name'])) {
|
||||||
|
$value = 'mailto:' . urlencode(sprintf('"%s" <%s>', addcslashes($member['name'], '"'), $member['email']));
|
||||||
|
}
|
||||||
|
else if (!empty($member['email'])) {
|
||||||
|
$value = 'mailto:' . $member['email'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value) {
|
||||||
|
$members .= "MEMBER:{$value}\r\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($members) {
|
||||||
|
$result = preg_replace('/\r\nEND:VCARD/', "\r\n{$members}END:VCARD", $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Version 4.0 of the vCard format requires Cyrus >= 3.6.0, we'll use Version 3.0 for now
|
||||||
|
|
||||||
|
$result = preg_replace('/\r\nVERSION:3\.0\r\n/', "\r\nVERSION:4.0\r\n", $result);
|
||||||
|
$result = preg_replace('/\r\nN:[^\r]+/', '', $result);
|
||||||
|
$result = preg_replace('/\r\nUID:([^\r]+)/', "\r\nUID:urn:uuid:\\1", $result);
|
||||||
|
*/
|
||||||
|
|
||||||
|
$result = preg_replace('/\r\nMEMBER:([^\r]+)/', "\r\nX-ADDRESSBOOKSERVER-MEMBER:\\1", $result);
|
||||||
|
$result = preg_replace('/\r\nKIND:([^\r]+)/', "\r\nX-ADDRESSBOOKSERVER-KIND:\\1", $result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
|
@ -578,7 +663,7 @@ class kolab_storage_dav_folder extends kolab_storage_folder
|
||||||
/**
|
/**
|
||||||
* Return folder name as string representation of this object
|
* Return folder name as string representation of this object
|
||||||
*
|
*
|
||||||
* @return string Full IMAP folder name
|
* @return string Folder display name
|
||||||
*/
|
*/
|
||||||
public function __toString()
|
public function __toString()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Reference in a new issue