1
0
mirror of https://github.com/owncloudarchive/contacts.git synced 2025-01-29 18:52:18 +01:00

Contacts: Use Sabre\VObject

This commit is contained in:
Thomas Tanghus 2012-11-22 00:14:03 +01:00
parent 1542849a22
commit 3a25cc050e
10 changed files with 134 additions and 145 deletions

View File

@ -36,10 +36,10 @@ debug('Adding new contact to: ' . $aid);
$isnew = isset($_POST['isnew']) ? $_POST['isnew'] : false;
$vobject = Sabre\VObject\Component::create('VCARD');
debug('vobject: ', print_r($vobject->serialize(), true));
$vcard = new OC_VObject($vobject);
$vcard->setUID();
$vcard = Sabre\VObject\Component::create('VCARD');
$uid = substr(md5(rand().time()), 0, 10);
$vcard->add('UID', $uid);
debug('vobject: ', print_r($vcard->serialize(), true));
$id = null;
try {

View File

@ -75,16 +75,17 @@ $contacts = array();
// Our new array for the contacts sorted by addressbook
if($contacts_alphabet) {
foreach($contacts_alphabet as $contact) {
$vcard = OC_VObject::parse($contact['carddata']);
if(is_null($vcard)) {
try {
$vcard = Sabre\VObject\Reader::read($contact['carddata']);
$details = OCA\Contacts\VCard::structureContact($vcard);
$contacts[] = array(
'id' => $contact['id'],
'aid' => $contact['addressbookid'],
'data' => $details,
);
} catch (Exception $e) {
continue;
}
$details = OCA\Contacts\VCard::structureContact($vcard);
$contacts[] = array(
'id' => $contact['id'],
'aid' => $contact['addressbookid'],
'data' => $details,
);
// This should never execute.
/*if(!isset($contacts_addressbook[$contact['addressbookid']])) {
$contacts_addressbook[$contact['addressbookid']] = array(

View File

@ -120,7 +120,7 @@ if(!$value) {
unset($vcard->children[$line]);
$checksum = '';
} else {
$vcard->setString($name, '');
unset($vcard->{$name});
}
} else {
/* setting value */

View File

@ -36,11 +36,13 @@ if( is_null($contact)) {
bailOut(OCA\Contacts\App::$l10n->t('Error reading contact photo.'));
} else {
$image = new OC_Image();
if(!$image->loadFromBase64($contact->getAsString('PHOTO'))) {
$image->loadFromBase64($contact->getAsString('LOGO'));
if(!isset($contact->PHOTO) || !$image->loadFromBase64((string)$contact->PHOTO)) {
if(isset($contact->LOGO)) {
$image->loadFromBase64((string)$contact->LOGO);
}
}
if($image->valid()) {
$tmpkey = 'contact-photo-'.$contact->getAsString('UID');
$tmpkey = 'contact-photo-'.$contact->UID;
if(OC_Cache::set($tmpkey, $image->data(), 600)) {
OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
exit();

View File

@ -89,16 +89,18 @@ if($data) {
OCP\Util::writeLog('contacts',
'savecrop.php: files: Adding PHOTO property.',
OCP\Util::DEBUG);
// NOTE: For vCard 3.0 the type must be e.g. JPEG or PNG
// For vCard 3.0 the type must be e.g. JPEG or PNG
// For version 4.0 the full mimetype should be used.
// https://tools.ietf.org/html/rfc2426#section-3.1.4
$type = strtoupper(array_pop(explode('/', $image->mimeType())));
$vcard->addProperty('PHOTO',
$type = $vcard->VERSION == '4.0'
? $image->mimeType()
: strtoupper(array_pop(explode('/', $image->mimeType())));
$vcard->add('PHOTO',
$image->__toString(), array('ENCODING' => 'b',
'TYPE' => $type));
}
$now = new DateTime;
$vcard->setString('REV', $now->format(DateTime::W3C));
$vcard->{'REV'} = $now->format(DateTime::W3C);
if(!OCA\Contacts\VCard::edit($id, $vcard)) {
bailOut(OCA\Contacts\App::$l10n->t('Error saving contact.'));
}

View File

@ -122,20 +122,21 @@ if(!count($parts) > 0) {
exit();
}
foreach($parts as $part) {
$card = OC_VObject::parse($part);
if (!$card) {
try {
$vcard = Sabre\VObject\Reader::read($contact['carddata']);
} catch (Exception $e) {
$failed += 1;
OCP\Util::writeLog('contacts',
'Import: skipping card. Error parsing VCard: ' . $part,
'Import: skipping card. Error parsing VCard: ' . $e->getMessage(),
OCP\Util::ERROR);
continue; // Ditch cards that can't be parsed by Sabre.
}
try {
OCA\Contacts\VCard::add($id, $card);
OCA\Contacts\VCard::add($id, $vcard);
$imported += 1;
} catch (Exception $e) {
OCP\Util::writeLog('contacts',
'Error importing vcard: ' . $e->getMessage() . $nl . $card,
'Error importing vcard: ' . $e->getMessage() . $nl . $vcard,
OCP\Util::ERROR);
$failed += 1;
}

View File

@ -8,6 +8,8 @@
namespace OCA\Contacts;
use Sabre\VObject;
/**
* This class manages our app actions
*/
@ -32,35 +34,43 @@ class App {
/**
* Properties to index.
*/
public static $index_properties = array('FN', 'NICKNAME', 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO');
public static $index_properties = array('FN', 'NICKNAME', 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'PHOTO');
const THUMBNAIL_PREFIX = 'contact-thumbnail-';
const THUMBNAIL_SIZE = 28;
/**
* @brief Gets the VCard as an OC_VObject
* @returns The card or null if the card could not be parsed.
* @brief Gets the VCard as a Sabre\VObject\Component
* @returns Sabre\VObject\Component|null The card or null if the card could not be parsed.
*/
public static function getContactVCard($id) {
$card = null;
$vcard = null;
try {
$card = VCard::find($id);
} catch(Exception $e) {
return null;
}
$vcard = \OC_VObject::parse($card['carddata']);
try {
$vcard = \Sabre\VObject\Reader::read($card['carddata']);
} 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;
}
if (!is_null($vcard) && !isset($vcard->REV)) {
$rev = new \DateTime('@'.$card['lastmodified']);
$vcard->setString('REV', $rev->format(\DateTime::W3C));
$vcard->REV = $rev->format(\DateTime::W3C);
}
return $vcard;
}
public static function getPropertyLineByChecksum($vcard, $checksum) {
$line = null;
for($i=0;$i<count($vcard->children);$i++) {
if(substr(md5($vcard->children[$i]->serialize()), 0, 8) == $checksum ) {
foreach($vcard->children as $i => $property) {
if(substr(md5($property->serialize()), 0, 8) == $checksum ) {
$line = $i;
break;
}
@ -259,28 +269,30 @@ class App {
* check VCard for new categories.
* @see OC_VCategories::loadFromVObject
*/
public static function loadCategoriesFromVCard($id, \OC_VObject $contact) {
public static function loadCategoriesFromVCard($id, $contact) {
if(!$contact instanceof \OC_VObject) {
$contact = new \OC_VObject($contact);
}
self::getVCategories()->loadFromVObject($id, $contact, true);
}
/**
* @brief Get the last modification time.
* @param $contact OC_VObject|integer
* @param OC_VObject|Sabre\VObject\Component|integer $contact
* @returns DateTime | null
*/
public static function lastModified($contact) {
if(is_numeric($contact)) {
$card = VCard::find($contact);
return ($card ? new \DateTime('@' . $card['lastmodified']) : null);
} elseif($contact instanceof \OC_VObject) {
$rev = $contact->getAsString('REV');
if ($rev) {
return \DateTime::createFromFormat(\DateTime::W3C, $rev);
}
} elseif($contact instanceof \OC_VObject || $contact instanceof VObject\Component) {
return isset($contact->REV)
? \DateTime::createFromFormat(\DateTime::W3C, $contact->REV)
: null;
}
}
public static function cacheThumbnail($id, OC_Image $image = null) {
public static function cacheThumbnail($id, \OC_Image $image = null) {
if(\OC_Cache::hasKey(self::THUMBNAIL_PREFIX . $id)) {
return \OC_Cache::get(self::THUMBNAIL_PREFIX . $id);
}
@ -295,11 +307,10 @@ class App {
return false;
}
$image = new \OC_Image();
$photo = $vcard->getAsString('PHOTO');
if(!$photo) {
if(!isset($vcard->PHOTO)) {
return false;
}
if(!$image->loadFromBase64($photo)) {
if(!$image->loadFromBase64($vcard->PHOTO)) {
return false;
}
}
@ -321,7 +332,7 @@ class App {
return \OC_Cache::get(self::THUMBNAIL_PREFIX . $id);
}
public static function updateDBProperties($contactid, \OC_VObject $vcard = null) {
public static function updateDBProperties($contactid, $vcard = null) {
$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_cards_properties` WHERE `contactid` = ?');
try {
$stmt->execute(array($contactid));

View File

@ -31,6 +31,8 @@
namespace OCA\Contacts;
use \Sabre\VObject;
/**
* This class contains all hooks.
*/
@ -83,24 +85,26 @@ class Hooks{
$info = explode('_', $name);
$aid = $info[1];
Addressbook::find($aid);
foreach(VCard::all($aid) as $card) {
$vcard = \OC_VObject::parse($card['carddata']);
if (!$vcard) {
foreach(VCard::all($aid) as $contact) {
try {
$vcard = VObject\Reader::read($contact['carddata']);
} catch (Exception $e) {
continue;
}
$birthday = $vcard->BDAY;
if ($birthday) {
$date = new \DateTime($birthday);
$vevent = new \OC_VObject('VEVENT');
$vevent = VObject\Component::create('VEVENT');
//$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV));
$vevent->setDateTime('DTSTART', $date,
\Sabre\VObject\Property\DateTime::DATE);
$vevent->setString('DURATION', 'P1D');
$vevent->setString('UID', substr(md5(rand().time()), 0, 10));
$vevent->add('DTSTART');
$vevent->DTSTART->setDateTime($date,
VObject\Property\DateTime::DATE);
$vevent->add('DURATION', 'P1D');
$vevent->{'UID'} = substr(md5(rand().time()), 0, 10);
// DESCRIPTION?
$vevent->setString('RRULE', 'FREQ=YEARLY');
$vevent->{'RRULE'} = 'FREQ=YEARLY';
$title = str_replace('{name}',
$vcard->getAsString('FN'),
$vcard->FN,
App::$l10n->t('{name}\'s Birthday'));
$parameters['events'][] = array(
'id' => 0,//$card['id'],

View File

@ -37,6 +37,8 @@
namespace OCA\Contacts;
use Sabre\VObject;
/**
* This class manages our vCards
*/
@ -224,14 +226,14 @@ class VCard {
/**
* @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0) and add mandatory fields if missing.
* @param aid Address book id.
* @param vcard An OC_VObject of type VCARD (passed by reference).
* @param vcard A Sabre\VObject\Component of type VCARD (passed by reference).
*/
protected static function updateValuesFromAdd($aid, &$vcard) { // any suggestions for a better method name? ;-)
$stringprops = array('N', 'FN', 'ORG', 'NICK', 'ADR', 'NOTE');
$typeprops = array('ADR', 'TEL', 'EMAIL');
$upgrade = false;
$fn = $n = $uid = $email = $org = null;
$version = $vcard->getAsString('VERSION');
$version = isset($vcard->VERSION) ? $vcard->VERSION : null;
// Add version if needed
if($version && $version < '3.0') {
$upgrade = true;
@ -283,7 +285,7 @@ class VCard {
} else {
$fn = 'Unknown Name';
}
$vcard->setString('FN', $fn);
$vcard->FN = $fn;
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn, OCP\Util::DEBUG);
}
if(!$n || $n == ';;;;') { // Fix missing 'N' field. Ugly hack ahead ;-)
@ -292,30 +294,30 @@ class VCard {
$slice[] = "";
}
$n = implode(';', $slice).';;;';
$vcard->setString('N', $n);
$vcard->N = $n;
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n, OCP\Util::DEBUG);
}
if(!$uid) {
$vcard->setUID();
$uid = $vcard->getAsString('UID');
$uid = substr(md5(rand().time()), 0, 10);
$vcard->add('UID', $uid);
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid, OCP\Util::DEBUG);
}
if(self::trueUID($aid, $uid)) {
$vcard->setString('UID', $uid);
$vcard->{'UID'} = $uid;
}
$now = new \DateTime;
$vcard->setString('REV', $now->format(\DateTime::W3C));
$vcard->{'REV'} = $now->format(\DateTime::W3C);
}
/**
* @brief Adds a card
* @param $aid integer Addressbook id
* @param $card OC_VObject vCard file
* @param $card Sabre\VObject\Component vCard file
* @param $uri string the uri of the card, default based on the UID
* @param $isChecked boolean If the vCard should be checked for validity and version.
* @return insertid on success or false.
*/
public static function add($aid, \OC_VObject $card, $uri=null, $isChecked=false) {
public static function add($aid, VObject\Component $card, $uri=null, $isChecked=false) {
if(is_null($card)) {
\OCP\Util::writeLog('contacts', __METHOD__ . ', No vCard supplied', \OCP\Util::ERROR);
return null;
@ -334,25 +336,20 @@ class VCard {
if(!$isChecked) {
self::updateValuesFromAdd($aid, $card);
}
$card->setString('VERSION', '3.0');
$card->{'VERSION'} = '3.0';
// Add product ID is missing.
$prodid = trim($card->getAsString('PRODID'));
if(!$prodid) {
//$prodid = trim($card->getAsString('PRODID'));
//if(!$prodid) {
if(!isset($card->PRODID)) {
$appinfo = \OCP\App::getAppInfo('contacts');
$appversion = \OCP\App::getAppVersion('contacts');
$prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
$card->setString('PRODID', $prodid);
$card->add('PRODID', $prodid);
}
$fn = $card->getAsString('FN');
if (empty($fn)) {
$fn = '';
}
$fn = isset($card->FN) ? $card->FN : '';
if (!$uri) {
$uid = $card->getAsString('UID');
$uri = $uid.'.vcf';
}
$uri = isset($uri) ? $uri : $card->UID . '.vcf';
$data = $card->serialize();
$stmt = \OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_cards` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' );
@ -382,11 +379,16 @@ class VCard {
* @param integer $id Addressbook id
* @param string $uri the uri the card will have
* @param string $data vCard file
* @return insertid
* @returns integer|false insertid or false on error
*/
public static function addFromDAVData($id, $uri, $data) {
$card = \OC_VObject::parse($data);
return self::add($id, $card, $uri);
try {
$vcard = Sabre\VObject\Reader::read($data);
return self::add($id, $vcard, $uri);
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
return false;
}
}
/**
@ -397,7 +399,12 @@ class VCard {
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `carddata` = ?, `lastmodified` = ? WHERE `id` = ?' );
$now = new \DateTime;
foreach($objects as $object) {
$vcard = \OC_VObject::parse($object[1]);
$vcard = null;
try {
$vcard = Sabre\VObject\Reader::read($contact['carddata']);
} catch(\Exception $e) {
\OC_Log::write('contacts', __METHOD__. $e->getMessage(), \OCP\Util::ERROR);
}
if(!is_null($vcard)) {
$oldcard = self::find($object[0]);
if (!$oldcard) {
@ -411,7 +418,7 @@ class VCard {
return false;
}
}
$vcard->setString('REV', $now->format(\DateTime::W3C));
$vcard->{'REV'} = $now->format(\DateTime::W3C);
$data = $vcard->serialize();
try {
$result = $stmt->execute(array($data,time(),$object[0]));
@ -431,10 +438,10 @@ class VCard {
/**
* @brief edits a card
* @param integer $id id of card
* @param OC_VObject $card vCard file
* @param Sabre\VObject\Component $card vCard file
* @return boolean true on success, otherwise an exception will be thrown
*/
public static function edit($id, \OC_VObject $card) {
public static function edit($id, VObject\Component $card) {
$oldcard = self::find($id);
if (!$oldcard) {
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
@ -476,13 +483,10 @@ class VCard {
}
App::loadCategoriesFromVCard($id, $card);
$fn = $card->getAsString('FN');
if (empty($fn)) {
$fn = null;
}
$fn = isset($card->FN) ? $card->FN : '';
$now = new \DateTime;
$card->setString('REV', $now->format(\DateTime::W3C));
$card->{'REV'} = $now->format(\DateTime::W3C);
$data = $card->serialize();
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE `id` = ?' );
@ -515,14 +519,15 @@ class VCard {
*/
public static function editFromDAVData($aid, $uri, $data) {
$oldcard = self::findWhereDAVDataIs($aid, $uri);
$card = \OC_VObject::parse($data);
if(!$card) {
try {
$vcard = Sabre\VObject\Reader::read($data);
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.
', Unable to parse VCARD, uri: '.$uri, \OCP\Util::ERROR);
', Unable to parse VCARD, : ' . $e->getMessage(), \OCP\Util::ERROR);
return false;
}
try {
self::edit($oldcard['id'], $card);
self::edit($oldcard['id'], $vcard);
return true;
} catch(\Exception $e) {
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '
@ -540,8 +545,8 @@ class VCard {
* @return boolean true on success, otherwise an exception will be thrown
*/
public static function delete($id) {
$card = self::find($id);
if (!$card) {
$vcard = self::find($id);
if (!$vcard) {
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
. $id . ' not found.', \OCP\Util::DEBUG);
throw new \Exception(
@ -657,60 +662,23 @@ class VCard {
return true;
}
/**
* @brief Escapes delimiters from an array and returns a string.
* @param array $value
* @param char $delimiter
* @return string
*/
public static function escapeDelimiters($value, $delimiter=';') {
foreach($value as &$i ) {
$i = implode("\\$delimiter", explode($delimiter, $i));
}
return implode($delimiter, $value);
}
/**
* @brief Creates an array out of a multivalue property
* @param string $value
* @param char $delimiter
* @return array
*/
public static function unescapeDelimiters($value, $delimiter=';') {
$array = explode($delimiter, $value);
for($i=0;$i<count($array);$i++) {
if(substr($array[$i], -1, 1)=="\\") {
if(isset($array[$i+1])) {
$array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter.$array[$i+1];
unset($array[$i+1]);
} else {
$array[$i] = substr($array[$i], 0, count($array[$i])-2).$delimiter;
}
$i = $i - 1;
}
}
$array = array_map('trim', $array);
return $array;
}
/**
* @brief Data structure of vCard
* @param object $property
* @param Sabre\VObject\Component $property
* @return associative array
*
* look at code ...
*/
public static function structureContact($object) {
public static function structureContact($vcard) {
$details = array();
foreach($object->children as $property) {
foreach($vcard->children as $property) {
$pname = $property->name;
$temp = self::structureProperty($property);
if(!is_null($temp)) {
// Get Apple X-ABLabels
if(isset($object->{$property->group . '.X-ABLABEL'})) {
$temp['label'] = $object->{$property->group . '.X-ABLABEL'}->value;
if(isset($vcard->{$property->group . '.X-ABLABEL'})) {
$temp['label'] = $vcard->{$property->group . '.X-ABLABEL'}->value;
if($temp['label'] == '_$!<Other>!$_') {
$temp['label'] = App::$l10n->t('Other');
}
@ -743,12 +711,12 @@ class VCard {
* but we should look out for any problems.
*/
public static function structureProperty($property) {
if(!in_array($property->name, App::$index_properties)) {
return;
}
$value = $property->value;
//$value = htmlspecialchars($value);
if($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG') {
$value = self::unescapeDelimiters($value);
} elseif($property->name == 'CATEGORIES') {
$value = self::unescapeDelimiters($value, ',');
if($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG' || $property->name == 'CATEGORIES') {
$value = $property->getParts();
}
elseif($property->name == 'BDAY') {
if(strpos($value, '-') === false) {

View File

@ -45,15 +45,15 @@ if (is_null($contact)) {
OCP\Util::ERROR);
} else {
// Photo :-)
if ($image->loadFromBase64($contact->getAsString('PHOTO'))) {
if (isset($contact->PHOTO) && $image->loadFromBase64((string)$contact->PHOTO)) {
// OK
$etag = md5($contact->getAsString('PHOTO'));
$etag = md5($contact->PHOTO);
}
else
// Logo :-/
if ($image->loadFromBase64($contact->getAsString('LOGO'))) {
if (isset($contact->LOGO) && $image->loadFromBase64((string)$contact->LOGO)) {
// OK
$etag = md5($contact->getAsString('LOGO'));
$etag = md5($contact->LOGO);
}
if ($image->valid()) {
$modified = OCA\Contacts\App::lastModified($contact);