mirror of
https://github.com/owncloudarchive/contacts.git
synced 2024-11-28 10:24:11 +01:00
Merge branch 'contacts_backends' into contacts_oc6
Conflicts: contacts/ajax/categories/add.php contacts/ajax/contact/add.php contacts/appinfo/app.php contacts/appinfo/migrate.php contacts/css/contacts.css contacts/index.php contacts/js/app.js contacts/js/contacts.js contacts/js/groups.js contacts/lib/app.php contacts/lib/carddav/addressbook.php contacts/lib/sabre/backend.php contacts/lib/vcard.php contacts/templates/contacts.php contacts/thumbnail.php
This commit is contained in:
commit
c3fa18e4a7
@ -1,37 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2011-2012 Thomas Tanghus <thomas@tanghus.net>
|
||||
* Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
debug('name: '.$_POST['name']);
|
||||
|
||||
$userid = OCP\USER::getUser();
|
||||
$name = isset($_POST['name'])?trim(strip_tags($_POST['name'])):null;
|
||||
$description = isset($_POST['description'])
|
||||
? trim(strip_tags($_POST['description']))
|
||||
: null;
|
||||
|
||||
if(is_null($name)) {
|
||||
bailOut('Cannot add addressbook with an empty name.');
|
||||
}
|
||||
$bookid = OCA\Contacts\Addressbook::add($userid, $name, $description);
|
||||
if(!$bookid) {
|
||||
bailOut('Error adding addressbook: '.$name);
|
||||
}
|
||||
|
||||
if(!OCA\Contacts\Addressbook::setActive($bookid, 1)) {
|
||||
bailOut('Error activating addressbook.');
|
||||
}
|
||||
$addressbook = OCA\Contacts\Addressbook::find($bookid);
|
||||
OCP\JSON::success(array('data' => array('addressbook' => $addressbook)));
|
@ -1,29 +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');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$category = isset($_POST['category']) ? trim(strip_tags($_POST['category'])) : null;
|
||||
|
||||
if(is_null($category) || $category === "") {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No category name given.'));
|
||||
}
|
||||
|
||||
$catman = new OC_VCategories('contact');
|
||||
$id = $catman->add($category);
|
||||
|
||||
if($id !== false) {
|
||||
OCP\JSON::success(array('data' => array('id'=>$id, 'name' => $category)));
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error adding group.'));
|
||||
}
|
@ -1,34 +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');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$categoryid = isset($_POST['categoryid']) ? $_POST['categoryid'] : null;
|
||||
$contactids = isset($_POST['contactids']) ? $_POST['contactids'] : null;
|
||||
|
||||
if(is_null($categoryid)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Group ID missing from request.'));
|
||||
}
|
||||
|
||||
if(is_null($contactids)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Contact ID missing from request.'));
|
||||
}
|
||||
|
||||
$catmgr = OCA\Contacts\App::getVCategories();
|
||||
|
||||
foreach($contactids as $contactid) {
|
||||
debug('contactid: ' . $contactid . ', categoryid: ' . $categoryid);
|
||||
$catmgr->addToCategory($contactid, $categoryid);
|
||||
}
|
||||
|
||||
OCP\JSON::success();
|
@ -1,51 +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');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$categories = isset($_POST['categories']) ? $_POST['categories'] : null;
|
||||
$fromobjects = (isset($_POST['fromobjects'])
|
||||
&& ($_POST['fromobjects'] === 'true' || $_POST['fromobjects'] === '1')) ? true : false;
|
||||
|
||||
if(is_null($categories)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No categories selected for deletion.'));
|
||||
}
|
||||
|
||||
debug(print_r($categories, true));
|
||||
if($fromobjects) {
|
||||
$addressbooks = OCA\Contacts\Addressbook::all(OCP\USER::getUser());
|
||||
if(count($addressbooks) == 0) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No address books found.'));
|
||||
}
|
||||
$addressbookids = array();
|
||||
foreach($addressbooks as $addressbook) {
|
||||
$addressbookids[] = $addressbook['id'];
|
||||
}
|
||||
$contacts = OCA\Contacts\VCard::all($addressbookids);
|
||||
if(count($contacts) == 0) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No contacts found.'));
|
||||
}
|
||||
|
||||
$cards = array();
|
||||
foreach($contacts as $contact) {
|
||||
$cards[] = array($contact['id'], $contact['carddata']);
|
||||
}
|
||||
}
|
||||
|
||||
$catman = new OC_VCategories('contact');
|
||||
$catman->delete($categories, $cards);
|
||||
|
||||
if($fromobjects) {
|
||||
OCA\Contacts\VCard::updateDataByID($cards);
|
||||
}
|
||||
OCP\JSON::success();
|
@ -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', ''),
|
||||
)
|
||||
)
|
||||
);
|
@ -1,34 +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');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$categoryid = isset($_POST['categoryid']) ? $_POST['categoryid'] : null;
|
||||
$contactids = isset($_POST['contactids']) ? $_POST['contactids'] : null;
|
||||
|
||||
if(is_null($categoryid)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Group ID missing from request.'));
|
||||
}
|
||||
|
||||
if(is_null($contactids)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Contact ID missing from request.'));
|
||||
}
|
||||
|
||||
$catmgr = OCA\Contacts\App::getVCategories();
|
||||
|
||||
foreach($contactids as $contactid) {
|
||||
debug('id: ' . $contactid .', categoryid: ' . $categoryid);
|
||||
$catmgr->removeFromCategory($contactid, $categoryid);
|
||||
}
|
||||
|
||||
OCP\JSON::success();
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$aid = isset($_POST['aid']) ? $_POST['aid'] : null;
|
||||
if(!$aid) {
|
||||
$ids = OCA\Contacts\Addressbook::activeIds();
|
||||
if(count($ids) > 0) {
|
||||
$aid = min($ids); // first active addressbook.
|
||||
} else {
|
||||
$addressbooks = OCA\Contacts\Addressbook::all(OCP\User::getUser());
|
||||
if(count($addressbooks) === 0) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('You have no addressbooks.'));
|
||||
} else {
|
||||
$aid = $addressbooks[0]['id'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$isnew = isset($_POST['isnew']) ? $_POST['isnew'] : false;
|
||||
|
||||
$vcard = Sabre\VObject\Component::create('VCARD');
|
||||
$uid = substr(md5(rand().time()), 0, 10);
|
||||
$vcard->add('UID', $uid);
|
||||
|
||||
$id = null;
|
||||
try {
|
||||
$id = OCA\Contacts\VCard::add($aid, $vcard, null, $isnew);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
|
||||
if(!$id) {
|
||||
bailOut('There was an error adding the contact.');
|
||||
}
|
||||
|
||||
$lastmodified = OCA\Contacts\App::lastModified($vcard);
|
||||
if(!$lastmodified) {
|
||||
$lastmodified = new DateTime();
|
||||
}
|
||||
OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'id' => $id,
|
||||
'aid' => $aid,
|
||||
'details' => OCA\Contacts\VCard::structureContact($vcard),
|
||||
'lastmodified' => $lastmodified->format('U')
|
||||
)
|
||||
));
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
* @copyright 2012 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/>.
|
||||
*
|
||||
*/
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$id = isset($_POST['id']) ? $_POST['id'] : null;
|
||||
if(!$id) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
|
||||
}
|
||||
|
||||
try {
|
||||
OCA\Contacts\VCard::delete($id);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
|
||||
OCP\JSON::success(array('data' => array( 'id' => $id )));
|
@ -1,73 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$id = isset($_POST['id']) ? $_POST['id'] : null;
|
||||
$name = isset($_POST['name']) ? $_POST['name'] : null;
|
||||
$checksum = isset($_POST['checksum']) ? $_POST['checksum'] : null;
|
||||
$l10n = OCA\Contacts\App::$l10n;
|
||||
|
||||
$multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
|
||||
|
||||
if(!$id) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
|
||||
}
|
||||
|
||||
if(!$name) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('element name is not set.'));
|
||||
}
|
||||
|
||||
if(!$checksum && in_array($name, $multi_properties)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('checksum is not set.'));
|
||||
}
|
||||
|
||||
$vcard = OCA\Contacts\App::getContactVCard( $id );
|
||||
|
||||
if(!is_null($checksum)) {
|
||||
$line = OCA\Contacts\App::getPropertyLineByChecksum($vcard, $checksum);
|
||||
if(is_null($line)) {
|
||||
bailOut($l10n->t('Information about vCard is incorrect. Please reload the page.'));
|
||||
exit();
|
||||
}
|
||||
unset($vcard->children[$line]);
|
||||
} else {
|
||||
unset($vcard->{$name});
|
||||
}
|
||||
|
||||
try {
|
||||
OCA\Contacts\VCard::edit($id, $vcard);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
|
||||
OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'id' => $id,
|
||||
'lastmodified' => OCA\Contacts\App::lastModified($vcard)->format('U'),
|
||||
)
|
||||
));
|
@ -23,10 +23,10 @@ $aid = isset($_GET['aid'])?$_GET['aid']:null;
|
||||
$active_addressbooks = array();
|
||||
if(is_null($aid)) {
|
||||
// Called initially to get the active addressbooks.
|
||||
$active_addressbooks = OCA\Contacts\Addressbook::active(OCP\USER::getUser());
|
||||
$active_addressbooks = OCA\Contacts\AddressbookLegacy::active(OCP\USER::getUser());
|
||||
} else {
|
||||
// called each time more contacts has to be shown.
|
||||
$active_addressbooks = array(OCA\Contacts\Addressbook::find($aid));
|
||||
$active_addressbooks = array(OCA\Contacts\AddressbookLegacy::find($aid));
|
||||
}
|
||||
|
||||
$lastModified = OCA\Contacts\App::lastModified();
|
||||
@ -42,18 +42,6 @@ $contacts_addressbook = array();
|
||||
$ids = array();
|
||||
foreach($active_addressbooks as $addressbook) {
|
||||
$ids[] = $addressbook['id'];
|
||||
/*if(!isset($contacts_addressbook[$addressbook['id']])) {
|
||||
$contacts_addressbook[$addressbook['id']]
|
||||
= array('contacts' => array('type' => 'book',));
|
||||
$contacts_addressbook[$addressbook['id']]['displayname']
|
||||
= $addressbook['displayname'];
|
||||
$contacts_addressbook[$addressbook['id']]['description']
|
||||
= $addressbook['description'];
|
||||
$contacts_addressbook[$addressbook['id']]['permissions']
|
||||
= $addressbook['permissions'];
|
||||
$contacts_addressbook[$addressbook['id']]['owner']
|
||||
= $addressbook['userid'];
|
||||
}*/
|
||||
}
|
||||
|
||||
$contacts_alphabet = array();
|
||||
@ -63,14 +51,6 @@ $contacts_alphabet = array_merge(
|
||||
$contacts_alphabet,
|
||||
OCA\Contacts\VCard::all($ids)
|
||||
);
|
||||
/*foreach($ids as $id) {
|
||||
if($id) {
|
||||
$contacts_alphabet = array_merge(
|
||||
$contacts_alphabet,
|
||||
OCA\Contacts\VCard::all($id, $offset, 50)
|
||||
);
|
||||
}
|
||||
}*/
|
||||
|
||||
uasort($contacts_alphabet, 'cmp');
|
||||
|
||||
@ -89,38 +69,11 @@ if($contacts_alphabet) {
|
||||
'data' => $details,
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', 'Exception: ' . $e->getMessage(), \OCP\Util::DEBUG);
|
||||
continue;
|
||||
}
|
||||
// This should never execute.
|
||||
/*if(!isset($contacts_addressbook[$contact['addressbookid']])) {
|
||||
$contacts_addressbook[$contact['addressbookid']] = array(
|
||||
'contacts' => array('type' => 'book',)
|
||||
);
|
||||
}
|
||||
$display = trim($contact['fullname']);
|
||||
if(!$display) {
|
||||
$vcard = OCA\Contacts\App::getContactVCard($contact['id']);
|
||||
if(!is_null($vcard)) {
|
||||
$struct = OCA\Contacts\VCard::structureContact($vcard);
|
||||
$display = isset($struct['EMAIL'][0])
|
||||
? $struct['EMAIL'][0]['value']
|
||||
: '[UNKNOWN]';
|
||||
}
|
||||
}
|
||||
$contacts_addressbook[$contact['addressbookid']]['contacts'][] = array(
|
||||
'type' => 'contact',
|
||||
'id' => $contact['id'],
|
||||
'addressbookid' => $contact['addressbookid'],
|
||||
'displayname' => htmlspecialchars($display),
|
||||
'permissions' =>
|
||||
isset($contacts_addressbook[$contact['addressbookid']]['permissions'])
|
||||
? $contacts_addressbook[$contact['addressbookid']]['permissions']
|
||||
: '0',
|
||||
);*/
|
||||
}
|
||||
}
|
||||
//unset($contacts_alphabet);
|
||||
//uasort($contacts, 'cmp');
|
||||
|
||||
OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
|
@ -1,260 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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;
|
||||
use Sabre\VObject as VObject;
|
||||
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
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($val)) {
|
||||
debug('Adding parameter: '.$key.'=>'.print_r($val, true));
|
||||
$property->add($key, strip_tags($parameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are a user
|
||||
\OCP\JSON::checkLoggedIn();
|
||||
\OCP\JSON::checkAppEnabled('contacts');
|
||||
\OCP\JSON::callCheck();
|
||||
$id = isset($_POST['id'])?$_POST['id']:null;
|
||||
$name = isset($_POST['name'])?$_POST['name']:null;
|
||||
$value = isset($_POST['value'])?$_POST['value']:null;
|
||||
$parameters = isset($_POST['parameters'])?$_POST['parameters']:null;
|
||||
$checksum = isset($_POST['checksum'])?$_POST['checksum']:null;
|
||||
|
||||
debug('value: ' . print_r($value, 1));
|
||||
|
||||
$multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
|
||||
|
||||
if(!$name) {
|
||||
bailOut(App::$l10n->t('element name is not set.'));
|
||||
}
|
||||
if(!$id) {
|
||||
bailOut(App::$l10n->t('id is not set.'));
|
||||
}
|
||||
if(!$checksum && in_array($name, $multi_properties)) {
|
||||
bailOut(App::$l10n->t('checksum is not set.'));
|
||||
}
|
||||
if(is_array($value)) {
|
||||
$value = array_map('strip_tags', $value);
|
||||
// NOTE: Important, otherwise the compound value will be
|
||||
// set in the order the fields appear in the form!
|
||||
ksort($value);
|
||||
//if($name == 'CATEGORIES') {
|
||||
// $value = VCard::escapeDelimiters($value, ',');
|
||||
//} else {
|
||||
// $value = VCard::escapeDelimiters($value, ';');
|
||||
//}
|
||||
} else {
|
||||
$value = trim(strip_tags($value));
|
||||
}
|
||||
|
||||
$vcard = App::getContactVCard($id);
|
||||
if(!$vcard) {
|
||||
bailOut(App::$l10n->t('Couldn\'t find vCard for %d.', array($id)));
|
||||
}
|
||||
|
||||
$property = null;
|
||||
|
||||
if(in_array($name, $multi_properties)) {
|
||||
if($checksum !== 'new') {
|
||||
$line = App::getPropertyLineByChecksum($vcard, $checksum);
|
||||
if(is_null($line)) {
|
||||
bailOut(App::$l10n->t(
|
||||
'Information about vCard is incorrect. Please reload the page: ').$checksum
|
||||
);
|
||||
}
|
||||
$property = $vcard->children[$line];
|
||||
$element = $property->name;
|
||||
|
||||
if($element != $name) {
|
||||
bailOut(App::$l10n->t(
|
||||
'Something went FUBAR. ').$name.' != '.$element
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Add new property
|
||||
$element = $name;
|
||||
if (!is_scalar($value)) {
|
||||
$property = VObject\Property::create($name);
|
||||
if(in_array($name, array('ADR',))) {
|
||||
$property->setParts($value);
|
||||
} else {
|
||||
bailOut(App::$l10n->t(
|
||||
'Cannot save property of type "%s" as array', array($name,)
|
||||
));
|
||||
}
|
||||
setParameters($property, $parameters);
|
||||
} else {
|
||||
$property = VObject\Property::create($name, $value, $parameters);
|
||||
}
|
||||
$vcard->add($property);
|
||||
$checksum = substr(md5($property->serialize()), 0, 8);
|
||||
try {
|
||||
VCard::edit($id, $vcard);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
\OCP\JSON::success(array('data' => array(
|
||||
'checksum' => $checksum,
|
||||
'oldchecksum' => $_POST['checksum'],
|
||||
)));
|
||||
exit();
|
||||
}
|
||||
} else {
|
||||
$element = $name;
|
||||
$property = $vcard->select($name);
|
||||
if(count($property) === 0) {
|
||||
$property = VObject\Property::create($name);
|
||||
$vcard->add($property);
|
||||
} else {
|
||||
$property = array_shift($property);
|
||||
}
|
||||
}
|
||||
|
||||
/* preprocessing value */
|
||||
switch($element) {
|
||||
case 'BDAY':
|
||||
$date = New \DateTime($value);
|
||||
$value = $date->format('Y-m-d');
|
||||
break;
|
||||
case 'FN':
|
||||
if(!$value) {
|
||||
// create a method thats returns an alternative for FN.
|
||||
//$value = getOtherValue();
|
||||
}
|
||||
break;
|
||||
case 'NOTE':
|
||||
$value = str_replace('\n', '\\n', $value);
|
||||
break;
|
||||
case 'EMAIL':
|
||||
$value = strtolower($value);
|
||||
break;
|
||||
case 'IMPP':
|
||||
if(is_null($parameters) || !isset($parameters['X-SERVICE-TYPE'])) {
|
||||
bailOut(App::$l10n->t('Missing IM parameter.'));
|
||||
}
|
||||
$impp = App::getIMOptions($parameters['X-SERVICE-TYPE']);
|
||||
if(is_null($impp)) {
|
||||
bailOut(App::$l10n->t('Unknown IM: '.$parameters['X-SERVICE-TYPE']));
|
||||
}
|
||||
$value = $impp['protocol'] . ':' . $value;
|
||||
break;
|
||||
}
|
||||
|
||||
// If empty remove the property
|
||||
if(!$value) {
|
||||
if(in_array($name, $multi_properties)) {
|
||||
unset($vcard->children[$line]);
|
||||
$checksum = '';
|
||||
} else {
|
||||
unset($vcard->{$name});
|
||||
}
|
||||
} else {
|
||||
/* setting value */
|
||||
switch($element) {
|
||||
case 'BDAY':
|
||||
$vcard->BDAY = $value;
|
||||
|
||||
if(!isset($vcard->BDAY['VALUE'])) {
|
||||
$vcard->BDAY->add('VALUE', 'DATE');
|
||||
} else {
|
||||
$vcard->BDAY->VALUE = 'DATE';
|
||||
}
|
||||
break;
|
||||
case 'ADR':
|
||||
case 'N':
|
||||
if(is_array($value)) {
|
||||
$property->setParts($value);
|
||||
} else {
|
||||
debug('Saving N ' . $value);
|
||||
$vcard->N = $value;
|
||||
}
|
||||
break;
|
||||
case 'EMAIL':
|
||||
case 'TEL':
|
||||
case 'IMPP':
|
||||
case 'URL':
|
||||
debug('Setting element: (EMAIL/TEL/ADR)'.$element);
|
||||
$property->setValue($value);
|
||||
break;
|
||||
default:
|
||||
$vcard->{$name} = $value;
|
||||
break;
|
||||
}
|
||||
setParameters($property, $parameters, true);
|
||||
|
||||
// Do checksum and be happy
|
||||
if(in_array($name, $multi_properties)) {
|
||||
$checksum = substr(md5($property->serialize()), 0, 8);
|
||||
}
|
||||
}
|
||||
//debug('New checksum: '.$checksum);
|
||||
//$vcard->children[$line] = $property; ???
|
||||
try {
|
||||
VCard::edit($id, $vcard);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
|
||||
if(in_array($name, $multi_properties)) {
|
||||
\OCP\JSON::success(array('data' => array(
|
||||
'line' => $line,
|
||||
'checksum' => $checksum,
|
||||
'oldchecksum' => $_POST['checksum'],
|
||||
'lastmodified' => App::lastModified($vcard)->format('U'),
|
||||
)));
|
||||
} else {
|
||||
\OCP\JSON::success(array('data' => array(
|
||||
'lastmodified' => App::lastModified($vcard)->format('U'),
|
||||
)));
|
||||
}
|
@ -20,22 +20,39 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts;
|
||||
|
||||
// Firefox and Konqueror tries to download application/json for me. --Arthur
|
||||
OCP\JSON::setContentTypeHeader('text/plain');
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
\OCP\JSON::setContentTypeHeader('text/plain');
|
||||
\OCP\JSON::checkLoggedIn();
|
||||
\OCP\JSON::checkAppEnabled('contacts');
|
||||
require_once 'loghandler.php';
|
||||
|
||||
if (!isset($_GET['id'])) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No contact ID was submitted.'));
|
||||
$contactid = isset($_GET['contactid']) ? $_GET['contactid'] : '';
|
||||
$addressbookid = isset($_GET['addressbookid']) ? $_GET['addressbookid'] : '';
|
||||
$backend = isset($_GET['backend']) ? $_GET['backend'] : '';
|
||||
|
||||
if(!$contactid) {
|
||||
bailOut('Missing contact id.');
|
||||
}
|
||||
|
||||
$contact = OCA\Contacts\App::getContactVCard($_GET['id']);
|
||||
if(!$addressbookid) {
|
||||
bailOut('Missing address book id.');
|
||||
}
|
||||
|
||||
$app = new App();
|
||||
// FIXME: Get backend and addressbookid
|
||||
$contact = $app->getContact($backend, $addressbookid, $contactid);
|
||||
if(!$contact) {
|
||||
\OC_Cache::remove($tmpkey);
|
||||
bailOut(App::$l10n
|
||||
->t('Error getting contact object.'));
|
||||
}
|
||||
// invalid vcard
|
||||
if( is_null($contact)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error reading contact photo.'));
|
||||
if(!$contact) {
|
||||
bailOut(App::$l10n->t('Error reading contact photo.'));
|
||||
} else {
|
||||
$image = new OC_Image();
|
||||
$image = new \OC_Image();
|
||||
if(!isset($contact->PHOTO) || !$image->loadFromBase64((string)$contact->PHOTO)) {
|
||||
if(isset($contact->LOGO)) {
|
||||
$image->loadFromBase64((string)$contact->LOGO);
|
||||
@ -43,13 +60,13 @@ if( is_null($contact)) {
|
||||
}
|
||||
if($image->valid()) {
|
||||
$tmpkey = 'contact-photo-'.$contact->UID;
|
||||
if(OC_Cache::set($tmpkey, $image->data(), 600)) {
|
||||
OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
|
||||
if(\OC_Cache::set($tmpkey, $image->data(), 600)) {
|
||||
\OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
|
||||
exit();
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error saving temporary file.'));
|
||||
bailOut(App::$l10n->t('Error saving temporary file.'));
|
||||
}
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('The loading photo is not valid.'));
|
||||
bailOut(App::$l10n->t('The loading photo is not valid.'));
|
||||
}
|
||||
}
|
||||
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2011 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');
|
||||
|
||||
$id = $_GET['id'];
|
||||
$checksum = isset($_GET['checksum'])?$_GET['checksum']:'';
|
||||
$vcard = OCA\Contacts\App::getContactVCard($id);
|
||||
$adr_types = OCA\Contacts\App::getTypesOfProperty('ADR');
|
||||
|
||||
$tmpl = new OCP\Template("contacts", "part.edit_address_dialog");
|
||||
if($checksum) {
|
||||
$line = OCA\Contacts\App::getPropertyLineByChecksum($vcard, $checksum);
|
||||
$element = $vcard->children[$line];
|
||||
$adr = OCA\Contacts\VCard::structureProperty($element);
|
||||
$types = array();
|
||||
if(isset($adr['parameters']['TYPE'])) {
|
||||
if(is_array($adr['parameters']['TYPE'])) {
|
||||
$types = array_map('htmlspecialchars', $adr['parameters']['TYPE']);
|
||||
$types = array_map('strtoupper', $types);
|
||||
} else {
|
||||
$types = array(strtoupper(htmlspecialchars($adr['parameters']['TYPE'])));
|
||||
}
|
||||
}
|
||||
$tmpl->assign('types', $types, false);
|
||||
$adr = array_map('htmlspecialchars', $adr['value']);
|
||||
$tmpl->assign('adr', $adr, false);
|
||||
}
|
||||
|
||||
$tmpl->assign('id', $id);
|
||||
$tmpl->assign('adr_types', $adr_types);
|
||||
|
||||
$page = $tmpl->fetchPage();
|
||||
OCP\JSON::success(array('data' => array('page'=>$page, 'checksum'=>$checksum)));
|
@ -1,34 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2011 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');
|
||||
require_once 'loghandler.php';
|
||||
|
||||
$tmpl = new OCP\Template("contacts", "part.edit_name_dialog");
|
||||
|
||||
$id = isset($_GET['id'])?$_GET['id']:'';
|
||||
|
||||
if($id) {
|
||||
$vcard = OCA\Contacts\App::getContactVCard($id);
|
||||
$name = array('', '', '', '', '');
|
||||
if($vcard->__isset('N')) {
|
||||
$property = $vcard->__get('N');
|
||||
if($property) {
|
||||
$name = OCA\Contacts\VCard::structureProperty($property);
|
||||
}
|
||||
}
|
||||
$name = array_map('htmlspecialchars', $name['value']);
|
||||
$tmpl->assign('name', $name, false);
|
||||
$tmpl->assign('id', $id, false);
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Contact ID is missing.'));
|
||||
}
|
||||
$page = $tmpl->fetchPage();
|
||||
OCP\JSON::success(array('data' => array('page'=>$page)));
|
@ -1,15 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de>
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\App::checkAppEnabled('contacts');
|
||||
$tmpl = new OCP\Template('contacts', 'part.import');
|
||||
$tmpl->assign('path', $_POST['path']);
|
||||
$tmpl->assign('filename', $_POST['filename']);
|
||||
$tmpl->printpage();
|
@ -24,8 +24,8 @@ OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
require_once 'loghandler.php';
|
||||
|
||||
if(!isset($_GET['id'])) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No contact ID was submitted.'));
|
||||
if(!isset($_GET['contact'])) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('No contact info was submitted.'));
|
||||
}
|
||||
|
||||
if(!isset($_GET['path'])) {
|
||||
@ -33,7 +33,7 @@ if(!isset($_GET['path'])) {
|
||||
}
|
||||
|
||||
$localpath = \OC\Files\Filesystem::getLocalFile($_GET['path']);
|
||||
$tmpkey = 'contact-photo-'.$_GET['id'];
|
||||
$tmpkey = 'contact-photo-'.$_GET['contact']['contactid'];
|
||||
|
||||
if(!file_exists($localpath)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('File doesn\'t exist:').$localpath);
|
||||
@ -55,7 +55,7 @@ if(!$image->fixOrientation()) { // No fatal error so we don't bail out.
|
||||
OCP\Util::DEBUG);
|
||||
}
|
||||
if(OC_Cache::set($tmpkey, $image->data(), 600)) {
|
||||
OCP\JSON::success(array('data' => array('id'=>$_GET['id'], 'tmp'=>$tmpkey)));
|
||||
OCP\JSON::success(array('data' => array('id'=>$_GET['contact']['contactid'], 'tmp'=>$tmpkey)));
|
||||
exit();
|
||||
} else {
|
||||
bailOut('Couldn\'t save temporary image: '.$tmpkey);
|
||||
|
@ -3,7 +3,7 @@
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
|
||||
* @copyright 2012-13 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
|
||||
@ -19,13 +19,13 @@
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
// Check if we are a user
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
|
||||
// Firefox and Konqueror tries to download application/json for me. --Arthur
|
||||
OCP\JSON::setContentTypeHeader('text/plain; charset=utf-8');
|
||||
namespace OCA\Contacts;
|
||||
|
||||
// Check if we are a user
|
||||
\OCP\JSON::checkLoggedIn();
|
||||
\OCP\JSON::checkAppEnabled('contacts');
|
||||
\OCP\JSON::callCheck();
|
||||
|
||||
require_once 'loghandler.php';
|
||||
|
||||
@ -38,92 +38,101 @@ $y1 = (isset($_POST['y1']) && $_POST['y1']) ? $_POST['y1'] : 0;
|
||||
$w = (isset($_POST['w']) && $_POST['w']) ? $_POST['w'] : -1;
|
||||
$h = (isset($_POST['h']) && $_POST['h']) ? $_POST['h'] : -1;
|
||||
$tmpkey = isset($_POST['tmpkey']) ? $_POST['tmpkey'] : '';
|
||||
$id = isset($_POST['id']) ? $_POST['id'] : '';
|
||||
|
||||
$contactid = isset($_POST['contactid']) ? $_POST['contactid'] : '';
|
||||
$addressbookid = isset($_POST['addressbookid']) ? $_POST['addressbookid'] : '';
|
||||
$backend = isset($_POST['backend']) ? $_POST['backend'] : '';
|
||||
|
||||
if($tmpkey == '') {
|
||||
bailOut('Missing key to temporary file.');
|
||||
}
|
||||
|
||||
if($id == '') {
|
||||
if($contactid == '') {
|
||||
bailOut('Missing contact id.');
|
||||
}
|
||||
|
||||
OCP\Util::writeLog('contacts', 'savecrop.php: key: '.$tmpkey, OCP\Util::DEBUG);
|
||||
if($addressbookid == '') {
|
||||
bailOut('Missing address book id.');
|
||||
}
|
||||
|
||||
$data = OC_Cache::get($tmpkey);
|
||||
\OCP\Util::writeLog('contacts', 'savecrop.php: key: '.$tmpkey, \OCP\Util::DEBUG);
|
||||
|
||||
$app = new App();
|
||||
// FIXME: Get backend and addressbookid
|
||||
$contact = $app->getContact($backend, $addressbookid, $contactid);
|
||||
if(!$contact) {
|
||||
\OC_Cache::remove($tmpkey);
|
||||
bailOut(App::$l10n
|
||||
->t('Error getting contact object.'));
|
||||
}
|
||||
|
||||
$data = \OC_Cache::get($tmpkey);
|
||||
if($data) {
|
||||
$image = new OC_Image();
|
||||
$image = new \OC_Image();
|
||||
if($image->loadFromData($data)) {
|
||||
$w = ($w != -1 ? $w : $image->width());
|
||||
$h = ($h != -1 ? $h : $image->height());
|
||||
OCP\Util::writeLog('contacts',
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'savecrop.php, x: '.$x1.' y: '.$y1.' w: '.$w.' h: '.$h,
|
||||
OCP\Util::DEBUG);
|
||||
\OCP\Util::DEBUG);
|
||||
if($image->crop($x1, $y1, $w, $h)) {
|
||||
if(($image->width() <= 200 && $image->height() <= 200)
|
||||
|| $image->resize(200)) {
|
||||
|
||||
$vcard = OCA\Contacts\App::getContactVCard($id);
|
||||
if(!$vcard) {
|
||||
OC_Cache::remove($tmpkey);
|
||||
bailOut(OCA\Contacts\App::$l10n
|
||||
->t('Error getting contact object.'));
|
||||
}
|
||||
if($vcard->__isset('PHOTO')) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
// 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 = strval($contact->VERSION) === '4.0'
|
||||
? $image->mimeType()
|
||||
: strtoupper(array_pop(explode('/', $image->mimeType())));
|
||||
if(isset($contact->PHOTO)) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'savecrop.php: PHOTO property exists.',
|
||||
OCP\Util::DEBUG);
|
||||
$property = $vcard->__get('PHOTO');
|
||||
\OCP\Util::DEBUG);
|
||||
$property = $contact->PHOTO;
|
||||
if(!$property) {
|
||||
OC_Cache::remove($tmpkey);
|
||||
bailOut(OCA\Contacts\App::$l10n
|
||||
\OC_Cache::remove($tmpkey);
|
||||
bailOut(App::$l10n
|
||||
->t('Error getting PHOTO property.'));
|
||||
}
|
||||
$property->setValue($image->__toString());
|
||||
$property->setValue(strval($image));
|
||||
$property->parameters = [];
|
||||
/*$property->ENCODING = 'b';
|
||||
$property->TYPE = $type;*/
|
||||
$property->parameters[]
|
||||
= new Sabre\VObject\Parameter('ENCODING', 'b');
|
||||
= new \Sabre\VObject\Parameter('ENCODING', 'b');
|
||||
$property->parameters[]
|
||||
= new Sabre\VObject\Parameter('TYPE', $image->mimeType());
|
||||
$vcard->__set('PHOTO', $property);
|
||||
= new \Sabre\VObject\Parameter('TYPE', $image->mimeType());
|
||||
$contact->PHOTO = $property;
|
||||
} else {
|
||||
OCP\Util::writeLog('contacts',
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'savecrop.php: files: Adding PHOTO property.',
|
||||
OCP\Util::DEBUG);
|
||||
// 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 = $vcard->VERSION == '4.0'
|
||||
? $image->mimeType()
|
||||
: strtoupper(array_pop(explode('/', $image->mimeType())));
|
||||
$vcard->add('PHOTO',
|
||||
$image->__toString(), array('ENCODING' => 'b',
|
||||
\OCP\Util::DEBUG);
|
||||
$contact->add('PHOTO',
|
||||
strval($image), array('ENCODING' => 'b',
|
||||
'TYPE' => $type));
|
||||
}
|
||||
$now = new DateTime;
|
||||
$vcard->{'REV'} = $now->format(DateTime::W3C);
|
||||
if(!OCA\Contacts\VCard::edit($id, $vcard)) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error saving contact.'));
|
||||
if(!$contact->save()) {
|
||||
bailOut(App::$l10n->t('Error saving contact.'));
|
||||
}
|
||||
OCA\Contacts\App::cacheThumbnail($id, $image);
|
||||
OCP\JSON::success(array(
|
||||
$thumbnail = $contact->cacheThumbnail($image);
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'id' => $id,
|
||||
'width' => $image->width(),
|
||||
'height' => $image->height(),
|
||||
'lastmodified' => OCA\Contacts\App::lastModified($vcard)->format('U')
|
||||
'id' => $contactid,
|
||||
'thumbnail' => $thumbnail,
|
||||
)
|
||||
));
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error resizing image'));
|
||||
bailOut(App::$l10n->t('Error resizing image'));
|
||||
}
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error cropping image'));
|
||||
bailOut(App::$l10n->t('Error cropping image'));
|
||||
}
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error creating temporary image'));
|
||||
bailOut(App::$l10n->t('Error creating temporary image'));
|
||||
}
|
||||
} else {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('Error finding image: ').$tmpkey);
|
||||
bailOut(App::$l10n->t('Error finding image: ').$tmpkey);
|
||||
}
|
||||
|
||||
OC_Cache::remove($tmpkey);
|
||||
\OC_Cache::remove($tmpkey);
|
||||
|
@ -32,57 +32,48 @@ $view = OCP\Files::getStorage('contacts');
|
||||
if(!$view->file_exists('imports')) {
|
||||
$view->mkdir('imports');
|
||||
}
|
||||
$tmpfile = md5(rand());
|
||||
|
||||
// If it is a Drag'n'Drop transfer it's handled here.
|
||||
$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
|
||||
$fn = strtr($fn, array('/' => '', "\\" => ''));
|
||||
if($fn) {
|
||||
if(OC\Files\Filesystem::isFileBlacklisted($fn)) {
|
||||
bailOut($l10n->t('Upload of blacklisted file:') . $fn);
|
||||
}
|
||||
if($view->file_put_contents('/imports/'.$fn, file_get_contents('php://input'))) {
|
||||
OCP\JSON::success(array('data' => array('file'=>$tmpfile, 'name'=>$fn)));
|
||||
exit();
|
||||
} else {
|
||||
bailOut($l10n->t('Error uploading contacts to storage.'));
|
||||
}
|
||||
}
|
||||
|
||||
// File input transfers are handled here
|
||||
if (!isset($_FILES['importfile'])) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
'ajax/uploadphoto.php: No file was uploaded. Unknown error.',
|
||||
OCP\Util::DEBUG);
|
||||
OCP\JSON::error(array('
|
||||
data' => array(
|
||||
'message' => 'No file was uploaded. Unknown error' )));
|
||||
exit();
|
||||
if (!isset($_FILES['file'])) {
|
||||
bailOut($l10n->t('No file was uploaded. Unknown error'));
|
||||
}
|
||||
$error = $_FILES['importfile']['error'];
|
||||
if($error !== UPLOAD_ERR_OK) {
|
||||
|
||||
$file=$_FILES['file'];
|
||||
|
||||
if($file['error'] !== UPLOAD_ERR_OK) {
|
||||
$errors = array(
|
||||
0=>$l10n->t("There is no error, the file uploaded with success"),
|
||||
1=>$l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini").ini_get('upload_max_filesize'),
|
||||
2=>$l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
|
||||
3=>$l10n->t("The uploaded file was only partially uploaded"),
|
||||
4=>$l10n->t("No file was uploaded"),
|
||||
6=>$l10n->t("Missing a temporary folder")
|
||||
UPLOAD_ERR_OK => $l10n->t("There is no error, the file uploaded with success"),
|
||||
UPLOAD_ERR_INI_SIZE => $l10n->t("The uploaded file exceeds the upload_max_filesize directive in php.ini")
|
||||
.ini_get('upload_max_filesize'),
|
||||
UPLOAD_ERR_FORM_SIZE => $l10n->t("The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"),
|
||||
UPLOAD_ERR_PARTIAL => $l10n->t("The uploaded file was only partially uploaded"),
|
||||
UPLOAD_ERR_NO_FILE => $l10n->t("No file was uploaded"),
|
||||
UPLOAD_ERR_NO_TMP_DIR => $l10n->t('Missing a temporary folder'),
|
||||
UPLOAD_ERR_CANT_WRITE => $l10n->t('Failed to write to disk'),
|
||||
);
|
||||
bailOut($errors[$error]);
|
||||
}
|
||||
$file=$_FILES['importfile'];
|
||||
|
||||
if(file_exists($file['tmp_name'])) {
|
||||
$filename = strtr($file['name'], array('/' => '', "\\" => ''));
|
||||
$maxUploadFilesize = OCP\Util::maxUploadFilesize('/');
|
||||
$maxHumanFilesize = OCP\Util::humanFileSize($maxUploadFilesize);
|
||||
|
||||
$totalSize = $file['size'];
|
||||
if ($maxUploadFilesize >= 0 and $totalSize > $maxUploadFilesize) {
|
||||
bailOut($l10n->t('Not enough storage available'));
|
||||
}
|
||||
|
||||
$tmpname = $file['tmp_name'];
|
||||
$filename = strtr($file['name'], array('/' => '', "\\" => ''));
|
||||
if(is_uploaded_file($tmpname)) {
|
||||
if(OC\Files\Filesystem::isFileBlacklisted($filename)) {
|
||||
bailOut($l10n->t('Upload of blacklisted file:') . $filename);
|
||||
}
|
||||
if($view->file_put_contents('/imports/'.$filename, file_get_contents($file['tmp_name']))) {
|
||||
OCP\JSON::success(array('data' => array('file'=>$filename, 'name'=>$filename)));
|
||||
if($view->file_put_contents('/imports/'.$filename, file_get_contents($tmpname))) {
|
||||
OCP\JSON::success(array('file'=>$filename));
|
||||
} else {
|
||||
bailOut($l10n->t('Error uploading contacts to storage.'));
|
||||
}
|
||||
} else {
|
||||
bailOut('Temporary file: \''.$file['tmp_name'].'\' has gone AWOL?');
|
||||
bailOut('Temporary file: \''.$tmpname.'\' has gone AWOL?');
|
||||
}
|
||||
|
||||
|
@ -29,13 +29,22 @@ OCP\JSON::callCheck();
|
||||
OCP\JSON::setContentTypeHeader('text/plain; charset=utf-8');
|
||||
require_once 'loghandler.php';
|
||||
$l10n = OCA\Contacts\App::$l10n;
|
||||
|
||||
$contactid = isset($_POST['contactid']) ? $_POST['contactid'] : '';
|
||||
$addressbookid = isset($_POST['addressbookid']) ? $_POST['addressbookid'] : '';
|
||||
$backend = isset($_POST['backend']) ? $_POST['backend'] : '';
|
||||
|
||||
if($contactid == '') {
|
||||
bailOut('Missing contact id.');
|
||||
}
|
||||
|
||||
if($addressbookid == '') {
|
||||
bailOut('Missing address book id.');
|
||||
}
|
||||
|
||||
// If it is a Drag'n'Drop transfer it's handled here.
|
||||
$fn = (isset($_SERVER['HTTP_X_FILE_NAME']) ? $_SERVER['HTTP_X_FILE_NAME'] : false);
|
||||
if ($fn) {
|
||||
if (!isset($_GET['id'])) {
|
||||
bailOut($l10n->t('No contact ID was submitted.'));
|
||||
}
|
||||
$id = $_GET['id'];
|
||||
$tmpkey = 'contact-photo-'.md5($fn);
|
||||
$data = file_get_contents('php://input');
|
||||
$image = new OC_Image();
|
||||
@ -50,10 +59,14 @@ if ($fn) {
|
||||
if(OC_Cache::set($tmpkey, $image->data(), 600)) {
|
||||
OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'mime'=>$_SERVER['CONTENT_TYPE'],
|
||||
'name'=>$fn,
|
||||
'id'=>$id,
|
||||
'tmp'=>$tmpkey)));
|
||||
'mime'=> $_SERVER['CONTENT_TYPE'],
|
||||
'name'=> $fn,
|
||||
'contactid'=> $id,
|
||||
'addressbookid'=> addressbookid,
|
||||
'backend'=> $backend,
|
||||
'tmp'=>$tmpkey
|
||||
))
|
||||
);
|
||||
exit();
|
||||
} else {
|
||||
bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey);
|
||||
@ -63,10 +76,6 @@ if ($fn) {
|
||||
}
|
||||
}
|
||||
|
||||
// Uploads from file dialog are handled here.
|
||||
if (!isset($_POST['id'])) {
|
||||
bailOut($l10n->t('No contact ID was submitted.'));
|
||||
}
|
||||
if (!isset($_FILES['imagefile'])) {
|
||||
bailOut($l10n->t('No file was uploaded. Unknown error'));
|
||||
}
|
||||
@ -101,9 +110,14 @@ if(file_exists($file['tmp_name'])) {
|
||||
'mime'=>$file['type'],
|
||||
'size'=>$file['size'],
|
||||
'name'=>$file['name'],
|
||||
'id'=>$_POST['id'],
|
||||
'tmp'=>$tmpkey,
|
||||
)));
|
||||
),
|
||||
'metadata' => array(
|
||||
'contactid'=> $contactid,
|
||||
'addressbookid'=> $addressbookid,
|
||||
'backend'=> $backend,
|
||||
),
|
||||
));
|
||||
exit();
|
||||
} else {
|
||||
bailOut($l10n->t('Couldn\'t save temporary image: ').$tmpkey);
|
||||
|
@ -1,26 +1,32 @@
|
||||
<?php
|
||||
OC::$CLASSPATH['OCA\Contacts\App'] = 'contacts/lib/app.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Addressbook'] = 'contacts/lib/addressbook.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\VCard'] = 'contacts/lib/vcard.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Hooks'] = 'contacts/lib/hooks.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Contact'] = 'contacts/lib/share/contact.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Addressbook'] = 'contacts/lib/share/addressbook.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\AddressbookProvider'] = 'contacts/lib/addressbookprovider.php';
|
||||
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'contacts/lib/sabre/backend.php';
|
||||
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBookRoot'] = 'contacts/lib/sabre/addressbookroot.php';
|
||||
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_UserAddressBooks'] = 'contacts/lib/sabre/useraddressbooks.php';
|
||||
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_AddressBook'] = 'contacts/lib/sabre/addressbook.php';
|
||||
OC::$CLASSPATH['OC_Connector_Sabre_CardDAV_Card'] = 'contacts/lib/sabre/card.php';
|
||||
OC::$CLASSPATH['OCA\\Contacts\\SearchProvider'] = 'contacts/lib/search.php';
|
||||
OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Contacts\Hooks', 'createUser');
|
||||
OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Contacts\Hooks', 'deleteUser');
|
||||
//require_once __DIR__ . '/../lib/contact.php';
|
||||
Sabre\VObject\Component::$classMap['VCARD'] = 'OCA\Contacts\Contact';
|
||||
Sabre\VObject\Property::$classMap['FN'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['TITLE'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['ROLE'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['NOTE'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['NICKNAME'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['EMAIL'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['TEL'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['IMPP'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['URL'] = 'OC\VObject\StringProperty';
|
||||
Sabre\VObject\Property::$classMap['GEO'] = 'Sabre\VObject\Property\Compound';
|
||||
|
||||
OCP\Util::connectHook('OC_User', 'post_createUser', 'OCA\Contacts\Hooks', 'userCreated');
|
||||
OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OCA\Contacts\Hooks', 'userDeleted');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'pre_deleteAddressBook', 'OCA\Contacts\Hooks', 'addressBookDeletion');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'pre_deleteContact', 'OCA\Contacts\Hooks', 'contactDeletion');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'post_createContact', 'OCA\Contacts\Hooks', 'contactUpdated');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'post_updateContact', 'OCA\Contacts\Hooks', 'contactUpdated');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'scanCategories', 'OCA\Contacts\Hooks', 'scanCategories');
|
||||
OCP\Util::connectHook('OCA\Contacts', 'indexProperties', 'OCA\Contacts\Hooks', 'indexProperties');
|
||||
OCP\Util::connectHook('OC_Calendar', 'getEvents', 'OCA\Contacts\Hooks', 'getBirthdayEvents');
|
||||
OCP\Util::connectHook('OC_Calendar', 'getSources', 'OCA\Contacts\Hooks', 'getCalenderSources');
|
||||
|
||||
OCP\App::addNavigationEntry( array(
|
||||
'id' => 'contacts_index',
|
||||
'order' => 10,
|
||||
'href' => OCP\Util::linkTo( 'contacts', 'index.php' ),
|
||||
'href' => \OC_Helper::linkToRoute('contacts_index'),
|
||||
'icon' => OCP\Util::imagePath( 'contacts', 'contacts.svg' ),
|
||||
'name' => OC_L10N::get('contacts')->t('Contacts') ));
|
||||
|
||||
@ -30,8 +36,9 @@ OC_Search::registerProvider('OCA\Contacts\SearchProvider');
|
||||
OCP\Share::registerBackend('contact', 'OCA\Contacts\Share_Backend_Contact');
|
||||
OCP\Share::registerBackend('addressbook', 'OCA\Contacts\Share_Backend_Addressbook', 'contact');
|
||||
|
||||
/*
|
||||
if(OCP\User::isLoggedIn()) {
|
||||
foreach(OCA\Contacts\Addressbook::all(OCP\USER::getUser()) as $addressbook) {
|
||||
OCP\Contacts::registerAddressBook(new OCA\Contacts\AddressbookProvider($addressbook['id']));
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
28
appinfo/classpath.php
Normal file
28
appinfo/classpath.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
OC::$CLASSPATH['OCA\Contacts\App'] = 'contacts/lib/app.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\AddressBook'] = 'contacts/lib/addressbook.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Contact'] = 'contacts/lib/contact.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\AddressbookLegacy'] = 'contacts/lib/addressbooklegacy.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\IPIMObject'] = 'contacts/lib/ipimobject.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\PIMCollectionAbstract'] = 'contacts/lib/abstractpimcollection.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\PIMObjectAbstract'] = 'contacts/lib/abstractpimobject.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\VCard'] = 'contacts/lib/vcard.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Hooks'] = 'contacts/lib/hooks.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Request'] = 'contacts/lib/request.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Utils\JSONSerializer'] = 'contacts/lib/utils/jsonserializer.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Utils\Properties'] = 'contacts/lib/utils/properties.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Backend\AbstractBackend'] = 'contacts/lib/backend/abstractbackend.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Backend\Database'] = 'contacts/lib/backend/database.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Backend\Shared'] = 'contacts/lib/backend/shared.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Contact'] = 'contacts/lib/share/contact.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\Share_Backend_Addressbook'] = 'contacts/lib/share/addressbook.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\AddressbookProvider'] = 'contacts/lib/addressbookprovider.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\VObject\VCard'] = 'contacts/lib/vobject/vcard.php';
|
||||
//OC::$CLASSPATH['OCA\Contacts\VObject\StringProperty'] = 'contacts/lib/vobject/stringproperty.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\Backend'] = 'contacts/lib/carddav/backend.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\Plugin'] = 'contacts/lib/carddav/plugin.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\AddressBookRoot'] = 'contacts/lib/carddav/addressbookroot.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\UserAddressBooks'] = 'contacts/lib/carddav/useraddressbooks.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\AddressBook'] = 'contacts/lib/carddav/addressbook.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\CardDAV\Card'] = 'contacts/lib/carddav/card.php';
|
||||
OC::$CLASSPATH['OCA\Contacts\SearchProvider'] = 'contacts/lib/search.php';
|
@ -4,9 +4,121 @@
|
||||
<name>*dbname*</name>
|
||||
<create>true</create>
|
||||
<overwrite>false</overwrite>
|
||||
|
||||
<charset>utf8</charset>
|
||||
<!--
|
||||
<table>
|
||||
<name>*dbprefix*addressbook</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>backend</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>addressbookid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>userid</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>displayname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>visibility</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<length>1</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>ctag</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>order</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*addressbook_backend</name>
|
||||
|
||||
<declaration>
|
||||
|
||||
<field>
|
||||
<name>id</name>
|
||||
<type>integer</type>
|
||||
<default>0</default>
|
||||
<notnull>true</notnull>
|
||||
<autoincrement>1</autoincrement>
|
||||
<unsigned>true</unsigned>
|
||||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>backendname</name>
|
||||
<type>text</type>
|
||||
<default></default>
|
||||
<notnull>true</notnull>
|
||||
<length>255</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>enabled</name>
|
||||
<type>integer</type>
|
||||
<default>1</default>
|
||||
<notnull>true</notnull>
|
||||
<length>1</length>
|
||||
</field>
|
||||
|
||||
</declaration>
|
||||
|
||||
</table>
|
||||
-->
|
||||
<table>
|
||||
|
||||
<name>*dbprefix*contacts_addressbooks</name>
|
||||
|
@ -1,5 +1,7 @@
|
||||
<?php
|
||||
class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
|
||||
namespace OCA\Contacts;
|
||||
|
||||
class MigrationProvider extends OC_Migration_Provider{
|
||||
|
||||
// Create the xml for the user supplied
|
||||
function export( ) {
|
||||
@ -21,26 +23,31 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
|
||||
$ids2 = $this->content->copyRows( $options );
|
||||
|
||||
// If both returned some ids then they worked
|
||||
if(is_array($ids) && is_array($ids2)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (is_array($ids) && is_array($ids2));
|
||||
|
||||
}
|
||||
|
||||
// Import function for contacts
|
||||
function import( ) {
|
||||
switch( $this->appinfo->version ) {
|
||||
function import() {
|
||||
switch($this->appinfo->version) {
|
||||
default:
|
||||
// All versions of the app have had the same db structure, so all can use the same import function
|
||||
$query = $this->content->prepare( 'SELECT * FROM contacts_addressbooks WHERE userid = ?' );
|
||||
$results = $query->execute( array( $this->olduid ) );
|
||||
$query = $this->content->prepare('SELECT * FROM `contacts_addressbooks` WHERE `userid` = ?');
|
||||
$results = $query->execute(array($this->olduid));
|
||||
$idmap = array();
|
||||
while( $row = $results->fetchRow() ) {
|
||||
while($row = $results->fetchRow()) {
|
||||
// Import each addressbook
|
||||
$addressbookquery = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)' );
|
||||
$addressbookquery->execute( array( $this->uid, $row['displayname'], $row['uri'], $row['description'], $row['ctag'] ) );
|
||||
$addressbookquery = OCP\DB::prepare('INSERT INTO `*PREFIX*contacts_addressbooks` '
|
||||
. '(`userid`, `displayname`, `uri`, `description`, `ctag`) VALUES (?, ?, ?, ?, ?)');
|
||||
$addressbookquery->execute(
|
||||
array(
|
||||
$this->uid,
|
||||
$row['displayname'],
|
||||
$row['uri'],
|
||||
$row['description'],
|
||||
$row['ctag']
|
||||
)
|
||||
);
|
||||
// Map the id
|
||||
$idmap[$row['id']] = OCP\DB::insertid('*PREFIX*contacts_addressbooks');
|
||||
// Make the addressbook active
|
||||
@ -49,12 +56,21 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
|
||||
// Now tags
|
||||
foreach($idmap as $oldid => $newid) {
|
||||
|
||||
$query = $this->content->prepare( 'SELECT * FROM contacts_cards WHERE addressbookid = ?' );
|
||||
$results = $query->execute( array( $oldid ) );
|
||||
while( $row = $results->fetchRow() ) {
|
||||
$query = $this->content->prepare('SELECT * FROM `contacts_cards` WHERE `addressbookid` = ?');
|
||||
$results = $query->execute(array($oldid));
|
||||
while($row = $results->fetchRow()) {
|
||||
// Import the contacts
|
||||
$contactquery = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_cards` (`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)' );
|
||||
$contactquery->execute( array( $newid, $row['fullname'], $row['carddata'], $row['uri'], $row['lastmodified'] ) );
|
||||
$contactquery = OCP\DB::prepare('INSERT INTO `*PREFIX*contacts_cards` '
|
||||
. '(`addressbookid`, `fullname`, `carddata`, `uri`, `lastmodified`) VALUES (?, ?, ?, ?, ?)');
|
||||
$contactquery->execute(
|
||||
array(
|
||||
$newid,
|
||||
$row['fullname'],
|
||||
$row['carddata'],
|
||||
$row['uri'],
|
||||
$row['lastmodified']
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
// All done!
|
||||
@ -67,4 +83,4 @@ class OC_Migration_Provider_Contacts extends OC_Migration_Provider{
|
||||
}
|
||||
|
||||
// Load the provider
|
||||
new OC_Migration_Provider_Contacts( 'contacts' );
|
||||
new MigrationProvider('contacts');
|
||||
|
@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
OCP\App::checkAppEnabled('contacts');
|
||||
require_once __DIR__ . '/classpath.php';
|
||||
|
||||
if(substr(OCP\Util::getRequestUri(), 0, strlen(OC_App::getAppWebPath('contacts').'/carddav.php')) == OC_App::getAppWebPath('contacts').'/carddav.php') {
|
||||
$baseuri = OC_App::getAppWebPath('contacts').'/carddav.php';
|
||||
@ -33,15 +34,19 @@ OC_App::loadApps($RUNTIME_APPTYPES);
|
||||
// Backends
|
||||
$authBackend = new OC_Connector_Sabre_Auth();
|
||||
$principalBackend = new OC_Connector_Sabre_Principal();
|
||||
$carddavBackend = new OC_Connector_Sabre_CardDAV();
|
||||
|
||||
$addressbookbackends = array();
|
||||
$addressbookbackends[] = new OCA\Contacts\Backend\Shared();
|
||||
$addressbookbackends[] = new OCA\Contacts\Backend\Database();
|
||||
$carddavBackend = new OCA\Contacts\CardDAV\Backend($addressbookbackends);
|
||||
$requestBackend = new OC_Connector_Sabre_Request();
|
||||
|
||||
// Root nodes
|
||||
$principalCollection = new Sabre_CalDAV_Principal_Collection($principalBackend);
|
||||
$principalCollection->disableListing = true; // Disable listening
|
||||
$principalCollection->disableListing = true; // Disable listing
|
||||
|
||||
$addressBookRoot = new OC_Connector_Sabre_CardDAV_AddressBookRoot($principalBackend, $carddavBackend);
|
||||
$addressBookRoot->disableListing = true; // Disable listening
|
||||
$addressBookRoot = new OCA\Contacts\CardDAV\AddressBookRoot($principalBackend, $carddavBackend);
|
||||
$addressBookRoot->disableListing = true; // Disable listing
|
||||
|
||||
$nodes = array(
|
||||
$principalCollection,
|
||||
@ -54,10 +59,13 @@ $server->httpRequest = $requestBackend;
|
||||
$server->setBaseUri($baseuri);
|
||||
// Add plugins
|
||||
$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend, 'ownCloud'));
|
||||
$server->addPlugin(new Sabre_CardDAV_Plugin());
|
||||
$server->addPlugin(new OCA\Contacts\CardDAV\Plugin());
|
||||
$server->addPlugin(new Sabre_DAVACL_Plugin());
|
||||
$server->addPlugin(new Sabre_DAV_Browser_Plugin(false)); // Show something in the Browser, but no upload
|
||||
$server->addPlugin(new Sabre_CardDAV_VCFExportPlugin());
|
||||
|
||||
if(defined('DEBUG') && DEBUG) {
|
||||
$server->debugExceptions = true;
|
||||
}
|
||||
// And off we go!
|
||||
$server->exec();
|
||||
|
532
appinfo/routes.php
Normal file
532
appinfo/routes.php
Normal file
@ -0,0 +1,532 @@
|
||||
<?php
|
||||
/**
|
||||
* @author Thomas Tanghus
|
||||
* Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net)
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
namespace OCA\Contacts;
|
||||
|
||||
require_once __DIR__.'/../ajax/loghandler.php';
|
||||
|
||||
//define the routes
|
||||
//for the index
|
||||
$this->create('contacts_index', '/')
|
||||
->actionInclude('contacts/index.php');
|
||||
// ->action(
|
||||
// function($params){
|
||||
// //
|
||||
// }
|
||||
// );
|
||||
|
||||
$this->create('contacts_jsconfig', 'ajax/config.js')
|
||||
->actionInclude('contacts/js/config.php');
|
||||
|
||||
/* TODO:
|
||||
- Check what it requires to be a RESTful API. I think maybe {user}
|
||||
shouldn't be in the URI but be authenticated in headers or elsewhere.
|
||||
- Do security checks: logged in, csrf
|
||||
- Move the actual code to controllers.
|
||||
*/
|
||||
$this->create('contacts_address_books_for_user', 'addressbooks/{user}/')
|
||||
->get()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$addressBooks = $app->getAddressBooksForUser();
|
||||
$response = array();
|
||||
foreach($addressBooks as $addressBook) {
|
||||
$response[] = $addressBook->getMetaData();
|
||||
}
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'addressbooks' => $response,
|
||||
)
|
||||
));
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_address_book_collection', 'addressbook/{user}/{backend}/{addressbookid}/contacts')
|
||||
->get()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$addressBook = $app->getAddressBook($params['backend'], $params['addressbookid']);
|
||||
$lastModified = $addressBook->lastModified();
|
||||
if(!is_null($lastModified)) {
|
||||
\OCP\Response::enableCaching();
|
||||
\OCP\Response::setLastModifiedHeader($lastModified);
|
||||
\OCP\Response::setETagHeader(md5($lastModified));
|
||||
}
|
||||
$contacts = array();
|
||||
foreach($addressBook->getChildren() as $contact) {
|
||||
//$contact->retrieve();
|
||||
//error_log(__METHOD__.' jsondata: '.print_r($contact, true));
|
||||
$response = Utils\JSONSerializer::serializeContact($contact);
|
||||
if($response !== null) {
|
||||
$contacts[] = $response;
|
||||
}
|
||||
}
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'contacts' => $contacts,
|
||||
)
|
||||
));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbookid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_address_book_add', 'addressbook/{user}/{backend}/add')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$backend = App::getBackend('local', $params['user']);
|
||||
$id = $backend->createAddressBook($_POST);
|
||||
if($id === false) {
|
||||
bailOut(App::$l10n->t('Error creating address book'));
|
||||
}
|
||||
\OCP\JSON::success(array(
|
||||
'data' => $backend->getAddressBook($id)
|
||||
));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbookid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_address_book_delete', 'addressbook/{user}/{backend}/{addressbookid}/delete')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$backend = App::getBackend('local', $params['user']);
|
||||
if(!$backend->deleteAddressBook($params['addressbookid'])) {
|
||||
bailOut(App::$l10n->t('Error deleting address book'));
|
||||
}
|
||||
\OCP\JSON::success();
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbookid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_address_book_add_contact', 'addressbook/{user}/{backend}/{addressbookid}/contact/add')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$addressBook = $app->getAddressBook($params['backend'], $params['addressbookid']);
|
||||
$id = $addressBook->addChild();
|
||||
if($id === false) {
|
||||
bailOut(App::$l10n->t('Error creating contact.'));
|
||||
}
|
||||
$contact = $addressBook->getChild($id);
|
||||
\OCP\JSON::success(array(
|
||||
'data' => Utils\JSONSerializer::serializeContact($contact),
|
||||
));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbookid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_address_book_delete_contact', 'addressbook/{user}/{backend}/{addressbookid}/contact/{contactid}/delete')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$app = new App($params['user']);
|
||||
$addressBook = $app->getAddressBook($params['backend'], $params['addressbookid']);
|
||||
$response = $addressBook->deleteChild($params['contactid']);
|
||||
if($response === false) {
|
||||
bailOut(App::$l10n->t('Error deleting contact.'));
|
||||
}
|
||||
\OCP\JSON::success();
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbookid', 'contactid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_contact_photo', 'addressbook/{user}/{backend}/{addressbookid}/contact/{contactid}/photo')
|
||||
->get()
|
||||
->action(
|
||||
function($params) {
|
||||
// TODO: Cache resized photo
|
||||
session_write_close();
|
||||
$etag = null;
|
||||
$caching = null;
|
||||
$max_size = 170;
|
||||
$app = new App();
|
||||
$contact = $app->getContact($params['backend'], $params['addressbookid'], $params['contactid']);
|
||||
$image = new \OC_Image();
|
||||
if (isset($contact->PHOTO) && $image->loadFromBase64((string)$contact->PHOTO)) {
|
||||
// OK
|
||||
$etag = md5($contact->PHOTO);
|
||||
}
|
||||
else
|
||||
// Logo :-/
|
||||
if (isset($contact->LOGO) && $image->loadFromBase64((string)$contact->LOGO)) {
|
||||
// OK
|
||||
$etag = md5($contact->LOGO);
|
||||
}
|
||||
if ($image->valid()) {
|
||||
$modified = $contact->lastModified();
|
||||
// Force refresh if modified within the last minute.
|
||||
if(!is_null($modified)) {
|
||||
$caching = (time() - $modified > 60) ? null : 0;
|
||||
}
|
||||
\OCP\Response::enableCaching($caching);
|
||||
if(!is_null($modified)) {
|
||||
\OCP\Response::setLastModifiedHeader($modified);
|
||||
}
|
||||
if($etag) {
|
||||
\OCP\Response::setETagHeader($etag);
|
||||
}
|
||||
if ($image->width() > $max_size || $image->height() > $max_size) {
|
||||
$image->resize($max_size);
|
||||
}
|
||||
header('Content-Type: ' . $image->mimeType());
|
||||
$image->show();
|
||||
}
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbook', 'contactid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_contact_delete_property', 'addressbook/{user}/{backend}/{addressbookid}/contact/{contactid}/property/delete')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$name = $request->post['name'];
|
||||
$checksum = isset($request->post['checksum']) ? $request->post['checksum'] : null;
|
||||
|
||||
debug('contacts_contact_delete_property, name: ' . print_r($name, true));
|
||||
debug('contacts_contact_delete_property, checksum: ' . print_r($checksum, true));
|
||||
|
||||
$app = new App($request->parameters['user']);
|
||||
$contact = $app->getContact(
|
||||
$request->parameters['backend'],
|
||||
$request->parameters['addressbookid'],
|
||||
$request->parameters['contactid']
|
||||
);
|
||||
|
||||
if(!$contact) {
|
||||
bailOut(App::$l10n->t('Couldn\'t find contact.'));
|
||||
}
|
||||
if(!$name) {
|
||||
bailOut(App::$l10n->t('Property name is not set.'));
|
||||
}
|
||||
if(!$checksum && in_array($name, Utils\Properties::$multi_properties)) {
|
||||
bailOut(App::$l10n->t('Property checksum is not set.'));
|
||||
}
|
||||
if(!is_null($checksum)) {
|
||||
try {
|
||||
$contact->unsetPropertyByChecksum($checksum);
|
||||
} catch(Exception $e) {
|
||||
bailOut(App::$l10n->t('Information about vCard is incorrect. Please reload the page.'));
|
||||
}
|
||||
} else {
|
||||
unset($contact->{$name});
|
||||
}
|
||||
if(!$contact->save()) {
|
||||
bailOut(App::$l10n->t('Error saving contact to backend.'));
|
||||
}
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'backend' => $request->parameters['backend'],
|
||||
'addressbookid' => $request->parameters['addressbookid'],
|
||||
'contactid' => $request->parameters['contactid'],
|
||||
'lastmodified' => $contact->lastModified(),
|
||||
)
|
||||
));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbook', 'contactid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
// Save a single property.
|
||||
$this->create('contacts_contact_save_property', 'addressbook/{user}/{backend}/{addressbookid}/contact/{contactid}/property/save')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
// TODO: When value is empty unset the property and return a checksum of 'new' if multi_property
|
||||
$name = $request->post['name'];
|
||||
$value = $request->post['value'];
|
||||
$parameters = isset($request->post['parameters']) ? $request->post['parameters'] : null;
|
||||
$checksum = isset($request->post['checksum']) ? $request->post['checksum'] : null;
|
||||
|
||||
debug('contacts_contact_save_property, name: ' . print_r($name, true));
|
||||
debug('contacts_contact_save_property, value: ' . print_r($value, true));
|
||||
debug('contacts_contact_save_property, parameters: ' . print_r($parameters, true));
|
||||
debug('contacts_contact_save_property, checksum: ' . print_r($checksum, true));
|
||||
|
||||
$app = new App($params['user']);
|
||||
$contact = $app->getContact($params['backend'], $params['addressbookid'], $params['contactid']);
|
||||
|
||||
$response = array('contactid' => $params['contactid']);
|
||||
|
||||
if(!$contact) {
|
||||
bailOut(App::$l10n->t('Couldn\'t find contact.'));
|
||||
}
|
||||
if(!$name) {
|
||||
bailOut(App::$l10n->t('Property name is not set.'));
|
||||
}
|
||||
if(is_array($value)) {
|
||||
// NOTE: Important, otherwise the compound value will be
|
||||
// set in the order the fields appear in the form!
|
||||
ksort($value);
|
||||
}
|
||||
if(!$checksum && in_array($name, Utils\Properties::$multi_properties)) {
|
||||
bailOut(App::$l10n->t('Property checksum is not set.'));
|
||||
} elseif($checksum && in_array($name, Utils\Properties::$multi_properties)) {
|
||||
try {
|
||||
$checksum = $contact->setPropertyByChecksum($checksum, $name, $value, $parameters);
|
||||
$response['checksum'] = $checksum;
|
||||
} catch(Exception $e) {
|
||||
bailOut(App::$l10n->t('Information about vCard is incorrect. Please reload the page.'));
|
||||
}
|
||||
} elseif(!in_array($name, Utils\Properties::$multi_properties)) {
|
||||
if(!$contact->setPropertyByName($name, $value, $parameters)) {
|
||||
bailOut(App::$l10n->t('Error setting property'));
|
||||
}
|
||||
}
|
||||
if(!$contact->save()) {
|
||||
bailOut(App::$l10n->t('Error saving property to backend'));
|
||||
}
|
||||
$response['lastmodified'] = $contact->lastModified();
|
||||
$contact->save();
|
||||
\OCP\JSON::success(array('data' => $response));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbook', 'contactid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
// Save all properties. Used for merging contacts.
|
||||
$this->create('contacts_contact_save_all', 'addressbook/{user}/{backend}/{addressbookid}/contact/{contactid}/save')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' params: '.print_r($request->parameters, true), \OCP\Util::DEBUG);
|
||||
|
||||
$app = new App($params['user']);
|
||||
$contact = $app->getContact($params['backend'], $params['addressbookid'], $params['contactid']);
|
||||
|
||||
$response = array('contactid' => $params['contactid']);
|
||||
|
||||
if(!$contact) {
|
||||
bailOut(App::$l10n->t('Couldn\'t find contact.'));
|
||||
}
|
||||
if(!$contact->mergeFromArray($request->params)) {
|
||||
bailOut(App::$l10n->t('Error merging into contact.'));
|
||||
}
|
||||
if(!$contact->save()) {
|
||||
bailOut(App::$l10n->t('Error saving contact to backend.'));
|
||||
}
|
||||
$data = Utils\JSONSerializer::serializeContact($contact);
|
||||
\OCP\JSON::success(array('data' => $data));
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'backend', 'addressbook', 'contactid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_categories_list', 'groups/{user}/')
|
||||
->get()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$catmgr = new \OC_VCategories('contact', $params['user']);
|
||||
$categories = $catmgr->categories(\OC_VCategories::FORMAT_MAP);
|
||||
foreach($categories as &$category) {
|
||||
$ids = $catmgr->idsForCategory($category['name']);
|
||||
$category['contacts'] = $ids;
|
||||
}
|
||||
|
||||
$favorites = $catmgr->getFavorites();
|
||||
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'categories' => $categories,
|
||||
'favorites' => $favorites,
|
||||
'shared' => \OCP\Share::getItemsSharedWith('addressbook', Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS),
|
||||
'lastgroup' => \OCP\Config::getUserValue(
|
||||
$params['user'],
|
||||
'contacts',
|
||||
'lastgroup', 'all'),
|
||||
'sortorder' => \OCP\Config::getUserValue(
|
||||
$params['user'],
|
||||
'contacts',
|
||||
'groupsort', ''),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_categories_add', 'groups/{user}/add')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$name = $request->post['name'];
|
||||
|
||||
if(is_null($name) || $name === "") {
|
||||
bailOut(App::$l10n->t('No group name given.'));
|
||||
}
|
||||
|
||||
$catman = new \OC_VCategories('contact', $params['user']);
|
||||
$id = $catman->add($name);
|
||||
|
||||
if($id !== false) {
|
||||
\OCP\JSON::success(array('data' => array('id'=>$id, 'name' => $name)));
|
||||
} else {
|
||||
bailOut(App::$l10n->t('Error adding group.'));
|
||||
}
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_categories_delete', 'groups/{user}/delete')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$name = $request->post['name'];
|
||||
|
||||
if(is_null($name) || $name === "") {
|
||||
bailOut(App::$l10n->t('No group name given.'));
|
||||
}
|
||||
|
||||
$catman = new \OC_VCategories('contact', $params['user']);
|
||||
$catman->delete($name);
|
||||
|
||||
\OCP\JSON::success();
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_categories_addto', 'groups/{user}/addto/{categoryid}')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$categoryid = $request['categoryid'];
|
||||
$ids = $request['contactids'];
|
||||
debug('request: '.print_r($request->post, true));
|
||||
|
||||
if(is_null($categoryid) || $categoryid === '') {
|
||||
bailOut(App::$l10n->t('Group ID missing from request.'));
|
||||
}
|
||||
|
||||
if(is_null($ids)) {
|
||||
bailOut(App::$l10n->t('Contact ID missing from request.'));
|
||||
}
|
||||
|
||||
$catman = new \OC_VCategories('contact', $params['user']);
|
||||
foreach($ids as $contactid) {
|
||||
debug('contactid: ' . $contactid . ', categoryid: ' . $categoryid);
|
||||
$catman->addToCategory($contactid, $categoryid);
|
||||
}
|
||||
|
||||
\OCP\JSON::success();
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'categoryid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_categories_removefrom', 'groups/{user}/removefrom/{categoryid}')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$categoryid = $request['categoryid'];
|
||||
$ids = $request['contactids'];
|
||||
debug('request: '.print_r($request->post, true));
|
||||
|
||||
if(is_null($categoryid) || $categoryid === '') {
|
||||
bailOut(App::$l10n->t('Group ID missing from request.'));
|
||||
}
|
||||
|
||||
if(is_null($ids)) {
|
||||
bailOut(App::$l10n->t('Contact ID missing from request.'));
|
||||
}
|
||||
|
||||
$catman = new \OC_VCategories('contact', $params['user']);
|
||||
foreach($ids as $contactid) {
|
||||
debug('contactid: ' . $contactid . ', categoryid: ' . $categoryid);
|
||||
$catman->removeFromCategory($contactid, $categoryid);
|
||||
}
|
||||
|
||||
\OCP\JSON::success();
|
||||
}
|
||||
)
|
||||
->requirements(array('user', 'categoryid'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_setpreference', 'preference/{user}/set')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
$request = Request::getRequest($params);
|
||||
$key = $request->post['key'];
|
||||
$value = $request->post['value'];
|
||||
|
||||
if(is_null($key) || $key === "") {
|
||||
bailOut(App::$l10n->t('No key is given.'));
|
||||
}
|
||||
|
||||
if(is_null($value) || $value === "") {
|
||||
bailOut(App::$l10n->t('No value is given.'));
|
||||
}
|
||||
|
||||
if(\OCP\Config::setUserValue($params['user'], 'contacts', $key, $value)) {
|
||||
\OCP\JSON::success(array(
|
||||
'data' => array(
|
||||
'key' => $key,
|
||||
'value' => $value)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
bailOut(App::$l10n->t(
|
||||
'Could not set preference: ' . $key . ':' . $value)
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
||||
|
||||
$this->create('contacts_index_properties', 'indexproperties/{user}/')
|
||||
->post()
|
||||
->action(
|
||||
function($params) {
|
||||
session_write_close();
|
||||
\OC_Hook::emit('OCA\Contacts', 'indexProperties', array());
|
||||
|
||||
\OCP\Config::setUserValue($params['user'], 'contacts', 'contacts_properties_indexed', 'yes');
|
||||
\OCP\JSON::success(array('isIndexed' => true));
|
||||
}
|
||||
)
|
||||
->requirements(array('user'))
|
||||
->defaults(array('user' => \OCP\User::getUser()));
|
@ -30,14 +30,14 @@
|
||||
top: -8px;
|
||||
left: -6px;
|
||||
}
|
||||
#content textarea { font-family: inherit; }
|
||||
#contact textarea { font-family: inherit; }
|
||||
|
||||
#content ::-moz-placeholder, #content input:-moz-placeholder, #content input[placeholder], #content input:placeholder, #content input:-ms-input-placeholder, #content input::-webkit-input-placeholder, #content input:-moz-placeholder {
|
||||
#contact ::-moz-placeholder, #contact input:-moz-placeholder, #contact input[placeholder], #contact input:placeholder, #contact input:-ms-input-placeholder, #contact input::-webkit-input-placeholder, #contact input:-moz-placeholder {
|
||||
color: #bbb;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#content input:not([type="checkbox"]), #content select:not(.button), #content textarea {
|
||||
#contact input:not([type="checkbox"]), #contact select:not(.button), #contact textarea {
|
||||
background-color: #fefefe; border: 1px solid #fff !important;
|
||||
-moz-appearance:none !important; -webkit-appearance: none !important;
|
||||
-moz-box-shadow: none; -webkit-box-shadow: none; box-shadow: none;
|
||||
@ -45,17 +45,17 @@
|
||||
-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
|
||||
float: left;
|
||||
}
|
||||
#content input[type="button"]:hover, #content select:hover, #content select:focus, #content select:active, #content input[type="button"]:focus, #content .button:hover, button:hover { background-color:#fff; color:#333; }
|
||||
#contact input[type="button"]:hover, #contact select:hover, #contact select:focus, #contact select:active, #contact input[type="button"]:focus, #contact .button:hover, button:hover { background-color:#fff; color:#333; }
|
||||
|
||||
#content fieldset, #content div, #content span, #content section {
|
||||
#contact fieldset, #contact div, #contact span, #contact section {
|
||||
-moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box;
|
||||
}
|
||||
#content fieldset.editor {
|
||||
#contact fieldset.editor {
|
||||
padding:4px;
|
||||
border: 1px solid #1d2d44;
|
||||
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px; outline:none;
|
||||
}
|
||||
#content input:invalid, #content input:hover:not([type="checkbox"]), #content input:active:not([type="checkbox"]), #content input:focus:not([type="checkbox"]), #content input.active, #content textarea:focus, #content textarea:hover {
|
||||
#contact input:invalid, #contact input:hover:not([type="checkbox"]), #contact input:active:not([type="checkbox"]), #contact input:focus:not([type="checkbox"]), #contact input.active, #contact textarea:focus, #contact textarea:hover {
|
||||
border: 1px solid silver !important;
|
||||
-moz-border-radius:.3em; -webkit-border-radius:.3em; border-radius:.3em;
|
||||
outline:none; float: left;
|
||||
@ -137,6 +137,7 @@
|
||||
#grouplist { z-index: 100; }
|
||||
#grouplist h3 .action:not(.starred):not(.checked):not(.favorite) { float: right; display: none; padding: 0; margin: auto; }
|
||||
#grouplist h3:not([data-type="shared"]):not(.editing):hover .action.numcontacts, #grouplist h3:not([data-type="shared"]):not(.editing) .active.action.numcontacts { display: inline-block; }
|
||||
.action.numcontacts { width: 6em; }
|
||||
#grouplist h3.active[data-type="category"]:not(.editing):hover .action.delete { display: inline-block; }
|
||||
#grouplist h3:not(.editing) .action.delete { width: 20px; height: 20px; }
|
||||
|
||||
@ -201,7 +202,6 @@
|
||||
.no-svg .mail { background-image:url('%webroot%/core/img/actions/mail.png'); }
|
||||
.no-svg .import, .no-svg .upload { background-image:url('%webroot%/core/img/actions/upload.png'); }
|
||||
.no-svg .export, .no-svg .download { background-image:url('%webroot%/core/img/actions/download.png'); }
|
||||
/*.no-svg .cloud { background-image:url('%webroot%/core/img/places/picture.png'); }*/
|
||||
.no-svg .globe { background-image:url('%webroot%/core/img/actions/public.png'); }
|
||||
.no-svg .settings { background-image:url('%webroot%/core/img/actions/settings.svg'); }
|
||||
.no-svg .starred { background-image:url('%appswebroot%/contacts/img/starred.png'); background-size: contain; }
|
||||
@ -222,7 +222,6 @@
|
||||
.svg .mail { background-image:url('%webroot%/core/img/actions/mail.svg'); }
|
||||
.svg .import,.svg .upload { background-image:url('%webroot%/core/img/actions/upload.svg'); }
|
||||
.svg .export,.svg .download { background-image:url('%webroot%/core/img/actions/download.svg'); }
|
||||
/*.svg .cloud { background-image:url('%webroot%/core/img/places/picture.svg'); }*/
|
||||
.svg .globe { background-image:url('%webroot%/core/img/actions/public.svg'); }
|
||||
.svg .settings { background-image:url('%webroot%/core/img/actions/settings.svg'); }
|
||||
.svg .starred { background-image:url('%appswebroot%/contacts/img/starred.svg'); background-size: contain; }
|
||||
@ -246,11 +245,16 @@
|
||||
}
|
||||
.control > * { background: none repeat scroll 0 0 #F8F8F8; color: #555 !important; font-size: 100%; margin: 0px; }
|
||||
|
||||
.dim {
|
||||
opacity: .50;filter:Alpha(Opacity=50);
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.ui-draggable { height: 3em; z-index: 1000; }
|
||||
.ui-draggable { height: 3em; z-index: 1000; }
|
||||
.ui-draggable-dragging {
|
||||
.dragContact {
|
||||
cursor: move;
|
||||
background-repeat: no-repeat !important;
|
||||
background-position: .3em .3em !important;
|
||||
background-color: #fff;
|
||||
z-index: 5;
|
||||
width: 200px; height: 30px;
|
||||
@ -258,6 +262,8 @@
|
||||
font-weight: bold;
|
||||
border: thin solid silver; border-radius: 3px;
|
||||
padding: 3px;
|
||||
z-index: 1000;
|
||||
transition: background-image 500ms ease 0s;
|
||||
}
|
||||
.ui-state-hover { border: 1px solid dashed; z-index: 1; }
|
||||
|
||||
@ -318,7 +324,7 @@ ul.propertylist { width: 450px; }
|
||||
.propertylist li > input[type="checkbox"].impp { clear: none; }
|
||||
.propertylist li > label.xab { display: block; color: #bbb; float:left; clear: both; padding: 0.5em 0 0 2.5em; }
|
||||
.propertylist li > label.xab:hover { color: #777; }
|
||||
#rightcontent label, #rightcontent dt, #rightcontent th, #rightcontent .label {
|
||||
#contact label, #contact dt, #contact th, #contact .label {
|
||||
float: left;
|
||||
font-size: 0.7em; font-weight: bold;
|
||||
color: #bbb !important;
|
||||
@ -327,9 +333,9 @@ ul.propertylist { width: 450px; }
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
#rightcontent label:hover, #rightcontent dt:hover, #rightcontent input.label:hover { color: #777 !important; }
|
||||
#rightcontent input.label, #rightcontent input.label { margin:-2px; }
|
||||
#rightcontent input.label:hover, #rightcontent input.label:active { border: 0 none !important; border-radius: 0; cursor: pointer; }
|
||||
#contact label:hover, #contact dt:hover, #contact input.label:hover { color: #777 !important; }
|
||||
#contact input.label, #contact input.label { margin:-2px; }
|
||||
#contact input.label:hover, #contact input.label:active { border: 0 none !important; border-radius: 0; cursor: pointer; }
|
||||
.typelist[type="button"] { float: left; max-width: 8em; border: 0; background-color: #fff; color: #bbb; box-shadow: none; } /* for multiselect */
|
||||
.typelist[type="button"]:hover { color: #777; } /* for multiselect */
|
||||
.addresslist { clear: both; font-weight: bold; }
|
||||
@ -407,7 +413,7 @@ ul.propertylist { width: 450px; }
|
||||
}
|
||||
/* Single elements */
|
||||
#file_upload_target, #import_upload_target, #crop_target { display:none; }
|
||||
#import_fileupload {
|
||||
#import_upload_start {
|
||||
height: 2.29em;
|
||||
/*width: 2.5em;*/
|
||||
width: 95%;
|
||||
@ -417,6 +423,12 @@ ul.propertylist { width: 450px; }
|
||||
cursor: pointer;
|
||||
z-index: 1001;
|
||||
}
|
||||
/* Those dang Germans and their long sentences ;) */
|
||||
label[for="import_upload_start"] {
|
||||
display: inline-block;
|
||||
max-width: 16em;
|
||||
white-space: pre-line;
|
||||
}
|
||||
.import-upload-button {
|
||||
background-image: url("%webroot%/core/img/actions/upload.svg");
|
||||
background-position: center center;
|
||||
@ -438,6 +450,10 @@ input:not([type="checkbox"]).propertytype {
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
background-image:url('%appswebroot%/contacts/img/person.png');
|
||||
}
|
||||
input[type="checkbox"].propertytype { width: 10px; }
|
||||
.contactphoto {
|
||||
float: left; display: inline-block;
|
||||
@ -466,8 +482,27 @@ input[type="checkbox"].propertytype { width: 10px; }
|
||||
#phototools li { display: inline; }
|
||||
#phototools li a { float:left; opacity: 0.6; }
|
||||
#phototools li a:hover { opacity: 0.8; }
|
||||
#contactphoto_fileupload, #import_fileupload { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; }
|
||||
#contactphoto_fileupload, #import_upload_start { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; z-index:1001; }
|
||||
|
||||
#dialog-merge-contacts .mergelist {
|
||||
margin: 10px;
|
||||
}
|
||||
#dialog-merge-contacts .mergelist > li {
|
||||
height: 30px;
|
||||
-webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms;
|
||||
position:relative;
|
||||
background-position: 0 .2em !important; background-repeat:no-repeat !important;
|
||||
}
|
||||
#dialog-merge-contacts .mergelist > li > input {
|
||||
opacity: 0;
|
||||
}
|
||||
#dialog-merge-contacts .mergelist > li > input:hover, #dialog-merge-contacts .mergelist > li > input:checked {
|
||||
opacity: 1;
|
||||
}
|
||||
#dialog-merge-contacts .mergelist > li > label {
|
||||
padding-left: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.no-svg .favorite { display: inline-block; float: left; height: 20px; width: 20px; background-image:url('%appswebroot%/contacts/img/inactive_star.png'); }
|
||||
.no-svg .favorite.active, .favorite:hover { background-image:url('%appswebroot%/contacts/img/active_star.png'); }
|
||||
.no-svg .favorite.inactive { background-image:url('%appswebroot%/contacts/img/inactive_star.png'); }
|
||||
@ -558,7 +593,6 @@ input[type="checkbox"].propertytype { width: 10px; }
|
||||
#contactlist tr > td.name { font-weight: bold; text-indent: 1.6em; -webkit-transition:background-image 500ms; -moz-transition:background-image 500ms; -o-transition:background-image 500ms; transition:background-image 500ms; position:relative; background-position:1em .5em !important; background-repeat:no-repeat !important; }
|
||||
#contactlist tr > td a.mailto { position: absolute; float: right; clear: none; cursor:pointer; width:16px; height:16px; margin: 2px; z-index: 200; opacity: 0.6; background-image:url('%webroot%/core/img/actions/mail.svg'); }
|
||||
#contactlist tr > td a.mailto:hover { opacity: 0.8; }
|
||||
#contactlist.dim { background-image: #ddd; opacity: .50;filter:Alpha(Opacity=50); }
|
||||
|
||||
#contact {
|
||||
background-color: white; color: #333333;
|
||||
|
36
css/jquery.ocdialog.css
Normal file
36
css/jquery.ocdialog.css
Normal file
@ -0,0 +1,36 @@
|
||||
.oc-dialog {
|
||||
background: white;
|
||||
color: #333333;
|
||||
border-radius: 3px; box-shadow: 0 0 7px #888888;
|
||||
padding: 15px;
|
||||
z-index: 200;
|
||||
font-size: 100%;
|
||||
}
|
||||
.oc-dialog-title {
|
||||
background: white;
|
||||
font-weight: bold;
|
||||
font-size: 110%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.oc-dialog-content {
|
||||
z-index: 200;
|
||||
background: white;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.oc-dialog-separator {
|
||||
}
|
||||
.oc-dialog-buttonrow {
|
||||
background: white;
|
||||
float: right;
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.oc-dialog-close {
|
||||
position:absolute;
|
||||
top:7px; right:7px;
|
||||
height:20px; width:20px;
|
||||
background:url('%webroot%/core/img/actions/delete.svg') no-repeat center;
|
||||
}
|
@ -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['addressbookid'], $_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');
|
||||
|
127
import.php
127
import.php
@ -5,12 +5,17 @@
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
//check for addressbooks rights or create new one
|
||||
ob_start();
|
||||
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\App::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
\OCP\JSON::checkLoggedIn();
|
||||
\OCP\App::checkAppEnabled('contacts');
|
||||
\OCP\JSON::callCheck();
|
||||
session_write_close();
|
||||
|
||||
$nl = "\n";
|
||||
@ -19,70 +24,49 @@ global $progresskey;
|
||||
$progresskey = 'contacts.import-' . (isset($_GET['progresskey'])?$_GET['progresskey']:'');
|
||||
|
||||
if (isset($_GET['progress']) && $_GET['progress']) {
|
||||
echo OC_Cache::get($progresskey);
|
||||
echo \OC_Cache::get($progresskey);
|
||||
die;
|
||||
}
|
||||
|
||||
function writeProgress($pct) {
|
||||
global $progresskey;
|
||||
OC_Cache::set($progresskey, $pct, 300);
|
||||
\OC_Cache::set($progresskey, $pct, 300);
|
||||
}
|
||||
writeProgress('10');
|
||||
$view = null;
|
||||
$inputfile = strtr($_POST['file'], array('/' => '', "\\" => ''));
|
||||
if(OC\Files\Filesystem::isFileBlacklisted($inputfile)) {
|
||||
OCP\JSON::error(array('data' => array('message' => 'Upload of blacklisted file: ' . $inputfile)));
|
||||
if(\OC\Files\Filesystem::isFileBlacklisted($inputfile)) {
|
||||
\OCP\JSON::error(array('data' => array('message' => 'Upload of blacklisted file: ' . $inputfile)));
|
||||
exit();
|
||||
}
|
||||
if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
|
||||
$view = OCP\Files::getStorage('contacts');
|
||||
$view = \OCP\Files::getStorage('contacts');
|
||||
$file = $view->file_get_contents('/imports/' . $inputfile);
|
||||
} else {
|
||||
$file = \OC\Files\Filesystem::file_get_contents($_POST['path'] . '/' . $inputfile);
|
||||
}
|
||||
if(!$file) {
|
||||
OCP\JSON::error(array('data' => array('message' => 'Import file was empty.')));
|
||||
\OCP\JSON::error(array('data' => array('message' => 'Import file was empty.')));
|
||||
exit();
|
||||
}
|
||||
if(isset($_POST['method']) && $_POST['method'] == 'new') {
|
||||
$id = OCA\Contacts\Addressbook::add(OCP\USER::getUser(),
|
||||
$_POST['addressbookname']);
|
||||
if(!$id) {
|
||||
OCP\JSON::error(
|
||||
array(
|
||||
'data' => array('message' => 'Error creating address book.')
|
||||
|
||||
$id = $_POST['id'];
|
||||
|
||||
if(!$id) {
|
||||
\OCP\JSON::error(
|
||||
array(
|
||||
'data' => array(
|
||||
'message' => 'Error getting the ID of the address book.',
|
||||
'file'=>\OCP\Util::sanitizeHTML($inputfile)
|
||||
)
|
||||
);
|
||||
exit();
|
||||
}
|
||||
OCA\Contacts\Addressbook::setActive($id, 1);
|
||||
}else{
|
||||
$id = $_POST['id'];
|
||||
if(!$id) {
|
||||
OCP\JSON::error(
|
||||
array(
|
||||
'data' => array(
|
||||
'message' => 'Error getting the ID of the address book.',
|
||||
'file'=>OCP\Util::sanitizeHTML($inputfile)
|
||||
)
|
||||
)
|
||||
);
|
||||
exit();
|
||||
}
|
||||
try {
|
||||
OCA\Contacts\Addressbook::find($id); // is owner access check
|
||||
} catch(Exception $e) {
|
||||
OCP\JSON::error(
|
||||
array(
|
||||
'data' => array(
|
||||
'message' => $e->getMessage(),
|
||||
'file'=>OCP\Util::sanitizeHTML($inputfile)
|
||||
)
|
||||
)
|
||||
);
|
||||
exit();
|
||||
}
|
||||
)
|
||||
);
|
||||
exit();
|
||||
}
|
||||
|
||||
$app = new App();
|
||||
$addressBook = $app->getAddressBook('local', $id);
|
||||
|
||||
//analyse the contacts file
|
||||
writeProgress('40');
|
||||
$file = str_replace(array("\r","\n\n"), array("\n","\n"), $file);
|
||||
@ -110,69 +94,72 @@ $imported = 0;
|
||||
$failed = 0;
|
||||
$partial = 0;
|
||||
if(!count($parts) > 0) {
|
||||
OCP\JSON::error(
|
||||
\OCP\JSON::error(
|
||||
array(
|
||||
'data' => array(
|
||||
'message' => 'No contacts to import in '
|
||||
. OCP\Util::sanitizeHTML($inputfile).'. Please check if the file is corrupted.',
|
||||
. \OCP\Util::sanitizeHTML($inputfile).'. Please check if the file is corrupted.',
|
||||
'file'=>OCP\Util::sanitizeHTML($inputfile)
|
||||
)
|
||||
)
|
||||
);
|
||||
if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
|
||||
if(!$view->unlink('/imports/' . $inputfile)) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
'Import: Error unlinking OC_FilesystemView ' . '/' . OCP\Util::sanitizeHTML($inputfile),
|
||||
OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'Import: Error unlinking OC_FilesystemView ' . '/' . \OCP\Util::sanitizeHTML($inputfile),
|
||||
\OCP\Util::ERROR);
|
||||
}
|
||||
}
|
||||
exit();
|
||||
}
|
||||
foreach($parts as $part) {
|
||||
try {
|
||||
$vcard = Sabre\VObject\Reader::read($part);
|
||||
} catch (Sabre\VObject\ParseException $e) {
|
||||
$vcard = VObject\Reader::read($part);
|
||||
} catch (VObject\ParseException $e) {
|
||||
try {
|
||||
$vcard = Sabre\VObject\Reader::read($part, Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES);
|
||||
$vcard = VObject\Reader::read($part, VObject\Reader::OPTION_IGNORE_INVALID_LINES);
|
||||
$partial += 1;
|
||||
OCP\Util::writeLog('contacts',
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'Import: Retrying reading card. Error parsing VCard: ' . $e->getMessage(),
|
||||
OCP\Util::ERROR);
|
||||
} catch (Exception $e) {
|
||||
\OCP\Util::ERROR);
|
||||
} catch (\Exception $e) {
|
||||
$failed += 1;
|
||||
OCP\Util::writeLog('contacts',
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'Import: skipping card. Error parsing VCard: ' . $e->getMessage(),
|
||||
OCP\Util::ERROR);
|
||||
\OCP\Util::ERROR);
|
||||
continue; // Ditch cards that can't be parsed by Sabre.
|
||||
}
|
||||
}
|
||||
try {
|
||||
OCA\Contacts\VCard::add($id, $vcard);
|
||||
$imported += 1;
|
||||
} catch (Exception $e) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
'Error importing vcard: ' . $e->getMessage() . $nl . $vcard,
|
||||
OCP\Util::ERROR);
|
||||
if($addressBook->addChild($vcard)) {
|
||||
$imported += 1;
|
||||
} else {
|
||||
$failed += 1;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __LINE__ . ' ' .
|
||||
'Error importing vcard: ' . $e->getMessage() . $nl . $vcard->serialize(),
|
||||
\OCP\Util::ERROR);
|
||||
$failed += 1;
|
||||
}
|
||||
}
|
||||
//done the import
|
||||
writeProgress('100');
|
||||
sleep(3);
|
||||
OC_Cache::remove($progresskey);
|
||||
\OC_Cache::remove($progresskey);
|
||||
if(isset($_POST['fstype']) && $_POST['fstype'] == 'OC_FilesystemView') {
|
||||
if(!$view->unlink('/imports/' . $inputfile)) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'Import: Error unlinking OC_FilesystemView ' . '/' . $inputfile,
|
||||
OCP\Util::ERROR);
|
||||
\OCP\Util::ERROR);
|
||||
}
|
||||
}
|
||||
OCP\JSON::success(
|
||||
\OCP\JSON::success(
|
||||
array(
|
||||
'data' => array(
|
||||
'imported'=>$imported,
|
||||
'failed'=>$failed,
|
||||
'file'=>OCP\Util::sanitizeHTML($inputfile),
|
||||
'file'=>\OCP\Util::sanitizeHTML($inputfile),
|
||||
)
|
||||
)
|
||||
);
|
||||
|
71
index.php
71
index.php
@ -1,64 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2012 Thomas Tanghus <thomas@tanghus.net>
|
||||
* Copyright (c) 2012, 2013 Thomas Tanghus <thomas@tanghus.net>
|
||||
* Copyright (c) 2011 Jakob Sack mail@jakobsack.de
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts;
|
||||
|
||||
// Check if we are a user
|
||||
OCP\User::checkLoggedIn();
|
||||
OCP\App::checkAppEnabled('contacts');
|
||||
\OCP\User::checkLoggedIn();
|
||||
\OCP\App::checkAppEnabled('contacts');
|
||||
|
||||
// Get active address books. This creates a default one if none exists.
|
||||
$ids = OCA\Contacts\Addressbook::activeIds(OCP\USER::getUser());
|
||||
//$ids = OCA\Contacts\Addressbook::activeIds(OCP\USER::getUser());
|
||||
|
||||
// Load the files we need
|
||||
OCP\App::setActiveNavigationEntry('contacts_index');
|
||||
\OCP\App::setActiveNavigationEntry('contacts_index');
|
||||
|
||||
$impp_types = OCA\Contacts\App::getTypesOfProperty('IMPP');
|
||||
$adr_types = OCA\Contacts\App::getTypesOfProperty('ADR');
|
||||
$phone_types = OCA\Contacts\App::getTypesOfProperty('TEL');
|
||||
$email_types = OCA\Contacts\App::getTypesOfProperty('EMAIL');
|
||||
$ims = OCA\Contacts\App::getIMOptions();
|
||||
$impp_types = Utils\Properties::getTypesForProperty('IMPP');
|
||||
$adr_types = Utils\Properties::getTypesForProperty('ADR');
|
||||
$phone_types = Utils\Properties::getTypesForProperty('TEL');
|
||||
$email_types = Utils\Properties::getTypesForProperty('EMAIL');
|
||||
$ims = Utils\Properties::getIMOptions();
|
||||
$im_protocols = array();
|
||||
foreach($ims as $name => $values) {
|
||||
$im_protocols[$name] = $values['displayname'];
|
||||
}
|
||||
$categories = OCA\Contacts\App::getCategories();
|
||||
|
||||
$maxUploadFilesize = OCP\Util::maxUploadFilesize('/');
|
||||
$maxUploadFilesize = \OCP\Util::maxUploadFilesize('/');
|
||||
|
||||
OCP\Util::addscript('', 'multiselect');
|
||||
OCP\Util::addscript('', 'jquery.multiselect');
|
||||
OCP\Util::addscript('', 'oc-vcategories');
|
||||
OCP\Util::addscript('contacts', 'modernizr.custom');
|
||||
OCP\Util::addscript('contacts', 'app');
|
||||
OCP\Util::addscript('contacts', 'contacts');
|
||||
OCP\Util::addscript('contacts', 'groups');
|
||||
OCP\Util::addscript('contacts', 'expanding');
|
||||
OCP\Util::addscript('contacts', 'jquery.combobox');
|
||||
OCP\Util::addscript('files', 'jquery.fileupload');
|
||||
OCP\Util::addscript('contacts', 'jquery.Jcrop');
|
||||
OCP\Util::addStyle('3rdparty/fontawesome', 'font-awesome');
|
||||
OCP\Util::addStyle('contacts', 'font-awesome');
|
||||
OCP\Util::addStyle('', 'multiselect');
|
||||
OCP\Util::addStyle('', 'jquery.multiselect');
|
||||
OCP\Util::addStyle('contacts', 'jquery.combobox');
|
||||
OCP\Util::addStyle('contacts', 'jquery.Jcrop');
|
||||
OCP\Util::addStyle('contacts', 'contacts');
|
||||
\OCP\Util::addscript('', 'multiselect');
|
||||
\OCP\Util::addscript('', 'jquery.multiselect');
|
||||
\OCP\Util::addscript('', 'oc-vcategories');
|
||||
\OCP\Util::addscript('', 'octemplate');
|
||||
\OCP\Util::addscript('contacts', 'modernizr.custom');
|
||||
\OCP\Util::addscript('contacts', 'app');
|
||||
\OCP\Util::addscript('contacts', 'contacts');
|
||||
\OCP\Util::addscript('contacts', 'storage');
|
||||
\OCP\Util::addscript('contacts', 'groups');
|
||||
//\OCP\Util::addscript('contacts', 'expanding');
|
||||
\OCP\Util::addscript('contacts', 'jquery.combobox');
|
||||
\OCP\Util::addscript('contacts', 'jquery.ocdialog');
|
||||
\OCP\Util::addscript('files', 'jquery.fileupload');
|
||||
\OCP\Util::addscript('contacts', 'jquery.Jcrop');
|
||||
\OCP\Util::addStyle('3rdparty/fontawesome', 'font-awesome');
|
||||
\OCP\Util::addStyle('contacts', 'font-awesome');
|
||||
\OCP\Util::addStyle('', 'multiselect');
|
||||
\OCP\Util::addStyle('', 'jquery.multiselect');
|
||||
\OCP\Util::addStyle('contacts', 'jquery.combobox');
|
||||
\OCP\Util::addStyle('contacts', 'jquery.ocdialog');
|
||||
\OCP\Util::addStyle('contacts', 'jquery.Jcrop');
|
||||
\OCP\Util::addStyle('contacts', 'contacts');
|
||||
|
||||
$tmpl = new OCP\Template( "contacts", "contacts", "user" );
|
||||
$tmpl = new \OCP\Template( "contacts", "contacts", "user" );
|
||||
$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize);
|
||||
$tmpl->assign('uploadMaxHumanFilesize',
|
||||
OCP\Util::humanFileSize($maxUploadFilesize), false);
|
||||
$tmpl->assign('addressbooks', OCA\Contacts\Addressbook::all(OCP\USER::getUser()));
|
||||
\OCP\Util::humanFileSize($maxUploadFilesize), false);
|
||||
//$tmpl->assign('addressbooks', OCA\Contacts\Addressbook::all(OCP\USER::getUser()));
|
||||
$tmpl->assign('phone_types', $phone_types);
|
||||
$tmpl->assign('email_types', $email_types);
|
||||
$tmpl->assign('adr_types', $adr_types);
|
||||
$tmpl->assign('impp_types', $impp_types);
|
||||
$tmpl->assign('categories', $categories);
|
||||
$tmpl->assign('im_protocols', $im_protocols);
|
||||
$tmpl->printPage();
|
||||
|
899
js/app.js
899
js/app.js
@ -74,14 +74,6 @@ utils.moveCursorToEnd = function(el) {
|
||||
}
|
||||
};
|
||||
|
||||
if (typeof Object.create !== 'function') {
|
||||
Object.create = function (o) {
|
||||
function F() {}
|
||||
F.prototype = o;
|
||||
return new F();
|
||||
};
|
||||
}
|
||||
|
||||
Array.prototype.clone = function() {
|
||||
return this.slice(0);
|
||||
};
|
||||
@ -157,25 +149,25 @@ OC.notify = function(params) {
|
||||
OC.Contacts = OC.Contacts || {
|
||||
init:function() {
|
||||
if(oc_debug === true) {
|
||||
$(document).ajaxError(function(e, xhr, settings, exception) {
|
||||
// Don't try to get translation because it's likely a network error.
|
||||
OC.notify({
|
||||
message: 'error in: ' + settings.url + ', '+'error: ' + xhr.responseText
|
||||
});
|
||||
$.error = console.error;
|
||||
$(document).ajaxError(function(e, xhr, settings, exception) {
|
||||
console.error('Error in: ', settings.url, ' : ', xhr.responseText, exception);
|
||||
});
|
||||
}
|
||||
|
||||
this.scrollTimeoutMiliSecs = 100;
|
||||
this.isScrolling = false;
|
||||
this.cacheElements();
|
||||
this.storage = new OC.Contacts.Storage();
|
||||
this.contacts = new OC.Contacts.ContactList(
|
||||
this.storage,
|
||||
this.$contactList,
|
||||
this.$contactListItemTemplate,
|
||||
this.$contactDragItemTemplate,
|
||||
this.$contactFullTemplate,
|
||||
this.detailTemplates
|
||||
);
|
||||
this.groups = new OC.Contacts.GroupList(this.$groupList, this.$groupListItemTemplate);
|
||||
this.groups = new OC.Contacts.GroupList(this.storage, this.$groupList, this.$groupListItemTemplate);
|
||||
OCCategories.changed = this.groups.categoriesChanged;
|
||||
OCCategories.app = 'contacts';
|
||||
OCCategories.type = 'contact';
|
||||
@ -233,7 +225,7 @@ OC.Contacts = OC.Contacts || {
|
||||
this.$ninjahelp = $('#ninjahelp');
|
||||
this.$firstRun = $('#firstrun');
|
||||
this.$settings = $('#contacts-settings');
|
||||
this.$importFileInput = $('#import_fileupload');
|
||||
this.$importFileInput = $('#import_upload_start');
|
||||
this.$importIntoSelect = $('#import_into');
|
||||
},
|
||||
// Build the select to add/remove from groups.
|
||||
@ -287,13 +279,18 @@ OC.Contacts = OC.Contacts || {
|
||||
$(window).trigger('beforeunload');
|
||||
});
|
||||
|
||||
$(window).bind('hashchange', function() {
|
||||
console.log('hashchange', window.location.hash)
|
||||
this.hashChange = function() {
|
||||
//console.log('hashchange', window.location.hash)
|
||||
var id = parseInt(window.location.hash.substr(1));
|
||||
if(id) {
|
||||
if(id && id !== self.currentid) {
|
||||
self.openContact(id);
|
||||
} else if(!id && self.currentid) {
|
||||
self.closeContact(self.currentid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(window).bind('popstate', this.hashChange);
|
||||
$(window).bind('hashchange', this.hashChange);
|
||||
|
||||
// App specific events
|
||||
$(document).bind('status.contact.deleted', function(e, data) {
|
||||
@ -312,7 +309,10 @@ OC.Contacts = OC.Contacts || {
|
||||
self.hideActions();
|
||||
});
|
||||
|
||||
// Keep error messaging at one place to be able to replace it.
|
||||
$(document).bind('status.contact.error', function(e, data) {
|
||||
console.warn(data.message);
|
||||
console.trace();
|
||||
OC.notify({message:data.message});
|
||||
});
|
||||
|
||||
@ -344,11 +344,15 @@ OC.Contacts = OC.Contacts || {
|
||||
self.openContact(self.currentid);
|
||||
}
|
||||
});
|
||||
if(!result.is_indexed) {
|
||||
if(!contacts_properties_indexed) {
|
||||
// Wait a couple of mins then check if contacts are indexed.
|
||||
setTimeout(function() {
|
||||
OC.notify({message:t('contacts', 'Indexing contacts'), timeout:20});
|
||||
$.post(OC.filePath('contacts', 'ajax', 'indexproperties.php'));
|
||||
$.when($.post(OC.Router.generate('contacts_index_properties')))
|
||||
.then(function(response) {
|
||||
if(!response.isIndexed) {
|
||||
OC.notify({message:t('contacts', 'Indexing contacts'), timeout:20});
|
||||
}
|
||||
});
|
||||
}, 10000);
|
||||
} else {
|
||||
console.log('contacts are indexed.');
|
||||
@ -418,41 +422,79 @@ OC.Contacts = OC.Contacts || {
|
||||
});
|
||||
|
||||
$(document).bind('request.contact.export', function(e, data) {
|
||||
var id = parseInt(data.id);
|
||||
var id = String(data.id);
|
||||
console.log('contact', data.id, 'request.contact.export');
|
||||
document.location.href = OC.linkTo('contacts', 'export.php') + '?contactid=' + self.currentid;
|
||||
document.location.href = OC.linkTo('contacts', 'export.php') + '?' + $.param(data);
|
||||
});
|
||||
|
||||
$(document).bind('request.contact.close', function(e, data) {
|
||||
var id = parseInt(data.id);
|
||||
var id = String(data.id);
|
||||
console.log('contact', data.id, 'request.contact.close');
|
||||
self.closeContact(id);
|
||||
});
|
||||
|
||||
$(document).bind('request.contact.delete', function(e, data) {
|
||||
var id = parseInt(data.id);
|
||||
console.log('contact', data.id, 'request.contact.delete');
|
||||
var id = String(data.contactid);
|
||||
console.log('contact', data, 'request.contact.delete');
|
||||
self.closeContact(id);
|
||||
self.contacts.delayedDelete(id);
|
||||
self.contacts.delayedDelete(data);
|
||||
self.$contactList.removeClass('dim');
|
||||
self.showActions(['add']);
|
||||
});
|
||||
|
||||
$(document).bind('request.select.contactphoto.fromlocal', function(e, result) {
|
||||
console.log('request.select.contactphoto.fromlocal', result);
|
||||
$('#contactphoto_fileupload').trigger('click');
|
||||
$(document).bind('request.contact.merge', function(e, data) {
|
||||
console.log('contact','request.contact.merge', data);
|
||||
var merger = self.contacts.findById(data.merger);
|
||||
var mergees = [];
|
||||
if(!merger) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Merge failed. Cannot find contact: {id}', {id:data.merger})
|
||||
});
|
||||
return;
|
||||
}
|
||||
$.each(data.mergees, function(idx, id) {
|
||||
var contact = self.contacts.findById(id);
|
||||
if(!contact) {
|
||||
console.warn('cannot find', id, 'by id');
|
||||
}
|
||||
mergees.push(contact);
|
||||
});
|
||||
if(!merger.merge(mergees)) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Merge failed.')
|
||||
});
|
||||
return;
|
||||
}
|
||||
merger.saveAll(function(response) {
|
||||
if(response.error) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Merge failed. Error saving contact.')
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
if(data.deleteOther) {
|
||||
self.contacts.delayedDelete(mergees);
|
||||
}
|
||||
self.openContact(merger.getId());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).bind('request.select.contactphoto.fromcloud', function(e, result) {
|
||||
console.log('request.select.contactphoto.fromcloud', result);
|
||||
$(document).bind('request.select.contactphoto.fromlocal', function(e, metadata) {
|
||||
console.log('request.select.contactphoto.fromlocal', metadata);
|
||||
$('#contactphoto_fileupload').trigger('click', metadata);
|
||||
});
|
||||
|
||||
$(document).bind('request.select.contactphoto.fromcloud', function(e, metadata) {
|
||||
console.log('request.select.contactphoto.fromcloud', metadata);
|
||||
OC.dialogs.filepicker(t('contacts', 'Select photo'), function(path) {
|
||||
self.cloudPhotoSelected(self.currentid, path);
|
||||
self.cloudPhotoSelected(metadata, path);
|
||||
}, false, 'image', true);
|
||||
});
|
||||
|
||||
$(document).bind('request.edit.contactphoto', function(e, result) {
|
||||
console.log('request.edit.contactphoto', result);
|
||||
self.editCurrentPhoto(result.id);
|
||||
$(document).bind('request.edit.contactphoto', function(e, metadata) {
|
||||
console.log('request.edit.contactphoto', metadata);
|
||||
self.editCurrentPhoto(metadata);
|
||||
});
|
||||
|
||||
$(document).bind('request.addressbook.activate', function(e, result) {
|
||||
@ -476,9 +518,8 @@ OC.Contacts = OC.Contacts || {
|
||||
}
|
||||
$.each(result.contacts, function(idx, contactid) {
|
||||
var contact = self.contacts.findById(contactid);
|
||||
console.log('contactid', contactid, contact);
|
||||
|
||||
self.contacts.findById(contactid).removeFromGroup(result.groupname);
|
||||
contact.removeFromGroup(result.groupname);
|
||||
});
|
||||
});
|
||||
|
||||
@ -494,10 +535,17 @@ OC.Contacts = OC.Contacts || {
|
||||
// Group sorted, save the sort order
|
||||
$(document).bind('status.groups.sorted', function(e, result) {
|
||||
console.log('status.groups.sorted', result);
|
||||
$.post(OC.filePath('contacts', 'ajax', 'setpreference.php'), {'key':'groupsort', 'value':result.sortorder}, function(jsondata) {
|
||||
if(jsondata.status !== 'success') {
|
||||
OC.notify({message: jsondata ? jsondata.data.message : t('contacts', 'Network or server error. Please inform administrator.')});
|
||||
$.when(self.storage.setPreference('groupsort', result.sortorder)).then(function(response) {
|
||||
if(response.error) {
|
||||
OC.notify({message: response ? response.message : t('contacts', 'Network or server error. Please inform administrator.')});
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed saving sort order: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
});
|
||||
// Group selected, only show contacts from that group
|
||||
@ -520,10 +568,17 @@ OC.Contacts = OC.Contacts || {
|
||||
} else {
|
||||
self.contacts.showContacts(self.currentgroup);
|
||||
}
|
||||
$.post(OC.filePath('contacts', 'ajax', 'setpreference.php'), {'key':'lastgroup', 'value':self.currentgroup}, function(jsondata) {
|
||||
if(!jsondata || jsondata.status !== 'success') {
|
||||
OC.notify({message: (jsondata && jsondata.data) ? jsondata.data.message : t('contacts', 'Network or server error. Please inform administrator.')});
|
||||
$.when(self.storage.setPreference('lastgroup', self.currentgroup)).then(function(response) {
|
||||
if(response.error) {
|
||||
OC.notify({message: response.message});
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed saving last group: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
self.$rightContent.scrollTop(0);
|
||||
});
|
||||
@ -567,6 +622,13 @@ OC.Contacts = OC.Contacts || {
|
||||
$('body').bind('click', bodyListener);
|
||||
}
|
||||
});
|
||||
$('#contactphoto_fileupload').on('click', function(event, metadata) {
|
||||
var form = $('#file_upload_form');
|
||||
form.find('input[name="contactid"]').val(metadata.contactid);
|
||||
form.find('input[name="addressbookid"]').val(metadata.addressbookid);
|
||||
form.find('input[name="backend"]').val(metadata.backend);
|
||||
});
|
||||
|
||||
$('#contactphoto_fileupload').on('change', function() {
|
||||
self.uploadPhoto(this.files);
|
||||
});
|
||||
@ -593,38 +655,35 @@ OC.Contacts = OC.Contacts || {
|
||||
self.buildGroupSelect();
|
||||
}
|
||||
if(isChecked) {
|
||||
self.showActions(['add', 'download', 'groups', 'delete', 'favorite']);
|
||||
self.showActions(['add', 'download', 'groups', 'delete', 'favorite', 'merge']);
|
||||
} else {
|
||||
self.showActions(['add']);
|
||||
}
|
||||
});
|
||||
|
||||
this.$contactList.on('change', 'input:checkbox', function(event) {
|
||||
if($(this).is(':checked')) {
|
||||
if(self.$groups.find('option').length === 1) {
|
||||
self.buildGroupSelect();
|
||||
}
|
||||
self.showActions(['add', 'download', 'groups', 'delete', 'favorite']);
|
||||
} else if(self.contacts.getSelectedContacts().length === 0) {
|
||||
var selected = self.contacts.getSelectedContacts();
|
||||
console.log('selected', selected.length);
|
||||
if(selected.length > 0 && self.$groups.find('option').length === 1) {
|
||||
self.buildGroupSelect();
|
||||
}
|
||||
if(selected.length === 0) {
|
||||
self.showActions(['add']);
|
||||
} else if(selected.length === 1) {
|
||||
self.showActions(['add', 'download', 'groups', 'delete', 'favorite']);
|
||||
} else {
|
||||
self.showActions(['add', 'download', 'groups', 'delete', 'favorite', 'merge']);
|
||||
}
|
||||
});
|
||||
|
||||
// Add to/remove from group multiple contacts.
|
||||
// FIXME: Refactor this to be usable for favoriting also.
|
||||
this.$groups.on('change', function() {
|
||||
var $opt = $(this).find('option:selected');
|
||||
var action = $opt.parent().data('action');
|
||||
var ids, groupName, groupId, buildnow = false;
|
||||
var groupName, groupId, buildnow = false;
|
||||
|
||||
// If a contact is open the action is only applied to that,
|
||||
// otherwise on all selected items.
|
||||
if(self.currentid) {
|
||||
ids = [self.currentid];
|
||||
buildnow = true;
|
||||
} else {
|
||||
ids = self.contacts.getSelectedContacts();
|
||||
}
|
||||
var contacts = self.contacts.getSelectedContacts();
|
||||
var ids = $.map(contacts, function(c) {return c.getId();});
|
||||
|
||||
self.setAllChecked(false);
|
||||
self.$toggleAll.prop('checked', false);
|
||||
@ -637,7 +696,7 @@ OC.Contacts = OC.Contacts || {
|
||||
console.log('add group...');
|
||||
self.$groups.val(-1);
|
||||
self.addGroup(function(response) {
|
||||
if(response.status === 'success') {
|
||||
if(!response.error) {
|
||||
groupId = response.id;
|
||||
groupName = response.name;
|
||||
self.groups.addTo(ids, groupId, function(result) {
|
||||
@ -676,7 +735,6 @@ OC.Contacts = OC.Contacts || {
|
||||
|
||||
groupName = $opt.text(), groupId = $opt.val();
|
||||
|
||||
console.log('trut', groupName, groupId);
|
||||
if(action === 'add') {
|
||||
self.groups.addTo(ids, $opt.val(), function(result) {
|
||||
console.log('after add', result);
|
||||
@ -751,14 +809,12 @@ OC.Contacts = OC.Contacts || {
|
||||
if(event.ctrlKey || event.metaKey) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
console.log('select', event);
|
||||
self.dontScroll = true;
|
||||
self.contacts.select($(this).data('id'), true);
|
||||
return;
|
||||
}
|
||||
if($(event.target).is('a.mailto')) {
|
||||
var mailto = 'mailto:' + $.trim($(this).find('.email').text());
|
||||
console.log('mailto', mailto);
|
||||
try {
|
||||
window.location.href=mailto;
|
||||
} catch(e) {
|
||||
@ -774,6 +830,7 @@ OC.Contacts = OC.Contacts || {
|
||||
*
|
||||
* @param object $list A jquery object of an unordered list
|
||||
* @param object book An object with the properties 'id', 'name' and 'permissions'.
|
||||
* @param bool add Whether the address book list should be updated.
|
||||
*/
|
||||
var appendAddressBook = function($list, book, add) {
|
||||
if(add) {
|
||||
@ -791,34 +848,25 @@ OC.Contacts = OC.Contacts || {
|
||||
var id = parseInt($(this).parents('li').first().data('id'));
|
||||
console.log('delete', id);
|
||||
var $li = $(this).parents('li').first();
|
||||
$.ajax({
|
||||
type:'POST',
|
||||
url:OC.filePath('contacts', 'ajax', 'addressbook/delete.php'),
|
||||
data:{ id: id },
|
||||
success:function(jsondata) {
|
||||
console.log(jsondata);
|
||||
if(jsondata.status == 'success') {
|
||||
self.contacts.unsetAddressbook(id);
|
||||
$li.remove();
|
||||
OC.notify({
|
||||
message:t('contacts','Deleting done. Click here to cancel reloading.'),
|
||||
timeout:5,
|
||||
timeouthandler:function() {
|
||||
console.log('reloading');
|
||||
window.location.href = OC.linkTo('contacts', 'index.php');
|
||||
},
|
||||
clickhandler:function() {
|
||||
console.log('reloading cancelled');
|
||||
OC.notify({cancel:true});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
OC.notify({message:jsondata.data.message});
|
||||
}
|
||||
},
|
||||
error:function(jqXHR, textStatus, errorThrown) {
|
||||
OC.notify({message:textStatus + ': ' + errorThrown});
|
||||
id = false;
|
||||
$.when(self.storage.deleteAddressBook('local',id))
|
||||
.then(function(response) {
|
||||
if(!response.error) {
|
||||
self.contacts.unsetAddressbook(id);
|
||||
$li.remove();
|
||||
OC.notify({
|
||||
message:t('contacts','Deleting done. Click here to cancel reloading.'),
|
||||
timeout:5,
|
||||
timeouthandler:function() {
|
||||
console.log('reloading');
|
||||
window.location.href = OC.Router.generate('contacts_index');
|
||||
},
|
||||
clickhandler:function() {
|
||||
console.log('reloading cancelled');
|
||||
OC.notify({cancel:true});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
OC.notify({message:response.message});
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -827,7 +875,7 @@ OC.Contacts = OC.Contacts || {
|
||||
var book = self.contacts.addressbooks[id];
|
||||
var uri = (book.owner === oc_current_user ) ? book.uri : book.uri + '_shared_by_' + book.owner;
|
||||
var link = OC.linkToRemote('carddav')+'/addressbooks/'+encodeURIComponent(oc_current_user)+'/'+encodeURIComponent(uri);
|
||||
var $dropdown = $('<div id="dropdown" class="drop"><input type="text" value="' + link + '" /></div>');
|
||||
var $dropdown = $('<div id="dropdown" class="drop"><input type="text" value="' + link + '" readonly /></div>');
|
||||
$dropdown.appendTo($(this).parents('li').first());
|
||||
var $input = $dropdown.find('input');
|
||||
$input.focus().get(0).select();
|
||||
@ -840,60 +888,51 @@ OC.Contacts = OC.Contacts || {
|
||||
$list.append($li);
|
||||
};
|
||||
|
||||
var $addAddressBookNew = this.$settings.find('.addaddressbook');
|
||||
var $addAddressBookPart = $addAddressBookNew.next('ul');
|
||||
var $addInput = $addAddressBookPart.find('input.addaddressbookinput').focus();
|
||||
$addInput.on('keydown', function(event) {
|
||||
if(event.keyCode === 13) {
|
||||
event.stopPropagation();
|
||||
$addAddressBookPart.find('.addaddressbookok').trigger('click');
|
||||
}
|
||||
});
|
||||
$addAddressBookPart.on('click keydown', 'button', function(event) {
|
||||
if(wrongKey(event)) {
|
||||
return;
|
||||
}
|
||||
if($(this).is('.addaddressbookok')) {
|
||||
if($addInput.val().trim() === '') {
|
||||
return false;
|
||||
} else {
|
||||
var name = $addInput.val().trim();
|
||||
$addInput.addClass('loading');
|
||||
$addAddressBookPart.find('button input').prop('disabled', true);
|
||||
console.log('adding', name);
|
||||
self.addAddressbook({
|
||||
name: name,
|
||||
description: ''
|
||||
}, function(response) {
|
||||
if(!response || !response.status) {
|
||||
OC.notify({
|
||||
message:t('contacts', 'Network or server error. Please inform administrator.')
|
||||
});
|
||||
return false;
|
||||
} else if(response.status === 'error') {
|
||||
OC.notify({message: response.message});
|
||||
return false;
|
||||
} else if(response.status === 'success') {
|
||||
var book = response.addressbook;
|
||||
var $list = self.$settings.find('[data-id="addressbooks"]').next('ul');
|
||||
appendAddressBook($list, book, true);
|
||||
}
|
||||
$addInput.removeClass('loading');
|
||||
$addAddressBookPart.find('button input').prop('disabled', false);
|
||||
$addAddressBookPart.hide().prev('button').show();
|
||||
});
|
||||
}
|
||||
} else if($(this).is('.addaddressbookcancel')) {
|
||||
$addAddressBookPart.hide().prev('button').show();
|
||||
}
|
||||
});
|
||||
|
||||
this.$settings.find('.addaddressbook').on('click keydown', function(event) {
|
||||
if(wrongKey(event)) {
|
||||
return;
|
||||
}
|
||||
$(this).hide();
|
||||
$addAddressBookPart.show();
|
||||
var $addAddressbookPart = $(this).next('ul').show();
|
||||
var $addinput = $addAddressbookPart.find('input.addaddressbookinput').focus();
|
||||
$addAddressbookPart.on('click keydown', 'button', function(event) {
|
||||
if(wrongKey(event)) {
|
||||
return;
|
||||
}
|
||||
if($(this).is('.addaddressbookok')) {
|
||||
if($addinput.val().trim() === '') {
|
||||
return false;
|
||||
} else {
|
||||
var name = $addinput.val().trim();
|
||||
$addinput.addClass('loading');
|
||||
$addAddressbookPart.find('button input').prop('disabled', true);
|
||||
//console.log('adding', name);
|
||||
$.when(self.storage.addAddressBook('local',
|
||||
{displayname: name, description: ''})).then(function(response) {
|
||||
if(response.error) {
|
||||
OC.notify({message: response.message});
|
||||
return false;
|
||||
} else {
|
||||
var book = response.data;
|
||||
var $list = self.$settings.find('[data-id="addressbooks"]').next('ul');
|
||||
appendAddressBook($list, book, true);
|
||||
}
|
||||
$addinput.removeClass('loading');
|
||||
$addAddressbookPart.find('button input').prop('disabled', false);
|
||||
$addAddressbookPart.hide().prev('button').show();
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed adding address book: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
}
|
||||
} else if($(this).is('.addaddressbookcancel')) {
|
||||
$addAddressbookPart.hide().prev('button').show();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.$settings.find('h2').on('click keydown', function(event) {
|
||||
@ -903,10 +942,9 @@ OC.Contacts = OC.Contacts || {
|
||||
if($(this).next('ul').is(':visible')) {
|
||||
return;
|
||||
}
|
||||
console.log('settings');
|
||||
//console.log('settings');
|
||||
var $list = $(this).next('ul');
|
||||
if($(this).data('id') === 'addressbooks') {
|
||||
console.log('addressbooks');
|
||||
|
||||
if(!self.$addressbookTmpl) {
|
||||
self.$addressbookTmpl = $('#addressbookTemplate');
|
||||
@ -922,27 +960,26 @@ OC.Contacts = OC.Contacts || {
|
||||
$list.find('a.action.share').css('display', 'none');
|
||||
}
|
||||
} else if($(this).data('id') === 'import') {
|
||||
console.log('import');
|
||||
$('.import-upload').show();
|
||||
$('.import-select').hide();
|
||||
|
||||
var addAddressbookCallback = function(select, name) {
|
||||
var id = false;
|
||||
self.addAddressbook({
|
||||
name: name,
|
||||
description: ''
|
||||
}, function(response) {
|
||||
if(!response || !response.status) {
|
||||
OC.notify({
|
||||
message:t('contacts', 'Network or server error. Please inform administrator.')
|
||||
});
|
||||
return false;
|
||||
} else if(response.status === 'error') {
|
||||
$.when(this.storage.addAddressBook('local',
|
||||
{name: name, description: ''})).then(function(response) {
|
||||
if(response.error) {
|
||||
OC.notify({message: response.message});
|
||||
return false;
|
||||
} else if(response.status === 'success') {
|
||||
id = response.addressbook.id;
|
||||
} else {
|
||||
id = response.data.id;
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed adding addres books: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
return id;
|
||||
};
|
||||
@ -970,6 +1007,7 @@ OC.Contacts = OC.Contacts || {
|
||||
self.currentid = 'new';
|
||||
// Properties that the contact doesn't know
|
||||
console.log('addContact, groupid', self.currentgroup);
|
||||
self.$contactList.addClass('dim');
|
||||
var groupprops = {
|
||||
favorite: false,
|
||||
groups: self.groups.categories,
|
||||
@ -1009,8 +1047,9 @@ OC.Contacts = OC.Contacts || {
|
||||
}
|
||||
console.log('delete');
|
||||
if(self.currentid) {
|
||||
console.assert(utils.isUInt(self.currentid), 'self.currentid is not an integer');
|
||||
self.contacts.delayedDelete(self.currentid);
|
||||
console.assert(typeof self.currentid === 'string', 'self.currentid is not a string');
|
||||
contactInfo = self.contacts[self.currentid].metaData();
|
||||
self.contacts.delayedDelete(contactInfo);
|
||||
} else {
|
||||
self.contacts.delayedDelete(self.contacts.getSelectedContacts());
|
||||
}
|
||||
@ -1022,31 +1061,48 @@ OC.Contacts = OC.Contacts || {
|
||||
return;
|
||||
}
|
||||
console.log('download');
|
||||
var contacts = self.contacts.getSelectedContacts();
|
||||
var ids = $.map(contacts, function(c) {return c.getId();});
|
||||
document.location.href = OC.linkTo('contacts', 'export.php')
|
||||
+ '?selectedids=' + self.contacts.getSelectedContacts().join(',');
|
||||
+ '?selectedids=' + ids.join(',');
|
||||
});
|
||||
|
||||
this.$header.on('click keydown', '.merge', function(event) {
|
||||
if(wrongKey(event)) {
|
||||
return;
|
||||
}
|
||||
console.log('merge');
|
||||
self.mergeSelectedContacts();
|
||||
});
|
||||
|
||||
this.$header.on('click keydown', '.favorite', function(event) {
|
||||
if(wrongKey(event)) {
|
||||
return;
|
||||
}
|
||||
if(!utils.isUInt(self.currentid)) {
|
||||
return;
|
||||
|
||||
var contacts = self.contacts.getSelectedContacts();
|
||||
|
||||
self.setAllChecked(false);
|
||||
self.$toggleAll.prop('checked', false);
|
||||
if(!self.currentid) {
|
||||
self.showActions(['add']);
|
||||
}
|
||||
// FIXME: This should only apply for contacts list.
|
||||
var state = self.groups.isFavorite(self.currentid);
|
||||
console.log('Favorite?', this, state);
|
||||
self.groups.setAsFavorite(self.currentid, !state, function(jsondata) {
|
||||
if(jsondata.status === 'success') {
|
||||
if(state) {
|
||||
self.$header.find('.favorite').switchClass('active', '');
|
||||
} else {
|
||||
self.$header.find('.favorite').switchClass('', 'active');
|
||||
}
|
||||
} else {
|
||||
OC.notify({message:t('contacts', jsondata.data.message)});
|
||||
|
||||
$.each(contacts, function(idx, contact) {
|
||||
if(!self.groups.isFavorite(contact.getId())) {
|
||||
self.groups.setAsFavorite(contact.getId(), true, function(result) {
|
||||
if(result.status !== 'success') {
|
||||
OC.notify({message:
|
||||
t('contacts',
|
||||
'Error setting {name} as favorite.',
|
||||
{name:contact.getDisplayName()})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
self.showActions(['add']);
|
||||
});
|
||||
|
||||
this.$contactList.on('mouseenter', 'td.email', function(event) {
|
||||
@ -1060,14 +1116,28 @@ OC.Contacts = OC.Contacts || {
|
||||
|
||||
// Import using jquery.fileupload
|
||||
$(function() {
|
||||
var uploadingFiles = {}, numfiles = 0, uploadedfiles = 0, retries = 0;
|
||||
var uploadingFiles = {},
|
||||
numfiles = 0,
|
||||
uploadedfiles = 0,
|
||||
importedfiles = 0,
|
||||
retries = 0,
|
||||
succeded = 0
|
||||
failed = 0;
|
||||
var aid, importError = false;
|
||||
var $progressbar = $('#import-progress');
|
||||
var $status = $('#import-status-text');
|
||||
|
||||
self.$importFileInput.on('fileuploaddone', function(e, data) {
|
||||
console.log('fileuploaddone', data);
|
||||
var file = data.result.file;
|
||||
console.log('fileuploaddone, file', file);
|
||||
uploadedfiles += 1;
|
||||
});
|
||||
|
||||
var waitForImport = function() {
|
||||
if(numfiles == 0 && uploadedfiles == 0) {
|
||||
$progressbar.progressbar('value',100);
|
||||
console.log('waitForImport', numfiles, uploadedfiles, importedfiles);
|
||||
if(uploadedfiles === importedfiles && importedfiles === numfiles) {
|
||||
$progressbar.progressbar('value', 100);
|
||||
if(!importError) {
|
||||
OC.notify({
|
||||
message:t('contacts','Import done. Click here to cancel reloading.'),
|
||||
@ -1082,7 +1152,9 @@ OC.Contacts = OC.Contacts || {
|
||||
}
|
||||
});
|
||||
}
|
||||
retries = aid = 0;
|
||||
$status.text(t('contacts', '{success} imported, {failed} failed.',
|
||||
{success:succeded, failed:failed})).fadeIn();
|
||||
numfiles = uploadedfiles = importedfiles = retries = aid = failed = succeded = 0;
|
||||
$progressbar.fadeOut();
|
||||
setTimeout(function() {
|
||||
$status.fadeOut('slow');
|
||||
@ -1110,11 +1182,11 @@ OC.Contacts = OC.Contacts || {
|
||||
|
||||
var importFiles = function(aid, uploadingFiles) {
|
||||
console.log('importFiles', aid, uploadingFiles);
|
||||
if(numfiles != uploadedfiles) {
|
||||
if(numfiles !== uploadedfiles) {
|
||||
OC.notify({message:t('contacts', 'Not all files uploaded. Retrying...')});
|
||||
retries += 1;
|
||||
if(retries > 3) {
|
||||
numfiles = uploadedfiles = retries = aid = 0;
|
||||
numfiles = uploadedfiles = importedfiles = retries = failed = succeded = aid = 0;
|
||||
uploadingFiles = {};
|
||||
$progressbar.fadeOut();
|
||||
OC.dialogs.alert(t('contacts', 'Something went wrong with the upload, please retry.'), t('contacts', 'Error'));
|
||||
@ -1130,14 +1202,19 @@ OC.Contacts = OC.Contacts || {
|
||||
$status.text(t('contacts', 'Importing from {filename}...', {filename:fileName})).fadeIn();
|
||||
doImport(fileName, aid, function(response) {
|
||||
if(response.status === 'success') {
|
||||
$status.text(t('contacts', '{success} imported, {failed} failed.',
|
||||
{success:response.data.imported, failed:response.data.failed})).fadeIn();
|
||||
importedfiles += 1;
|
||||
succeded += response.data.imported;
|
||||
failed += response.data.failed;
|
||||
OC.notify({
|
||||
message:t('contacts', '{success} imported, {failed} failed from {file}',
|
||||
{success:response.data.imported, failed:response.data.failed, file:response.data.failed}
|
||||
)
|
||||
});
|
||||
} else {
|
||||
$('.import-upload').show();
|
||||
$('.import-select').hide();
|
||||
}
|
||||
delete uploadingFiles[fileName];
|
||||
numfiles -= 1; uploadedfiles -= 1;
|
||||
$progressbar.progressbar('value',50+(50/(todo-uploadedfiles)));
|
||||
});
|
||||
});
|
||||
@ -1155,80 +1232,34 @@ OC.Contacts = OC.Contacts || {
|
||||
importFiles(aid, uploadingFiles);
|
||||
});
|
||||
|
||||
$('#import_fileupload').fileupload({
|
||||
self.$importFileInput.fileupload({
|
||||
acceptFileTypes: /^text\/(directory|vcard|x-vcard)$/i,
|
||||
add: function(e, data) {
|
||||
var files = data.files;
|
||||
var file = data.files[0];
|
||||
console.log('add', file.name);
|
||||
var totalSize=0;
|
||||
if(files) {
|
||||
numfiles += files.length; uploadedfiles = 0;
|
||||
for(var i=0;i<files.length;i++) {
|
||||
if(files[i].size ==0 && files[i].type== '') {
|
||||
OC.dialogs.alert(t('files', 'Unable to upload your file as it is a directory or has 0 bytes'), t('files', 'Upload Error'));
|
||||
return;
|
||||
}
|
||||
totalSize+=files[i].size;
|
||||
if(file) {
|
||||
numfiles += 1;
|
||||
if(file.size === 0 || file.type== '') {
|
||||
OC.dialogs.alert(t('contacts', 'Unable to upload your file as it is a directory or has 0 bytes'), t('contacts', 'Upload Error'));
|
||||
return;
|
||||
}
|
||||
totalSize+=file.size;
|
||||
}
|
||||
if(totalSize>$('#max_upload').val()) {
|
||||
OC.dialogs.alert(t('contacts','The file you are trying to upload exceed the maximum size for file uploads on this server.'), t('contacts','Upload too large'));
|
||||
numfiles = uploadedfiles = retries = aid = 0;
|
||||
numfiles = uploadedfiles = importedfiles = retries = failed = succeded = aid = 0;
|
||||
uploadingFiles = {};
|
||||
return;
|
||||
} else {
|
||||
if($.support.xhrFileUpload) {
|
||||
$.each(files, function(i, file) {
|
||||
var fileName = file.name;
|
||||
console.log('file.name', file.name);
|
||||
var jqXHR = $('#import_fileupload').fileupload('send',
|
||||
{
|
||||
files: file,
|
||||
formData: function(form) {
|
||||
var formArray = form.serializeArray();
|
||||
formArray['aid'] = aid;
|
||||
return formArray;
|
||||
}})
|
||||
.success(function(response, textStatus, jqXHR) {
|
||||
if(response.status == 'success') {
|
||||
// import the file
|
||||
uploadedfiles += 1;
|
||||
} else {
|
||||
OC.notify({message:response.data.message});
|
||||
$('.import-upload').show();
|
||||
$('.import-select').hide();
|
||||
$('#import-progress').hide();
|
||||
$('#import-status-text').hide();
|
||||
}
|
||||
return false;
|
||||
})
|
||||
.error(function(jqXHR, textStatus, errorThrown) {
|
||||
console.log(textStatus);
|
||||
OC.notify({message:errorThrown + ': ' + textStatus});
|
||||
});
|
||||
uploadingFiles[fileName] = jqXHR;
|
||||
});
|
||||
} else {
|
||||
data.submit().success(function(data, status) {
|
||||
response = jQuery.parseJSON(data[0].body.innerText);
|
||||
if(response[0] != undefined && response[0].status == 'success') {
|
||||
var file=response[0];
|
||||
delete uploadingFiles[file.name];
|
||||
$('tr').filterAttr('data-file',file.name).data('mime',file.mime);
|
||||
var size = $('tr').filterAttr('data-file',file.name).find('td.filesize').text();
|
||||
if(size==t('files','Pending')){
|
||||
$('tr').filterAttr('data-file',file.name).find('td.filesize').text(file.size);
|
||||
}
|
||||
FileList.loadingDone(file.name);
|
||||
} else {
|
||||
OC.notify({message:response.data.message});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
var jqXHR = data.submit();
|
||||
uploadingFiles[file.name] = jqXHR;
|
||||
|
||||
},
|
||||
fail: function(e, data) {
|
||||
console.log('fail');
|
||||
OC.notify({message:data.errorThrown + ': ' + data.textStatus});
|
||||
numfiles = uploadedfiles = importedfiles = retries = failed = succeded = aid = 0;
|
||||
$('.import-upload').show();
|
||||
$('.import-select').hide();
|
||||
// TODO: Remove file from upload queue.
|
||||
@ -1240,19 +1271,12 @@ OC.Contacts = OC.Contacts || {
|
||||
start: function(e, data) {
|
||||
$progressbar.progressbar({value:0});
|
||||
$progressbar.fadeIn();
|
||||
if(data.dataType != 'iframe ') {
|
||||
$('#upload input.stop').show();
|
||||
}
|
||||
},
|
||||
stop: function(e, data) {
|
||||
console.log('stop, data', data);
|
||||
// stop only gets fired once so we collect uploaded items here.
|
||||
$('.import-upload').hide();
|
||||
$('.import-select').show();
|
||||
|
||||
if(data.dataType != 'iframe ') {
|
||||
$('#upload input.stop').hide();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -1261,8 +1285,8 @@ OC.Contacts = OC.Contacts || {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
$(document).on('keypress', function(event) {
|
||||
if(!$(event.target).is('body')) {
|
||||
$(document).on('keyup', function(event) {
|
||||
if(!$(event.target).is('body') || event.isPropagationStopped()) {
|
||||
return;
|
||||
}
|
||||
var keyCode = Math.max(event.keyCode, event.which);
|
||||
@ -1350,41 +1374,123 @@ OC.Contacts = OC.Contacts || {
|
||||
$('.tooltipped.downwards.onfocus').tipsy({trigger: 'focus', gravity: 'n'});
|
||||
$('.tooltipped.rightwards.onfocus').tipsy({trigger: 'focus', gravity: 'w'});
|
||||
},
|
||||
mergeSelectedContacts: function() {
|
||||
var contacts = this.contacts.getSelectedContacts();
|
||||
var self = this;
|
||||
this.$rightContent.append('<div id="merge_contacts_dialog"></div>');
|
||||
if(!this.$mergeContactsTmpl) {
|
||||
this.$mergeContactsTmpl = $('#mergeContactsTemplate');
|
||||
}
|
||||
var $dlg = this.$mergeContactsTmpl.octemplate();
|
||||
var $liTmpl = $dlg.find('li').detach();
|
||||
var $mergeList = $dlg.find('.mergelist');
|
||||
$.each(contacts, function(idx, contact) {
|
||||
var $li = $liTmpl
|
||||
.octemplate({idx: idx, id: contact.getId(), displayname: contact.getDisplayName()});
|
||||
if(!contact.data.thumbnail) {
|
||||
$li.addClass('thumbnail');
|
||||
} else {
|
||||
$li.css('background-image', 'url(data:image/png;base64,' + contact.data.thumbnail + ')');
|
||||
}
|
||||
if(idx === 0) {
|
||||
$li.find('input:radio').prop('checked', true);
|
||||
}
|
||||
$mergeList.append($li);
|
||||
});
|
||||
this.$contactList.addClass('dim');
|
||||
$('#merge_contacts_dialog').html($dlg).ocdialog({
|
||||
closeOnEscape: true,
|
||||
title: t('contacts', 'Merge contacts'),
|
||||
height: 'auto', width: 'auto',
|
||||
buttons: [
|
||||
{
|
||||
text: t('contacts', 'Merge contacts'),
|
||||
click:function() {
|
||||
// Do the merging, use $(this) to get dialog
|
||||
var contactid = $(this).find('input:radio:checked').val();
|
||||
var others = [];
|
||||
var deleteOther = $(this).find('#delete_other').prop('checked');
|
||||
console.log('Selected contact', contactid, 'Delete others', deleteOther);
|
||||
$.each($(this).find('input:radio:not(:checked)'), function(idx, item) {
|
||||
others.push($(item).val());
|
||||
});
|
||||
console.log('others', others);
|
||||
$(document).trigger('request.contact.merge', {
|
||||
merger: contactid,
|
||||
mergees: others,
|
||||
deleteOther: deleteOther
|
||||
});
|
||||
|
||||
$(this).ocdialog('close');
|
||||
},
|
||||
defaultButton: true
|
||||
},
|
||||
{
|
||||
text: t('contacts', 'Cancel'),
|
||||
click:function(dlg) {
|
||||
$(this).ocdialog('close');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
],
|
||||
close: function(event, ui) {
|
||||
$(this).ocdialog('destroy').remove();
|
||||
$('#add_group_dialog').remove();
|
||||
self.$contactList.removeClass('dim');
|
||||
},
|
||||
open: function(event, ui) {
|
||||
$dlg.find('input').focus();
|
||||
}
|
||||
});
|
||||
},
|
||||
addGroup: function(cb) {
|
||||
var self = this;
|
||||
$('body').append('<div id="add_group_dialog"></div>');
|
||||
this.$rightContent.append('<div id="add_group_dialog"></div>');
|
||||
if(!this.$addGroupTmpl) {
|
||||
this.$addGroupTmpl = $('#addGroupTemplate');
|
||||
}
|
||||
this.$contactList.addClass('dim');
|
||||
var $dlg = this.$addGroupTmpl.octemplate();
|
||||
$('#add_group_dialog').html($dlg).dialog({
|
||||
$('#add_group_dialog').html($dlg).ocdialog({
|
||||
modal: true,
|
||||
closeOnEscape: true,
|
||||
title: t('contacts', 'Add group'),
|
||||
height: 'auto', width: 'auto',
|
||||
buttons: {
|
||||
'Ok':function() {
|
||||
self.groups.addGroup(
|
||||
{name:$dlg.find('input:text').val()},
|
||||
function(response) {
|
||||
if(typeof cb === 'function') {
|
||||
cb(response);
|
||||
} else {
|
||||
if(response.status !== 'success') {
|
||||
OC.notify({message: response.message});
|
||||
buttons: [
|
||||
{
|
||||
text: t('contacts', 'OK'),
|
||||
click:function() {
|
||||
var name = $(this).find('input').val();
|
||||
if(name.trim() === '') {
|
||||
return false;
|
||||
}
|
||||
self.groups.addGroup(
|
||||
{name:$dlg.find('input:text').val()},
|
||||
function(response) {
|
||||
if(typeof cb === 'function') {
|
||||
cb(response);
|
||||
} else {
|
||||
if(response.error) {
|
||||
OC.notify({message: response.message});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
$(this).dialog('close');
|
||||
});
|
||||
$(this).ocdialog('close');
|
||||
},
|
||||
defaultButton: true
|
||||
},
|
||||
'Cancel':function() {
|
||||
$(this).dialog('close');
|
||||
return false;
|
||||
{
|
||||
text: t('contacts', 'Cancel'),
|
||||
click:function(dlg) {
|
||||
$(this).ocdialog('close');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
close: function(event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
$(this).ocdialog('destroy').remove();
|
||||
$('#add_group_dialog').remove();
|
||||
self.$contactList.removeClass('dim');
|
||||
},
|
||||
open: function(event, ui) {
|
||||
$dlg.find('input').focus();
|
||||
@ -1401,6 +1507,7 @@ OC.Contacts = OC.Contacts || {
|
||||
this.$rightContent.scrollTop(this.contacts.contactPos(id)-30);
|
||||
},
|
||||
closeContact: function(id) {
|
||||
$(window).unbind('hashchange', this.hashChange);
|
||||
if(typeof this.currentid === 'number') {
|
||||
var contact = this.contacts.findById(id);
|
||||
if(contact && contact.close()) {
|
||||
@ -1419,12 +1526,16 @@ OC.Contacts = OC.Contacts || {
|
||||
$(document).trigger('status.nomorecontacts');
|
||||
}
|
||||
//$('body').unbind('click', this.bodyListener);
|
||||
window.location.hash = '';
|
||||
$(window).bind('hashchange', this.hashChange);
|
||||
},
|
||||
openContact: function(id) {
|
||||
this.hideActions();
|
||||
console.log('Contacts.openContact', id);
|
||||
if(this.currentid) {
|
||||
this.closeContact(this.currentid);
|
||||
}
|
||||
$(window).unbind('hashchange', this.hashChange);
|
||||
this.currentid = parseInt(id);
|
||||
console.log('Contacts.openContact, Favorite', this.currentid, this.groups.isFavorite(this.currentid), this.groups);
|
||||
this.setAllChecked(false);
|
||||
@ -1458,10 +1569,12 @@ OC.Contacts = OC.Contacts || {
|
||||
if($contactelem.find($(e.target)).length === 0) {
|
||||
self.closeContact(self.currentid);
|
||||
}
|
||||
};
|
||||
};*/
|
||||
window.location.hash = this.currentid.toString();
|
||||
setTimeout(function() {
|
||||
$('body').bind('click', self.bodyListener);
|
||||
}, 500);*/
|
||||
//$('body').bind('click', self.bodyListener);
|
||||
$(window).bind('hashchange', this.hashChange);
|
||||
}, 500);
|
||||
},
|
||||
update: function() {
|
||||
console.log('update');
|
||||
@ -1475,7 +1588,6 @@ OC.Contacts = OC.Contacts || {
|
||||
var file = filelist[0];
|
||||
var target = $('#file_upload_target');
|
||||
var form = $('#file_upload_form');
|
||||
form.find('input[name="id"]').val(this.currentid);
|
||||
var totalSize=0;
|
||||
if(file.size > $('#max_upload').val()){
|
||||
OC.notify({
|
||||
@ -1489,7 +1601,10 @@ OC.Contacts = OC.Contacts || {
|
||||
var response=jQuery.parseJSON(target.contents().text());
|
||||
if(response != undefined && response.status == 'success') {
|
||||
console.log('response', response);
|
||||
self.editPhoto(self.currentid, response.data.tmp);
|
||||
self.editPhoto(
|
||||
response.metadata,
|
||||
response.data.tmp
|
||||
);
|
||||
//alert('File: ' + file.tmp + ' ' + file.name + ' ' + file.mime);
|
||||
} else {
|
||||
OC.notify({message:response.data.message});
|
||||
@ -1498,28 +1613,28 @@ OC.Contacts = OC.Contacts || {
|
||||
form.submit();
|
||||
}
|
||||
},
|
||||
cloudPhotoSelected:function(id, path) {
|
||||
cloudPhotoSelected:function(metadata, path) {
|
||||
var self = this;
|
||||
console.log('cloudPhotoSelected, id', id);
|
||||
console.log('cloudPhotoSelected', metadata);
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'oc_photo.php'),
|
||||
{path: path, id: id},function(jsondata) {
|
||||
{path: path, contact: metadata},function(jsondata) {
|
||||
if(jsondata.status == 'success') {
|
||||
//alert(jsondata.data.page);
|
||||
self.editPhoto(jsondata.data.id, jsondata.data.tmp);
|
||||
$('#edit_photo_dialog_img').html(jsondata.data.page);
|
||||
self.editPhoto(metadata, jsondata.data.tmp);
|
||||
//$('#edit_photo_dialog_img').html(jsondata.data.page);
|
||||
}
|
||||
else{
|
||||
OC.notify({message: jsondata.data.message});
|
||||
}
|
||||
});
|
||||
},
|
||||
editCurrentPhoto:function(id) {
|
||||
editCurrentPhoto:function(metadata) {
|
||||
var self = this;
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),
|
||||
{id: id}, function(jsondata) {
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'), metadata,
|
||||
function(jsondata) {
|
||||
if(jsondata.status == 'success') {
|
||||
//alert(jsondata.data.page);
|
||||
self.editPhoto(jsondata.data.id, jsondata.data.tmp);
|
||||
self.editPhoto(metadata, jsondata.data.tmp);
|
||||
$('#edit_photo_dialog_img').html(jsondata.data.page);
|
||||
}
|
||||
else{
|
||||
@ -1527,8 +1642,8 @@ OC.Contacts = OC.Contacts || {
|
||||
}
|
||||
});
|
||||
},
|
||||
editPhoto:function(id, tmpkey) {
|
||||
console.log('editPhoto', id, tmpkey);
|
||||
editPhoto:function(metadata, tmpkey) {
|
||||
console.log('editPhoto', metadata, tmpkey);
|
||||
$('.tipsy').remove();
|
||||
// Simple event handler, called from onChange and onSelect
|
||||
// event handlers, as per the Jcrop invocation above
|
||||
@ -1550,7 +1665,13 @@ OC.Contacts = OC.Contacts || {
|
||||
this.$cropBoxTmpl = $('#cropBoxTemplate');
|
||||
}
|
||||
$('body').append('<div id="edit_photo_dialog"></div>');
|
||||
var $dlg = this.$cropBoxTmpl.octemplate({id: id, tmpkey: tmpkey});
|
||||
var $dlg = this.$cropBoxTmpl.octemplate(
|
||||
{
|
||||
backend: metadata.backend,
|
||||
addressbookid: metadata.addressbookid,
|
||||
contactid: metadata.contactid,
|
||||
tmpkey: tmpkey
|
||||
});
|
||||
|
||||
var cropphoto = new Image();
|
||||
$(cropphoto).load(function () {
|
||||
@ -1590,208 +1711,38 @@ OC.Contacts = OC.Contacts || {
|
||||
});
|
||||
}).error(function () {
|
||||
OC.notify({message:t('contacts','Error loading profile picture.')});
|
||||
}).attr('src', OC.linkTo('contacts', 'tmpphoto.php')+'?tmpkey='+tmpkey);
|
||||
}).attr('src', OC.linkTo('contacts', 'tmpphoto.php')+'?tmpkey='+tmpkey+'&refresh='+Math.random());
|
||||
},
|
||||
savePhoto:function($dlg) {
|
||||
var form = $dlg.find('#cropform');
|
||||
q = form.serialize();
|
||||
console.log('savePhoto', q);
|
||||
$.post(OC.filePath('contacts', 'ajax', 'savecrop.php'), q, function(response) {
|
||||
var jsondata = $.parseJSON(response);
|
||||
console.log('savePhoto, jsondata', typeof jsondata);
|
||||
if(jsondata && jsondata.status === 'success') {
|
||||
//var jsondata = $.parseJSON(response);
|
||||
console.log('savePhoto, response', typeof response);
|
||||
if(response && response.status === 'success') {
|
||||
// load cropped photo.
|
||||
$(document).trigger('status.contact.photoupdated', {
|
||||
id: jsondata.data.id
|
||||
id: response.data.id,
|
||||
thumbnail: response.data.thumbnail
|
||||
});
|
||||
} else {
|
||||
if(!jsondata) {
|
||||
if(!response) {
|
||||
OC.notify({message:t('contacts', 'Network or server error. Please inform administrator.')});
|
||||
} else {
|
||||
OC.notify({message: jsondata.data.message});
|
||||
OC.notify({message: response.data.message});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
addAddressbook:function(data, cb) {
|
||||
$.ajax({
|
||||
type:'POST',
|
||||
async:false,
|
||||
url:OC.filePath('contacts', 'ajax', 'addressbook/add.php'),
|
||||
data:{ name: data.name, description: data.description },
|
||||
success:function(jsondata) {
|
||||
if(jsondata.status == 'success') {
|
||||
if(typeof cb === 'function') {
|
||||
cb({
|
||||
status:'success',
|
||||
addressbook: jsondata.data.addressbook
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if(typeof cb === 'function') {
|
||||
cb({status:'error', message:jsondata.data.message});
|
||||
} else {
|
||||
OC.notify({message:textStatus + ': ' + errorThrown});
|
||||
}
|
||||
}
|
||||
},
|
||||
error:function(jqXHR, textStatus, errorThrown) {
|
||||
if(typeof cb === 'function') {
|
||||
cb({
|
||||
status:'success',
|
||||
message: textStatus + ': ' + errorThrown
|
||||
});
|
||||
} else {
|
||||
OC.notify({message:textStatus + ': ' + errorThrown});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
// NOTE: Deprecated
|
||||
selectAddressbook:function(cb) {
|
||||
var self = this;
|
||||
var jqxhr = $.get(OC.filePath('contacts', 'templates', 'selectaddressbook.html'), function(data) {
|
||||
$('body').append('<div id="addressbook_dialog"></div>');
|
||||
var $dlg = $('#addressbook_dialog').html(data).octemplate({
|
||||
nameplaceholder: t('contacts', 'Enter name'),
|
||||
descplaceholder: t('contacts', 'Enter description')
|
||||
}).dialog({
|
||||
modal: true, height: 'auto', width: 'auto',
|
||||
title: t('contacts', 'Select addressbook'),
|
||||
buttons: {
|
||||
'Ok':function() {
|
||||
aid = $(this).find('input:checked').val();
|
||||
if(aid == 'new') {
|
||||
var displayname = $(this).find('input.name').val();
|
||||
var description = $(this).find('input.desc').val();
|
||||
if(!$.trim(displayname)) {
|
||||
OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error'));
|
||||
return false;
|
||||
}
|
||||
console.log('ID, name and desc', aid, displayname, description);
|
||||
if(typeof cb === 'function') {
|
||||
// TODO: Create addressbook
|
||||
var data = {name:displayname, description:description};
|
||||
self.addAddressbook(data, function(data) {
|
||||
if(data.status === 'success') {
|
||||
cb({
|
||||
status:'success',
|
||||
addressbook:data.addressbook
|
||||
});
|
||||
} else {
|
||||
cb({status:'error'});
|
||||
}
|
||||
});
|
||||
}
|
||||
$(this).dialog('close');
|
||||
} else {
|
||||
console.log('aid ' + aid);
|
||||
if(typeof cb === 'function') {
|
||||
cb({
|
||||
status:'success',
|
||||
addressbook:self.contacts.addressbooks[parseInt(aid)]
|
||||
});
|
||||
}
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
'Cancel':function() {
|
||||
$(this).dialog('close');
|
||||
}
|
||||
},
|
||||
close: function(event, ui) {
|
||||
$(this).dialog('destroy').remove();
|
||||
$('#addressbook_dialog').remove();
|
||||
},
|
||||
open: function(event, ui) {
|
||||
console.log('open', $(this));
|
||||
var $lastrow = $(this).find('tr.new');
|
||||
$.each(self.contacts.addressbooks, function(i, book) {
|
||||
console.log('book', i, book);
|
||||
if(book.owner === OC.currentUser
|
||||
|| (book.permissions & OC.PERMISSION_UPDATE
|
||||
|| book.permissions & OC.PERMISSION_CREATE
|
||||
|| book.permissions & OC.PERMISSION_DELETE)) {
|
||||
var row = '<tr><td><input id="book_{id}" name="book" type="radio" value="{id}"</td>'
|
||||
+ '<td><label for="book_{id}">{displayname}</label></td>'
|
||||
+ '<td>{description}</td></tr>';
|
||||
var $row = $(row).octemplate({
|
||||
id:book.id,
|
||||
displayname:book.displayname,
|
||||
description:book.description
|
||||
});
|
||||
$lastrow.before($row);
|
||||
}
|
||||
});
|
||||
$(this).find('input[type="radio"]').first().prop('checked', true);
|
||||
$lastrow.find('input.name,input.desc').on('focus', function(e) {
|
||||
$lastrow.find('input[type="radio"]').prop('checked', true);
|
||||
});
|
||||
}
|
||||
});
|
||||
}).error(function() {
|
||||
OC.notify({message: t('contacts', 'Network or server error. Please inform administrator.')});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
(function( $ ) {
|
||||
/**
|
||||
* Object Template
|
||||
* Inspired by micro templating done by e.g. underscore.js
|
||||
*/
|
||||
var Template = {
|
||||
init: function(vars, options, elem) {
|
||||
// Mix in the passed in options with the default options
|
||||
this.vars = vars;
|
||||
this.options = $.extend({},this.options,options);
|
||||
|
||||
this.elem = elem;
|
||||
var self = this;
|
||||
|
||||
if(typeof this.options.escapeFunction === 'function') {
|
||||
$.each(this.vars, function(key, val) {
|
||||
if(typeof val === 'string') {
|
||||
self.vars[key] = self.options.escapeFunction(val);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var _html = this._build(this.vars);
|
||||
return $(_html);
|
||||
},
|
||||
// From stackoverflow.com/questions/1408289/best-way-to-do-variable-interpolation-in-javascript
|
||||
_build: function(o){
|
||||
var data = this.elem.attr('type') === 'text/template' ? this.elem.html() : this.elem.get(0).outerHTML;
|
||||
try {
|
||||
return data.replace(/{([^{}]*)}/g,
|
||||
function (a, b) {
|
||||
var r = o[b];
|
||||
return typeof r === 'string' || typeof r === 'number' ? r : a;
|
||||
}
|
||||
);
|
||||
} catch(e) {
|
||||
console.error(e, 'data:', data)
|
||||
}
|
||||
},
|
||||
options: {
|
||||
escapeFunction: function(str) {return $('<i></i>').text(str).html();}
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.octemplate = function(vars, options) {
|
||||
var vars = vars ? vars : {};
|
||||
if(this.length) {
|
||||
var _template = Object.create(Template);
|
||||
return _template.init(vars, options, this);
|
||||
}
|
||||
};
|
||||
|
||||
})( jQuery );
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
OC.Contacts.init();
|
||||
OC.Router.registerLoadedCallback(function() {
|
||||
$.getScript(OC.Router.generate('contacts_jsconfig'), function() {
|
||||
OC.Contacts.init();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -2,8 +2,8 @@
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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
|
||||
@ -20,21 +20,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
// Check if we are a user
|
||||
OCP\JSON::setContentTypeHeader('text/javascript');
|
||||
OCP\JSON::checkLoggedIn();
|
||||
OCP\JSON::checkAppEnabled('contacts');
|
||||
OCP\JSON::callCheck();
|
||||
require_once __DIR__.'/../loghandler.php';
|
||||
|
||||
$id = $_POST['id'];
|
||||
if(!$id) {
|
||||
bailOut(OCA\Contacts\App::$l10n->t('id is not set.'));
|
||||
}
|
||||
$user = OCP\User::getUser();
|
||||
|
||||
try {
|
||||
OCA\Contacts\Addressbook::delete($id);
|
||||
} catch(Exception $e) {
|
||||
bailOut($e->getMessage());
|
||||
}
|
||||
|
||||
OCP\JSON::success(array('data' => array( 'id' => $id )));
|
||||
echo 'var contacts_groups_sortorder=[' . OCP\Config::getUserValue($user, 'contacts', 'groupsort', '') . '],';
|
||||
echo 'contacts_properties_indexed = '
|
||||
. (OCP\Config::getUserValue($user, 'contacts', 'contacts_properties_indexed', 'no') === 'no'
|
||||
? 'false' : 'true') . ',';
|
||||
echo 'contacts_categories_indexed = '
|
||||
. (OCP\Config::getUserValue($user, 'contacts', 'contacts_categories_indexed', 'no') === 'no'
|
||||
? 'false' : 'true') . ',';
|
||||
echo 'lang=\'' . OCP\Config::getUserValue($user, 'core', 'lang', 'en') . '\';';
|
780
js/contacts.js
780
js/contacts.js
@ -7,17 +7,18 @@ OC.Contacts = OC.Contacts || {};
|
||||
* An item which binds the appropriate html and event handlers
|
||||
* @param parent the parent ContactList
|
||||
* @param id The integer contact id.
|
||||
* @param access An access object containing and 'owner' string variable and an integer 'permissions' variable.
|
||||
* @param metadata An metadata object containing and 'owner' string variable and an integer 'permissions' variable.
|
||||
* @param data the data used to populate the contact
|
||||
* @param listtemplate the jquery object used to render the contact list item
|
||||
* @param fulltemplate the jquery object used to render the entire contact
|
||||
* @param detailtemplates A map of jquery objects used to render the contact parts e.g. EMAIL, TEL etc.
|
||||
*/
|
||||
var Contact = function(parent, id, access, data, listtemplate, dragtemplate, fulltemplate, detailtemplates) {
|
||||
//console.log('contact:', id, access); //parent, id, data, listtemplate, fulltemplate);
|
||||
var Contact = function(parent, id, metadata, data, listtemplate, dragtemplate, fulltemplate, detailtemplates) {
|
||||
//console.log('contact:', id, metadata, data); //parent, id, data, listtemplate, fulltemplate);
|
||||
this.parent = parent,
|
||||
this.storage = parent.storage,
|
||||
this.id = id,
|
||||
this.access = access,
|
||||
this.metadata = metadata,
|
||||
this.data = data,
|
||||
this.$dragTemplate = dragtemplate,
|
||||
this.$listTemplate = listtemplate,
|
||||
@ -27,6 +28,98 @@ OC.Contacts = OC.Contacts || {};
|
||||
this.multi_properties = ['EMAIL', 'TEL', 'IMPP', 'ADR', 'URL'];
|
||||
};
|
||||
|
||||
Contact.prototype.metaData = function() {
|
||||
return {
|
||||
contactid: this.id,
|
||||
addressbookid: this.metadata.parent,
|
||||
backend: this.metadata.backend
|
||||
}
|
||||
};
|
||||
|
||||
Contact.prototype.getDisplayName = function() {
|
||||
return this.getPreferredValue('FN');
|
||||
};
|
||||
|
||||
Contact.prototype.getId = function() {
|
||||
return this.id;
|
||||
};
|
||||
|
||||
Contact.prototype.getParent = function() {
|
||||
return this.metadata.parent;
|
||||
};
|
||||
|
||||
Contact.prototype.getBackend = function() {
|
||||
return this.metadata.backend;
|
||||
};
|
||||
|
||||
Contact.prototype.merge = function(mergees) {
|
||||
console.log('Contact.merge, mergees', mergees);
|
||||
if(!mergees instanceof Array && !mergees instanceof Contact) {
|
||||
throw new TypeError('BadArgument: Contact.merge() only takes Contacts');
|
||||
} else {
|
||||
if(mergees instanceof Contact) {
|
||||
mergees = [mergees];
|
||||
}
|
||||
}
|
||||
|
||||
// For multi_properties
|
||||
var addIfNotExists = function(name, newproperty) {
|
||||
// If the property isn't set at all just add it and return.
|
||||
if(!self.data[name]) {
|
||||
self.data[name] = [newproperty];
|
||||
return;
|
||||
}
|
||||
var found = false;
|
||||
$.each(self.data[name], function(idx, property) {
|
||||
if(name === 'ADR') {
|
||||
// Do a simple string comparison
|
||||
if(property.value.join(';').toLowerCase() === newproperty.value.join(';').toLowerCase()) {
|
||||
found = true;
|
||||
return false; // break loop
|
||||
}
|
||||
} else {
|
||||
if(property.value.toLowerCase() === newproperty.value.toLowerCase()) {
|
||||
found = true;
|
||||
return false; // break loop
|
||||
}
|
||||
}
|
||||
});
|
||||
if(found) {
|
||||
return;
|
||||
}
|
||||
// Not found, so adding it.
|
||||
self.data[name].push(newproperty);
|
||||
}
|
||||
|
||||
var self = this;
|
||||
$.each(mergees, function(idx, mergee) {
|
||||
console.log('Contact.merge, mergee', mergee);
|
||||
if(!mergee instanceof Contact) {
|
||||
throw new TypeError('BadArgument: Contact.merge() only takes Contacts');
|
||||
}
|
||||
if(mergee === self) {
|
||||
throw new Error('BadArgument: Why should I merge with myself?');
|
||||
}
|
||||
$.each(mergee.data, function(name, properties) {
|
||||
if(self.multi_properties.indexOf(name) === -1) {
|
||||
if(self.data[name] && self.data[name].length > 0) {
|
||||
// If the property exists don't touch it.
|
||||
return true; // continue
|
||||
} else {
|
||||
// Otherwise add it.
|
||||
self.data[name] = properties;
|
||||
}
|
||||
} else {
|
||||
$.each(properties, function(idx, property) {
|
||||
addIfNotExists(name, property);
|
||||
});
|
||||
}
|
||||
});
|
||||
console.log('Merged', self.data);
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
Contact.prototype.showActions = function(act) {
|
||||
this.$footer.children().hide();
|
||||
if(act && act.length > 0) {
|
||||
@ -68,7 +161,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
newvalue: params.newvalue,
|
||||
oldvalue: params.oldvalue
|
||||
});
|
||||
console.log('undoQueue', this.undoQueue);
|
||||
//console.log('undoQueue', this.undoQueue);
|
||||
}
|
||||
|
||||
Contact.prototype.addProperty = function($option, name) {
|
||||
@ -137,6 +230,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
console.log('Contact.deleteProperty, element', element, $container);
|
||||
var params = {
|
||||
name: element,
|
||||
backend: this.metadata.backend,
|
||||
addressbookid: this.metadata.parent,
|
||||
id: this.id
|
||||
};
|
||||
if(this.multi_properties.indexOf(element) !== -1) {
|
||||
@ -153,16 +248,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
this.setAsSaving(obj, true);
|
||||
var self = this;
|
||||
$.post(OC.filePath('contacts', 'ajax', 'contact/deleteproperty.php'), params, function(jsondata) {
|
||||
if(!jsondata) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
status: 'error',
|
||||
message: t('contacts', 'Network or server error. Please inform administrator.')
|
||||
});
|
||||
self.setAsSaving(obj, false);
|
||||
return false;
|
||||
}
|
||||
if(jsondata.status == 'success') {
|
||||
$.when(this.storage.deleteProperty(this.metadata.backend, this.metadata.parent, this.id, params))
|
||||
.then(function(response) {
|
||||
if(!response.error) {
|
||||
// TODO: Test if removing from internal data structure works
|
||||
if(self.multi_properties.indexOf(element) !== -1) {
|
||||
// First find out if an existing element by looking for checksum
|
||||
@ -206,19 +294,68 @@ OC.Contacts = OC.Contacts || {};
|
||||
return true;
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
status: 'error',
|
||||
message: jsondata.data.message
|
||||
message: response.message
|
||||
});
|
||||
self.setAsSaving(obj, false);
|
||||
return false;
|
||||
}
|
||||
},'json');
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.warn( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed deleting property: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Save all properties. Used for merging contacts.
|
||||
* If this is a new contact it will first be saved to the datastore and a
|
||||
* new datastructure will be added to the object.
|
||||
*/
|
||||
Contact.prototype.saveAll = function(cb) {
|
||||
console.log('Contact.saveAll');
|
||||
if(!this.id) {
|
||||
var self = this;
|
||||
this.add({isnew:true}, function(response) {
|
||||
if(response.error) {
|
||||
console.warn('No response object');
|
||||
return false;
|
||||
}
|
||||
self.saveAll();
|
||||
});
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
this.setAsSaving(this.$fullelem, true);
|
||||
var data = JSON.stringify(this.data);
|
||||
//console.log('stringified', data);
|
||||
$.when(this.storage.saveAllProperties(this.metadata.backend, this.metadata.parent, this.id, data))
|
||||
.then(function(response) {
|
||||
if(!response.error) {
|
||||
self.data = response.data.data;
|
||||
self.metadata = response.data.metadata;
|
||||
if(typeof cb === 'function') {
|
||||
cb({error:false});
|
||||
}
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: response.message
|
||||
});
|
||||
if(typeof cb === 'function') {
|
||||
cb({error:true, message:response.message});
|
||||
}
|
||||
}
|
||||
self.setAsSaving(self.$fullelem, false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Act on change of a property.
|
||||
* If this is a new contact it will first be saved to the datastore and a
|
||||
* new datastructure will be added to the object. FIXME: Not implemented yet.
|
||||
* new datastructure will be added to the object.
|
||||
* If the obj argument is not provided 'name' and 'value' MUST be provided
|
||||
* and this is only allowed for single elements like N, FN, CATEGORIES.
|
||||
* @param obj. The form form field that has changed.
|
||||
@ -241,32 +378,25 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
var obj = null;
|
||||
var element = null;
|
||||
var q = '';
|
||||
var args = [];
|
||||
if(params.obj) {
|
||||
obj = params.obj;
|
||||
q = this.queryStringFor(obj);
|
||||
args = this.argumentsFor(obj);
|
||||
args['parameters'] = this.parametersFor(obj);
|
||||
element = this.propertyTypeFor(obj);
|
||||
} else {
|
||||
args = params;
|
||||
element = params.name;
|
||||
var value = utils.isArray(params.value)
|
||||
? $.param(params.value)
|
||||
: encodeURIComponent(params.value);
|
||||
q = 'id=' + this.id + '&value=' + value + '&name=' + element;
|
||||
}
|
||||
console.log('q', q);
|
||||
console.log('args', args);
|
||||
var self = this;
|
||||
this.setAsSaving(obj, true);
|
||||
$.post(OC.filePath('contacts', 'ajax', 'contact/saveproperty.php'), q, function(jsondata){
|
||||
if(!jsondata) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
status: 'error',
|
||||
message: t('contacts', 'Network or server error. Please inform administrator.')
|
||||
});
|
||||
$(obj).addClass('error');
|
||||
self.setAsSaving(obj, false);
|
||||
return false;
|
||||
}
|
||||
if(jsondata.status == 'success') {
|
||||
$.when(this.storage.saveProperty(this.metadata.backend, this.metadata.parent, this.id, args))
|
||||
.then(function(response) {
|
||||
if(!response.error) {
|
||||
if(!self.data[element]) {
|
||||
self.data[element] = [];
|
||||
}
|
||||
@ -283,7 +413,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
self.pushToUndo({
|
||||
action:'save',
|
||||
name: element,
|
||||
newchecksum: jsondata.data.checksum,
|
||||
newchecksum: response.data.checksum,
|
||||
oldchecksum: checksum,
|
||||
newvalue: value,
|
||||
oldvalue: obj.defaultValue
|
||||
@ -294,7 +424,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
name: element,
|
||||
value: value,
|
||||
parameters: parameters,
|
||||
checksum: jsondata.data.checksum
|
||||
checksum: response.data.checksum
|
||||
};
|
||||
return false;
|
||||
}
|
||||
@ -304,17 +434,17 @@ OC.Contacts = OC.Contacts || {};
|
||||
self.pushToUndo({
|
||||
action:'add',
|
||||
name: element,
|
||||
newchecksum: jsondata.data.checksum,
|
||||
newchecksum: response.data.checksum,
|
||||
newvalue: value,
|
||||
});
|
||||
self.data[element].push({
|
||||
name: element,
|
||||
value: value,
|
||||
parameters: parameters,
|
||||
checksum: jsondata.data.checksum,
|
||||
checksum: response.data.checksum,
|
||||
});
|
||||
}
|
||||
self.propertyContainerFor(obj).data('checksum', jsondata.data.checksum);
|
||||
self.propertyContainerFor(obj).data('checksum', response.data.checksum);
|
||||
} else {
|
||||
// Save value and parameters internally
|
||||
var value = obj ? self.valueFor(obj) : params.value;
|
||||
@ -327,6 +457,16 @@ OC.Contacts = OC.Contacts || {};
|
||||
case 'CATEGORIES':
|
||||
// We deal with this in addToGroup()
|
||||
break;
|
||||
case 'BDAY':
|
||||
// reverse order again.
|
||||
value = $.datepicker.formatDate('yy-mm-dd', $.datepicker.parseDate('dd-mm-yy', value));
|
||||
self.data[element][0] = {
|
||||
name: element,
|
||||
value: value,
|
||||
parameters: self.parametersFor(obj),
|
||||
checksum: response.data.checksum
|
||||
};
|
||||
break;
|
||||
case 'FN':
|
||||
if(!self.data.FN || !self.data.FN.length) {
|
||||
self.data.FN = [{name:'FN', value:'', parameters:[]}];
|
||||
@ -380,7 +520,6 @@ OC.Contacts = OC.Contacts || {};
|
||||
$fullname.val(self.data.FN[0]['value']);
|
||||
update_fn = true;
|
||||
} else if($fullname.val() == value[1] + ' ') {
|
||||
console.log('change', value);
|
||||
self.data.FN[0]['value'] = value[1] + ' ' + value[0];
|
||||
$fullname.val(self.data.FN[0]['value']);
|
||||
update_fn = true;
|
||||
@ -395,7 +534,6 @@ OC.Contacts = OC.Contacts || {};
|
||||
}, 1000);
|
||||
}
|
||||
case 'NICKNAME':
|
||||
case 'BDAY':
|
||||
case 'ORG':
|
||||
case 'TITLE':
|
||||
case 'NOTE':
|
||||
@ -403,7 +541,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
name: element,
|
||||
value: value,
|
||||
parameters: self.parametersFor(obj),
|
||||
checksum: jsondata.data.checksum
|
||||
checksum: response.data.checksum
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@ -418,13 +556,12 @@ OC.Contacts = OC.Contacts || {};
|
||||
return true;
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
status: 'error',
|
||||
message: jsondata.data.message
|
||||
message: response.message
|
||||
});
|
||||
self.setAsSaving(obj, false);
|
||||
return false;
|
||||
}
|
||||
},'json');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -499,22 +636,16 @@ OC.Contacts = OC.Contacts || {};
|
||||
* @returns The callback gets an object as argument with a variable 'status' of either 'success'
|
||||
* or 'error'. On success the 'data' property of that object contains the contact id as 'id', the
|
||||
* addressbook id as 'aid' and the contact data structure as 'details'.
|
||||
* TODO: Use Storage for adding and make sure to get all metadata.
|
||||
*/
|
||||
Contact.prototype.add = function(params, cb) {
|
||||
var self = this;
|
||||
$.post(OC.filePath('contacts', 'ajax', 'contact/add.php'),
|
||||
params, function(jsondata) {
|
||||
if(!jsondata) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
status: 'error',
|
||||
message: t('contacts', 'Network or server error. Please inform administrator.')
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if(jsondata.status === 'success') {
|
||||
self.id = parseInt(jsondata.data.id);
|
||||
self.access.id = parseInt(jsondata.data.aid);
|
||||
self.data = jsondata.data.details;
|
||||
$.when(this.storage.addContact(this.metadata.backend, this.metadata.parent))
|
||||
.then(function(response) {
|
||||
if(!response.error) {
|
||||
self.id = String(response.data.metadata.id);
|
||||
self.metadata = response.data.metadata;
|
||||
self.data = response.data.data;
|
||||
self.$groupSelect.multiselect('enable');
|
||||
// Add contact to current group
|
||||
if(self.groupprops && self.groupprops.currentgroup.id !== 'all'
|
||||
@ -534,9 +665,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
id: self.id,
|
||||
contact: self
|
||||
});
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: response.message
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if(typeof cb == 'function') {
|
||||
cb(jsondata);
|
||||
cb(response);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -548,9 +684,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
*/
|
||||
Contact.prototype.destroy = function(cb) {
|
||||
var self = this;
|
||||
$.post(OC.filePath('contacts', 'ajax', 'contact/delete.php'),
|
||||
{id: this.id}, function(jsondata) {
|
||||
if(jsondata && jsondata.status === 'success') {
|
||||
$.when(this.storage.deleteContact(
|
||||
this.metadata.backend,
|
||||
this.metadata.parent,
|
||||
this.id)
|
||||
).then(function(response) {
|
||||
//$.post(OC.filePath('contacts', 'ajax', 'contact/delete.php'),
|
||||
// {id: this.id}, function(response) {
|
||||
if(!response.error) {
|
||||
if(self.$listelem) {
|
||||
self.$listelem.remove();
|
||||
}
|
||||
@ -559,22 +700,49 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
}
|
||||
if(typeof cb == 'function') {
|
||||
var retval = {status: jsondata ? jsondata.status : 'error'};
|
||||
if(jsondata) {
|
||||
if(jsondata.status === 'success') {
|
||||
retval['id'] = jsondata.id;
|
||||
} else {
|
||||
retval['message'] = jsondata.message;
|
||||
}
|
||||
if(response.error) {
|
||||
cb(response);
|
||||
} else {
|
||||
retval['message'] = t('contacts', 'There was an unknown error when trying to delete this contact');
|
||||
retval['id'] = self.id;
|
||||
cb({id:self.id});
|
||||
}
|
||||
cb(retval);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Contact.prototype.argumentsFor = function(obj) {
|
||||
var args = {};
|
||||
var ptype = this.propertyTypeFor(obj);
|
||||
args['name'] = ptype;
|
||||
|
||||
if(this.multi_properties.indexOf(ptype) !== -1) {
|
||||
args['checksum'] = this.checksumFor(obj);
|
||||
}
|
||||
|
||||
if($(obj).hasClass('propertycontainer')) {
|
||||
if($(obj).is('select[data-element="categories"]')) {
|
||||
args['value'] = [];
|
||||
$.each($(obj).find(':selected'), function(idx, e) {
|
||||
args['value'].push($(e).text());
|
||||
});
|
||||
} else {
|
||||
args['value'] = $(obj).val();
|
||||
}
|
||||
} else {
|
||||
var $elements = this.propertyContainerFor(obj)
|
||||
.find('input.value,select.value,textarea.value');
|
||||
if($elements.length > 1) {
|
||||
args['value'] = [];
|
||||
$.each($elements, function(idx, e) {
|
||||
args['value'][parseInt($(e).attr('name').substr(6,1))] = $(e).val();
|
||||
//args['value'].push($(e).val());
|
||||
});
|
||||
} else {
|
||||
args['value'] = $elements.val();
|
||||
}
|
||||
}
|
||||
return args;
|
||||
};
|
||||
|
||||
Contact.prototype.queryStringFor = function(obj) {
|
||||
var q = 'id=' + this.id;
|
||||
var ptype = this.propertyTypeFor(obj);
|
||||
@ -617,7 +785,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
Contact.prototype.valueFor = function(obj) {
|
||||
var $container = this.propertyContainerFor(obj);
|
||||
console.assert($container.length > 0, 'Couldn\'t find container for ' + $(obj));
|
||||
return $container.is('input')
|
||||
return $container.is('input.value')
|
||||
? $container.val()
|
||||
: (function() {
|
||||
var $elem = $container.find('textarea.value,input.value:not(:checkbox)');
|
||||
@ -636,23 +804,29 @@ OC.Contacts = OC.Contacts || {};
|
||||
|
||||
Contact.prototype.parametersFor = function(obj, asText) {
|
||||
var parameters = [];
|
||||
$.each(this.propertyContainerFor(obj).find('select.parameter,input:checkbox:checked.parameter,textarea'), function(i, elem) {
|
||||
$.each(this.propertyContainerFor(obj)
|
||||
.find('select.parameter,input:checkbox:checked.parameter,textarea'), // Why do I look for textarea?
|
||||
function(i, elem) {
|
||||
var $elem = $(elem);
|
||||
var paramname = $elem.data('parameter');
|
||||
if(!parameters[paramname]) {
|
||||
parameters[paramname] = [];
|
||||
}
|
||||
var val;
|
||||
if(asText) {
|
||||
if($elem.is(':checkbox')) {
|
||||
val = $elem.attr('title');
|
||||
} else if($elem.is('select')) {
|
||||
val = $elem.find(':selected').text();
|
||||
if($elem.is(':checkbox')) {
|
||||
if(asText) {
|
||||
parameters[paramname].push($elem.attr('title'));
|
||||
} else {
|
||||
parameters[paramname].push($elem.attr('value'));
|
||||
}
|
||||
} else {
|
||||
val = $elem.val();
|
||||
} else if($elem.is('select')) {
|
||||
$.each($elem.find(':selected'), function(idx, e) {
|
||||
if(asText) {
|
||||
parameters[paramname].push($(e).text());
|
||||
} else {
|
||||
parameters[paramname].push($(e).val());
|
||||
}
|
||||
});
|
||||
}
|
||||
parameters[paramname].push(val);
|
||||
});
|
||||
return parameters;
|
||||
};
|
||||
@ -673,6 +847,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
name: this.getPreferredValue('FN', '')
|
||||
});
|
||||
}
|
||||
this.setThumbnail(this.$dragelem);
|
||||
return this.$dragelem;
|
||||
}
|
||||
|
||||
@ -683,18 +858,23 @@ OC.Contacts = OC.Contacts || {};
|
||||
Contact.prototype.renderListItem = function(isnew) {
|
||||
this.$listelem = this.$listTemplate.octemplate({
|
||||
id: this.id,
|
||||
name: isnew ? this.getPreferredValue('FN', '') : this.getPreferredValue('FN', ''),
|
||||
email: isnew ? this.getPreferredValue('EMAIL', '') : this.getPreferredValue('EMAIL', ''),
|
||||
tel: isnew ? this.getPreferredValue('TEL', '') : this.getPreferredValue('TEL', ''),
|
||||
adr: isnew ? this.getPreferredValue('ADR', []).clean('').join(', ') : this.getPreferredValue('ADR', []).clean('').join(', '),
|
||||
parent: this.metadata.parent,
|
||||
backend: this.metadata.backend,
|
||||
name: this.getPreferredValue('FN', ''),
|
||||
email: this.getPreferredValue('EMAIL', ''),
|
||||
tel: this.getPreferredValue('TEL', ''),
|
||||
adr: this.getPreferredValue('ADR', []).clean('').join(', '),
|
||||
categories: this.getPreferredValue('CATEGORIES', [])
|
||||
.clean('').join(' / ')
|
||||
});
|
||||
if(this.access.owner !== OC.currentUser
|
||||
&& !(this.access.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.access.permissions & OC.PERMISSION_DELETE)) {
|
||||
if(this.metadata.owner !== OC.currentUser
|
||||
&& !(this.metadata.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.metadata.permissions & OC.PERMISSION_DELETE)) {
|
||||
this.$listelem.find('input:checkbox').prop('disabled', true).css('opacity', '0');
|
||||
}
|
||||
if(isnew) {
|
||||
this.setThumbnail();
|
||||
}
|
||||
return this.$listelem;
|
||||
};
|
||||
|
||||
@ -723,7 +903,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
});
|
||||
self.$groupSelect.bind('multiselectclick', function(event, ui) {
|
||||
var action = ui.checked ? 'addtogroup' : 'removefromgroup';
|
||||
console.assert(typeof self.id === 'number', 'ID is not a number')
|
||||
console.assert(typeof self.id === 'string', 'ID is not a string')
|
||||
$(document).trigger('request.contact.' + action, {
|
||||
id: self.id,
|
||||
groupid: parseInt(ui.value)
|
||||
@ -739,6 +919,34 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
};
|
||||
|
||||
var buildAddressBookSelect = function(availableAddressBooks) {
|
||||
console.log('address books', availableAddressBooks.length, availableAddressBooks);
|
||||
/* TODO:
|
||||
* - Check address books permissions.
|
||||
* - Add method to change address book.
|
||||
*/
|
||||
$.each(availableAddressBooks, function(idx, addressBook) {
|
||||
//console.log('addressBook', idx, addressBook);
|
||||
var $option = $('<option data-backend="'
|
||||
+ addressBook.backend + '" value="' + addressBook.id + '">'
|
||||
+ addressBook.displayname + '(' + addressBook.backend + ')</option>');
|
||||
if(self.metadata.parent === addressBook.id
|
||||
&& self.metadata.backend === addressBook.backend) {
|
||||
$option.attr('selected', 'selected');
|
||||
}
|
||||
self.$addressBookSelect.append($option);
|
||||
});
|
||||
self.$addressBookSelect.multiselect({
|
||||
header: false,
|
||||
selectedList: 3,
|
||||
noneSelectedText: self.$addressBookSelect.attr('title'),
|
||||
selectedText: t('contacts', '# groups')
|
||||
});
|
||||
if(self.id) {
|
||||
self.$addressBookSelect.multiselect('disable');
|
||||
}
|
||||
};
|
||||
|
||||
var n = this.getPreferredValue('N', ['', '', '', '', '']);
|
||||
//console.log('Contact.renderContact', this.data);
|
||||
var values = this.data
|
||||
@ -770,6 +978,11 @@ OC.Contacts = OC.Contacts || {};
|
||||
this.$groupSelect = this.$fullelem.find('#contactgroups');
|
||||
buildGroupSelect(groupprops.groups);
|
||||
|
||||
if(Object.keys(this.parent.addressbooks).length > 1) {
|
||||
this.$addressBookSelect = this.$fullelem.find('#contactaddressbooks');
|
||||
buildAddressBookSelect(this.parent.addressbooks);
|
||||
}
|
||||
|
||||
this.$addMenu = this.$fullelem.find('#addproperty');
|
||||
this.$addMenu.on('change', function(event) {
|
||||
//console.log('add', $(this).val());
|
||||
@ -820,13 +1033,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
id: self.id
|
||||
});
|
||||
} else if($(this).is('.export')) {
|
||||
$(document).trigger('request.contact.export', {
|
||||
id: self.id
|
||||
});
|
||||
$(document).trigger('request.contact.export', self.metaData());
|
||||
} else if($(this).is('.delete')) {
|
||||
$(document).trigger('request.contact.delete', {
|
||||
id: self.id
|
||||
});
|
||||
$(document).trigger('request.contact.delete', self.metaData());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@ -847,7 +1056,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
if($(this).hasClass('value') && this.value === this.defaultValue) {
|
||||
return;
|
||||
}
|
||||
console.log('change', this.defaultValue, this.value);
|
||||
//console.log('change', this.defaultValue, this.value);
|
||||
this.defaultValue = this.value;
|
||||
self.saveProperty({obj:event.target});
|
||||
});
|
||||
|
||||
@ -971,9 +1181,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
if($meta.length) {
|
||||
$meta.html(meta.join('/'));
|
||||
}
|
||||
if(self.access.owner === OC.currentUser
|
||||
|| self.access.permissions & OC.PERMISSION_UPDATE
|
||||
|| self.access.permissions & OC.PERMISSION_DELETE) {
|
||||
if(self.metadata.owner === OC.currentUser
|
||||
|| self.metadata.permissions & OC.PERMISSION_UPDATE
|
||||
|| self.metadata.permissions & OC.PERMISSION_DELETE) {
|
||||
$property.find('select.type[name="parameters[TYPE][]"]')
|
||||
.combobox({
|
||||
singleclick: true,
|
||||
@ -985,9 +1195,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
}
|
||||
});
|
||||
if(this.access.owner !== OC.currentUser
|
||||
&& !(this.access.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.access.permissions & OC.PERMISSION_DELETE)) {
|
||||
if(this.metadata.owner !== OC.currentUser
|
||||
&& !(this.metadata.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.metadata.permissions & OC.PERMISSION_DELETE)) {
|
||||
this.setEnabled(false);
|
||||
this.showActions(['close', 'export']);
|
||||
} else {
|
||||
@ -998,9 +1208,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
};
|
||||
|
||||
Contact.prototype.isEditable = function() {
|
||||
return ((this.access.owner === OC.currentUser)
|
||||
|| (this.access.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.access.permissions & OC.PERMISSION_DELETE));
|
||||
return ((this.metadata.owner === OC.currentUser)
|
||||
|| (this.metadata.permissions & OC.PERMISSION_UPDATE
|
||||
|| this.metadata.permissions & OC.PERMISSION_DELETE));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1063,7 +1273,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
var val = self.valueFor(input);
|
||||
var params = self.parametersFor(input, true);
|
||||
$(this).find('.meta').html(params['TYPE'].join('/'));
|
||||
$(this).find('.adr').html(escapeHTML(self.valueFor($editor.find('input').first()).clean('').join(', ')));
|
||||
$(this).find('.adr').html(self.valueFor($editor.find('input').first()).clean('').join(', '));
|
||||
$(this).next('.listactions').css('display', 'inline-block');
|
||||
$('body').unbind('click', bodyListener);
|
||||
});
|
||||
@ -1151,27 +1361,68 @@ OC.Contacts = OC.Contacts || {};
|
||||
return this.detailTemplates['impp'].octemplate(values);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a thumbnail for the contact if a PHOTO property exists
|
||||
*/
|
||||
Contact.prototype.setThumbnail = function($elem, refresh) {
|
||||
if(!this.data.thumbnail && !refresh) {
|
||||
return;
|
||||
}
|
||||
if(!$elem) {
|
||||
$elem = this.getListItemElement().find('td.name');
|
||||
}
|
||||
if(!$elem.hasClass('thumbnail') && !refresh) {
|
||||
return;
|
||||
}
|
||||
if(this.data.thumbnail) {
|
||||
$elem.removeClass('thumbnail');
|
||||
$elem.css('background-image', 'url(data:image/png;base64,' + this.data.thumbnail + ')');
|
||||
} else {
|
||||
$elem.addClass('thumbnail');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the PHOTO property.
|
||||
*/
|
||||
Contact.prototype.loadPhoto = function(dontloadhandlers) {
|
||||
var self = this;
|
||||
var id = this.id || 'new';
|
||||
var refreshstr = '&refresh='+Math.random();
|
||||
this.$photowrapper = this.$fullelem.find('#photowrapper');
|
||||
this.$photowrapper.addClass('loading').addClass('wait');
|
||||
var id = this.id || 'new',
|
||||
backend = this.metadata.backend,
|
||||
parent = this.metadata.parent,
|
||||
src;
|
||||
|
||||
var $phototools = this.$fullelem.find('#phototools');
|
||||
delete this.photo;
|
||||
$('img.contactphoto').remove();
|
||||
this.photo = new Image();
|
||||
$(this.photo).load(function () {
|
||||
$(this).addClass('contactphoto');
|
||||
self.$photowrapper.css({width: $(this).get(0).width + 10, height: $(this).get(0).height + 10});
|
||||
if(!this.$photowrapper) {
|
||||
this.$photowrapper = this.$fullelem.find('#photowrapper');
|
||||
}
|
||||
|
||||
var finishLoad = function(image) {
|
||||
console.log('finishLoad', self.getDisplayName(), image.width, image.height);
|
||||
$(image).addClass('contactphoto');
|
||||
self.$photowrapper.css({width: image.width + 10, height: image.height + 10});
|
||||
self.$photowrapper.removeClass('loading').removeClass('wait');
|
||||
$(this).insertAfter($phototools).fadeIn();
|
||||
}).error(function () {
|
||||
OC.notify({message:t('contacts','Error loading profile picture.')});
|
||||
}).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+id+refreshstr);
|
||||
$(image).insertAfter($phototools).fadeIn();
|
||||
};
|
||||
|
||||
this.$photowrapper.addClass('loading').addClass('wait');
|
||||
if(this.getPreferredValue('PHOTO', null) === null) {
|
||||
$.when(this.storage.getDefaultPhoto())
|
||||
.then(function(image) {
|
||||
$('img.contactphoto').detach();
|
||||
finishLoad(image);
|
||||
});
|
||||
} else {
|
||||
$.when(this.storage.getContactPhoto(backend, parent, id))
|
||||
.then(function(image) {
|
||||
$('img.contactphoto').remove();
|
||||
finishLoad(image);
|
||||
})
|
||||
.fail(function(defaultImage) {
|
||||
$('img.contactphoto').remove();
|
||||
finishLoad(defaultImage);
|
||||
});
|
||||
}
|
||||
|
||||
if(!dontloadhandlers && this.isEditable()) {
|
||||
this.$photowrapper.on('mouseenter', function(event) {
|
||||
@ -1190,19 +1441,13 @@ OC.Contacts = OC.Contacts || {};
|
||||
$phototools.find('li a').tipsy();
|
||||
|
||||
$phototools.find('.edit').on('click', function() {
|
||||
$(document).trigger('request.edit.contactphoto', {
|
||||
id: self.id
|
||||
});
|
||||
$(document).trigger('request.edit.contactphoto', self.metaData());
|
||||
});
|
||||
$phototools.find('.cloud').on('click', function() {
|
||||
$(document).trigger('request.select.contactphoto.fromcloud', {
|
||||
id: self.id
|
||||
});
|
||||
$(document).trigger('request.select.contactphoto.fromcloud', self.metaData());
|
||||
});
|
||||
$phototools.find('.upload').on('click', function() {
|
||||
$(document).trigger('request.select.contactphoto.fromlocal', {
|
||||
id: self.id
|
||||
});
|
||||
$(document).trigger('request.select.contactphoto.fromlocal', self.metaData());
|
||||
});
|
||||
if(this.data && this.data.PHOTO) {
|
||||
$phototools.find('.delete').show();
|
||||
@ -1211,11 +1456,11 @@ OC.Contacts = OC.Contacts || {};
|
||||
$phototools.find('.delete').hide();
|
||||
$phototools.find('.edit').hide();
|
||||
}
|
||||
$(document).bind('status.contact.photoupdated', function(e, result) {
|
||||
$(document).bind('status.contact.photoupdated', function(e, data) {
|
||||
console.log('status.contact.photoupdated', data);
|
||||
self.loadPhoto(true);
|
||||
var refreshstr = '&refresh='+Math.random();
|
||||
self.getListItemElement().find('td.name')
|
||||
.css('background', 'url(' + OC.filePath('', '', 'remote.php')+'/contactthumbnail?id='+self.id+refreshstr + ')');
|
||||
self.data.thumbnail = data.thumbnail;
|
||||
self.setThumbnail(null, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1346,12 +1591,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
};
|
||||
|
||||
Contact.prototype.next = function() {
|
||||
var $next = this.$listelem.next('tr:visible');
|
||||
// This used to work..?
|
||||
//var $next = this.$listelem.next('tr:visible');
|
||||
var $next = this.$listelem.nextAll('tr').filter(':visible').first();
|
||||
if($next.length > 0) {
|
||||
this.$listelem.removeClass('active');
|
||||
$next.addClass('active');
|
||||
$(document).trigger('status.contact.currentlistitem', {
|
||||
id: parseInt($next.data('id')),
|
||||
id: String($next.data('id')),
|
||||
pos: Math.round($next.position().top),
|
||||
height: Math.round($next.height())
|
||||
});
|
||||
@ -1359,12 +1606,13 @@ OC.Contacts = OC.Contacts || {};
|
||||
};
|
||||
|
||||
Contact.prototype.prev = function() {
|
||||
var $prev = this.$listelem.prev('tr:visible');
|
||||
//var $prev = this.$listelem.prev('tr:visible');
|
||||
var $prev = this.$listelem.prevAll('tr').filter(':visible').first();
|
||||
if($prev.length > 0) {
|
||||
this.$listelem.removeClass('active');
|
||||
$prev.addClass('active');
|
||||
$(document).trigger('status.contact.currentlistitem', {
|
||||
id: parseInt($prev.data('id')),
|
||||
id: String($prev.data('id')),
|
||||
pos: Math.round($prev.position().top),
|
||||
height: Math.round($prev.height())
|
||||
});
|
||||
@ -1372,6 +1620,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
};
|
||||
|
||||
var ContactList = function(
|
||||
storage,
|
||||
contactlist,
|
||||
contactlistitemtemplate,
|
||||
contactdragitemtemplate,
|
||||
@ -1382,17 +1631,19 @@ OC.Contacts = OC.Contacts || {};
|
||||
var self = this;
|
||||
this.length = 0;
|
||||
this.contacts = {};
|
||||
this.addressbooks = {};
|
||||
this.deletionQueue = [];
|
||||
this.storage = storage;
|
||||
this.$contactList = contactlist;
|
||||
this.$contactDragItemTemplate = contactdragitemtemplate;
|
||||
this.$contactListItemTemplate = contactlistitemtemplate;
|
||||
this.$contactFullTemplate = contactfulltemplate;
|
||||
this.contactDetailTemplates = contactdetailtemplates;
|
||||
this.$contactList.scrollTop(0);
|
||||
this.loadContacts(0);
|
||||
this.getAddressBooks();
|
||||
$(document).bind('status.contact.added', function(e, data) {
|
||||
self.length += 1;
|
||||
self.contacts[parseInt(data.id)] = data.contact;
|
||||
self.contacts[String(data.id)] = data.contact;
|
||||
self.insertContact(data.contact.renderListItem(true));
|
||||
});
|
||||
|
||||
@ -1404,6 +1655,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the number of contacts in the list
|
||||
* @return integer
|
||||
*/
|
||||
ContactList.prototype.count = function() {
|
||||
return Object.keys(this.contacts.contacts).length
|
||||
}
|
||||
|
||||
/**
|
||||
* Show/hide contacts belonging to an addressbook.
|
||||
* @param int aid. Addressbook id.
|
||||
@ -1412,9 +1671,9 @@ OC.Contacts = OC.Contacts || {};
|
||||
*/
|
||||
ContactList.prototype.showFromAddressbook = function(aid, show, hideothers) {
|
||||
console.log('ContactList.showFromAddressbook', aid, show);
|
||||
aid = parseInt(aid);
|
||||
aid = String(aid);
|
||||
for(var contact in this.contacts) {
|
||||
if(this.contacts[contact].access.id === aid) {
|
||||
if(this.contacts[contact].metadata.parent === aid) {
|
||||
this.contacts[contact].getListItemElement().toggle(show);
|
||||
} else if(hideothers) {
|
||||
this.contacts[contact].getListItemElement().hide();
|
||||
@ -1429,7 +1688,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
ContactList.prototype.showSharedAddressbooks = function(show) {
|
||||
console.log('ContactList.showSharedAddressbooks', show);
|
||||
for(var contact in this.contacts) {
|
||||
if(this.contacts[contact].access.owner !== OC.currentUser) {
|
||||
if(this.contacts[contact].metadata.owner !== OC.currentUser) {
|
||||
if(show) {
|
||||
this.contacts[contact].getListItemElement().show();
|
||||
} else {
|
||||
@ -1444,6 +1703,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
* @param Array contacts. A list of contact ids.
|
||||
*/
|
||||
ContactList.prototype.showContacts = function(contacts) {
|
||||
console.log('showContacts', contacts);
|
||||
var self = this;
|
||||
if(contacts.length === 0) {
|
||||
// ~5 times faster
|
||||
$('tr:visible.contact').hide();
|
||||
@ -1451,7 +1712,16 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
if(contacts === 'all') {
|
||||
// ~2 times faster
|
||||
$('tr.contact:not(:visible)').show();
|
||||
var $elems = $('tr.contact:not(:visible)');
|
||||
$elems.show();
|
||||
$.each($elems, function(idx, elem) {
|
||||
try {
|
||||
var id = $(elem).data('id');
|
||||
self.contacts[id].setThumbnail();
|
||||
} catch(e) {
|
||||
console.warn('Failed getting id from', $elem, e);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
for(var id in this.contacts) {
|
||||
@ -1459,10 +1729,11 @@ OC.Contacts = OC.Contacts || {};
|
||||
if(contact === null) {
|
||||
continue;
|
||||
}
|
||||
if(contacts.indexOf(parseInt(id)) === -1) {
|
||||
if(contacts.indexOf(String(id)) === -1) {
|
||||
contact.getListItemElement().hide();
|
||||
} else {
|
||||
contact.getListItemElement().show();
|
||||
contact.setThumbnail();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1503,34 +1774,54 @@ OC.Contacts = OC.Contacts || {};
|
||||
*/
|
||||
ContactList.prototype.findById = function(id) {
|
||||
if(!id) {
|
||||
console.warn('id missing');
|
||||
console.warn('ContactList.findById: id missing');
|
||||
console.trace();
|
||||
return false;
|
||||
}
|
||||
id = parseInt(id);
|
||||
id = String(id);
|
||||
if(typeof this.contacts[id] === 'undefined') {
|
||||
console.warn('Could not find contact with id', id);
|
||||
console.trace();
|
||||
return null;
|
||||
}
|
||||
return this.contacts[parseInt(id)];
|
||||
return this.contacts[String(id)];
|
||||
};
|
||||
|
||||
ContactList.prototype.delayedDelete = function(id) {
|
||||
/**
|
||||
* @param object data An object or array of objects containing contact identification
|
||||
* {
|
||||
* contactid: '1234',
|
||||
* addressbookid: '4321',
|
||||
* backend: 'local'
|
||||
* }
|
||||
*/
|
||||
ContactList.prototype.delayedDelete = function(data) {
|
||||
console.log('delayedDelete, data:', typeof data, data);
|
||||
var self = this;
|
||||
if(utils.isUInt(id)) {
|
||||
if(!utils.isArray(data)) {
|
||||
this.currentContact = null;
|
||||
self.$contactList.show();
|
||||
this.deletionQueue.push(id);
|
||||
} else if(utils.isArray(id)) {
|
||||
$.extend(this.deletionQueue, id);
|
||||
//self.$contactList.show();
|
||||
if(data instanceof Contact) {
|
||||
this.deletionQueue.push(data);
|
||||
} else {
|
||||
var contact = this.findById(data.contactid);
|
||||
this.deletionQueue.push(contact);
|
||||
}
|
||||
} else if(utils.isArray(data)) {
|
||||
$.each(data, function(idx, contact) {
|
||||
//console.log('delayedDelete, meta:', contact);
|
||||
self.deletionQueue.push(contact);
|
||||
});
|
||||
//$.extend(this.deletionQueue, data);
|
||||
} else {
|
||||
throw { name: 'WrongParameterType', message: 'ContactList.delayedDelete only accept integers or arrays.'};
|
||||
throw { name: 'WrongParameterType', message: 'ContactList.delayedDelete only accept objects or arrays.'};
|
||||
}
|
||||
$.each(this.deletionQueue, function(idx, id) {
|
||||
self.contacts[id].detach().setChecked(false);
|
||||
//console.log('delayedDelete, deletionQueue', this.deletionQueue);
|
||||
$.each(this.deletionQueue, function(idx, contact) {
|
||||
//console.log('delayedDelete', contact);
|
||||
contact.detach().setChecked(false);
|
||||
});
|
||||
console.log('deletionQueue', this.deletionQueue);
|
||||
//console.log('deletionQueue', this.deletionQueue);
|
||||
if(!window.onbeforeunload) {
|
||||
window.onbeforeunload = function(e) {
|
||||
e = e || window.event;
|
||||
@ -1548,16 +1839,16 @@ OC.Contacts = OC.Contacts || {};
|
||||
message:t('contacts','Click to undo deletion of {num} contacts', {num: self.deletionQueue.length}),
|
||||
//timeout:5,
|
||||
timeouthandler:function() {
|
||||
console.log('timeout');
|
||||
//console.log('timeout');
|
||||
// Don't fire all deletes at once
|
||||
self.deletionTimer = setInterval(function() {
|
||||
self.deleteContacts();
|
||||
}, 500);
|
||||
},
|
||||
clickhandler:function() {
|
||||
console.log('clickhandler');
|
||||
$.each(self.deletionQueue, function(idx, id) {
|
||||
self.insertContact(self.contacts[id].getListItemElement());
|
||||
//console.log('clickhandler');
|
||||
$.each(self.deletionQueue, function(idx, contact) {
|
||||
self.insertContact(contact.getListItemElement());
|
||||
});
|
||||
OC.notify({cancel:true});
|
||||
OC.notify({message:t('contacts', 'Cancelled deletion of {num}', {num: self.deletionQueue.length})});
|
||||
@ -1568,20 +1859,19 @@ OC.Contacts = OC.Contacts || {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a contact with this id
|
||||
* @param id the id of the contact
|
||||
* Delete contacts in the queue
|
||||
*/
|
||||
ContactList.prototype.deleteContacts = function() {
|
||||
var self = this;
|
||||
console.log('ContactList.deleteContacts, deletionQueue', this.deletionQueue);
|
||||
//console.log('ContactList.deleteContacts, deletionQueue', this.deletionQueue);
|
||||
if(typeof this.deletionTimer === 'undefined') {
|
||||
console.log('No deletion timer!');
|
||||
window.onbeforeunload = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var id = this.deletionQueue.shift();
|
||||
if(typeof id === 'undefined') {
|
||||
var contact = this.deletionQueue.shift();
|
||||
if(typeof contact === 'undefined') {
|
||||
clearInterval(this.deletionTimer);
|
||||
delete this.deletionTimer;
|
||||
window.onbeforeunload = null;
|
||||
@ -1589,13 +1879,10 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
|
||||
// Let contact remove itself.
|
||||
var contact = this.findById(id);
|
||||
if(contact === null) {
|
||||
return false;
|
||||
}
|
||||
var id = contact.getId();
|
||||
contact.destroy(function(response) {
|
||||
console.log('deleteContact', response, self.length);
|
||||
if(response.status === 'success') {
|
||||
if(!response.error) {
|
||||
delete self.contacts[id];
|
||||
$(document).trigger('status.contact.deleted', {
|
||||
id: id
|
||||
@ -1661,10 +1948,17 @@ OC.Contacts = OC.Contacts || {};
|
||||
* @param object props
|
||||
*/
|
||||
ContactList.prototype.addContact = function(props) {
|
||||
var addressBook = this.addressbooks[Object.keys(this.addressbooks)[0]]
|
||||
var metadata = {
|
||||
parent: addressBook.id,
|
||||
backend: addressBook.backend,
|
||||
permissions: addressBook.permissions,
|
||||
owner: addressBook.owner
|
||||
};
|
||||
var contact = new Contact(
|
||||
this,
|
||||
null,
|
||||
{owner:OC.currentUser, permissions: 31},
|
||||
metadata,
|
||||
null,
|
||||
this.$contactListItemTemplate,
|
||||
this.$contactDragItemTemplate,
|
||||
@ -1672,7 +1966,6 @@ OC.Contacts = OC.Contacts || {};
|
||||
this.contactDetailTemplates
|
||||
);
|
||||
if(utils.isUInt(this.currentContact)) {
|
||||
console.assert(typeof this.currentContact == 'number', 'this.currentContact is not a number');
|
||||
this.contacts[this.currentContact].close();
|
||||
}
|
||||
return contact.renderContact(props);
|
||||
@ -1681,13 +1974,15 @@ OC.Contacts = OC.Contacts || {};
|
||||
/**
|
||||
* Get contacts selected in list
|
||||
*
|
||||
* @returns array of integer contact ids.
|
||||
* @returns array of contact ids.
|
||||
*/
|
||||
ContactList.prototype.getSelectedContacts = function() {
|
||||
var contacts = [];
|
||||
|
||||
var self = this;
|
||||
$.each(this.$contactList.find('tr > td > input:checkbox:visible:checked'), function(a, b) {
|
||||
contacts.push(parseInt($(b).parents('tr').first().data('id')));
|
||||
var id = String($(b).parents('tr').first().data('id'));
|
||||
contacts.push(self.contacts[id]);
|
||||
});
|
||||
return contacts;
|
||||
};
|
||||
@ -1703,7 +1998,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
self.contacts[contact].setCurrent(false);
|
||||
});
|
||||
}
|
||||
this.contacts[parseInt(id)].setCurrent(true);
|
||||
this.contacts[String(id)].setCurrent(true);
|
||||
};
|
||||
|
||||
// Should only be neccesary with progressive loading, but it's damn fast, so... ;)
|
||||
@ -1715,6 +2010,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
return $(a).find('td.name').text().toUpperCase().localeCompare($(b).find('td.name').text().toUpperCase());
|
||||
});
|
||||
|
||||
// TODO: Test if I couldn't just append rows.
|
||||
var items = [];
|
||||
$.each(rows, function(index, row) {
|
||||
items.push(row);
|
||||
@ -1741,40 +2037,88 @@ OC.Contacts = OC.Contacts || {};
|
||||
* @param object book
|
||||
*/
|
||||
ContactList.prototype.setAddressbook = function(book) {
|
||||
this.addressbooks[parseInt(book.id)] = {
|
||||
owner: book.userid,
|
||||
uri: book.uri,
|
||||
permissions: parseInt(book.permissions),
|
||||
id: parseInt(book.id),
|
||||
displayname: book.displayname,
|
||||
description: book.description,
|
||||
active: Boolean(parseInt(book.active))
|
||||
};
|
||||
console.log('setAddressbook', book.id, this.addressbooks);
|
||||
var id = String(book.id);
|
||||
this.addressbooks[id] = book;
|
||||
};
|
||||
|
||||
/**
|
||||
* Load contacts
|
||||
* @param int offset
|
||||
*/
|
||||
ContactList.prototype.loadContacts = function(offset, cb) {
|
||||
ContactList.prototype.getAddressBooks = function() {
|
||||
var self = this;
|
||||
// Should the actual ajax call be in the controller?
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'contact/list.php'), {offset: offset}, function(jsondata) {
|
||||
if (jsondata && jsondata.status == 'success') {
|
||||
//console.log('ContactList.loadContacts', jsondata.data);
|
||||
self.addressbooks = {};
|
||||
$.each(jsondata.data.addressbooks, function(i, book) {
|
||||
self.setAddressbook(book);
|
||||
$.when(this.storage.getAddressBooksForUser()).then(function(response) {
|
||||
console.log('ContactList.getAddressBooks', response);
|
||||
if(!response.error) {
|
||||
var num = response.data.addressbooks.length;
|
||||
$.each(response.data.addressbooks, function(idx, addressBook) {
|
||||
self.setAddressbook(addressBook);
|
||||
self.loadContacts(
|
||||
addressBook['backend'],
|
||||
addressBook['id'],
|
||||
function(cbresponse) {
|
||||
//console.log('loaded', idx, cbresponse);
|
||||
num -= 1;
|
||||
if(num === 0) {
|
||||
if(self.length > 0) {
|
||||
setTimeout(function() {
|
||||
self.doSort(); // TODO: Test this
|
||||
self.setCurrent(self.$contactList.find('tr:visible').first().data('id'), false);
|
||||
}
|
||||
, 2000);
|
||||
}
|
||||
$(document).trigger('status.contacts.loaded', {
|
||||
status: true,
|
||||
numcontacts: self.length
|
||||
});
|
||||
if(self.length === 0) {
|
||||
$(document).trigger('status.nomorecontacts');
|
||||
}
|
||||
}
|
||||
if(cbresponse.error) {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message:
|
||||
t('contacts', 'Failed loading contacts from {addressbook}: {error}',
|
||||
{addressbook:addressBook['displayname'], error:err})
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: response.message
|
||||
});
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.warn( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed loading address books: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load contacts
|
||||
* @param int offset
|
||||
*/
|
||||
ContactList.prototype.loadContacts = function(backend, addressBookId, cb) {
|
||||
var self = this;
|
||||
$.when(this.storage.getContacts(backend, addressBookId)).then(function(response) {
|
||||
//console.log('ContactList.loadContacts', response);
|
||||
if(!response.error) {
|
||||
var items = [];
|
||||
if(jsondata.data.contacts.length === 0) {
|
||||
$(document).trigger('status.nomorecontacts');
|
||||
}
|
||||
$.each(jsondata.data.contacts, function(c, contact) {
|
||||
self.contacts[parseInt(contact.id)]
|
||||
$.each(response.data.contacts, function(c, contact) {
|
||||
var id = String(contact.metadata.id);
|
||||
contact.metadata.backend = backend;
|
||||
self.contacts[id]
|
||||
= new Contact(
|
||||
self,
|
||||
parseInt(contact.id),
|
||||
self.addressbooks[parseInt(contact.aid)],
|
||||
id,
|
||||
contact.metadata,
|
||||
contact.data,
|
||||
self.$contactListItemTemplate,
|
||||
self.$contactDragItemTemplate,
|
||||
@ -1782,14 +2126,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
self.contactDetailTemplates
|
||||
);
|
||||
self.length +=1;
|
||||
var $item = self.contacts[parseInt(contact.id)].renderListItem();
|
||||
var $item = self.contacts[id].renderListItem();
|
||||
items.push($item.get(0));
|
||||
$item.find('td.name').draggable({
|
||||
cursor: 'move',
|
||||
distance: 10,
|
||||
revert: 'invalid',
|
||||
helper: function (e,ui) {
|
||||
return self.contacts[parseInt(contact.id)].renderDragItem().appendTo('body');
|
||||
return self.contacts[id].renderDragItem().appendTo('body');
|
||||
},
|
||||
opacity: 1,
|
||||
scope: 'contacts'
|
||||
@ -1802,22 +2146,24 @@ OC.Contacts = OC.Contacts || {};
|
||||
if(items.length > 0) {
|
||||
self.$contactList.append(items);
|
||||
}
|
||||
setTimeout(function() {
|
||||
self.doSort();
|
||||
self.setCurrent(self.$contactList.find('tr:visible:first-child').data('id'), false);
|
||||
}
|
||||
, 2000);
|
||||
$(document).trigger('status.contacts.loaded', {
|
||||
status: true,
|
||||
numcontacts: jsondata.data.contacts.length,
|
||||
is_indexed: jsondata.data.is_indexed
|
||||
cb({error:false});
|
||||
} else {
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: response.message
|
||||
});
|
||||
cb({
|
||||
error:true,
|
||||
message: response.message
|
||||
});
|
||||
}
|
||||
if(typeof cb === 'function') {
|
||||
cb();
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.warn( "Request Failed: " + err);
|
||||
cb({error: true, message: err});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
OC.Contacts.ContactList = ContactList;
|
||||
|
||||
})(window, jQuery, OC);
|
||||
|
103
js/groups.js
103
js/groups.js
@ -16,7 +16,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
* 'contacts': An array of contact ids belonging to that group
|
||||
* 'obj': A reference to the groupList object.
|
||||
*/
|
||||
var GroupList = function(groupList, listItemTmpl) {
|
||||
var GroupList = function(storage, groupList, listItemTmpl) {
|
||||
this.storage = storage;
|
||||
this.$groupList = groupList;
|
||||
var self = this;
|
||||
var numtypes = ['category', 'fav', 'all'];
|
||||
@ -29,7 +30,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
if($(event.target).is('.action.delete')) {
|
||||
var id = $(event.target).parents('h3').first().data('id');
|
||||
self.deleteGroup(id, function(response) {
|
||||
if(response.status !== 'success') {
|
||||
if(response.error) {
|
||||
OC.notify({message:response.data.message});
|
||||
}
|
||||
});
|
||||
@ -129,7 +130,6 @@ OC.Contacts = OC.Contacts || {};
|
||||
* @param function cb. Optional callback function.
|
||||
*/
|
||||
GroupList.prototype.setAsFavorite = function(contactid, state, cb) {
|
||||
contactid = parseInt(contactid);
|
||||
var $groupelem = this.findById('fav');
|
||||
var contacts = $groupelem.data('contacts');
|
||||
if(state) {
|
||||
@ -185,7 +185,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
var self = this;
|
||||
var doPost = false;
|
||||
if(typeof contactid === 'number') {
|
||||
if(typeof contactid === 'string') {
|
||||
if(contacts.indexOf(contactid) === -1) {
|
||||
ids.push(contactid);
|
||||
doPost = true;
|
||||
@ -211,14 +211,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
console.warn('Invalid data type: ' + typeof contactid);
|
||||
}
|
||||
if(doPost) {
|
||||
$.post(OC.filePath('contacts', 'ajax', 'categories/addto.php'), {contactids: ids, categoryid: groupid},function(jsondata) {
|
||||
if(!jsondata) {
|
||||
if(typeof cb === 'function') {
|
||||
cb({status:'error', message:'Network or server error. Please inform administrator.'});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(jsondata.status === 'success') {
|
||||
$.when(this.storage.addToGroup(ids, groupid)).then(function(response) {
|
||||
if(!response.error) {
|
||||
contacts = contacts.concat(ids).sort();
|
||||
$groupelem.data('contacts', contacts);
|
||||
var $numelem = $groupelem.find('.numcontacts');
|
||||
@ -237,7 +231,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
} else {
|
||||
if(typeof cb == 'function') {
|
||||
cb({status:'error', message:jsondata.data.message});
|
||||
cb({status:'error', message:response.message});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -300,14 +294,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
}
|
||||
if(doPost) {
|
||||
$.post(OC.filePath('contacts', 'ajax', 'categories/removefrom.php'), {contactids: ids, categoryid: groupid},function(jsondata) {
|
||||
if(!jsondata) {
|
||||
if(typeof cb === 'function') {
|
||||
cb({status:'error', message:'Network or server error. Please inform administrator.'});
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(jsondata.status === 'success') {
|
||||
$.when(this.storage.removeFromGroup(ids, groupid)).then(function(response) {
|
||||
if(!response.error) {
|
||||
$.each(ids, function(idx, id) {
|
||||
contacts.splice(contacts.indexOf(id), 1);
|
||||
});
|
||||
@ -323,7 +311,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
} else {
|
||||
if(typeof cb == 'function') {
|
||||
cb({status:'error', message:jsondata.data.message});
|
||||
cb({status:'error', message:response.message});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -363,7 +351,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
var dragitem = ui.draggable, droptarget = $(this);
|
||||
console.log('dropped', dragitem);
|
||||
if(dragitem.is('.name')) {
|
||||
var id = dragitem.parent().data('id');
|
||||
var id = String(dragitem.parent().data('id'));
|
||||
console.log('contact dropped', id, 'on', $(this).data('id'));
|
||||
if($(this).data('type') === 'fav') {
|
||||
$(this).data('obj').setAsFavorite(id, true);
|
||||
@ -397,22 +385,35 @@ OC.Contacts = OC.Contacts || {};
|
||||
var contacts = $elem.data('contacts');
|
||||
var self = this;
|
||||
console.log('delete group', groupid, contacts);
|
||||
$.post(OC.filePath('contacts', 'ajax', 'categories/delete.php'), {categories: name}, function(jsondata) {
|
||||
if (jsondata && jsondata.status == 'success') {
|
||||
$.when(this.storage.deleteGroup(name)).then(function(response) {
|
||||
if (!response.error) {
|
||||
$.each(self.categories, function(idx, category) {
|
||||
if(category.id === groupid) {
|
||||
self.categories.splice(self.categories.indexOf(category), 1);
|
||||
return false; // Break loop
|
||||
}
|
||||
});
|
||||
$(document).trigger('status.group.groupremoved', {
|
||||
groupid: groupid,
|
||||
newgroupid: parseInt($newelem.data('id')),
|
||||
groupname: self.nameById(groupid),
|
||||
groupname: name,
|
||||
contacts: contacts
|
||||
});
|
||||
$elem.remove();
|
||||
self.selectGroup({element:$newelem});
|
||||
} else {
|
||||
//
|
||||
console.log('Error', response);
|
||||
}
|
||||
if(typeof cb === 'function') {
|
||||
cb(jsondata);
|
||||
cb(response);
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed deleting group: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -441,7 +442,7 @@ OC.Contacts = OC.Contacts || {};
|
||||
$input.prop('disabled', true);
|
||||
$elem.data('rawname', '');
|
||||
self.addGroup({name:name, element: $elem}, function(response) {
|
||||
if(response.status === 'success') {
|
||||
if(!response.error) {
|
||||
$elem.prepend(escapeHTML(response.name)).removeClass('editing').attr('data-id', response.id);
|
||||
$input.next('.checked').remove();
|
||||
$input.remove();
|
||||
@ -545,10 +546,10 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
return;
|
||||
}
|
||||
$.post(OC.filePath('contacts', 'ajax', 'categories/add.php'), {category: name}, function(jsondata) {
|
||||
if (jsondata && jsondata.status == 'success') {
|
||||
name = jsondata.data.name;
|
||||
var id = jsondata.data.id;
|
||||
$.when(this.storage.addGroup(name)).then(function(response) {
|
||||
if (!response.error) {
|
||||
name = response.data.name;
|
||||
var id = response.data.id;
|
||||
var tmpl = self.$groupListItemTemplate;
|
||||
var $elem = params.element
|
||||
? params.element
|
||||
@ -578,13 +579,20 @@ OC.Contacts = OC.Contacts || {};
|
||||
$elem.tipsy({trigger:'manual', gravity:'w', fallback: t('contacts', 'You can drag groups to\narrange them as you like.')});
|
||||
$elem.tipsy('show');
|
||||
if(typeof cb === 'function') {
|
||||
cb({status:'success', id:parseInt(id), name:name});
|
||||
cb({id:parseInt(id), name:name});
|
||||
}
|
||||
} else {
|
||||
if(typeof cb === 'function') {
|
||||
cb({status:'error', message:jsondata.data.message});
|
||||
cb({error:true, message:response.data.message});
|
||||
}
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed adding group: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -595,15 +603,14 @@ OC.Contacts = OC.Contacts || {};
|
||||
var tmpl = this.$groupListItemTemplate;
|
||||
|
||||
tmpl.octemplate({id: 'all', type: 'all', num: numcontacts, name: t('contacts', 'All')}).appendTo($groupList);
|
||||
$.getJSON(OC.filePath('contacts', 'ajax', 'categories/list.php'), {}, function(jsondata) {
|
||||
if (jsondata && jsondata.status == 'success') {
|
||||
self.lastgroup = jsondata.data.lastgroup;
|
||||
self.sortorder = jsondata.data.sortorder.length > 0
|
||||
? $.map(jsondata.data.sortorder.split(','), function(c) {return parseInt(c);})
|
||||
: [];
|
||||
$.when(this.storage.getGroupsForUser()).then(function(response) {
|
||||
if (response && !response.error) {
|
||||
self.lastgroup = response.data.lastgroup;
|
||||
self.sortorder = contacts_groups_sortorder;
|
||||
console.log('sortorder', self.sortorder);
|
||||
// Favorites
|
||||
var contacts = $.map(jsondata.data.favorites, function(c) {return parseInt(c);});
|
||||
// Map to strings easier lookup an contacts list.
|
||||
var contacts = $.map(response.data.favorites, function(c) {return String(c);});
|
||||
var $elem = tmpl.octemplate({
|
||||
id: 'fav',
|
||||
type: 'fav',
|
||||
@ -627,8 +634,8 @@ OC.Contacts = OC.Contacts || {};
|
||||
}
|
||||
console.log('favorites', $elem.data('contacts'));
|
||||
// Normal groups
|
||||
$.each(jsondata.data.categories, function(c, category) {
|
||||
var contacts = $.map(category.contacts, function(c) {return parseInt(c);});
|
||||
$.each(response.data.categories, function(c, category) {
|
||||
var contacts = $.map(category.contacts, function(c) {return String(c);});
|
||||
var $elem = (tmpl).octemplate({
|
||||
id: category.id,
|
||||
type: 'category',
|
||||
@ -663,13 +670,13 @@ OC.Contacts = OC.Contacts || {};
|
||||
});
|
||||
|
||||
// Shared addressbook
|
||||
$.each(jsondata.data.shared, function(c, shared) {
|
||||
$.each(response.data.shared, function(c, shared) {
|
||||
var sharedindicator = '<img class="shared svg" src="' + OC.imagePath('core', 'actions/shared') + '"'
|
||||
+ 'title="' + t('contacts', 'Shared by {owner}', {owner:shared.userid}) + '" />';
|
||||
var $elem = (tmpl).octemplate({
|
||||
id: shared.id,
|
||||
type: 'shared',
|
||||
num: '', //jsondata.data.shared.length,
|
||||
num: response.data.shared.length,
|
||||
name: shared.displayname
|
||||
});
|
||||
$elem.find('.numcontacts').after(sharedindicator);
|
||||
@ -703,6 +710,10 @@ OC.Contacts = OC.Contacts || {};
|
||||
if(typeof cb === 'function') {
|
||||
cb();
|
||||
}
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
});
|
||||
};
|
||||
|
||||
|
194
js/jquery.ocdialog.js
Normal file
194
js/jquery.ocdialog.js
Normal file
@ -0,0 +1,194 @@
|
||||
(function($) {
|
||||
$.widget('oc.ocdialog', {
|
||||
options: {
|
||||
width: 'auto',
|
||||
height: 'auto',
|
||||
closeButton: true,
|
||||
closeOnEscape: true
|
||||
},
|
||||
_create: function() {
|
||||
console.log('ocdialog._create');
|
||||
var self = this;
|
||||
|
||||
this.originalCss = {
|
||||
display: this.element[0].style.display,
|
||||
width: this.element[0].style.width,
|
||||
height: this.element[0].style.height,
|
||||
};
|
||||
|
||||
this.originalTitle = this.element.attr('title');
|
||||
this.options.title = this.options.title || this.originalTitle;
|
||||
|
||||
this.$dialog = $('<div class="oc-dialog" />')
|
||||
.attr({
|
||||
// Setting tabIndex makes the div focusable
|
||||
tabIndex: -1,
|
||||
role: 'dialog'
|
||||
})
|
||||
.insertBefore(this.element);
|
||||
this.$dialog.append(this.element.detach());
|
||||
this.element.removeAttr('title').addClass('oc-dialog-content').appendTo(this.$dialog);
|
||||
|
||||
this.$dialog.css({
|
||||
display: 'inline-block',
|
||||
position: 'fixed'
|
||||
});
|
||||
|
||||
$(document).on('keydown keyup', function(event) {
|
||||
if(event.target !== self.$dialog.get(0) && self.$dialog.find($(event.target)).length === 0) {
|
||||
return;
|
||||
}
|
||||
// Escape
|
||||
if(event.keyCode === 27 && self.options.closeOnEscape) {
|
||||
self.close();
|
||||
return false;
|
||||
}
|
||||
// Enter
|
||||
if(event.keyCode === 13) {
|
||||
event.stopImmediatePropagation();
|
||||
if(event.type === 'keyup') {
|
||||
event.preventDefault();
|
||||
return false;
|
||||
}
|
||||
// If no button is selected we trigger the primary
|
||||
if(self.$buttonrow && self.$buttonrow.find($(event.target)).length === 0) {
|
||||
var $button = self.$buttonrow.find('button.primary');
|
||||
if($button) {
|
||||
$button.trigger('click');
|
||||
}
|
||||
} else if(self.$buttonrow) {
|
||||
$(event.target).trigger('click');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
$(window).resize(function() {
|
||||
self.parent = self.$dialog.parent().length > 0 ? self.$dialog.parent() : $('body');
|
||||
var pos = self.parent.position();
|
||||
self.$dialog.css({
|
||||
left: pos.left + (self.parent.width() - self.$dialog.outerWidth())/2,
|
||||
top: pos.top + (self.parent.height() - self.$dialog.outerHeight())/2
|
||||
});
|
||||
});
|
||||
|
||||
this._setOptions(this.options);
|
||||
$(window).trigger('resize');
|
||||
},
|
||||
_init: function() {
|
||||
console.log('ocdialog._init');
|
||||
this.$dialog.focus();
|
||||
this._trigger('open');
|
||||
},
|
||||
_setOption: function(key, value) {
|
||||
console.log('_setOption', key, value);
|
||||
var self = this;
|
||||
switch(key) {
|
||||
case 'title':
|
||||
var $title = $('<h3 class="oc-dialog-title">' + this.options.title
|
||||
+ '</h3>'); //<hr class="oc-dialog-separator" />');
|
||||
if(this.$title) {
|
||||
this.$title.replaceWith($title);
|
||||
} else {
|
||||
this.$title = $title.prependTo(this.$dialog);
|
||||
}
|
||||
this._setSizes();
|
||||
break;
|
||||
case 'buttons':
|
||||
var $buttonrow = $('<div class="oc-dialog-buttonrow" />');
|
||||
if(this.$buttonrow) {
|
||||
this.$buttonrow.replaceWith($buttonrow);
|
||||
} else {
|
||||
this.$buttonrow = $buttonrow.appendTo(this.$dialog);
|
||||
}
|
||||
$.each(value, function(idx, val) {
|
||||
var $button = $('<button>' + val.text + '</button>');
|
||||
if(val.defaultButton) {
|
||||
$button.addClass('primary');
|
||||
self.$defaultButton = $button;
|
||||
}
|
||||
self.$buttonrow.append($button);
|
||||
$button.click(function() {
|
||||
val.click.apply(self.element[0], arguments);
|
||||
});
|
||||
});
|
||||
this.$buttonrow.find('button')
|
||||
.on('focus', function(event) {
|
||||
self.$buttonrow.find('button').removeClass('primary');
|
||||
$(this).addClass('primary');
|
||||
});
|
||||
this._setSizes();
|
||||
break;
|
||||
case 'closeButton':
|
||||
console.log('closeButton', value);
|
||||
if(value) {
|
||||
var $closeButton = $('<a class="oc-dialog-close svg"></a>');
|
||||
console.log('closeButton', $closeButton);
|
||||
this.$dialog.prepend($closeButton);
|
||||
$closeButton.on('click', function() {
|
||||
self.close();
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'width':
|
||||
this.$dialog.css('width', value);
|
||||
break;
|
||||
case 'height':
|
||||
this.$dialog.css('height', value);
|
||||
break;
|
||||
case 'close':
|
||||
this.closeCB = value;
|
||||
break;
|
||||
}
|
||||
//this._super(key, value);
|
||||
$.Widget.prototype._setOption.apply(this, arguments );
|
||||
},
|
||||
_setOptions: function(options) {
|
||||
console.log('_setOptions', options);
|
||||
//this._super(options);
|
||||
$.Widget.prototype._setOptions.apply(this, arguments);
|
||||
},
|
||||
_setSizes: function() {
|
||||
var content_height = this.$dialog.height();
|
||||
if(this.$title) {
|
||||
content_height -= this.$title.outerHeight(true);
|
||||
}
|
||||
if(this.$buttonrow) {
|
||||
content_height -= this.$buttonrow.outerHeight(true);
|
||||
}
|
||||
this.parent = this.$dialog.parent().length > 0 ? this.$dialog.parent() : $('body');
|
||||
content_height = Math.min(content_height, this.parent.height()-20)
|
||||
this.element.css({
|
||||
height: content_height + 'px',
|
||||
width: this.$dialog.innerWidth() + 'px'
|
||||
});
|
||||
},
|
||||
widget: function() {
|
||||
return this.$dialog
|
||||
},
|
||||
close: function() {
|
||||
console.log('close');
|
||||
var self = this;
|
||||
// Ugly hack to catch remaining keyup events.
|
||||
setTimeout(function() {
|
||||
self._trigger('close', self);
|
||||
self.$dialog.hide();
|
||||
}, 200);
|
||||
},
|
||||
destroy: function() {
|
||||
console.log('destroy');
|
||||
if(this.$title) {
|
||||
this.$title.remove()
|
||||
}
|
||||
if(this.$buttonrow) {
|
||||
this.$buttonrow.remove()
|
||||
}
|
||||
|
||||
if(this.originalTitle) {
|
||||
this.element.attr('title', this.originalTitle);
|
||||
}
|
||||
this.element.removeClass('oc-dialog-content')
|
||||
.css(this.originalCss).detach().insertBefore(this.$dialog);
|
||||
this.$dialog.remove();
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
444
js/storage.js
Normal file
444
js/storage.js
Normal file
@ -0,0 +1,444 @@
|
||||
OC.Contacts = OC.Contacts || {};
|
||||
|
||||
/**
|
||||
* TODO: Use $.Deferred.
|
||||
*/
|
||||
|
||||
(function(window, $, OC) {
|
||||
'use strict';
|
||||
|
||||
var JSONResponse = function(response) {
|
||||
if(!response || !response.status || response.status === 'error') {
|
||||
this.error = true;
|
||||
this.message = response.data.message || 'Unknown error.';
|
||||
} else {
|
||||
this.error = false;
|
||||
if(response.data) {
|
||||
this.data = response.data;
|
||||
} else {
|
||||
this.data = response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object for saving contact data to backends
|
||||
*
|
||||
* All methods returns a jQuery.Deferred object which resolves
|
||||
* to either the requested response or an error object:
|
||||
* {
|
||||
* status: 'error',
|
||||
* message: The error message
|
||||
* }
|
||||
*
|
||||
* @param string user The user to query for. Defaults to current user
|
||||
*/
|
||||
var Storage = function(user) {
|
||||
this.user = user ? user : OC.currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all address books registered for this user.
|
||||
*
|
||||
* @return An array containing object of address book metadata e.g.:
|
||||
* {
|
||||
* backend:'local',
|
||||
* id:'1234'
|
||||
* permissions:31,
|
||||
* displayname:'Contacts'
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.getAddressBooksForUser = function() {
|
||||
return this.requestRoute(
|
||||
'contacts_address_books_for_user',
|
||||
'GET',
|
||||
{user: this.user}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an address book to a specific backend
|
||||
*
|
||||
* @param string backend - currently defaults to 'local'
|
||||
* @param object params An object {displayname:"My contacts", description:""}
|
||||
* @return An array containing contact data e.g.:
|
||||
* {
|
||||
* metadata:
|
||||
* {
|
||||
* id:'1234'
|
||||
* permissions:31,
|
||||
* displayname:'My contacts',
|
||||
* lastmodified: (unix timestamp),
|
||||
* owner: 'joye',
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.addAddressBook = function(backend, parameters) {
|
||||
console.log('Storage.addAddressBook', backend);
|
||||
return this.requestRoute(
|
||||
'contacts_address_book_add',
|
||||
'POST',
|
||||
{user: this.user, backend: 'local'},
|
||||
parameters
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an address book from a specific backend
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
*/
|
||||
Storage.prototype.deleteAddressBook = function(backend, addressbookid) {
|
||||
console.log('Storage.deleteAddressBook', backend, addressbookid);
|
||||
return this.requestRoute(
|
||||
'contacts_address_book_delete',
|
||||
'POST',
|
||||
{user: this.user, backend: 'local', addressbookid: addressbookid}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get contacts from an address book from a specific backend
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @return An array containing contact data e.g.:
|
||||
* {
|
||||
* metadata:
|
||||
* {
|
||||
* id:'1234'
|
||||
* permissions:31,
|
||||
* displayname:'John Q. Public',
|
||||
* lastmodified: (unix timestamp),
|
||||
* owner: 'joye',
|
||||
* parent: (id of the parent address book)
|
||||
* data: //array of VCard data
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.getContacts = function(backend, addressbookid) {
|
||||
return this.requestRoute(
|
||||
'contacts_address_book_collection',
|
||||
'GET',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a contact to an address book from a specific backend
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @return An array containing contact data e.g.:
|
||||
* {
|
||||
* metadata:
|
||||
* {
|
||||
* id:'1234'
|
||||
* permissions:31,
|
||||
* displayname:'John Q. Public',
|
||||
* lastmodified: (unix timestamp),
|
||||
* owner: 'joye',
|
||||
* parent: (id of the parent address book)
|
||||
* data: //array of VCard data
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.addContact = function(backend, addressbookid) {
|
||||
console.log('Storage.addContact', backend, addressbookid);
|
||||
return this.requestRoute(
|
||||
'contacts_address_book_add_contact',
|
||||
'POST',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a contact from an address book from a specific backend
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @param string contactid Address book ID
|
||||
*/
|
||||
Storage.prototype.deleteContact = function(backend, addressbookid, contactid) {
|
||||
console.log('Storage.deleteContact', backend, addressbookid, contactid);
|
||||
return this.requestRoute(
|
||||
'contacts_address_book_delete_contact',
|
||||
'POST',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid, contactid: contactid}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Image instance for a contacts profile picture
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @param string contactid Address book ID
|
||||
* @return Image
|
||||
*/
|
||||
Storage.prototype.getContactPhoto = function(backend, addressbookid, contactid) {
|
||||
var photo = new Image();
|
||||
var url = OC.Router.generate(
|
||||
'contacts_contact_photo',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid, contactid: contactid}
|
||||
);
|
||||
var defer = $.Deferred();
|
||||
$.when(
|
||||
$(photo).load(function() {
|
||||
defer.resolve(photo);
|
||||
})
|
||||
.error(function() {
|
||||
console.log('Error loading default photo', arguments)
|
||||
})
|
||||
.attr('src', url + '?refresh=' + Math.random())
|
||||
)
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
defer.reject();
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed loading photo: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
return defer.promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Image instance for default profile picture
|
||||
*
|
||||
* This method loads the default picture only once and caches it.
|
||||
*
|
||||
* @return Image
|
||||
*/
|
||||
Storage.prototype.getDefaultPhoto = function() {
|
||||
console.log('Storage.getDefaultPhoto');
|
||||
if(!this._defaultPhoto) {
|
||||
var url = OC.imagePath('contacts', 'person_large.png');
|
||||
this._defaultPhoto = new Image();
|
||||
$.when(
|
||||
$(this._defaultPhoto)
|
||||
.load(function() {
|
||||
console.log('Default photo loaded', arguments);
|
||||
}).error(function() {
|
||||
console.log('Error loading default photo', arguments)
|
||||
}).attr('src', url)
|
||||
)
|
||||
.then(function(response) {
|
||||
console.log('Storage.defaultPhoto', response);
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log( "Request Failed: " + err);
|
||||
$(document).trigger('status.contact.error', {
|
||||
message: t('contacts', 'Failed loading default photo: {error}', {error:err})
|
||||
});
|
||||
});
|
||||
};
|
||||
return this._defaultPhoto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a single property.
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @param string contactid Contact ID
|
||||
* @param object params An object with the following properties:
|
||||
* @param string name The name of the property e.g. EMAIL.
|
||||
* @param string checksum For non-singular properties such as email this must contain
|
||||
* an 8 character md5 checksum of the serialized \Sabre\Property
|
||||
*/
|
||||
Storage.prototype.deleteProperty = function(backend, addressbookid, contactid, params) {
|
||||
return this.requestRoute(
|
||||
'contacts_contact_delete_property',
|
||||
'POST',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid, contactid: contactid},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a property.
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @param string contactid Contact ID
|
||||
* @param object params An object with the following properties:
|
||||
* @param string name The name of the property e.g. EMAIL.
|
||||
* @param string|array value The of the property
|
||||
* @param array parameters Optional parameters for the property
|
||||
* @param string checksum For non-singular properties such as email this must contain
|
||||
* an 8 character md5 checksum of the serialized \Sabre\Property
|
||||
*/
|
||||
Storage.prototype.saveProperty = function(backend, addressbookid, contactid, params) {
|
||||
return this.requestRoute(
|
||||
'contacts_contact_save_property',
|
||||
'POST',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid, contactid: contactid},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save all properties. Used when merging contacts.
|
||||
*
|
||||
* @param string backend
|
||||
* @param string addressbookid Address book ID
|
||||
* @param string contactid Contact ID
|
||||
* @param object params An object with the all properties:
|
||||
*/
|
||||
Storage.prototype.saveAllProperties = function(backend, addressbookid, contactid, params) {
|
||||
console.log('Storage.saveAllProperties', params);
|
||||
return this.requestRoute(
|
||||
'contacts_contact_save_all',
|
||||
'POST',
|
||||
{user: this.user, backend: backend, addressbookid: addressbookid, contactid: contactid},
|
||||
params
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all groups for this user.
|
||||
*
|
||||
* @return An array containing the groups, the favorites, any shared
|
||||
* address books, the last selected group and the sort order of the groups.
|
||||
* {
|
||||
* 'categories': [{'id':1',Family'}, {...}],
|
||||
* 'favorites': [123,456],
|
||||
* 'shared': [],
|
||||
* 'lastgroup':'1',
|
||||
* 'sortorder':'3,2,4'
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.getGroupsForUser = function() {
|
||||
console.log('getGroupsForUser');
|
||||
return this.requestRoute(
|
||||
'contacts_categories_list',
|
||||
'GET',
|
||||
{user: this.user}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a group
|
||||
*
|
||||
* @param string name
|
||||
* @return A JSON object containing the (maybe sanitized) group name and its ID:
|
||||
* {
|
||||
* 'id':1234,
|
||||
* 'name':'My group'
|
||||
* }
|
||||
*/
|
||||
Storage.prototype.addGroup = function(name) {
|
||||
console.log('Storage.addGroup', name);
|
||||
return this.requestRoute(
|
||||
'contacts_categories_add',
|
||||
'POST',
|
||||
{user: this.user},
|
||||
{name: name}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a group
|
||||
*
|
||||
* @param string name
|
||||
*/
|
||||
Storage.prototype.deleteGroup = function(name) {
|
||||
return this.requestRoute(
|
||||
'contacts_categories_delete',
|
||||
'POST',
|
||||
{user: this.user},
|
||||
{name: name}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add contacts to a group
|
||||
*
|
||||
* @param array contactids
|
||||
*/
|
||||
Storage.prototype.addToGroup = function(contactids, categoryid) {
|
||||
console.log('Storage.addToGroup', contactids, categoryid);
|
||||
return this.requestRoute(
|
||||
'contacts_categories_addto',
|
||||
'POST',
|
||||
{user: this.user, categoryid: categoryid},
|
||||
{contactids: contactids}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove contacts from a group
|
||||
*
|
||||
* @param array contactids
|
||||
*/
|
||||
Storage.prototype.removeFromGroup = function(contactids, categoryid) {
|
||||
console.log('Storage.addToGroup', contactids, categoryid);
|
||||
return this.requestRoute(
|
||||
'contacts_categories_removefrom',
|
||||
'POST',
|
||||
{user: this.user, categoryid: categoryid},
|
||||
{contactids: contactids}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a user preference
|
||||
*
|
||||
* @param string key
|
||||
* @param string value
|
||||
*/
|
||||
Storage.prototype.setPreference = function(key, value) {
|
||||
return this.requestRoute(
|
||||
'contacts_setpreference',
|
||||
'POST',
|
||||
{user: this.user},
|
||||
{key: key, value:value}
|
||||
);
|
||||
}
|
||||
|
||||
Storage.prototype.requestRoute = function(route, type, routeParams, params) {
|
||||
var isJSON = (typeof params === 'string');
|
||||
var contentType = isJSON ? 'application/json' : 'application/x-www-form-urlencoded';
|
||||
var processData = !isJSON;
|
||||
contentType += '; charset=UTF-8';
|
||||
var self = this;
|
||||
var url = OC.Router.generate(route, routeParams);
|
||||
var ajaxParams = {
|
||||
type: type,
|
||||
url: url,
|
||||
dataType: 'json',
|
||||
contentType: contentType,
|
||||
processData: processData,
|
||||
data: params
|
||||
};
|
||||
var defer = $.Deferred();
|
||||
/*$.when($.ajax(ajaxParams)).then(function(response) {
|
||||
defer.resolve(new JSONResponse(response));
|
||||
}).fail(function(jqxhr, textStatus, error) {
|
||||
defer.reject(
|
||||
new JSONResponse({
|
||||
status:'error',
|
||||
data:{message:t('contacts', 'Request failed: {error}', {error:textStatus + ', ' + error})}
|
||||
})
|
||||
);
|
||||
});*/
|
||||
|
||||
var jqxhr = $.ajax(ajaxParams)
|
||||
.done(function(response) {
|
||||
defer.resolve(new JSONResponse(response));
|
||||
})
|
||||
.fail(function(jqxhr, textStatus, error) {
|
||||
defer.reject(
|
||||
new JSONResponse({
|
||||
status:'error',
|
||||
data:{message:t('contacts', 'Request failed: {error}', {error:textStatus + ', ' + error})}
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
return defer.promise();
|
||||
}
|
||||
|
||||
OC.Contacts.Storage = Storage;
|
||||
|
||||
})(window, jQuery, OC);
|
168
lib/abstractpimcollection.php
Normal file
168
lib/abstractpimcollection.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Collection class for PIM object
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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;
|
||||
|
||||
/**
|
||||
* Subclass this for PIM collections
|
||||
*/
|
||||
|
||||
abstract class PIMCollectionAbstract extends PIMObjectAbstract implements \Iterator, \Countable, \ArrayAccess {
|
||||
|
||||
// Iterator properties
|
||||
|
||||
protected $objects = array();
|
||||
|
||||
protected $counter = 0;
|
||||
|
||||
/**
|
||||
* This is a collection so return null.
|
||||
* @return null
|
||||
*/
|
||||
function getParent() {
|
||||
null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its id
|
||||
* TODO: Maybe implement it here?
|
||||
*
|
||||
* @param string $id
|
||||
* @return IPIMObject
|
||||
*/
|
||||
abstract function getChild($id);
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return IPIMObject[]
|
||||
*/
|
||||
abstract function getChildren($limit = null, $offset = null);
|
||||
|
||||
/**
|
||||
* Checks if a child-node with the specified id exists
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
abstract function childExists($id);
|
||||
|
||||
/**
|
||||
* Add a child to the collection
|
||||
*
|
||||
* It's up to the implementations to "keep track" of the children.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return string ID of the newly added child
|
||||
*/
|
||||
abstract public function addChild($data);
|
||||
|
||||
/**
|
||||
* Delete a child from the collection
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function deleteChild($id);
|
||||
|
||||
// Iterator methods
|
||||
|
||||
public function rewind() {
|
||||
$this->counter = 0;
|
||||
}
|
||||
|
||||
public function next() {
|
||||
$this->counter++;
|
||||
}
|
||||
|
||||
public function valid() {
|
||||
return array_key_exists($this->counter, $this->objects);
|
||||
}
|
||||
|
||||
public function current() {
|
||||
return $this->objects[$this->counter];
|
||||
}
|
||||
|
||||
/** Implementations can choose to return the current objects ID/UUID
|
||||
* to be able to iterate over the collection with ID => Object pairs:
|
||||
* foreach($collection as $id => $object) {}
|
||||
*/
|
||||
public function key() {
|
||||
return $this->counter;
|
||||
}
|
||||
|
||||
// Countable method.
|
||||
|
||||
/**
|
||||
* For implementations using a backend where fetching all object at once
|
||||
* would give too much overhead, they can maintain an internal count value
|
||||
* and fetch objects progressively. Simply watch the diffence between
|
||||
* $this->counter, the value of count($this->objects) and the internal
|
||||
* value, and fetch more objects when needed.
|
||||
*/
|
||||
public function count() {
|
||||
return count($this->objects);
|
||||
}
|
||||
|
||||
// ArrayAccess methods
|
||||
|
||||
public function offsetSet($offset, $value) {
|
||||
if (is_null($offset)) {
|
||||
$this->objects[] = $value;
|
||||
} else {
|
||||
$this->objects[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->objects[$offset]);
|
||||
}
|
||||
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->objects[$offset]);
|
||||
}
|
||||
|
||||
public function offsetGet($offset) {
|
||||
return isset($this->objects[$offset]) ? $this->objects[$offset] : null;
|
||||
}
|
||||
|
||||
// Magic property accessors
|
||||
// NOTE: They should go in the implementations(?)
|
||||
/*
|
||||
public function __set($id, $value) {
|
||||
$this->objects[$id] = $value;
|
||||
}
|
||||
|
||||
public function __get($id) {
|
||||
return $this->objects[$id];
|
||||
}
|
||||
|
||||
public function __isset($id) {
|
||||
return isset($this->objects[$id]);
|
||||
}
|
||||
|
||||
public function __unset($id) {
|
||||
unset($this->objects[$id]);
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
137
lib/abstractpimobject.php
Normal file
137
lib/abstractpimobject.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Interface for PIM object
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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;
|
||||
|
||||
/**
|
||||
* Subclass this class or implement IPIMObject interface for PIM objects
|
||||
*/
|
||||
|
||||
abstract class PIMObjectAbstract implements IPIMObject {
|
||||
|
||||
/**
|
||||
* This variable holds the ID of this object.
|
||||
* Depending on the backend, this can be either a string
|
||||
* or an integer, so we treat them all as strings.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* This variable holds the owner of this object.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $owner;
|
||||
|
||||
/**
|
||||
* This variable holds the parent of this object if any.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* This variable holds the permissions of this object.
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
protected $permissions;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
function getDisplayName() {
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
return $this->owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this object is part of a collection return a reference
|
||||
* to the parent object, otherwise return null.
|
||||
* @return IPIMObject|null
|
||||
*/
|
||||
function getParent() {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask of
|
||||
*
|
||||
* \OCP\PERMISSION_CREATE
|
||||
* \OCP\PERMISSION_READ
|
||||
* \OCP\PERMISSION_UPDATE
|
||||
* \OCP\PERMISSION_DELETE
|
||||
* \OCP\PERMISSION_SHARE
|
||||
* or
|
||||
* \OCP\PERMISSION_ALL
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function getPermissions() {
|
||||
return $this->permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractBackend
|
||||
*/
|
||||
function getBackend() {
|
||||
return $this->backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $permission
|
||||
* @return boolean
|
||||
*/
|
||||
function hasPermission($permission) {
|
||||
return $this->getPermissions() & $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the data to backend
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function update(array $data);
|
||||
|
||||
/**
|
||||
* Delete the data from backend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function delete();
|
||||
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
* @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
|
||||
@ -19,416 +19,262 @@
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* The following SQL statement is just a help for developers and will not be
|
||||
* executed!
|
||||
*
|
||||
* CREATE TABLE contacts_addressbooks (
|
||||
* id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
* userid VARCHAR(255) NOT NULL,
|
||||
* displayname VARCHAR(255),
|
||||
* uri VARCHAR(100),
|
||||
* description TEXT,
|
||||
* ctag INT(11) UNSIGNED NOT NULL DEFAULT '1'
|
||||
* );
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts;
|
||||
|
||||
/**
|
||||
* This class manages our addressbooks.
|
||||
*/
|
||||
class Addressbook {
|
||||
class Addressbook extends PIMCollectionAbstract {
|
||||
|
||||
protected $_count;
|
||||
/**
|
||||
* @brief Returns the list of addressbooks for a specific user.
|
||||
* @param string $uid
|
||||
* @param boolean $active Only return addressbooks with this $active state, default(=false) is don't care
|
||||
* @param boolean $shared Whether to also return shared addressbook. Defaults to true.
|
||||
* @return array or false.
|
||||
* @var Backend\AbstractBackend
|
||||
*/
|
||||
public static function all($uid, $active = false, $shared = true) {
|
||||
$values = array($uid);
|
||||
$active_where = '';
|
||||
if ($active) {
|
||||
$active_where = ' AND `active` = ?';
|
||||
$values[] = 1;
|
||||
}
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' . $active_where . ' ORDER BY `displayname`' );
|
||||
$result = $stmt->execute($values);
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OCP\Util::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
return false;
|
||||
protected $backend;
|
||||
|
||||
/**
|
||||
* An array containing the mandatory:
|
||||
* 'displayname'
|
||||
* 'discription'
|
||||
* 'permissions'
|
||||
*
|
||||
* And the optional:
|
||||
* 'Etag'
|
||||
* 'lastModified'
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $addressBookInfo;
|
||||
|
||||
/**
|
||||
* @param AbstractBackend $backend The storage backend
|
||||
* @param array $addressBookInfo
|
||||
*/
|
||||
public function __construct(Backend\AbstractBackend $backend, array $addressBookInfo) {
|
||||
$this->backend = $backend;
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
if(is_null($this->getId())) {
|
||||
$id = $this->backend->createAddressBook($addressBookInfo);
|
||||
if($id === false) {
|
||||
throw new \Exception('Error creating address book.');
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' uid: '.$uid, \OCP\Util::DEBUG);
|
||||
$this->addressBookInfo = $this->backend->getAddressBook($id);
|
||||
//print(__METHOD__. ' '. __LINE__ . ' addressBookInfo: ' . print_r($this->backend->getAddressBook($id), true));
|
||||
}
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' backend: ' . print_r($this->backend, true), \OCP\Util::DEBUG);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getId() {
|
||||
return isset($this->addressBookInfo['id'])
|
||||
? $this->addressBookInfo['id']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getMetaData() {
|
||||
$metadata = $this->addressBookInfo;
|
||||
$metadata['lastmodified'] = $this->lastModified();
|
||||
$metadata['backend'] = $this->getBackend()->name;
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getDisplayName() {
|
||||
return $this->addressBookInfo['displayname'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getURI() {
|
||||
return $this->addressBookInfo['uri'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getOwner() {
|
||||
return $this->addressBookInfo['owner'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getPermissions() {
|
||||
return $this->addressBookInfo['permissions'];
|
||||
}
|
||||
|
||||
function getBackend() {
|
||||
return $this->backend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific child node, referenced by its id
|
||||
*
|
||||
* @param string $id
|
||||
* @return Contact|null
|
||||
*/
|
||||
function getChild($id) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id, \OCP\Util::DEBUG);
|
||||
if(!isset($this->objects[$id])) {
|
||||
$contact = $this->backend->getContact($this->getId(), $id);
|
||||
if($contact) {
|
||||
$this->objects[$id] = new Contact($this, $this->backend, $contact);
|
||||
}
|
||||
}
|
||||
// When requesting a single contact we preparse it
|
||||
if(isset($this->objects[$id])) {
|
||||
$this->objects[$id]->retrieve();
|
||||
return $this->objects[$id];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a child-node with the specified id exists
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
function childExists($id) {
|
||||
return ($this->getChild($id) !== null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array with all the child nodes
|
||||
*
|
||||
* @return Contact[]
|
||||
*/
|
||||
function getChildren($limit = null, $offset = null, $omitdata = false) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' backend: ' . print_r($this->backend, true), \OCP\Util::DEBUG);
|
||||
$contacts = array();
|
||||
|
||||
foreach($this->backend->getContacts($this->getId(), $limit, $offset, $omitdata) as $contact) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$contact['id'], \OCP\Util::DEBUG);
|
||||
if(!isset($this->objects[$contact['id']])) {
|
||||
$this->objects[$contact['id']] = new Contact($this, $this->backend, $contact);
|
||||
}
|
||||
$contacts[] = $this->objects[$contact['id']];
|
||||
}
|
||||
return $contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a contact to the address book
|
||||
* This takes an array or a VCard|Contact and return
|
||||
* the ID or false.
|
||||
*
|
||||
* @param array|VObject\VCard $data
|
||||
* @return int|bool
|
||||
*/
|
||||
public function addChild($data = null) {
|
||||
$contact = new Contact($this, $this->backend, $data);
|
||||
if($contact->save() === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$addressbooks = array();
|
||||
while( $row = $result->fetchRow()) {
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
$addressbooks[] = $row;
|
||||
}
|
||||
|
||||
if($shared === true) {
|
||||
$addressbooks = array_merge($addressbooks, \OCP\Share::getItemsSharedWith('addressbook', Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS));
|
||||
}
|
||||
if(!$active && !count($addressbooks)) {
|
||||
$id = self::addDefault($uid);
|
||||
return array(self::find($id),);
|
||||
}
|
||||
return $addressbooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get active addressbook IDs for a user.
|
||||
* @param integer $uid User id. If null current user will be used.
|
||||
* @return array
|
||||
*/
|
||||
public static function activeIds($uid = null) {
|
||||
if(is_null($uid)) {
|
||||
$uid = \OCP\USER::getUser();
|
||||
}
|
||||
|
||||
// query all addressbooks to force creation of default if it desn't exist.
|
||||
$activeaddressbooks = self::all($uid);
|
||||
$ids = array();
|
||||
foreach($activeaddressbooks as $addressbook) {
|
||||
if($addressbook['active']) {
|
||||
$ids[] = $addressbook['id'];
|
||||
}
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of active addressbooks for a specific user.
|
||||
* @param string $uid
|
||||
* @return array
|
||||
*/
|
||||
public static function active($uid) {
|
||||
return self::all($uid, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of addressbooks for a principal (DAV term of user)
|
||||
* @param string $principaluri
|
||||
* @return array
|
||||
*/
|
||||
public static function allWherePrincipalURIIs($principaluri) {
|
||||
$uid = self::extractUserID($principaluri);
|
||||
return self::all($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the data of one address book
|
||||
* @param integer $id
|
||||
* @return associative array or false.
|
||||
*/
|
||||
public static function find($id) {
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?' );
|
||||
$result = $stmt->execute(array($id));
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: ' . $id, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
|
||||
if($row['userid'] != \OCP\USER::getUser() && !\OC_Group::inGroup(\OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_READ)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to read this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
$row['permissions'] = $sharedAddressbook['permissions'];
|
||||
} else {
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds default address book
|
||||
* @return $id ID of the newly created addressbook or false on error.
|
||||
*/
|
||||
public static function addDefault($uid = null) {
|
||||
if(is_null($uid)) {
|
||||
$uid = \OCP\USER::getUser();
|
||||
}
|
||||
$id = self::add($uid, 'Contacts', 'Default Address Book');
|
||||
if($id !== false) {
|
||||
self::setActive($id, true);
|
||||
$id = $contact->getId();
|
||||
if($this->count() !== null) {
|
||||
$this->_count += 1;
|
||||
}
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id, \OCP\Util::DEBUG);
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new address book
|
||||
* @param string $userid
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @return insertid
|
||||
* Delete a contact from the address book
|
||||
*
|
||||
* @param string $id
|
||||
* @return bool
|
||||
*/
|
||||
public static function add($uid,$name,$description='') {
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT `uri` FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' );
|
||||
$result = $stmt->execute(array($uid));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return false;
|
||||
public function deleteChild($id) {
|
||||
if($this->backend->deleteContact($this->getId(), $id)) {
|
||||
if(isset($this->objects[$id])) {
|
||||
unset($this->objects[$id]);
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ' exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ' uid: ' . $uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$uris = array();
|
||||
while($row = $result->fetchRow()) {
|
||||
$uris[] = $row['uri'];
|
||||
}
|
||||
|
||||
$uri = self::createURI($name, $uris );
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)' );
|
||||
$result = $stmt->execute(array($uid,$name,$uri,$description,1));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return false;
|
||||
if($this->count() !== null) {
|
||||
$this->_count -= 1;
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', uid: '.$uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return \OCP\DB::insertid('*PREFIX*contacts_addressbooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new address book from the data sabredav provides
|
||||
* @param string $principaluri
|
||||
* @param string $uri
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @return insertid or false
|
||||
*/
|
||||
public static function addFromDAVData($principaluri, $uri, $name, $description) {
|
||||
$uid = self::extractUserID($principaluri);
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare('INSERT INTO `*PREFIX*contacts_addressbooks` '
|
||||
. '(`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)');
|
||||
$result = $stmt->execute(array($uid, $name, $uri, $description, 1));
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', uid: ' . $uid, \OCP\Util::DEBUG);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', uri: ' . $uri, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return \OCP\DB::insertid('*PREFIX*contacts_addressbooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Edits an addressbook
|
||||
* @param integer $id
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @return boolean
|
||||
*/
|
||||
public static function edit($id,$name,$description) {
|
||||
// Need these ones for checking uri
|
||||
$addressbook = self::find($id);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser() && !\OC_Group::inGroup(OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_UPDATE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to update this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if(is_null($name)) {
|
||||
$name = $addressbook['name'];
|
||||
}
|
||||
if(is_null($description)) {
|
||||
$description = $addressbook['description'];
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare('UPDATE `*PREFIX*contacts_addressbooks` SET `displayname`=?,`description`=?, `ctag`=`ctag`+1 WHERE `id`=?');
|
||||
$result = $stmt->execute(array($name,$description,$id));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error updating the addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', id: ' . $id, \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error updating the addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Activates an addressbook
|
||||
* @param integer $id
|
||||
* @param boolean $active
|
||||
* @return boolean
|
||||
*/
|
||||
public static function setActive($id,$active) {
|
||||
$sql = 'UPDATE `*PREFIX*contacts_addressbooks` SET `active` = ? WHERE `id` = ?';
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare($sql);
|
||||
$stmt->execute(array(intval($active), $id));
|
||||
return true;
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception for ' . $id.': ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal implements Countable
|
||||
* @return int|null
|
||||
*/
|
||||
public function count() {
|
||||
if(!isset($this->_count)) {
|
||||
$this->_count = $this->backend->numContacts($this->getId());
|
||||
}
|
||||
return $this->_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update and save the address book data to backend
|
||||
* NOTE: @see IPIMObject::update for consistency considerations.
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function update(array $data) {
|
||||
if(count($data) === 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an addressbook is active.
|
||||
* @param integer $id ID of the address book.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isActive($id) {
|
||||
$sql = 'SELECT `active` FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?';
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( $sql );
|
||||
$result = $stmt->execute(array($id));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return false;
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
return (bool)$row['active'];
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes an address book
|
||||
* @param integer $id
|
||||
* @return boolean true on success, otherwise an exception will be thrown
|
||||
*/
|
||||
public static function delete($id) {
|
||||
$addressbook = self::find($id);
|
||||
|
||||
if ($addressbook['userid'] != \OCP\User::getUser() && !\OC_Group::inGroup(\OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_DELETE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to delete this addressbook.'
|
||||
)
|
||||
);
|
||||
foreach($data as $key => $value) {
|
||||
switch($key) {
|
||||
case 'displayname':
|
||||
$this->addressBookInfo['displayname'] = $value;
|
||||
break;
|
||||
case 'description':
|
||||
$this->addressBookInfo['description'] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// First delete cards belonging to this addressbook.
|
||||
$cards = VCard::all($id);
|
||||
foreach($cards as $card) {
|
||||
try {
|
||||
VCard::delete($card['id']);
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__METHOD__.', exception deleting vCard '.$card['id'].': '
|
||||
. $e->getMessage(),
|
||||
\OCP\Util::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?');
|
||||
$stmt->execute(array($id));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__METHOD__.', exception for ' . $id . ': '
|
||||
. $e->getMessage(),
|
||||
\OCP\Util::ERROR);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error deleting this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
\OCP\Share::unshareAll('addressbook', $id);
|
||||
|
||||
if(count(self::all(\OCP\User::getUser())) == 0) {
|
||||
self::addDefault();
|
||||
}
|
||||
|
||||
return true;
|
||||
return $this->backend->updateAddressBook($this->getId(), $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates ctag for addressbook
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
* Save the address book data to backend
|
||||
* NOTE: @see IPIMObject::update for consistency considerations.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function touch($id) {
|
||||
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_addressbooks` SET `ctag` = `ctag` + 1 WHERE `id` = ?' );
|
||||
$stmt->execute(array($id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a URI for Addressbook
|
||||
* @param string $name name of the addressbook
|
||||
* @param array $existing existing addressbook URIs
|
||||
* @return string new name
|
||||
*/
|
||||
public static function createURI($name,$existing) {
|
||||
$name = str_replace(' ', '_', strtolower($name));
|
||||
$newname = $name;
|
||||
$i = 1;
|
||||
while(in_array($newname, $existing)) {
|
||||
$newname = $name.$i;
|
||||
$i = $i + 1;
|
||||
public function save() {
|
||||
if(!$this->hasPermission(OCP\PERMISSION_UPDATE)) {
|
||||
throw new Exception('You don\'t have permissions to update the address book.');
|
||||
}
|
||||
return $newname;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gets the userid from a principal path
|
||||
* @return string
|
||||
* Delete the address book from backend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function extractUserID($principaluri) {
|
||||
list($prefix, $userid) = \Sabre_DAV_URLUtil::splitPath($principaluri);
|
||||
return $userid;
|
||||
public function delete() {
|
||||
if(!$this->hasPermission(OCP\PERMISSION_DELETE)) {
|
||||
throw new Exception('You don\'t have permissions to delete the address book.');
|
||||
}
|
||||
return $this->backend->deleteAddressBook($this->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the last modification time for the object.
|
||||
*
|
||||
* Must return a UNIX time stamp or null if the backend
|
||||
* doesn't support it.
|
||||
*
|
||||
* @returns int | null
|
||||
*/
|
||||
public function lastModified() {
|
||||
return $this->backend->lastModifiedAddressBook($this->getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
387
lib/addressbooklegacy.php
Normal file
387
lib/addressbooklegacy.php
Normal file
@ -0,0 +1,387 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
/*
|
||||
*
|
||||
* The following SQL statement is just a help for developers and will not be
|
||||
* executed!
|
||||
*
|
||||
* CREATE TABLE contacts_addressbooks (
|
||||
* id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
* userid VARCHAR(255) NOT NULL,
|
||||
* displayname VARCHAR(255),
|
||||
* uri VARCHAR(100),
|
||||
* description TEXT,
|
||||
* ctag INT(11) UNSIGNED NOT NULL DEFAULT '1'
|
||||
* );
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts;
|
||||
|
||||
/**
|
||||
* This class manages our addressbooks.
|
||||
*/
|
||||
class AddressbookLegacy {
|
||||
/**
|
||||
* @brief Returns the list of addressbooks for a specific user.
|
||||
* @param string $uid
|
||||
* @param boolean $active Only return addressbooks with this $active state, default(=false) is don't care
|
||||
* @param boolean $shared Whether to also return shared addressbook. Defaults to true.
|
||||
* @return array or false.
|
||||
*/
|
||||
public static function all($uid, $active = false, $shared = true) {
|
||||
$values = array($uid);
|
||||
$active_where = '';
|
||||
if ($active) {
|
||||
$active_where = ' AND `active` = ?';
|
||||
$values[] = 1;
|
||||
}
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' . $active_where . ' ORDER BY `displayname`' );
|
||||
$result = $stmt->execute($values);
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OCP\Util::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__.' uid: '.$uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
$addressbooks = array();
|
||||
while( $row = $result->fetchRow()) {
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
$addressbooks[] = $row;
|
||||
}
|
||||
|
||||
if($shared === true) {
|
||||
$addressbooks = array_merge($addressbooks, \OCP\Share::getItemsSharedWith('addressbook', Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS));
|
||||
}
|
||||
if(!$active && !count($addressbooks)) {
|
||||
$id = self::addDefault($uid);
|
||||
return array(self::find($id),);
|
||||
}
|
||||
return $addressbooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get active addressbook IDs for a user.
|
||||
* @param integer $uid User id. If null current user will be used.
|
||||
* @return array
|
||||
*/
|
||||
public static function activeIds($uid = null) {
|
||||
if(is_null($uid)) {
|
||||
$uid = \OCP\USER::getUser();
|
||||
}
|
||||
|
||||
// query all addressbooks to force creation of default if it desn't exist.
|
||||
$activeaddressbooks = self::all($uid);
|
||||
$ids = array();
|
||||
foreach($activeaddressbooks as $addressbook) {
|
||||
if($addressbook['active']) {
|
||||
$ids[] = $addressbook['id'];
|
||||
}
|
||||
}
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of active addressbooks for a specific user.
|
||||
* @param string $uid
|
||||
* @return array
|
||||
*/
|
||||
public static function active($uid) {
|
||||
return self::all($uid, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets the data of one address book
|
||||
* @param integer $id
|
||||
* @return associative array or false.
|
||||
*/
|
||||
public static function find($id) {
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?' );
|
||||
$result = $stmt->execute(array($id));
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: ' . $id, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
|
||||
if($row['userid'] != \OCP\USER::getUser() && !\OC_Group::inGroup(\OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_READ)) {
|
||||
throw new Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to read this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
$row['permissions'] = $sharedAddressbook['permissions'];
|
||||
} else {
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds default address book
|
||||
* @return $id ID of the newly created addressbook or false on error.
|
||||
*/
|
||||
public static function addDefault($uid = null) {
|
||||
if(is_null($uid)) {
|
||||
$uid = \OCP\USER::getUser();
|
||||
}
|
||||
$id = self::add($uid, 'Contacts', 'Default Address Book');
|
||||
if($id !== false) {
|
||||
self::setActive($id, true);
|
||||
}
|
||||
return $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new address book
|
||||
* @param string $userid
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @return insertid
|
||||
*/
|
||||
public static function add($uid,$name,$description='') {
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT `uri` FROM `*PREFIX*contacts_addressbooks` WHERE `userid` = ? ' );
|
||||
$result = $stmt->execute(array($uid));
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ' uid: ' . $uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$uris = array();
|
||||
while($row = $result->fetchRow()) {
|
||||
$uris[] = $row['uri'];
|
||||
}
|
||||
|
||||
$uri = self::createURI($name, $uris );
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_addressbooks` (`userid`,`displayname`,`uri`,`description`,`ctag`) VALUES(?,?,?,?,?)' );
|
||||
$result = $stmt->execute(array($uid,$name,$uri,$description,1));
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', uid: '.$uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return \OCP\DB::insertid('*PREFIX*contacts_addressbooks');
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Edits an addressbook
|
||||
* @param integer $id
|
||||
* @param string $name
|
||||
* @param string $description
|
||||
* @return boolean
|
||||
*/
|
||||
public static function edit($id,$name,$description) {
|
||||
// Need these ones for checking uri
|
||||
$addressbook = self::find($id);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser() && !\OC_Group::inGroup(OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_UPDATE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to update this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if(is_null($name)) {
|
||||
$name = $addressbook['name'];
|
||||
}
|
||||
if(is_null($description)) {
|
||||
$description = $addressbook['description'];
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare('UPDATE `*PREFIX*contacts_addressbooks` SET `displayname`=?,`description`=?, `ctag`=`ctag`+1 WHERE `id`=?');
|
||||
$result = $stmt->execute(array($name,$description,$id));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
throw new Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error updating the addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch(Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', id: ' . $id, \OCP\Util::DEBUG);
|
||||
throw new Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error updating the addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Activates an addressbook
|
||||
* @param integer $id
|
||||
* @param boolean $active
|
||||
* @return boolean
|
||||
*/
|
||||
public static function setActive($id,$active) {
|
||||
$sql = 'UPDATE `*PREFIX*contacts_addressbooks` SET `active` = ? WHERE `id` = ?';
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare($sql);
|
||||
$stmt->execute(array(intval($active), $id));
|
||||
return true;
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', exception for ' . $id.': ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an addressbook is active.
|
||||
* @param integer $id ID of the address book.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isActive($id) {
|
||||
$sql = 'SELECT `active` FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?';
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( $sql );
|
||||
$result = $stmt->execute(array($id));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return false;
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
return (bool)$row['active'];
|
||||
} catch(Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief removes an address book
|
||||
* @param integer $id
|
||||
* @return boolean true on success, otherwise an exception will be thrown
|
||||
*/
|
||||
public static function delete($id) {
|
||||
$addressbook = self::find($id);
|
||||
|
||||
if ($addressbook['userid'] != \OCP\User::getUser() && !\OC_Group::inGroup(\OCP\User::getUser(), 'admin')) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_DELETE)) {
|
||||
throw new Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to delete this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// First delete cards belonging to this addressbook.
|
||||
$cards = VCard::all($id);
|
||||
foreach($cards as $card) {
|
||||
try {
|
||||
VCard::delete($card['id']);
|
||||
} catch(Exception $e) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__METHOD__.', exception deleting vCard '.$card['id'].': '
|
||||
. $e->getMessage(),
|
||||
\OCP\Util::ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_addressbooks` WHERE `id` = ?');
|
||||
$stmt->execute(array($id));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__METHOD__.', exception for ' . $id . ': '
|
||||
. $e->getMessage(),
|
||||
\OCP\Util::ERROR);
|
||||
throw new Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error deleting this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
\OCP\Share::unshareAll('addressbook', $id);
|
||||
|
||||
if(count(self::all(\OCP\User::getUser())) == 0) {
|
||||
self::addDefault();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates ctag for addressbook
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
*/
|
||||
public static function touch($id) {
|
||||
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_addressbooks` SET `ctag` = `ctag` + 1 WHERE `id` = ?' );
|
||||
$stmt->execute(array($id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a URI for Addressbook
|
||||
* @param string $name name of the addressbook
|
||||
* @param array $existing existing addressbook URIs
|
||||
* @return string new name
|
||||
*/
|
||||
public static function createURI($name,$existing) {
|
||||
$name = str_replace(' ', '_', strtolower($name));
|
||||
$newname = $name;
|
||||
$i = 1;
|
||||
while(in_array($newname, $existing)) {
|
||||
$newname = $name.$i;
|
||||
$i = $i + 1;
|
||||
}
|
||||
return $newname;
|
||||
}
|
||||
|
||||
}
|
@ -24,6 +24,7 @@ namespace OCA\Contacts;
|
||||
|
||||
/**
|
||||
* This class manages our addressbooks.
|
||||
* TODO: Port this to use the new backend
|
||||
*/
|
||||
class AddressbookProvider implements \OCP\IAddressBook {
|
||||
|
||||
@ -48,7 +49,6 @@ class AddressbookProvider implements \OCP\IAddressBook {
|
||||
*/
|
||||
public function __construct($id) {
|
||||
$this->id = $id;
|
||||
\Sabre\VObject\Property::$classMap['GEO'] = 'Sabre\\VObject\\Property\\Compound';
|
||||
}
|
||||
|
||||
public function getAddressbook() {
|
||||
|
421
lib/app.php
421
lib/app.php
@ -1,6 +1,6 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2011 Bart Visscher bartv@thisnet.nl
|
||||
* Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net)
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
@ -16,200 +16,152 @@ use Sabre\VObject;
|
||||
App::$l10n = \OC_L10N::get('contacts');
|
||||
|
||||
class App {
|
||||
/*
|
||||
* @brief language object for calendar app
|
||||
*/
|
||||
|
||||
public static $l10n;
|
||||
/*
|
||||
* @brief categories of the user
|
||||
*/
|
||||
/**
|
||||
* @brief Categories of the user
|
||||
* @var OC_VCategories
|
||||
*/
|
||||
public static $categories = null;
|
||||
|
||||
/**
|
||||
* Properties there can be more than one of.
|
||||
* @brief language object for calendar app
|
||||
*
|
||||
* @var OC_L10N
|
||||
*/
|
||||
public static $multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
|
||||
public static $l10n;
|
||||
|
||||
/**
|
||||
* Properties to index.
|
||||
* An array holding the current users address books.
|
||||
* @var array
|
||||
*/
|
||||
public static $index_properties = array('BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME', 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'PHOTO');
|
||||
|
||||
const THUMBNAIL_PREFIX = 'contact-thumbnail-';
|
||||
const THUMBNAIL_SIZE = 28;
|
||||
|
||||
protected static $addressBooks = array();
|
||||
/**
|
||||
* @brief Gets the VCard as a \Sabre\VObject\Component
|
||||
* @param integer $id
|
||||
* @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;
|
||||
}
|
||||
* If backends are added to this map, they will be automatically mapped
|
||||
* to their respective classes, if constructed with the 'getBackend' method.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $backendClasses = array(
|
||||
'local' => 'OCA\Contacts\Backend\Database',
|
||||
'shared' => 'OCA\Contacts\Backend\Shared',
|
||||
);
|
||||
|
||||
if(!$card) {
|
||||
return null;
|
||||
}
|
||||
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->REV = $rev->format(\DateTime::W3C);
|
||||
}
|
||||
return $vcard;
|
||||
public function __construct(
|
||||
$user = null,
|
||||
$addressBooksTableName = '*PREFIX*addressbook',
|
||||
$backendsTableName = '*PREFIX*addressbooks_backend',
|
||||
$dbBackend = null
|
||||
) {
|
||||
$this->user = $user ? $user : \OCP\User::getUser();
|
||||
$this->addressBooksTableName = $addressBooksTableName;
|
||||
$this->backendsTableName = $backendsTableName;
|
||||
$this->dbBackend = $dbBackend
|
||||
? $dbBackend
|
||||
: new Backend\Database($user);
|
||||
}
|
||||
|
||||
public static function getPropertyLineByChecksum($vcard, $checksum) {
|
||||
$line = null;
|
||||
foreach($vcard->children as $i => $property) {
|
||||
if(substr(md5($property->serialize()), 0, 8) == $checksum ) {
|
||||
$line = $i;
|
||||
break;
|
||||
/**
|
||||
* Gets backend by name.
|
||||
*
|
||||
* @param string $name
|
||||
* @return \Backend\AbstractBackend
|
||||
*/
|
||||
static public function getBackend($name, $user = null) {
|
||||
$name = $name ? $name : 'local';
|
||||
if (isset(self::$backendClasses[$name])) {
|
||||
return new self::$backendClasses[$name]($user);
|
||||
} else {
|
||||
throw new \Exception('No backend for: ' . $name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all registered address books for current user.
|
||||
* For now this is hard-coded to using the Database and
|
||||
* Shared backends, but eventually admins will be able to
|
||||
* register additional backends, and users will be able to
|
||||
* subscribe to address books using those backends.
|
||||
*
|
||||
* @return AddressBook[]
|
||||
*/
|
||||
public function getAddressBooksForUser() {
|
||||
if(!self::$addressBooks) {
|
||||
foreach(array_keys(self::$backendClasses) as $backendName) {
|
||||
$backend = self::getBackend($backendName, $this->user);
|
||||
$addressBooks = $backend->getAddressBooksForUser();
|
||||
if($backendName === 'local' && count($addressBooks) === 0) {
|
||||
$id = $backend->createAddressBook(array('displayname' => 'Contacts'));
|
||||
if($id !== false) {
|
||||
$addressBook = $backend->getAddressBook($id);
|
||||
$addressBooks = array($addressBook);
|
||||
} else {
|
||||
// TODO: Write log
|
||||
}
|
||||
}
|
||||
foreach($addressBooks as $addressBook) {
|
||||
$addressBook['backend'] = $backendName;
|
||||
self::$addressBooks[] = new AddressBook($backend, $addressBook);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $line;
|
||||
return self::$addressBooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of vcard prop => label
|
||||
* Get an address book from a specific backend.
|
||||
*
|
||||
* @param string $backendName
|
||||
* @param string $addressbookid
|
||||
* @return AddressBook|null
|
||||
*/
|
||||
public static function getIMOptions($im = null) {
|
||||
$l10n = self::$l10n;
|
||||
$ims = array(
|
||||
'jabber' => array(
|
||||
'displayname' => (string)$l10n->t('Jabber'),
|
||||
'xname' => 'X-JABBER',
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'aim' => array(
|
||||
'displayname' => (string)$l10n->t('AIM'),
|
||||
'xname' => 'X-AIM',
|
||||
'protocol' => 'aim',
|
||||
),
|
||||
'msn' => array(
|
||||
'displayname' => (string)$l10n->t('MSN'),
|
||||
'xname' => 'X-MSN',
|
||||
'protocol' => 'msn',
|
||||
),
|
||||
'twitter' => array(
|
||||
'displayname' => (string)$l10n->t('Twitter'),
|
||||
'xname' => 'X-TWITTER',
|
||||
'protocol' => 'twitter',
|
||||
),
|
||||
'googletalk' => array(
|
||||
'displayname' => (string)$l10n->t('GoogleTalk'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'facebook' => array(
|
||||
'displayname' => (string)$l10n->t('Facebook'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'xmpp' => array(
|
||||
'displayname' => (string)$l10n->t('XMPP'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'icq' => array(
|
||||
'displayname' => (string)$l10n->t('ICQ'),
|
||||
'xname' => 'X-ICQ',
|
||||
'protocol' => 'icq',
|
||||
),
|
||||
'yahoo' => array(
|
||||
'displayname' => (string)$l10n->t('Yahoo'),
|
||||
'xname' => 'X-YAHOO',
|
||||
'protocol' => 'ymsgr',
|
||||
),
|
||||
'skype' => array(
|
||||
'displayname' => (string)$l10n->t('Skype'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'skype',
|
||||
),
|
||||
'qq' => array(
|
||||
'displayname' => (string)$l10n->t('QQ'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'x-apple',
|
||||
),
|
||||
'gadugadu' => array(
|
||||
'displayname' => (string)$l10n->t('GaduGadu'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'x-apple',
|
||||
),
|
||||
);
|
||||
if(is_null($im)) {
|
||||
return $ims;
|
||||
} else {
|
||||
$ims['ymsgr'] = $ims['yahoo'];
|
||||
$ims['gtalk'] = $ims['googletalk'];
|
||||
return isset($ims[$im]) ? $ims[$im] : null;
|
||||
public function getAddressBook($backendName, $addressbookid) {
|
||||
foreach(self::$addressBooks as $addressBook) {
|
||||
if($addressBook->backend->name === $backendName
|
||||
&& $addressBook->getId() === $addressbookid
|
||||
) {
|
||||
return $addressBook;
|
||||
}
|
||||
}
|
||||
// TODO: Check for return values
|
||||
$backend = self::getBackend($backendName, $this->user);
|
||||
$info = $backend->getAddressBook($addressbookid);
|
||||
// FIXME: Backend name should be set by the backend.
|
||||
$info['backend'] = $backendName;
|
||||
$addressBook = new AddressBook($backend, $info);
|
||||
self::$addressBooks[] = $addressBook;
|
||||
return $addressBook;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return types for property $prop
|
||||
* Get a Contact from an address book from a specific backend.
|
||||
*
|
||||
* @param string $backendName
|
||||
* @param string $addressbookid
|
||||
* @param string $id - Contact id
|
||||
* @return Contact|null
|
||||
*
|
||||
*/
|
||||
public static function getTypesOfProperty($prop) {
|
||||
$l = self::$l10n;
|
||||
switch($prop) {
|
||||
case 'ADR':
|
||||
case 'IMPP':
|
||||
return array(
|
||||
'WORK' => $l->t('Work'),
|
||||
'HOME' => $l->t('Home'),
|
||||
'OTHER' => $l->t('Other'),
|
||||
);
|
||||
case 'TEL':
|
||||
return array(
|
||||
'HOME' => $l->t('Home'),
|
||||
'CELL' => $l->t('Mobile'),
|
||||
'WORK' => $l->t('Work'),
|
||||
'TEXT' => $l->t('Text'),
|
||||
'VOICE' => $l->t('Voice'),
|
||||
'MSG' => $l->t('Message'),
|
||||
'FAX' => $l->t('Fax'),
|
||||
'VIDEO' => $l->t('Video'),
|
||||
'PAGER' => $l->t('Pager'),
|
||||
'OTHER' => $l->t('Other'),
|
||||
);
|
||||
case 'EMAIL':
|
||||
return array(
|
||||
'WORK' => $l->t('Work'),
|
||||
'HOME' => $l->t('Home'),
|
||||
'INTERNET' => $l->t('Internet'),
|
||||
'OTHER' => $l->t('Other'),
|
||||
);
|
||||
}
|
||||
public function getContact($backendName, $addressbookid, $id) {
|
||||
$addressBook = $this->getAddressBook($backendName, $addressbookid);
|
||||
// TODO: Check for return value
|
||||
return $addressBook->getChild($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the vcategories object of the user
|
||||
* @return (object) $vcategories
|
||||
*/
|
||||
* @brief returns the vcategories object of the user
|
||||
* @return (object) $vcategories
|
||||
*/
|
||||
public static function getVCategories() {
|
||||
if (is_null(self::$categories)) {
|
||||
if(\OC_VCategories::isEmpty('contact')) {
|
||||
self::scanCategories();
|
||||
}
|
||||
self::$categories = new \OC_VCategories('contact',
|
||||
null,
|
||||
self::getDefaultCategories());
|
||||
null,
|
||||
self::getDefaultCategories());
|
||||
}
|
||||
return self::$categories;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the categories for the user
|
||||
* @return (Array) $categories
|
||||
@ -219,19 +171,6 @@ class App {
|
||||
return ($categories ? $categories : self::getDefaultCategories());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the default categories of ownCloud
|
||||
* @return (array) $categories
|
||||
*/
|
||||
public static function getDefaultCategories() {
|
||||
return array(
|
||||
(string)self::$l10n->t('Friends'),
|
||||
(string)self::$l10n->t('Family'),
|
||||
(string)self::$l10n->t('Work'),
|
||||
(string)self::$l10n->t('Other'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* scan vcards for categories.
|
||||
* @param $vccontacts VCards to scan. null to check all vcards for the current user.
|
||||
@ -269,140 +208,4 @@ class App {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* check VCard for new categories.
|
||||
* @see OC_VCategories::loadFromVObject
|
||||
*/
|
||||
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 OC_VObject|Sabre\VObject\Component|integer|null $contact
|
||||
* @returns DateTime | null
|
||||
*/
|
||||
public static function lastModified($contact = null) {
|
||||
if(is_null($contact)) {
|
||||
// FIXME: This doesn't take shared address books into account.
|
||||
$sql = 'SELECT MAX(`lastmodified`) FROM `*PREFIX*contacts_cards`, `*PREFIX*contacts_addressbooks` ' .
|
||||
'WHERE `*PREFIX*contacts_cards`.`addressbookid` = `*PREFIX*contacts_addressbooks`.`id` AND ' .
|
||||
'`*PREFIX*contacts_addressbooks`.`userid` = ?';
|
||||
$stmt = \OCP\DB::prepare($sql);
|
||||
$result = $stmt->execute(array(\OCP\USER::getUser()));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return null;
|
||||
}
|
||||
$lastModified = $result->fetchOne();
|
||||
if(!is_null($lastModified)) {
|
||||
return new \DateTime('@' . $lastModified);
|
||||
}
|
||||
} else if(is_numeric($contact)) {
|
||||
$card = VCard::find($contact, array('lastmodified'));
|
||||
return ($card ? new \DateTime('@' . $card['lastmodified']) : null);
|
||||
} 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) {
|
||||
if(\OC_Cache::hasKey(self::THUMBNAIL_PREFIX . $id) && $image === null) {
|
||||
return \OC_Cache::get(self::THUMBNAIL_PREFIX . $id);
|
||||
}
|
||||
if(is_null($image)) {
|
||||
$vcard = self::getContactVCard($id);
|
||||
|
||||
// invalid vcard
|
||||
if(is_null($vcard)) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__METHOD__.' The VCard for ID ' . $id . ' is not RFC compatible',
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
$image = new \OC_Image();
|
||||
if(!isset($vcard->PHOTO)) {
|
||||
return false;
|
||||
}
|
||||
if(!$image->loadFromBase64((string)$vcard->PHOTO)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(!$image->centerCrop()) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'thumbnail.php. Couldn\'t crop thumbnail for ID ' . $id,
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
if(!$image->resize(self::THUMBNAIL_SIZE)) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'thumbnail.php. Couldn\'t resize thumbnail for ID ' . $id,
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
// Cache for around a month
|
||||
\OC_Cache::set(self::THUMBNAIL_PREFIX . $id, $image->data(), 3000000);
|
||||
\OCP\Util::writeLog('contacts', 'Caching ' . $id, \OCP\Util::DEBUG);
|
||||
return \OC_Cache::get(self::THUMBNAIL_PREFIX . $id);
|
||||
}
|
||||
|
||||
public static function updateDBProperties($contactid, $vcard = null) {
|
||||
$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_cards_properties` WHERE `contactid` = ?');
|
||||
try {
|
||||
$stmt->execute(array($contactid));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
|
||||
. $id, \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error deleting properties for this contact.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if(is_null($vcard)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$stmt = \OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_cards_properties` '
|
||||
. '(`userid`, `contactid`,`name`,`value`,`preferred`) VALUES(?,?,?,?,?)' );
|
||||
foreach($vcard->children as $property) {
|
||||
if(!in_array($property->name, self::$index_properties)) {
|
||||
continue;
|
||||
}
|
||||
$preferred = 0;
|
||||
foreach($property->parameters as $parameter) {
|
||||
if($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') {
|
||||
$preferred = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$result = $stmt->execute(
|
||||
array(
|
||||
\OCP\User::getUser(),
|
||||
$contactid,
|
||||
$property->name,
|
||||
$property->value,
|
||||
$preferred,
|
||||
)
|
||||
);
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OCP\Util::writeLog('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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
315
lib/backend/abstractbackend.php
Normal file
315
lib/backend/abstractbackend.php
Normal file
@ -0,0 +1,315 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Base class for Contacts backends
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Subclass this class for address book backends
|
||||
*
|
||||
* The following methods MUST be implemented:
|
||||
* @method array getAddressBooksForUser(string $userid) FIXME: I'm not sure about this one.
|
||||
* @method array getAddressBook(string $addressbookid)
|
||||
* @method array getContacts(string $addressbookid)
|
||||
* @method array getContact(string $addressbookid, mixed $id)
|
||||
* The following methods MAY be implemented:
|
||||
* @method bool hasAddressBook(string $addressbookid)
|
||||
* @method bool updateAddressBook(string $addressbookid, array $updates)
|
||||
* @method string createAddressBook(string $addressbookid, array $properties)
|
||||
* @method bool deleteAddressBook(string $addressbookid)
|
||||
* @method int lastModifiedAddressBook(string $addressbookid)
|
||||
* @method array numContacts(string $addressbookid)
|
||||
* @method bool updateContact(string $addressbookid, string $id, array $updates)
|
||||
* @method string createContact(string $addressbookid, string $id, array $properties)
|
||||
* @method bool deleteContact(string $addressbookid, string $id)
|
||||
* @method int lastModifiedContact(string $addressbookid)
|
||||
*/
|
||||
|
||||
abstract class AbstractBackend {
|
||||
|
||||
/**
|
||||
* The name of the backend.
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
protected $possibleContactPermissions = array(
|
||||
\OCP\PERMISSION_CREATE => 'createContact',
|
||||
\OCP\PERMISSION_READ => 'getContact',
|
||||
\OCP\PERMISSION_UPDATE => 'updateContact',
|
||||
\OCP\PERMISSION_DELETE => 'deleteContact',
|
||||
);
|
||||
|
||||
protected $possibleAddressBookPermissions = array(
|
||||
\OCP\PERMISSION_CREATE => 'createAddressBook',
|
||||
\OCP\PERMISSION_READ => 'getAddressBook',
|
||||
\OCP\PERMISSION_UPDATE => 'updateAddressBook',
|
||||
\OCP\PERMISSION_DELETE => 'deleteAddressBook',
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Get all permissions for contacts
|
||||
* @returns bitwise-or'ed actions
|
||||
*
|
||||
* Returns the supported actions as int to be
|
||||
* compared with \OCP\PERMISSION_CREATE etc.
|
||||
* TODO: When getting the permissions we also have to check for
|
||||
* configured permissions and return min() of the two values.
|
||||
* A user can for example configure an address book with a backend
|
||||
* that implements deleteContact() but wants to set it read-only.
|
||||
*/
|
||||
public function getContactPermissions() {
|
||||
$permissions = 0;
|
||||
foreach($this->possibleContactPermissions AS $permission => $methodName) {
|
||||
if(method_exists($this, $methodName)) {
|
||||
$permissions |= $permission;
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get all permissions for address book.
|
||||
* @returns bitwise-or'ed actions
|
||||
*
|
||||
* Returns the supported actions as int to be
|
||||
* compared with \OCP\PERMISSION_CREATE etc.
|
||||
*/
|
||||
public function getAddressBookPermissions() {
|
||||
$permissions = 0;
|
||||
foreach($this->possibleAddressBookPermissions AS $permission => $methodName) {
|
||||
if(method_exists($this, $methodName)) {
|
||||
$permissions |= $permission;
|
||||
}
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if backend implements action for contacts
|
||||
* @param $actions bitwise-or'ed actions
|
||||
* @returns boolean
|
||||
*
|
||||
* Returns the supported actions as int to be
|
||||
* compared with \OCP\PERMISSION_CREATE etc.
|
||||
*/
|
||||
public function hasContactPermission($permission) {
|
||||
return (bool)($this->getContactPermissions() & $permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if backend implements action for contacts
|
||||
* @param $actions bitwise-or'ed actions
|
||||
* @returns boolean
|
||||
*
|
||||
* Returns the supported actions as int to be
|
||||
* compared with \OCP\PERMISSION_CREATE etc.
|
||||
*/
|
||||
public function hasAddressBooksPermission($permission) {
|
||||
return (bool)($this->getAddressBooksPermissions() & $permission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the backend has the address book
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @return bool
|
||||
*/
|
||||
public function hasAddressBook($addressbookid) {
|
||||
return count($this->getAddressBook($addressbookid)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of contacts in an address book.
|
||||
* Implementations can choose to override this method to either
|
||||
* get the result more effectively or to return null if the backend
|
||||
* cannot determine the number.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @return integer|null
|
||||
*/
|
||||
public function numContacts($addressbookid) {
|
||||
return count($this->getContacts($addressbookid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* The returned arrays MUST contain a unique 'id' for the
|
||||
* backend and a 'displayname', and it MAY contain a
|
||||
* 'description'.
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @return array
|
||||
public function getAddressBooksForUser($userid) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get an addressbook's properties
|
||||
*
|
||||
* The returned array MUST contain 'displayname' and an integer 'permissions'
|
||||
* value using there ownCloud CRUDS constants (which MUST be at least
|
||||
* \OCP\PERMISSION_READ).
|
||||
* Currently the only ones supported are 'displayname' and
|
||||
* 'description', but backends can implement additional.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @return array $properties
|
||||
*/
|
||||
public abstract function getAddressBook($addressbookid);
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* The $properties array contains the changes to be made.
|
||||
*
|
||||
* Currently the only ones supported are 'displayname' and
|
||||
* 'description', but backends can implement additional.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param array $properties
|
||||
* @return bool
|
||||
public function updateAddressBook($addressbookid, array $properties) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* Currently the only ones supported are 'displayname' and
|
||||
* 'description', but backends can implement additional.
|
||||
* '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) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @return bool
|
||||
public function deleteAddressBook($addressbookid) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Get the last modification time for an address book.
|
||||
*
|
||||
* Must return a UNIX time stamp or null if the backend
|
||||
* doesn't support it.
|
||||
*
|
||||
* TODO: Implement default methods get/set for backends that
|
||||
* don't support.
|
||||
* @param string $addressbookid
|
||||
* @returns int | null
|
||||
*/
|
||||
public function lastModifiedAddressBook($addressbookid) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all contacts for a specific addressbook id.
|
||||
*
|
||||
* The returned array MUST contain the unique ID of the contact mapped to 'id', a
|
||||
* displayname mapped to 'displayname' and an integer 'permissions' value using there
|
||||
* ownCloud CRUDS constants (which MUST be at least \OCP\PERMISSION_READ), and SHOULD
|
||||
* contain the properties of the contact formatted as a vCard 3.0
|
||||
* https://tools.ietf.org/html/rfc2426 mapped to 'carddata' or as an
|
||||
* \OCA\Contacts\VObject\VCard object mapped to 'vcard'.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* array(
|
||||
* 0 => array('id' => '4e111fef5df', 'permissions' => 1, 'displayname' => 'John Q. Public', 'vcard' => $object),
|
||||
* 1 => array('id' => 'bbcca2d1535', 'permissions' => 32, 'displayname' => 'Jane Doe', 'carddata' => $data)
|
||||
* );
|
||||
*
|
||||
* For contacts that contain loads of data, the 'carddata' or 'vcard' MAY be omitted
|
||||
* as it can be fetched later.
|
||||
*
|
||||
* TODO: Some sort of ETag?
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param bool $omitdata Don't fetch the entire carddata or vcard.
|
||||
* @return array
|
||||
*/
|
||||
public abstract function getContacts($addressbookid, $limit = null, $offset = null, $omitdata = false);
|
||||
|
||||
/**
|
||||
* Returns a specfic contact.
|
||||
*
|
||||
* Same as getContacts except that either 'carddata' or 'vcard' is mandatory.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $id
|
||||
* @return array|bool
|
||||
*/
|
||||
public abstract function getContact($addressbookid, $id);
|
||||
|
||||
/**
|
||||
* Creates a new contact
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param string $carddata
|
||||
* @return string|bool The identifier for the new contact or false on error.
|
||||
public function createContact($addressbookid, $carddata) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Updates a contact
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $id
|
||||
* @param string $carddata
|
||||
* @return bool
|
||||
public function updateContact($addressbookid, $id, $carddata) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Deletes a contact
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $id
|
||||
* @return bool
|
||||
public function deleteContact($addressbookid, $id) {
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
}
|
||||
}
|
724
lib/backend/database.php
Normal file
724
lib/backend/database.php
Normal file
@ -0,0 +1,724 @@
|
||||
<?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;
|
||||
use OCA\Contacts\VObject\VCard;
|
||||
use Sabre\VObject\Reader;
|
||||
|
||||
/**
|
||||
* Subclass this class for Cantacts backends
|
||||
*/
|
||||
|
||||
class Database extends AbstractBackend {
|
||||
|
||||
public $name = 'local';
|
||||
static private $preparedQueries = array();
|
||||
|
||||
/**
|
||||
* Sets up the backend
|
||||
*
|
||||
* @param string $addressBooksTableName
|
||||
* @param string $cardsTableName
|
||||
*/
|
||||
public function __construct(
|
||||
$userid = null,
|
||||
$addressBooksTableName = '*PREFIX*contacts_addressbooks',
|
||||
$cardsTableName = '*PREFIX*contacts_cards',
|
||||
$indexTableName = '*PREFIX*contacts_cards_properties'
|
||||
) {
|
||||
$this->userid = $userid ? $userid : \OCP\User::getUser();
|
||||
$this->addressBooksTableName = $addressBooksTableName;
|
||||
$this->cardsTableName = $cardsTableName;
|
||||
$this->indexTableName = $indexTableName;
|
||||
$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 {
|
||||
if(!isset(self::$preparedQueries['addressbooksforuser'])) {
|
||||
$sql = 'SELECT `id`, `displayname`, `description`, `ctag` AS `lastmodified`, `userid` AS `owner`, `uri` FROM `'
|
||||
. $this->addressBooksTableName
|
||||
. '` WHERE `userid` = ? ORDER BY `displayname`';
|
||||
self::$preparedQueries['addressbooksforuser'] = \OCP\DB::prepare($sql);
|
||||
}
|
||||
$result = self::$preparedQueries['addressbooksforuser']->execute(array($userid));
|
||||
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;
|
||||
$this->addressbooks[$row['id']] = $row;
|
||||
}
|
||||
return $this->addressbooks;
|
||||
}
|
||||
|
||||
public function getAddressBook($addressbookid) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' id: '
|
||||
// . $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];
|
||||
}
|
||||
// Hmm, not found. Lets query the db.
|
||||
try {
|
||||
$query = 'SELECT `id`, `displayname`, `description`, `userid` AS `owner`, `ctag` AS `lastmodified`, `uri` FROM `'
|
||||
. $this->addressBooksTableName
|
||||
. '` WHERE `id` = ?';
|
||||
if(!isset(self::$preparedQueries['getaddressbook'])) {
|
||||
self::$preparedQueries['getaddressbook'] = \OCP\DB::prepare($query);
|
||||
}
|
||||
$result = self::$preparedQueries['getaddressbook']->execute(array($addressbookid));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OCP\Util::write('contacts', __METHOD__. 'DB error: '
|
||||
. \OC_DB::getErrorMessage($result), \OCP\Util::ERROR);
|
||||
return array();
|
||||
}
|
||||
$row = $result->fetchRow();
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
return $row;
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' exception: '
|
||||
. $e->getMessage(), \OCP\Util::ERROR);
|
||||
return array();
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
public function hasAddressBook($addressbookid) {
|
||||
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
||||
return true;
|
||||
}
|
||||
return count($this->getAddressBook($addressbookid)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'];
|
||||
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
||||
$this->addressbooks[$addressbookid]['displayname'] = $changes['displayname'];
|
||||
}
|
||||
}
|
||||
|
||||
if(isset($changes['description'])) {
|
||||
$query .= '`description` = ?, ';
|
||||
$updates[] = $changes['description'];
|
||||
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
||||
$this->addressbooks[$addressbookid]['description'] = $changes['description'];
|
||||
}
|
||||
}
|
||||
|
||||
$query .= '`ctag` = `ctag` + 1 WHERE `id` = ?';
|
||||
$updates[] = $addressbookid;
|
||||
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare($query);
|
||||
$result = $stmt->execute($updates);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
if(count($properties) === 0 || !isset($properties['displayname'])) {
|
||||
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'] : '';
|
||||
$ctag = time();
|
||||
$updates[] = $ctag;
|
||||
|
||||
try {
|
||||
if(!isset(self::$preparedQueries['createaddressbook'])) {
|
||||
self::$preparedQueries['createaddressbook'] = \OCP\DB::prepare($query);
|
||||
}
|
||||
$result = self::$preparedQueries['createaddressbook']->execute($updates);
|
||||
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;
|
||||
}
|
||||
|
||||
$newid = \OCP\DB::insertid($this->addressBooksTableName);
|
||||
if($this->addressbooks) {
|
||||
$updates['id'] = $newid;
|
||||
$updates['ctag'] = $ctag;
|
||||
$updates['lastmodified'] = $ctag;
|
||||
$this->addressbooks[$addressbookid] = $updates;
|
||||
}
|
||||
return $newid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteAddressBook($addressbookid) {
|
||||
\OC_Hook::emit('OCA\Contacts', 'pre_deleteAddressBook',
|
||||
array('id' => $addressbookid)
|
||||
);
|
||||
|
||||
// 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);
|
||||
return;
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is_null($result)) {
|
||||
while($id = $result->fetchOne()) {
|
||||
$ids[] = $id;
|
||||
}
|
||||
}
|
||||
|
||||
// Purge contact property indexes
|
||||
$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);
|
||||
|
||||
if(!isset(self::$preparedQueries['deleteaddressbookcontacts'])) {
|
||||
self::$preparedQueries['deleteaddressbookcontacts'] =
|
||||
\OCP\DB::prepare('DELETE FROM `' . $this->cardsTableName
|
||||
. '` WHERE `addressbookid` = ?');
|
||||
}
|
||||
try {
|
||||
self::$preparedQueries['deleteaddressbookcontacts']
|
||||
->execute(array($addressbookid));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isset(self::$preparedQueries['deleteaddressbook'])) {
|
||||
self::$preparedQueries['deleteaddressbook'] =
|
||||
\OCP\DB::prepare('DELETE FROM `'
|
||||
. $this->addressBooksTableName . '` WHERE `id` = ?');
|
||||
}
|
||||
try {
|
||||
self::$preparedQueries['deleteaddressbook']
|
||||
->execute(array($addressbookid));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
||||
unset($this->addressbooks[$addressbookid]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates ctag for addressbook
|
||||
* @param integer $id
|
||||
* @return boolean
|
||||
*/
|
||||
public function touchAddressBook($id) {
|
||||
$query = 'UPDATE `' . $this->addressBooksTableName
|
||||
. '` SET `ctag` = `ctag` + 1 WHERE `id` = ?';
|
||||
if(!isset(self::$preparedQueries['touchaddressbook'])) {
|
||||
self::$preparedQueries['touchaddressbook'] = \OCP\DB::prepare($query);
|
||||
}
|
||||
self::$preparedQueries['touchaddressbook']->execute(array($id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function lastModifiedAddressBook($addressbookid) {
|
||||
if($this->addressbooks && isset($this->addressbooks[$addressbookid])) {
|
||||
return $this->addressbooks[$addressbookid]['lastmodified'];
|
||||
}
|
||||
$sql = 'SELECT MAX(`lastmodified`) FROM `' . $this->cardsTableName . '`, `' . $this->addressBooksTableName . '` ' .
|
||||
'WHERE `' . $this->cardsTableName . '`.`addressbookid` = `*PREFIX*contacts_addressbooks`.`id` AND ' .
|
||||
'`' . $this->addressBooksTableName . '`.`userid` = ? AND `' . $this->addressBooksTableName . '`.`id` = ?';
|
||||
if(!isset(self::$preparedQueries['lastmodifiedaddressbook'])) {
|
||||
self::$preparedQueries['lastmodifiedaddressbook'] = \OCP\DB::prepare($sql);
|
||||
}
|
||||
$result = self::$preparedQueries['lastmodifiedaddressbook']->execute(array($this->userid, $addressbookid));
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OC_Log::write('contacts', __METHOD__. 'DB error: ' . \OC_DB::getErrorMessage($result), \OC_Log::ERROR);
|
||||
return null;
|
||||
}
|
||||
return $result->fetchOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' addressbookid: ' . $addressbookid, \OCP\Util::DEBUG);
|
||||
$cards = array();
|
||||
try {
|
||||
$qfields = $omitdata ? '`id`, `fullname` AS `displayname`' : '*';
|
||||
$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)) {
|
||||
while($row = $result->fetchRow()) {
|
||||
$row['permissions'] = \OCP\PERMISSION_ALL;
|
||||
$cards[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific contact.
|
||||
*
|
||||
* 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, $noCollection = false) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $addressbookid . ' ' . $id['uri'], \OCP\Util::DEBUG);
|
||||
|
||||
$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.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$ids = array($id);
|
||||
|
||||
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($ids);
|
||||
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;
|
||||
}
|
||||
|
||||
public function hasContact($addressbookid, $id) {
|
||||
return $this->getContact($addressbookid, $id) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new contact
|
||||
*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $contact
|
||||
* @return string|bool The identifier for the new contact or false on error.
|
||||
*/
|
||||
public function createContact($addressbookid, $contact, $uri = null) {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$uri = is_null($uri) ? $contact->UID . '.vcf' : $uri;
|
||||
$now = new \DateTime;
|
||||
$contact->REV = $now->format(\DateTime::W3C);
|
||||
|
||||
$appinfo = \OCP\App::getAppInfo('contacts');
|
||||
$appversion = \OCP\App::getAppVersion('contacts');
|
||||
$prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
|
||||
$contact->PRODID = $prodid;
|
||||
|
||||
$data = $contact->serialize();
|
||||
if(!isset(self::$preparedQueries['createcontact'])) {
|
||||
self::$preparedQueries['createcontact'] = \OCP\DB::prepare('INSERT INTO `'
|
||||
. $this->cardsTableName
|
||||
. '` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' );
|
||||
}
|
||||
try {
|
||||
$result = self::$preparedQueries['createcontact']
|
||||
->execute(
|
||||
array(
|
||||
$addressbookid,
|
||||
(string)$contact->FN,
|
||||
$contact->serialize(),
|
||||
$uri,
|
||||
time()
|
||||
)
|
||||
);
|
||||
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);
|
||||
|
||||
$this->touchAddressBook($addressbookid);
|
||||
\OC_Hook::emit('OCA\Contacts', 'post_createContact',
|
||||
array('id' => $newid, 'parent' => $addressbookid, 'contact' => $contact)
|
||||
);
|
||||
return (string)$newid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a contact
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $id Contact ID
|
||||
* @param mixed $contact
|
||||
* @see getContact
|
||||
* @return bool
|
||||
*/
|
||||
public function updateContact($addressbookid, $id, $contact, $noCollection = false) {
|
||||
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'];
|
||||
$qname = 'createcontactbyid';
|
||||
} elseif(isset($id['uri'])) {
|
||||
$where_query = '`id` = ?';
|
||||
$id = $id['uri'];
|
||||
$qname = 'createcontactbyuri';
|
||||
} else {
|
||||
throw new Exception(
|
||||
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
|
||||
);
|
||||
}
|
||||
} 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($updates);
|
||||
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;
|
||||
}
|
||||
|
||||
$this->touchAddressBook($addressbookid);
|
||||
\OC_Hook::emit('OCA\Contacts', 'post_updateContact',
|
||||
array('id' => $id, 'parent' => $addressbookid, 'contact' => $contact)
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a contact
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param string $id
|
||||
* @see getContact
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteContact($addressbookid, $id) {
|
||||
$where_query = '`id` = ?';
|
||||
if(is_array($id)) {
|
||||
$where_query = '';
|
||||
if(isset($id['id'])) {
|
||||
$id = $id['id'];
|
||||
$qname = 'deletecontactsbyid';
|
||||
} elseif(isset($id['uri'])) {
|
||||
$where_query = '`id` = ?';
|
||||
$id = $id['uri'];
|
||||
$qname = 'deletecontactsbyuri';
|
||||
} else {
|
||||
throw new Exception(
|
||||
__METHOD__ . ' If second argument is an array, either \'id\' or \'uri\' has to be set.'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
$qname = 'deletecontactsbyid';
|
||||
}
|
||||
\OC_Hook::emit('OCA\Contacts', 'pre_deleteContact',
|
||||
array('id' => $id)
|
||||
);
|
||||
if(!isset(self::$preparedQueries[$qname])) {
|
||||
self::$preparedQueries[$qname] = \OCP\DB::prepare('DELETE FROM `'
|
||||
. $this->cardsTableName
|
||||
. '` WHERE ' . $where_query . ' AND `addressbookid` = ?');
|
||||
}
|
||||
try {
|
||||
$result = self::$preparedQueries[$qname]->execute(array($id, $addressbookid));
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
return ($contact ? $contact['lastmodified'] : null);
|
||||
}
|
||||
|
||||
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;
|
||||
while(in_array($newname, $uris)) {
|
||||
$newname = $name.$i;
|
||||
$i = $i + 1;
|
||||
}
|
||||
return $newname;
|
||||
}
|
||||
|
||||
}
|
124
lib/backend/shared.php
Normal file
124
lib/backend/shared.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Backend for Shared 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;
|
||||
|
||||
/**
|
||||
* Subclass this class for Cantacts backends
|
||||
*/
|
||||
|
||||
class Shared extends Database {
|
||||
|
||||
public $name = 'shared';
|
||||
public $addressbooks = array();
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressBooksForUser($userid = null) {
|
||||
$userid = $userid ? $userid : $this->userid;
|
||||
|
||||
$this->addressbooks = \OCP\Share::getItemsSharedWith(
|
||||
'addressbook',
|
||||
Contacts\Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS
|
||||
);
|
||||
|
||||
return $this->addressbooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific address book.
|
||||
*
|
||||
* @param string $addressbookid
|
||||
* @param mixed $id Contact ID
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAddressBook($addressbookid) {
|
||||
$addressbook = \OCP\Share::getItemSharedWithBySource(
|
||||
'addressbook',
|
||||
$addressbookid,
|
||||
Contacts\Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS
|
||||
);
|
||||
// Not sure if I'm doing it wrongly, or if its supposed to return
|
||||
// the info in an array?
|
||||
return (isset($addressbook['permissions']) ? $addressbook : $addressbook[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all contacts for a specific addressbook id.
|
||||
*
|
||||
* TODO: Check for parent permissions
|
||||
*
|
||||
* @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) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' addressbookid: '
|
||||
// . $addressbookid, \OCP\Util::DEBUG);
|
||||
$addressbook = \OCP\Share::getItemSharedWithBySource(
|
||||
'addressbook',
|
||||
$addressbookid,
|
||||
Contacts\Share_Backend_Addressbook::FORMAT_ADDRESSBOOKS,
|
||||
null, // parameters
|
||||
true // includeCollection
|
||||
);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' shared: '
|
||||
. print_r($addressbook, true), \OCP\Util::DEBUG);
|
||||
|
||||
$addressbook = $this->getAddressBook($addressbookid);
|
||||
$permissions = $addressbook['permissions'];
|
||||
|
||||
$cards = array();
|
||||
try {
|
||||
$qfields = $omitdata ? '`id`, `fullname` AS `displayname`, `lastmodified`' : '*';
|
||||
$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)) {
|
||||
while( $row = $result->fetchRow()) {
|
||||
$row['permissions'] = $permissions;
|
||||
$cards[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
}
|
@ -20,14 +20,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts\CardDAV;
|
||||
|
||||
use OCA\Contacts;
|
||||
|
||||
/**
|
||||
* This class overrides __construct to get access to $addressBookInfo and
|
||||
* $carddavBackend, Sabre_CardDAV_AddressBook::getACL() to return read/write
|
||||
* permissions based on user and shared state and it overrides
|
||||
* Sabre_CardDAV_AddressBook::getChild() and Sabre_CardDAV_AddressBook::getChildren()
|
||||
* to instantiate OC_Connector_Sabre_CardDAV_Cards.
|
||||
* to instantiate \OCA\Contacts\CardDAV\Cards.
|
||||
*/
|
||||
class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook {
|
||||
class AddressBook extends \Sabre_CardDAV_AddressBook {
|
||||
|
||||
/**
|
||||
* CardDAV backend
|
||||
@ -43,7 +47,7 @@ class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook {
|
||||
* @param array $addressBookInfo
|
||||
*/
|
||||
public function __construct(
|
||||
Sabre_CardDAV_Backend_Abstract $carddavBackend,
|
||||
\Sabre_CardDAV_Backend_Abstract $carddavBackend,
|
||||
array $addressBookInfo) {
|
||||
|
||||
$this->carddavBackend = $carddavBackend;
|
||||
@ -70,41 +74,42 @@ class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook {
|
||||
$writeprincipal = $this->getOwner();
|
||||
$createprincipal = $this->getOwner();
|
||||
$deleteprincipal = $this->getOwner();
|
||||
$uid = OCA\Contacts\Addressbook::extractUserID($this->getOwner());
|
||||
$uid = $this->carddavBackend->userIDByPrincipal($this->getOwner());
|
||||
|
||||
$readWriteACL = array(
|
||||
array(
|
||||
'privilege' => '{DAV:}read',
|
||||
'principal' => 'principals/' . OCP\User::getUser(),
|
||||
'principal' => 'principals/' . \OCP\User::getUser(),
|
||||
'protected' => true,
|
||||
),
|
||||
array(
|
||||
'privilege' => '{DAV:}write',
|
||||
'principal' => 'principals/' . OCP\User::getUser(),
|
||||
'principal' => 'principals/' . \OCP\User::getUser(),
|
||||
'protected' => true,
|
||||
),
|
||||
);
|
||||
|
||||
if($uid != OCP\USER::getUser()) {
|
||||
$sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $this->addressBookInfo['id']);
|
||||
if($uid !== \OCP\User::getUser()) {
|
||||
list($backendName, $id) = explode('::', $this->addressBookInfo['id']);
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if($sharedAddressbook) {
|
||||
if(($sharedAddressbook['permissions'] & OCP\PERMISSION_CREATE)
|
||||
&& ($sharedAddressbook['permissions'] & OCP\PERMISSION_UPDATE)
|
||||
&& ($sharedAddressbook['permissions'] & OCP\PERMISSION_DELETE)
|
||||
if(($sharedAddressbook['permissions'] & \OCP\PERMISSION_CREATE)
|
||||
&& ($sharedAddressbook['permissions'] & \OCP\PERMISSION_UPDATE)
|
||||
&& ($sharedAddressbook['permissions'] & \OCP\PERMISSION_DELETE)
|
||||
) {
|
||||
return $readWriteACL;
|
||||
}
|
||||
if ($sharedAddressbook['permissions'] & OCP\PERMISSION_CREATE) {
|
||||
$createprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if ($sharedAddressbook['permissions'] & \OCP\PERMISSION_CREATE) {
|
||||
$createprincipal = 'principals/' . \OCP\User::getUser();
|
||||
}
|
||||
if ($sharedAddressbook['permissions'] & OCP\PERMISSION_READ) {
|
||||
$readprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if ($sharedAddressbook['permissions'] & \OCP\PERMISSION_READ) {
|
||||
$readprincipal = 'principals/' . \OCP\User::getUser();
|
||||
}
|
||||
if ($sharedAddressbook['permissions'] & OCP\PERMISSION_UPDATE) {
|
||||
$writeprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if ($sharedAddressbook['permissions'] & \OCP\PERMISSION_UPDATE) {
|
||||
$writeprincipal = 'principals/' . \OCP\User::getUser();
|
||||
}
|
||||
if ($sharedAddressbook['permissions'] & OCP\PERMISSION_DELETE) {
|
||||
$deleteprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if ($sharedAddressbook['permissions'] & \OCP\PERMISSION_DELETE) {
|
||||
$deleteprincipal = 'principals/' . \OCP\User::getUser();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -198,8 +203,10 @@ class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook {
|
||||
public function getChild($name) {
|
||||
|
||||
$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'],$name);
|
||||
if (!$obj) throw new Sabre_DAV_Exception_NotFound('Card not found');
|
||||
return new OC_Connector_Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
if (!$obj) {
|
||||
throw new \Sabre_DAV_Exception_NotFound('Card not found');
|
||||
}
|
||||
return new Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
|
||||
}
|
||||
|
||||
@ -213,7 +220,7 @@ class OC_Connector_Sabre_CardDAV_AddressBook extends Sabre_CardDAV_AddressBook {
|
||||
$objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
|
||||
$children = array();
|
||||
foreach($objs as $obj) {
|
||||
$children[] = new OC_Connector_Sabre_CardDAV_Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
$children[] = new Card($this->carddavBackend,$this->addressBookInfo,$obj);
|
||||
}
|
||||
return $children;
|
||||
|
@ -20,11 +20,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts\CardDAV;
|
||||
|
||||
/**
|
||||
* This class overrides Sabre_CardDAV_AddressBookRoot::getChildForPrincipal()
|
||||
* to instantiate OC_Connector_CardDAV_UserAddressBooks.
|
||||
*/
|
||||
class OC_Connector_Sabre_CardDAV_AddressBookRoot extends Sabre_CardDAV_AddressBookRoot {
|
||||
class AddressBookRoot extends \Sabre_CardDAV_AddressBookRoot {
|
||||
|
||||
/**
|
||||
* This method returns a node for a principal.
|
||||
@ -38,7 +40,7 @@ class OC_Connector_Sabre_CardDAV_AddressBookRoot extends Sabre_CardDAV_AddressBo
|
||||
*/
|
||||
public function getChildForPrincipal(array $principal) {
|
||||
|
||||
return new OC_Connector_Sabre_CardDAV_UserAddressBooks($this->carddavBackend, $principal['uri']);
|
||||
return new UserAddressBooks($this->carddavBackend, $principal['uri']);
|
||||
|
||||
}
|
||||
|
258
lib/carddav/backend.php
Normal file
258
lib/carddav/backend.php
Normal file
@ -0,0 +1,258 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
*
|
||||
* 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\CardDAV;
|
||||
|
||||
use OCA\Contacts;
|
||||
|
||||
class Backend extends \Sabre_CardDAV_Backend_Abstract {
|
||||
|
||||
public function __construct($backends) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__, \OCP\Util::DEBUG);
|
||||
$this->backends = $backends;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressBooksForUser($principaluri) {
|
||||
$userid = $this->userIDByPrincipal($principaluri);
|
||||
$userAddressBooks = array();
|
||||
foreach($this->backends as $backend) {
|
||||
$addressBooks = $backend->getAddressBooksForUser($userid);
|
||||
|
||||
foreach($addressBooks as $addressBook) {
|
||||
if($addressBook['owner'] != \OCP\USER::getUser()) {
|
||||
$addressBook['uri'] = $addressBook['uri'] . '_shared_by_' . $addressBook['owner'];
|
||||
$addressBook['displayname'] = $addressBook['displayname'];
|
||||
}
|
||||
$userAddressbooks[] = array(
|
||||
'id' => $backend->name . '::' . $addressBook['id'],
|
||||
'uri' => $addressBook['uri'],
|
||||
'principaluri' => 'principals/'.$addressBook['owner'],
|
||||
'{DAV:}displayname' => $addressBook['displayname'],
|
||||
'{' . \Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description'
|
||||
=> $addressBook['description'],
|
||||
'{http://calendarserver.org/ns/}getctag' => $addressBook['lastmodified'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $userAddressbooks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateAddressBook($addressbookid, array $mutations) {
|
||||
$name = null;
|
||||
$description = null;
|
||||
$changes = array();
|
||||
|
||||
foreach($mutations as $property=>$newvalue) {
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$changes['name'] = $newvalue;
|
||||
break;
|
||||
case '{' . \Sabre_CardDAV_Plugin::NS_CARDDAV
|
||||
. '}addressbook-description' :
|
||||
$changes['description'] = $newvalue;
|
||||
break;
|
||||
default :
|
||||
// If any unsupported values were being updated, we must
|
||||
// let the entire request fail.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
return $backend->updateAddressBook($id, $changes);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @param string $uri Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createAddressBook($principaluri, $uri, array $properties) {
|
||||
|
||||
$properties = array();
|
||||
$userid = $this->userIDByPrincipal($principaluri);
|
||||
|
||||
foreach($properties as $property=>$newvalue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$properties['displayname'] = $newvalue;
|
||||
break;
|
||||
case '{' . \Sabre_CardDAV_Plugin::NS_CARDDAV
|
||||
. '}addressbook-description' :
|
||||
$properties['description'] = $newvalue;
|
||||
break;
|
||||
default :
|
||||
throw new \Sabre_DAV_Exception_BadRequest('Unknown property: '
|
||||
. $property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$properties['uri'] = $uri;
|
||||
|
||||
list(,$backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$backend->createAddressBook($properties, $userid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param int $addressbookid
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAddressBook($addressbookid) {
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$backend->deleteAddressBook($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @return array
|
||||
*/
|
||||
public function getCards($addressbookid) {
|
||||
$contacts = array();
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$contacts = $backend->getContacts($id);
|
||||
|
||||
$cards = array();
|
||||
foreach($contacts as $contact) {
|
||||
//OCP\Util::writeLog('contacts', __METHOD__.', uri: ' . $i['uri'], OCP\Util::DEBUG);
|
||||
$cards[] = array(
|
||||
'id' => $contact['id'],
|
||||
//'carddata' => $i['carddata'],
|
||||
'size' => strlen($contact['carddata']),
|
||||
'etag' => '"' . md5($contact['carddata']) . '"',
|
||||
'uri' => $contact['uri'],
|
||||
'lastmodified' => $contact['lastmodified'] );
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specfic card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @return array
|
||||
*/
|
||||
public function getCard($addressbookid, $carduri) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' identifier: ' . $carduri . ' ' . print_r($addressbookid, true), \OCP\Util::DEBUG);
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$contact = $backend->getContact($id, array('uri' => $carduri));
|
||||
return ($contact ? $contact : false);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new card
|
||||
*
|
||||
* We don't return an Etag as the carddata can have been modified
|
||||
* by Plugin::validate()
|
||||
*
|
||||
* @see Plugin::validate()
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @param string $carddata
|
||||
* @return string|null
|
||||
*/
|
||||
public function createCard($addressbookid, $carduri, $carddata) {
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$backend->createContact($id, $carddata, $carduri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @param string $carddata
|
||||
* @return null
|
||||
*/
|
||||
public function updateCard($addressbookid, $carduri, $carddata) {
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
$backend->updateContact($id, array('uri' => $carduri,), $carddata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteCard($addressbookid, $carduri) {
|
||||
list($id, $backend) = $this->getBackendForAddressBook($addressbookid);
|
||||
return $backend->deleteContact($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief gets the userid from a principal path
|
||||
* @return string
|
||||
*/
|
||||
public function userIDByPrincipal($principaluri) {
|
||||
list(, $userid) = \Sabre_DAV_URLUtil::splitPath($principaluri);
|
||||
return $userid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backend for an address book
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @return array(string, \OCA\Contacts\Backend\AbstractBackend)
|
||||
*/
|
||||
public function getBackendForAddressBook($addressbookid) {
|
||||
list($backendName, $id) = explode('::', $addressbookid);
|
||||
foreach($this->backends as $backend) {
|
||||
if($backend->name === $backendName && $backend->hasAddressBook($id)) {
|
||||
return array($id, $backend);
|
||||
}
|
||||
}
|
||||
throw new \Sabre_DAV_Exception_NotFound('Backend not found: ' . $addressbookid);
|
||||
}
|
||||
}
|
@ -20,11 +20,15 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts\CardDAV;
|
||||
|
||||
use OCA\Contacts;
|
||||
|
||||
/**
|
||||
* This class overrides Sabre_CardDAV_Card::getACL()
|
||||
* to return read/write permissions based on user and shared state.
|
||||
*/
|
||||
class OC_Connector_Sabre_CardDAV_Card extends Sabre_CardDAV_Card {
|
||||
class Card extends \Sabre_CardDAV_Card {
|
||||
|
||||
/**
|
||||
* Array with information about the containing addressbook
|
||||
@ -40,7 +44,7 @@ class OC_Connector_Sabre_CardDAV_Card extends Sabre_CardDAV_Card {
|
||||
* @param array $addressBookInfo
|
||||
* @param array $cardData
|
||||
*/
|
||||
public function __construct(Sabre_CardDAV_Backend_Abstract $carddavBackend, array $addressBookInfo, array $cardData) {
|
||||
public function __construct(\Sabre_CardDAV_Backend_Abstract $carddavBackend, array $addressBookInfo, array $cardData) {
|
||||
|
||||
$this->addressBookInfo = $addressBookInfo;
|
||||
parent::__construct($carddavBackend, $addressBookInfo, $cardData);
|
||||
@ -63,15 +67,16 @@ class OC_Connector_Sabre_CardDAV_Card extends Sabre_CardDAV_Card {
|
||||
|
||||
$readprincipal = $this->getOwner();
|
||||
$writeprincipal = $this->getOwner();
|
||||
$uid = OCA\Contacts\Addressbook::extractUserID($this->getOwner());
|
||||
$uid = $this->carddavBackend->userIDByPrincipal($this->getOwner());
|
||||
|
||||
if($uid != OCP\USER::getUser()) {
|
||||
$sharedAddressbook = OCP\Share::getItemSharedWithBySource('addressbook', $this->addressBookInfo['id']);
|
||||
if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\PERMISSION_READ)) {
|
||||
$readprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if($uid != \OCP\USER::getUser()) {
|
||||
list($backendName, $id) = explode('::', $this->addressBookInfo['id']);
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $id);
|
||||
if ($sharedAddressbook && ($sharedAddressbook['permissions'] & \OCP\PERMISSION_READ)) {
|
||||
$readprincipal = 'principals/' . \OCP\USER::getUser();
|
||||
}
|
||||
if ($sharedAddressbook && ($sharedAddressbook['permissions'] & OCP\PERMISSION_UPDATE)) {
|
||||
$writeprincipal = 'principals/' . OCP\USER::getUser();
|
||||
if ($sharedAddressbook && ($sharedAddressbook['permissions'] & \OCP\PERMISSION_UPDATE)) {
|
||||
$writeprincipal = 'principals/' . \OCP\USER::getUser();
|
||||
}
|
||||
}
|
||||
|
69
lib/carddav/plugin.php
Normal file
69
lib/carddav/plugin.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - CardDAV plugin
|
||||
*
|
||||
* The CardDAV plugin adds CardDAV functionality to the WebDAV server
|
||||
*
|
||||
* @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\CardDAV;
|
||||
|
||||
use Sabre\VObject;
|
||||
use OCA\Contacts\VObject\VCard;
|
||||
|
||||
/**
|
||||
* This class overrides Sabre_CardDAV_Plugin::validateVCard() to be able
|
||||
* to import partially invalid vCards by ignoring invalid lines and to
|
||||
* validate and upgrade using \OCA\Contacts\VCard.
|
||||
*/
|
||||
class Plugin extends \Sabre_CardDAV_Plugin {
|
||||
|
||||
/**
|
||||
* Checks if the submitted vCard data is in fact, valid.
|
||||
*
|
||||
* An exception is thrown if it's not.
|
||||
*
|
||||
* @param resource|string $data
|
||||
* @return void
|
||||
*/
|
||||
protected function validateVCard(&$data) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__, \OCP\Util::DEBUG);
|
||||
|
||||
// If it's a stream, we convert it to a string first.
|
||||
if (is_resource($data)) {
|
||||
$data = stream_get_contents($data);
|
||||
}
|
||||
|
||||
// Converting the data to unicode, if needed.
|
||||
$data = \Sabre_DAV_StringUtil::ensureUTF8($data);
|
||||
|
||||
try {
|
||||
$vobj = VObject\Reader::read($data, VObject\Reader::OPTION_IGNORE_INVALID_LINES);
|
||||
} catch (VObject\ParseException $e) {
|
||||
throw new \Sabre_DAV_Exception_UnsupportedMediaType('This resource only supports valid vcard data. Parse error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
if ($vobj->name !== 'VCARD') {
|
||||
throw new \Sabre_DAV_Exception_UnsupportedMediaType('This collection can only support vcard objects.');
|
||||
}
|
||||
|
||||
$vobj->validate(VCard::REPAIR|VCard::UPGRADE);
|
||||
$data = $vobj->serialize();
|
||||
}
|
||||
}
|
@ -20,11 +20,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\Contacts\CardDAV;
|
||||
|
||||
/**
|
||||
* This class overrides Sabre_CardDAV_UserAddressBooks::getChildren()
|
||||
* to instantiate OC_Connector_Sabre_CardDAV_AddressBooks.
|
||||
* to instantiate \OCA\Contacts\CardDAV\AddressBooks.
|
||||
*/
|
||||
class OC_Connector_Sabre_CardDAV_UserAddressBooks extends Sabre_CardDAV_UserAddressBooks {
|
||||
class UserAddressBooks extends \Sabre_CardDAV_UserAddressBooks {
|
||||
|
||||
/**
|
||||
* Returns a list of addressbooks
|
||||
@ -36,7 +38,7 @@ class OC_Connector_Sabre_CardDAV_UserAddressBooks extends Sabre_CardDAV_UserAddr
|
||||
$addressbooks = $this->carddavBackend->getAddressbooksForUser($this->principalUri);
|
||||
$objs = array();
|
||||
foreach($addressbooks as $addressbook) {
|
||||
$objs[] = new OC_Connector_Sabre_CardDAV_AddressBook($this->carddavBackend, $addressbook);
|
||||
$objs[] = new AddressBook($this->carddavBackend, $addressbook);
|
||||
}
|
||||
return $objs;
|
||||
|
647
lib/contact.php
Normal file
647
lib/contact.php
Normal file
@ -0,0 +1,647 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Contact object
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @copyright 2012 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;
|
||||
|
||||
use Sabre\VObject\Property;
|
||||
|
||||
/**
|
||||
* Subclass this class or implement IPIMObject interface for PIM objects
|
||||
*/
|
||||
|
||||
class Contact extends VObject\VCard implements IPIMObject {
|
||||
|
||||
const THUMBNAIL_PREFIX = 'contact-thumbnail-';
|
||||
const THUMBNAIL_SIZE = 28;
|
||||
|
||||
/**
|
||||
* The name of the object type in this case VCARD.
|
||||
*
|
||||
* This is used when serializing the object.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $name = 'VCARD';
|
||||
|
||||
protected $props = array();
|
||||
|
||||
/**
|
||||
* Create a new Contact object
|
||||
*
|
||||
* @param AddressBook $parent
|
||||
* @param AbstractBackend $backend
|
||||
* @param mixed $data
|
||||
*/
|
||||
public function __construct($parent, $backend, $data = null) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__ . ' ' . print_r($data, true), \OCP\Util::DEBUG);
|
||||
//\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($data->children as $child) {
|
||||
$this->add($child);
|
||||
$this->setRetrieved(true);
|
||||
}
|
||||
} elseif(is_array($data)) {
|
||||
foreach($data as $key => $value) {
|
||||
switch($key) {
|
||||
case 'id':
|
||||
$this->props['id'] = $value;
|
||||
break;
|
||||
case 'lastmodified':
|
||||
$this->props['lastmodified'] = $value;
|
||||
break;
|
||||
case 'uri':
|
||||
$this->props['uri'] = $value;
|
||||
break;
|
||||
case 'carddata':
|
||||
$this->props['carddata'] = $value;
|
||||
$this->retrieve();
|
||||
break;
|
||||
case 'vcard':
|
||||
$this->props['vcard'] = $value;
|
||||
$this->retrieve();
|
||||
break;
|
||||
case 'displayname':
|
||||
case 'fullname':
|
||||
$this->props['displayname'] = $value;
|
||||
$this->FN = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array|null
|
||||
*/
|
||||
public function getMetaData() {
|
||||
if(!isset($this->props['displayname'])) {
|
||||
if(!$this->retrieve()) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' error reading: '.print_r($this->props, true), \OCP\Util::ERROR);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return array(
|
||||
'id' => $this->getId(),
|
||||
'displayname' => $this->getDisplayName(),
|
||||
'permissions' => $this->getPermissions(),
|
||||
'lastmodified' => $this->lastModified(),
|
||||
'owner' => $this->getOwner(),
|
||||
'parent' => $this->getParent()->getId(),
|
||||
'backend' => $this->getBackend()->name,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a unique key combined of backend name, address book id and contact id.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function combinedKey() {
|
||||
return $this->getBackend()->name . '::' . $this->getParent()->getId() . '::' . $this->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getOwner() {
|
||||
return $this->props['parent']->getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getId() {
|
||||
return isset($this->props['id']) ? $this->props['id'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
function getDisplayName() {
|
||||
return isset($this->props['displayname']) ? $this->props['displayname'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getURI() {
|
||||
return isset($this->props['uri']) ? $this->props['uri'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this object is part of a collection return a reference
|
||||
* to the parent object, otherwise return null.
|
||||
* @return IPIMObject|null
|
||||
*/
|
||||
function getParent() {
|
||||
return $this->props['parent'];
|
||||
}
|
||||
|
||||
function getBackend() {
|
||||
return $this->props['backend'];
|
||||
}
|
||||
|
||||
/** CRUDS permissions (Create, Read, Update, Delete, Share)
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function getPermissions() {
|
||||
return $this->props['parent']->getPermissions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $permission
|
||||
* @return bool
|
||||
*/
|
||||
function hasPermission($permission) {
|
||||
return $this->getPermissions() & $permission;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the address book data to backend
|
||||
* FIXME
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function update(array $data) {
|
||||
|
||||
foreach($data as $key => $value) {
|
||||
switch($key) {
|
||||
case 'displayname':
|
||||
$this->addressBookInfo['displayname'] = $value;
|
||||
break;
|
||||
case 'description':
|
||||
$this->addressBookInfo['description'] = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $this->props['backend']->updateContact(
|
||||
$this->getParent()->getId(),
|
||||
$this->getId(),
|
||||
$this
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the data from backend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete() {
|
||||
return $this->props['backend']->deleteContact(
|
||||
$this->getParent()->getId(),
|
||||
$this->getId()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the contact data to backend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
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()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data from the backend
|
||||
* FIXME: Clean this up and make sure the logic is OK.
|
||||
*/
|
||||
public function retrieve() {
|
||||
//error_log(__METHOD__);
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' ' . print_r($this->props, true), \OCP\Util::DEBUG);
|
||||
if($this->isRetrieved()) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__. ' children', \OCP\Util::DEBUG);
|
||||
return true;
|
||||
} else {
|
||||
$data = null;
|
||||
if(isset($this->props['vcard'])
|
||||
&& $this->props['vcard'] instanceof VObject\VCard) {
|
||||
foreach($this->props['vcard']->children() as $child) {
|
||||
$this->add($child);
|
||||
if($child->name === 'FN') {
|
||||
$this->props['displayname']
|
||||
= 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->getParent()->getId(),
|
||||
$this->id
|
||||
);
|
||||
if($result) {
|
||||
if(isset($result['vcard'])
|
||||
&& $result['vcard'] instanceof VObject\VCard) {
|
||||
foreach($result['vcard']->children() as $child) {
|
||||
$this->add($child);
|
||||
}
|
||||
$this->setRetrieved(true);
|
||||
return true;
|
||||
} elseif(isset($result['carddata'])) {
|
||||
// Save internal values
|
||||
$data = $result['carddata'];
|
||||
$this->props['carddata'] = $result['carddata'];
|
||||
$this->props['lastmodified'] = $result['lastmodified'];
|
||||
$this->props['displayname'] = $result['displayname'];
|
||||
$this->props['permissions'] = $result['permissions'];
|
||||
} else {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__
|
||||
. ' Could not get vcard or carddata: '
|
||||
. $this->getId()
|
||||
. print_r($result, true), \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' Error getting contact: ' . $this->getId(), \OCP\Util::DEBUG);
|
||||
}
|
||||
} elseif(isset($this->props['carddata'])) {
|
||||
$data = $this->props['carddata'];
|
||||
//error_log(__METHOD__.' data: '.print_r($data, true));
|
||||
}
|
||||
try {
|
||||
$obj = \Sabre\VObject\Reader::read(
|
||||
$data,
|
||||
\Sabre\VObject\Reader::OPTION_IGNORE_INVALID_LINES
|
||||
);
|
||||
if($obj) {
|
||||
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 for: ' . $this->getId() . ' ' . $e->getMessage(),
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property index in the contact by the checksum of its serialized value
|
||||
*
|
||||
* @param string $checksum An 8 char m5d checksum.
|
||||
* @return \Sabre\VObject\Property Property by reference
|
||||
* @throws An exception with error code 404 if the property is not found.
|
||||
*/
|
||||
public function getPropertyIndexByChecksum($checksum) {
|
||||
$this->retrieve();
|
||||
$idx = 0;
|
||||
foreach($this->children as $i => &$property) {
|
||||
if(substr(md5($property->serialize()), 0, 8) == $checksum ) {
|
||||
return $idx;
|
||||
}
|
||||
$idx += 1;
|
||||
}
|
||||
throw new Exception('Property not found', 404);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a property by the checksum of its serialized value
|
||||
*
|
||||
* @param string $checksum An 8 char m5d checksum.
|
||||
* @return \Sabre\VObject\Property Property by reference
|
||||
* @throws An exception with error code 404 if the property is not found.
|
||||
*/
|
||||
public function getPropertyByChecksum($checksum) {
|
||||
$this->retrieve();
|
||||
foreach($this->children as $i => &$property) {
|
||||
if(substr(md5($property->serialize()), 0, 8) == $checksum ) {
|
||||
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) {
|
||||
$idx = $this->getPropertyIndexByChecksum($checksum);
|
||||
unset($this->children[$idx]);
|
||||
$this->setSaved(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()) {
|
||||
// FIXME: Change the debug and bailOut calls
|
||||
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 {
|
||||
debug('Saving ADR ' . $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:
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG);
|
||||
$property->setValue($value);
|
||||
break;
|
||||
}
|
||||
$this->setParameters($property, $parameters);
|
||||
$this->setSaved(false);
|
||||
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->add('VALUE', 'DATE');
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' BDAY: '.$this->BDAY->serialize(), \OCP\Util::DEBUG);
|
||||
break;
|
||||
case 'CATEGORIES':
|
||||
case 'N':
|
||||
case 'ORG':
|
||||
$property = $this->select($name);
|
||||
if(count($property) === 0) {
|
||||
$property = \Sabre\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->{$name} = $value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' adding: '.$name. ' ' . $value, \OCP\Util::DEBUG);
|
||||
$this->{$name} = $value;
|
||||
break;
|
||||
}
|
||||
$this->setSaved(false);
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function lastModified() {
|
||||
if(!isset($this->props['lastmodified'])) {
|
||||
$this->retrieve();
|
||||
}
|
||||
return isset($this->props['lastmodified'])
|
||||
? $this->props['lastmodified']
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge in data from a multi-dimentional array
|
||||
*
|
||||
* NOTE: This is *NOT* tested!
|
||||
* The data array has this structure:
|
||||
*
|
||||
* array(
|
||||
* 'EMAIL' => array(array('value' => 'johndoe@example.com', 'parameters' = array('TYPE' => array('HOME','VOICE'))))
|
||||
* );
|
||||
* @param array $data
|
||||
*/
|
||||
public function mergeFromArray(array $data) {
|
||||
foreach($data as $name => $properties) {
|
||||
if(in_array($name, array('PHOTO', 'UID'))) {
|
||||
continue;
|
||||
}
|
||||
if(!is_array($properties)) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' not an array?: ' .$name. ' '.print_r($properties, true), \OCP\Util::DEBUG);
|
||||
}
|
||||
if(in_array($name, Utils\Properties::$multi_properties)) {
|
||||
unset($this->{$name});
|
||||
}
|
||||
foreach($properties as $parray) {
|
||||
//$property = Property::create($name, $parray['value'], $parray['parameters']);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' adding: ' .$name. ' '.print_r($parray['value'], true) . ' ' . print_r($parray['parameters'], true), \OCP\Util::DEBUG);
|
||||
if(in_array($name, Utils\Properties::$multi_properties)) {
|
||||
// TODO: wrap in try/catch, check return value
|
||||
$this->setPropertyByChecksum('new', $name, $parray['value'], $parray['parameters']);
|
||||
} else {
|
||||
// TODO: Check return value
|
||||
if(!isset($this->{$name})) {
|
||||
$this->setPropertyByName($name, $parray['value'], $parray['parameters']);
|
||||
}
|
||||
}
|
||||
//$this->add($name, $parray['value'], $parray['parameters']);
|
||||
}
|
||||
}
|
||||
$this->setSaved(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function cacheThumbnail(\OC_Image $image = null) {
|
||||
$key = self::THUMBNAIL_PREFIX . $this->combinedKey();
|
||||
//\OC_Cache::remove($key);
|
||||
if(\OC_Cache::hasKey($key) && $image === null) {
|
||||
return \OC_Cache::get($key);
|
||||
}
|
||||
if(is_null($image)) {
|
||||
$this->retrieve();
|
||||
$image = new \OC_Image();
|
||||
if(!isset($this->PHOTO) && !isset($this->LOGO)) {
|
||||
return false;
|
||||
}
|
||||
if(!$image->loadFromBase64((string)$this->PHOTO)) {
|
||||
if(!$image->loadFromBase64((string)$this->LOGO)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!$image->centerCrop()) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'thumbnail.php. Couldn\'t crop thumbnail for ID ' . $key,
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
if(!$image->resize(self::THUMBNAIL_SIZE)) {
|
||||
\OCP\Util::writeLog('contacts',
|
||||
'thumbnail.php. Couldn\'t resize thumbnail for ID ' . $key,
|
||||
\OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
// Cache as base64 for around a month
|
||||
\OC_Cache::set($key, strval($image), 3000000);
|
||||
\OCP\Util::writeLog('contacts', 'Caching ' . $key, \OCP\Util::DEBUG);
|
||||
return \OC_Cache::get($key);
|
||||
}
|
||||
|
||||
public function __set($key, $value) {
|
||||
parent::__set($key, $value);
|
||||
$this->setSaved(false);
|
||||
}
|
||||
|
||||
public function __unset($key) {
|
||||
parent::__unset($key);
|
||||
$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'];
|
||||
}
|
||||
|
||||
}
|
103
lib/hooks.php
103
lib/hooks.php
@ -42,8 +42,8 @@ class Hooks{
|
||||
* @param paramters parameters from postCreateUser-Hook
|
||||
* @return array
|
||||
*/
|
||||
static public function createUser($parameters) {
|
||||
Addressbook::addDefault($parameters['uid']);
|
||||
public static function userCreated($parameters) {
|
||||
//Addressbook::addDefault($parameters['uid']);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -52,17 +52,101 @@ class Hooks{
|
||||
* @param paramters parameters from postDeleteUser-Hook
|
||||
* @return array
|
||||
*/
|
||||
static public function deleteUser($parameters) {
|
||||
$addressbooks = Addressbook::all($parameters['uid']);
|
||||
public static function userDeleted($parameters) {
|
||||
$backend = new Backend\Database();
|
||||
$addressbook = $backend->getAddressBooksForUser($parameters['uid']);
|
||||
|
||||
foreach($addressbooks as $addressbook) {
|
||||
Addressbook::delete($addressbook['id']);
|
||||
// Purging of contact categories and and properties is done by backend.
|
||||
$backend->deleteAddressBook($addressbook['id']);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static public function getCalenderSources($parameters) {
|
||||
/**
|
||||
* Delete any registred address books (Future)
|
||||
*/
|
||||
public static function addressBookDeletion($parameters) {
|
||||
}
|
||||
|
||||
public static function contactDeletion($parameters) {
|
||||
$catctrl = new \OC_VCategories('contact');
|
||||
$catctrl->purgeObjects(array($parameters['id']));
|
||||
Utils\Properties::updateIndex($parameters['id']);
|
||||
|
||||
// Contact sharing not implemented, but keep for future.
|
||||
//\OCP\Share::unshareAll('contact', $id);
|
||||
}
|
||||
|
||||
public static function contactUpdated($parameters) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' parameters: '.print_r($parameters, true), \OCP\Util::DEBUG);
|
||||
$catctrl = new \OC_VCategories('contact');
|
||||
$catctrl->loadFromVObject(
|
||||
$parameters['id'],
|
||||
new \OC_VObject($parameters['contact']), // OC_VCategories still uses OC_VObject
|
||||
true // force save
|
||||
);
|
||||
Utils\Properties::updateIndex($parameters['id'], $parameters['contact']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan vCards for categories.
|
||||
*/
|
||||
public static function scanCategories() {
|
||||
$offset = 0;
|
||||
$limit = 10;
|
||||
|
||||
$categories = new \OC_VCategories('contact');
|
||||
|
||||
$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) {
|
||||
$cards[] = array($contact['id'], $contact['carddata']);
|
||||
}
|
||||
\OCP\Util::writeLog('contacts',
|
||||
__CLASS__.'::'.__METHOD__
|
||||
.', scanning: ' . $limit . ' starting from ' . $offset,
|
||||
\OCP\Util::DEBUG);
|
||||
// only reset on first batch.
|
||||
$categories->rescan($cards, true, ($offset === 0 ? true : false));
|
||||
$offset += $limit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan vCards for categories.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function getCalenderSources($parameters) {
|
||||
/*
|
||||
$base_url = \OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id=';
|
||||
foreach(Addressbook::all(\OCP\USER::getUser()) as $addressbook) {
|
||||
$parameters['sources'][]
|
||||
@ -75,9 +159,10 @@ class Hooks{
|
||||
'editable' => false,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
static public function getBirthdayEvents($parameters) {
|
||||
public static function getBirthdayEvents($parameters) {
|
||||
$name = $parameters['calendar_id'];
|
||||
if (strpos($name, 'birthday_') != 0) {
|
||||
return;
|
||||
|
139
lib/ipimobject.php
Normal file
139
lib/ipimobject.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Interface for PIM object
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Implement this interface for PIM objects
|
||||
*/
|
||||
|
||||
interface IPIMObject {
|
||||
|
||||
/**
|
||||
* If this object is part of a collection return a reference
|
||||
* to the parent object, otherwise return null.
|
||||
* @return IPIMObject|null
|
||||
*/
|
||||
//function getParent();
|
||||
|
||||
/**
|
||||
* Get the identifier for the object.
|
||||
* @return string
|
||||
*/
|
||||
public function getId();
|
||||
|
||||
/**
|
||||
* A convenience method for getting all the info about the object.
|
||||
*
|
||||
* The returned array MUST contain:
|
||||
* 'id' @see getId().
|
||||
* 'displayname' @see getDisplayName()
|
||||
* 'owner' @see getOwner()
|
||||
* 'permissions' @see getPermissions
|
||||
* 'lastmodified' @see lastModified()
|
||||
*
|
||||
* If the object is part of a collection it MUST contain
|
||||
* 'parent' The identifier for the parent object. @see getParent()
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function getMetaData();
|
||||
|
||||
/**
|
||||
* FIXME: This should probably not be in the interface
|
||||
* as it's *DAV specific.
|
||||
* @return string
|
||||
*/
|
||||
public function getURI();
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
function getDisplayName();
|
||||
|
||||
/**
|
||||
* Get the owner of the object.
|
||||
* @return string|null
|
||||
*/
|
||||
function getOwner();
|
||||
|
||||
/**
|
||||
* If this object is part of a collection return a reference
|
||||
* to the parent object, otherwise return null.
|
||||
* @return IPIMObject|null
|
||||
*/
|
||||
function getParent();
|
||||
|
||||
|
||||
/** CRUDS permissions (Create, Read, Update, Delete, Share) using a bitmask of
|
||||
*
|
||||
* \OCP\PERMISSION_CREATE
|
||||
* \OCP\PERMISSION_READ
|
||||
* \OCP\PERMISSION_UPDATE
|
||||
* \OCP\PERMISSION_DELETE
|
||||
* \OCP\PERMISSION_SHARE
|
||||
* or
|
||||
* \OCP\PERMISSION_ALL
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
function getPermissions();
|
||||
|
||||
/**
|
||||
* @return AbstractBackend
|
||||
*/
|
||||
function getBackend();
|
||||
|
||||
/**
|
||||
* @param integer $permission
|
||||
* @return boolean
|
||||
*/
|
||||
function hasPermission($permission);
|
||||
|
||||
/**
|
||||
* Save the contact data to backend
|
||||
* FIXME: This isn't consistent. We need a proper interface
|
||||
* for creating new instances and saving to storage.
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function update(array $data);
|
||||
|
||||
/**
|
||||
* @brief Get the last modification time for the object.
|
||||
*
|
||||
* Must return a UNIX time stamp or null if the backend
|
||||
* doesn't support it.
|
||||
*
|
||||
* @returns int | null
|
||||
*/
|
||||
public function lastModified();
|
||||
|
||||
/**
|
||||
* Delete the data from backend
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function delete();
|
||||
|
||||
}
|
208
lib/request.php
Normal file
208
lib/request.php
Normal file
@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Request
|
||||
*
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* Class for accessing variables in the request.
|
||||
* This class provides an immutable object with request variables.
|
||||
*/
|
||||
|
||||
class Request implements \ArrayAccess, \Countable {
|
||||
|
||||
protected $items = array();
|
||||
|
||||
protected $varnames = array('get', 'post', 'files', 'server', 'env', 'session', 'cookies', 'urlParams', 'params', 'parameters', 'method');
|
||||
|
||||
/**
|
||||
* @param array $vars And associative array with the following optional values:
|
||||
* @param array 'params' the parsed json array
|
||||
* @param array 'urlParams' the parameters which were matched from the URL
|
||||
* @param array 'get' the $_GET array
|
||||
* @param array 'post' the $_POST array
|
||||
* @param array 'files' the $_FILES array
|
||||
* @param array 'server' the $_SERVER array
|
||||
* @param array 'env' the $_ENV array
|
||||
* @param array 'session' the $_SESSION array
|
||||
* @param array 'cookies' the $_COOKIE array
|
||||
* @param string 'method' the request method (GET, POST etc)
|
||||
* @see http://www.php.net/manual/en/reserved.variables.php
|
||||
*/
|
||||
public function __construct(array $vars = array()) {
|
||||
|
||||
foreach($this->varnames as $name) {
|
||||
$this->items[$name] = isset($vars[$name]) ? $vars[$name] : array();
|
||||
}
|
||||
|
||||
$this->items['parameters'] = array_merge(
|
||||
$this->items['params'],
|
||||
$this->items['get'],
|
||||
$this->items['post'],
|
||||
$this->items['urlParams']
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an instance of Request using default request variables.
|
||||
*/
|
||||
public static function getRequest($urlParams) {
|
||||
// Ensure that params is an array, not null
|
||||
$params = json_decode(file_get_contents('php://input'), true);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' params: '.print_r($params, true), \OCP\Util::DEBUG);
|
||||
$params = is_null($params) ? array() : $params;
|
||||
return new self(
|
||||
array(
|
||||
'get' => $_GET,
|
||||
'post' => $_POST,
|
||||
'files' => $_FILES,
|
||||
'server' => $_SERVER,
|
||||
'env' => $_ENV,
|
||||
'session' => $_SESSION,
|
||||
'cookies' => $_COOKIE,
|
||||
'method' => (isset($_SERVER) && isset($_SERVER['REQUEST_METHOD']))
|
||||
? $_SERVER['REQUEST_METHOD']
|
||||
: '',
|
||||
'params' => $params,
|
||||
'urlParams' => $urlParams
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Countable method.
|
||||
public function count() {
|
||||
return count(array_keys($this->items['parameters']));
|
||||
}
|
||||
|
||||
/**
|
||||
* ArrayAccess methods
|
||||
*
|
||||
* Gives access to the combined GET, POST and urlParams arrays
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* $var = $request['myvar'];
|
||||
*
|
||||
* or
|
||||
*
|
||||
* if(!isset($request['myvar']) {
|
||||
* // Do something
|
||||
* }
|
||||
*
|
||||
* $request['myvar'] = 'something'; // This throws an exception.
|
||||
*
|
||||
* @param string offset The key to lookup
|
||||
* @return string|null
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->items['parameters'][$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see offsetExists
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
return isset($this->items['parameters'][$offset])
|
||||
? $this->items['parameters'][$offset]
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see offsetExists
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
throw new \RuntimeException('You cannot change the contents of the request object');
|
||||
}
|
||||
|
||||
/**
|
||||
* @see offsetExists
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
throw new \RuntimeException('You cannot change the contents of the request object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from a request variable or the default value e.g:
|
||||
* $request->get('post', 'some_key', 'default value');
|
||||
*
|
||||
* @param string $vars Which variables to look in e.g. 'get', 'post, 'session'
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
*/
|
||||
public function getVar($vars, $name, $default = null) {
|
||||
return isset($this->{$vars}[$name]) ? $this->{$vars}[$name] : $default;
|
||||
}
|
||||
|
||||
// Magic property accessors
|
||||
public function __set($name, $value) {
|
||||
throw new \RuntimeException('You cannot change the contents of the request object');
|
||||
}
|
||||
|
||||
/**
|
||||
* Access request variables by method and name.
|
||||
* Examples:
|
||||
*
|
||||
* $request->post['myvar']; // Only look for POST variables
|
||||
* $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
|
||||
* Looks in the combined GET, POST and urlParams array.
|
||||
*
|
||||
* if($request->method !== 'POST') {
|
||||
* throw new Exception('This function can only be invoked using POST');
|
||||
* }
|
||||
*
|
||||
* @param string $name The key to look for.
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function __get($name) {
|
||||
switch($name) {
|
||||
case 'get':
|
||||
case 'post':
|
||||
case 'files':
|
||||
case 'server':
|
||||
case 'env':
|
||||
case 'session':
|
||||
case 'cookies':
|
||||
case 'params':
|
||||
case 'parameters':
|
||||
case 'urlParams':
|
||||
return isset($this->items[$name])
|
||||
? $this->items[$name]
|
||||
: null;
|
||||
break;
|
||||
case 'method':
|
||||
return $this->items['method'];
|
||||
break;
|
||||
default;
|
||||
return isset($this[$name]) ? $this[$name] : null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function __isset($name) {
|
||||
return isset($this->items['parameters'][$name]);
|
||||
}
|
||||
|
||||
public function __unset($id) {
|
||||
throw new \RunTimeException('You cannot change the contents of the request object');
|
||||
}
|
||||
|
||||
}
|
@ -1,212 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Addressbook
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* This CardDAV backend uses PDO to store addressbooks
|
||||
*/
|
||||
class OC_Connector_Sabre_CardDAV extends Sabre_CardDAV_Backend_Abstract {
|
||||
/**
|
||||
* Returns the list of addressbooks for a specific user.
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @return array
|
||||
*/
|
||||
public function getAddressBooksForUser($principaluri) {
|
||||
$data = OCA\Contacts\Addressbook::allWherePrincipalURIIs($principaluri);
|
||||
$addressbooks = array();
|
||||
|
||||
foreach($data as $i) {
|
||||
if($i['userid'] != OCP\USER::getUser()) {
|
||||
$i['uri'] = $i['uri'] . '_shared_by_' . $i['userid'];
|
||||
}
|
||||
$addressbooks[] = array(
|
||||
'id' => $i['id'],
|
||||
'uri' => $i['uri'],
|
||||
'principaluri' => 'principals/'.$i['userid'],
|
||||
'{DAV:}displayname' => $i['displayname'],
|
||||
'{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description'
|
||||
=> $i['description'],
|
||||
'{http://calendarserver.org/ns/}getctag' => $i['ctag'],
|
||||
);
|
||||
}
|
||||
|
||||
return $addressbooks;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates an addressbook's properties
|
||||
*
|
||||
* See Sabre_DAV_IProperties for a description of the mutations array, as
|
||||
* well as the return value.
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param array $mutations
|
||||
* @see Sabre_DAV_IProperties::updateProperties
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateAddressBook($addressbookid, array $mutations) {
|
||||
$name = null;
|
||||
$description = null;
|
||||
|
||||
foreach($mutations as $property=>$newvalue) {
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$name = $newvalue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV
|
||||
. '}addressbook-description' :
|
||||
$description = $newvalue;
|
||||
break;
|
||||
default :
|
||||
// If any unsupported values were being updated, we must
|
||||
// let the entire request fail.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
OCA\Contacts\Addressbook::edit($addressbookid, $name, $description);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new address book
|
||||
*
|
||||
* @param string $principaluri
|
||||
* @param string $url Just the 'basename' of the url.
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createAddressBook($principaluri, $url, array $properties) {
|
||||
|
||||
$displayname = null;
|
||||
$description = null;
|
||||
|
||||
foreach($properties as $property=>$newvalue) {
|
||||
|
||||
switch($property) {
|
||||
case '{DAV:}displayname' :
|
||||
$name = $newvalue;
|
||||
break;
|
||||
case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV
|
||||
. '}addressbook-description' :
|
||||
$description = $newvalue;
|
||||
break;
|
||||
default :
|
||||
throw new Sabre_DAV_Exception_BadRequest('Unknown property: '
|
||||
. $property);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
OCA\Contacts\Addressbook::addFromDAVData(
|
||||
$principaluri,
|
||||
$url,
|
||||
$name,
|
||||
$description
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an entire addressbook and all its contents
|
||||
*
|
||||
* @param int $addressbookid
|
||||
* @return void
|
||||
*/
|
||||
public function deleteAddressBook($addressbookid) {
|
||||
OCA\Contacts\Addressbook::delete($addressbookid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cards for a specific addressbook id.
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @return array
|
||||
*/
|
||||
public function getCards($addressbookid) {
|
||||
$data = OCA\Contacts\VCard::all($addressbookid);
|
||||
$cards = array();
|
||||
foreach($data as $i) {
|
||||
//OCP\Util::writeLog('contacts', __METHOD__.', uri: ' . $i['uri'], OCP\Util::DEBUG);
|
||||
$cards[] = array(
|
||||
'id' => $i['id'],
|
||||
//'carddata' => $i['carddata'],
|
||||
'size' => strlen($i['carddata']),
|
||||
'etag' => '"' . md5($i['carddata']) . '"',
|
||||
'uri' => $i['uri'],
|
||||
'lastmodified' => $i['lastmodified'] );
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specfic card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @return array
|
||||
*/
|
||||
public function getCard($addressbookid, $carduri) {
|
||||
return OCA\Contacts\VCard::findWhereDAVDataIs($addressbookid, $carduri);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @param string $carddata
|
||||
* @return bool
|
||||
*/
|
||||
public function createCard($addressbookid, $carduri, $carddata) {
|
||||
OCA\Contacts\VCard::addFromDAVData($addressbookid, $carduri, $carddata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @param string $carddata
|
||||
* @return bool
|
||||
*/
|
||||
public function updateCard($addressbookid, $carduri, $carddata) {
|
||||
return OCA\Contacts\VCard::editFromDAVData(
|
||||
$addressbookid, $carduri, $carddata
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a card
|
||||
*
|
||||
* @param mixed $addressbookid
|
||||
* @param string $carduri
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteCard($addressbookid, $carduri) {
|
||||
return OCA\Contacts\VCard::deleteFromDAVData($addressbookid, $carduri);
|
||||
}
|
||||
}
|
@ -10,6 +10,14 @@ namespace OCA\Contacts;
|
||||
|
||||
class Share_Backend_Addressbook implements \OCP\Share_Backend_Collection {
|
||||
const FORMAT_ADDRESSBOOKS = 1;
|
||||
const FORMAT_COLLECTION = 2;
|
||||
|
||||
public $backend;
|
||||
|
||||
public function __construct() {
|
||||
// Currently only share
|
||||
$this->backend = new Backend\Database();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the source of the item to be stored in the database
|
||||
@ -23,8 +31,8 @@ class Share_Backend_Addressbook implements \OCP\Share_Backend_Collection {
|
||||
* The formatItems() function will translate the source returned back into the item
|
||||
*/
|
||||
public function isValidSource($itemSource, $uidOwner) {
|
||||
$addressbook = Addressbook::find( $itemSource );
|
||||
if( $addressbook === false || $addressbook['userid'] != $uidOwner) {
|
||||
$addressbook = $this->backend->getAddressBook($itemSource);
|
||||
if(!$addressbook || $addressbook['userid'] !== $uidOwner) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -41,17 +49,20 @@ class Share_Backend_Addressbook implements \OCP\Share_Backend_Collection {
|
||||
* If it does generate a new name e.g. name_#
|
||||
*/
|
||||
public function generateTarget($itemSource, $shareWith, $exclude = null) {
|
||||
$addressbook = Addressbook::find( $itemSource );
|
||||
$addressbook = $this->backend->getAddressBook($itemSource);
|
||||
|
||||
$user_addressbooks = array();
|
||||
foreach(Addressbook::all($shareWith) as $user_addressbook) {
|
||||
|
||||
foreach($this->backend->getAddressBooksForUser($shareWith) as $user_addressbook) {
|
||||
$user_addressbooks[] = $user_addressbook['displayname'];
|
||||
}
|
||||
$name = $addressbook['displayname'];
|
||||
$name = $addressbook['displayname'] . '(' . $addressbook['userid'] . ')';
|
||||
$suffix = '';
|
||||
while (in_array($name.$suffix, $user_addressbooks)) {
|
||||
$suffix++;
|
||||
}
|
||||
|
||||
$suffix = $suffix ? ' ' . $suffix : '';
|
||||
return $name.$suffix;
|
||||
}
|
||||
|
||||
@ -68,26 +79,33 @@ class Share_Backend_Addressbook implements \OCP\Share_Backend_Collection {
|
||||
* This function allows the backend to control the output of shared items with custom formats.
|
||||
* It is only called through calls to the public getItem(s)Shared(With) functions.
|
||||
*/
|
||||
public function formatItems($items, $format, $parameters = null) {
|
||||
public function formatItems($items, $format, $parameters = null, $include = false) {
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__
|
||||
// . ' ' . $include . ' ' . print_r($items, true), \OCP\Util::DEBUG);
|
||||
$addressbooks = array();
|
||||
if ($format == self::FORMAT_ADDRESSBOOKS) {
|
||||
if ($format === self::FORMAT_ADDRESSBOOKS) {
|
||||
foreach ($items as $item) {
|
||||
$addressbook = Addressbook::find($item['item_source']);
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' item_source: ' . $item['item_source'] . ' include: '
|
||||
// . (int)$include, \OCP\Util::DEBUG);
|
||||
$addressbook = $this->backend->getAddressBook($item['item_source']);
|
||||
if ($addressbook) {
|
||||
$addressbook['displayname'] = $item['item_target'];
|
||||
$addressbook['displayname'] = $addressbook['displayname'] . ' (' . $addressbook['owner'] . ')';
|
||||
$addressbook['permissions'] = $item['permissions'];
|
||||
$addressbooks[] = $addressbook;
|
||||
}
|
||||
}
|
||||
} elseif ($format === self::FORMAT_COLLECTION) {
|
||||
foreach ($items as $item) {
|
||||
}
|
||||
}
|
||||
return $addressbooks;
|
||||
}
|
||||
|
||||
public function getChildren($itemSource) {
|
||||
$query = \OCP\DB::prepare('SELECT `id`, `fullname` FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ?');
|
||||
$result = $query->execute(array($itemSource));
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' item_source: ' . $itemSource, \OCP\Util::DEBUG);
|
||||
$contacts = $this->backend->getContacts($itemSource, null, null, true);
|
||||
$children = array();
|
||||
while ($contact = $result->fetchRow()) {
|
||||
foreach($contacts as $contact) {
|
||||
$children[] = array('source' => $contact['id'], 'target' => $contact['fullname']);
|
||||
}
|
||||
return $children;
|
||||
|
@ -27,6 +27,11 @@ class Share_Backend_Contact implements \OCP\Share_Backend {
|
||||
|
||||
private static $contact;
|
||||
|
||||
public function __construct() {
|
||||
// Currently only share
|
||||
$this->backend = new Backend\Database();
|
||||
}
|
||||
|
||||
public function isValidSource($itemSource, $uidOwner) {
|
||||
self::$contact = VCard::find($itemSource);
|
||||
if (self::$contact) {
|
||||
|
207
lib/utils/jsonserializer.php
Normal file
207
lib/utils/jsonserializer.php
Normal file
@ -0,0 +1,207 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - JSONSerializer
|
||||
*
|
||||
* @author Thomas Tanghus, Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
* @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\Utils;
|
||||
|
||||
use OCA\Contacts\VObject;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* General method serialize method. Use this for arrays
|
||||
* of contacts.
|
||||
*
|
||||
* @param Contact[] $input
|
||||
* @return array
|
||||
*/
|
||||
public static function serialize($input) {
|
||||
$response = array();
|
||||
if(is_array($input)) {
|
||||
foreach($input as $object) {
|
||||
if($object instanceof Contact) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' serializing: ' . print_r($object, true), \OCP\Util::DEBUG);
|
||||
$tmp = self::serializeContact($object);
|
||||
if($tmp !== null) {
|
||||
$response[] = $tmp;
|
||||
}
|
||||
} else {
|
||||
throw new \Exception(
|
||||
'Only arrays of OCA\\Contacts\\VObject\\VCard '
|
||||
. 'and Sabre\VObject\Property are accepted.'
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if($input instanceof VObject\VCard) {
|
||||
return self::serializeContact($input);
|
||||
} elseif($input instanceof Sabre\VObject\Property) {
|
||||
return self::serializeProperty($input);
|
||||
} else {
|
||||
throw new \Exception(
|
||||
'Only instances of OCA\\Contacts\\VObject\\VCard '
|
||||
. 'and Sabre\VObject\Property are accepted.'
|
||||
);
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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['thumbnail'] = $contact->cacheThumbnail();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get data structure of property.
|
||||
* @param \Sabre\VObject\Property $property
|
||||
* @return associative array
|
||||
*
|
||||
* returns an associative array with
|
||||
* ['name'] name of property
|
||||
* ['value'] htmlspecialchars escaped value of property
|
||||
* ['parameters'] associative array name=>value
|
||||
* ['checksum'] checksum of whole property
|
||||
* NOTE: $value is not escaped anymore. It shouldn't make any difference
|
||||
* but we should look out for any problems.
|
||||
*/
|
||||
public static function serializeProperty(\Sabre\VObject\Property $property) {
|
||||
if(!in_array($property->name, Properties::$index_properties)) {
|
||||
return;
|
||||
}
|
||||
$value = $property->value;
|
||||
if($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG' || $property->name == 'CATEGORIES') {
|
||||
$value = $property->getParts();
|
||||
$value = array_map('trim', $value);
|
||||
}
|
||||
elseif($property->name == 'BDAY') {
|
||||
if(strpos($value, '-') === false) {
|
||||
if(strlen($value) >= 8) {
|
||||
$value = substr($value, 0, 4).'-'.substr($value, 4, 2).'-'.substr($value, 6, 2);
|
||||
} else {
|
||||
return null; // Badly malformed :-(
|
||||
}
|
||||
}
|
||||
} elseif($property->name == 'PHOTO') {
|
||||
$value = true;
|
||||
}
|
||||
elseif($property->name == 'IMPP') {
|
||||
if(strpos($value, ':') !== false) {
|
||||
$value = explode(':', $value);
|
||||
$protocol = array_shift($value);
|
||||
if(!isset($property['X-SERVICE-TYPE'])) {
|
||||
$property['X-SERVICE-TYPE'] = strtoupper($protocol);
|
||||
}
|
||||
$value = implode('', $value);
|
||||
}
|
||||
}
|
||||
if(is_string($value)) {
|
||||
$value = strtr($value, array('\,' => ',', '\;' => ';'));
|
||||
}
|
||||
$temp = array(
|
||||
//'name' => $property->name,
|
||||
'value' => $value,
|
||||
'parameters' => array()
|
||||
);
|
||||
|
||||
// This cuts around a 3rd off of the json response size.
|
||||
if(in_array($property->name, Properties::$multi_properties)) {
|
||||
$temp['checksum'] = substr(md5($property->serialize()), 0, 8);
|
||||
}
|
||||
foreach($property->parameters as $parameter) {
|
||||
// Faulty entries by kaddressbook
|
||||
// Actually TYPE=PREF is correct according to RFC 2426
|
||||
// but this way is more handy in the UI. Tanghus.
|
||||
if($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') {
|
||||
$parameter->name = 'PREF';
|
||||
$parameter->value = '1';
|
||||
}
|
||||
// NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters
|
||||
// like TYPE=HOME,CELL,VOICE. Tanghus.
|
||||
// TODO: Check if parameter is has commas and split + merge if so.
|
||||
if ($parameter->name == 'TYPE') {
|
||||
$pvalue = $parameter->value;
|
||||
if(is_string($pvalue) && strpos($pvalue, ',') !== false) {
|
||||
$pvalue = array_map('trim', explode(',', $pvalue));
|
||||
}
|
||||
$pvalue = is_array($pvalue) ? $pvalue : array($pvalue);
|
||||
if (isset($temp['parameters'][$parameter->name])) {
|
||||
$temp['parameters'][$parameter->name][] = \OCP\Util::sanitizeHTML($pvalue);
|
||||
}
|
||||
else {
|
||||
$temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($pvalue);
|
||||
}
|
||||
}
|
||||
else{
|
||||
$temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($parameter->value);
|
||||
}
|
||||
}
|
||||
return $temp;
|
||||
}
|
||||
}
|
263
lib/utils/properties.php
Normal file
263
lib/utils/properties.php
Normal file
@ -0,0 +1,263 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - Interface for PIM object
|
||||
*
|
||||
* @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\Utils;
|
||||
|
||||
Properties::$l10n = \OC_L10N::get('contacts');
|
||||
|
||||
Class Properties {
|
||||
|
||||
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 OC_L10N
|
||||
*/
|
||||
public static $l10n;
|
||||
|
||||
/**
|
||||
* Properties there can be more than one of.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $multi_properties = array('EMAIL', 'TEL', 'IMPP', 'ADR', 'URL');
|
||||
|
||||
/**
|
||||
* Properties to index.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $index_properties = array(
|
||||
'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME',
|
||||
'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'PHOTO');
|
||||
|
||||
/**
|
||||
* Get options for IMPP properties
|
||||
* @param string $im
|
||||
* @return array of vcard prop => label
|
||||
*/
|
||||
public static function getIMOptions($im = null) {
|
||||
$l10n = self::$l10n;
|
||||
$ims = array(
|
||||
'jabber' => array(
|
||||
'displayname' => (string)$l10n->t('Jabber'),
|
||||
'xname' => 'X-JABBER',
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'aim' => array(
|
||||
'displayname' => (string)$l10n->t('AIM'),
|
||||
'xname' => 'X-AIM',
|
||||
'protocol' => 'aim',
|
||||
),
|
||||
'msn' => array(
|
||||
'displayname' => (string)$l10n->t('MSN'),
|
||||
'xname' => 'X-MSN',
|
||||
'protocol' => 'msn',
|
||||
),
|
||||
'twitter' => array(
|
||||
'displayname' => (string)$l10n->t('Twitter'),
|
||||
'xname' => 'X-TWITTER',
|
||||
'protocol' => 'twitter',
|
||||
),
|
||||
'googletalk' => array(
|
||||
'displayname' => (string)$l10n->t('GoogleTalk'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'facebook' => array(
|
||||
'displayname' => (string)$l10n->t('Facebook'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'xmpp' => array(
|
||||
'displayname' => (string)$l10n->t('XMPP'),
|
||||
'xname' => null,
|
||||
'protocol' => 'xmpp',
|
||||
),
|
||||
'icq' => array(
|
||||
'displayname' => (string)$l10n->t('ICQ'),
|
||||
'xname' => 'X-ICQ',
|
||||
'protocol' => 'icq',
|
||||
),
|
||||
'yahoo' => array(
|
||||
'displayname' => (string)$l10n->t('Yahoo'),
|
||||
'xname' => 'X-YAHOO',
|
||||
'protocol' => 'ymsgr',
|
||||
),
|
||||
'skype' => array(
|
||||
'displayname' => (string)$l10n->t('Skype'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'skype',
|
||||
),
|
||||
'qq' => array(
|
||||
'displayname' => (string)$l10n->t('QQ'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'x-apple',
|
||||
),
|
||||
'gadugadu' => array(
|
||||
'displayname' => (string)$l10n->t('GaduGadu'),
|
||||
'xname' => 'X-SKYPE',
|
||||
'protocol' => 'x-apple',
|
||||
),
|
||||
);
|
||||
if(is_null($im)) {
|
||||
return $ims;
|
||||
} else {
|
||||
$ims['ymsgr'] = $ims['yahoo'];
|
||||
$ims['gtalk'] = $ims['googletalk'];
|
||||
return isset($ims[$im]) ? $ims[$im] : null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get standard set of TYPE values for different properties.
|
||||
*
|
||||
* @param string $prop
|
||||
* @return array Type values for property $prop
|
||||
*/
|
||||
public static function getTypesForProperty($prop) {
|
||||
$l = self::$l10n;
|
||||
switch($prop) {
|
||||
case 'LABEL':
|
||||
case 'ADR':
|
||||
case 'IMPP':
|
||||
return array(
|
||||
'WORK' => (string)$l->t('Work'),
|
||||
'HOME' => (string)$l->t('Home'),
|
||||
'OTHER' => (string)$l->t('Other'),
|
||||
);
|
||||
case 'TEL':
|
||||
return array(
|
||||
'HOME' => (string)$l->t('Home'),
|
||||
'CELL' => (string)$l->t('Mobile'),
|
||||
'WORK' => (string)$l->t('Work'),
|
||||
'TEXT' => (string)$l->t('Text'),
|
||||
'VOICE' => (string)$l->t('Voice'),
|
||||
'MSG' => (string)$l->t('Message'),
|
||||
'FAX' => (string)$l->t('Fax'),
|
||||
'VIDEO' => (string)$l->t('Video'),
|
||||
'PAGER' => (string)$l->t('Pager'),
|
||||
'OTHER' => (string)$l->t('Other'),
|
||||
);
|
||||
case 'EMAIL':
|
||||
return array(
|
||||
'WORK' => (string)$l->t('Work'),
|
||||
'HOME' => (string)$l->t('Home'),
|
||||
'INTERNET' => (string)$l->t('Internet'),
|
||||
'OTHER' => (string)$l->t('Other'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the default categories of ownCloud
|
||||
* @return (array) $categories
|
||||
*/
|
||||
public static function getDefaultCategories() {
|
||||
$l10n = self::$l10n;
|
||||
return array(
|
||||
(string)$l10n->t('Friends'),
|
||||
(string)$l10n->t('Family'),
|
||||
(string)$l10n->t('Work'),
|
||||
(string)$l10n->t('Other'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function generateUID($app = 'contacts') {
|
||||
return date('Ymd\\THis') . '.' . time(). '@' . OCP\Util::getServerHostName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the contact property index.
|
||||
*
|
||||
* If vcard is null the properties for that contact will be purged.
|
||||
* If it is a valid object the old properties will first be purged
|
||||
* and the current properties indexed.
|
||||
*
|
||||
* @param string $contactid
|
||||
* @param \OCA\VObject\VCard|null $vcard
|
||||
*/
|
||||
public static function updateIndex($contactid, $vcard = null) {
|
||||
if(!isset(self::$deleteindexstmt)) {
|
||||
self::$deleteindexstmt
|
||||
= \OCP\DB::prepare('DELETE FROM `' . self::$indexTableName . '`'
|
||||
. ' WHERE `contactid` = ?');
|
||||
}
|
||||
try {
|
||||
self::$deleteindexstmt->execute(array($contactid));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
|
||||
. $id, \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error deleting properties for this contact.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if(is_null($vcard)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isset(self::$updateindexstmt)) {
|
||||
self::$updateindexstmt = \OCP\DB::prepare( 'INSERT INTO `' . self::$indexTableName . '` '
|
||||
. '(`userid`, `contactid`,`name`,`value`,`preferred`) VALUES(?,?,?,?,?)' );
|
||||
}
|
||||
foreach($vcard->children as $property) {
|
||||
if(!in_array($property->name, self::$index_properties)) {
|
||||
continue;
|
||||
}
|
||||
$preferred = 0;
|
||||
foreach($property->parameters as $parameter) {
|
||||
if($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') {
|
||||
$preferred = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
$result = self::$updateindexstmt->execute(
|
||||
array(
|
||||
\OCP\User::getUser(),
|
||||
$contactid,
|
||||
$property->name,
|
||||
$property->value,
|
||||
$preferred,
|
||||
)
|
||||
);
|
||||
if (\OC_DB::isError($result)) {
|
||||
\OCP\Util::writeLog('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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
818
lib/vcard.php
818
lib/vcard.php
@ -4,7 +4,7 @@
|
||||
*
|
||||
* @author Jakob Sack
|
||||
* @copyright 2011 Jakob Sack mail@jakobsack.de
|
||||
* @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
|
||||
* @copyright 2012-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
|
||||
@ -43,377 +43,6 @@ use Sabre\VObject;
|
||||
* This class manages our vCards
|
||||
*/
|
||||
class VCard {
|
||||
/**
|
||||
* @brief Returns all cards of an address book
|
||||
* @param integer $id
|
||||
* @param integer $offset
|
||||
* @param integer $limit
|
||||
* @param array $fields An array of the fields to return. Defaults to all.
|
||||
* @return array|false
|
||||
*
|
||||
* The cards are associative arrays. You'll find the original vCard in
|
||||
* ['carddata']
|
||||
*/
|
||||
public static function all($id, $offset=null, $limit=null, $fields = array()) {
|
||||
$result = null;
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.'count fields:' . count($fields), \OCP\Util::DEBUG);
|
||||
$qfields = count($fields) > 0
|
||||
? '`' . implode('`,`', $fields) . '`'
|
||||
: '*';
|
||||
if(is_array($id) && count($id)) {
|
||||
$id_sql = join(',', array_fill(0, count($id), '?'));
|
||||
$sql = 'SELECT ' . $qfields . ' FROM `*PREFIX*contacts_cards` WHERE `addressbookid` IN ('.$id_sql.') ORDER BY `fullname`';
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare($sql, $limit, $offset);
|
||||
$result = $stmt->execute($id);
|
||||
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__.', ids: ' . join(',', $id), \OCP\Util::DEBUG);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.'SQL:' . $sql, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
} elseif(is_int($id) || is_string($id)) {
|
||||
try {
|
||||
$sql = 'SELECT ' . $qfields . ' FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? ORDER BY `fullname`';
|
||||
$stmt = \OCP\DB::prepare($sql, $limit, $offset);
|
||||
$result = $stmt->execute(array($id));
|
||||
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__.', ids: '. $id, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__
|
||||
. '. Addressbook id(s) argument is empty: '
|
||||
. print_r($id, true), \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$cards = array();
|
||||
if(!is_null($result)) {
|
||||
while( $row = $result->fetchRow()) {
|
||||
$cards[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
return $cards;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a card
|
||||
* @param integer $id
|
||||
* @param array $fields An array of the fields to return. Defaults to all.
|
||||
* @return associative array or false.
|
||||
*/
|
||||
public static function find($id, $fields = array() ) {
|
||||
if(count($fields) > 0 && !in_array('addressbookid', $fields)) {
|
||||
$fields[] = 'addressbookid';
|
||||
}
|
||||
try {
|
||||
$qfields = count($fields) > 0
|
||||
? '`' . implode('`,`', $fields) . '`'
|
||||
: '*';
|
||||
$stmt = \OCP\DB::prepare( 'SELECT ' . $qfields . ' FROM `*PREFIX*contacts_cards` WHERE `id` = ?' );
|
||||
$result = $stmt->execute(array($id));
|
||||
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();
|
||||
if($row) {
|
||||
try {
|
||||
$addressbook = Addressbook::find($row['addressbookid']);
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '.$e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '. $id, \OCP\Util::DEBUG);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
return $row;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief finds a card by its DAV Data
|
||||
* @param integer $aid Addressbook id
|
||||
* @param string $uri the uri ('filename')
|
||||
* @return associative array or false.
|
||||
*/
|
||||
public static function findWhereDAVDataIs($aid, $uri) {
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' );
|
||||
$result = $stmt->execute(array($aid,$uri));
|
||||
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__.', aid: '.$aid.' uri'.$uri, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
|
||||
return $result->fetchRow();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Format property TYPE parameters for upgrading from v. 2.1
|
||||
* @param $property Reference to a Sabre_VObject_Property.
|
||||
* In version 2.1 e.g. a phone can be formatted like: TEL;HOME;CELL:123456789
|
||||
* This has to be changed to either TEL;TYPE=HOME,CELL:123456789 or TEL;TYPE=HOME;TYPE=CELL:123456789 - both are valid.
|
||||
*/
|
||||
public static function formatPropertyTypes(&$property) {
|
||||
foreach($property->parameters as $key=>&$parameter) {
|
||||
$types = App::getTypesOfProperty($property->name);
|
||||
if(is_array($types) && in_array(strtoupper($parameter->name), array_keys($types)) || strtoupper($parameter->name) == 'PREF') {
|
||||
$property->parameters[] = new \Sabre\VObject\Parameter('TYPE', $parameter->name);
|
||||
}
|
||||
unset($property->parameters[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode properties for upgrading from v. 2.1
|
||||
* @param $property Reference to a Sabre_VObject_Property.
|
||||
* The only encoding allowed in version 3.0 is 'b' for binary. All encoded strings
|
||||
* must therefor be decoded and the parameters removed.
|
||||
*/
|
||||
public static function decodeProperty(&$property) {
|
||||
// Check out for encoded string and decode them :-[
|
||||
foreach($property->parameters as $key=>&$parameter) {
|
||||
if(strtoupper($parameter->name) == 'ENCODING') {
|
||||
if(strtoupper($parameter->value) == 'QUOTED-PRINTABLE') { // what kind of other encodings could be used?
|
||||
// Decode quoted-printable and strip any control chars
|
||||
// except \n and \r
|
||||
$property->value = preg_replace(
|
||||
'/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F]/',
|
||||
'',
|
||||
quoted_printable_decode($property->value)
|
||||
);
|
||||
unset($property->parameters[$key]);
|
||||
}
|
||||
} elseif(strtoupper($parameter->name) == 'CHARSET') {
|
||||
unset($property->parameters[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a contact with the same UID already exist in the address book.
|
||||
* @param $aid Address book ID.
|
||||
* @param $uid UID (passed by reference).
|
||||
* @returns true if the UID has been changed.
|
||||
*/
|
||||
protected static function trueUID($aid, &$uid) {
|
||||
$stmt = \OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' );
|
||||
$uri = $uid.'.vcf';
|
||||
try {
|
||||
$result = $stmt->execute(array($aid,$uri));
|
||||
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__.', aid: '.$aid.' uid'.$uid, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
if($result->numRows() > 0) {
|
||||
while(true) {
|
||||
$tmpuid = substr(md5(rand().time()), 0, 10);
|
||||
$uri = $tmpuid.'.vcf';
|
||||
$result = $stmt->execute(array($aid, $uri));
|
||||
if($result->numRows() > 0) {
|
||||
continue;
|
||||
} else {
|
||||
$uid = $tmpuid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 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 = isset($vcard->VERSION) ? $vcard->VERSION : null;
|
||||
// Add version if needed
|
||||
if($version && $version < '3.0') {
|
||||
$upgrade = true;
|
||||
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. Updating from version: '.$version, OCP\Util::DEBUG);
|
||||
}
|
||||
foreach($vcard->children as &$property) {
|
||||
// Decode string properties and remove obsolete properties.
|
||||
if($upgrade && in_array($property->name, $stringprops)) {
|
||||
self::decodeProperty($property);
|
||||
}
|
||||
if(function_exists('iconv')) {
|
||||
$property->value = str_replace("\r\n", "\n", iconv(mb_detect_encoding($property->value, 'UTF-8, ISO-8859-1'), 'utf-8', $property->value));
|
||||
} else {
|
||||
$property->value = str_replace("\r\n", "\n", mb_convert_encoding($property->value, 'UTF-8', mb_detect_encoding($property->value, 'UTF-8, ISO-8859-1'), $property->value));
|
||||
}
|
||||
if(in_array($property->name, $stringprops)) {
|
||||
$property->value = strip_tags($property->value);
|
||||
}
|
||||
// Fix format of type parameters.
|
||||
if($upgrade && in_array($property->name, $typeprops)) {
|
||||
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. before: '.$property->serialize(), OCP\Util::DEBUG);
|
||||
self::formatPropertyTypes($property);
|
||||
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. after: '.$property->serialize(), OCP\Util::DEBUG);
|
||||
}
|
||||
if($property->name == 'FN') {
|
||||
$fn = $property->value;
|
||||
}
|
||||
else if($property->name == 'N') {
|
||||
$n = $property->value;
|
||||
}
|
||||
else if($property->name == 'UID') {
|
||||
$uid = $property->value;
|
||||
}
|
||||
else if($property->name == 'ORG') {
|
||||
$org = $property->value;
|
||||
}
|
||||
else if($property->name == 'EMAIL' && is_null($email)) { // only use the first email as substitute for missing N or FN.
|
||||
$email = $property->value;
|
||||
}
|
||||
}
|
||||
// Check for missing 'N', 'FN' and 'UID' properties
|
||||
if(!$fn) {
|
||||
if($n && $n != ';;;;') {
|
||||
$fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
|
||||
} elseif($email) {
|
||||
$fn = $email;
|
||||
} elseif($org) {
|
||||
$fn = $org;
|
||||
} else {
|
||||
$fn = 'Unknown Name';
|
||||
}
|
||||
$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 ;-)
|
||||
$slice = array_reverse(array_slice(explode(' ', $fn), 0, 2)); // Take 2 first name parts of 'FN' and reverse.
|
||||
if(count($slice) < 2) { // If not enought, add one more...
|
||||
$slice[] = "";
|
||||
}
|
||||
$n = implode(';', $slice).';;;';
|
||||
$vcard->N = $n;
|
||||
//OCP\Util::writeLog('contacts', 'OCA\Contacts\VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n, OCP\Util::DEBUG);
|
||||
}
|
||||
if(!$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->{'UID'} = $uid;
|
||||
}
|
||||
$now = new \DateTime;
|
||||
$vcard->{'REV'} = $now->format(\DateTime::W3C);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a card
|
||||
* @param $aid integer Addressbook id
|
||||
* @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, VObject\Component $card, $uri=null, $isChecked=false) {
|
||||
if(is_null($card)) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__ . ', No vCard supplied', \OCP\Util::ERROR);
|
||||
return null;
|
||||
};
|
||||
$addressbook = Addressbook::find($aid);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $aid);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_CREATE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to add contacts to this addressbook.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if(!$isChecked) {
|
||||
self::updateValuesFromAdd($aid, $card);
|
||||
}
|
||||
$card->{'VERSION'} = '3.0';
|
||||
// Add product ID is missing.
|
||||
//$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->add('PRODID', $prodid);
|
||||
}
|
||||
|
||||
$fn = isset($card->FN) ? $card->FN : '';
|
||||
|
||||
$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(?,?,?,?,?)' );
|
||||
try {
|
||||
$result = $stmt->execute(array($aid, $fn, $data, $uri, time()));
|
||||
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__.', aid: '.$aid.' uri'.$uri, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
$newid = \OCP\DB::insertid('*PREFIX*contacts_cards');
|
||||
App::loadCategoriesFromVCard($newid, $card);
|
||||
App::updateDBProperties($newid, $card);
|
||||
App::cacheThumbnail($newid);
|
||||
|
||||
Addressbook::touch($aid);
|
||||
\OC_Hook::emit('\OCA\Contacts\VCard', 'post_createVCard', $newid);
|
||||
return $newid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a card with the data provided by sabredav
|
||||
* @param integer $id Addressbook id
|
||||
* @param string $uri the uri the card will have
|
||||
* @param string $data vCard file
|
||||
* @returns integer|false insertid or false on error
|
||||
*/
|
||||
public static function addFromDAVData($id, $uri, $data) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Mass updates an array of cards
|
||||
@ -435,13 +64,6 @@ class VCard {
|
||||
return false;
|
||||
}
|
||||
|
||||
$addressbook = Addressbook::find($oldcard['addressbookid']);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedContact = \OCP\Share::getItemSharedWithBySource('contact', $object[0], \OCP\Share::FORMAT_NONE, null, true);
|
||||
if (!$sharedContact || !($sharedContact['permissions'] & \OCP\PERMISSION_UPDATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$vcard->{'REV'} = $now->format(\DateTime::W3C);
|
||||
$data = $vcard->serialize();
|
||||
try {
|
||||
@ -459,442 +81,4 @@ class VCard {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief edits a card
|
||||
* @param integer $id id of card
|
||||
* @param Sabre\VObject\Component $card vCard file
|
||||
* @return boolean true on success, otherwise an exception will be thrown
|
||||
*/
|
||||
public static function edit($id, VObject\Component $card) {
|
||||
$oldcard = self::find($id);
|
||||
if (!$oldcard) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
|
||||
. $id . ' not found.', \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'Could not find the vCard with ID.' . $id
|
||||
)
|
||||
);
|
||||
}
|
||||
if(is_null($card)) {
|
||||
return false;
|
||||
}
|
||||
// NOTE: Owner checks are being made in the ajax files, which should be done
|
||||
// inside the lib files to prevent any redundancies with sharing checks
|
||||
$addressbook = Addressbook::find($oldcard['addressbookid']);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource(
|
||||
'addressbook',
|
||||
$oldcard['addressbookid'],
|
||||
\OCP\Share::FORMAT_NONE, null, true);
|
||||
$sharedContact = \OCP\Share::getItemSharedWithBySource('contact', $id, \OCP\Share::FORMAT_NONE, null, true);
|
||||
$addressbook_permissions = 0;
|
||||
$contact_permissions = 0;
|
||||
if ($sharedAddressbook) {
|
||||
$addressbook_permissions = $sharedAddressbook['permissions'];
|
||||
}
|
||||
if ($sharedContact) {
|
||||
$contact_permissions = $sharedEvent['permissions'];
|
||||
}
|
||||
$permissions = max($addressbook_permissions, $contact_permissions);
|
||||
if (!($permissions & \OCP\PERMISSION_UPDATE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to edit this contact.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
App::loadCategoriesFromVCard($id, $card);
|
||||
|
||||
$fn = isset($card->FN) ? $card->FN : '';
|
||||
|
||||
$now = new \DateTime;
|
||||
$card->REV = $now->format(\DateTime::W3C);
|
||||
|
||||
$data = $card->serialize();
|
||||
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE `id` = ?' );
|
||||
try {
|
||||
$result = $stmt->execute(array($fn, $data, time(), $id));
|
||||
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;
|
||||
}
|
||||
|
||||
App::cacheThumbnail($oldcard['id']);
|
||||
App::updateDBProperties($id, $card);
|
||||
Addressbook::touch($oldcard['addressbookid']);
|
||||
\OC_Hook::emit('\OCA\Contacts\VCard', 'post_updateVCard', $id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief edits a card with the data provided by sabredav
|
||||
* @param integer $id Addressbook id
|
||||
* @param string $uri the uri of the card
|
||||
* @param string $data vCard file
|
||||
* @return boolean
|
||||
*/
|
||||
public static function editFromDAVData($aid, $uri, $data) {
|
||||
$oldcard = self::findWhereDAVDataIs($aid, $uri);
|
||||
try {
|
||||
$vcard = \Sabre\VObject\Reader::read($data);
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', Unable to parse VCARD, : ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
self::edit($oldcard['id'], $vcard);
|
||||
return true;
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', exception: '
|
||||
. $e->getMessage() . ', '
|
||||
. \OCP\USER::getUser(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', uri'
|
||||
. $uri, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deletes a card
|
||||
* @param integer $id id of card
|
||||
* @return boolean true on success, otherwise an exception will be thrown
|
||||
*/
|
||||
public static function delete($id) {
|
||||
$contact = self::find($id);
|
||||
if (!$contact) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
|
||||
. $id . ' not found.', \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'Could not find the vCard with ID: ' . $id, 404
|
||||
)
|
||||
);
|
||||
}
|
||||
$addressbook = Addressbook::find($contact['addressbookid']);
|
||||
if(!$addressbook) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'Could not find the Addressbook with ID: '
|
||||
. $contact['addressbookid'], 404
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ($addressbook['userid'] != \OCP\User::getUser() && !\OC_Group::inGroup(\OCP\User::getUser(), 'admin')) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', '
|
||||
. $addressbook['userid'] . ' != ' . \OCP\User::getUser(), \OCP\Util::DEBUG);
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource(
|
||||
'addressbook',
|
||||
$contact['addressbookid'],
|
||||
\OCP\Share::FORMAT_NONE, null, true);
|
||||
$sharedContact = \OCP\Share::getItemSharedWithBySource(
|
||||
'contact',
|
||||
$id,
|
||||
\OCP\Share::FORMAT_NONE, null, true);
|
||||
$addressbook_permissions = 0;
|
||||
$contact_permissions = 0;
|
||||
if ($sharedAddressbook) {
|
||||
$addressbook_permissions = $sharedAddressbook['permissions'];
|
||||
}
|
||||
if ($sharedContact) {
|
||||
$contact_permissions = $sharedEvent['permissions'];
|
||||
}
|
||||
$permissions = max($addressbook_permissions, $contact_permissions);
|
||||
|
||||
if (!($permissions & \OCP\PERMISSION_DELETE)) {
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'You do not have the permissions to delete this contact.', 403
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
$aid = $contact['addressbookid'];
|
||||
\OC_Hook::emit('\OCA\Contacts\VCard', 'pre_deleteVCard',
|
||||
array('aid' => null, 'id' => $id, 'uri' => null)
|
||||
);
|
||||
$stmt = \OCP\DB::prepare('DELETE FROM `*PREFIX*contacts_cards` WHERE `id` = ?');
|
||||
try {
|
||||
$stmt->execute(array($id));
|
||||
} catch(\Exception $e) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.
|
||||
', exception: ' . $e->getMessage(), \OCP\Util::ERROR);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', id: '
|
||||
. $id, \OCP\Util::DEBUG);
|
||||
throw new \Exception(
|
||||
App::$l10n->t(
|
||||
'There was an error deleting this contact.'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
App::updateDBProperties($id);
|
||||
App::getVCategories()->purgeObjects(array($id));
|
||||
Addressbook::touch($addressbook['id']);
|
||||
|
||||
// Contact sharing not enabled, so comment out this
|
||||
//\OCP\Share::unshareAll('contact', $id);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief deletes a card with the data provided by sabredav
|
||||
* @param integer $aid Addressbook id
|
||||
* @param string $uri the uri of the card
|
||||
* @return boolean
|
||||
*/
|
||||
public static function deleteFromDAVData($aid, $uri) {
|
||||
$contact = self::findWhereDAVDataIs($aid, $uri);
|
||||
if(!$contact) {
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', contact not found: '
|
||||
. $uri, \OCP\Util::DEBUG);
|
||||
throw new \Sabre_DAV_Exception_NotFound(
|
||||
App::$l10n->t(
|
||||
'Contact not found.'
|
||||
)
|
||||
);
|
||||
}
|
||||
$id = $contact['id'];
|
||||
try {
|
||||
return self::delete($id);
|
||||
} catch (\Exception $e) {
|
||||
switch($e->getCode()) {
|
||||
case 403:
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', forbidden: '
|
||||
. $uri, \OCP\Util::DEBUG);
|
||||
throw new \Sabre_DAV_Exception_Forbidden(
|
||||
App::$l10n->t(
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
break;
|
||||
case 404:
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', contact not found: '
|
||||
. $uri, \OCP\Util::DEBUG);
|
||||
throw new \Sabre_DAV_Exception_NotFound(
|
||||
App::$l10n->t(
|
||||
$e->getMessage()
|
||||
)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw $e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Data structure of vCard
|
||||
* @param Sabre\VObject\Component $property
|
||||
* @return associative array
|
||||
*
|
||||
* look at code ...
|
||||
*/
|
||||
public static function structureContact($vcard) {
|
||||
$details = array();
|
||||
|
||||
foreach($vcard->children as $property) {
|
||||
$pname = $property->name;
|
||||
$temp = self::structureProperty($property);
|
||||
if(!is_null($temp)) {
|
||||
// Get Apple X-ABLabels
|
||||
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');
|
||||
}
|
||||
if($temp['label'] == '_$!<HomePage>!$_') {
|
||||
$temp['label'] = App::$l10n->t('HomePage');
|
||||
}
|
||||
}
|
||||
if(array_key_exists($pname, $details)) {
|
||||
$details[$pname][] = $temp;
|
||||
}
|
||||
else{
|
||||
$details[$pname] = array($temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $details;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Data structure of properties
|
||||
* @param object $property
|
||||
* @return associative array
|
||||
*
|
||||
* returns an associative array with
|
||||
* ['name'] name of property
|
||||
* ['value'] htmlspecialchars escaped value of property
|
||||
* ['parameters'] associative array name=>value
|
||||
* ['checksum'] checksum of whole property
|
||||
* NOTE: $value is not escaped anymore. It shouldn't make any difference
|
||||
* 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;
|
||||
if($property->name == 'ADR' || $property->name == 'N' || $property->name == 'ORG' || $property->name == 'CATEGORIES') {
|
||||
$value = $property->getParts();
|
||||
$value = array_map('trim', $value);
|
||||
}
|
||||
elseif($property->name == 'BDAY') {
|
||||
if(strpos($value, '-') === false) {
|
||||
if(strlen($value) >= 8) {
|
||||
$value = substr($value, 0, 4).'-'.substr($value, 4, 2).'-'.substr($value, 6, 2);
|
||||
} else {
|
||||
return null; // Badly malformed :-(
|
||||
}
|
||||
}
|
||||
} elseif($property->name == 'PHOTO') {
|
||||
$value = true;
|
||||
}
|
||||
elseif($property->name == 'IMPP') {
|
||||
if(strpos($value, ':') !== false) {
|
||||
$value = explode(':', $value);
|
||||
$protocol = array_shift($value);
|
||||
if(!isset($property['X-SERVICE-TYPE'])) {
|
||||
$property['X-SERVICE-TYPE'] = strtoupper(\OCP\Util::sanitizeHTML($protocol));
|
||||
}
|
||||
$value = implode('', $value);
|
||||
}
|
||||
}
|
||||
if(is_string($value)) {
|
||||
$value = strtr($value, array('\,' => ',', '\;' => ';'));
|
||||
}
|
||||
$temp = array(
|
||||
//'name' => $property->name,
|
||||
'value' => $value,
|
||||
'parameters' => array()
|
||||
);
|
||||
|
||||
// This cuts around a 3rd off of the json response size.
|
||||
if(in_array($property->name, App::$multi_properties)) {
|
||||
$temp['checksum'] = substr(md5($property->serialize()), 0, 8);
|
||||
}
|
||||
foreach($property->parameters as $parameter) {
|
||||
// Faulty entries by kaddressbook
|
||||
// Actually TYPE=PREF is correct according to RFC 2426
|
||||
// but this way is more handy in the UI. Tanghus.
|
||||
if($parameter->name == 'TYPE' && strtoupper($parameter->value) == 'PREF') {
|
||||
$parameter->name = 'PREF';
|
||||
$parameter->value = '1';
|
||||
}
|
||||
// NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters
|
||||
// like TYPE=HOME,CELL,VOICE. Tanghus.
|
||||
// TODO: Check if parameter is has commas and split + merge if so.
|
||||
if ($parameter->name == 'TYPE') {
|
||||
$pvalue = $parameter->value;
|
||||
if(is_string($pvalue) && strpos($pvalue, ',') !== false) {
|
||||
$pvalue = array_map('trim', explode(',', $pvalue));
|
||||
}
|
||||
$pvalue = is_array($pvalue) ? $pvalue : array($pvalue);
|
||||
if (isset($temp['parameters'][$parameter->name])) {
|
||||
$temp['parameters'][$parameter->name][] = \OCP\Util::sanitizeHTML($pvalue);
|
||||
}
|
||||
else {
|
||||
$temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($pvalue);
|
||||
}
|
||||
}
|
||||
else{
|
||||
$temp['parameters'][$parameter->name] = \OCP\Util::sanitizeHTML($parameter->value);
|
||||
}
|
||||
}
|
||||
return $temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move card(s) to an address book
|
||||
* @param integer $aid Address book id
|
||||
* @param $id Array or integer of cards to be moved.
|
||||
* @return boolean
|
||||
*
|
||||
*/
|
||||
public static function moveToAddressBook($aid, $id, $isAddressbook = false) {
|
||||
Addressbook::find($aid);
|
||||
$addressbook = Addressbook::find($aid);
|
||||
if ($addressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedAddressbook = \OCP\Share::getItemSharedWithBySource('addressbook', $aid);
|
||||
if (!$sharedAddressbook || !($sharedAddressbook['permissions'] & \OCP\PERMISSION_CREATE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(is_array($id)) {
|
||||
foreach ($id as $index => $cardId) {
|
||||
$card = self::find($cardId);
|
||||
if (!$card) {
|
||||
unset($id[$index]);
|
||||
}
|
||||
$oldAddressbook = Addressbook::find($card['addressbookid']);
|
||||
if ($oldAddressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedContact = \OCP\Share::getItemSharedWithBySource('contact', $cardId, \OCP\Share::FORMAT_NONE, null, true);
|
||||
if (!$sharedContact || !($sharedContact['permissions'] & \OCP\PERMISSION_DELETE)) {
|
||||
unset($id[$index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
$id_sql = join(',', array_fill(0, count($id), '?'));
|
||||
$prep = 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` IN ('.$id_sql.')';
|
||||
try {
|
||||
$stmt = \OCP\DB::prepare( $prep );
|
||||
//$aid = array($aid);
|
||||
$vals = array_merge((array)$aid, $id);
|
||||
$result = $stmt->execute($vals);
|
||||
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);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', ids: '.join(',', $vals), \OCP\Util::DEBUG);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.', SQL:'.$prep, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$stmt = null;
|
||||
if($isAddressbook) {
|
||||
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `addressbookid` = ?' );
|
||||
} else {
|
||||
$card = self::find($id);
|
||||
if (!$card) {
|
||||
return false;
|
||||
}
|
||||
$oldAddressbook = Addressbook::find($card['addressbookid']);
|
||||
if ($oldAddressbook['userid'] != \OCP\User::getUser()) {
|
||||
$sharedContact = \OCP\Share::getItemSharedWithBySource('contact', $id, \OCP\Share::FORMAT_NONE, null, true);
|
||||
if (!$sharedContact || !($sharedContact['permissions'] & \OCP\PERMISSION_DELETE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
$stmt = \OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` = ?' );
|
||||
}
|
||||
try {
|
||||
$result = $stmt->execute(array($aid, $id));
|
||||
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::DEBUG);
|
||||
\OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id, \OCP\Util::DEBUG);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
\OC_Hook::emit('\OCA\Contacts\VCard', 'post_moveToAddressbook', array('aid' => $aid, 'id' => $id));
|
||||
Addressbook::touch($aid);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
82
lib/vobject/stringproperty.php
Normal file
82
lib/vobject/stringproperty.php
Normal file
@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - VObject String Property
|
||||
*
|
||||
* This class adds escaping/unescaping of simple string properties.
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
*
|
||||
* 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\VObject;
|
||||
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This class overrides \Sabre\VObject\Property::serialize() properly
|
||||
* escape commas and semi-colons in string properties.
|
||||
*/
|
||||
class StringProperty extends VObject\Property {
|
||||
|
||||
/**
|
||||
* Turns the object back into a serialized blob.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function serialize() {
|
||||
|
||||
$str = $this->name;
|
||||
if ($this->group) {
|
||||
$str = $this->group . '.' . $this->name;
|
||||
}
|
||||
|
||||
foreach($this->parameters as $param) {
|
||||
$str.=';' . $param->serialize();
|
||||
}
|
||||
|
||||
$src = array(
|
||||
'\\',
|
||||
"\n",
|
||||
';',
|
||||
',',
|
||||
);
|
||||
$out = array(
|
||||
'\\\\',
|
||||
'\n',
|
||||
'\;',
|
||||
'\,',
|
||||
);
|
||||
$value = strtr($this->value, array('\,' => ',', '\;' => ';', '\\\\' => '\\'));
|
||||
$str.=':' . str_replace($src, $out, $value);
|
||||
|
||||
$out = '';
|
||||
while(strlen($str) > 0) {
|
||||
if (strlen($str) > 75) {
|
||||
$out .= mb_strcut($str, 0, 75, 'utf-8') . "\r\n";
|
||||
$str = ' ' . mb_strcut($str, 75, strlen($str), 'utf-8');
|
||||
} else {
|
||||
$out .= $str . "\r\n";
|
||||
$str = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $out;
|
||||
|
||||
}
|
||||
|
||||
}
|
229
lib/vobject/vcard.php
Normal file
229
lib/vobject/vcard.php
Normal file
@ -0,0 +1,229 @@
|
||||
<?php
|
||||
/**
|
||||
* ownCloud - VCard component
|
||||
*
|
||||
* This component represents the BEGIN:VCARD and END:VCARD found in every
|
||||
* vcard.
|
||||
*
|
||||
* @author Thomas Tanghus
|
||||
* @author Evert Pot (http://www.rooftopsolutions.nl/)
|
||||
*
|
||||
* 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\VObject;
|
||||
|
||||
use OCA\Contacts\Utils;
|
||||
use Sabre\VObject;
|
||||
|
||||
/**
|
||||
* This class overrides \Sabre\VObject\Component\VCard::validate() to be add
|
||||
* to import partially invalid vCards by ignoring invalid lines and to
|
||||
* validate and upgrade using ....
|
||||
*/
|
||||
class VCard extends VObject\Component\VCard {
|
||||
|
||||
/**
|
||||
* The following constants are used by the validate() method.
|
||||
*/
|
||||
const REPAIR = 1;
|
||||
const UPGRADE = 2;
|
||||
|
||||
/**
|
||||
* VCards with version 2.1, 3.0 and 4.0 are found.
|
||||
*
|
||||
* If the VCARD doesn't know its version, 3.0 is assumed and if
|
||||
* option UPGRADE is given it will be upgraded to version 3.0.
|
||||
*/
|
||||
const DEFAULT_VERSION = '3.0';
|
||||
|
||||
/**
|
||||
* @brief Format property TYPE parameters for upgrading from v. 2.1
|
||||
* @param $property Reference to a \Sabre\VObject\Property.
|
||||
* In version 2.1 e.g. a phone can be formatted like: TEL;HOME;CELL:123456789
|
||||
* This has to be changed to either TEL;TYPE=HOME,CELL:123456789 or TEL;TYPE=HOME;TYPE=CELL:123456789 - both are valid.
|
||||
*/
|
||||
protected function formatPropertyTypes(&$property) {
|
||||
foreach($property->parameters as $key=>&$parameter) {
|
||||
$types = Utils\Properties::getTypesForProperty($property->name);
|
||||
if(is_array($types) && in_array(strtoupper($parameter->name), array_keys($types))
|
||||
|| strtoupper($parameter->name) == 'PREF') {
|
||||
unset($property->parameters[$key]);
|
||||
$property->add('TYPE', $parameter->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Decode properties for upgrading from v. 2.1
|
||||
* @param $property Reference to a \Sabre\VObject\Property.
|
||||
* The only encoding allowed in version 3.0 is 'b' for binary. All encoded strings
|
||||
* must therefore be decoded and the parameters removed.
|
||||
*/
|
||||
protected function decodeProperty(&$property) {
|
||||
// Check out for encoded string and decode them :-[
|
||||
foreach($property->parameters as $key=>&$parameter) {
|
||||
if(strtoupper($parameter->name) == 'ENCODING') {
|
||||
if(strtoupper($parameter->value) == 'QUOTED-PRINTABLE') {
|
||||
// Decode quoted-printable and strip any control chars
|
||||
// except \n and \r
|
||||
$property->value = str_replace(
|
||||
"\r\n", "\n",
|
||||
VObject\StringUtil::convertToUTF8(
|
||||
quoted_printable_decode($property->value)
|
||||
)
|
||||
);
|
||||
unset($property->parameters[$key]);
|
||||
} else if(strtoupper($parameter->value) == 'BASE64') {
|
||||
$parameter->value = 'b';
|
||||
}
|
||||
} elseif(strtoupper($parameter->name) == 'CHARSET') {
|
||||
unset($property->parameters[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
$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::UPGRADE) {
|
||||
$this->VERSION = self::DEFAULT_VERSION;
|
||||
foreach($this->children as &$property) {
|
||||
$this->decodeProperty($property);
|
||||
$this->formatPropertyTypes($property);
|
||||
//\OCP\Util::writeLog('contacts', __METHOD__.' upgrade: '.$property->name, \OCP\Util::DEBUG);
|
||||
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']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
parent::validate($options),
|
||||
$warnings
|
||||
);
|
||||
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@ function getStandardImage() {
|
||||
}
|
||||
|
||||
$id = isset($_GET['id']) ? $_GET['id'] : null;
|
||||
$parent = isset($_GET['parent']) ? $_GET['parent'] : null;
|
||||
$backend = isset($_GET['backend']) ? $_GET['backend'] : null;
|
||||
$etag = null;
|
||||
$caching = null;
|
||||
$max_size = 170;
|
||||
@ -35,7 +37,8 @@ if(!extension_loaded('gd') || !function_exists('gd_info')) {
|
||||
getStandardImage();
|
||||
}
|
||||
|
||||
$contact = OCA\Contacts\App::getContactVCard($id);
|
||||
$app = new OCA\Contacts\App();
|
||||
$contact = $app->getContact($backend, $parent, $id);
|
||||
$image = new OC_Image();
|
||||
if (!$image || !$contact) {
|
||||
getStandardImage();
|
||||
@ -58,10 +61,10 @@ if (is_null($contact)) {
|
||||
$etag = md5($contact->LOGO);
|
||||
}
|
||||
if ($image->valid()) {
|
||||
$modified = OCA\Contacts\App::lastModified($contact);
|
||||
$modified = $contact->lastModified();
|
||||
// Force refresh if modified within the last minute.
|
||||
if(!is_null($modified)) {
|
||||
$caching = (time() - $modified->format('U') > 60) ? null : 0;
|
||||
$caching = (time() - $modified > 60) ? null : 0;
|
||||
}
|
||||
OCP\Response::enableCaching($caching);
|
||||
if(!is_null($modified)) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
<form class="float" id="file_upload_form" action="<?php print_unescaped(OCP\Util::linkTo('contacts', 'ajax/uploadphoto.php')); ?>" method="post" enctype="multipart/form-data" target="file_upload_target">
|
||||
<input type="hidden" name="id" value="">
|
||||
<input type="hidden" name="contactid" value="">
|
||||
<input type="hidden" name="addressbookid" value="">
|
||||
<input type="hidden" name="backend" value="">
|
||||
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>">
|
||||
<input type="hidden" name="MAX_FILE_SIZE" value="<?php p($_['uploadMaxFilesize']) ?>" id="max_upload">
|
||||
<input type="hidden" class="max_human_file_size" value="(max <?php p($_['uploadMaxHumanFilesize']); ?>)">
|
||||
@ -25,17 +27,30 @@
|
||||
<button class="addaddressbookcancel"><?php p($l->t('Cancel')); ?></button>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="addaddressbook icon-plus text"><?php p($l->t('New')); ?></button>
|
||||
<ul class="hidden">
|
||||
<li><input class="addaddressbookinput" type="text" placeholder="<?php p($l->t('Display name')); ?>" /></li>
|
||||
<li>
|
||||
<button class="addaddressbookok"><?php p($l->t('OK')); ?></button>
|
||||
<button class="addaddressbookcancel"><?php p($l->t('Cancel')); ?></button>
|
||||
</li>
|
||||
</ul>
|
||||
<h2 data-id="import" tabindex="0" role="button"><?php p($l->t('Import')); ?></h2>
|
||||
<ul class="hidden">
|
||||
<li class="import-upload">
|
||||
<form id="import_upload_form" action="<?php print_unescaped(OCP\Util::linkTo('contacts', 'ajax/uploadimport.php')); ?>" method="post" enctype="multipart/form-data" target="import_upload_target">
|
||||
<form
|
||||
id="import_upload_form"
|
||||
data-upload-id="1"
|
||||
action="<?php print_unescaped(OCP\Util::linkTo('contacts', 'ajax/uploadimport.php')); ?>"
|
||||
method="post" enctype="multipart/form-data"
|
||||
target="import_upload_target_1">
|
||||
<input type="hidden" name="MAX_FILE_SIZE" value="<?php p($_['uploadMaxFilesize']) ?>" id="max_upload">
|
||||
<label for="import_fileupload"><?php p($l->t('Select files to import')); ?>
|
||||
<label for="import_upload_start"><?php p($l->t('Select files to import')); ?>
|
||||
<button class="import-upload-button" title="<?php p($l->t('Select files')); ?>"></button>
|
||||
</label>
|
||||
<input id="import_fileupload" type="file" accept="text/vcard,text/x-vcard,text/directory" multiple="multiple" name="importfile" />
|
||||
<input id="import_upload_start" type="file" accept="text/vcard,text/x-vcard,text/directory" multiple="multiple" name="file" />
|
||||
</form>
|
||||
<iframe name="import_upload_target" id='import_upload_target' src=""></iframe>
|
||||
<!-- iframe name="import_upload_target" id='import_upload_target' src=""></iframe -->
|
||||
</li>
|
||||
<li class="import-select hidden"><label><?php p($l->t('Import into:')); ?></label></li>
|
||||
<li class="import-select hidden">
|
||||
@ -59,6 +74,7 @@
|
||||
<option value="-1" disabled="disabled" selected="selected"><?php p($l->t('Groups')); ?></option>
|
||||
</select>
|
||||
<button class="favorite action svg inactive control" title="<?php p($l->t('Favorite')); ?>"></button>
|
||||
<button class="merge"><?php p($l->t('Merge selected')); ?></button>
|
||||
<a class="delete action" title="<?php p($l->t('Delete Contact')); ?>"></a>
|
||||
</div>
|
||||
</div>
|
||||
@ -113,7 +129,9 @@
|
||||
enctype="multipart/form-data"
|
||||
target="crop_target"
|
||||
action="<?php print_unescaped(OCP\Util::linkToAbsolute('contacts', 'ajax/savecrop.php')); ?>">
|
||||
<input type="hidden" id="id" name="id" value="{id}" />
|
||||
<input type="hidden" id="contactid" name="contactid" value="{contactid}" />
|
||||
<input type="hidden" id="addressbookid" name="addressbookid" value="{addressbookid}" />
|
||||
<input type="hidden" id="backend" name="backend" value="{backend}" />
|
||||
<input type="hidden" id="tmpkey" name="tmpkey" value="{tmpkey}" />
|
||||
<fieldset id="coords">
|
||||
<input type="hidden" id="x1" name="x1" value="" />
|
||||
@ -134,10 +152,24 @@
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="mergeContactsTemplate" type="text/template">
|
||||
<div id="dialog-merge-contacts" title="<?php p($l->t('Merge contacts')); ?>">
|
||||
<p><?php p($l->t('Which contact should the data be merged into?')); ?></p>
|
||||
<fieldset>
|
||||
<ul class="mergelist">
|
||||
<li><input id="mergee_{idx}" type="radio" name="contact" value="{id}"><label for="mergee_{idx}" >{displayname}</label></li>
|
||||
</ul>
|
||||
</fieldset>
|
||||
<p>
|
||||
<input type="checkbox" id="delete_other" name="delete_other" />
|
||||
<label for="delete_other"><?php p($l->t('Delete the other(s) after successful merge?')); ?></label>
|
||||
</p>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script id="contactListItemTemplate" type="text/template">
|
||||
<tr class="contact" data-id="{id}">
|
||||
<td class="name"
|
||||
style="background: url('<?php print_unescaped(OC_Helper::linkToRemoteBase('contactthumbnail')); ?>?id={id}')">
|
||||
<td class="name thumbnail">
|
||||
<input type="checkbox" name="id" value="{id}" /><span class="nametext">{name}</span>
|
||||
</td>
|
||||
<td class="email">
|
||||
@ -151,8 +183,7 @@
|
||||
</script>
|
||||
|
||||
<script id="contactDragItemTemplate" type="text/template">
|
||||
<div class="dragContact" data-id="{id}"
|
||||
style="background: url('<?php print_unescaped(OC_Helper::linkToRemoteBase('contactthumbnail')); ?>?id={id}')">
|
||||
<div class="dragContact thumbnail" data-id="{id}">
|
||||
{name}
|
||||
</div>
|
||||
</script>
|
||||
@ -173,10 +204,10 @@
|
||||
<li>
|
||||
<div id="photowrapper" class="propertycontainer" data-element="photo">
|
||||
<ul id="phototools" class="transparent hidden">
|
||||
<li><a class="action delete" title="<?php p($l->t('Delete current photo')); ?>"></a></li>
|
||||
<li><a class="action edit" title="<?php p($l->t('Edit current photo')); ?>"></a></li>
|
||||
<li><a class="action upload" title="<?php p($l->t('Upload new photo')); ?>"></a></li>
|
||||
<li><a class="action cloud icon-cloud" title="<?php p($l->t('Select photo from ownCloud')); ?>"></a></li>
|
||||
<li><a class="action delete" title="<?php echo $l->t('Delete current photo'); ?>"></a></li>
|
||||
<li><a class="action edit" title="<?php echo $l->t('Edit current photo'); ?>"></a></li>
|
||||
<li><a class="action upload" title="<?php echo $l->t('Upload new photo'); ?>"></a></li>
|
||||
<li><a class="action cloud icon-cloud" title="<?php echo $l->t('Select photo from ownCloud'); ?>"></a></li>
|
||||
</ul>
|
||||
<a class="favorite action {favorite}"></a>
|
||||
</div>
|
||||
@ -207,6 +238,9 @@
|
||||
<div class="groupscontainer propertycontainer" data-element="categories">
|
||||
<select id="contactgroups" title="<?php p($l->t('Select groups')); ?>" name="value" multiple></select>
|
||||
</div>
|
||||
<div>
|
||||
<select class="hidden" id="contactaddressbooks" title="<?php p($l->t('Select address book')); ?>" name="value" multiple></select>
|
||||
</div>
|
||||
<dl class="form">
|
||||
<dt data-element="nickname">
|
||||
<?php p($l->t('Nickname')); ?>
|
||||
@ -289,7 +323,7 @@
|
||||
</script>
|
||||
|
||||
<script id="contactDetailsTemplate" class="hidden" type="text/template">
|
||||
<div class="email">
|
||||
<div class="email" type="text/template">
|
||||
<li data-element="email" data-checksum="{checksum}" class="propertycontainer">
|
||||
<span class="parameters">
|
||||
<select class="rtl type parameter" data-parameter="TYPE" name="parameters[TYPE][]">
|
||||
@ -304,7 +338,7 @@
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
<div class="tel">
|
||||
<div class="tel" type="text/template">
|
||||
<li data-element="tel" data-checksum="{checksum}" class="propertycontainer">
|
||||
<span class="parameters">
|
||||
<select class="rtl type parameter" data-parameter="TYPE" name="parameters[TYPE][]">
|
||||
@ -318,7 +352,7 @@
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
<div class="url">
|
||||
<div class="url" type="text/template">
|
||||
<li data-element="url" data-checksum="{checksum}" class="propertycontainer">
|
||||
<span class="parameters">
|
||||
<select class="rtl type parameter" data-parameter="TYPE" name="parameters[TYPE][]">
|
||||
@ -333,7 +367,7 @@
|
||||
</span>
|
||||
</li>
|
||||
</div>
|
||||
<div class="adr">
|
||||
<div class="adr" type="text/template">
|
||||
<li data-element="adr" data-checksum="{checksum}" data-lang="<?php p(OCP\Config::getUserValue(OCP\USER::getUser(), 'core', 'lang', 'en')); ?>" class="propertycontainer">
|
||||
<span class="float display">
|
||||
<label class="meta parameters"></label>
|
||||
@ -380,7 +414,7 @@
|
||||
</fieldset>
|
||||
</li>
|
||||
</div>
|
||||
<div class="impp">
|
||||
<div class="impp" type="text/template">
|
||||
<li data-element="impp" data-checksum="{checksum}" class="propertycontainer">
|
||||
<span class="parameters">
|
||||
<select class="type parameter" data-parameter="TYPE" name="parameters[TYPE][]">
|
||||
|
121
tests/contacts.php
Normal file
121
tests/contacts.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net)
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
OC_App::loadApp('contacts');
|
||||
class Test_Contacts_Backend_Datebase extends PHPUnit_Framework_TestCase {
|
||||
protected static $schema_file = 'static://test_db_scheme';
|
||||
protected static $test_prefix;
|
||||
protected static $backend;
|
||||
protected static $user;
|
||||
protected static $addressBooksTableName;
|
||||
protected static $cardsTableName;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
$dbfile = __DIR__.'/../appinfo/database.xml';
|
||||
|
||||
self::$test_prefix = '_'.OC_Util::generate_random_bytes('4').'_';
|
||||
$content = file_get_contents($dbfile);
|
||||
$content = str_replace( '*dbprefix*', '*dbprefix*'.self::$test_prefix, $content );
|
||||
file_put_contents( self::$schema_file, $content );
|
||||
OC_DB::createDbFromStructure(self::$schema_file);
|
||||
|
||||
self::$addressBooksTableName = '*PREFIX*'.self::$test_prefix.'contacts_addressbooks';
|
||||
self::$cardsTableName = '*PREFIX*'.self::$test_prefix.'contacts_cards';
|
||||
|
||||
OC_User::clearBackends();
|
||||
OC_User::useBackend('dummy');
|
||||
self::$user = uniqid('user_');
|
||||
OC_User::createUser(self::$user, 'pass');
|
||||
OC_User::setUserId(self::$user);
|
||||
|
||||
self::$backend = new OCA\Contacts\Backend\Database(
|
||||
self::$user,
|
||||
self::$addressBooksTableName,
|
||||
self::$cardsTableName
|
||||
);
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass() {
|
||||
OC_DB::removeDBStructure(self::$schema_file);
|
||||
unlink(self::$schema_file);
|
||||
}
|
||||
|
||||
public function testDatabaseBackend() {
|
||||
|
||||
$this->assertEquals(array(), self::$backend->getAddressBooksForUser());
|
||||
|
||||
$aid = self::$backend->createAddressBook(
|
||||
array(
|
||||
'displayname' => 'Contacts',
|
||||
'description' => 'My Contacts',
|
||||
)
|
||||
);
|
||||
|
||||
// Test address books
|
||||
$this->assertEquals(1, count(self::$backend->getAddressBooksForUser()));
|
||||
$this->assertTrue(self::$backend->hasAddressBook($aid));
|
||||
$addressBook = self::$backend->getAddressBook($aid);
|
||||
$this->assertEquals('Contacts', $addressBook['displayname']);
|
||||
$this->assertEquals('My Contacts', $addressBook['description']);
|
||||
self::$backend->updateAddressBook($aid, array('description' => 'All my contacts'));
|
||||
$addressBook = self::$backend->getAddressBook($aid);
|
||||
$this->assertEquals('All my contacts', $addressBook['description']);
|
||||
|
||||
// Test contacts
|
||||
$this->assertEquals(array(), self::$backend->getContacts($aid));
|
||||
|
||||
$carddata = file_get_contents(__DIR__ . '/data/test.vcf');
|
||||
$id = self::$backend->createContact($aid, $carddata);
|
||||
$this->assertNotEquals(false, $id); // Isn't there an assertNotFalse() ?
|
||||
$this->assertEquals(1, count(self::$backend->getContacts($aid)));
|
||||
$this->assertTrue(self::$backend->hasContact($aid, $id));
|
||||
$contact = self::$backend->getContact($aid, $id);
|
||||
$this->assertEquals('Max Mustermann', $contact['displayname']);
|
||||
$carddata = file_get_contents(__DIR__ . '/data/test2.vcf');
|
||||
$this->assertTrue(self::$backend->updateContact($aid, $id, $carddata));
|
||||
$contact = self::$backend->getContact($aid, $id);
|
||||
$this->assertEquals('John Q. Public', $contact['displayname']);
|
||||
$this->assertTrue(self::$backend->deleteContact($aid, $id));
|
||||
|
||||
$this->assertTrue(self::$backend->deleteAddressBook($aid));
|
||||
}
|
||||
|
||||
public function testAddressBook() {
|
||||
$addressBook = new OCA\Contacts\AddressBook(
|
||||
self::$backend,
|
||||
array(
|
||||
'displayname' => 'Contacts',
|
||||
'description' => 'My Contacts',
|
||||
)
|
||||
);
|
||||
$this->assertEquals(0, count($addressBook));
|
||||
$id = $addressBook->addChild(
|
||||
array(
|
||||
'displayname' => 'John Q. Public'
|
||||
)
|
||||
);
|
||||
$this->assertNotEquals(false, $id);
|
||||
$this->assertEquals(1, count($addressBook));
|
||||
$contact = $addressBook->getChild($id);
|
||||
$this->assertEquals('John Q. Public', (string)$contact->FN);
|
||||
$contact->FN = 'Max Mustermann';
|
||||
$contact->save();
|
||||
$contact = $addressBook[$id];
|
||||
$metadata = $contact->getMetaData();
|
||||
$this->assertEquals('Max Mustermann', $metadata['displayname']);
|
||||
|
||||
// Array access
|
||||
$this->assertEquals($contact, $addressBook[$id]);
|
||||
$this->assertTrue(isset($addressBook[$id]));
|
||||
|
||||
// Magic accessors
|
||||
//$this->assertEquals($contact, $addressBook->{$id});
|
||||
|
||||
$this->assertTrue($addressBook->deleteChild($id));
|
||||
$this->assertEquals(0, count($addressBook));
|
||||
}
|
||||
}
|
11
tests/data/test.vcf
Normal file
11
tests/data/test.vcf
Normal file
@ -0,0 +1,11 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:Mustermann;Max;;;
|
||||
FN:Max Mustermann
|
||||
ORG:ownCloud
|
||||
URL:http://owncloud.org/
|
||||
UID:e472ea61
|
||||
EMAIL;TYPE=INTERNET:max.mustermann@example.org
|
||||
TEL;TYPE=voice,work,pref:+49 1234 56788
|
||||
ADR;TYPE=intl,work,postal,parcel:;;Musterstraße 1;Musterstadt;;12345;Germany
|
||||
END:VCARD
|
11
tests/data/test2.vcf
Normal file
11
tests/data/test2.vcf
Normal file
@ -0,0 +1,11 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:Public;John;Quinlan;;
|
||||
FN:John Q. Public
|
||||
ORG:ownCloud
|
||||
URL:http://owncloud.org/
|
||||
UID:e472ea61
|
||||
EMAIL;TYPE=INTERNET:joe.public@example.org
|
||||
TEL;TYPE=voice,work,pref:+49 1234 56788
|
||||
ADR;TYPE=intl,work,postal,parcel:;;Elm Street 1;Metropolis;;12345;USA
|
||||
END:VCARD
|
91
tests/request.php
Normal file
91
tests/request.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (c) 2013 Thomas Tanghus (thomas@tanghus.net)
|
||||
* This file is licensed under the Affero General Public License version 3 or
|
||||
* later.
|
||||
* See the COPYING-README file.
|
||||
*/
|
||||
OC_App::loadApp('contacts');
|
||||
class Test_Contacts_Request extends PHPUnit_Framework_TestCase {
|
||||
protected static $user;
|
||||
|
||||
public static function setUpBeforeClass() {
|
||||
OC_User::clearBackends();
|
||||
OC_User::useBackend('dummy');
|
||||
self::$user = uniqid('user_');
|
||||
OC_User::createUser(self::$user, 'pass');
|
||||
OC_User::setUserId(self::$user);
|
||||
}
|
||||
|
||||
public function testRequestAccessors() {
|
||||
$vars = array(
|
||||
'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
|
||||
'urlParams' => array('user' => self::$user, 'trut' => 'trat'),
|
||||
);
|
||||
|
||||
$request = new OCA\Contacts\Request($vars);
|
||||
|
||||
$this->assertEquals(4, count($request));
|
||||
$this->assertEquals('Joey', $request['nickname']);
|
||||
$this->assertEquals('Joey', $request->{'nickname'});
|
||||
$this->assertEquals('Joey', $request->get['nickname']);
|
||||
$this->assertEquals('Joey', $request->getVar('get', 'nickname'));
|
||||
$this->assertTrue(isset($request['nickname']));
|
||||
$this->assertTrue(isset($request->{'nickname'}));
|
||||
$this->assertEquals(false, isset($request->{'flickname'}));
|
||||
$this->assertEquals(null, $request->{'flickname'});
|
||||
}
|
||||
|
||||
// urlParams has precedence over POST which has precedence over GET
|
||||
public function testPrecedence() {
|
||||
$vars = array(
|
||||
'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
|
||||
'post' => array('name' => 'Jane Doe', 'nickname' => 'Janey'),
|
||||
'urlParams' => array('user' => self::$user, 'name' => 'Johnny Weissmüller'),
|
||||
);
|
||||
|
||||
$request = new OCA\Contacts\Request($vars);
|
||||
|
||||
$this->assertEquals(3, count($request));
|
||||
$this->assertEquals('Janey', $request->{'nickname'});
|
||||
$this->assertEquals('Johnny Weissmüller', $request->{'name'});
|
||||
}
|
||||
|
||||
// Test default value
|
||||
public function testDefaultValue() {
|
||||
$vars = array(
|
||||
'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
|
||||
);
|
||||
|
||||
$request = new OCA\Contacts\Request($vars);
|
||||
|
||||
$this->assertEquals(2, count($request));
|
||||
$this->assertEquals('Joey', $request->getVar('get', 'nickname', 'Janey'));
|
||||
$this->assertEquals('Apocalypse Now', $request->getVar('get', 'flickname', 'Apocalypse Now'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testImmutableArrayAccess() {
|
||||
$vars = array(
|
||||
'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
|
||||
);
|
||||
|
||||
$request = new OCA\Contacts\Request($vars);
|
||||
$request['nickname'] = 'Janey';
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException RuntimeException
|
||||
*/
|
||||
public function testImmutableMagicAccess() {
|
||||
$vars = array(
|
||||
'get' => array('name' => 'John Q. Public', 'nickname' => 'Joey'),
|
||||
);
|
||||
|
||||
$request = new OCA\Contacts\Request($vars);
|
||||
$request->{'nickname'} = 'Janey';
|
||||
}
|
||||
|
||||
}
|
@ -27,30 +27,36 @@ session_write_close();
|
||||
//OCP\Util::writeLog('contacts', OCP\Util::getRequestUri(), OCP\Util::DEBUG);
|
||||
|
||||
function getStandardImage() {
|
||||
$image = new \OC_Image();
|
||||
$file = __DIR__ . DIRECTORY_SEPARATOR . 'img' . DIRECTORY_SEPARATOR . 'person.png';
|
||||
OCP\Response::setLastModifiedHeader(filemtime($file));
|
||||
OCP\Response::enableCaching();
|
||||
OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person.png'));
|
||||
$image->loadFromFile($file);
|
||||
$image();
|
||||
exit();
|
||||
}
|
||||
|
||||
if(!extension_loaded('gd') || !function_exists('gd_info')) {
|
||||
OCP\Util::writeLog('contacts',
|
||||
'thumbnail.php. GD module not installed', OCP\Util::DEBUG);
|
||||
getStandardImage();
|
||||
OCP\Response::enableCaching();
|
||||
OCP\Response::redirect(OCP\Util::imagePath('contacts', 'person.png'));
|
||||
exit();
|
||||
}
|
||||
|
||||
$id = $_GET['id'];
|
||||
$parent = $_GET['parent'];
|
||||
$backend = $_GET['backend'];
|
||||
$caching = null;
|
||||
|
||||
$image = OCA\Contacts\App::cacheThumbnail($id);
|
||||
$app = new OCA\Contacts\App();
|
||||
$contact = $app->getContact($backend, $parent, $id);
|
||||
$image = $contact->cacheThumbnail();
|
||||
if($image !== false) {
|
||||
try {
|
||||
$modified = OCA\Contacts\App::lastModified($id);
|
||||
} catch(Exception $e) {
|
||||
getStandardImage();
|
||||
}
|
||||
$modified = $contact->lastModified();
|
||||
// Force refresh if modified within the last minute.
|
||||
if(!is_null($modified)) {
|
||||
$caching = (time() - $modified->format('U') > 60) ? null : 0;
|
||||
$caching = (time() - $modified > 60) ? null : 0;
|
||||
OCP\Response::setLastModifiedHeader($modified);
|
||||
}
|
||||
OCP\Response::enableCaching($caching);
|
||||
|
Loading…
Reference in New Issue
Block a user