1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2024-12-01 13:24:10 +01:00

Contacts: More updates on backends

This commit is contained in:
Thomas Tanghus 2013-03-25 17:10:21 +01:00
parent da0b072e88
commit 3eb5303c3e
9 changed files with 265 additions and 83 deletions

View File

@ -1,46 +0,0 @@
<?php
/**
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
* This file is licensed under the Affero General Public License version 3 or
* later.
* See the COPYING-README file.
*/
OCP\JSON::checkLoggedIn();
OCP\JSON::checkAppEnabled('contacts');
$catmgr = OCA\Contacts\App::getVCategories();
$categories = $catmgr->categories(OC_VCategories::FORMAT_MAP);
foreach($categories as &$category) {
$ids = array();
$contacts = $catmgr->itemsForCategory(
$category['name'],
array(
'tablename' => '*PREFIX*contacts_cards',
'fields' => array('id',),
));
foreach($contacts as $contact) {
$ids[] = $contact['id'];
}
$category['contacts'] = $ids;
}
$favorites = $catmgr->getFavorites();
OCP\JSON::success(array(
'data' => array(
'categories' => $categories,
'favorites' => $favorites,
'shared' => OCP\Share::getItemsSharedWith('addressbook', OCA\Contacts\Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS),
'lastgroup' => OCP\Config::getUserValue(
OCP\User::getUser(),
'contacts',
'lastgroup', 'all'),
'sortorder' => OCP\Config::getUserValue(
OCP\User::getUser(),
'contacts',
'groupsort', ''),
)
)
);

View File

