summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2015-12-02 16:17:58 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2015-12-02 16:17:58 +0100
commitdf5872ec50a68de5d99bd6b5cf17ceb94f2ef833 (patch)
treeeedcfa1459f9cf6ef67bec10787883875ed1fc3f
parentc35a450cb1c0df0f77dea4c69182a381543b679a (diff)
parent28ceab2f616d45a53dcc00c2cff53d5524bf0e9e (diff)
downloadnextcloud-server-df5872ec50a68de5d99bd6b5cf17ceb94f2ef833.tar.gz
nextcloud-server-df5872ec50a68de5d99bd6b5cf17ceb94f2ef833.zip
Merge pull request #20719 from owncloud/adding-system-addressbook-of-users
Adding system addressbook for users of this instance - a occ command …
-rw-r--r--apps/dav/appinfo/register_command.php3
-rw-r--r--apps/dav/command/syncsystemaddressbook.php107
-rw-r--r--apps/dav/lib/carddav/addressbook.php36
-rw-r--r--apps/dav/lib/carddav/addressbookroot.php12
-rw-r--r--apps/dav/lib/carddav/card.php39
-rw-r--r--apps/dav/lib/carddav/carddavbackend.php6
-rw-r--r--apps/dav/lib/carddav/converter.php158
-rw-r--r--apps/dav/lib/carddav/plugin.php47
-rw-r--r--apps/dav/lib/carddav/useraddressbooks.php36
-rw-r--r--apps/dav/lib/dav/systemprincipalbackend.php183
-rw-r--r--apps/dav/lib/rootcollection.php24
-rw-r--r--apps/dav/lib/server.php2
-rw-r--r--apps/dav/tests/travis/caldavtest/config/serverinfo.xml2
-rw-r--r--apps/dav/tests/travis/caldavtest/tests/CardDAV/sync-report.xml10
-rw-r--r--apps/dav/tests/unit/carddav/convertertest.php136
-rw-r--r--apps/files_sharing/lib/helper.php16
-rw-r--r--apps/files_sharing/settings-personal.php4
-rw-r--r--lib/private/avatar.php4
-rw-r--r--lib/private/user/manager.php34
-rw-r--r--lib/private/user/user.php100
-rw-r--r--lib/public/iuser.php17
-rw-r--r--lib/public/iusermanager.php7
-rw-r--r--tests/lib/contacts/localadressbook.php7
-rw-r--r--tests/lib/user/user.php24
24 files changed, 939 insertions, 75 deletions
diff --git a/apps/dav/appinfo/register_command.php b/apps/dav/appinfo/register_command.php
index 1f0df054110..af41036cddc 100644
--- a/apps/dav/appinfo/register_command.php
+++ b/apps/dav/appinfo/register_command.php
@@ -2,7 +2,9 @@
use OCA\DAV\Command\CreateAddressBook;
use OCA\DAV\Command\CreateCalendar;
+use OCA\DAV\Command\SyncSystemAddressBook;
+$config = \OC::$server->getConfig();
$dbConnection = \OC::$server->getDatabaseConnection();
$userManager = OC::$server->getUserManager();
$config = \OC::$server->getConfig();
@@ -10,3 +12,4 @@ $config = \OC::$server->getConfig();
/** @var Symfony\Component\Console\Application $application */
$application->add(new CreateAddressBook($userManager, $dbConnection, $config));
$application->add(new CreateCalendar($userManager, $dbConnection));
+$application->add(new SyncSystemAddressBook($userManager, $dbConnection, $config));
diff --git a/apps/dav/command/syncsystemaddressbook.php b/apps/dav/command/syncsystemaddressbook.php
new file mode 100644
index 00000000000..bb2896abc60
--- /dev/null
+++ b/apps/dav/command/syncsystemaddressbook.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace OCA\DAV\Command;
+
+use OCA\DAV\CardDAV\CardDavBackend;
+use OCA\DAV\CardDAV\Converter;
+use OCA\DAV\Connector\Sabre\Principal;
+use OCP\IConfig;
+use OCP\IDBConnection;
+use OCP\IUser;
+use OCP\IUserManager;
+use Sabre\CardDAV\Plugin;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\Property\Text;
+use Sabre\VObject\Reader;
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class SyncSystemAddressBook extends Command {
+
+ /** @var IUserManager */
+ protected $userManager;
+
+ /** @var \OCP\IDBConnection */
+ protected $dbConnection;
+
+ /** @var IConfig */
+ protected $config;
+
+ /** @var CardDavBackend */
+ private $backend;
+
+ /**
+ * @param IUserManager $userManager
+ * @param IDBConnection $dbConnection
+ * @param IConfig $config
+ */
+ function __construct(IUserManager $userManager, IDBConnection $dbConnection, IConfig $config) {
+ parent::__construct();
+ $this->userManager = $userManager;
+ $this->dbConnection = $dbConnection;
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ $this
+ ->setName('dav:sync-system-addressbook')
+ ->setDescription('Synchronizes users to the system addressbook');
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ */
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $principalBackend = new Principal(
+ $this->config,
+ $this->userManager
+ );
+
+ $this->backend = new CardDavBackend($this->dbConnection, $principalBackend);
+
+ // ensure system addressbook exists
+ $systemAddressBook = $this->ensureSystemAddressBookExists();
+ $converter = new Converter();
+
+ $output->writeln('Syncing users ...');
+ $progress = new ProgressBar($output);
+ $progress->start();
+ $this->userManager->callForAllUsers(function($user) use ($systemAddressBook, $converter, $progress) {
+ /** @var IUser $user */
+ $name = $user->getBackendClassName();
+ $userId = $user->getUID();
+
+ $cardId = "$name:$userId.vcf";
+ $card = $this->backend->getCard($systemAddressBook['id'], $cardId);
+ if ($card === false) {
+ $vCard = $converter->createCardFromUser($user);
+ $this->backend->createCard($systemAddressBook['id'], $cardId, $vCard->serialize());
+ } else {
+ $vCard = Reader::read($card['carddata']);
+ if ($converter->updateCard($vCard, $user)) {
+ $this->backend->updateCard($systemAddressBook['id'], $cardId, $vCard->serialize());
+ }
+ }
+ $progress->advance();
+ });
+ $progress->finish();
+ $output->writeln('');
+ }
+
+ protected function ensureSystemAddressBookExists() {
+ $book = $this->backend->getAddressBooksByUri('system');
+ if (!is_null($book)) {
+ return $book;
+ }
+ $systemPrincipal = "principals/system/system";
+ $this->backend->createAddressBook($systemPrincipal, 'system', [
+ '{' . Plugin::NS_CARDDAV . '}addressbook-description' => 'System addressbook which holds all users of this instance'
+ ]);
+
+ return $this->backend->getAddressBooksByUri('system');
+ }
+}
diff --git a/apps/dav/lib/carddav/addressbook.php b/apps/dav/lib/carddav/addressbook.php
index eff1ad321e5..507657e9682 100644
--- a/apps/dav/lib/carddav/addressbook.php
+++ b/apps/dav/lib/carddav/addressbook.php
@@ -3,6 +3,7 @@
namespace OCA\DAV\CardDAV;
use OCA\DAV\CardDAV\Sharing\IShareableAddressBook;
+use Sabre\DAV\Exception\NotFound;
class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook {
@@ -51,4 +52,39 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres
$carddavBackend = $this->carddavBackend;
$carddavBackend->getShares($this->getName());
}
+
+ function getACL() {
+ $acl = parent::getACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+ function getChildACL() {
+ $acl = parent::getChildACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+ function getChild($name) {
+ $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
+ if (!$obj) {
+ throw new NotFound('Card not found');
+ }
+ return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
+ }
+
}
diff --git a/apps/dav/lib/carddav/addressbookroot.php b/apps/dav/lib/carddav/addressbookroot.php
index ee99ac8d798..8c78d024556 100644
--- a/apps/dav/lib/carddav/addressbookroot.php
+++ b/apps/dav/lib/carddav/addressbookroot.php
@@ -20,4 +20,14 @@ class AddressBookRoot extends \Sabre\CardDAV\AddressBookRoot {
}
-} \ No newline at end of file
+ function getName() {
+
+ // Grabbing all the components of the principal path.
+ $parts = explode('/', $this->principalPrefix);
+
+ // We are only interested in the second part.
+ return $parts[1];
+
+ }
+
+}
diff --git a/apps/dav/lib/carddav/card.php b/apps/dav/lib/carddav/card.php
new file mode 100644
index 00000000000..cea0b1e41c7
--- /dev/null
+++ b/apps/dav/lib/carddav/card.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\CardDAV;
+
+class Card extends \Sabre\CardDAV\Card {
+
+ function getACL() {
+ $acl = parent::getACL();
+ if ($this->getOwner() === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
+}
diff --git a/apps/dav/lib/carddav/carddavbackend.php b/apps/dav/lib/carddav/carddavbackend.php
index daa31725fa1..29b056672b4 100644
--- a/apps/dav/lib/carddav/carddavbackend.php
+++ b/apps/dav/lib/carddav/carddavbackend.php
@@ -108,7 +108,7 @@ class CardDavBackend implements BackendInterface, SyncSupport {
return $addressBooks;
}
- private function getAddressBooksByUri($addressBookUri) {
+ public function getAddressBooksByUri($addressBookUri) {
$query = $this->db->getQueryBuilder();
$result = $query->select(['id', 'uri', 'displayname', 'principaluri', 'description', 'synctoken'])
->from('addressbooks')
@@ -117,10 +117,10 @@ class CardDavBackend implements BackendInterface, SyncSupport {
->execute();
$row = $result->fetch();
- if (is_null($row)) {
+ $result->closeCursor();
+ if ($row === false) {
return null;
}
- $result->closeCursor();
return [
'id' => $row['id'],
diff --git a/apps/dav/lib/carddav/converter.php b/apps/dav/lib/carddav/converter.php
new file mode 100644
index 00000000000..56b73eba4c0
--- /dev/null
+++ b/apps/dav/lib/carddav/converter.php
@@ -0,0 +1,158 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\CardDAV;
+
+use OCP\IImage;
+use OCP\IUser;
+use Sabre\VObject\Component\VCard;
+use Sabre\VObject\Property\Text;
+
+class Converter {
+
+ /**
+ * @param IUser $user
+ * @return VCard
+ */
+ public function createCardFromUser(IUser $user) {
+
+ $uid = $user->getUID();
+ $displayName = $user->getDisplayName();
+ $displayName = empty($displayName ) ? $uid : $displayName;
+ $emailAddress = $user->getEMailAddress();
+ $cloudId = $user->getCloudId();
+ $image = $user->getAvatarImage(-1);
+
+ $vCard = new VCard();
+ $vCard->add(new Text($vCard, 'UID', $uid));
+ if (!empty($displayName)) {
+ $vCard->add(new Text($vCard, 'FN', $displayName));
+ $vCard->add(new Text($vCard, 'N', $this->splitFullName($displayName)));
+ }
+ if (!empty($emailAddress)) {
+ $vCard->add(new Text($vCard, 'EMAIL', $emailAddress, ['TYPE' => 'OTHER']));
+ }
+ if (!empty($cloudId)) {
+ $vCard->add(new Text($vCard, 'CLOUD', $cloudId));
+ }
+ if ($image) {
+ $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
+ }
+ $vCard->validate();
+
+ return $vCard;
+ }
+
+ /**
+ * @param VCard $vCard
+ * @param IUser $user
+ * @return bool
+ */
+ public function updateCard(VCard $vCard, IUser $user) {
+ $uid = $user->getUID();
+ $displayName = $user->getDisplayName();
+ $displayName = empty($displayName ) ? $uid : $displayName;
+ $emailAddress = $user->getEMailAddress();
+ $cloudId = $user->getCloudId();
+ $image = $user->getAvatarImage(-1);
+
+ $updated = false;
+ if($this->propertyNeedsUpdate($vCard, 'FN', $displayName)) {
+ $vCard->FN = new Text($vCard, 'FN', $displayName);
+ unset($vCard->N);
+ $vCard->add(new Text($vCard, 'N', $this->splitFullName($displayName)));
+ $updated = true;
+ }
+ if($this->propertyNeedsUpdate($vCard, 'EMAIL', $emailAddress)) {
+ $vCard->EMAIL = new Text($vCard, 'EMAIL', $emailAddress);
+ $updated = true;
+ }
+ if($this->propertyNeedsUpdate($vCard, 'CLOUD', $cloudId)) {
+ $vCard->CLOUD = new Text($vCard, 'CLOUD', $cloudId);
+ $updated = true;
+ }
+
+ if($this->propertyNeedsUpdate($vCard, 'PHOTO', $image)) {
+ $vCard->add('PHOTO', $image->data(), ['ENCODING' => 'b', 'TYPE' => $image->mimeType()]);
+ $updated = true;
+ }
+
+ if (empty($emailAddress) && !is_null($vCard->EMAIL)) {
+ unset($vCard->EMAIL);
+ $updated = true;
+ }
+ if (empty($cloudId) && !is_null($vCard->CLOUD)) {
+ unset($vCard->CLOUD);
+ $updated = true;
+ }
+ if (empty($image) && !is_null($vCard->PHOTO)) {
+ unset($vCard->PHOTO);
+ $updated = true;
+ }
+
+ return $updated;
+ }
+
+ /**
+ * @param VCard $vCard
+ * @param string $name
+ * @param string|IImage $newValue
+ * @return bool
+ */
+ private function propertyNeedsUpdate(VCard $vCard, $name, $newValue) {
+ if (is_null($newValue)) {
+ return false;
+ }
+ $value = $vCard->__get($name);
+ if (!is_null($value)) {
+ $value = $value->getValue();
+ $newValue = $newValue instanceof IImage ? $newValue->data() : $newValue;
+
+ return $value !== $newValue;
+ }
+ return true;
+ }
+
+ /**
+ * @param string $fullName
+ * @return string[]
+ */
+ public function splitFullName($fullName) {
+ // Very basic western style parsing. I'm not gonna implement
+ // https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/NameSplitter.java ;)
+
+ $elements = explode(' ', $fullName);
+ $result = ['', '', '', '', ''];
+ if (count($elements) > 2) {
+ $result[0] = implode(' ', array_slice($elements, count($elements)-1));
+ $result[1] = $elements[0];
+ $result[2] = implode(' ', array_slice($elements, 1, count($elements)-2));
+ } elseif (count($elements) === 2) {
+ $result[0] = $elements[1];
+ $result[1] = $elements[0];
+ } else {
+ $result[0] = $elements[0];
+ }
+
+ return $result;
+ }
+
+}
diff --git a/apps/dav/lib/carddav/plugin.php b/apps/dav/lib/carddav/plugin.php
new file mode 100644
index 00000000000..f2b3dcbfda9
--- /dev/null
+++ b/apps/dav/lib/carddav/plugin.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\CardDAV;
+
+use Sabre\HTTP\URLUtil;
+
+class Plugin extends \Sabre\CardDAV\Plugin {
+
+ /**
+ * Returns the addressbook home for a given principal
+ *
+ * @param string $principal
+ * @return string
+ */
+ protected function getAddressbookHomeForPrincipal($principal) {
+
+ if (strrpos($principal, 'principals/users', -strlen($principal)) !== FALSE) {
+ list(, $principalId) = URLUtil::splitPath($principal);
+ return self::ADDRESSBOOK_ROOT . '/users/' . $principalId;
+ }
+ if (strrpos($principal, 'principals/system', -strlen($principal)) !== FALSE) {
+ list(, $principalId) = URLUtil::splitPath($principal);
+ return self::ADDRESSBOOK_ROOT . '/system/' . $principalId;
+ }
+
+ throw new \LogicException('This is not supposed to happen');
+ }
+}
diff --git a/apps/dav/lib/carddav/useraddressbooks.php b/apps/dav/lib/carddav/useraddressbooks.php
index 5f618a0ece3..093cee0e1b2 100644
--- a/apps/dav/lib/carddav/useraddressbooks.php
+++ b/apps/dav/lib/carddav/useraddressbooks.php
@@ -11,13 +11,39 @@ class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome {
*/
function getChildren() {
- $addressbooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
- $objs = [];
- foreach($addressbooks as $addressbook) {
- $objs[] = new AddressBook($this->carddavBackend, $addressbook);
+ $addressBooks = $this->carddavBackend->getAddressBooksForUser($this->principalUri);
+ $objects = [];
+ foreach($addressBooks as $addressBook) {
+ $objects[] = new AddressBook($this->carddavBackend, $addressBook);
}
- return $objs;
+ return $objects;
}
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ $acl = parent::getACL();
+ if ($this->principalUri === 'principals/system/system') {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ];
+ }
+
+ return $acl;
+ }
+
}
diff --git a/apps/dav/lib/dav/systemprincipalbackend.php b/apps/dav/lib/dav/systemprincipalbackend.php
new file mode 100644
index 00000000000..2c2049ace60
--- /dev/null
+++ b/apps/dav/lib/dav/systemprincipalbackend.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\DAV;
+
+use Sabre\DAVACL\PrincipalBackend\AbstractBackend;
+use Sabre\HTTP\URLUtil;
+
+class SystemPrincipalBackend extends AbstractBackend {
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ * {http://sabredav.org/ns}email-address - This is a custom SabreDAV
+ * field that's actually injected in a number of other properties. If
+ * you have an email address, use this property.
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === 'principals/system') {
+ $principals[] = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getPrincipalByPath($path) {
+
+ $elements = explode('/', $path);
+ if ($elements[0] !== 'principals') {
+ return null;
+ }
+ if ($elements[1] === 'system') {
+ $principal = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ return $principal;
+ }
+
+ return null;
+ }
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documentation for more info and examples.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ return [];
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMemberSet($principal) {
+ // TODO: for now the group principal has only one member, the user itself
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [$principal['uri']];
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMembership($principal) {
+ list($prefix, $name) = URLUtil::splitPath($principal);
+
+ if ($prefix === 'principals/system') {
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [];
+ }
+ return [];
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param array $members
+ * @return void
+ */
+ function setGroupMemberSet($principal, array $members) {
+ throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
+ }
+}
diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php
index 3e349fa31c9..c1635c9cde5 100644
--- a/apps/dav/lib/rootcollection.php
+++ b/apps/dav/lib/rootcollection.php
@@ -6,6 +6,7 @@ use OCA\DAV\CalDAV\CalDavBackend;
use OCA\DAV\CardDAV\AddressBookRoot;
use OCA\DAV\CardDAV\CardDavBackend;
use OCA\DAV\Connector\Sabre\Principal;
+use OCA\DAV\DAV\SystemPrincipalBackend;
use Sabre\CalDAV\CalendarRoot;
use Sabre\CalDAV\Principal\Collection;
use Sabre\DAV\SimpleCollection;
@@ -23,24 +24,33 @@ class RootCollection extends SimpleCollection {
$disableListing = !$config->getSystemValue('debug', false);
// setup the first level of the dav tree
- $principalCollection = new Collection($principalBackend, 'principals/users');
- $principalCollection->disableListing = $disableListing;
+ $userPrincipals = new Collection($principalBackend, 'principals/users');
+ $userPrincipals->disableListing = $disableListing;
+ $systemPrincipals = new Collection(new SystemPrincipalBackend(), 'principals/system');
+ $systemPrincipals->disableListing = $disableListing;
$filesCollection = new Files\RootCollection($principalBackend, 'principals/users');
$filesCollection->disableListing = $disableListing;
$caldavBackend = new CalDavBackend($db);
$calendarRoot = new CalendarRoot($principalBackend, $caldavBackend, 'principals/users');
$calendarRoot->disableListing = $disableListing;
- $cardDavBackend = new CardDavBackend(\OC::$server->getDatabaseConnection(), $principalBackend);
+ $usersCardDavBackend = new CardDavBackend($db, $principalBackend);
+ $usersAddressBookRoot = new AddressBookRoot($principalBackend, $usersCardDavBackend, 'principals/users');
+ $usersAddressBookRoot->disableListing = $disableListing;
- $addressBookRoot = new AddressBookRoot($principalBackend, $cardDavBackend, 'principals/users');
- $addressBookRoot->disableListing = $disableListing;
+ $systemCardDavBackend = new CardDavBackend($db, $principalBackend);
+ $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system');
+ $systemAddressBookRoot->disableListing = $disableListing;
$children = [
- new SimpleCollection('principals', [$principalCollection]),
+ new SimpleCollection('principals', [
+ $userPrincipals,
+ $systemPrincipals]),
$filesCollection,
$calendarRoot,
- $addressBookRoot,
+ new SimpleCollection('addressbooks', [
+ $usersAddressBookRoot,
+ $systemAddressBookRoot]),
];
parent::__construct('root', $children);
diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php
index ffdb917085e..a031f2c442b 100644
--- a/apps/dav/lib/server.php
+++ b/apps/dav/lib/server.php
@@ -58,7 +58,7 @@ class Server {
$this->server->addPlugin(new CardDAV\Sharing\Plugin($authBackend, \OC::$server->getRequest()));
// addressbook plugins
- $this->server->addPlugin(new \Sabre\CardDAV\Plugin());
+ $this->server->addPlugin(new \OCA\DAV\CardDAV\Plugin());
// Finder on OS X requires Class 2 WebDAV support (locking), since we do
// not provide locking we emulate it using a fake locking plugin.
diff --git a/apps/dav/tests/travis/caldavtest/config/serverinfo.xml b/apps/dav/tests/travis/caldavtest/config/serverinfo.xml
index a474bb7135c..c80e47f9481 100644
--- a/apps/dav/tests/travis/caldavtest/config/serverinfo.xml
+++ b/apps/dav/tests/travis/caldavtest/config/serverinfo.xml
@@ -569,7 +569,7 @@
<!-- relative path to user addressbook home-->
<substitution>
<key>$addressbookhome%d:</key>
- <value>$addressbooks:$userid%d:</value>
+ <value>$addressbooks:users/$userid%d:</value>
</substitution>
<!-- relative path to user addressbook-->
<substitution>
diff --git a/apps/dav/tests/travis/caldavtest/tests/CardDAV/sync-report.xml b/apps/dav/tests/travis/caldavtest/tests/CardDAV/sync-report.xml
index 0321e61edbc..ffa6662981c 100644
--- a/apps/dav/tests/travis/caldavtest/tests/CardDAV/sync-report.xml
+++ b/apps/dav/tests/travis/caldavtest/tests/CardDAV/sync-report.xml
@@ -181,8 +181,7 @@
</verify>
</request>
</test>
- <!--
- <test name='6'>
+ <!-- test name='6'>
<require-feature>
<feature>sync-report-home</feature>
</require-feature>
@@ -236,7 +235,7 @@
</arg>
</verify>
</request>
- </test>
+ </test -->
<test name='8'>
<description>remove new resource</description>
<request>
@@ -264,14 +263,15 @@
<callback>multistatusItems</callback>
<arg>
<name>okhrefs</name>
- <value>$calendar_sync_extra_items:</value>
+ <!-- no sync on addressbook level -->
+ <!--<value>$calendar_sync_extra_items:</value>-->
<value>1.vcf</value>
<value>2.vcf</value>
</arg>
</verify>
</request>
</test>
- <test name='10'>
+ <!--test name='10'>
<require-feature>
<feature>sync-report-home</feature>
</require-feature>
diff --git a/apps/dav/tests/unit/carddav/convertertest.php b/apps/dav/tests/unit/carddav/convertertest.php
new file mode 100644
index 00000000000..f4e2ea3f002
--- /dev/null
+++ b/apps/dav/tests/unit/carddav/convertertest.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Tests\Unit;
+
+use OCA\DAV\CardDAV\Converter;
+use Test\TestCase;
+
+class ConverterTests extends TestCase {
+
+ /**
+ * @dataProvider providesNewUsers
+ */
+ public function testCreation($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
+ $user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
+
+ $converter = new Converter();
+ $vCard = $converter->createCardFromUser($user);
+ $cardData = $vCard->serialize();
+
+ $this->assertEquals($expectedVCard, $cardData);
+ }
+
+ public function providesNewUsers() {
+ return [
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n"],
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nEMAIL;TYPE=OTHER:foo@bar.net\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar", "foo@bar.net"],
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:Dr. Foo Bar\r\nN:Bar;Dr.;Foo;;\r\nCLOUD:foo@bar.net\r\nPHOTO;ENCODING=b;TYPE=JPEG:MTIzNDU2Nzg5\r\nEND:VCARD\r\n", "Dr. Foo Bar", null, "foo@bar.net"],
+ ];
+ }
+
+ /**
+ * @dataProvider providesNewUsers
+ */
+ public function testUpdateOfUnchangedUser($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
+ $user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
+
+ $converter = new Converter();
+ $vCard = $converter->createCardFromUser($user);
+ $updated = $converter->updateCard($vCard, $user);
+ $this->assertFalse($updated);
+ $cardData = $vCard->serialize();
+
+ $this->assertEquals($expectedVCard, $cardData);
+ }
+
+ /**
+ * @dataProvider providesUsersForUpdateOfRemovedElement
+ */
+ public function testUpdateOfRemovedElement($expectedVCard, $displayName = null, $eMailAddress = null, $cloudId = null) {
+ $user = $this->getUserMock($displayName, $eMailAddress, $cloudId);
+
+ $converter = new Converter();
+ $vCard = $converter->createCardFromUser($user);
+
+ $user1 = $this->getMockBuilder('OCP\IUser')->disableOriginalConstructor()->getMock();
+ $user1->method('getUID')->willReturn('12345');
+ $user1->method('getDisplayName')->willReturn(null);
+ $user1->method('getEMailAddress')->willReturn(null);
+ $user1->method('getCloudId')->willReturn(null);
+ $user1->method('getAvatarImage')->willReturn(null);
+
+ $updated = $converter->updateCard($vCard, $user1);
+ $this->assertTrue($updated);
+ $cardData = $vCard->serialize();
+
+ $this->assertEquals($expectedVCard, $cardData);
+ }
+
+ public function providesUsersForUpdateOfRemovedElement() {
+ return [
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar"],
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar", "foo@bar.net"],
+ ["BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Sabre//Sabre VObject 3.4.7//EN\r\nUID:12345\r\nFN:12345\r\nN:12345;;;;\r\nEND:VCARD\r\n", "Dr. Foo Bar", null, "foo@bar.net"],
+ ];
+ }
+
+ /**
+ * @dataProvider providesNames
+ * @param $expected
+ * @param $fullName
+ */
+ public function testNameSplitter($expected, $fullName) {
+
+ $converter = new Converter();
+ $r = $converter->splitFullName($fullName);
+ $r = implode(';', $r);
+ $this->assertEquals($expected, $r);
+ }
+
+ public function providesNames() {
+ return [
+ ['Sauron;;;;', 'Sauron'],
+ ['Baggins;Bilbo;;;', 'Bilbo Baggins'],
+ ['Tolkien;John;Ronald Reuel;;', 'John Ronald Reuel Tolkien'],
+ ];
+ }
+
+ /**
+ * @param $displayName
+ * @param $eMailAddress
+ * @param $cloudId
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ protected function getUserMock($displayName, $eMailAddress, $cloudId) {
+ $image0 = $this->getMockBuilder('OCP\IImage')->disableOriginalConstructor()->getMock();
+ $image0->method('mimeType')->willReturn('JPEG');
+ $image0->method('data')->willReturn('123456789');
+ $user = $this->getMockBuilder('OCP\IUser')->disableOriginalConstructor()->getMock();
+ $user->method('getUID')->willReturn('12345');
+ $user->method('getDisplayName')->willReturn($displayName);
+ $user->method('getEMailAddress')->willReturn($eMailAddress);
+ $user->method('getCloudId')->willReturn($cloudId);
+ $user->method('getAvatarImage')->willReturn($image0);
+ return $user;
+ }
+}
diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php
index a804737c490..391b491e1ff 100644
--- a/apps/files_sharing/lib/helper.php
+++ b/apps/files_sharing/lib/helper.php
@@ -310,20 +310,4 @@ class Helper {
\OC::$server->getConfig()->setSystemValue('share_folder', $shareFolder);
}
- /**
- * remove protocol from URL
- *
- * @param string $url
- * @return string
- */
- public static function removeProtocolFromUrl($url) {
- if (strpos($url, 'https://') === 0) {
- return substr($url, strlen('https://'));
- } else if (strpos($url, 'http://') === 0) {
- return substr($url, strlen('http://'));
- }
-
- return $url;
- }
-
}
diff --git a/apps/files_sharing/settings-personal.php b/apps/files_sharing/settings-personal.php
index deaa7b92ac7..85fad9c3eaf 100644
--- a/apps/files_sharing/settings-personal.php
+++ b/apps/files_sharing/settings-personal.php
@@ -32,9 +32,7 @@ if (count($matches) > 0 && $matches[1] <= 9) {
$isIE8 = true;
}
-$uid = \OC::$server->getUserSession()->getUser()->getUID();
-$server = \OC::$server->getURLGenerator()->getAbsoluteURL('/');
-$cloudID = $uid . '@' . rtrim(\OCA\Files_Sharing\Helper::removeProtocolFromUrl($server), '/');
+$cloudID = \OC::$server->getUserSession()->getUser()->getCloudId();
$url = 'https://owncloud.org/federation#' . $cloudID;
$ownCloudLogoPath = \OC::$server->getURLGenerator()->imagePath('core', 'logo-icon.svg');
diff --git a/lib/private/avatar.php b/lib/private/avatar.php
index 872da35f947..37a813f3ff8 100644
--- a/lib/private/avatar.php
+++ b/lib/private/avatar.php
@@ -79,7 +79,9 @@ class Avatar implements \OCP\IAvatar {
/** @var File $node */
$node = $this->folder->get('avatar.' . $ext);
$avatar->loadFromData($node->getContent());
- $avatar->resize($size);
+ if ($size > 0) {
+ $avatar->resize($size);
+ }
$this->folder->newFile('avatar.' . $size . '.' . $ext)->putContent($avatar->data());
}
return $avatar;
diff --git a/lib/private/user/manager.php b/lib/private/user/manager.php
index 109f08f47a0..2cb866d0056 100644
--- a/lib/private/user/manager.php
+++ b/lib/private/user/manager.php
@@ -294,21 +294,47 @@ class Manager extends PublicEmitter implements IUserManager {
$userCountStatistics = array();
foreach ($this->backends as $backend) {
if ($backend->implementsActions(\OC_User_Backend::COUNT_USERS)) {
- $backendusers = $backend->countUsers();
- if($backendusers !== false) {
+ $backendUsers = $backend->countUsers();
+ if($backendUsers !== false) {
if($backend instanceof \OCP\IUserBackend) {
$name = $backend->getBackendName();
} else {
$name = get_class($backend);
}
if(isset($userCountStatistics[$name])) {
- $userCountStatistics[$name] += $backendusers;
+ $userCountStatistics[$name] += $backendUsers;
} else {
- $userCountStatistics[$name] = $backendusers;
+ $userCountStatistics[$name] = $backendUsers;
}
}
}
}
return $userCountStatistics;
}
+
+ /**
+ * The callback is executed for each user on each backend.
+ * If the callback returns false no further users will be retrieved.
+ *
+ * @param \Closure $callback
+ * @return void
+ * @since 9.0.0
+ */
+ public function callForAllUsers(\Closure $callback, $search = '') {
+ foreach($this->getBackends() as $backend) {
+ $limit = 500;
+ $offset = 0;
+ do {
+ $users = $backend->getUsers($search, $limit, $offset);
+ foreach ($users as $user) {
+ $user = $this->get($user);
+ $return = $callback($user);
+ if ($return === false) {
+ break;
+ }
+ }
+ $offset += $limit;
+ } while (count($users) >= $limit);
+ }
+ }
}
diff --git a/lib/private/user/user.php b/lib/private/user/user.php
index 2740b25d5d3..d1fa641504c 100644
--- a/lib/private/user/user.php
+++ b/lib/private/user/user.php
@@ -30,61 +30,56 @@
namespace OC\User;
use OC\Hooks\Emitter;
+use OCP\IAvatarManager;
+use OCP\IImage;
+use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IConfig;
class User implements IUser {
- /**
- * @var string $uid
- */
+ /** @var string $uid */
private $uid;
- /**
- * @var string $displayName
- */
+ /** @var string $displayName */
private $displayName;
- /**
- * @var \OC_User_Interface $backend
- */
+ /** @var \OC_User_Interface $backend */
private $backend;
- /**
- * @var bool $enabled
- */
+ /** @var bool $enabled */
private $enabled;
- /**
- * @var Emitter|Manager $emitter
- */
+ /** @var Emitter|Manager $emitter */
private $emitter;
- /**
- * @var string $home
- */
+ /** @var string $home */
private $home;
- /**
- * @var int $lastLogin
- */
+ /** @var int $lastLogin */
private $lastLogin;
- /**
- * @var \OCP\IConfig $config
- */
+ /** @var \OCP\IConfig $config */
private $config;
+ /** @var IAvatarManager */
+ private $avatarManager;
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
/**
* @param string $uid
* @param \OC_User_Interface $backend
* @param \OC\Hooks\Emitter $emitter
- * @param \OCP\IConfig $config
+ * @param IConfig|null $config
+ * @param IURLGenerator $urlGenerator
*/
- public function __construct($uid, $backend, $emitter = null, IConfig $config = null) {
+ public function __construct($uid, $backend, $emitter = null, IConfig $config = null, $urlGenerator = null) {
$this->uid = $uid;
$this->backend = $backend;
$this->emitter = $emitter;
$this->config = $config;
+ $this->urlGenerator = $urlGenerator;
if ($this->config) {
$enabled = $this->config->getUserValue($uid, 'core', 'enabled', 'true');
$this->enabled = ($enabled === 'true');
@@ -93,6 +88,9 @@ class User implements IUser {
$this->enabled = true;
$this->lastLogin = \OC::$server->getConfig()->getUserValue($uid, 'login', 'lastLogin', 0);
}
+ if (is_null($this->urlGenerator)) {
+ $this->urlGenerator = \OC::$server->getURLGenerator();
+ }
}
/**
@@ -105,7 +103,7 @@ class User implements IUser {
}
/**
- * get the displayname for the user, if no specific displayname is set it will fallback to the user id
+ * get the display name for the user, if no specific display name is set it will fallback to the user id
*
* @return string
*/
@@ -316,4 +314,52 @@ class User implements IUser {
public function getEMailAddress() {
return $this->config->getUserValue($this->uid, 'settings', 'email');
}
+
+ /**
+ * get the avatar image if it exists
+ *
+ * @param int $size
+ * @return IImage|null
+ * @since 9.0.0
+ */
+ public function getAvatarImage($size) {
+ // delay the initialization
+ if (is_null($this->avatarManager)) {
+ $this->avatarManager = \OC::$server->getAvatarManager();
+ }
+
+ $avatar = $this->avatarManager->getAvatar($this->uid);
+ $image = $avatar->get(-1);
+ if ($image) {
+ return $image;
+ }
+
+ return null;
+ }
+
+ /**
+ * get the federation cloud id
+ *
+ * @return string
+ * @since 9.0.0
+ */
+ public function getCloudId() {
+ $uid = $this->getUID();
+ $server = $this->urlGenerator->getAbsoluteURL('/');
+ return $uid . '@' . rtrim( $this->removeProtocolFromUrl($server), '/');
+ }
+
+ /**
+ * @param string $url
+ * @return string
+ */
+ private function removeProtocolFromUrl($url) {
+ if (strpos($url, 'https://') === 0) {
+ return substr($url, strlen('https://'));
+ } else if (strpos($url, 'http://') === 0) {
+ return substr($url, strlen('http://'));
+ }
+
+ return $url;
+ }
}
diff --git a/lib/public/iuser.php b/lib/public/iuser.php
index 1e52cd59036..67e2b107e0b 100644
--- a/lib/public/iuser.php
+++ b/lib/public/iuser.php
@@ -152,4 +152,21 @@ interface IUser {
* @since 9.0.0
*/
public function getEMailAddress();
+
+ /**
+ * get the avatar image if it exists
+ *
+ * @param int $size
+ * @return IImage|null
+ * @since 9.0.0
+ */
+ public function getAvatarImage($size);
+
+ /**
+ * get the federation cloud id
+ *
+ * @return string
+ * @since 9.0.0
+ */
+ public function getCloudId();
}
diff --git a/lib/public/iusermanager.php b/lib/public/iusermanager.php
index e3857d6231a..3a0abaca7b7 100644
--- a/lib/public/iusermanager.php
+++ b/lib/public/iusermanager.php
@@ -134,4 +134,11 @@ interface IUserManager {
* @since 8.0.0
*/
public function countUsers();
+
+ /**
+ * @param \Closure $callback
+ * @return void
+ * @since 9.0.0
+ */
+ public function callForAllUsers (\Closure $callback, $search = '');
}
diff --git a/tests/lib/contacts/localadressbook.php b/tests/lib/contacts/localadressbook.php
index 6bfcf3d890a..e5c43460835 100644
--- a/tests/lib/contacts/localadressbook.php
+++ b/tests/lib/contacts/localadressbook.php
@@ -98,6 +98,11 @@ class SimpleUserForTesting implements IUser {
}
public function getEMailAddress() {
- // TODO: Implement getEMailAddress() method.
+ }
+
+ public function getAvatarImage($size) {
+ }
+
+ public function getCloudId() {
}
}
diff --git a/tests/lib/user/user.php b/tests/lib/user/user.php
index bc1365d35bf..1f613edc4e6 100644
--- a/tests/lib/user/user.php
+++ b/tests/lib/user/user.php
@@ -11,6 +11,13 @@ namespace Test\User;
use OC\Hooks\PublicEmitter;
+/**
+ * Class User
+ *
+ * @group DB
+ *
+ * @package Test\User
+ */
class User extends \Test\TestCase {
public function testDisplayName() {
/**
@@ -454,4 +461,21 @@ class User extends \Test\TestCase {
$this->assertTrue($user->delete());
$this->assertEquals(2, $hooksCalled);
}
+
+ public function testGetCloudId() {
+ /**
+ * @var \OC_User_Backend | \PHPUnit_Framework_MockObject_MockObject $backend
+ */
+ $backend = $this->getMock('\Test\Util\User\Dummy');
+ $urlGenerator = $this->getMockBuilder('\OC\URLGenerator')
+ ->setMethods(['getAbsoluteURL'])
+ ->disableOriginalConstructor()->getMock();
+ $urlGenerator
+ ->expects($this->any())
+ ->method('getAbsoluteURL')
+ ->withAnyParameters()
+ ->willReturn('http://localhost:8888/owncloud');
+ $user = new \OC\User\User('foo', $backend, null, null, $urlGenerator);
+ $this->assertEquals("foo@localhost:8888/owncloud", $user->getCloudId());
+ }
}