1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2024-12-12 23:09:15 +01:00
OwncloudContactsOfficial/lib/backend/database.php

1046 lines
29 KiB
PHP
Raw Normal View History

<?php
/**
* ownCloud - Database backend for Contacts
*
* @author Thomas Tanghus
2014-01-26 00:40:22 +01:00
* @copyright 2013-2014 Thomas Tanghus (thomas@tanghus.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
* License as published by the Free Software Foundation; either
* version 3 of the License, or any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Contacts\Backend;
2013-11-28 14:15:17 +01:00
use OCA\Contacts\Contact,
OCA\Contacts\VObject\VCard,
OCA\Contacts\Utils\Properties,
Sabre\VObject\Reader;
/**
2014-04-24 13:39:29 +02:00
* Backend class for a users own contacts.
*/
class Database extends AbstractBackend {
static private $preparedQueries = array();
2013-03-12 09:15:40 +01:00
2014-04-24 13:39:29 +02:00
/**
* The name of the backend.
*
* @var string
*/
public $name = 'local';
2014-03-08 17:37:05 +01:00
/**
* The cached address books.
2014-04-24 13:39:29 +02:00
*
2014-03-08 17:37:05 +01:00
* @var array[]
*/
2014-03-14 20:57:26 +01:00
public $addressBooks;
2014-03-08 17:37:05 +01:00
/**
* The table that holds the address books.
2014-04-24 13:39:29 +02:00
*
2014-03-08 17:37:05 +01:00
* @var string
*/
public $addressBooksTableName;
/**
* The table that holds the contact vCards.
2014-04-24 13:39:29 +02:00
*
2014-03-08 17:37:05 +01:00
* @var string
*/
public $cardsTableName;
/**
* The table that holds the indexed vCard properties.
2014-04-24 13:39:29 +02:00
*
2014-03-08 17:37:05 +01:00
* @var string
*/
public $indexTableName;
/**
* Sets up the backend
*
* @param string $addressBooksTableName
* @param string $cardsTableName
*/
public function __construct(
$userid = null,
$options = array(
'addressBooksTableName' => '*PREFIX*contacts_addressbooks',
'cardsTableName' => '*PREFIX*contacts_cards',
'indexTableName' => '*PREFIX*contacts_cards_properties'
)
) {
$this->userid = $userid ? $userid : \OCP\User::getUser();
$this->addressBooksTableName = $options['addressBooksTableName'];
$this->cardsTableName = $options['cardsTableName'];
$this->indexTableName = $options['indexTableName'];
2014-03-14 20:57:26 +01:00
$this->addressBooks = array();
}
/**
2014-03-16 01:29:24 +01:00
* {@inheritdoc}
*/
public function getAddressBooksForUser(array $options = array()) {
try {
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('getaddressbooksforuser')
->execute(array($this->userid));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
2014-03-18 23:51:07 +01:00
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
2014-03-14 20:57:26 +01:00
return $this->addressBooks;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
2014-03-18 23:51:07 +01:00
\OCP\Util::writeLog('contacts', __METHOD__.' exception: '
. $e->getMessage(), \OCP\Util::ERROR);
2014-03-14 20:57:26 +01:00
return $this->addressBooks;
}
2014-03-08 16:22:51 +01:00
while ($row = $result->fetchRow()) {
$row['permissions'] = \OCP\PERMISSION_ALL;
2014-03-14 20:57:26 +01:00
$this->addressBooks[$row['id']] = $row;
}
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
return $this->addressBooks;
}
2014-03-16 01:29:24 +01:00
/**
* {@inheritdoc}
*/
2014-03-14 20:57:26 +01:00
public function getAddressBook($addressBookId, array $options = array()) {
$owner = isset($options['shared_by']) ? $options['shared_by'] : $this->userid;
2013-03-12 09:15:40 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.' id: '
2014-03-14 20:57:26 +01:00
// . $addressBookId, \OCP\Util::DEBUG);
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
//print(__METHOD__ . ' ' . __LINE__ .' addressBookInfo: ' . print_r($this->addressBooks[$addressBookId], true));
return $this->addressBooks[$addressBookId];
2013-03-12 09:15:40 +01:00
}
2014-03-08 16:22:51 +01:00
// Hmm, not found. Lets query the db.
2013-03-12 09:15:40 +01:00
try {
$result = $this->getPreparedQuery('getaddressbook')->execute(array($addressBookId, $owner));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
2013-03-12 09:15:40 +01:00
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
2013-05-26 22:15:52 +02:00
return null;
}
2014-03-08 16:22:51 +01:00
$row = $result->fetchRow();
2014-03-08 16:22:51 +01:00
if (!$row) {
return null;
2013-03-12 09:15:40 +01:00
}
2014-03-08 17:37:05 +01:00
$row['permissions'] = \OCP\PERMISSION_ALL;
$row['backend'] = $this->name;
2014-03-14 20:57:26 +01:00
$this->addressBooks[$addressBookId] = $row;
return $row;
2014-03-08 16:22:51 +01:00
2013-03-12 09:15:40 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.' exception: '
. $e->getMessage(), \OCP\Util::ERROR);
2013-05-26 22:15:52 +02:00
return null;
2013-03-12 09:15:40 +01:00
}
2014-03-08 16:22:51 +01:00
2013-03-12 09:15:40 +01:00
}
2014-03-16 01:29:24 +01:00
/**
* {@inheritdoc}
*/
2014-03-23 19:02:16 +01:00
public function hasAddressBook($addressBookId) {
2014-03-08 16:22:51 +01:00
// First check if it's already cached
2014-03-14 20:57:26 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
return true;
2013-03-12 09:15:40 +01:00
}
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
return count($this->getAddressBook($addressBookId)) > 0;
2013-03-12 09:15:40 +01:00
}
/**
* Updates an addressbook's properties
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
* @param array $changes
* @return bool
*/
2014-03-19 01:59:09 +01:00
public function updateAddressBook($addressBookId, array $changes) {
2014-03-08 16:22:51 +01:00
if (count($changes) === 0) {
return false;
}
$query = 'UPDATE `' . $this->addressBooksTableName . '` SET ';
$updates = array();
2014-03-08 16:22:51 +01:00
if (isset($changes['displayname'])) {
$query .= '`displayname` = ?, ';
$updates[] = $changes['displayname'];
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
$this->addressBooks[$addressBookId]['displayname'] = $changes['displayname'];
}
2014-03-08 16:22:51 +01:00
}
2014-03-08 16:22:51 +01:00
if (isset($changes['description'])) {
$query .= '`description` = ?, ';
$updates[] = $changes['description'];
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
$this->addressBooks[$addressBookId]['description'] = $changes['description'];
}
2014-03-08 16:22:51 +01:00
}
2013-04-27 02:20:12 +02:00
$query .= '`ctag` = ? + 1 WHERE `id` = ?';
2014-03-18 23:51:07 +01:00
$now = time();
$updates[] = $now;
2014-03-14 20:57:26 +01:00
$updates[] = $addressBookId;
2014-03-18 23:51:07 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
$this->addressBooks[$addressBookId]['lastmodified'] = $now;
}
try {
2014-03-08 16:22:51 +01:00
$stmt = \OCP\DB::prepare($query);
$result = $stmt->execute($updates);
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts',
__METHOD__. 'DB error: '
2013-09-16 02:24:08 +02:00
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts',
__METHOD__ . ', exception: '
. $e->getMessage(), \OCP\Util::ERROR);
return false;
}
return true;
}
/**
* Creates a new address book
*
* Supported properties are 'displayname', 'description' and 'uri'.
* 'uri' is supported to allow to add from CardDAV requests, and MUST
* be used for the 'uri' database field if present.
* 'displayname' MUST be present.
*
* @param array $properties
* @param array $options - Optional (backend specific options)
* @return string|false The ID if the newly created AddressBook or false on error.
*/
2014-03-23 19:02:16 +01:00
public function createAddressBook(array $properties) {
2014-03-08 16:22:51 +01:00
if (count($properties) === 0 || !isset($properties['displayname'])) {
return false;
}
$updates = array($this->userid, $properties['displayname']);
$updates[] = isset($properties['uri'])
? $properties['uri']
: $this->createAddressBookURI($properties['displayname']);
$updates[] = isset($properties['description']) ? $properties['description'] : '';
$ctag = time();
$updates[] = $ctag;
try {
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('createaddressbook')->execute($updates);
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
$newid = \OCP\DB::insertid($this->addressBooksTableName);
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
if ($this->addressBooks) {
$updates['id'] = $newid;
$updates['ctag'] = $ctag;
$updates['lastmodified'] = $ctag;
$updates['permissions'] = \OCP\PERMISSION_ALL;
2014-03-14 20:57:26 +01:00
$this->addressBooks[$newid] = $updates;
}
2014-03-08 16:22:51 +01:00
return $newid;
}
/**
2014-03-18 23:51:07 +01:00
* Get all contact ids from the address book to run pre_deleteAddressBook hook
2013-05-25 01:35:08 +02:00
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
*/
2014-03-18 23:51:07 +01:00
protected function preDeleteAddressBook($addressBookId) {
// Get all contact ids for this address book
$ids = array();
$result = null;
2014-03-08 16:22:51 +01:00
try {
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('getcontactids')
->execute(array($addressBookId));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
2013-09-16 02:24:08 +02:00
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
if (!is_null($result)) {
while ($id = $result->fetchOne()) {
$ids[] = $id;
}
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
\OCP\Util::emitHook('OCA\Contacts', 'pre_deleteAddressBook',
array('addressbookid' => $addressBookId, 'contactids' => $ids)
);
}
2014-03-18 23:51:07 +01:00
}
2014-03-18 23:51:07 +01:00
/**
* Deletes an entire addressbook and all its contents
*
* NOTE: For efficience this method bypasses the cleanup hooks and deletes
* property indexes and category/group relations by itself.
*
* @param string $addressBookId
* @param array $options - Optional (backend specific options)
* @return bool
*/
public function deleteAddressBook($addressBookId) {
2014-03-18 23:51:07 +01:00
$this->preDeleteAddressBook($addressBookId);
2014-03-08 16:22:51 +01:00
try {
2014-03-18 23:51:07 +01:00
$this->getPreparedQuery('deleteaddressbookcontacts')
2014-03-14 20:57:26 +01:00
->execute(array($addressBookId));
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
try {
2014-03-18 23:51:07 +01:00
$this->getPreparedQuery('deleteaddressbook')
2014-03-14 20:57:26 +01:00
->execute(array($addressBookId));
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
2014-03-14 20:57:26 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
unset($this->addressBooks[$addressBookId]);
}
return true;
}
/**
* @brief Updates ctag for addressbook
* @param integer $id
* @return boolean
*/
2013-11-07 14:24:55 +01:00
public function setModifiedAddressBook($id) {
2013-04-27 02:20:12 +02:00
$ctag = time();
2014-03-18 23:51:07 +01:00
$this->getPreparedQuery('touchaddressbook')->execute(array($ctag, $id));
return true;
}
2014-03-16 01:29:24 +01:00
/**
* {@inheritdoc}
*/
2014-03-14 20:57:26 +01:00
public function lastModifiedAddressBook($addressBookId) {
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
if ($this->addressBooks && isset($this->addressBooks[$addressBookId])) {
return $this->addressBooks[$addressBookId]['lastmodified'];
}
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
$addressBook = $this->getAddressBook($addressBookId);
2014-03-18 23:51:07 +01:00
if($addressBook) {
$this->addressBooks[$addressBookId] = $addressBook;
}
return $addressBook ? $addressBook['lastmodified'] : null;
}
/**
* Returns the number of contacts in a specific address book.
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
* @param bool $omitdata Don't fetch the entire carddata or vcard.
* @return array
*/
2014-03-14 20:57:26 +01:00
public function numContacts($addressBookId) {
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('numcontacts')->execute(array($addressBookId));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return null;
}
2014-03-08 16:22:51 +01:00
return (int)$result->fetchOne();
}
/**
2014-03-16 01:29:24 +01:00
* {@inheritdoc}
*/
2014-03-18 23:51:07 +01:00
public function getContacts($addressBookId, array $options = array()) {
2014-03-14 20:57:26 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.' addressbookid: ' . $addressBookId, \OCP\Util::DEBUG);
$cards = array();
try {
2014-03-18 23:51:07 +01:00
$queryIdentifier = (isset($options['omitdata']) && $options['omitdata'] === true)
? 'getcontactsomitdata'
: 'getcontacts';
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery($queryIdentifier, $options)->execute(array($addressBookId));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return $cards;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return $cards;
}
2014-03-08 16:22:51 +01:00
if (!is_null($result)) {
while ($row = $result->fetchRow()) {
$row['permissions'] = \OCP\PERMISSION_ALL;
$cards[] = $row;
}
2014-03-08 16:22:51 +01:00
}
2014-04-01 21:28:49 +02:00
return $cards;
}
/**
* Returns a specific contact.
*
2014-04-24 13:39:29 +02:00
* NOTE: The contact $id for Database and Shared backends can be an array containing
2013-03-12 09:15:40 +01:00
* either 'id' or 'uri' to be able to play seamlessly with the
* CardDAV backend.
2014-04-24 13:39:29 +02:00
* NOTE: $addressbookid isn't always used in the query, so there's no access control.
* This is because the groups backend - \OCP\Tags - doesn't no about parent collections
* only object IDs. Hence a hack is made with an optional 'noCollection'.
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
2014-04-24 13:39:29 +02:00
* @param string|array $id Contact ID
* @param array $options - Optional (backend specific options)
* @return array|null
*/
2014-03-14 20:57:26 +01:00
public function getContact($addressBookId, $id, array $options = array()) {
2014-03-18 23:51:07 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $addressBookId . ' / ' . $id, \OCP\Util::DEBUG);
2013-03-12 09:15:40 +01:00
2014-03-18 23:51:07 +01:00
// When dealing with tags we have no idea if which address book it's in
// but since they're all in the same table they have unique IDs anyway
$noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
2014-03-18 23:51:07 +01:00
$queryIdentifier = 'getcontact';
$queries = array();
// When querying from CardDAV we don't have the ID, only the uri
2014-03-08 16:22:51 +01:00
if (is_array($id)) {
if (isset($id['id'])) {
2014-03-18 23:51:07 +01:00
$queries[] = $id['id'];
$queryIdentifier .= 'byid';
2014-03-08 16:22:51 +01:00
} elseif (isset($id['uri'])) {
2014-03-18 23:51:07 +01:00
$queries[] = $id['uri'];
$queryIdentifier .= 'byuri';
2013-03-12 09:15:40 +01:00
} else {
throw new \Exception(
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
);
}
2014-03-18 23:51:07 +01:00
} else {
if (!trim($id)) {
throw new \Exception(
__METHOD__ . ' Missing or empty second argument \'$id\'.'
);
}
$queries[] = $id;
$queryIdentifier .= 'byid';
2013-03-12 09:15:40 +01:00
}
2013-03-25 17:10:21 +01:00
2014-03-18 23:51:07 +01:00
if ($noCollection) {
$queryIdentifier .= 'nocollection';
} else {
$queries[] = $addressBookId;
2013-03-25 17:10:21 +01:00
}
try {
2014-03-18 23:51:07 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.', identifier: '. $queryIdentifier . ', queries: ' . implode(',', $queries), \OCP\Util::DEBUG);
$result = $this->getPreparedQuery($queryIdentifier)->execute($queries);
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return null;
}
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
} catch (\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
\OCP\Util::writeLog('contacts', __METHOD__.', id: '. $id, \OCP\Util::DEBUG);
return null;
}
$row = $result->fetchRow();
2014-03-08 16:22:51 +01:00
if (!$row) {
if (is_array($id)) {
$idstr = implode(", ", $id);
} else {
$idstr = $id;
}
\OCP\Util::writeLog('contacts', __METHOD__.', Not found, id: '. $idstr, \OCP\Util::DEBUG);
return null;
}
2014-03-08 16:22:51 +01:00
$row['permissions'] = \OCP\PERMISSION_ALL;
return $row;
}
2014-03-14 20:57:26 +01:00
public function hasContact($addressBookId, $id) {
2014-03-18 23:51:07 +01:00
try {
return $this->getContact($addressBookId, $id) !== null;
} catch (\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return false;
}
}
/**
* Creates a new contact
*
2013-03-12 09:15:40 +01:00
* In the Database and Shared backends contact be either a Contact object or a string
* with carddata to be able to play seamlessly with the CardDAV backend.
* If this method is called by the CardDAV backend, the carddata is already validated.
2013-03-17 21:31:46 +01:00
* NOTE: It's assumed that this method is called either from the CardDAV backend, the
* import script, or from the ownCloud web UI in which case either the uri parameter is
* set, or the contact has a UID. If neither is set, it will fail.
2013-03-12 09:15:40 +01:00
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
* @param VCard|string $contact
* @param array $options - Optional (backend specific options)
* @return string|bool The identifier for the new contact or false on error.
*/
2014-03-14 20:57:26 +01:00
public function createContact($addressBookId, $contact, array $options = array()) {
2014-03-18 23:51:07 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.' addressBookId: ' . $addressBookId, \OCP\Util::DEBUG);
$uri = isset($options['uri']) ? $options['uri'] : null;
2014-03-08 16:22:51 +01:00
if (!$contact instanceof VCard) {
2013-03-12 09:15:40 +01:00
try {
$contact = Reader::read($contact);
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return false;
}
}
try {
$contact->validate(VCard::REPAIR|VCard::UPGRADE);
} catch (\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__ . ' ' .
'Error validating vcard: ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
2014-03-14 20:57:26 +01:00
$uri = is_null($uri) ? $this->uniqueURI($addressBookId, $contact->UID . '.vcf') : $uri;
$now = new \DateTime;
$contact->REV = $now->format(\DateTime::W3C);
$appinfo = \OCP\App::getAppInfo('contacts');
$appversion = \OCP\App::getAppVersion('contacts');
2014-03-18 23:51:07 +01:00
$prodid = '-//ownCloud//NONSGML ' . $appinfo['name'] . ' ' . $appversion.'//EN';
2013-03-29 16:42:33 +01:00
$contact->PRODID = $prodid;
try {
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('createcontact')
->execute(
array(
2014-03-14 20:57:26 +01:00
$addressBookId,
(string)$contact->FN,
$contact->serialize(),
$uri,
time()
)
);
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return false;
}
$newid = \OCP\DB::insertid($this->cardsTableName);
2014-03-14 20:57:26 +01:00
$this->setModifiedAddressBook($addressBookId);
2013-09-16 02:24:08 +02:00
\OCP\Util::emitHook('OCA\Contacts', 'post_createContact',
2014-03-14 20:57:26 +01:00
array('id' => $newid, 'parent' => $addressBookId, 'backend' => $this->name, 'contact' => $contact)
);
return (string)$newid;
}
/**
* Updates a contact
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
2014-04-24 13:39:29 +02:00
* @param string|array $id Contact ID
* @param VCard|string $contact
* @param array $options - Optional (backend specific options)
2013-03-12 09:15:40 +01:00
* @see getContact
* @return bool
2014-03-18 23:51:07 +01:00
* @throws \Exception if $contact is a string but can't be parsed as a VCard
* @throws \Exception if the Contact to update couldn't be found
*/
2014-03-14 20:57:26 +01:00
public function updateContact($addressBookId, $id, $contact, array $options = array()) {
2014-03-18 23:51:07 +01:00
//\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $addressBookId . ' / ' . $id, \OCP\Util::DEBUG);
$noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
$isBatch = isset($options['isBatch']) ? $options['isBatch'] : false;
$updateRevision = true;
$isCardDAV = false;
2014-03-08 16:22:51 +01:00
if (!$contact instanceof VCard) {
2013-03-12 09:15:40 +01:00
try {
$contact = Reader::read($contact);
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return false;
}
}
2013-10-03 18:09:19 +02:00
2014-03-08 16:22:51 +01:00
if (is_array($id)) {
if (isset($id['id'])) {
2013-03-12 09:15:40 +01:00
$id = $id['id'];
2014-03-08 16:22:51 +01:00
} elseif (isset($id['uri'])) {
$updateRevision = false;
$isCardDAV = true;
2013-10-03 18:09:19 +02:00
$id = $this->getIdFromUri($id['uri']);
2014-03-08 16:22:51 +01:00
if (is_null($id)) {
2013-10-03 18:09:19 +02:00
\OCP\Util::writeLog('contacts', __METHOD__ . ' Couldn\'t find contact', \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
2013-03-12 09:15:40 +01:00
} else {
throw new \Exception(
2013-03-12 09:15:40 +01:00
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
);
}
}
2013-03-25 17:10:21 +01:00
2014-03-08 16:22:51 +01:00
if ($updateRevision || !isset($contact->REV)) {
$now = new \DateTime;
$contact->REV = $now->format(\DateTime::W3C);
}
$data = $contact->serialize();
2013-03-25 17:10:21 +01:00
2014-03-08 16:22:51 +01:00
if ($noCollection) {
2013-09-03 14:07:20 +02:00
$me = $this->getContact(null, $id, $options);
2014-03-14 20:57:26 +01:00
$addressBookId = $me['parent'];
}
2013-10-03 18:09:19 +02:00
2014-03-14 20:57:26 +01:00
$updates = array($contact->FN, $data, time(), $id, $addressBookId);
2013-03-25 17:10:21 +01:00
try {
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('updatecontact')->execute($updates);
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '
. $e->getMessage(), \OCP\Util::ERROR);
\OCP\Util::writeLog('contacts', __METHOD__.', id' . $id, \OCP\Util::DEBUG);
return false;
}
2014-03-14 20:57:26 +01:00
$this->setModifiedAddressBook($addressBookId);
2014-03-08 16:22:51 +01:00
if (!$isBatch) {
2013-09-16 02:24:08 +02:00
\OCP\Util::emitHook('OCA\Contacts', 'post_updateContact',
array(
'backend' => $this->name,
2014-03-14 20:57:26 +01:00
'addressBookId' => $addressBookId,
'contactId' => $id,
'contact' => $contact,
'carddav' => $isCardDAV
)
);
2014-03-08 16:22:51 +01:00
}
2014-03-08 16:22:51 +01:00
return true;
}
/**
* Deletes a contact
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
2014-04-24 13:39:29 +02:00
* @param string|array $id
* @param array $options - Optional (backend specific options)
2013-03-12 09:15:40 +01:00
* @see getContact
* @return bool
*/
2014-03-14 20:57:26 +01:00
public function deleteContact($addressBookId, $id, array $options = array()) {
// TODO: pass the uri in $options instead.
2013-10-03 18:09:19 +02:00
$noCollection = isset($options['noCollection']) ? $options['noCollection'] : false;
$isBatch = isset($options['isBatch']) ? $options['isBatch'] : false;
2014-03-08 16:22:51 +01:00
if (is_array($id)) {
if (isset($id['id'])) {
2013-03-12 09:15:40 +01:00
$id = $id['id'];
2014-03-08 16:22:51 +01:00
} elseif (isset($id['uri'])) {
2013-10-03 18:09:19 +02:00
$id = $this->getIdFromUri($id['uri']);
2014-03-08 16:22:51 +01:00
if (is_null($id)) {
2013-10-03 18:09:19 +02:00
\OCP\Util::writeLog('contacts', __METHOD__ . ' Couldn\'t find contact', \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
2013-03-12 09:15:40 +01:00
} else {
throw new \Exception(
2013-03-12 09:15:40 +01:00
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
);
}
}
2014-03-08 16:22:51 +01:00
if (!$isBatch) {
2013-09-16 02:24:08 +02:00
\OCP\Util::emitHook('OCA\Contacts', 'pre_deleteContact',
array('id' => $id)
);
}
2013-10-03 18:09:19 +02:00
2014-03-08 16:22:51 +01:00
if ($noCollection) {
2013-10-03 18:09:19 +02:00
$me = $this->getContact(null, $id, $options);
2014-03-14 20:57:26 +01:00
$addressBookId = $me['parent'];
2013-10-03 18:09:19 +02:00
}
try {
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$result = $this->getPreparedQuery('deletecontact')
->execute(array($id, $addressBookId));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
. $id, \OCP\Util::DEBUG);
return false;
}
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
$this->setModifiedAddressBook($addressBookId);
return true;
}
2013-03-15 17:02:33 +01:00
/**
* @brief Get the last modification time for a contact.
*
* Must return a UNIX time stamp or null if the backend
* doesn't support it.
*
2014-03-14 20:57:26 +01:00
* @param string $addressBookId
2013-03-15 17:02:33 +01:00
* @param mixed $id
* @returns int | null
*/
2014-03-14 20:57:26 +01:00
public function lastModifiedContact($addressBookId, $id) {
2014-03-08 16:22:51 +01:00
2014-03-14 20:57:26 +01:00
$contact = $this->getContact($addressBookId, $id);
2013-03-16 15:59:23 +01:00
return ($contact ? $contact['lastmodified'] : null);
2014-03-08 16:22:51 +01:00
2013-03-15 17:02:33 +01:00
}
2013-10-03 18:09:19 +02:00
/**
* @brief Get the contact id from the uri.
*
* @param mixed $id
* @returns int | null
*/
public function getIdFromUri($uri) {
2014-03-08 16:22:51 +01:00
2014-03-18 23:51:07 +01:00
$stmt = $this->getPreparedQuery('contactidfromuri');
2013-10-03 18:09:19 +02:00
$result = $stmt->execute(array($uri));
2014-03-08 16:22:51 +01:00
2013-10-03 18:09:19 +02:00
if (\OCP\DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return null;
}
2014-03-08 16:22:51 +01:00
$one = $result->fetchOne();
2014-03-08 16:22:51 +01:00
if (!$one) {
2013-10-03 18:09:19 +02:00
\OCP\Util::writeLog('contacts', __METHOD__.', Not found, uri: '. $uri, \OCP\Util::DEBUG);
return null;
}
return $one;
2013-10-03 18:09:19 +02:00
}
2014-04-24 13:39:29 +02:00
/**
* Create a unique URI based on the display name.
*
* @param string $displayName
* @return string
*/
private function createAddressBookURI($displayName) {
2014-03-08 16:22:51 +01:00
2014-04-24 13:39:29 +02:00
$name = str_replace(' ', '_', strtolower($displayName));
2014-03-08 16:22:51 +01:00
try {
2014-03-18 23:51:07 +01:00
$stmt = $this->getPreparedQuery('addressbookuris');
2014-04-24 13:39:29 +02:00
$result = $stmt->execute(array($this->userid));
2014-03-08 16:22:51 +01:00
2013-09-16 02:24:08 +02:00
if (\OCP\DB::isError($result)) {
2014-03-18 23:51:07 +01:00
\OCP\Util::writeLog('contacts',
__METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result),
\OCP\Util::ERROR
);
return $name;
}
2014-03-08 16:22:51 +01:00
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__ . ' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
return $name;
}
$uris = array();
2014-03-08 16:22:51 +01:00
while ($row = $result->fetchRow()) {
$uris[] = $row['uri'];
}
$newname = $name;
2014-03-08 16:22:51 +01:00
$i = 1
;
while (in_array($newname, $uris)) {
$newname = $name.$i;
$i = $i + 1;
}
return $newname;
}
2013-11-28 14:15:17 +01:00
/**
* @brief Checks if a contact with the same URI already exist in the address book.
* @param string $addressBookId Address book ID.
* @param string $uri
* @returns string Unique URI
*/
protected function uniqueURI($addressBookId, $uri) {
2014-03-18 23:51:07 +01:00
$stmt = $this->getPreparedQuery('counturi');
2013-11-28 14:15:17 +01:00
$result = $stmt->execute(array($addressBookId, $uri));
$result = $result->fetchRow();
2013-11-28 14:15:17 +01:00
2014-03-08 16:22:51 +01:00
if (is_array($result) && count($result) > 0 && $result['count'] > 0) {
while (true) {
2013-11-28 14:15:17 +01:00
$uri = Properties::generateUID() . '.vcf';
$result = $stmt->execute(array($addressBookId, $uri));
2014-03-08 16:22:51 +01:00
if (is_array($result) && count($result) > 0 && $result['count'] > 0) {
2013-11-28 14:15:17 +01:00
continue;
} else {
return $uri;
}
2014-03-08 16:22:51 +01:00
2013-11-28 14:15:17 +01:00
}
}
return $uri;
2013-11-28 14:15:17 +01:00
}
2014-03-18 23:51:07 +01:00
2014-03-19 01:50:10 +01:00
/**
* Collect (nearly) all queries in one place
*
* @param string $identifier
* @param array $options Can be used for e.g. offset/limit
* @throws \Exception If $identifier isn't known
* @return \OC_DB_StatementWrapper
*/
2014-03-18 23:51:07 +01:00
protected function getPreparedQuery($identifier, array $options = array()) {
if (isset(self::$preparedQueries[$identifier])) {
return self::$preparedQueries[$identifier];
}
2014-03-19 01:50:10 +01:00
$args = array();
2014-03-18 23:51:07 +01:00
switch ($identifier) {
case 'getaddressbooksforuser':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `displayname`, `description`, `ctag`'
2014-03-18 23:51:07 +01:00
. ' AS `lastmodified`, `userid` AS `owner`, `uri` FROM `'
. $this->addressBooksTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `userid` = ? ORDER BY `displayname`';
2014-03-18 23:51:07 +01:00
break;
case 'getaddressbook':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `displayname`, `description`, '
2014-03-18 23:51:07 +01:00
. '`userid` AS `owner`, `ctag` AS `lastmodified`, `uri` FROM `'
. $this->addressBooksTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `id` = ? AND `userid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'createaddressbook':
2014-03-19 01:50:10 +01:00
$args[] = 'INSERT INTO `'
2014-03-18 23:51:07 +01:00
. $this->addressBooksTableName . '` '
. '(`userid`,`displayname`,`uri`,`description`,`ctag`) '
2014-03-19 01:50:10 +01:00
. 'VALUES(?,?,?,?,?)';
2014-03-18 23:51:07 +01:00
break;
case 'deleteaddressbookcontacts':
2014-03-19 01:50:10 +01:00
$args[] = 'DELETE FROM `' . $this->cardsTableName
. '` WHERE `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'deleteaddressbook':
2014-03-19 01:50:10 +01:00
$args[] = 'DELETE FROM `'
. $this->addressBooksTableName . '` WHERE `id` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'touchaddressbook':
2014-03-19 01:50:10 +01:00
$args[] = 'UPDATE `' . $this->addressBooksTableName
. '` SET `ctag` = ? + 1 WHERE `id` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'counturi':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT COUNT(*) AS `count` FROM `'
2014-03-18 23:51:07 +01:00
. $this->cardsTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `addressbookid` = ? AND `uri` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'addressbookuris':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `uri` FROM `'
. $this->addressBooksTableName . '` WHERE `userid` = ? ';
2014-03-18 23:51:07 +01:00
break;
case 'contactidfromuri':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id` FROM `'
2014-03-18 23:51:07 +01:00
. $this->cardsTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `uri` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'deletecontact':
2014-03-19 01:50:10 +01:00
$args[] = 'DELETE FROM `'
2014-03-18 23:51:07 +01:00
. $this->cardsTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `id` = ? AND `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'updatecontact':
2014-03-19 01:50:10 +01:00
$args[] = 'UPDATE `' . $this->cardsTableName
2014-03-18 23:51:07 +01:00
. '` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ?'
2014-03-19 01:50:10 +01:00
. ' WHERE `id` = ? AND `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'createcontact':
2014-03-19 01:50:10 +01:00
$args[] = 'INSERT INTO `'
2014-03-18 23:51:07 +01:00
. $this->cardsTableName
. '` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) '
2014-03-19 01:50:10 +01:00
. ' VALUES(?,?,?,?,?)';
2014-03-18 23:51:07 +01:00
break;
2014-03-19 01:59:09 +01:00
case 'getcontactbyid':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
. $this->cardsTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `id` = ? AND `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'getcontactbyuri':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
. $this->cardsTableName
2014-03-19 01:50:10 +01:00
. '` WHERE `uri` = ? AND `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'getcontactbyidnocollection':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
2014-03-19 01:50:10 +01:00
. $this->cardsTableName . '` WHERE `id` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'getcontactbyurinocollection':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
2014-03-19 01:50:10 +01:00
. $this->cardsTableName . '` WHERE `uri` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'getcontactids':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id` FROM `'
. $this->cardsTableName . '` WHERE `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
case 'getcontacts':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `carddata`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
2014-03-19 01:50:10 +01:00
. $this->cardsTableName . '` WHERE `addressbookid` = ?';
$args[] = isset($options['limit']) ? $options['limit'] : null;
$args[] = isset($options['offset']) ? $options['offset'] : null;
2014-03-18 23:51:07 +01:00
break;
case 'getcontactsomitdata':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT `id`, `uri`, `lastmodified`, '
2014-03-18 23:51:07 +01:00
. '`addressbookid` AS `parent`, `fullname` AS `displayname` FROM `'
2014-03-19 01:50:10 +01:00
. $this->cardsTableName . '` WHERE `addressbookid` = ?';
$args[] = isset($options['limit']) ? $options['limit'] : null;
$args[] = isset($options['offset']) ? $options['offset'] : null;
2014-03-18 23:51:07 +01:00
break;
case 'numcontacts':
2014-03-19 01:50:10 +01:00
$args[] = 'SELECT COUNT(*) AS `count` FROM `'
. $this->cardsTableName . '` WHERE `addressbookid` = ?';
2014-03-18 23:51:07 +01:00
break;
default:
throw new \Exception('Unknown query identifier: ' . $identifier);
}
2014-03-19 01:50:10 +01:00
self::$preparedQueries[$identifier] = call_user_func_array('\OCP\DB::prepare', $args);
2014-03-18 23:51:07 +01:00
return self::$preparedQueries[$identifier];
}
public function getSearchProvider($addressbook) {
return new \OCA\Contacts\AddressbookProvider($addressbook);
}
}