@ -43,7 +43,9 @@ if(!is_null($bookid)) {
}
} elseif(!is_null($contactid)) {
try {
$data = OCA\Contacts\VCard::find($contactid);
$app = new OCA\Contacts\App();
$contact = $app->getContact($_GET['backend'], $_GET['parent'], $_GET['contactid']);
$data = $contact->serialize();
} catch(Exception $e) {
OCP\JSON::error(
array(
@ -56,8 +58,8 @@ if(!is_null($bookid)) {
}
header('Content-Type: text/vcard');
header('Content-Disposition: inline; filename='
. str_replace(' ', '_', $data['fullname']) . '.vcf');
echo $data['carddata'];
. str_replace(' ', '_', $contact->FN) . '.vcf');
echo $data;
} elseif(!is_null($selectedids)) {
$selectedids = explode(',', $selectedids);
$l10n = \OC_L10N::get('contacts');

View File

@ -104,6 +104,13 @@ abstract class PIMObjectAbstract implements IPIMObject {
return $this->permissions;
}
/**
* @return AbstractBackend
*/
function getBackend() {
return $this->backend;
}
/**
* @param integer $permission
* @return boolean

View File

@ -76,6 +76,7 @@ class Addressbook extends PIMCollectionAbstract {
public function getMetaData() {
$metadata = $this->addressBookInfo;
$metadata['lastmodified'] = $this->lastModified();
$metadata['backend'] = $this->getBackend()->name;
return $metadata;
}
@ -107,6 +108,10 @@ class Addressbook extends PIMCollectionAbstract {
return $this->addressBookInfo['permissions'];
}
function getBackend() {
return $this->backend;
}
/**
* Returns a specific child node, referenced by its id
*
@ -164,8 +169,8 @@ class Addressbook extends PIMCollectionAbstract {
* @param array|VObject\VCard $data
* @return int|bool
*/
public function addChild($data) {
if($data instanceof VObject\VCard || is_array($data)) {
public function addChild($data = null) {
//if($data instanceof VObject\VCard || is_array($data)) {
$contact = new Contact($this, $this->backend, $data);
if($contact->save() === false) {
return false;
@ -173,12 +178,12 @@ class Addressbook extends PIMCollectionAbstract {
$id = $contact->getId();
$this->objects[$id] = $contact;
return $id;
} else {
/*} else {
throw new Exception(
__METHOD__
. ' This method accepts only an array or an instance of OCA\\Contacts\\VCard'
);
}
}*/
}
/**

View File

@ -37,7 +37,7 @@ class App {
protected static $addressBooks = array();
/**
* If backends are added to this map, they will be automatically mapped
* to their respective classes, if constructed with the 'createBackend' method.
* to their respective classes, if constructed with the 'getBackend' method.
*
* @var array
*/
@ -61,16 +61,15 @@ class App {
}
/**
* Creates the new backend by name, but in addition will also see if
* there's a class mapped to the property name.
* Gets backend by name.
*
* @param string $name
* @return \Backend\AbstractBackend
*/
static public function createBackend($name) {
static public function getBackend($name, $user = null) {
$name = $name ? $name : 'database';
if (isset(self::$backendClasses[$name])) {
return new self::$backendClasses[$name]();
return new self::$backendClasses[$name]($user);
} else {
throw new \Exception('No backend for: ' . $name);
}
@ -88,7 +87,7 @@ class App {
public function getAddressBooksForUser() {
if(!self::$addressBooks) {
foreach(array_keys(self::$backendClasses) as $backendName) {
$backend = self::createBackend($backendName);
$backend = self::getBackend($backendName, $this->user);
$addressBooks = $backend->getAddressBooksForUser();
foreach($addressBooks as $addressBook) {
$addressBook['backend'] = $backendName;
@ -115,7 +114,7 @@ class App {
}
}
// TODO: Check for return values
$backend = self::createBackend($backendName);
$backend = self::getBackend($backendName, $this->user);
$info = $backend->getAddressBook($addressbookid);
// FIXME: Backend name should be set by the backend.
$info['backend'] = $backendName;

View File

@ -353,15 +353,21 @@ class Database extends AbstractBackend {
* 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.
* 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.
*
* @param string $addressbookid
* @param mixed $id Contact ID
* @return array|false
*/
public function getContact($addressbookid, $id) {
public function getContact($addressbookid, $id, $noCollection = false) {
//\OCP\Util::writeLog('contacts', __METHOD__.' identifier: '
// . print_r($id, true), \OCP\Util::DEBUG);
$ids = array($id);
$where_query = '`id` = ?';
if(is_array($id)) {
$where_query = '';
@ -376,11 +382,17 @@ class Database extends AbstractBackend {
);
}
}
if(!$noCollection) {
$where_query .= ' AND `addressbookid` = ?';
$ids[] = $addressbookid;
}
try {
$query = 'SELECT `id`, `carddata`, `lastmodified`, `fullname` AS `displayname` FROM `'
. $this->cardsTableName . '` WHERE ' . $where_query;
$stmt = \OCP\DB::prepare($query);
$result = $stmt->execute(array($id));
$result = $stmt->execute($ids);
if (\OC_DB::isError($result)) {
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;
@ -485,7 +497,7 @@ class Database extends AbstractBackend {
* @see getContact
* @return bool
*/
public function updateContact($addressbookid, $id, $contact) {
public function updateContact($addressbookid, $id, $contact, $noCollection = false) {
if(!$contact instanceof Contact) {
try {
$contact = Reader::read($contact);
@ -512,17 +524,25 @@ class Database extends AbstractBackend {
} else {
$qname = 'createcontactbyid';
}
$now = new \DateTime;
$contact->REV = $now->format(\DateTime::W3C);
$data = $contact->serialize();
$updates = array($contact->FN, $data, time(), $id);
if(!$noCollection) {
$where_query .= ' AND `addressbookid` = ?';
$updates[] = $addressbookid;
}
$query = 'UPDATE `' . $this->cardsTableName
. '` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE ' . $where_query;
if(!isset(self::$preparedQueries[$qname])) {
self::$preparedQueries[$qname] = \OCP\DB::prepare($query);
}
try {
$result = self::$preparedQueries[$qname]->execute(array($contact->FN, $data, time(), $id));
$result = self::$preparedQueries[$qname]->execute($updates);
if (\OC_DB::isError($result)) {
\OCP\Util::writeLog('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
return false;

View File

@ -54,11 +54,14 @@ class Contact extends VObject\VCard implements IPIMObject {
//\OCP\Util::writeLog('contacts', __METHOD__, \OCP\Util::DEBUG);
$this->props['parent'] = $parent;
$this->props['backend'] = $backend;
$this->props['retrieved'] = false;
$this->props['saved'] = false;
if(!is_null($data)) {
if($data instanceof VObject\VCard) {
foreach($obj->children as $child) {
$this->add($child);
$this->setRetrieved(true);
}
} elseif(is_array($data)) {
foreach($data as $key => $value) {
@ -83,15 +86,15 @@ class Contact extends VObject\VCard implements IPIMObject {
case 'displayname':
case 'fullname':
$this->props['displayname'] = $value;
//$this->FN = $value;
$this->FN = $value;
break;
}
}
} else {
}/* else {
throw new Exception(
__METHOD__ . ' 3rd argument must either be an array or a subclass of \VObject\VCard'
);
}
}*/
}
}
@ -112,6 +115,7 @@ class Contact extends VObject\VCard implements IPIMObject {
'lastmodified' => $this->lastModified(),
'owner' => $this->getOwner(),
'parent' => $this->getParent()->getId(),
'backend' => $this->getBackend()->name,
);
}
@ -215,21 +219,34 @@ class Contact extends VObject\VCard implements IPIMObject {
*
* @return bool
*/
public function save() {
public function save($force = false) {
if($this->isSaved() && !$force) {
\OCP\Util::writeLog('contacts', __METHOD__.' Already saved: ' . print_r($this->props, true), \OCP\Util::DEBUG);
return true;
}
if(isset($this->FN)) {
$this->props['displayname'] = (string)$this->FN;
}
if($this->getId()) {
return $this->props['backend']->updateContact(
$this->getParent()->getId(),
$this->getId(),
$this->serialize()
);
if($this->props['backend']
->updateContact(
$this->getParent()->getId(),
$this->getId(),
$this->serialize()
)
) {
$this->props['lastmodified'] = time();
$this->setSaved(true);
return true;
} else {
return false;
}
} else {
//print(__METHOD__.' ' . print_r($this->getParent(), true));
$this->props['id'] = $this->props['backend']->createContact(
$this->getParent()->getId(), $this
);
$this->setSaved(true);
return $this->getId() !== false;
}
}
@ -241,7 +258,7 @@ class Contact extends VObject\VCard implements IPIMObject {
public function retrieve() {
//error_log(__METHOD__);
//\OCP\Util::writeLog('contacts', __METHOD__.' ' . print_r($this->props, true), \OCP\Util::DEBUG);
if($this->children) {
if($this->isRetrieved()) {
//\OCP\Util::writeLog('contacts', __METHOD__. ' children', \OCP\Util::DEBUG);
return true;
} else {
@ -255,12 +272,13 @@ class Contact extends VObject\VCard implements IPIMObject {
= strtr($child->value, array('\,' => ',', '\;' => ';', '\\\\' => '\\'));
}
}
$this->setRetrieved(true);
//$this->children = $this->props['vcard']->children();
unset($this->props['vcard']);
return true;
} elseif(!isset($this->props['carddata'])) {
$result = $this->props['backend']->getContact(
$this->parent->getId(),
$this->getParent()->getId(),
$this->id
);
if($result) {
@ -269,6 +287,7 @@ class Contact extends VObject\VCard implements IPIMObject {
foreach($result['vcard']->children() as $child) {
$this->add($child);
}
$this->setRetrieved(true);
return true;
} elseif(isset($result['carddata'])) {
// Save internal values
@ -294,13 +313,14 @@ class Contact extends VObject\VCard implements IPIMObject {
foreach($obj->children as $child) {
$this->add($child);
}
$this->setRetrieved(true);
} else {
\OCP\Util::writeLog('contacts', __METHOD__.' Error reading: ' . print_r($data, true), \OCP\Util::DEBUG);
return false;
}
} catch (\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__ .
' Error parsing carddata: ' . $e->getMessage(),
' Error parsing carddata for: ' . $this->getId() . ' ' . $e->getMessage(),
\OCP\Util::ERROR);
return false;
}
@ -309,20 +329,161 @@ class Contact extends VObject\VCard implements IPIMObject {
}
/**
* Delete a property by the checksum of its serialized value
* Get a property by the checksum of its serialized value
*
* @param string $checksum An 8 char m5d checksum.
* @return bool
* @return \Sabre\VObject\Property Property by reference
* @throws An exception with error code 404 if the property is not found.
*/
public function unsetPropertyByChecksum($checksum) {
public function getPropertyByChecksum($checksum) {
$this->retrieve();
foreach($this->children as $i => $property) {
foreach($this->children as $i => &$property) {
if(substr(md5($property->serialize()), 0, 8) == $checksum ) {
unset($this->children[$i]);
return true;
return $property;
}
}
throw new Exception('Property not found', 404);
}
/**
* Delete a property by the checksum of its serialized value
* It is up to the caller to call ->save()
*
* @param string $checksum An 8 char m5d checksum.
* @throws @see getPropertyByChecksum
*/
public function unsetPropertyByChecksum($checksum) {
$property = $this->getPropertyByChecksum($checksum);
unset($property);
}
/**
* Set a property by the checksum of its serialized value
* It is up to the caller to call ->save()
*
* @param string $checksum An 8 char m5d checksum.
* @param string $name Property name
* @param mixed $value
* @param array $parameters
* @throws @see getPropertyByChecksum
* @return string new checksum
*/
public function setPropertyByChecksum($checksum, $name, $value, $parameters=array()) {
$property = $this->getPropertyByChecksum($checksum);
switch($name) {
case 'EMAIL':
$value = strtolower($value);
$property->setValue($value);
break;
case 'ADR':
if(is_array($value)) {
$property->setParts($value);
} else {
debug('Saving N ' . $value);
$property->setValue($value);
}
break;
case 'IMPP':
if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) {
bailOut(App::$l10n->t('Missing IM parameter.'));
}
$impp = Utils\Properties::getIMOptions($parameters['X-SERVICE-TYPE']);
if(is_null($impp)) {
bailOut(App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE']));
}
$value = $impp['protocol'] . ':' . $value;
$property->setValue($value);
break;
default:
$property->setValue($value);
break;
}
$this->setParameters($property, $parameters);
return substr(md5($property->serialize()), 0, 8);
}
/**
* Set a property by the property name.
* It is up to the caller to call ->save()
*
* @param string $name Property name
* @param mixed $value
* @param array $parameters
* @return bool
*/
public function setPropertyByName($name, $value, $parameters=array()) {
// TODO: parameters are ignored for now.
switch($name) {
case 'BDAY':
try {
$date = New \DateTime($value);
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts',
__METHOD__.' DateTime exception: ' . $e->getMessage(),
\OCP\Util::ERROR
);
return false;
}
$value = $date->format('Y-m-d');
$this->BDAY = $value;
$this->BDAY->VALUE = 'DATE';
break;
case 'N':
$property = $this->select($name);
if(count($property) === 0) {
$property = VObject\Property::create($name);
$this->add($property);
} else {
// Actually no idea why this works
$property = array_shift($property);
}
if(is_array($value)) {
$property->setParts($value);
} else {
$this->N = $value;
}
break;
default:
$this->{$name} = $value;
break;
}
return true;
}
protected function setParameters($property, $parameters, $reset = false) {
if(!$parameters) {
return;
}
if($reset) {
$property->parameters = array();
}
debug('Setting parameters: ' . print_r($parameters, true));
foreach($parameters as $key => $parameter) {
debug('Adding parameter: ' . $key);
if(is_array($parameter)) {
foreach($parameter as $val) {
if(is_array($val)) {
foreach($val as $val2) {
if(trim($key) && trim($val2)) {
debug('Adding parameter: '.$key.'=>'.print_r($val2, true));
$property->add($key, strip_tags($val2));
}
}
} else {
if(trim($key) && trim($val)) {
debug('Adding parameter: '.$key.'=>'.print_r($val, true));
$property->add($key, strip_tags($val));
}
}
}
} else {
if(trim($key) && trim($parameter)) {
debug('Adding parameter: '.$key.'=>'.print_r($parameter, true));
$property->add($key, strip_tags($parameter));
}
}
}
return false;
}
public function lastModified() {
@ -368,4 +529,31 @@ class Contact extends VObject\VCard implements IPIMObject {
\OCP\Util::writeLog('contacts', 'Caching ' . $key, \OCP\Util::DEBUG);
return \OC_Cache::get(self::THUMBNAIL_PREFIX . $key);
}
public function __set($key, $value) {
parent::__set($key, $value);
$this->setSaved(false);
}
public function __unset($id) {
parent::__unset($key, $value);
$this->setSaved(false);
}
protected function setRetrieved($state) {
$this->props['retrieved'] = $state;
}
protected function isRetrieved() {
return $this->props['retrieved'];
}
protected function setSaved($state) {
$this->props['saved'] = $state;
}
protected function isSaved() {
return $this->props['saved'];
}
}

View File

@ -98,6 +98,11 @@ interface IPIMObject {
*/
function getPermissions();
/**
* @return AbstractBackend
*/
function getBackend();
/**
* @param integer $permission
* @return boolean

View File

@ -30,6 +30,8 @@ use OCA\Contacts\Contact;
* This class serializes properties, components an
* arrays of components into a format suitable for
* passing to a JSON response.
* TODO: Return jCard (almost) compliant data, but still omitting unneeded data.
* http://tools.ietf.org/html/draft-kewisch-vcard-in-json-01
*/
class JSONSerializer {