/** * @brief Data structure of vCard * @param VObject\VCard $contact * @return associative array|null */ public static function serializeContact(Contact $contact) { if (!$contact->retrieve()) { \OCP\Util::writeLog('contacts', __METHOD__ . ' error reading: ' . print_r($contact, true), \OCP\Util::DEBUG); return null; } $details = array(); if (isset($contact->PHOTO) || isset($contact->LOGO)) { $details['photo'] = true; $details['thumbnail'] = Properties::cacheThumbnail($contact->getBackend()->name, $contact->getParent()->getId(), $contact->getId(), null, $contact); } foreach ($contact->children as $property) { $pname = $property->name; $temp = self::serializeProperty($property); if (!is_null($temp)) { // Get Apple X-ABLabels if (isset($contact->{$property->group . '.X-ABLABEL'})) { $temp['label'] = $contact->{$property->group . '.X-ABLABEL'}->value; if ($temp['label'] == '_$!<Other>!$_') { $temp['label'] = Properties::$l10n->t('Other'); } if ($temp['label'] == '_$!<HomePage>!$_') { $temp['label'] = Properties::$l10n->t('HomePage'); } } if (array_key_exists($pname, $details)) { $details[$pname][] = $temp; } else { $details[$pname] = array($temp); } } } return array('data' => $details, 'metadata' => $contact->getMetaData()); }
/** * @NoAdminRequired * @NoCSRFRequired */ public function index() { \OC::$server->getNavigationManager()->setActiveEntry($this->appName); $importManager = new ImportManager(); $imppTypes = Properties::getTypesForProperty('IMPP'); $adrTypes = Properties::getTypesForProperty('ADR'); $phoneTypes = Properties::getTypesForProperty('TEL'); $emailTypes = Properties::getTypesForProperty('EMAIL'); $ims = Properties::getIMOptions(); $imProtocols = array(); foreach($ims as $name => $values) { $imProtocols[$name] = $values['displayname']; } $maxUploadFilesize = \OCP\Util::maxUploadFilesize('/'); $response = new TemplateResponse($this->appName, 'contacts'); $response->setParams(array( 'uploadMaxFilesize' => $maxUploadFilesize, 'uploadMaxHumanFilesize' => \OCP\Util::humanFileSize($maxUploadFilesize), 'phoneTypes' => $phoneTypes, 'emailTypes' => $emailTypes, 'adrTypes' => $adrTypes, 'imppTypes' => $imppTypes, 'imProtocols' => $imProtocols, 'importManager' => $importManager, )); return $response; }
public function tearDown() { unset($this->backend); unset($this->ab); Utils\Properties::purgeIndexes($this->contactIds); parent::tearDown(); }
/** * @NoAdminRequired * @NoCSRFRequired */ public function index() { $imppTypes = Properties::getTypesForProperty('IMPP'); $adrTypes = Properties::getTypesForProperty('ADR'); $phoneTypes = Properties::getTypesForProperty('TEL'); $emailTypes = Properties::getTypesForProperty('EMAIL'); $cloudTypes = Properties::getTypesForProperty('CLOUD'); $ims = Properties::getIMOptions(); $imProtocols = array(); foreach ($ims as $name => $values) { $imProtocols[$name] = $values['displayname']; } $maxUploadFilesize = $this->utilFactory->maxUploadFilesize('/'); \OCP\Util::addScript('placeholder', null); \OCP\Util::addScript('../vendor/blueimp-md5/js/md5', null); \OCP\Util::addScript('jquery.avatar', null); \OCP\Util::addScript('avatar', null); $response = new TemplateResponse($this->appName, 'contacts'); $response->setParams(['uploadMaxFilesize' => $maxUploadFilesize, 'uploadMaxHumanFilesize' => $this->utilFactory->humanFileSize($maxUploadFilesize), 'phoneTypes' => $phoneTypes, 'emailTypes' => $emailTypes, 'cloudTypes' => $cloudTypes, 'adrTypes' => $adrTypes, 'imppTypes' => $imppTypes, 'imProtocols' => $imProtocols, 'importManager' => $this->importManager]); return $response; }
/** * @NoAdminRequired * @NoCSRFRequired */ public function index() { \OC::$server->getNavigationManager()->setActiveEntry($this->appName); $importManager = new ImportManager(); $imppTypes = Properties::getTypesForProperty('IMPP'); $adrTypes = Properties::getTypesForProperty('ADR'); $phoneTypes = Properties::getTypesForProperty('TEL'); $emailTypes = Properties::getTypesForProperty('EMAIL'); $ims = Properties::getIMOptions(); $imProtocols = array(); foreach ($ims as $name => $values) { $imProtocols[$name] = $values['displayname']; } $maxUploadFilesize = \OCP\Util::maxUploadFilesize('/'); \OCP\Util::addScript('', 'jquery.multiselect'); \OCP\Util::addScript('', 'tags'); \OCP\Util::addScript('placeholder'); \OCP\Util::addScript('3rdparty', 'md5/md5.min'); \OCP\Util::addScript('jquery.avatar'); \OCP\Util::addScript('avatar'); \OCP\Util::addScript('contacts', 'jquery.combobox'); \OCP\Util::addScript('contacts', 'modernizr.custom'); \OCP\Util::addScript('contacts', 'app'); \OCP\Util::addScript('contacts', 'addressbooks'); \OCP\Util::addScript('contacts', 'contacts'); \OCP\Util::addScript('contacts', 'storage'); \OCP\Util::addScript('contacts', 'groups'); \OCP\Util::addScript('contacts', 'jquery.ocaddnew'); \OCP\Util::addScript('contacts', 'otherbackendconfig'); \OCP\Util::addScript('files', 'jquery.fileupload'); \OCP\Util::addScript('3rdparty/Jcrop', 'jquery.Jcrop'); \OCP\Util::addStyle('', 'jquery.multiselect'); \OCP\Util::addStyle('contacts', 'jquery.combobox'); \OCP\Util::addStyle('contacts', 'jquery.ocaddnew'); \OCP\Util::addStyle('3rdparty/Jcrop', 'jquery.Jcrop'); \OCP\Util::addStyle('contacts', 'contacts'); $response = new TemplateResponse($this->appName, 'contacts'); $response->setParams(array('uploadMaxFilesize' => $maxUploadFilesize, 'uploadMaxHumanFilesize' => \OCP\Util::humanFileSize($maxUploadFilesize), 'phoneTypes' => $phoneTypes, 'emailTypes' => $emailTypes, 'adrTypes' => $adrTypes, 'imppTypes' => $imppTypes, 'imProtocols' => $imProtocols, 'importManager' => $importManager)); return $response; }
/** * Validates the node for correctness. * * The following options are supported: * - VCard::REPAIR - If something is broken, and automatic repair may * be attempted. * - VCard::UPGRADE - If needed the vCard will be upgraded to version 3.0. * * An array is returned with warnings. * * Every item in the array has the following properties: * * level - (number between 1 and 3 with severity information) * * message - (human readable message) * * node - (reference to the offending node) * * @param int $options * @return array */ public function validate($options = 0) { $warnings = array(); if ($options & self::UPGRADE) { $this->VERSION = self::DEFAULT_VERSION; foreach ($this->children as &$property) { $this->decodeProperty($property); $this->fixPropertyParameters($property); /* What exactly was I thinking here? switch((string)$property->name) { case 'LOGO': case 'SOUND': case 'PHOTO': if(isset($property['TYPE']) && strpos((string)$property['TYPE'], '/') === false) { $property['TYPE'] = 'image/' . strtolower($property['TYPE']); } }*/ } } $version = $this->select('VERSION'); if (count($version) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = self::DEFAULT_VERSION; if (!$options & self::UPGRADE) { $options |= self::UPGRADE; } } } else { $version = (string) $this->VERSION; if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') { $warnings[] = array('level' => 1, 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = self::DEFAULT_VERSION; if (!$options & self::UPGRADE) { $options |= self::UPGRADE; } } } } $fn = $this->select('FN'); if (count($fn) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The FN property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR && count($fn) === 0) { // We're going to try to see if we can use the contents of the // N property. if (isset($this->N)) { $value = explode(';', (string) $this->N); if (isset($value[1]) && $value[1]) { $this->FN = $value[1] . ' ' . $value[0]; } else { $this->FN = $value[0]; } // Otherwise, the ORG property may work } elseif (isset($this->ORG)) { $this->FN = (string) $this->ORG; } elseif (isset($this->EMAIL)) { $this->FN = (string) $this->EMAIL; } } } $n = $this->select('N'); if (count($n) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The N property must appear in the VCARD component exactly 1 time', 'node' => $this); // TODO: Make a better effort parsing FN. if ($options & self::REPAIR && count($n) === 0) { // Take 2 first name parts of 'FN' and reverse. $slice = array_reverse(array_slice(explode(' ', (string) $this->FN), 0, 2)); if (count($slice) < 2) { // If not enought, add one more... $slice[] = ""; } $this->N = implode(';', $slice) . ';;;'; } } if (!isset($this->UID)) { $warnings[] = array('level' => 1, 'message' => 'Every vCard must have a UID', 'node' => $this); if ($options & self::REPAIR) { $this->UID = Utils\Properties::generateUID(); } } if ($options & self::REPAIR || $options & self::UPGRADE) { $now = new \DateTime(); $this->REV = $now->format(\DateTime::W3C); } return array_merge(parent::validate($options), $warnings); }
/** * 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()) { if ($checksum === 'new') { $property = Property::create($name); $this->add($property); } else { $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 { $property->setValue($value); } break; case 'IMPP': if (is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) { throw new \InvalidArgumentException(self::$l10n->t(' Missing IM parameter for: ') . $name . ' ' . $value, 412); } $serviceType = $parameters['X-SERVICE-TYPE']; if (is_array($serviceType)) { $serviceType = $serviceType[0]; } $impp = Utils\Properties::getIMOptions($serviceType); if (is_null($impp)) { throw new \UnexpectedValueException(self::$l10n->t('Unknown IM: ') . $serviceType, 415); } $value = $impp['protocol'] . ':' . $value; $property->setValue($value); break; default: \OCP\Util::writeLog('contacts', __METHOD__ . ' adding: ' . $name . ' ' . $value, \OCP\Util::DEBUG); $property->setValue($value); break; } $this->setParameters($property, $parameters, true); $this->setSaved(false); return substr(md5($property->serialize()), 0, 8); }
public function __unset($key) { if (!$this->isRetrieved()) { $this->retrieve(); } parent::__unset($key); if ($key === 'PHOTO') { Properties::cacheThumbnail($this->getBackend()->name, $this->getParent()->getId(), $this->getId(), null, $this, array('remove' => true)); } $this->setSaved(false); }
/** * Get a photo from the oC and crops it with the suplied geometry. * @NoAdminRequired * @NoCSRFRequired */ public function cropPhoto() { $params = $this->request->urlParams; $x = isset($this->request->post['x']) && $this->request->post['x'] ? $this->request->post['x'] : 0; $y = isset($this->request->post['y']) && $this->request->post['y'] ? $this->request->post['y'] : 0; $w = isset($this->request->post['w']) && $this->request->post['w'] ? $this->request->post['w'] : -1; $h = isset($this->request->post['h']) && $this->request->post['h'] ? $this->request->post['h'] : -1; $tmpkey = $params['key']; $maxSize = isset($this->request->post['maxSize']) ? $this->request->post['maxSize'] : 200; $app = new App($this->api->getUserId()); $addressBook = $app->getAddressBook($params['backend'], $params['addressBookId']); $contact = $addressBook->getChild($params['contactId']); $response = new JSONResponse(); if (!$contact) { return $response->bailOut(App::$l10n->t('Couldn\'t find contact.')); } $data = $this->server->getCache()->get($tmpkey); if (!$data) { return $response->bailOut(App::$l10n->t('Image has been removed from cache')); } $image = new \OCP\Image(); if (!$image->loadFromData($data)) { return $response->bailOut(App::$l10n->t('Error creating temporary image')); } $w = $w !== -1 ? $w : $image->width(); $h = $h !== -1 ? $h : $image->height(); if (!$image->crop($x, $y, $w, $h)) { return $response->bailOut(App::$l10n->t('Error cropping image')); } if ($image->width() < $maxSize || $image->height() < $maxSize) { if (!$image->resize(200)) { return $response->bailOut(App::$l10n->t('Error resizing image')); } } // 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 if (strval($contact->VERSION) === '4.0') { $type = $image->mimeType(); } else { $type = explode('/', $image->mimeType()); $type = strtoupper(array_pop($type)); } if (isset($contact->PHOTO)) { $property = $contact->PHOTO; if (!$property) { $this->server->getCache()->remove($tmpkey); return $response->bailOut(App::$l10n->t('Error getting PHOTO property.')); } $property->setValue(strval($image)); $property->parameters = array(); $property->parameters[] = new \Sabre\VObject\Parameter('ENCODING', 'b'); $property->parameters[] = new \Sabre\VObject\Parameter('TYPE', $image->mimeType()); $contact->PHOTO = $property; } else { $contact->add('PHOTO', strval($image), array('ENCODING' => 'b', 'TYPE' => $type)); // TODO: Fix this hack $contact->setSaved(false); } if (!$contact->save()) { return $response->bailOut(App::$l10n->t('Error saving contact.')); } $thumbnail = Properties::cacheThumbnail($params['backend'], $params['addressBookId'], $params['contactId'], $image); $response->setData(array('status' => 'success', 'data' => array('id' => $params['contactId'], 'thumbnail' => $thumbnail))); $this->server->getCache()->remove($tmpkey); return $response; }
/** * @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) { $stmt = $this->getPreparedQuery('counturi'); $result = $stmt->execute(array($addressBookId, $uri)); $result = $result->fetchRow(); if (is_array($result) && count($result) > 0 && $result['count'] > 0) { while (true) { $uri = Properties::generateUID() . '.vcf'; $result = $stmt->execute(array($addressBookId, $uri)); if (is_array($result) && count($result) > 0 && $result['count'] > 0) { continue; } else { return $uri; } } } return $uri; }
/** * Scan vCards for properties. */ public static function indexProperties() { $offset = 0; $limit = 10; $app = new App(); $backend = $app->getBackend('local'); $addressBookInfos = $backend->getAddressBooksForUser(); foreach ($addressBookInfos as $addressBookInfo) { $addressBook = new AddressBook($backend, $addressBookInfo); $contacts = $addressBook->getChildren($limit, $offset, false); \OCP\Util::writeLog('contacts', __METHOD__ . ', indexing: ' . $limit . ' starting from ' . $offset, \OCP\Util::DEBUG); foreach ($contacts as $contact) { if (!$contact->retrieve()) { \OCP\Util::writeLog('contacts', __METHOD__ . ', Error loading contact ' . print_r($contact, true), \OCP\Util::DEBUG); } Utils\Properties::updateIndex($contact->getId(), $contact); } $offset += $limit; } $stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_cards_properties` WHERE NOT EXISTS(SELECT NULL FROM `*PREFIX*contacts_cards` WHERE `*PREFIX*contacts_cards`.id = `*PREFIX*contacts_cards_properties`.contactid)'); $result = $stmt->execute(array()); }
/** * Validates the node for correctness. * * The following options are supported: * - VCard::REPAIR - If something is broken, and automatic repair may * be attempted. * - VCard::UPGRADE - If needed the vCard will be upgraded to version 3.0. * * An array is returned with warnings. * * Every item in the array has the following properties: * * level - (number between 1 and 3 with severity information) * * message - (human readable message) * * node - (reference to the offending node) * * @param int $options * @return array */ public function validate($options = 0) { $warnings = array(); if ($options & self::UPGRADE) { $this->VERSION = self::DEFAULT_VERSION; foreach ($this->children as $idx => &$property) { $this->decodeProperty($property); $this->fixPropertyParameters($property); /* What exactly was I thinking here? switch((string)$property->name) { case 'LOGO': case 'SOUND': case 'PHOTO': if(isset($property['TYPE']) && strpos((string)$property['TYPE'], '/') === false) { $property['TYPE'] = 'image/' . strtolower($property['TYPE']); } }*/ } } $version = $this->select('VERSION'); if (count($version) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The VERSION property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = self::DEFAULT_VERSION; if (!$options & self::UPGRADE) { $options |= self::UPGRADE; } } } else { $version = (string) $this->VERSION; if ($version !== '2.1' && $version !== '3.0' && $version !== '4.0') { $warnings[] = array('level' => 1, 'message' => 'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.', 'node' => $this); if ($options & self::REPAIR) { $this->VERSION = self::DEFAULT_VERSION; if (!$options & self::UPGRADE) { $options |= self::UPGRADE; } } } } $fn = $this->select('FN'); if (count($fn) !== 1 || trim((string) $this->FN) === '') { $warnings[] = array('level' => 1, 'message' => 'The FN property must appear in the VCARD component exactly 1 time', 'node' => $this); if ($options & self::REPAIR) { // We're going to try to see if we can use the contents of the // N property. if (isset($this->N) && substr((string) $this->N, 2) !== ';;' && (string) $this->N !== '') { $value = explode(';', (string) $this->N); if (isset($value[1]) && $value[1]) { $this->FN = $value[1] . ' ' . $value[0]; } else { $this->FN = $value[0]; } // Otherwise, the ORG property may work } elseif (isset($this->ORG)) { $this->FN = (string) $this->ORG; } elseif (isset($this->EMAIL)) { $this->FN = (string) $this->EMAIL; } } } if (isset($this->BDAY)) { if ($options & self::REPAIR) { // If the BDAY has a format of e.g. 19960401 $bday = (string) $this->BDAY; if (strlen($bday) >= 8 && is_int(substr($bday, 0, 4)) && is_int(substr($bday, 4, 2)) && is_int(substr($bday, 6, 2))) { $this->BDAY = substr($bday, 0, 4) . '-' . substr($bday, 4, 2) . '-' . substr($bday, 6, 2); $this->BDAY->VALUE = 'DATE'; } else { if (empty($bday)) { // We don't want "New \DateTime($bday)" evaluate to current date because of empty value. // So we'll leave this item empty. \OCP\Util::writeLog('contacts', __METHOD__ . ' Removing invalid/empty BDAY.', \OCP\Util::DEBUG); unset($this->BDAY); } else { if ($bday[5] !== '-' || $bday[7] !== '-') { try { // Skype exports as e.g. Jan 14, 1996 $date = new \DateTime($bday); $this->BDAY = $date->format('Y-m-d'); $this->BDAY->VALUE = 'DATE'; } catch (\Exception $e) { \OCP\Util::writeLog('contacts', __METHOD__ . ' Removing invalid BDAY: ' . $bday, \OCP\Util::DEBUG); unset($this->BDAY); } } } } } } $n = $this->select('N'); if (count($n) !== 1) { $warnings[] = array('level' => 1, 'message' => 'The N property must appear in the VCARD component exactly 1 time', 'node' => $this); // TODO: Make a better effort parsing FN. if ($options & self::REPAIR && count($n) === 0) { // Take 2 first name parts of 'FN' and reverse. $slice = array_reverse(array_slice(explode(' ', (string) $this->FN), 0, 2)); if (count($slice) < 2) { // If not enought, add one more... $slice[] = ""; } $this->N = $slice; } } if (!isset($this->UID) || trim((string) $this->UID) === '') { $warnings[] = array('level' => 1, 'message' => 'Every vCard must have a UID', 'node' => $this); if ($options & self::REPAIR) { $this->UID = Utils\Properties::generateUID(); } } if ($options & self::REPAIR || $options & self::UPGRADE) { $now = new \DateTime(); $this->REV = $now->format(\DateTime::W3C); } return array_merge(parent::validate($options), $warnings); }
/** * Scan vCards for properties. */ public static function indexProperties() { $offset = 0; $limit = 10; $app = new App(); $backend = $app->getBackend('local'); $addressBookInfos = $backend->getAddressBooksForUser(); foreach ($addressBookInfos as $addressBookInfo) { $addressBook = new AddressBook($backend, $addressBookInfo); while ($contacts = $addressBook->getChildren($limit, $offset, false)) { foreach ($contacts as $contact) { $contact->retrieve(); } \OCP\Util::writeLog('contacts', __CLASS__ . '::' . __METHOD__ . ', indexing: ' . $limit . ' starting from ' . $offset, \OCP\Util::DEBUG); Utils\Properties::updateIndex($contact->getId(), $contact); $offset += $limit; } } }
* * 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\Utils; use OCA\Contacts\App; use OCA\Contacts\VObject\VCard; use Sabre\VObject\UUIDUtil; Properties::$l10n = \OCP\Util::getL10N('contacts'); class Properties { const THUMBNAIL_PREFIX = 'contact-thumbnail-'; const THUMBNAIL_SIZE = 32; private static $deleteindexstmt; private static $updateindexstmt; protected static $cardsTableName = '*PREFIX*contacts_cards'; protected static $indexTableName = '*PREFIX*contacts_cards_properties'; /** * @brief language object for calendar app * * @var \OCP\IL10N */ public static $l10n; /**
/** * Get a photo from the oC and crops it with the suplied geometry. * @NoAdminRequired * @NoCSRFRequired */ public function cropPhoto() { $params = $this->request->urlParams; $x = $this->params('x', 0); $y = $this->params('y', 0); $w = $this->params('w', -1); $h = $this->params('h', -1); $tmpkey = $params['key']; $addressBook = $this->app->getAddressBook($params['backend'], $params['addressBookId']); $contact = $addressBook->getChild($params['contactId']); $response = new JSONResponse(); $tmpPhoto = new TemporaryPhoto($this->cache, $tmpkey); $image = $tmpPhoto->getPhoto(); if (!$image || !$image->valid()) { return $response->bailOut(App::$l10n->t('Error loading image from cache')); } $w = $w !== -1 ? $w : $image->width(); $h = $h !== -1 ? $h : $image->height(); $image->crop($x, $y, $w, $h); if (!$contact->setPhoto($image)) { $tmpPhoto->remove($tmpkey); return $response->bailOut(App::$l10n->t('Error getting PHOTO property.')); } if (!$contact->save()) { return $response->bailOut(App::$l10n->t('Error saving contact.')); } $thumbnail = Properties::cacheThumbnail($params['backend'], $params['addressBookId'], $params['contactId'], $image, $contact); $response->setData(array('status' => 'success', 'data' => array('id' => $params['contactId'], 'thumbnail' => $thumbnail))); $tmpPhoto->remove($tmpkey); return $response; }
/** * @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) { $stmt = \OCP\DB::prepare('SELECT * FROM `' . $this->cardsTableName . '` WHERE `addressbookid` = ? AND `uri` = ?'); $result = $stmt->execute(array($addressBookId, $uri)); if ($result->numRows() > 0) { while (true) { $uri = Properties::generateUID() . '.vcf'; $result = $stmt->execute(array($addressBookId, $uri)); if ($result->numRows() > 0) { continue; } else { return $uri; } } } else { return $uri; } }