2013-03-10 12:34:41 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* ownCloud - Database backend for Contacts
|
|
|
|
*
|
|
|
|
* @author Thomas Tanghus
|
|
|
|
* @copyright 2013 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;
|
|
|
|
|
|
|
|
use OCA\Contacts\Contact;
|
2013-03-20 11:24:05 +01:00
|
|
|
use OCA\Contacts\VObject\VCard;
|
2013-03-12 09:15:40 +01:00
|
|
|
use Sabre\VObject\Reader;
|
2013-03-10 12:34:41 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Subclass this class for Cantacts backends
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Database extends AbstractBackend {
|
|
|
|
|
2013-04-07 22:33:40 +02:00
|
|
|
public $name = 'local';
|
2013-03-13 05:19:38 +01:00
|
|
|
static private $preparedQueries = array();
|
2013-03-12 09:15:40 +01:00
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
/**
|
|
|
|
* Sets up the backend
|
|
|
|
*
|
|
|
|
* @param string $addressBooksTableName
|
|
|
|
* @param string $cardsTableName
|
|
|
|
*/
|
|
|
|
public function __construct(
|
|
|
|
$userid = null,
|
|
|
|
$addressBooksTableName = '*PREFIX*contacts_addressbooks',
|
2013-03-29 05:00:03 +01:00
|
|
|
$cardsTableName = '*PREFIX*contacts_cards',
|
|
|
|
$indexTableName = '*PREFIX*contacts_cards_properties'
|
2013-03-10 12:34:41 +01:00
|
|
|
) {
|
|
|
|
$this->userid = $userid ? $userid : \OCP\User::getUser();
|
|
|
|
$this->addressBooksTableName = $addressBooksTableName;
|
|
|
|
$this->cardsTableName = $cardsTableName;
|
2013-03-29 05:00:03 +01:00
|
|
|
$this->indexTableName = $indexTableName;
|
2013-03-10 12:34:41 +01:00
|
|
|
$this->addressbooks = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the list of addressbooks for a specific user.
|
|
|
|
*
|
|
|
|
* TODO: Create default if none exists.
|
|
|
|
*
|
|
|
|
* @param string $principaluri
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getAddressBooksForUser($userid = null) {
|
|
|
|
$userid = $userid ? $userid : $this->userid;
|
|
|
|
|
|
|
|
try {
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['addressbooksforuser'])) {
|
2013-03-30 04:22:30 +01:00
|
|
|
$sql = 'SELECT `id`, `displayname`, `description`, `ctag` AS `lastmodified`, `userid` AS `owner`, `uri` FROM `'
|
2013-03-13 05:19:38 +01:00
|
|
|
. $this->addressBooksTableName
|
2013-03-22 15:03:50 +01:00
|
|
|
. '` WHERE `userid` = ? ORDER BY `displayname`';
|
|
|
|
self::$preparedQueries['addressbooksforuser'] = \OCP\DB::prepare($sql);
|
2013-03-13 05:19:38 +01:00
|
|
|
}
|
|
|
|
$result = self::$preparedQueries['addressbooksforuser']->execute(array($userid));
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OCP\Util::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return $this->addressbooks;
|
|
|
|
}
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return $this->addressbooks;
|
|
|
|
}
|
|
|
|
|
|
|
|
while( $row = $result->fetchRow()) {
|
|
|
|
$row['permissions'] = \OCP\PERMISSION_ALL;
|
2013-03-17 00:38:00 +01:00
|
|
|
$this->addressbooks[$row['id']] = $row;
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
return $this->addressbooks;
|
|
|
|
}
|
|
|
|
|
2013-03-12 09:15:40 +01:00
|
|
|
public function getAddressBook($addressbookid) {
|
|
|
|
//\OCP\Util::writeLog('contacts', __METHOD__.' id: '
|
|
|
|
// . $addressbookid, \OCP\Util::DEBUG);
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
2013-03-20 11:24:05 +01:00
|
|
|
//print(__METHOD__ . ' ' . __LINE__ .' addressBookInfo: ' . print_r($this->addressbooks[$addressbookid], true));
|
2013-03-17 00:38:00 +01:00
|
|
|
return $this->addressbooks[$addressbookid];
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
2013-03-20 11:24:05 +01:00
|
|
|
// Hmm, not found. Lets query the db.
|
2013-03-12 09:15:40 +01:00
|
|
|
try {
|
2013-04-07 21:42:27 +02:00
|
|
|
$query = 'SELECT `id`, `displayname`, `description`, `userid` AS `owner`, `ctag` AS `lastmodified`, `uri` FROM `'
|
2013-03-20 11:24:05 +01:00
|
|
|
. $this->addressBooksTableName
|
2013-03-12 09:15:40 +01:00
|
|
|
. '` WHERE `id` = ?';
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['getaddressbook'])) {
|
|
|
|
self::$preparedQueries['getaddressbook'] = \OCP\DB::prepare($query);
|
|
|
|
}
|
|
|
|
$result = self::$preparedQueries['getaddressbook']->execute(array($addressbookid));
|
2013-03-12 09:15:40 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OCP\Util::write('contacts', __METHOD__. 'DB error: '
|
|
|
|
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return array();
|
|
|
|
}
|
2013-03-20 11:24:05 +01:00
|
|
|
$row = $result->fetchRow();
|
|
|
|
$row['permissions'] = \OCP\PERMISSION_ALL;
|
2013-05-02 20:41:26 +02:00
|
|
|
$row['backend'] = $this->name;
|
2013-04-27 03:09:24 +02:00
|
|
|
$this->addressbooks[$addressbookid] = $row;
|
2013-03-20 11:24:05 +01:00
|
|
|
return $row;
|
2013-03-12 09:15:40 +01:00
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.' exception: '
|
|
|
|
. $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
return array();
|
|
|
|
}
|
|
|
|
|
|
|
|
public function hasAddressBook($addressbookid) {
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
|
|
|
return true;
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
2013-03-13 05:19:38 +01:00
|
|
|
return count($this->getAddressBook($addressbookid)) > 0;
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
/**
|
|
|
|
* Updates an addressbook's properties
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param array $changes
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function updateAddressBook($addressbookid, array $changes) {
|
|
|
|
if(count($changes) === 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = 'UPDATE `' . $this->addressBooksTableName . '` SET ';
|
|
|
|
|
|
|
|
$updates = array();
|
|
|
|
|
|
|
|
if(isset($changes['displayname'])) {
|
|
|
|
$query .= '`displayname` = ?, ';
|
|
|
|
$updates[] = $changes['displayname'];
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
|
|
|
$this->addressbooks[$addressbookid]['displayname'] = $changes['displayname'];
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(isset($changes['description'])) {
|
|
|
|
$query .= '`description` = ?, ';
|
|
|
|
$updates[] = $changes['description'];
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
|
|
|
$this->addressbooks[$addressbookid]['description'] = $changes['description'];
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
2013-04-27 02:20:12 +02:00
|
|
|
$query .= '`ctag` = ? + 1 WHERE `id` = ?';
|
|
|
|
$updates[] = time();
|
2013-03-10 12:34:41 +01:00
|
|
|
$updates[] = $addressbookid;
|
|
|
|
|
|
|
|
try {
|
|
|
|
$stmt = \OCP\DB::prepare($query);
|
|
|
|
$result = $stmt->execute($updates);
|
|
|
|
if (\OC_DB::isError($result)) {
|
2013-03-13 05:19:38 +01:00
|
|
|
\OC_Log::write('contacts',
|
|
|
|
__METHOD__. 'DB error: '
|
|
|
|
. \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
2013-03-10 12:34:41 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch(Exception $e) {
|
2013-03-13 05:19:38 +01:00
|
|
|
\OCP\Util::writeLog('contacts',
|
|
|
|
__METHOD__ . ', exception: '
|
|
|
|
. $e->getMessage(), \OCP\Util::ERROR);
|
2013-03-10 12:34:41 +01:00
|
|
|
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
|
|
|
|
* @return string|false The ID if the newly created AddressBook or false on error.
|
|
|
|
*/
|
|
|
|
public function createAddressBook(array $properties, $userid = null) {
|
|
|
|
$userid = $userid ? $userid : $this->userid;
|
2013-03-20 11:24:05 +01:00
|
|
|
if(count($properties) === 0 || !isset($properties['displayname'])) {
|
2013-03-10 12:34:41 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$query = 'INSERT INTO `' . $this->addressBooksTableName . '` '
|
|
|
|
. '(`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)';
|
|
|
|
|
|
|
|
$updates = array($userid, $properties['displayname']);
|
|
|
|
$updates[] = isset($properties['uri'])
|
|
|
|
? $properties['uri']
|
|
|
|
: $this->createAddressBookURI($properties['displayname']);
|
|
|
|
$updates[] = isset($properties['description']) ? $properties['description'] : '';
|
2013-03-17 00:38:00 +01:00
|
|
|
$ctag = time();
|
|
|
|
$updates[] = $ctag;
|
2013-03-10 12:34:41 +01:00
|
|
|
|
|
|
|
try {
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['createaddressbook'])) {
|
|
|
|
self::$preparedQueries['createaddressbook'] = \OCP\DB::prepare($query);
|
|
|
|
}
|
|
|
|
$result = self::$preparedQueries['createaddressbook']->execute($updates);
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch(Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
$newid = \OCP\DB::insertid($this->addressBooksTableName);
|
|
|
|
if($this->addressbooks) {
|
|
|
|
$updates['id'] = $newid;
|
|
|
|
$updates['ctag'] = $ctag;
|
|
|
|
$updates['lastmodified'] = $ctag;
|
|
|
|
$this->addressbooks[$addressbookid] = $updates;
|
|
|
|
}
|
|
|
|
return $newid;
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes an entire addressbook and all its contents
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function deleteAddressBook($addressbookid) {
|
2013-03-29 05:00:03 +01:00
|
|
|
\OC_Hook::emit('OCA\Contacts', 'pre_deleteAddressBook',
|
2013-03-10 12:34:41 +01:00
|
|
|
array('id' => $addressbookid)
|
|
|
|
);
|
|
|
|
|
2013-03-29 05:00:03 +01:00
|
|
|
// Clean up sharing
|
|
|
|
\OCP\Share::unshareAll('addressbook', $addressbookid);
|
|
|
|
|
|
|
|
// Get all contact ids for this address book
|
|
|
|
$ids = array();
|
|
|
|
$result = null;
|
|
|
|
$stmt = \OCP\DB::prepare('SELECT `id` FROM `' . $this->cardsTableName . '`'
|
|
|
|
. ' WHERE `addressbookid` = ?');
|
|
|
|
try {
|
|
|
|
$result = $stmt->execute(array($addressbookid));
|
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
|
|
|
|
. \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
2013-04-26 23:56:53 +02:00
|
|
|
return false;
|
2013-03-29 05:00:03 +01:00
|
|
|
}
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.
|
|
|
|
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
2013-04-26 23:56:53 +02:00
|
|
|
return false;
|
2013-03-29 05:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!is_null($result)) {
|
|
|
|
while($id = $result->fetchOne()) {
|
|
|
|
$ids[] = $id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Purge contact property indexes
|
2013-04-26 16:53:33 +02:00
|
|
|
if(count($ids)) {
|
|
|
|
$stmt = \OCP\DB::prepare('DELETE FROM `' . $this->indexTableName
|
|
|
|
.'` WHERE `contactid` IN ('.str_repeat('?,', count($ids)-1).'?)');
|
|
|
|
try {
|
|
|
|
$stmt->execute($ids);
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.
|
|
|
|
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Purge categories
|
|
|
|
$catctrl = new \OC_VCategories('contact');
|
|
|
|
$catctrl->purgeObjects($ids);
|
|
|
|
|
2013-03-29 05:00:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['deleteaddressbookcontacts'])) {
|
|
|
|
self::$preparedQueries['deleteaddressbookcontacts'] =
|
2013-03-29 05:00:03 +01:00
|
|
|
\OCP\DB::prepare('DELETE FROM `' . $this->cardsTableName
|
2013-03-13 05:19:38 +01:00
|
|
|
. '` WHERE `addressbookid` = ?');
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-13 05:19:38 +01:00
|
|
|
self::$preparedQueries['deleteaddressbookcontacts']
|
|
|
|
->execute(array($addressbookid));
|
2013-03-10 12:34:41 +01:00
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.
|
|
|
|
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['deleteaddressbook'])) {
|
|
|
|
self::$preparedQueries['deleteaddressbook'] =
|
|
|
|
\OCP\DB::prepare('DELETE FROM `'
|
|
|
|
. $this->addressBooksTableName . '` WHERE `id` = ?');
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-13 05:19:38 +01:00
|
|
|
self::$preparedQueries['deleteaddressbook']
|
|
|
|
->execute(array($addressbookid));
|
2013-03-10 12:34:41 +01:00
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.
|
|
|
|
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
|
|
|
unset($this->addressbooks[$addressbookid]);
|
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Updates ctag for addressbook
|
|
|
|
* @param integer $id
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2013-03-16 15:59:23 +01:00
|
|
|
public function touchAddressBook($id) {
|
2013-03-13 05:19:38 +01:00
|
|
|
$query = 'UPDATE `' . $this->addressBooksTableName
|
2013-04-27 02:20:12 +02:00
|
|
|
. '` SET `ctag` = ? + 1 WHERE `id` = ?';
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries['touchaddressbook'])) {
|
|
|
|
self::$preparedQueries['touchaddressbook'] = \OCP\DB::prepare($query);
|
|
|
|
}
|
2013-04-27 02:20:12 +02:00
|
|
|
$ctag = time();
|
|
|
|
self::$preparedQueries['touchaddressbook']->execute(array($ctag, $id));
|
2013-03-10 12:34:41 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-03-16 15:59:23 +01:00
|
|
|
public function lastModifiedAddressBook($addressbookid) {
|
2013-03-17 00:38:00 +01:00
|
|
|
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
|
|
|
return $this->addressbooks[$addressbookid]['lastmodified'];
|
|
|
|
}
|
2013-04-27 03:09:24 +02:00
|
|
|
$addressBook = $this->getAddressBook($addressbookid);
|
|
|
|
return $addressBook ? $addressBook['lastmodified'] : null;
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
2013-03-28 17:32:36 +01:00
|
|
|
/**
|
|
|
|
* Returns the number of contacts in a specific address book.
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param bool $omitdata Don't fetch the entire carddata or vcard.
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function numContacts($addressbookid) {
|
|
|
|
$query = 'SELECT COUNT(*) AS `count` FROM `' . $this->cardsTableName . '` WHERE '
|
|
|
|
. '`addressbookid` = ?';
|
|
|
|
|
|
|
|
if(!isset(self::$preparedQueries['count'])) {
|
|
|
|
self::$preparedQueries['count'] = \OCP\DB::prepare($query);
|
|
|
|
}
|
|
|
|
$result = self::$preparedQueries['count']->execute(array($addressbookid));
|
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (int)$result->fetchOne();
|
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
/**
|
|
|
|
* Returns all contacts for a specific addressbook id.
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param bool $omitdata Don't fetch the entire carddata or vcard.
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getContacts($addressbookid, $limit = null, $offset = null, $omitdata = false) {
|
2013-04-08 01:27:35 +02:00
|
|
|
//\OCP\Util::writeLog('contacts', __METHOD__.' addressbookid: ' . $addressbookid, \OCP\Util::DEBUG);
|
2013-03-10 12:34:41 +01:00
|
|
|
$cards = array();
|
|
|
|
try {
|
2013-03-16 15:59:23 +01:00
|
|
|
$qfields = $omitdata ? '`id`, `fullname` AS `displayname`' : '*';
|
2013-03-10 12:34:41 +01:00
|
|
|
$query = 'SELECT ' . $qfields . ' FROM `' . $this->cardsTableName
|
|
|
|
. '` WHERE `addressbookid` = ? ORDER BY `fullname`';
|
|
|
|
$stmt = \OCP\DB::prepare($query, $limit, $offset);
|
|
|
|
$result = $stmt->execute(array($addressbookid));
|
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return $cards;
|
|
|
|
}
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return $cards;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!is_null($result)) {
|
2013-03-17 00:38:00 +01:00
|
|
|
while($row = $result->fetchRow()) {
|
2013-03-10 12:34:41 +01:00
|
|
|
$row['permissions'] = \OCP\PERMISSION_ALL;
|
|
|
|
$cards[] = $row;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $cards;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-04-08 01:27:35 +02:00
|
|
|
* Returns a specific contact.
|
2013-03-10 12:34:41 +01:00
|
|
|
*
|
2013-03-12 09:15:40 +01:00
|
|
|
* The $id for Database and Shared backends can be an array containing
|
|
|
|
* either 'id' or 'uri' to be able to play seamlessly with the
|
|
|
|
* CardDAV backend.
|
2013-03-25 17:10:21 +01:00
|
|
|
* FIXME: $addressbookid isn't used in the query, so there's no access control.
|
|
|
|
* OTOH the groups backend - OC_VCategories - doesn't no about parent collections
|
|
|
|
* only object IDs. Hmm.
|
|
|
|
* I could make a hack and add an optional, not documented 'nostrict' argument
|
|
|
|
* so it doesn't look for addressbookid.
|
2013-03-10 12:34:41 +01:00
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param mixed $id Contact ID
|
2013-03-17 00:38:00 +01:00
|
|
|
* @return array|false
|
2013-03-10 12:34:41 +01:00
|
|
|
*/
|
2013-03-25 17:10:21 +01:00
|
|
|
public function getContact($addressbookid, $id, $noCollection = false) {
|
2013-04-08 01:27:35 +02:00
|
|
|
//\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $addressbookid . ' ' . $id['uri'], \OCP\Util::DEBUG);
|
2013-03-12 09:15:40 +01:00
|
|
|
|
|
|
|
$where_query = '`id` = ?';
|
|
|
|
if(is_array($id)) {
|
|
|
|
$where_query = '';
|
|
|
|
if(isset($id['id'])) {
|
|
|
|
$id = $id['id'];
|
|
|
|
} elseif(isset($id['uri'])) {
|
|
|
|
$where_query = '`uri` = ?';
|
|
|
|
$id = $id['uri'];
|
|
|
|
} else {
|
|
|
|
throw new \Exception(
|
|
|
|
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
|
|
|
|
);
|
2013-04-08 01:27:35 +02:00
|
|
|
return false;
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
|
|
|
}
|
2013-04-08 01:27:35 +02:00
|
|
|
$ids = array($id);
|
2013-03-25 17:10:21 +01:00
|
|
|
|
|
|
|
if(!$noCollection) {
|
|
|
|
$where_query .= ' AND `addressbookid` = ?';
|
|
|
|
$ids[] = $addressbookid;
|
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-17 00:38:00 +01:00
|
|
|
$query = 'SELECT `id`, `carddata`, `lastmodified`, `fullname` AS `displayname` FROM `'
|
|
|
|
. $this->cardsTableName . '` WHERE ' . $where_query;
|
|
|
|
$stmt = \OCP\DB::prepare($query);
|
2013-03-25 17:10:21 +01:00
|
|
|
$result = $stmt->execute($ids);
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
$row = $result->fetchRow();
|
|
|
|
$row['permissions'] = \OCP\PERMISSION_ALL;
|
|
|
|
return $row;
|
|
|
|
}
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
public function hasContact($addressbookid, $id) {
|
|
|
|
return $this->getContact($addressbookid, $id) !== false;
|
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
*
|
2013-03-10 12:34:41 +01:00
|
|
|
* @param string $addressbookid
|
2013-03-12 09:15:40 +01:00
|
|
|
* @param mixed $contact
|
2013-03-17 00:38:00 +01:00
|
|
|
* @return string|bool The identifier for the new contact or false on error.
|
2013-03-10 12:34:41 +01:00
|
|
|
*/
|
|
|
|
public function createContact($addressbookid, $contact, $uri = null) {
|
|
|
|
|
2013-03-12 09:15:40 +01:00
|
|
|
if(!$contact instanceof Contact) {
|
|
|
|
try {
|
|
|
|
$contact = Reader::read($contact);
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-20 11:24:05 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
$uri = is_null($uri) ? $contact->UID . '.vcf' : $uri;
|
2013-03-10 12:34:41 +01:00
|
|
|
$now = new \DateTime;
|
|
|
|
$contact->REV = $now->format(\DateTime::W3C);
|
|
|
|
|
2013-03-20 11:24:05 +01:00
|
|
|
$appinfo = \OCP\App::getAppInfo('contacts');
|
|
|
|
$appversion = \OCP\App::getAppVersion('contacts');
|
|
|
|
$prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
|
2013-03-29 16:42:33 +01:00
|
|
|
$contact->PRODID = $prodid;
|
2013-03-20 11:24:05 +01:00
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
$data = $contact->serialize();
|
2013-03-17 00:38:00 +01:00
|
|
|
if(!isset(self::$preparedQueries['createcontact'])) {
|
|
|
|
self::$preparedQueries['createcontact'] = \OCP\DB::prepare('INSERT INTO `'
|
2013-03-13 05:19:38 +01:00
|
|
|
. $this->cardsTableName
|
|
|
|
. '` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' );
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-17 00:38:00 +01:00
|
|
|
$result = self::$preparedQueries['createcontact']
|
2013-03-20 11:24:05 +01:00
|
|
|
->execute(
|
|
|
|
array(
|
|
|
|
$addressbookid,
|
|
|
|
(string)$contact->FN,
|
|
|
|
$contact->serialize(),
|
|
|
|
$uri,
|
|
|
|
time()
|
|
|
|
)
|
|
|
|
);
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$newid = \OCP\DB::insertid($this->cardsTableName);
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
$this->touchAddressBook($addressbookid);
|
2013-03-29 05:00:03 +01:00
|
|
|
\OC_Hook::emit('OCA\Contacts', 'post_createContact',
|
|
|
|
array('id' => $newid, 'parent' => $addressbookid, 'contact' => $contact)
|
2013-03-10 12:34:41 +01:00
|
|
|
);
|
2013-03-17 00:38:00 +01:00
|
|
|
return (string)$newid;
|
2013-03-10 12:34:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Updates a contact
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
2013-03-12 09:15:40 +01:00
|
|
|
* @param mixed $id Contact ID
|
|
|
|
* @param mixed $contact
|
|
|
|
* @see getContact
|
2013-03-10 12:34:41 +01:00
|
|
|
* @return bool
|
|
|
|
*/
|
2013-03-25 17:10:21 +01:00
|
|
|
public function updateContact($addressbookid, $id, $contact, $noCollection = false) {
|
2013-05-05 16:27:10 +02:00
|
|
|
$updateRevision = true;
|
2013-03-12 09:15:40 +01:00
|
|
|
if(!$contact instanceof Contact) {
|
|
|
|
try {
|
|
|
|
$contact = Reader::read($contact);
|
|
|
|
} catch(\Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$where_query = '`id` = ?';
|
|
|
|
if(is_array($id)) {
|
|
|
|
$where_query = '';
|
|
|
|
if(isset($id['id'])) {
|
|
|
|
$id = $id['id'];
|
2013-03-13 05:19:38 +01:00
|
|
|
$qname = 'createcontactbyid';
|
2013-03-12 09:15:40 +01:00
|
|
|
} elseif(isset($id['uri'])) {
|
2013-05-05 16:27:10 +02:00
|
|
|
$updateRevision = false;
|
2013-03-12 09:15:40 +01:00
|
|
|
$where_query = '`id` = ?';
|
|
|
|
$id = $id['uri'];
|
2013-03-13 05:19:38 +01:00
|
|
|
$qname = 'createcontactbyuri';
|
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.'
|
|
|
|
);
|
|
|
|
}
|
2013-03-13 05:19:38 +01:00
|
|
|
} else {
|
|
|
|
$qname = 'createcontactbyid';
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
2013-03-25 17:10:21 +01:00
|
|
|
|
2013-05-05 16:27:10 +02:00
|
|
|
if($updateRevision || !isset($contact->REV)) {
|
|
|
|
$now = new \DateTime;
|
|
|
|
$contact->REV = $now->format(\DateTime::W3C);
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
$data = $contact->serialize();
|
2013-03-25 17:10:21 +01:00
|
|
|
|
|
|
|
$updates = array($contact->FN, $data, time(), $id);
|
|
|
|
if(!$noCollection) {
|
|
|
|
$where_query .= ' AND `addressbookid` = ?';
|
|
|
|
$updates[] = $addressbookid;
|
|
|
|
}
|
|
|
|
|
2013-03-13 05:19:38 +01:00
|
|
|
$query = 'UPDATE `' . $this->cardsTableName
|
|
|
|
. '` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE ' . $where_query;
|
|
|
|
if(!isset(self::$preparedQueries[$qname])) {
|
|
|
|
self::$preparedQueries[$qname] = \OCP\DB::prepare($query);
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-25 17:10:21 +01:00
|
|
|
$result = self::$preparedQueries[$qname]->execute($updates);
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
|
2013-03-17 00:38:00 +01:00
|
|
|
$this->touchAddressBook($addressbookid);
|
2013-03-29 05:00:03 +01:00
|
|
|
\OC_Hook::emit('OCA\Contacts', 'post_updateContact',
|
|
|
|
array('id' => $id, 'parent' => $addressbookid, 'contact' => $contact)
|
2013-03-10 12:34:41 +01:00
|
|
|
);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Deletes a contact
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param string $id
|
2013-03-12 09:15:40 +01:00
|
|
|
* @see getContact
|
2013-03-10 12:34:41 +01:00
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function deleteContact($addressbookid, $id) {
|
2013-03-12 09:15:40 +01:00
|
|
|
$where_query = '`id` = ?';
|
|
|
|
if(is_array($id)) {
|
|
|
|
$where_query = '';
|
|
|
|
if(isset($id['id'])) {
|
|
|
|
$id = $id['id'];
|
2013-03-13 05:19:38 +01:00
|
|
|
$qname = 'deletecontactsbyid';
|
2013-03-12 09:15:40 +01:00
|
|
|
} elseif(isset($id['uri'])) {
|
|
|
|
$where_query = '`id` = ?';
|
|
|
|
$id = $id['uri'];
|
2013-03-13 05:19:38 +01:00
|
|
|
$qname = 'deletecontactsbyuri';
|
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.'
|
|
|
|
);
|
|
|
|
}
|
2013-03-13 05:19:38 +01:00
|
|
|
} else {
|
|
|
|
$qname = 'deletecontactsbyid';
|
2013-03-12 09:15:40 +01:00
|
|
|
}
|
2013-03-29 05:00:03 +01:00
|
|
|
\OC_Hook::emit('OCA\Contacts', 'pre_deleteContact',
|
2013-03-10 12:34:41 +01:00
|
|
|
array('id' => $id)
|
|
|
|
);
|
2013-03-13 05:19:38 +01:00
|
|
|
if(!isset(self::$preparedQueries[$qname])) {
|
|
|
|
self::$preparedQueries[$qname] = \OCP\DB::prepare('DELETE FROM `'
|
|
|
|
. $this->cardsTableName
|
|
|
|
. '` WHERE ' . $where_query . ' AND `addressbookid` = ?');
|
|
|
|
}
|
2013-03-10 12:34:41 +01:00
|
|
|
try {
|
2013-03-17 00:38:00 +01:00
|
|
|
$result = self::$preparedQueries[$qname]->execute(array($id, $addressbookid));
|
2013-03-10 12:34:41 +01:00
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: '
|
|
|
|
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
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.
|
|
|
|
*
|
|
|
|
* @param string $addressbookid
|
|
|
|
* @param mixed $id
|
|
|
|
* @returns int | null
|
|
|
|
*/
|
|
|
|
public function lastModifiedContact($addressbookid, $id) {
|
|
|
|
$contact = $this->getContact($addressbookid, $id);
|
2013-03-16 15:59:23 +01:00
|
|
|
return ($contact ? $contact['lastmodified'] : null);
|
2013-03-15 17:02:33 +01:00
|
|
|
}
|
|
|
|
|
2013-03-10 12:34:41 +01:00
|
|
|
private function createAddressBookURI($displayname, $userid = null) {
|
|
|
|
$userid = $userid ? $userid : \OCP\User::getUser();
|
|
|
|
$name = str_replace(' ', '_', strtolower($displayname));
|
|
|
|
try {
|
|
|
|
$stmt = \OCP\DB::prepare('SELECT `uri` FROM `' . $this->addressBooksTableName . '` WHERE `userid` = ? ');
|
|
|
|
$result = $stmt->execute(array($userid));
|
|
|
|
if (\OC_DB::isError($result)) {
|
|
|
|
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
} catch(Exception $e) {
|
|
|
|
\OCP\Util::writeLog('contacts', __METHOD__ . ' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
|
|
|
return $name;
|
|
|
|
}
|
|
|
|
$uris = array();
|
|
|
|
while($row = $result->fetchRow()) {
|
|
|
|
$uris[] = $row['uri'];
|
|
|
|
}
|
|
|
|
|
|
|
|
$newname = $name;
|
|
|
|
$i = 1;
|
2013-03-16 15:59:23 +01:00
|
|
|
while(in_array($newname, $uris)) {
|
2013-03-10 12:34:41 +01:00
|
|
|
$newname = $name.$i;
|
|
|
|
$i = $i + 1;
|
|
|
|
}
|
|
|
|
return $newname;
|
|
|
|
}
|
|
|
|
|
2013-03-13 05:19:38 +01:00
|
|
|
}
|