|
|
|
|
@ -50,7 +50,6 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
|
|
|
|
|
use OCP\EventDispatcher\IEventDispatcher;
|
|
|
|
|
use OCP\IDBConnection;
|
|
|
|
|
use OCP\IGroupManager;
|
|
|
|
|
use OCP\IUser;
|
|
|
|
|
use OCP\IUserManager;
|
|
|
|
|
use PDO;
|
|
|
|
|
use Sabre\CardDAV\Backend\BackendInterface;
|
|
|
|
|
@ -61,7 +60,6 @@ use Sabre\VObject\Component\VCard;
|
|
|
|
|
use Sabre\VObject\Reader;
|
|
|
|
|
|
|
|
|
|
class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
|
|
|
|
|
use TTransactional;
|
|
|
|
|
|
|
|
|
|
public const PERSONAL_ADDRESSBOOK_URI = 'contacts';
|
|
|
|
|
@ -145,87 +143,89 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getAddressBooksForUser($principalUri) {
|
|
|
|
|
$principalUriOriginal = $principalUri;
|
|
|
|
|
$principalUri = $this->convertPrincipal($principalUri, true);
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
|
|
|
|
|
->from('addressbooks')
|
|
|
|
|
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)));
|
|
|
|
|
return $this->atomic(function () use ($principalUri) {
|
|
|
|
|
$principalUriOriginal = $principalUri;
|
|
|
|
|
$principalUri = $this->convertPrincipal($principalUri, true);
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
|
|
|
|
|
->from('addressbooks')
|
|
|
|
|
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)));
|
|
|
|
|
|
|
|
|
|
$addressBooks = [];
|
|
|
|
|
$addressBooks = [];
|
|
|
|
|
|
|
|
|
|
$result = $query->execute();
|
|
|
|
|
while ($row = $result->fetch()) {
|
|
|
|
|
$addressBooks[$row['id']] = [
|
|
|
|
|
'id' => $row['id'],
|
|
|
|
|
'uri' => $row['uri'],
|
|
|
|
|
'principaluri' => $this->convertPrincipal($row['principaluri'], false),
|
|
|
|
|
'{DAV:}displayname' => $row['displayname'],
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
|
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->addOwnerPrincipal($addressBooks[$row['id']]);
|
|
|
|
|
}
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
$result = $query->execute();
|
|
|
|
|
while ($row = $result->fetch()) {
|
|
|
|
|
$addressBooks[$row['id']] = [
|
|
|
|
|
'id' => $row['id'],
|
|
|
|
|
'uri' => $row['uri'],
|
|
|
|
|
'principaluri' => $this->convertPrincipal($row['principaluri'], false),
|
|
|
|
|
'{DAV:}displayname' => $row['displayname'],
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
|
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->addOwnerPrincipal($addressBooks[$row['id']]);
|
|
|
|
|
}
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
|
|
|
|
|
// query for shared addressbooks
|
|
|
|
|
$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
|
|
|
|
|
$principals = array_merge($principals, $this->principalBackend->getCircleMembership($principalUriOriginal));
|
|
|
|
|
// query for shared addressbooks
|
|
|
|
|
$principals = $this->principalBackend->getGroupMembership($principalUriOriginal, true);
|
|
|
|
|
$principals = array_merge($principals, $this->principalBackend->getCircleMembership($principalUriOriginal));
|
|
|
|
|
|
|
|
|
|
$principals[] = $principalUri;
|
|
|
|
|
$principals[] = $principalUri;
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$result = $query->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access'])
|
|
|
|
|
->from('dav_shares', 's')
|
|
|
|
|
->join('s', 'addressbooks', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
|
|
|
|
|
->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
|
|
|
|
|
->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
|
|
|
|
|
->setParameter('type', 'addressbook')
|
|
|
|
|
->setParameter('principaluri', $principals, IQueryBuilder::PARAM_STR_ARRAY)
|
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
|
|
$readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
|
|
|
|
|
while ($row = $result->fetch()) {
|
|
|
|
|
if ($row['principaluri'] === $principalUri) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$result = $query->select(['a.id', 'a.uri', 'a.displayname', 'a.principaluri', 'a.description', 'a.synctoken', 's.access'])
|
|
|
|
|
->from('dav_shares', 's')
|
|
|
|
|
->join('s', 'addressbooks', 'a', $query->expr()->eq('s.resourceid', 'a.id'))
|
|
|
|
|
->where($query->expr()->in('s.principaluri', $query->createParameter('principaluri')))
|
|
|
|
|
->andWhere($query->expr()->eq('s.type', $query->createParameter('type')))
|
|
|
|
|
->setParameter('type', 'addressbook')
|
|
|
|
|
->setParameter('principaluri', $principals, IQueryBuilder::PARAM_STR_ARRAY)
|
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
|
|
$readOnly = (int)$row['access'] === Backend::ACCESS_READ;
|
|
|
|
|
if (isset($addressBooks[$row['id']])) {
|
|
|
|
|
if ($readOnly) {
|
|
|
|
|
// New share can not have more permissions then the old one.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isset($addressBooks[$row['id']][$readOnlyPropertyName]) &&
|
|
|
|
|
$addressBooks[$row['id']][$readOnlyPropertyName] === 0) {
|
|
|
|
|
// Old share is already read-write, no more permissions can be gained
|
|
|
|
|
$readOnlyPropertyName = '{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}read-only';
|
|
|
|
|
while ($row = $result->fetch()) {
|
|
|
|
|
if ($row['principaluri'] === $principalUri) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[, $name] = \Sabre\Uri\split($row['principaluri']);
|
|
|
|
|
$uri = $row['uri'] . '_shared_by_' . $name;
|
|
|
|
|
$displayName = $row['displayname'] . ' (' . ($this->userManager->getDisplayName($name) ?? $name ?? '') . ')';
|
|
|
|
|
|
|
|
|
|
$addressBooks[$row['id']] = [
|
|
|
|
|
'id' => $row['id'],
|
|
|
|
|
'uri' => $uri,
|
|
|
|
|
'principaluri' => $principalUriOriginal,
|
|
|
|
|
'{DAV:}displayname' => $displayName,
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
|
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
|
|
|
|
|
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
|
|
|
|
|
$readOnlyPropertyName => $readOnly,
|
|
|
|
|
];
|
|
|
|
|
$readOnly = (int)$row['access'] === Backend::ACCESS_READ;
|
|
|
|
|
if (isset($addressBooks[$row['id']])) {
|
|
|
|
|
if ($readOnly) {
|
|
|
|
|
// New share can not have more permissions then the old one.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isset($addressBooks[$row['id']][$readOnlyPropertyName]) &&
|
|
|
|
|
$addressBooks[$row['id']][$readOnlyPropertyName] === 0) {
|
|
|
|
|
// Old share is already read-write, no more permissions can be gained
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->addOwnerPrincipal($addressBooks[$row['id']]);
|
|
|
|
|
}
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
[, $name] = \Sabre\Uri\split($row['principaluri']);
|
|
|
|
|
$uri = $row['uri'] . '_shared_by_' . $name;
|
|
|
|
|
$displayName = $row['displayname'] . ' (' . ($this->userManager->getDisplayName($name) ?? $name ?? '') . ')';
|
|
|
|
|
|
|
|
|
|
$addressBooks[$row['id']] = [
|
|
|
|
|
'id' => $row['id'],
|
|
|
|
|
'uri' => $uri,
|
|
|
|
|
'principaluri' => $principalUriOriginal,
|
|
|
|
|
'{DAV:}displayname' => $displayName,
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description' => $row['description'],
|
|
|
|
|
'{http://calendarserver.org/ns/}getctag' => $row['synctoken'],
|
|
|
|
|
'{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0',
|
|
|
|
|
'{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}owner-principal' => $row['principaluri'],
|
|
|
|
|
$readOnlyPropertyName => $readOnly,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$this->addOwnerPrincipal($addressBooks[$row['id']]);
|
|
|
|
|
}
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
|
|
|
|
|
return array_values($addressBooks);
|
|
|
|
|
return array_values($addressBooks);
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function getUsersOwnAddressBooks($principalUri) {
|
|
|
|
|
@ -333,40 +333,42 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function updateAddressBook($addressBookId, \Sabre\DAV\PropPatch $propPatch) {
|
|
|
|
|
$supportedProperties = [
|
|
|
|
|
'{DAV:}displayname',
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description',
|
|
|
|
|
];
|
|
|
|
|
$this->atomic(function () use ($addressBookId, $propPatch) {
|
|
|
|
|
$supportedProperties = [
|
|
|
|
|
'{DAV:}displayname',
|
|
|
|
|
'{' . Plugin::NS_CARDDAV . '}addressbook-description',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) {
|
|
|
|
|
$updates = [];
|
|
|
|
|
foreach ($mutations as $property => $newValue) {
|
|
|
|
|
switch ($property) {
|
|
|
|
|
case '{DAV:}displayname':
|
|
|
|
|
$updates['displayname'] = $newValue;
|
|
|
|
|
break;
|
|
|
|
|
case '{' . Plugin::NS_CARDDAV . '}addressbook-description':
|
|
|
|
|
$updates['description'] = $newValue;
|
|
|
|
|
break;
|
|
|
|
|
$propPatch->handle($supportedProperties, function ($mutations) use ($addressBookId) {
|
|
|
|
|
$updates = [];
|
|
|
|
|
foreach ($mutations as $property => $newValue) {
|
|
|
|
|
switch ($property) {
|
|
|
|
|
case '{DAV:}displayname':
|
|
|
|
|
$updates['displayname'] = $newValue;
|
|
|
|
|
break;
|
|
|
|
|
case '{' . Plugin::NS_CARDDAV . '}addressbook-description':
|
|
|
|
|
$updates['description'] = $newValue;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->update('addressbooks');
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->update('addressbooks');
|
|
|
|
|
|
|
|
|
|
foreach ($updates as $key => $value) {
|
|
|
|
|
$query->set($key, $query->createNamedParameter($value));
|
|
|
|
|
}
|
|
|
|
|
$query->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
foreach ($updates as $key => $value) {
|
|
|
|
|
$query->set($key, $query->createNamedParameter($value));
|
|
|
|
|
}
|
|
|
|
|
$query->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$this->addChange($addressBookId, "", 2);
|
|
|
|
|
$this->addChange($addressBookId, "", 2);
|
|
|
|
|
|
|
|
|
|
$addressBookRow = $this->getAddressBookById((int)$addressBookId);
|
|
|
|
|
$shares = $this->getShares((int)$addressBookId);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookUpdatedEvent((int)$addressBookId, $addressBookRow, $shares, $mutations));
|
|
|
|
|
$addressBookRow = $this->getAddressBookById((int)$addressBookId);
|
|
|
|
|
$shares = $this->getShares((int)$addressBookId);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookUpdatedEvent((int)$addressBookId, $addressBookRow, $shares, $mutations));
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -410,7 +412,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
$values['displayname'] = $url;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[$addressBookId, $addressBookRow] = $this->atomic(function() use ($values) {
|
|
|
|
|
[$addressBookId, $addressBookRow] = $this->atomic(function () use ($values) {
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert('addressbooks')
|
|
|
|
|
->values([
|
|
|
|
|
@ -442,38 +444,40 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function deleteAddressBook($addressBookId) {
|
|
|
|
|
$addressBookId = (int)$addressBookId;
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$this->atomic(function () use ($addressBookId) {
|
|
|
|
|
$addressBookId = (int)$addressBookId;
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete($this->dbCardsTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
|
|
|
|
|
->setParameter('addressbookid', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete($this->dbCardsTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
|
|
|
|
|
->setParameter('addressbookid', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete('addressbookchanges')
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
|
|
|
|
|
->setParameter('addressbookid', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete('addressbookchanges')
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createParameter('addressbookid')))
|
|
|
|
|
->setParameter('addressbookid', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete('addressbooks')
|
|
|
|
|
->where($query->expr()->eq('id', $query->createParameter('id')))
|
|
|
|
|
->setParameter('id', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete('addressbooks')
|
|
|
|
|
->where($query->expr()->eq('id', $query->createParameter('id')))
|
|
|
|
|
->setParameter('id', $addressBookId, IQueryBuilder::PARAM_INT)
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$this->sharingBackend->deleteAllShares($addressBookId);
|
|
|
|
|
$this->sharingBackend->deleteAllShares($addressBookId);
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete($this->dbCardsPropertiesTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId, IQueryBuilder::PARAM_INT)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->delete($this->dbCardsPropertiesTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId, IQueryBuilder::PARAM_INT)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
if ($addressBookData) {
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookDeletedEvent($addressBookId, $addressBookData, $shares));
|
|
|
|
|
}
|
|
|
|
|
if ($addressBookData) {
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookDeletedEvent($addressBookId, $addressBookData, $shares));
|
|
|
|
|
}
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -631,47 +635,48 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
public function createCard($addressBookId, $cardUri, $cardData, bool $checkAlreadyExists = true) {
|
|
|
|
|
$etag = md5($cardData);
|
|
|
|
|
$uid = $this->getUID($cardData);
|
|
|
|
|
|
|
|
|
|
if ($checkAlreadyExists) {
|
|
|
|
|
$q = $this->db->getQueryBuilder();
|
|
|
|
|
$q->select('uid')
|
|
|
|
|
->from($this->dbCardsTable)
|
|
|
|
|
->where($q->expr()->eq('addressbookid', $q->createNamedParameter($addressBookId)))
|
|
|
|
|
->andWhere($q->expr()->eq('uid', $q->createNamedParameter($uid)))
|
|
|
|
|
->setMaxResults(1);
|
|
|
|
|
$result = $q->executeQuery();
|
|
|
|
|
$count = (bool)$result->fetchOne();
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
if ($count) {
|
|
|
|
|
throw new \Sabre\DAV\Exception\BadRequest('VCard object with uid already exists in this addressbook collection.');
|
|
|
|
|
return $this->atomic(function () use ($addressBookId, $cardUri, $cardData, $checkAlreadyExists, $etag, $uid) {
|
|
|
|
|
if ($checkAlreadyExists) {
|
|
|
|
|
$q = $this->db->getQueryBuilder();
|
|
|
|
|
$q->select('uid')
|
|
|
|
|
->from($this->dbCardsTable)
|
|
|
|
|
->where($q->expr()->eq('addressbookid', $q->createNamedParameter($addressBookId)))
|
|
|
|
|
->andWhere($q->expr()->eq('uid', $q->createNamedParameter($uid)))
|
|
|
|
|
->setMaxResults(1);
|
|
|
|
|
$result = $q->executeQuery();
|
|
|
|
|
$count = (bool)$result->fetchOne();
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
if ($count) {
|
|
|
|
|
throw new \Sabre\DAV\Exception\BadRequest('VCard object with uid already exists in this addressbook collection.');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert('cards')
|
|
|
|
|
->values([
|
|
|
|
|
'carddata' => $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB),
|
|
|
|
|
'uri' => $query->createNamedParameter($cardUri),
|
|
|
|
|
'lastmodified' => $query->createNamedParameter(time()),
|
|
|
|
|
'addressbookid' => $query->createNamedParameter($addressBookId),
|
|
|
|
|
'size' => $query->createNamedParameter(strlen($cardData)),
|
|
|
|
|
'etag' => $query->createNamedParameter($etag),
|
|
|
|
|
'uid' => $query->createNamedParameter($uid),
|
|
|
|
|
])
|
|
|
|
|
->execute();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert('cards')
|
|
|
|
|
->values([
|
|
|
|
|
'carddata' => $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB),
|
|
|
|
|
'uri' => $query->createNamedParameter($cardUri),
|
|
|
|
|
'lastmodified' => $query->createNamedParameter(time()),
|
|
|
|
|
'addressbookid' => $query->createNamedParameter($addressBookId),
|
|
|
|
|
'size' => $query->createNamedParameter(strlen($cardData)),
|
|
|
|
|
'etag' => $query->createNamedParameter($etag),
|
|
|
|
|
'uid' => $query->createNamedParameter($uid),
|
|
|
|
|
])
|
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
|
|
$etagCacheKey = "$addressBookId#$cardUri";
|
|
|
|
|
$this->etagCache[$etagCacheKey] = $etag;
|
|
|
|
|
$etagCacheKey = "$addressBookId#$cardUri";
|
|
|
|
|
$this->etagCache[$etagCacheKey] = $etag;
|
|
|
|
|
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 1);
|
|
|
|
|
$this->updateProperties($addressBookId, $cardUri, $cardData);
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 1);
|
|
|
|
|
$this->updateProperties($addressBookId, $cardUri, $cardData);
|
|
|
|
|
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardCreatedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardCreatedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -702,34 +707,37 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
public function updateCard($addressBookId, $cardUri, $cardData) {
|
|
|
|
|
$uid = $this->getUID($cardData);
|
|
|
|
|
$etag = md5($cardData);
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
|
|
|
|
|
// check for recently stored etag and stop if it is the same
|
|
|
|
|
$etagCacheKey = "$addressBookId#$cardUri";
|
|
|
|
|
if (isset($this->etagCache[$etagCacheKey]) && $this->etagCache[$etagCacheKey] === $etag) {
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
}
|
|
|
|
|
return $this->atomic(function () use ($addressBookId, $cardUri, $cardData, $uid, $etag) {
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
|
|
|
|
|
$query->update($this->dbCardsTable)
|
|
|
|
|
->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB))
|
|
|
|
|
->set('lastmodified', $query->createNamedParameter(time()))
|
|
|
|
|
->set('size', $query->createNamedParameter(strlen($cardData)))
|
|
|
|
|
->set('etag', $query->createNamedParameter($etag))
|
|
|
|
|
->set('uid', $query->createNamedParameter($uid))
|
|
|
|
|
->where($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
|
|
|
|
|
->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->execute();
|
|
|
|
|
// check for recently stored etag and stop if it is the same
|
|
|
|
|
$etagCacheKey = "$addressBookId#$cardUri";
|
|
|
|
|
if (isset($this->etagCache[$etagCacheKey]) && $this->etagCache[$etagCacheKey] === $etag) {
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->etagCache[$etagCacheKey] = $etag;
|
|
|
|
|
$query->update($this->dbCardsTable)
|
|
|
|
|
->set('carddata', $query->createNamedParameter($cardData, IQueryBuilder::PARAM_LOB))
|
|
|
|
|
->set('lastmodified', $query->createNamedParameter(time()))
|
|
|
|
|
->set('size', $query->createNamedParameter(strlen($cardData)))
|
|
|
|
|
->set('etag', $query->createNamedParameter($etag))
|
|
|
|
|
->set('uid', $query->createNamedParameter($uid))
|
|
|
|
|
->where($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
|
|
|
|
|
->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
|
|
$this->etagCache[$etagCacheKey] = $etag;
|
|
|
|
|
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 2);
|
|
|
|
|
$this->updateProperties($addressBookId, $cardUri, $cardData);
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 2);
|
|
|
|
|
$this->updateProperties($addressBookId, $cardUri, $cardData);
|
|
|
|
|
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardUpdatedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardUpdatedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
return '"' . $etag . '"';
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -740,32 +748,34 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @return bool
|
|
|
|
|
*/
|
|
|
|
|
public function deleteCard($addressBookId, $cardUri) {
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$cardId = $this->getCardId($addressBookId, $cardUri);
|
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
|
$cardId = null;
|
|
|
|
|
}
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$ret = $query->delete($this->dbCardsTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
return $this->atomic(function () use ($addressBookId, $cardUri) {
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$shares = $this->getShares($addressBookId);
|
|
|
|
|
$objectRow = $this->getCard($addressBookId, $cardUri);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$cardId = $this->getCardId($addressBookId, $cardUri);
|
|
|
|
|
} catch (\InvalidArgumentException $e) {
|
|
|
|
|
$cardId = null;
|
|
|
|
|
}
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$ret = $query->delete($this->dbCardsTable)
|
|
|
|
|
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->andWhere($query->expr()->eq('uri', $query->createNamedParameter($cardUri)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 3);
|
|
|
|
|
$this->addChange($addressBookId, $cardUri, 3);
|
|
|
|
|
|
|
|
|
|
if ($ret === 1) {
|
|
|
|
|
if ($cardId !== null) {
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardDeletedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
$this->purgeProperties($addressBookId, $cardId);
|
|
|
|
|
if ($ret === 1) {
|
|
|
|
|
if ($cardId !== null) {
|
|
|
|
|
$this->dispatcher->dispatchTyped(new CardDeletedEvent($addressBookId, $addressBookData, $shares, $objectRow));
|
|
|
|
|
$this->purgeProperties($addressBookId, $cardId);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
return false;
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -826,81 +836,83 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
*/
|
|
|
|
|
public function getChangesForAddressBook($addressBookId, $syncToken, $syncLevel, $limit = null) {
|
|
|
|
|
// Current synctoken
|
|
|
|
|
$qb = $this->db->getQueryBuilder();
|
|
|
|
|
$qb->select('synctoken')
|
|
|
|
|
->from('addressbooks')
|
|
|
|
|
->where(
|
|
|
|
|
$qb->expr()->eq('id', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
);
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
$currentToken = $stmt->fetchOne();
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
|
|
|
|
|
if (is_null($currentToken)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = [
|
|
|
|
|
'syncToken' => $currentToken,
|
|
|
|
|
'added' => [],
|
|
|
|
|
'modified' => [],
|
|
|
|
|
'deleted' => [],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if ($syncToken) {
|
|
|
|
|
return $this->atomic(function () use ($addressBookId, $syncToken, $syncLevel, $limit) {
|
|
|
|
|
$qb = $this->db->getQueryBuilder();
|
|
|
|
|
$qb->select('uri', 'operation')
|
|
|
|
|
->from('addressbookchanges')
|
|
|
|
|
$qb->select('synctoken')
|
|
|
|
|
->from('addressbooks')
|
|
|
|
|
->where(
|
|
|
|
|
$qb->expr()->andX(
|
|
|
|
|
$qb->expr()->gte('synctoken', $qb->createNamedParameter($syncToken)),
|
|
|
|
|
$qb->expr()->lt('synctoken', $qb->createNamedParameter($currentToken)),
|
|
|
|
|
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
)
|
|
|
|
|
)->orderBy('synctoken');
|
|
|
|
|
$qb->expr()->eq('id', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
);
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
$currentToken = $stmt->fetchOne();
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
|
|
|
|
|
if (is_int($limit) && $limit > 0) {
|
|
|
|
|
$qb->setMaxResults($limit);
|
|
|
|
|
if (is_null($currentToken)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fetching all changes
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
$result = [
|
|
|
|
|
'syncToken' => $currentToken,
|
|
|
|
|
'added' => [],
|
|
|
|
|
'modified' => [],
|
|
|
|
|
'deleted' => [],
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$changes = [];
|
|
|
|
|
if ($syncToken) {
|
|
|
|
|
$qb = $this->db->getQueryBuilder();
|
|
|
|
|
$qb->select('uri', 'operation')
|
|
|
|
|
->from('addressbookchanges')
|
|
|
|
|
->where(
|
|
|
|
|
$qb->expr()->andX(
|
|
|
|
|
$qb->expr()->gte('synctoken', $qb->createNamedParameter($syncToken)),
|
|
|
|
|
$qb->expr()->lt('synctoken', $qb->createNamedParameter($currentToken)),
|
|
|
|
|
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
)
|
|
|
|
|
)->orderBy('synctoken');
|
|
|
|
|
|
|
|
|
|
if (is_int($limit) && $limit > 0) {
|
|
|
|
|
$qb->setMaxResults($limit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This loop ensures that any duplicates are overwritten, only the
|
|
|
|
|
// last change on a node is relevant.
|
|
|
|
|
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
|
|
|
|
$changes[$row['uri']] = $row['operation'];
|
|
|
|
|
}
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
// Fetching all changes
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
|
|
|
|
|
foreach ($changes as $uri => $operation) {
|
|
|
|
|
switch ($operation) {
|
|
|
|
|
case 1:
|
|
|
|
|
$result['added'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
$result['modified'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
$result['deleted'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
$changes = [];
|
|
|
|
|
|
|
|
|
|
// This loop ensures that any duplicates are overwritten, only the
|
|
|
|
|
// last change on a node is relevant.
|
|
|
|
|
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
|
|
|
|
$changes[$row['uri']] = $row['operation'];
|
|
|
|
|
}
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
|
|
|
|
|
foreach ($changes as $uri => $operation) {
|
|
|
|
|
switch ($operation) {
|
|
|
|
|
case 1:
|
|
|
|
|
$result['added'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
$result['modified'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
$result['deleted'][] = $uri;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$qb = $this->db->getQueryBuilder();
|
|
|
|
|
$qb->select('uri')
|
|
|
|
|
->from('cards')
|
|
|
|
|
->where(
|
|
|
|
|
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
);
|
|
|
|
|
// No synctoken supplied, this is the initial sync.
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$qb = $this->db->getQueryBuilder();
|
|
|
|
|
$qb->select('uri')
|
|
|
|
|
->from('cards')
|
|
|
|
|
->where(
|
|
|
|
|
$qb->expr()->eq('addressbookid', $qb->createNamedParameter($addressBookId))
|
|
|
|
|
);
|
|
|
|
|
// No synctoken supplied, this is the initial sync.
|
|
|
|
|
$stmt = $qb->executeQuery();
|
|
|
|
|
$result['added'] = $stmt->fetchAll(\PDO::FETCH_COLUMN);
|
|
|
|
|
$stmt->closeCursor();
|
|
|
|
|
}
|
|
|
|
|
return $result;
|
|
|
|
|
return $result;
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -911,19 +923,32 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @param int $operation 1 = add, 2 = modify, 3 = delete
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
protected function addChange($addressBookId, $objectUri, $operation) {
|
|
|
|
|
$sql = 'INSERT INTO `*PREFIX*addressbookchanges`(`uri`, `synctoken`, `addressbookid`, `operation`) SELECT ?, `synctoken`, ?, ? FROM `*PREFIX*addressbooks` WHERE `id` = ?';
|
|
|
|
|
$stmt = $this->db->prepare($sql);
|
|
|
|
|
$stmt->execute([
|
|
|
|
|
$objectUri,
|
|
|
|
|
$addressBookId,
|
|
|
|
|
$operation,
|
|
|
|
|
$addressBookId
|
|
|
|
|
]);
|
|
|
|
|
$stmt = $this->db->prepare('UPDATE `*PREFIX*addressbooks` SET `synctoken` = `synctoken` + 1 WHERE `id` = ?');
|
|
|
|
|
$stmt->execute([
|
|
|
|
|
$addressBookId
|
|
|
|
|
]);
|
|
|
|
|
protected function addChange(int $addressBookId, string $objectUri, int $operation): void {
|
|
|
|
|
$this->atomic(function () use ($addressBookId, $objectUri, $operation) {
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->select('synctoken')
|
|
|
|
|
->from('addressbooks')
|
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId)));
|
|
|
|
|
$result = $query->executeQuery();
|
|
|
|
|
$syncToken = (int)$result->fetchOne();
|
|
|
|
|
$result->closeCursor();
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert('addressbookchanges')
|
|
|
|
|
->values([
|
|
|
|
|
'uri' => $query->createNamedParameter($objectUri),
|
|
|
|
|
'synctoken' => $query->createNamedParameter($syncToken),
|
|
|
|
|
'addressbookid' => $query->createNamedParameter($addressBookId),
|
|
|
|
|
'operation' => $query->createNamedParameter($operation),
|
|
|
|
|
])
|
|
|
|
|
->executeStatement();
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->update('addressbooks')
|
|
|
|
|
->set('synctoken', $query->createNamedParameter($syncToken + 1, IQueryBuilder::PARAM_INT))
|
|
|
|
|
->where($query->expr()->eq('id', $query->createNamedParameter($addressBookId)))
|
|
|
|
|
->executeStatement();
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -973,13 +998,15 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @param list<string> $remove
|
|
|
|
|
*/
|
|
|
|
|
public function updateShares(IShareable $shareable, array $add, array $remove): void {
|
|
|
|
|
$addressBookId = $shareable->getResourceId();
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$oldShares = $this->getShares($addressBookId);
|
|
|
|
|
$this->atomic(function () use ($shareable, $add, $remove) {
|
|
|
|
|
$addressBookId = $shareable->getResourceId();
|
|
|
|
|
$addressBookData = $this->getAddressBookById($addressBookId);
|
|
|
|
|
$oldShares = $this->getShares($addressBookId);
|
|
|
|
|
|
|
|
|
|
$this->sharingBackend->updateShares($shareable, $add, $remove);
|
|
|
|
|
$this->sharingBackend->updateShares($shareable, $add, $remove);
|
|
|
|
|
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookShareUpdatedEvent($addressBookId, $addressBookData, $oldShares, $add, $remove));
|
|
|
|
|
$this->dispatcher->dispatchTyped(new AddressBookShareUpdatedEvent($addressBookId, $addressBookData, $oldShares, $add, $remove));
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -998,7 +1025,9 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @return array an array of contacts which are arrays of key-value-pairs
|
|
|
|
|
*/
|
|
|
|
|
public function search($addressBookId, $pattern, $searchProperties, $options = []): array {
|
|
|
|
|
return $this->searchByAddressBookIds([$addressBookId], $pattern, $searchProperties, $options);
|
|
|
|
|
return $this->atomic(function () use ($addressBookId, $pattern, $searchProperties, $options) {
|
|
|
|
|
return $this->searchByAddressBookIds([$addressBookId], $pattern, $searchProperties, $options);
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1014,11 +1043,13 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
string $pattern,
|
|
|
|
|
array $searchProperties,
|
|
|
|
|
array $options = []): array {
|
|
|
|
|
$addressBookIds = array_map(static function ($row):int {
|
|
|
|
|
return (int) $row['id'];
|
|
|
|
|
}, $this->getAddressBooksForUser($principalUri));
|
|
|
|
|
return $this->atomic(function () use ($principalUri, $pattern, $searchProperties, $options) {
|
|
|
|
|
$addressBookIds = array_map(static function ($row):int {
|
|
|
|
|
return (int) $row['id'];
|
|
|
|
|
}, $this->getAddressBooksForUser($principalUri));
|
|
|
|
|
|
|
|
|
|
return $this->searchByAddressBookIds($addressBookIds, $pattern, $searchProperties, $options);
|
|
|
|
|
return $this->searchByAddressBookIds($addressBookIds, $pattern, $searchProperties, $options);
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -1219,27 +1250,24 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
* @param string $vCardSerialized
|
|
|
|
|
*/
|
|
|
|
|
protected function updateProperties($addressBookId, $cardUri, $vCardSerialized) {
|
|
|
|
|
$cardId = $this->getCardId($addressBookId, $cardUri);
|
|
|
|
|
$vCard = $this->readCard($vCardSerialized);
|
|
|
|
|
|
|
|
|
|
$this->purgeProperties($addressBookId, $cardId);
|
|
|
|
|
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert($this->dbCardsPropertiesTable)
|
|
|
|
|
->values(
|
|
|
|
|
[
|
|
|
|
|
'addressbookid' => $query->createNamedParameter($addressBookId),
|
|
|
|
|
'cardid' => $query->createNamedParameter($cardId),
|
|
|
|
|
'name' => $query->createParameter('name'),
|
|
|
|
|
'value' => $query->createParameter('value'),
|
|
|
|
|
'preferred' => $query->createParameter('preferred')
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
$this->atomic(function () use ($addressBookId, $cardUri, $vCardSerialized) {
|
|
|
|
|
$cardId = $this->getCardId($addressBookId, $cardUri);
|
|
|
|
|
$vCard = $this->readCard($vCardSerialized);
|
|
|
|
|
|
|
|
|
|
$this->purgeProperties($addressBookId, $cardId);
|
|
|
|
|
|
|
|
|
|
$this->db->beginTransaction();
|
|
|
|
|
$query = $this->db->getQueryBuilder();
|
|
|
|
|
$query->insert($this->dbCardsPropertiesTable)
|
|
|
|
|
->values(
|
|
|
|
|
[
|
|
|
|
|
'addressbookid' => $query->createNamedParameter($addressBookId),
|
|
|
|
|
'cardid' => $query->createNamedParameter($cardId),
|
|
|
|
|
'name' => $query->createParameter('name'),
|
|
|
|
|
'value' => $query->createParameter('value'),
|
|
|
|
|
'preferred' => $query->createParameter('preferred')
|
|
|
|
|
]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
foreach ($vCard->children() as $property) {
|
|
|
|
|
if (!in_array($property->name, self::$indexProperties)) {
|
|
|
|
|
continue;
|
|
|
|
|
@ -1256,10 +1284,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
|
|
|
|
|
$query->setParameter('preferred', $preferred);
|
|
|
|
|
$query->execute();
|
|
|
|
|
}
|
|
|
|
|
$this->db->commit();
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
$this->db->rollBack();
|
|
|
|
|
}
|
|
|
|
|
}, $this->db);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|