diff options
author | mbi <knox@users.noreply.github.com> | 2015-12-30 10:34:42 +0100 |
---|---|---|
committer | mbi <knox@users.noreply.github.com> | 2015-12-30 10:34:42 +0100 |
commit | 63974992f9a0244df0bbff891956deec7d35904b (patch) | |
tree | a4152551e4a4366957e06572cf04e4e0e941f38f /apps | |
parent | 1aff941be6ea3c09e86e234b7e2dc77fe4aa3696 (diff) | |
parent | 45c41e2b2cefa97c9d8c3bc73cfc088839640d1d (diff) | |
download | nextcloud-server-63974992f9a0244df0bbff891956deec7d35904b.tar.gz nextcloud-server-63974992f9a0244df0bbff891956deec7d35904b.zip |
Merge branch 'master' into master
Diffstat (limited to 'apps')
255 files changed, 7138 insertions, 643 deletions
diff --git a/apps/dav/appinfo/app.php b/apps/dav/appinfo/app.php new file mode 100644 index 00000000000..950754ee941 --- /dev/null +++ b/apps/dav/appinfo/app.php @@ -0,0 +1,42 @@ +<?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/> + * + */ + +$cm = \OC::$server->getContactsManager(); +$cm->register(function() use ($cm) { + $db = \OC::$server->getDatabaseConnection(); + $userId = \OC::$server->getUserSession()->getUser()->getUID(); + $principal = new \OCA\DAV\Connector\Sabre\Principal( + \OC::$server->getConfig(), + \OC::$server->getUserManager() + ); + $cardDav = new \OCA\DAV\CardDAV\CardDavBackend($db, $principal, \OC::$server->getLogger()); + $addressBooks = $cardDav->getAddressBooksForUser("principals/$userId"); + foreach ($addressBooks as $addressBookInfo) { + $addressBook = new \OCA\DAV\CardDAV\AddressBook($cardDav, $addressBookInfo); + $cm->registerAddressBook( + new OCA\DAV\CardDAV\AddressBookImpl( + $addressBook, + $addressBookInfo, + $cardDav + ) + ); + } +}); diff --git a/apps/dav/appinfo/database.xml b/apps/dav/appinfo/database.xml index 48641c2be6f..50c8aa7d8ca 100644 --- a/apps/dav/appinfo/database.xml +++ b/apps/dav/appinfo/database.xml @@ -572,6 +572,78 @@ CREATE TABLE calendarobjects ( </table> <table> + <name>*dbprefix*cards_properties</name> + <declaration> + <field> + <name>id</name> + <type>integer</type> + <default>0</default> + <notnull>true</notnull> + <autoincrement>1</autoincrement> + <unsigned>true</unsigned> + <length>11</length> + </field> + <field> + <name>addressbookid</name> + <type>integer</type> + <default></default> + <notnull>true</notnull> + <length>11</length> + </field> + <field> + <name>cardid</name> + <type>integer</type> + <default></default> + <notnull>true</notnull> + <unsigned>true</unsigned> + <length>11</length> + </field> + <field> + <name>name</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>64</length> + </field> + <field> + <name>value</name> + <type>text</type> + <default></default> + <notnull>false</notnull> + <length>255</length> + </field> + <field> + <name>preferred</name> + <type>integer</type> + <default>1</default> + <notnull>true</notnull> + <length>4</length> + </field> + <index> + <name>card_contactid_index</name> + <field> + <name>cardid</name> + <sorting>ascending</sorting> + </field> + </index> + <index> + <name>card_name_index</name> + <field> + <name>name</name> + <sorting>ascending</sorting> + </field> + </index> + <index> + <name>card_value_index</name> + <field> + <name>value</name> + <sorting>ascending</sorting> + </field> + </index> + </declaration> + </table> + + <table> <name>*dbprefix*dav_shares</name> <declaration> <field> diff --git a/apps/dav/appinfo/register_command.php b/apps/dav/appinfo/register_command.php index af41036cddc..603832e0c48 100644 --- a/apps/dav/appinfo/register_command.php +++ b/apps/dav/appinfo/register_command.php @@ -8,8 +8,9 @@ $config = \OC::$server->getConfig(); $dbConnection = \OC::$server->getDatabaseConnection(); $userManager = OC::$server->getUserManager(); $config = \OC::$server->getConfig(); +$logger = \OC::$server->getLogger(); /** @var Symfony\Component\Console\Application $application */ -$application->add(new CreateAddressBook($userManager, $dbConnection, $config)); +$application->add(new CreateAddressBook($userManager, $dbConnection, $config, $logger)); $application->add(new CreateCalendar($userManager, $dbConnection)); $application->add(new SyncSystemAddressBook($userManager, $dbConnection, $config)); diff --git a/apps/dav/command/createaddressbook.php b/apps/dav/command/createaddressbook.php index ea89e7aa0a8..7b70cea7f80 100644 --- a/apps/dav/command/createaddressbook.php +++ b/apps/dav/command/createaddressbook.php @@ -6,6 +6,7 @@ use OCA\DAV\CardDAV\CardDavBackend; use OCA\DAV\Connector\Sabre\Principal; use OCP\IConfig; use OCP\IDBConnection; +use OCP\ILogger; use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -23,15 +24,25 @@ class CreateAddressBook extends Command { /** @var IConfig */ private $config; + /** @var ILogger */ + private $logger; + /** * @param IUserManager $userManager * @param IDBConnection $dbConnection + * @param IConfig $config + * @param ILogger $logger */ - function __construct(IUserManager $userManager, IDBConnection $dbConnection, IConfig $config) { + function __construct(IUserManager $userManager, + IDBConnection $dbConnection, + IConfig $config, + ILogger $logger + ) { parent::__construct(); $this->userManager = $userManager; $this->dbConnection = $dbConnection; $this->config = $config; + $this->logger = $logger; } protected function configure() { diff --git a/apps/dav/lib/carddav/addressbookimpl.php b/apps/dav/lib/carddav/addressbookimpl.php new file mode 100644 index 00000000000..838ef5aec65 --- /dev/null +++ b/apps/dav/lib/carddav/addressbookimpl.php @@ -0,0 +1,219 @@ +<?php +/** + * @author Thomas Müller <thomas.mueller@tmit.eu> + * @author Björn Schießle <schiessle@owncloud.com> + * + * @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\Constants; +use OCP\IAddressBook; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Property\Text; +use Sabre\VObject\Reader; +use Sabre\VObject\UUIDUtil; + +class AddressBookImpl implements IAddressBook { + + /** @var CardDavBackend */ + private $backend; + + /** @var array */ + private $addressBookInfo; + + /** @var AddressBook */ + private $addressBook; + + /** + * AddressBookImpl constructor. + * + * @param AddressBook $addressBook + * @param array $addressBookInfo + * @param CardDavBackend $backend + */ + public function __construct( + AddressBook $addressBook, + array $addressBookInfo, + CardDavBackend $backend) { + + $this->addressBook = $addressBook; + $this->addressBookInfo = $addressBookInfo; + $this->backend = $backend; + } + + /** + * @return string defining the technical unique key + * @since 5.0.0 + */ + public function getKey() { + return $this->addressBookInfo['id']; + } + + /** + * In comparison to getKey() this function returns a human readable (maybe translated) name + * + * @return mixed + * @since 5.0.0 + */ + public function getDisplayName() { + return $this->addressBookInfo['{DAV:}displayname']; + } + + /** + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - for future use. One should always have options! + * @return array an array of contacts which are arrays of key-value-pairs + * @since 5.0.0 + */ + public function search($pattern, $searchProperties, $options) { + $result = $this->backend->search($this->getKey(), $pattern, $searchProperties); + + $vCards = []; + foreach ($result as $cardData) { + $vCards[] = $this->vCard2Array($this->readCard($cardData)); + } + + return $vCards; + } + + /** + * @param array $properties this array if key-value-pairs defines a contact + * @return array an array representing the contact just created or updated + * @since 5.0.0 + */ + public function createOrUpdate($properties) { + $update = false; + if (!isset($properties['UID'])) { // create a new contact + $uid = $this->createUid(); + $uri = $uid . '.vcf'; + $vCard = $this->createEmptyVCard($uid); + } else { // update existing contact + $uid = $properties['UID']; + $uri = $uid . '.vcf'; + $vCardData = $this->backend->getCard($this->getKey(), $uri); + $vCard = $this->readCard($vCardData['carddata']); + $update = true; + } + + foreach ($properties as $key => $value) { + $vCard->$key = $vCard->createProperty($key, $value); + } + + if ($update) { + $this->backend->updateCard($this->getKey(), $uri, $vCard->serialize()); + } else { + $this->backend->createCard($this->getKey(), $uri, $vCard->serialize()); + } + + return $this->vCard2Array($vCard); + + } + + /** + * @return mixed + * @since 5.0.0 + */ + public function getPermissions() { + $permissions = $this->addressBook->getACL(); + $result = 0; + foreach ($permissions as $permission) { + switch($permission['privilege']) { + case '{DAV:}read': + $result |= Constants::PERMISSION_READ; + break; + case '{DAV:}write': + $result |= Constants::PERMISSION_CREATE; + $result |= Constants::PERMISSION_UPDATE; + break; + case '{DAV:}all': + $result |= Constants::PERMISSION_ALL; + break; + } + } + + return $result; + } + + /** + * @param object $id the unique identifier to a contact + * @return bool successful or not + * @since 5.0.0 + */ + public function delete($id) { + $uri = $this->backend->getCardUri($id); + return $this->backend->deleteCard($this->addressBookInfo['id'], $uri); + } + + /** + * read vCard data into a vCard object + * + * @param string $cardData + * @return VCard + */ + protected function readCard($cardData) { + return Reader::read($cardData); + } + + /** + * create UID for contact + * + * @return string + */ + protected function createUid() { + do { + $uid = $this->getUid(); + } while (!empty($this->backend->getContact($uid . '.vcf'))); + + return $uid; + } + + /** + * getUid is only there for testing, use createUid instead + */ + protected function getUid() { + return UUIDUtil::getUUID(); + } + + /** + * create empty vcard + * + * @param string $uid + * @return VCard + */ + protected function createEmptyVCard($uid) { + $vCard = new VCard(); + $vCard->add(new Text($vCard, 'UID', $uid)); + return $vCard; + } + + /** + * create array with all vCard properties + * + * @param VCard $vCard + * @return array + */ + protected function vCard2Array(VCard $vCard) { + $result = []; + foreach ($vCard->children as $property) { + $result[$property->name] = $property->getValue(); + } + return $result; + } +} diff --git a/apps/dav/lib/carddav/carddavbackend.php b/apps/dav/lib/carddav/carddavbackend.php index 29b056672b4..95175b20d1b 100644 --- a/apps/dav/lib/carddav/carddavbackend.php +++ b/apps/dav/lib/carddav/carddavbackend.php @@ -23,19 +23,48 @@ namespace OCA\DAV\CardDAV; use OCA\DAV\Connector\Sabre\Principal; +use OCP\IDBConnection; +use OCP\ILogger; use Sabre\CardDAV\Backend\BackendInterface; use Sabre\CardDAV\Backend\SyncSupport; use Sabre\CardDAV\Plugin; use Sabre\DAV\Exception\BadRequest; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Reader; class CardDavBackend implements BackendInterface, SyncSupport { /** @var Principal */ private $principalBackend; - public function __construct(\OCP\IDBConnection $db, Principal $principalBackend) { + /** @var ILogger */ + private $logger; + + /** @var string */ + private $dbCardsTable = 'cards'; + + /** @var string */ + private $dbCardsPropertiesTable = 'cards_properties'; + + /** @var IDBConnection */ + private $db; + + /** @var array properties to index */ + public static $indexProperties = array( + 'BDAY', 'UID', 'N', 'FN', 'TITLE', 'ROLE', 'NOTE', 'NICKNAME', + 'ORG', 'CATEGORIES', 'EMAIL', 'TEL', 'IMPP', 'ADR', 'URL', 'GEO', 'CLOUD'); + + /** + * CardDavBackend constructor. + * + * @param IDBConnection $db + * @param Principal $principalBackend + * @param ILogger $logger + */ + public function __construct(IDBConnection $db, Principal $principalBackend, ILogger $logger) { $this->db = $db; $this->principalBackend = $principalBackend; + $this->logger = $logger; } /** @@ -263,6 +292,11 @@ class CardDavBackend implements BackendInterface, SyncSupport { ->where($query->expr()->eq('resourceid', $query->createNamedParameter($addressBookId))) ->andWhere($query->expr()->eq('type', $query->createNamedParameter('addressbook'))) ->execute(); + + $query->delete($this->dbCardsPropertiesTable) + ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) + ->execute(); + } /** @@ -398,7 +432,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { $query = $this->db->getQueryBuilder(); $query->insert('cards') ->values([ - 'carddata' => $query->createNamedParameter($cardData), + 'carddata' => $query->createNamedParameter($cardData, \PDO::PARAM_LOB), 'uri' => $query->createNamedParameter($cardUri), 'lastmodified' => $query->createNamedParameter(time()), 'addressbookid' => $query->createNamedParameter($addressBookId), @@ -408,6 +442,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { ->execute(); $this->addChange($addressBookId, $cardUri, 1); + $this->updateProperties($addressBookId, $cardUri, $cardData); return '"' . $etag . '"'; } @@ -451,6 +486,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { ->execute(); $this->addChange($addressBookId, $cardUri, 2); + $this->updateProperties($addressBookId, $cardUri, $cardData); return '"' . $etag . '"'; } @@ -463,6 +499,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { * @return bool */ function deleteCard($addressBookId, $cardUri) { + $cardId = $this->getCardId($cardUri); $query = $this->db->getQueryBuilder(); $ret = $query->delete('cards') ->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))) @@ -471,7 +508,12 @@ class CardDavBackend implements BackendInterface, SyncSupport { $this->addChange($addressBookId, $cardUri, 3); - return $ret === 1; + if ($ret === 1) { + $this->purgeProperties($addressBookId, $cardId); + return true; + } + + return false; } /** @@ -638,6 +680,87 @@ class CardDavBackend implements BackendInterface, SyncSupport { } /** + * search contact + * + * @param int $addressBookId + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @return array an array of contacts which are arrays of key-value-pairs + */ + public function search($addressBookId, $pattern, $searchProperties) { + $query = $this->db->getQueryBuilder(); + $query2 = $this->db->getQueryBuilder(); + $query2->selectDistinct('cp.cardid')->from($this->dbCardsPropertiesTable, 'cp'); + foreach ($searchProperties as $property) { + $query2->orWhere( + $query2->expr()->andX( + $query2->expr()->eq('cp.name', $query->createNamedParameter($property)), + $query2->expr()->like('cp.value', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')) + ) + ); + } + $query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId))); + + $query->select('c.carddata')->from($this->dbCardsTable, 'c') + ->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL()))); + + $result = $query->execute(); + $cards = $result->fetchAll(); + + $result->closeCursor(); + + return array_map(function($array) {return $this->readBlob($array['carddata']);}, $cards); + + } + + /** + * get URI from a given contact + * + * @param int $id + * @return string + */ + public function getCardUri($id) { + $query = $this->db->getQueryBuilder(); + $query->select('uri')->from($this->dbCardsTable) + ->where($query->expr()->eq('id', $query->createParameter('id'))) + ->setParameter('id', $id); + + $result = $query->execute(); + $uri = $result->fetch(); + $result->closeCursor(); + + if (!isset($uri['uri'])) { + throw new \InvalidArgumentException('Card does not exists: ' . $id); + } + + return $uri['uri']; + } + + /** + * return contact with the given URI + * + * @param string $uri + * @returns array + */ + public function getContact($uri) { + $result = []; + $query = $this->db->getQueryBuilder(); + $query->select('*')->from($this->dbCardsTable) + ->where($query->expr()->eq('uri', $query->createParameter('uri'))) + ->setParameter('uri', $uri); + $queryResult = $query->execute(); + $contact = $queryResult->fetch(); + $queryResult->closeCursor(); + + if (is_array($contact)) { + $result = $contact; + } + + return $result; + } + + + /** * @param string $addressBookUri * @param string $element */ @@ -658,7 +781,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { } // remove the share if it already exists - $this->unshare($addressBookUri, $element); + $this->unshare($addressBookUri, $element['href']); $query = $this->db->getQueryBuilder(); $query->insert('dav_shares') @@ -677,8 +800,7 @@ class CardDavBackend implements BackendInterface, SyncSupport { * @param string $element */ private function unshare($addressBookUri, $element) { - $user = $element['href']; - $parts = explode(':', $user, 2); + $parts = explode(':', $element, 2); if ($parts[0] !== 'principal') { return; } @@ -734,4 +856,93 @@ class CardDavBackend implements BackendInterface, SyncSupport { return $shares; } + + /** + * update properties table + * + * @param int $addressBookId + * @param string $cardUri + * @param string $vCardSerialized + */ + protected function updateProperties($addressBookId, $cardUri, $vCardSerialized) { + $cardId = $this->getCardId($cardUri); + $vCard = $this->readCard($vCardSerialized); + + $this->purgeProperties($addressBookId, $cardId); + + $query = $this->db->getQueryBuilder(); + $query->insert($this->dbCardsPropertiesTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter($addressBookId), + 'cardid' => $query->createNamedParameter($cardId), + 'name' => $query->createParameter('name'), + 'value' => $query->createParameter('value'), + 'preferred' => $query->createParameter('preferred') + ] + ); + + foreach ($vCard->children as $property) { + if(!in_array($property->name, self::$indexProperties)) { + continue; + } + $preferred = 0; + foreach($property->parameters as $parameter) { + if ($parameter->name == 'TYPE' && strtoupper($parameter->getValue()) == 'PREF') { + $preferred = 1; + break; + } + } + $query->setParameter('name', $property->name); + $query->setParameter('value', substr($property->getValue(), 0, 254)); + $query->setParameter('preferred', $preferred); + $query->execute(); + } + } + + /** + * read vCard data into a vCard object + * + * @param string $cardData + * @return VCard + */ + protected function readCard($cardData) { + return Reader::read($cardData); + } + + /** + * delete all properties from a given card + * + * @param int $addressBookId + * @param int $cardId + */ + protected function purgeProperties($addressBookId, $cardId) { + $query = $this->db->getQueryBuilder(); + $query->delete($this->dbCardsPropertiesTable) + ->where($query->expr()->eq('cardid', $query->createNamedParameter($cardId))) + ->andWhere($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId))); + $query->execute(); + } + + /** + * get ID from a given contact + * + * @param string $uri + * @return int + */ + protected function getCardId($uri) { + $query = $this->db->getQueryBuilder(); + $query->select('id')->from($this->dbCardsTable) + ->where($query->expr()->eq('uri', $query->createNamedParameter($uri))); + + $result = $query->execute(); + $cardIds = $result->fetch(); + $result->closeCursor(); + + if (!isset($cardIds['id'])) { + throw new \InvalidArgumentException('Card does not exists: ' . $uri); + } + + return (int)$cardIds['id']; + } } diff --git a/apps/dav/lib/carddav/sharing/plugin.php b/apps/dav/lib/carddav/sharing/plugin.php index 99c6f8f912c..fd415b4566b 100644 --- a/apps/dav/lib/carddav/sharing/plugin.php +++ b/apps/dav/lib/carddav/sharing/plugin.php @@ -9,11 +9,24 @@ use Sabre\DAV\Exception\NotFound; use Sabre\DAV\Server; use Sabre\DAV\ServerPlugin; use Sabre\DAV\XMLUtil; +use Sabre\DAVACL\IACL; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; class Plugin extends ServerPlugin { + /** @var Auth */ + private $auth; + + /** @var IRequest */ + private $request; + + /** + * Plugin constructor. + * + * @param Auth $authBackEnd + * @param IRequest $request + */ public function __construct(Auth $authBackEnd, IRequest $request) { $this->auth = $authBackEnd; $this->request = $request; @@ -68,6 +81,7 @@ class Plugin extends ServerPlugin { function initialize(Server $server) { $this->server = $server; $server->resourceTypeMapping['OCA\\DAV\CardDAV\\ISharedAddressbook'] = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}shared'; + $this->server->xml->elementMap['{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}share'] = 'OCA\\DAV\\CardDAV\\Sharing\\Xml\\ShareRequest'; $this->server->on('method:POST', [$this, 'httpPost']); } @@ -109,9 +123,7 @@ class Plugin extends ServerPlugin { // re-populated the request body with the existing data. $request->setBody($requestBody); - $dom = XMLUtil::loadDOMDocument($requestBody); - - $documentType = XMLUtil::toClarkNotation($dom->firstChild); + $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType); switch ($documentType) { @@ -124,19 +136,18 @@ class Plugin extends ServerPlugin { return; } - $this->server->transactionType = 'post-calendar-share'; + $this->server->transactionType = 'post-oc-addressbook-share'; // Getting ACL info $acl = $this->server->getPlugin('acl'); // If there's no ACL support, we allow everything if ($acl) { + /** @var \Sabre\DAVACL\Plugin $acl */ $acl->checkPrivileges($path, '{DAV:}write'); } - $mutations = $this->parseShareRequest($dom); - - $node->updateShares($mutations[0], $mutations[1]); + $node->updateShares($message->set, $message->remove); $response->setStatus(200); // Adding this because sending a response body may cause issues, @@ -148,59 +159,6 @@ class Plugin extends ServerPlugin { } } - /** - * Parses the 'share' POST request. - * - * This method returns an array, containing two arrays. - * The first array is a list of new sharees. Every element is a struct - * containing a: - * * href element. (usually a mailto: address) - * * commonName element (often a first and lastname, but can also be - * false) - * * readOnly (true or false) - * * summary (A description of the share, can also be false) - * - * The second array is a list of sharees that are to be removed. This is - * just a simple array with 'hrefs'. - * - * @param \DOMDocument $dom - * @return array - */ - function parseShareRequest(\DOMDocument $dom) { - - $xpath = new \DOMXPath($dom); - $xpath->registerNamespace('cs', \Sabre\CardDAV\Plugin::NS_CARDDAV); - $xpath->registerNamespace('d', 'urn:DAV'); - - $set = []; - $elems = $xpath->query('cs:set'); - - for ($i = 0; $i < $elems->length; $i++) { - - $xset = $elems->item($i); - $set[] = [ - 'href' => $xpath->evaluate('string(d:href)', $xset), - 'commonName' => $xpath->evaluate('string(cs:common-name)', $xset), - 'summary' => $xpath->evaluate('string(cs:summary)', $xset), - 'readOnly' => $xpath->evaluate('boolean(cs:read)', $xset) !== false - ]; - - } - - $remove = []; - $elems = $xpath->query('cs:remove'); - - for ($i = 0; $i < $elems->length; $i++) { - - $xremove = $elems->item($i); - $remove[] = $xpath->evaluate('string(d:href)', $xremove); - - } - - return [$set, $remove]; - - } - private function protectAgainstCSRF() { $user = $this->auth->getCurrentUser(); if ($this->auth->isDavAuthenticated($user)) { diff --git a/apps/dav/lib/carddav/sharing/xml/sharerequest.php b/apps/dav/lib/carddav/sharing/xml/sharerequest.php new file mode 100644 index 00000000000..175c5ffc306 --- /dev/null +++ b/apps/dav/lib/carddav/sharing/xml/sharerequest.php @@ -0,0 +1,65 @@ +<?php + +namespace OCA\DAV\CardDAV\Sharing\Xml; + +use Sabre\Xml\Reader; +use Sabre\Xml\XmlDeserializable; + +class ShareRequest implements XmlDeserializable { + + public $set = []; + + public $remove = []; + + /** + * Constructor + * + * @param array $set + * @param array $remove + */ + function __construct(array $set, array $remove) { + + $this->set = $set; + $this->remove = $remove; + + } + + static function xmlDeserialize(Reader $reader) { + + $elems = $reader->parseInnerTree([ + '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV. '}set' => 'Sabre\\Xml\\Element\\KeyValue', + '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}remove' => 'Sabre\\Xml\\Element\\KeyValue', + ]); + + $set = []; + $remove = []; + + foreach ($elems as $elem) { + switch ($elem['name']) { + + case '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}set' : + $sharee = $elem['value']; + + $sumElem = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}summary'; + $commonName = '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}common-name'; + + $set[] = [ + 'href' => $sharee['{DAV:}href'], + 'commonName' => isset($sharee[$commonName]) ? $sharee[$commonName] : null, + 'summary' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null, + 'readOnly' => !array_key_exists('{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}read-write', $sharee), + ]; + break; + + case '{' . \Sabre\CardDAV\Plugin::NS_CARDDAV . '}remove' : + $remove[] = $elem['value']['{DAV:}href']; + break; + + } + } + + return new self($set, $remove); + + } + +} diff --git a/apps/dav/lib/connector/sabre/auth.php b/apps/dav/lib/connector/sabre/auth.php index 4f319770234..7f4f4a531b1 100644 --- a/apps/dav/lib/connector/sabre/auth.php +++ b/apps/dav/lib/connector/sabre/auth.php @@ -160,7 +160,7 @@ class Auth extends AbstractBasic { return [true, $this->principalPrefix . $user]; } - if (!$this->userSession->isLoggedIn() && $request->getHeader('X-Requested-With') === 'XMLHttpRequest') { + if (!$this->userSession->isLoggedIn() && in_array('XMLHttpRequest', explode(',', $request->getHeader('X-Requested-With')))) { // do not re-authenticate over ajax, use dummy auth name to prevent browser popup $response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"'); $response->setStatus(401); diff --git a/apps/dav/lib/connector/sabre/file.php b/apps/dav/lib/connector/sabre/file.php index c66f627c0a3..6a0a39d04e7 100644 --- a/apps/dav/lib/connector/sabre/file.php +++ b/apps/dav/lib/connector/sabre/file.php @@ -329,7 +329,7 @@ class File extends Node implements IFile { if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'PROPFIND') { return $mimeType; } - return \OC_Helper::getSecureMimeType($mimeType); + return \OC::$server->getMimeTypeDetector()->getSecureMimeType($mimeType); } /** diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index 1c78e9dc845..aa756281745 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -31,6 +31,7 @@ use \Sabre\DAV\PropFind; use \Sabre\DAV\PropPatch; use \Sabre\HTTP\RequestInterface; use \Sabre\HTTP\ResponseInterface; +use OCP\Files\StorageNotAvailableException; class FilesPlugin extends \Sabre\DAV\ServerPlugin { @@ -225,9 +226,13 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { if ($node instanceof \OCA\DAV\Connector\Sabre\File) { $propFind->handle(self::DOWNLOADURL_PROPERTYNAME, function() use ($node) { /** @var $node \OCA\DAV\Connector\Sabre\File */ - $directDownloadUrl = $node->getDirectDownload(); - if (isset($directDownloadUrl['url'])) { - return $directDownloadUrl['url']; + try { + $directDownloadUrl = $node->getDirectDownload(); + if (isset($directDownloadUrl['url'])) { + return $directDownloadUrl['url']; + } + } catch (StorageNotAvailableException $e) { + return false; } return false; }); diff --git a/apps/dav/lib/connector/sabre/objecttree.php b/apps/dav/lib/connector/sabre/objecttree.php index 2e9c1b9916c..809d202aea4 100644 --- a/apps/dav/lib/connector/sabre/objecttree.php +++ b/apps/dav/lib/connector/sabre/objecttree.php @@ -190,7 +190,7 @@ class ObjectTree extends \Sabre\DAV\Tree { $targetNodeExists = $this->nodeExists($destinationPath); $sourceNode = $this->getNodeForPath($sourcePath); if ($sourceNode instanceof \Sabre\DAV\ICollection && $targetNodeExists) { - throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode . ', target exists'); + throw new \Sabre\DAV\Exception\Forbidden('Could not copy directory ' . $sourceNode->getName() . ', target exists'); } list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($sourcePath); list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destinationPath); diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php index 9ee32822bbd..96cc2bbc46a 100644 --- a/apps/dav/lib/rootcollection.php +++ b/apps/dav/lib/rootcollection.php @@ -41,11 +41,11 @@ class RootCollection extends SimpleCollection { \OC::$server->getSystemTagObjectMapper() ); - $usersCardDavBackend = new CardDavBackend($db, $principalBackend); + $usersCardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getLogger()); $usersAddressBookRoot = new AddressBookRoot($principalBackend, $usersCardDavBackend, 'principals/users'); $usersAddressBookRoot->disableListing = $disableListing; - $systemCardDavBackend = new CardDavBackend($db, $principalBackend); + $systemCardDavBackend = new CardDavBackend($db, $principalBackend, \OC::$server->getLogger()); $systemAddressBookRoot = new AddressBookRoot(new SystemPrincipalBackend(), $systemCardDavBackend, 'principals/system'); $systemAddressBookRoot->disableListing = $disableListing; diff --git a/apps/dav/lib/systemtag/systemtagsbyidcollection.php b/apps/dav/lib/systemtag/systemtagsbyidcollection.php index e7b7b6d0acc..974d04efa5f 100644 --- a/apps/dav/lib/systemtag/systemtagsbyidcollection.php +++ b/apps/dav/lib/systemtag/systemtagsbyidcollection.php @@ -46,14 +46,25 @@ class SystemTagsByIdCollection implements ICollection { $this->tagManager = $tagManager; } + /** + * @param string $name + * @param resource|string $data Initial payload + * @throws Forbidden + */ function createFile($name, $data = null) { throw new Forbidden('Cannot create tags by id'); } + /** + * @param string $name + */ function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } + /** + * @param string $name + */ function getChild($name) { try { $tags = $this->tagManager->getTagsByIds([$name]); @@ -72,6 +83,9 @@ class SystemTagsByIdCollection implements ICollection { }, $tags); } + /** + * @param string $name + */ function childExists($name) { try { $this->tagManager->getTagsByIds([$name]); diff --git a/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php b/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php index e544073613f..2a28b9c83aa 100644 --- a/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php +++ b/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php @@ -61,14 +61,25 @@ class SystemTagsObjectTypeCollection implements ICollection { $this->objectType = $objectType; } + /** + * @param string $name + * @param resource|string $data Initial payload + * @throws Forbidden + */ function createFile($name, $data = null) { throw new Forbidden('Permission denied to create nodes'); } + /** + * @param string $name + */ function createDirectory($name) { throw new Forbidden('Permission denied to create collections'); } + /** + * @param string $objectId + */ function getChild($objectId) { return new SystemTagsObjectMappingCollection( $objectId, @@ -83,6 +94,9 @@ class SystemTagsObjectTypeCollection implements ICollection { throw new MethodNotAllowed(); } + /** + * @param string $name + */ function childExists($name) { return true; } @@ -95,6 +109,9 @@ class SystemTagsObjectTypeCollection implements ICollection { return $this->objectType; } + /** + * @param string $name + */ function setName($name) { throw new Forbidden('Permission denied to rename this collection'); } diff --git a/apps/dav/tests/travis/caldav/install.sh b/apps/dav/tests/travis/caldav/install.sh new file mode 100644 index 00000000000..e836e37f86f --- /dev/null +++ b/apps/dav/tests/travis/caldav/install.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + + +if [ ! -f CalDAVTester/run.py ]; then + cd "$SCRIPTPATH" + git clone https://github.com/DeepDiver1975/CalDAVTester.git + cd "$SCRIPTPATH/CalDAVTester" + python run.py -s + cd "$SCRIPTPATH" +fi + +# create test user +cd "$SCRIPTPATH/../../../../../" +OC_PASS=user01 php occ user:add --password-from-env user01 +php occ dav:create-calendar user01 calendar +OC_PASS=user02 php occ user:add --password-from-env user02 +php occ dav:create-calendar user02 calendar +cd "$SCRIPTPATH/../../../../../" diff --git a/apps/dav/tests/travis/caldav/script.sh b/apps/dav/tests/travis/caldav/script.sh new file mode 100644 index 00000000000..9a818b553f7 --- /dev/null +++ b/apps/dav/tests/travis/caldav/script.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + +# start the server +php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." & + +sleep 30 + +# run the tests +cd "$SCRIPTPATH/CalDAVTester" +PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/../caldavtest/config/serverinfo.xml" -o cdt.txt \ + "$SCRIPTPATH/../caldavtest/tests/CalDAV/current-user-principal.xml" +RESULT=$? + +tail "$/../../../../../data-autotest/owncloud.log" + +exit $RESULT diff --git a/apps/dav/tests/travis/caldavtest/tests/CalDAV/current-user-principal.xml b/apps/dav/tests/travis/caldavtest/tests/CalDAV/current-user-principal.xml new file mode 100644 index 00000000000..d01058fee0a --- /dev/null +++ b/apps/dav/tests/travis/caldavtest/tests/CalDAV/current-user-principal.xml @@ -0,0 +1,151 @@ +<?xml version="1.0" standalone="no"?> + +<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd"> + +<!-- + Copyright (c) 2006-2015 Apple Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<caldavtest> + <description>Test DAV:current-user-principal support</description> + + <require-feature> + <feature>caldav</feature> + <feature>current-user-principal</feature> + </require-feature> + + <start/> + + <test-suite name='Check for the property on /'> + <require-feature> + <feature>own-root</feature> + </require-feature> + <test name='1'> + <description>Check for authenticated property on /</description> + <request> + <method>PROPFIND</method> + <ruri>$root:</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/current-user-principal/1.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri1:</href>]]></value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <description>Check for authenticated property on / (user02)</description> + <request user="$userid2:" pswd="$pswd2:"> + <method>PROPFIND</method> + <ruri>$root:</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/current-user-principal/1.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri2:</href>]]></value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='Check for the property on /principals/'> + <test name='1'> + <description>Check for authenticated property on /</description> + <request> + <method>PROPFIND</method> + <ruri>$principalcollection:</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/current-user-principal/1.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri1:</href>]]></value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <description>Check for unauthenticated property on /</description> + <request auth="no"> + <method>PROPFIND</method> + <ruri>$principals_users:</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/current-user-principal/1.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + <arg> + <name>status</name> + <value>401</value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <description>Check for authenticated property on / (user02)</description> + <request user="$userid2:" pswd="$pswd2:"> + <method>PROPFIND</method> + <ruri>$principalcollection:</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/current-user-principal/1.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value><![CDATA[{DAV:}current-user-principal$<href xmlns="DAV:">$principaluri2:</href>]]></value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <end/> +</caldavtest> diff --git a/apps/dav/tests/travis/caldavtest/tests/CalDAV/sync-report.xml b/apps/dav/tests/travis/caldavtest/tests/CalDAV/sync-report.xml new file mode 100644 index 00000000000..c675af82065 --- /dev/null +++ b/apps/dav/tests/travis/caldavtest/tests/CalDAV/sync-report.xml @@ -0,0 +1,3512 @@ +<?xml version="1.0" standalone="no"?> + +<!DOCTYPE caldavtest SYSTEM "caldavtest.dtd"> + +<!-- + Copyright (c) 2006-2015 Apple Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<caldavtest> + <require-feature> + <feature>caldav</feature> + <feature>sync-report</feature> + </require-feature> + + <start> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/1.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/2.txt</filepath> + </data> + </request> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/4.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/5.txt</filepath> + </data> + </request> + </start> + + <test-suite name='support-report-set/sync-token property'> + <test name='1'> + <description>Not on calendars</description> + <request> + <method>PROPFIND</method> + <ruri>$calendars:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/1.xml</filepath> + </data> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>notexists</name> + <value>$verify-property-prefix:/{DAV:}supported-report-set/{DAV:}supported-report/{DAV:}report/{DAV:}sync-collection</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}supported-report-set</value> + </arg> + <arg> + <name>badprops</name> + <value>{DAV:}sync-token</value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <description>On calendar-home</description> + <request> + <method>PROPFIND</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/1.xml</filepath> + </data> + <verify> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>$verify-property-prefix:/{DAV:}supported-report-set/{DAV:}supported-report/{DAV:}report/{DAV:}sync-collection</value> + <value>$verify-property-prefix:/{DAV:}sync-token[+data:,]</value> + </arg> + </verify> + <verify> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}supported-report-set</value> + <value>{DAV:}sync-token</value> + </arg> + </verify> + <verify> + <exclude-feature> + <feature>sync-report-home</feature> + </exclude-feature> + <callback>xmlElementMatch</callback> + <arg> + <name>notexists</name> + <value>$verify-property-prefix:/{DAV:}supported-report-set/{DAV:}supported-report/{DAV:}report/{DAV:}sync-collection</value> + </arg> + </verify> + <verify> + <exclude-feature> + <feature>sync-report-home</feature> + </exclude-feature> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}supported-report-set</value> + </arg> + <arg> + <name>badprops</name> + <value>{DAV:}sync-token</value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <description>On calendar</description> + <request> + <method>PROPFIND</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/1.xml</filepath> + </data> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>$verify-property-prefix:/{DAV:}supported-report-set/{DAV:}supported-report/{DAV:}report/{DAV:}sync-collection</value> + <value>$verify-property-prefix:/{DAV:}sync-token[+data:,]</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}supported-report-set</value> + <value>{DAV:}sync-token</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>On inbox</description> + <request> + <method>PROPFIND</method> + <ruri>$inboxpath1:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/1.xml</filepath> + </data> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>$verify-property-prefix:/{DAV:}supported-report-set/{DAV:}supported-report/{DAV:}report/{DAV:}sync-collection</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}supported-report-set</value> + <value>{DAV:}sync-token</value> + </arg> + </verify> + </request> + </test> + <test name='5'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>Look for options header tag on principal</description> + <request> + <method>OPTIONS</method> + <ruri>$principal1:</ruri> + <verify> + <callback>header</callback> + <arg> + <name>header</name> + <value>*DAV$.*calendarserver-home-sync[^-]*</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - sync-level'> + <test name='1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:1, depth:0</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:1, depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:1, depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:0</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='5'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='6'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='7'> + <description>sync-level:1, depth:0</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='8'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:1, depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='9'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:1, depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='10'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:0</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='11'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='12'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>sync-level:infinity, depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/9.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='13'> + <description>Bad sync-level</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/10.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + <arg> + <name>status</name> + <value>400</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - empty token - no props'> + <test name='1'> + <description>initial query - calendar collection depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>initial query - home depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>initial query - home depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>add new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/3.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + </test> + <test name='5'> + <description>new resource - calendar collection depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + <value>3.ics</value> + </arg> + </verify> + </request> + </test> + <test name='6'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>new resource - home depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='7'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>new resource - home depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar1/3.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='8'> + <description>remove new resource</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar1/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + </test> + <test name='9'> + <description>remove new resource - calendar collection depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='10'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>remove new resource - home depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='11'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>remove new resource - home depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='12'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/1.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + </test> + <test name='13'> + <description>changed resource - calendar collection depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + </request> + </test> + <test name='14'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>changed resource - home depth:1</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar2/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + <test name='15'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <description>changed resource - home depth:infinity</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar1/</value> + <value>synccalendar1/1.ics</value> + <value>synccalendar1/2.ics</value> + <value>synccalendar2/</value> + <value>synccalendar2/1.ics</value> + <value>synccalendar2/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - no props - calendar depth:1'> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/3.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>3.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar1/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>3.ics</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/4.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>3.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='5'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar1/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/1.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>1.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - empty token - props'> + <test name='1'> + <description>initial query</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/6.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + <value>3.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + </request> + </test> + <test name='3'> + <description>remove resource new resource</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar2/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/4.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - props'> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + <value>1.ics</value> + <value>2.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/6.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>3.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar2/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>3.ics</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/7.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>3.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='5'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar2/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/4.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>1.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - no props - home depth:infinity'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>Initialize</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + </request> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + </request> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/7.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/8.txt</filepath> + </data> + </request> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar4/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/10.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/11.txt</filepath> + </data> + </request> + </test> + <test name='2'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar3/1.ics</value> + <value>synccalendar3/2.ics</value> + <value>synccalendar4/</value> + <value>synccalendar4/1.ics</value> + <value>synccalendar4/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/9.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + <value>synccalendar3/3.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='4'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>synccalendar3/3.ics</value> + </arg> + </verify> + </request> + </test> + <test name='5'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/4.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>synccalendar3/3.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/7.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + <value>synccalendar3/1.ics</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='7'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - props - home depth:infinity'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/5.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar3/1.ics</value> + <value>synccalendar3/2.ics</value> + <value>synccalendar4/</value> + <value>synccalendar4/1.ics</value> + <value>synccalendar4/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>ignore</name> + <value>$calendarhome1:/$outbox:/</value> + <value>$calendarhome1:/$freebusy:</value> + <value>$calendarhome1:/$notification:/</value> + <value>$calendarhome1:/$dropbox:/</value> + </arg> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/12.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + <value>synccalendar4/3.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar4/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>synccalendar4/3.ics</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/7.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>synccalendar4/3.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>count</name> + <value>2</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='5'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/10.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + <value>synccalendar4/1.ics</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/6.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - delete/create calendar - home depth:infinity'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar3/1.ics</value> + <value>synccalendar3/2.ics</value> + <value>synccalendar4/</value> + <value>synccalendar4/1.ics</value> + <value>synccalendar4/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>remove resource then calendar</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>add calendar - test last sync</description> + <request> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/4.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>add calendar - test previous sync</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - no props - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>Initialize</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar1/</ruri> + </request> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar2/</ruri> + </request> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/7.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/8.txt</filepath> + </data> + </request> + <request end-delete="yes"> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar4/</ruri> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/10.txt</filepath> + </data> + </request> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/2.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/11.txt</filepath> + </data> + </request> + </test> + <test name='2'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/9.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='4'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + </request> + </test> + <test name='5'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/13.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/7.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='7'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - props - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/14.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>ignore</name> + <value>$calendarhome1:/$outbox:/</value> + <value>$calendarhome1:/$freebusy:</value> + <value>$calendarhome1:/$notification:/</value> + <value>$calendarhome1:/$dropbox:/</value> + </arg> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>new resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/3.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/12.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/15.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>remove resource (treated as new)</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar4/3.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/15.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>remove resource (treated as old)</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/16.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>count</name> + <value>1</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='5'> + <description>changed resource</description> + <request> + <method>PUT</method> + <ruri>$calendarhome1:/synccalendar4/1.ics</ruri> + <data> + <content-type>text/calendar; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/put/10.txt</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/15.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar4/</value> + </arg> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='6'> + <description>no change</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/15.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + </verify> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{DAV:}getcontenttype</value> + <value>{DAV:}getetag</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - diff token - delete/create calendar - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>remove resource then calendar</description> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/1.ics</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>DELETE</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>badhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken2:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>add calendar - test last sync</description> + <request> + <method>MKCALENDAR</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/13.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + </request> + </test> + <test name='4'> + <description>add calendar - test previous sync</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - empty inbox'> + <test name='1'> + <description>initial query</description> + <request> + <method>REPORT</method> + <ruri>$inboxpath1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_sync_extra_items:</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='simple reports - valid token'> + <test name='1'> + <description>initial query</description> + <request> + <method>REPORT</method> + <ruri>$calendarpath1:/</ruri> + <header> + <name>Depth</name> + <value>1</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/11.xml</filepath> + </data> + <verify> + <callback>prepostcondition</callback> + <arg> + <name>error</name> + <value>{DAV:}valid-sync-token</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <test-suite name='calendar webdav property change - home depth:infinity'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/2.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + <value>synccalendar4/1.ics</value> + <value>synccalendar4/2.ics</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>Change a property</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/17.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>Remove a property</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/18.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/3.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + <test-suite name='calendar webdav property change - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>Change a property</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/17.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>Remove a property</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarhome1:/synccalendar3/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/18.xml</filepath> + </data> + <verify> + <callback>statusCode</callback> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>synccalendar3/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + + <test-suite name='default calendar property change - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/8.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>Change property on Inbox</description> + <request> + <method>PROPPATCH</method> + <ruri>$inboxpath1:/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/19.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL</value> + </arg> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$inbox:/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>Reset the property</description> + <request> + <method>PROPPATCH</method> + <ruri>$inboxpath1:/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/20.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL</value> + </arg> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/12.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$inbox:/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + + <test-suite name='schedule-calendar-transp in response - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - grab token</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/22.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar_home_items_initial_sync:</value> + <value>synccalendar3/</value> + <value>synccalendar4/</value> + </arg> + <arg> + <name>badhrefs</name> + <value>$dropbox:/</value> + </arg> + </verify> + <verify> + <callback>dataString</callback> + <arg> + <name>contains</name> + <value>opaque</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='2'> + <description>Change property on calendar</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarpath1:/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/Common/PROPPATCH/calendar-transp-transparent.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp</value> + </arg> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/23.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar:/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <verify> + <callback>dataString</callback> + <arg> + <name>contains</name> + <value>transparent</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + <test name='3'> + <description>Reset the property</description> + <request> + <method>PROPPATCH</method> + <ruri>$calendarpath1:/</ruri> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/Common/PROPPATCH/calendar-transp-opaque.xml</filepath> + </data> + <verify> + <callback>propfindItems</callback> + <arg> + <name>okprops</name> + <value>{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp</value> + </arg> + </verify> + </request> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/23.xml</filepath> + </data> + <verify> + <callback>multistatusItems</callback> + <arg> + <name>okhrefs</name> + <value>$calendar:/</value> + </arg> + </verify> + <verify> + <callback>xmlElementMatch</callback> + <arg> + <name>exists</name> + <value>/{DAV:}multistatus/{DAV:}sync-token[!$synctoken1:]</value> + </arg> + </verify> + <verify> + <callback>dataString</callback> + <arg> + <name>contains</name> + <value>opaque</value> + </arg> + </verify> + <grabelement> + <name>/{DAV:}multistatus/{DAV:}sync-token</name> + <variable>$synctoken1:</variable> + </grabelement> + </request> + </test> + </test-suite> + + + + <test-suite name='Prefer:return=minimal - home depth:1'> + <require-feature> + <feature>sync-report-home</feature> + </require-feature> + <test name='1'> + <description>initial query - no minimal</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/24.xml</filepath> + </data> + <verify> + <callback>dataString</callback> + <arg> + <name>contains</name> + <value>foobar</value> + </arg> + </verify> + </request> + </test> + <test name='2'> + <description>initial query - with minimal</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>infinity</value> + </header> + <header> + <name>Prefer</name> + <value>return=minimal</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/24.xml</filepath> + </data> + <verify> + <callback>dataString</callback> + <arg> + <name>notcontains</name> + <value>foobar</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + + <test-suite name='limited reports'> + <test name='1'> + <exclude-feature> + <feature>sync-report-limit</feature> + </exclude-feature> + <description>Limit not allowed</description> + <request> + <method>REPORT</method> + <ruri>$calendarhome1:/</ruri> + <header> + <name>Depth</name> + <value>0</value> + </header> + <data> + <content-type>text/xml; charset=utf-8</content-type> + <filepath>Resource/CalDAV/reports/sync/21.xml</filepath> + </data> + <verify> + <callback>prepostcondition</callback> + <arg> + <name>error</name> + <value>{DAV:}number-of-matches-within-limits</value> + </arg> + </verify> + </request> + </test> + </test-suite> + + <end/> + +</caldavtest> diff --git a/apps/dav/tests/travis/carddav/install.sh b/apps/dav/tests/travis/carddav/install.sh new file mode 100644 index 00000000000..fa5d141ce0d --- /dev/null +++ b/apps/dav/tests/travis/carddav/install.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + + +if [ ! -f CalDAVTester/run.py ]; then + cd "$SCRIPTPATH" + git clone https://github.com/DeepDiver1975/CalDAVTester.git + cd "$SCRIPTPATH/CalDAVTester" + python run.py -s + cd "$SCRIPTPATH" +fi + +# create test user +cd "$SCRIPTPATH/../../../../../" +OC_PASS=user01 php occ user:add --password-from-env user01 +php occ dav:create-addressbook user01 addressbook +OC_PASS=user02 php occ user:add --password-from-env user02 +php occ dav:create-addressbook user02 addressbook +cd "$SCRIPTPATH/../../../../../" diff --git a/apps/dav/tests/travis/carddav/script.sh b/apps/dav/tests/travis/carddav/script.sh new file mode 100644 index 00000000000..46a6a98e273 --- /dev/null +++ b/apps/dav/tests/travis/carddav/script.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + +# start the server +php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." & + +sleep 30 + +# run the tests +cd "$SCRIPTPATH/CalDAVTester" +PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/../caldavtest/config/serverinfo.xml" -o cdt.txt \ + "$SCRIPTPATH/../caldavtest/tests/CardDAV/current-user-principal.xml" \ + "$SCRIPTPATH/../caldavtest/tests/CardDAV/sync-report.xml" +RESULT=$? + +tail "$/../../../../../data-autotest/owncloud.log" + +exit $RESULT diff --git a/apps/dav/tests/travis/carddavtester.sh b/apps/dav/tests/travis/carddavtester.sh deleted file mode 100644 index 17f7e8eb4a8..00000000000 --- a/apps/dav/tests/travis/carddavtester.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -SCRIPT=`realpath $0` -SCRIPTPATH=`dirname $SCRIPT` - - -# start the server -php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." & - - -if [ ! -f CalDAVTester/run.py ]; then - cd "$SCRIPTPATH" - git clone https://github.com/DeepDiver1975/CalDAVTester.git - cd "$SCRIPTPATH/CalDAVTester" - python run.py -s - cd "$SCRIPTPATH" -fi - -# create test user -cd "$SCRIPTPATH/../../../../" -OC_PASS=user01 php occ user:add --password-from-env user01 -php occ dav:create-addressbook user01 addressbook -OC_PASS=user02 php occ user:add --password-from-env user02 -php occ dav:create-addressbook user02 addressbook -cd "$SCRIPTPATH/../../../../" - -# run the tests -cd "$SCRIPTPATH/CalDAVTester" -PYTHONPATH="$SCRIPTPATH/pycalendar/src" python testcaldav.py --print-details-onfail -s "$SCRIPTPATH/caldavtest/config/serverinfo.xml" -o cdt.txt \ - "$SCRIPTPATH/caldavtest/tests/CardDAV/current-user-principal.xml" \ - "$SCRIPTPATH/caldavtest/tests/CardDAV/sync-report.xml" -RESULT=$? - -tail "$SCRIPTPATH/../../../../data-autotest/owncloud.log" - -exit $RESULT diff --git a/apps/dav/tests/travis/litmus-v1.sh b/apps/dav/tests/travis/litmus-v1/install.sh index ab0690f392e..0ee2cb08d82 100644 --- a/apps/dav/tests/travis/litmus-v1.sh +++ b/apps/dav/tests/travis/litmus-v1/install.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -SCRIPT=`realpath $0` -SCRIPTPATH=`dirname $SCRIPT` - - -# start the server -php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." & - # compile litmus if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then @@ -17,7 +10,3 @@ if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then ./configure make fi - -# run the tests -cd /tmp/litmus/litmus-0.13 -make URL=http://127.0.0.1:8888/remote.php/webdav CREDS="admin admin" TESTS="basic copymove props locks" check diff --git a/apps/dav/tests/travis/litmus-v1/script.sh b/apps/dav/tests/travis/litmus-v1/script.sh new file mode 100644 index 00000000000..cba305683b2 --- /dev/null +++ b/apps/dav/tests/travis/litmus-v1/script.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + + +# start the server +php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." & + +sleep 30 + +# run the tests +cd /tmp/litmus/litmus-0.13 +make URL=http://127.0.0.1:8888/remote.php/webdav CREDS="admin admin" TESTS="basic copymove props locks" check diff --git a/apps/dav/tests/travis/litmus-v2.sh b/apps/dav/tests/travis/litmus-v2/install.sh index 892ad327d3b..0ee2cb08d82 100644 --- a/apps/dav/tests/travis/litmus-v2.sh +++ b/apps/dav/tests/travis/litmus-v2/install.sh @@ -1,11 +1,4 @@ #!/usr/bin/env bash -SCRIPT=`realpath $0` -SCRIPTPATH=`dirname $SCRIPT` - - -# start the server -php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../.." & - # compile litmus if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then @@ -17,7 +10,3 @@ if [ ! -f /tmp/litmus/litmus-0.13.tar.gz ]; then ./configure make fi - -# run the tests -cd /tmp/litmus/litmus-0.13 -make URL=http://127.0.0.1:8888/remote.php/dav/files/admin CREDS="admin admin" TESTS="basic copymove props locks" check diff --git a/apps/dav/tests/travis/litmus-v2/script.sh b/apps/dav/tests/travis/litmus-v2/script.sh new file mode 100644 index 00000000000..966ed5a2052 --- /dev/null +++ b/apps/dav/tests/travis/litmus-v2/script.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +SCRIPT=`realpath $0` +SCRIPTPATH=`dirname $SCRIPT` + + +# start the server +php -S 127.0.0.1:8888 -t "$SCRIPTPATH/../../../../.." & + +sleep 30 + +# run the tests +cd /tmp/litmus/litmus-0.13 +make URL=http://127.0.0.1:8888/remote.php/dav/files/admin CREDS="admin admin" TESTS="basic copymove props locks" check diff --git a/apps/dav/tests/unit/carddav/addressbookimpltest.php b/apps/dav/tests/unit/carddav/addressbookimpltest.php new file mode 100644 index 00000000000..73053888b9d --- /dev/null +++ b/apps/dav/tests/unit/carddav/addressbookimpltest.php @@ -0,0 +1,287 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @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\CardDAV; + + +use OCA\DAV\CardDAV\AddressBook; +use OCA\DAV\CardDAV\AddressBookImpl; +use OCA\DAV\CardDAV\CardDavBackend; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Property\Text; +use Test\TestCase; + +class AddressBookImplTest extends TestCase { + + /** @var AddressBookImpl */ + private $addressBookImpl; + + /** @var array */ + private $addressBookInfo; + + /** @var AddressBook | \PHPUnit_Framework_MockObject_MockObject */ + private $addressBook; + + /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject */ + private $backend; + + /** @var VCard | \PHPUnit_Framework_MockObject_MockObject */ + private $vCard; + + public function setUp() { + parent::setUp(); + + $this->addressBookInfo = [ + 'id' => 42, + '{DAV:}displayname' => 'display name' + ]; + $this->addressBook = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBook') + ->disableOriginalConstructor()->getMock(); + $this->backend = $this->getMockBuilder('\OCA\DAV\CardDAV\CardDavBackend') + ->disableOriginalConstructor()->getMock(); + $this->vCard = $this->getMock('Sabre\VObject\Component\VCard'); + + $this->addressBookImpl = new AddressBookImpl( + $this->addressBook, + $this->addressBookInfo, + $this->backend + ); + } + + public function testGetKey() { + $this->assertSame($this->addressBookInfo['id'], + $this->addressBookImpl->getKey()); + } + + public function testGetDisplayName() { + $this->assertSame($this->addressBookInfo['{DAV:}displayname'], + $this->addressBookImpl->getDisplayName()); + } + + public function testSearch() { + + /** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */ + $addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl') + ->setConstructorArgs( + [ + $this->addressBook, + $this->addressBookInfo, + $this->backend + ] + ) + ->setMethods(['vCard2Array', 'readCard']) + ->getMock(); + + $pattern = 'pattern'; + $searchProperties = 'properties'; + + $this->backend->expects($this->once())->method('search') + ->with($this->addressBookInfo['id'], $pattern, $searchProperties) + ->willReturn( + [ + 'cardData1', + 'cardData2' + ] + ); + + $addressBookImpl->expects($this->exactly(2))->method('readCard') + ->willReturn($this->vCard); + $addressBookImpl->expects($this->exactly(2))->method('vCard2Array') + ->with($this->vCard)->willReturn('vCard'); + + $result = $addressBookImpl->search($pattern, $searchProperties, []); + $this->assertTrue((is_array($result))); + $this->assertSame(2, count($result)); + } + + /** + * @dataProvider dataTestCreate + * + * @param array $properties + */ + public function testCreate($properties) { + + $uid = 'uid'; + + /** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */ + $addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl') + ->setConstructorArgs( + [ + $this->addressBook, + $this->addressBookInfo, + $this->backend + ] + ) + ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard']) + ->getMock(); + + $addressBookImpl->expects($this->once())->method('createUid') + ->willReturn($uid); + $addressBookImpl->expects($this->once())->method('createEmptyVCard') + ->with($uid)->willReturn($this->vCard); + $this->vCard->expects($this->exactly(count($properties))) + ->method('createProperty'); + $this->backend->expects($this->once())->method('createCard'); + $this->backend->expects($this->never())->method('updateCard'); + $this->backend->expects($this->never())->method('getCard'); + $addressBookImpl->expects($this->once())->method('vCard2Array') + ->with($this->vCard)->willReturn(true); + + $this->assertTrue($addressBookImpl->createOrUpdate($properties)); + } + + public function dataTestCreate() { + return [ + [[]], + [['FN' => 'John Doe']] + ]; + } + + public function testUpdate() { + + $uid = 'uid'; + $properties = ['UID' => $uid, 'FN' => 'John Doe']; + + /** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */ + $addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl') + ->setConstructorArgs( + [ + $this->addressBook, + $this->addressBookInfo, + $this->backend + ] + ) + ->setMethods(['vCard2Array', 'createUid', 'createEmptyVCard', 'readCard']) + ->getMock(); + + $addressBookImpl->expects($this->never())->method('createUid'); + $addressBookImpl->expects($this->never())->method('createEmptyVCard'); + $this->backend->expects($this->once())->method('getCard') + ->with($this->addressBookInfo['id'], $uid . '.vcf') + ->willReturn(['carddata' => 'data']); + $addressBookImpl->expects($this->once())->method('readCard') + ->with('data')->willReturn($this->vCard); + $this->vCard->expects($this->exactly(count($properties))) + ->method('createProperty'); + $this->backend->expects($this->never())->method('createCard'); + $this->backend->expects($this->once())->method('updateCard'); + $addressBookImpl->expects($this->once())->method('vCard2Array') + ->with($this->vCard)->willReturn(true); + + $this->assertTrue($addressBookImpl->createOrUpdate($properties)); + } + + /** + * @dataProvider dataTestGetPermissions + * + * @param array $permissions + * @param int $expected + */ + public function testGetPermissions($permissions, $expected) { + $this->addressBook->expects($this->once())->method('getACL') + ->willReturn($permissions); + + $this->assertSame($expected, + $this->addressBookImpl->getPermissions() + ); + } + + public function dataTestGetPermissions() { + return [ + [[], 0], + [[['privilege' => '{DAV:}read']], 1], + [[['privilege' => '{DAV:}write']], 6], + [[['privilege' => '{DAV:}all']], 31], + [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 7], + [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}all']], 31], + [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}write']], 31], + [[['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write'],['privilege' => '{DAV:}all']], 31], + [[['privilege' => '{DAV:}all'],['privilege' => '{DAV:}read'],['privilege' => '{DAV:}write']], 31], + ]; + } + + public function testDelete() { + $cardId = 1; + $cardUri = 'cardUri'; + $this->backend->expects($this->once())->method('getCardUri') + ->with($cardId)->willReturn($cardUri); + $this->backend->expects($this->once())->method('deleteCard') + ->with($this->addressBookInfo['id'], $cardUri) + ->willReturn(true); + + $this->assertTrue($this->addressBookImpl->delete($cardId)); + } + + public function testReadCard() { + $vCard = new VCard(); + $vCard->add(new Text($vCard, 'UID', 'uid')); + $vCardSerialized = $vCard->serialize(); + + $result = $this->invokePrivate($this->addressBookImpl, 'readCard', [$vCardSerialized]); + $resultSerialized = $result->serialize(); + + $this->assertSame($vCardSerialized, $resultSerialized); + } + + public function testCreateUid() { + /** @var \PHPUnit_Framework_MockObject_MockObject | AddressBookImpl $addressBookImpl */ + $addressBookImpl = $this->getMockBuilder('OCA\DAV\CardDAV\AddressBookImpl') + ->setConstructorArgs( + [ + $this->addressBook, + $this->addressBookInfo, + $this->backend + ] + ) + ->setMethods(['getUid']) + ->getMock(); + + $addressBookImpl->expects($this->at(0))->method('getUid')->willReturn('uid0'); + $addressBookImpl->expects($this->at(1))->method('getUid')->willReturn('uid1'); + + // simulate that 'uid0' already exists, so the second uid will be returned + $this->backend->expects($this->exactly(2))->method('getContact') + ->willReturnCallback( + function($uid) { + return ($uid === 'uid0.vcf'); + } + ); + + $this->assertSame('uid1', + $this->invokePrivate($addressBookImpl, 'createUid', []) + ); + + } + + public function testCreateEmptyVCard() { + $uid = 'uid'; + $expectedVCard = new VCard(); + $expectedVCard->add(new Text($expectedVCard, 'UID', $uid)); + $expectedVCardSerialized = $expectedVCard->serialize(); + + $result = $this->invokePrivate($this->addressBookImpl, 'createEmptyVCard', [$uid]); + $resultSerialized = $result->serialize(); + + $this->assertSame($expectedVCardSerialized, $resultSerialized); + } + +} diff --git a/apps/dav/tests/unit/carddav/carddavbackendtest.php b/apps/dav/tests/unit/carddav/carddavbackendtest.php index dd5e205242a..fe01aa65cca 100644 --- a/apps/dav/tests/unit/carddav/carddavbackendtest.php +++ b/apps/dav/tests/unit/carddav/carddavbackendtest.php @@ -20,8 +20,14 @@ */ namespace OCA\DAV\Tests\Unit\CardDAV; +use InvalidArgumentException; use OCA\DAV\CardDAV\CardDavBackend; +use OCA\DAV\Connector\Sabre\Principal; +use OCP\IDBConnection; +use OCP\ILogger; use Sabre\DAV\PropPatch; +use Sabre\VObject\Component\VCard; +use Sabre\VObject\Property\Text; use Test\TestCase; /** @@ -36,22 +42,46 @@ class CardDavBackendTest extends TestCase { /** @var CardDavBackend */ private $backend; + /** @var Principal | \PHPUnit_Framework_MockObject_MockObject */ + private $principal; + + /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */ + private $logger; + + /** @var IDBConnection */ + private $db; + + /** @var string */ + private $dbCardsTable = 'cards'; + + /** @var string */ + private $dbCardsPropertiesTable = 'cards_properties'; + const UNIT_TEST_USER = 'carddav-unit-test'; public function setUp() { parent::setUp(); - $principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') + $this->principal = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Principal') ->disableOriginalConstructor() ->setMethods(['getPrincipalByPath']) ->getMock(); - $principal->method('getPrincipalByPath') + $this->principal->method('getPrincipalByPath') ->willReturn([ 'uri' => 'principals/best-friend' ]); + $this->logger = $this->getMock('\OCP\ILogger'); + + $this->db = \OC::$server->getDatabaseConnection(); + + $this->backend = new CardDavBackend($this->db, $this->principal, $this->logger); + + // start every test with a empty cards_properties and cards table + $query = $this->db->getQueryBuilder(); + $query->delete('cards_properties')->execute(); + $query = $this->db->getQueryBuilder(); + $query->delete('cards')->execute(); - $db = \OC::$server->getDatabaseConnection(); - $this->backend = new CardDavBackend($db, $principal); $this->tearDown(); } @@ -96,23 +126,32 @@ class CardDavBackendTest extends TestCase { } public function testCardOperations() { + + /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backend */ + $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') + ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setMethods(['updateProperties', 'purgeProperties'])->getMock(); + // create a new address book - $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); - $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER); + $backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); + $books = $backend->getAddressBooksForUser(self::UNIT_TEST_USER); $this->assertEquals(1, count($books)); $bookId = $books[0]['id']; - // create a card $uri = $this->getUniqueID('card'); - $this->backend->createCard($bookId, $uri, ''); + // updateProperties is expected twice, once for createCard and once for updateCard + $backend->expects($this->at(0))->method('updateProperties')->with($bookId, $uri, ''); + $backend->expects($this->at(1))->method('updateProperties')->with($bookId, $uri, '***'); + // create a card + $backend->createCard($bookId, $uri, ''); // get all the cards - $cards = $this->backend->getCards($bookId); + $cards = $backend->getCards($bookId); $this->assertEquals(1, count($cards)); $this->assertEquals('', $cards[0]['carddata']); // get the cards - $card = $this->backend->getCard($bookId, $uri); + $card = $backend->getCard($bookId, $uri); $this->assertNotNull($card); $this->assertArrayHasKey('id', $card); $this->assertArrayHasKey('uri', $card); @@ -122,17 +161,23 @@ class CardDavBackendTest extends TestCase { $this->assertEquals('', $card['carddata']); // update the card - $this->backend->updateCard($bookId, $uri, '***'); - $card = $this->backend->getCard($bookId, $uri); + $backend->updateCard($bookId, $uri, '***'); + $card = $backend->getCard($bookId, $uri); $this->assertEquals('***', $card['carddata']); // delete the card - $this->backend->deleteCard($bookId, $uri); - $cards = $this->backend->getCards($bookId); + $backend->expects($this->once())->method('purgeProperties')->with($bookId, $card['id']); + $backend->deleteCard($bookId, $uri); + $cards = $backend->getCards($bookId); $this->assertEquals(0, count($cards)); } public function testMultiCard() { + + $this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') + ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setMethods(['updateProperties'])->getMock(); + // create a new address book $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER); @@ -175,6 +220,11 @@ class CardDavBackendTest extends TestCase { } public function testSyncSupport() { + + $this->backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') + ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setMethods(['updateProperties'])->getMock(); + // create a new address book $this->backend->createAddressBook(self::UNIT_TEST_USER, 'Example', []); $books = $this->backend->getAddressBooksForUser(self::UNIT_TEST_USER); @@ -213,7 +263,7 @@ class CardDavBackendTest extends TestCase { $books = $this->backend->getAddressBooksForUser('principals/best-friend'); $this->assertEquals(1, count($books)); - $this->backend->updateShares('Example', [], [['href' => 'principal:principals/best-friend']]); + $this->backend->updateShares('Example', [], ['principal:principals/best-friend']); $shares = $this->backend->getShares('Example'); $this->assertEquals(0, count($shares)); @@ -221,4 +271,273 @@ class CardDavBackendTest extends TestCase { $books = $this->backend->getAddressBooksForUser('principals/best-friend'); $this->assertEquals(0, count($books)); } + + public function testUpdateProperties() { + + $bookId = 42; + $cardUri = 'card-uri'; + $cardId = 2; + + $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend') + ->setConstructorArgs([$this->db, $this->principal, $this->logger]) + ->setMethods(['getCardId'])->getMock(); + + $backend->expects($this->any())->method('getCardId')->willReturn($cardId); + + // add properties for new vCard + $vCard = new VCard(); + $vCard->add(new Text($vCard, 'UID', $cardUri)); + $vCard->add(new Text($vCard, 'FN', 'John Doe')); + $this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]); + + $query = $this->db->getQueryBuilder(); + $result = $query->select('*')->from('cards_properties')->execute()->fetchAll(); + + $this->assertSame(2, count($result)); + + $this->assertSame('UID', $result[0]['name']); + $this->assertSame($cardUri, $result[0]['value']); + $this->assertSame($bookId, (int)$result[0]['addressbookid']); + $this->assertSame($cardId, (int)$result[0]['cardid']); + + $this->assertSame('FN', $result[1]['name']); + $this->assertSame('John Doe', $result[1]['value']); + $this->assertSame($bookId, (int)$result[1]['addressbookid']); + $this->assertSame($cardId, (int)$result[1]['cardid']); + + // update properties for existing vCard + $vCard = new VCard(); + $vCard->add(new Text($vCard, 'FN', 'John Doe')); + $this->invokePrivate($backend, 'updateProperties', [$bookId, $cardUri, $vCard->serialize()]); + + $query = $this->db->getQueryBuilder(); + $result = $query->select('*')->from('cards_properties')->execute()->fetchAll(); + + $this->assertSame(1, count($result)); + + $this->assertSame('FN', $result[0]['name']); + $this->assertSame('John Doe', $result[0]['value']); + $this->assertSame($bookId, (int)$result[0]['addressbookid']); + $this->assertSame($cardId, (int)$result[0]['cardid']); + } + + public function testPurgeProperties() { + + $query = $this->db->getQueryBuilder(); + $query->insert('cards_properties') + ->values( + [ + 'addressbookid' => $query->createNamedParameter(1), + 'cardid' => $query->createNamedParameter(1), + 'name' => $query->createNamedParameter('name1'), + 'value' => $query->createNamedParameter('value1'), + 'preferred' => $query->createNamedParameter(0) + ] + ); + $query->execute(); + + $query = $this->db->getQueryBuilder(); + $query->insert('cards_properties') + ->values( + [ + 'addressbookid' => $query->createNamedParameter(1), + 'cardid' => $query->createNamedParameter(2), + 'name' => $query->createNamedParameter('name2'), + 'value' => $query->createNamedParameter('value2'), + 'preferred' => $query->createNamedParameter(0) + ] + ); + $query->execute(); + + $this->invokePrivate($this->backend, 'purgeProperties', [1, 1]); + + $query = $this->db->getQueryBuilder(); + $result = $query->select('*')->from('cards_properties')->execute()->fetchAll(); + $this->assertSame(1, count($result)); + $this->assertSame(1 ,(int)$result[0]['addressbookid']); + $this->assertSame(2 ,(int)$result[0]['cardid']); + + } + + public function testGetCardId() { + $query = $this->db->getQueryBuilder(); + + $query->insert('cards') + ->values( + [ + 'addressbookid' => $query->createNamedParameter(1), + 'carddata' => $query->createNamedParameter(''), + 'uri' => $query->createNamedParameter('uri'), + 'lastmodified' => $query->createNamedParameter(4738743), + 'etag' => $query->createNamedParameter('etag'), + 'size' => $query->createNamedParameter(120) + ] + ); + $query->execute(); + $id = $query->getLastInsertId(); + + $this->assertSame($id, + $this->invokePrivate($this->backend, 'getCardId', ['uri'])); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetCardIdFailed() { + $this->invokePrivate($this->backend, 'getCardId', ['uri']); + } + + /** + * @dataProvider dataTestSearch + * + * @param string $pattern + * @param array $expected + */ + public function testSearch($pattern, $properties, $expected) { + /** @var VCard $vCards */ + $vCards = []; + $vCards[0] = new VCard(); + $vCards[0]->add(new Text($vCards[0], 'UID', 'uid')); + $vCards[0]->add(new Text($vCards[0], 'FN', 'John Doe')); + $vCards[0]->add(new Text($vCards[0], 'CLOUD', 'john@owncloud.org')); + $vCards[1] = new VCard(); + $vCards[1]->add(new Text($vCards[1], 'UID', 'uid')); + $vCards[1]->add(new Text($vCards[1], 'FN', 'John M. Doe')); + + $vCardIds = []; + $query = $this->db->getQueryBuilder(); + for($i=0; $i<2; $i++) { + $query->insert($this->dbCardsTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter(0), + 'carddata' => $query->createNamedParameter($vCards[$i]->serialize(), \PDO::PARAM_LOB), + 'uri' => $query->createNamedParameter('uri' . $i), + 'lastmodified' => $query->createNamedParameter(time()), + 'etag' => $query->createNamedParameter('etag' . $i), + 'size' => $query->createNamedParameter(120), + ] + ); + $query->execute(); + $vCardIds[] = $query->getLastInsertId(); + } + + $query->insert($this->dbCardsPropertiesTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter(0), + 'cardid' => $query->createNamedParameter($vCardIds[0]), + 'name' => $query->createNamedParameter('FN'), + 'value' => $query->createNamedParameter('John Doe'), + 'preferred' => $query->createNamedParameter(0) + ] + ); + $query->execute(); + $query->insert($this->dbCardsPropertiesTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter(0), + 'cardid' => $query->createNamedParameter($vCardIds[0]), + 'name' => $query->createNamedParameter('CLOUD'), + 'value' => $query->createNamedParameter('John@owncloud.org'), + 'preferred' => $query->createNamedParameter(0) + ] + ); + $query->execute(); + $query->insert($this->dbCardsPropertiesTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter(0), + 'cardid' => $query->createNamedParameter($vCardIds[1]), + 'name' => $query->createNamedParameter('FN'), + 'value' => $query->createNamedParameter('John M. Doe'), + 'preferred' => $query->createNamedParameter(0) + ] + ); + $query->execute(); + + $result = $this->backend->search(0, $pattern, $properties); + + // check result + $this->assertSame(count($expected), count($result)); + $found = []; + foreach ($result as $r) { + foreach ($expected as $exp) { + if (strpos($r, $exp) > 0) { + $found[$exp] = true; + break; + } + } + } + + $this->assertSame(count($expected), count($found)); + } + + public function dataTestSearch() { + return [ + ['John', ['FN'], ['John Doe', 'John M. Doe']], + ['M. Doe', ['FN'], ['John M. Doe']], + ['Do', ['FN'], ['John Doe', 'John M. Doe']], + // check if duplicates are handled correctly + ['John', ['FN', 'CLOUD'], ['John Doe', 'John M. Doe']], + ]; + } + + public function testGetCardUri() { + $query = $this->db->getQueryBuilder(); + $query->insert($this->dbCardsTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter(1), + 'carddata' => $query->createNamedParameter('carddata', \PDO::PARAM_LOB), + 'uri' => $query->createNamedParameter('uri'), + 'lastmodified' => $query->createNamedParameter(5489543), + 'etag' => $query->createNamedParameter('etag'), + 'size' => $query->createNamedParameter(120), + ] + ); + $query->execute(); + + $id = $query->getLastInsertId(); + + $this->assertSame('uri', $this->backend->getCardUri($id)); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testGetCardUriFailed() { + $this->backend->getCardUri(1); + } + + public function testGetContact() { + $query = $this->db->getQueryBuilder(); + for($i=0; $i<2; $i++) { + $query->insert($this->dbCardsTable) + ->values( + [ + 'addressbookid' => $query->createNamedParameter($i), + 'carddata' => $query->createNamedParameter('carddata' . $i, \PDO::PARAM_LOB), + 'uri' => $query->createNamedParameter('uri' . $i), + 'lastmodified' => $query->createNamedParameter(5489543), + 'etag' => $query->createNamedParameter('etag' . $i), + 'size' => $query->createNamedParameter(120), + ] + ); + $query->execute(); + } + + $result = $this->backend->getContact('uri0'); + $this->assertSame(7, count($result)); + $this->assertSame(0, (int)$result['addressbookid']); + $this->assertSame('uri0', $result['uri']); + $this->assertSame(5489543, (int)$result['lastmodified']); + $this->assertSame('etag0', $result['etag']); + $this->assertSame(120, (int)$result['size']); + } + + public function testGetContactFail() { + $this->assertEmpty($this->backend->getContact('uri')); + } + } diff --git a/apps/dav/tests/unit/carddav/sharing/plugintest.php b/apps/dav/tests/unit/carddav/sharing/plugintest.php new file mode 100644 index 00000000000..e9532acbe3b --- /dev/null +++ b/apps/dav/tests/unit/carddav/sharing/plugintest.php @@ -0,0 +1,81 @@ +<?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\CardDAV; + + +use OCA\DAV\CardDAV\Sharing\IShareableAddressBook; +use OCA\DAV\CardDAV\Sharing\Plugin; +use OCA\DAV\Connector\Sabre\Auth; +use OCP\IRequest; +use Sabre\DAV\Server; +use Sabre\DAV\SimpleCollection; +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; +use Test\TestCase; + +class PluginTest extends TestCase { + + /** @var Plugin */ + private $plugin; + /** @var Server */ + private $server; + /** @var IShareableAddressBook | \PHPUnit_Framework_MockObject_MockObject */ + private $book; + + public function setUp() { + parent::setUp(); + + /** @var Auth | \PHPUnit_Framework_MockObject_MockObject $authBackend */ + $authBackend = $this->getMockBuilder('OCA\DAV\Connector\Sabre\Auth')->disableOriginalConstructor()->getMock(); + $authBackend->method('isDavAuthenticated')->willReturn(true); + + /** @var IRequest $request */ + $request = $this->getMockBuilder('OCP\IRequest')->disableOriginalConstructor()->getMock(); + $this->plugin = new Plugin($authBackend, $request); + + $root = new SimpleCollection('root'); + $this->server = new \Sabre\DAV\Server($root); + /** @var SimpleCollection $node */ + $this->book = $this->getMockBuilder('OCA\DAV\CardDAV\Sharing\IShareableAddressBook')->disableOriginalConstructor()->getMock(); + $this->book->method('getName')->willReturn('addressbook1.vcf'); + $root->addChild($this->book); + $this->plugin->initialize($this->server); + } + + public function testSharing() { + + $this->book->expects($this->once())->method('updateShares')->with([[ + 'href' => 'principal:principals/admin', + 'commonName' => null, + 'summary' => null, + 'readOnly' => false + ]], ['mailto:wilfredo@example.com']); + + // setup request + $request = new Request(); + $request->addHeader('Content-Type', 'application/xml'); + $request->setUrl('addressbook1.vcf'); + $request->setBody('<?xml version="1.0" encoding="utf-8" ?><CS:share xmlns:D="DAV:" xmlns:CS="urn:ietf:params:xml:ns:carddav"><CS:set><D:href>principal:principals/admin</D:href><CS:read-write/></CS:set> <CS:remove><D:href>mailto:wilfredo@example.com</D:href></CS:remove></CS:share>'); + $response = new Response(); + $this->plugin->httpPost($request, $response); + } +} diff --git a/apps/dav/tests/unit/connector/sabre/file.php b/apps/dav/tests/unit/connector/sabre/file.php index 2a6cf46ef16..ad4c1d29ed4 100644 --- a/apps/dav/tests/unit/connector/sabre/file.php +++ b/apps/dav/tests/unit/connector/sabre/file.php @@ -48,6 +48,14 @@ class File extends \Test\TestCase { parent::tearDown(); } + private function getMockStorage() { + $storage = $this->getMock('\OCP\Files\Storage'); + $storage->expects($this->any()) + ->method('getId') + ->will($this->returnValue('home::someuser')); + return $storage; + } + /** * @param string $string */ @@ -149,7 +157,7 @@ class File extends \Test\TestCase { ->method('getRelativePath') ->will($this->returnArgument(0)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -209,7 +217,7 @@ class File extends \Test\TestCase { $_SERVER['HTTP_OC_CHUNKED'] = true; - $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', null, null, [ + $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL ], null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -219,7 +227,7 @@ class File extends \Test\TestCase { $this->assertNull($file->put('test data one')); $file->releaseLock(ILockingProvider::LOCK_SHARED); - $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', null, null, [ + $info = new \OC\Files\FileInfo('/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL ], null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -261,7 +269,7 @@ class File extends \Test\TestCase { $info = new \OC\Files\FileInfo( $viewRoot . '/' . ltrim($path, '/'), - null, + $this->getMockStorage(), null, ['permissions' => \OCP\Constants::PERMISSION_ALL], null @@ -450,7 +458,7 @@ class File extends \Test\TestCase { $_SERVER['CONTENT_LENGTH'] = 123456; $_SERVER['REQUEST_METHOD'] = 'PUT'; - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -483,7 +491,7 @@ class File extends \Test\TestCase { // simulate situation where the target file is locked $view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE); - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -518,7 +526,7 @@ class File extends \Test\TestCase { $_SERVER['HTTP_OC_CHUNKED'] = true; - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', null, null, [ + $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-0', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL ], null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -526,7 +534,7 @@ class File extends \Test\TestCase { $this->assertNull($file->put('test data one')); $file->releaseLock(ILockingProvider::LOCK_SHARED); - $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', null, null, [ + $info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt-chunking-12345-2-1', $this->getMockStorage(), null, [ 'permissions' => \OCP\Constants::PERMISSION_ALL ], null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -555,7 +563,7 @@ class File extends \Test\TestCase { ->method('getRelativePath') ->will($this->returnArgument(0)); - $info = new \OC\Files\FileInfo('/*', null, null, array( + $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -591,7 +599,7 @@ class File extends \Test\TestCase { ->method('getRelativePath') ->will($this->returnArgument(0)); - $info = new \OC\Files\FileInfo('/*', null, null, array( + $info = new \OC\Files\FileInfo('/*', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); $file = new \OCA\DAV\Connector\Sabre\File($view, $info); @@ -618,7 +626,7 @@ class File extends \Test\TestCase { $_SERVER['CONTENT_LENGTH'] = 12345; $_SERVER['REQUEST_METHOD'] = 'PUT'; - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -654,7 +662,7 @@ class File extends \Test\TestCase { ->method('unlink') ->will($this->returnValue(true)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -672,7 +680,7 @@ class File extends \Test\TestCase { $view = $this->getMock('\OC\Files\View', array()); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => 0 ), null); @@ -695,7 +703,7 @@ class File extends \Test\TestCase { ->method('unlink') ->will($this->returnValue(false)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -718,7 +726,7 @@ class File extends \Test\TestCase { ->method('unlink') ->willThrowException(new ForbiddenException('', true)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -753,7 +761,7 @@ class File extends \Test\TestCase { $path = 'test-locking.txt'; $info = new \OC\Files\FileInfo( '/' . $this->user . '/files/' . $path, - null, + $this->getMockStorage(), null, ['permissions' => \OCP\Constants::PERMISSION_ALL], null @@ -865,7 +873,7 @@ class File extends \Test\TestCase { ->method('fopen') ->will($this->returnValue(false)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); @@ -883,7 +891,7 @@ class File extends \Test\TestCase { ->method('fopen') ->willThrowException(new ForbiddenException('', true)); - $info = new \OC\Files\FileInfo('/test.txt', null, null, array( + $info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, array( 'permissions' => \OCP\Constants::PERMISSION_ALL ), null); diff --git a/apps/dav/tests/unit/connector/sabre/filesplugin.php b/apps/dav/tests/unit/connector/sabre/filesplugin.php index b33c8340f72..642fc3258cd 100644 --- a/apps/dav/tests/unit/connector/sabre/filesplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesplugin.php @@ -2,6 +2,8 @@ namespace OCA\DAV\Tests\Unit\Connector\Sabre; +use OCP\Files\StorageNotAvailableException; + /** * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> * This file is licensed under the Affero General Public License version 3 or @@ -143,6 +145,29 @@ class FilesPlugin extends \Test\TestCase { $this->assertEquals(array(self::SIZE_PROPERTYNAME), $propFind->get404Properties()); } + public function testGetPropertiesStorageNotAvailable() { + $node = $this->createTestNode('\OCA\DAV\Connector\Sabre\File'); + + $propFind = new \Sabre\DAV\PropFind( + '/dummyPath', + array( + self::DOWNLOADURL_PROPERTYNAME, + ), + 0 + ); + + $node->expects($this->once()) + ->method('getDirectDownload') + ->will($this->throwException(new StorageNotAvailableException())); + + $this->plugin->handleGetProperties( + $propFind, + $node + ); + + $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); + } + public function testGetPublicPermissions() { $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view, true); $this->plugin->initialize($this->server); diff --git a/apps/dav/tests/unit/connector/sabre/objecttree.php b/apps/dav/tests/unit/connector/sabre/objecttree.php index 1cea4ff0b69..6164b96c986 100644 --- a/apps/dav/tests/unit/connector/sabre/objecttree.php +++ b/apps/dav/tests/unit/connector/sabre/objecttree.php @@ -294,4 +294,45 @@ class ObjectTree extends \Test\TestCase { $this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path)); } + + /** + * @expectedException \Sabre\DAV\Exception\Forbidden + * @expectedExceptionMessage Could not copy directory nameOfSourceNode, target exists + */ + public function testFailingMove() { + $source = 'a/b'; + $destination = 'b/b'; + $updatables = array('a' => true, 'a/b' => true, 'b' => true, 'b/b' => false); + $deletables = array('a/b' => true); + + $view = new TestDoubleFileView($updatables, $deletables); + + $info = new FileInfo('', null, null, array(), null); + + $rootDir = new \OCA\DAV\Connector\Sabre\Directory($view, $info); + $objectTree = $this->getMock('\OCA\DAV\Connector\Sabre\ObjectTree', + array('nodeExists', 'getNodeForPath'), + array($rootDir, $view)); + + $sourceNode = $this->getMockBuilder('\Sabre\DAV\ICollection') + ->disableOriginalConstructor() + ->getMock(); + $sourceNode->expects($this->once()) + ->method('getName') + ->will($this->returnValue('nameOfSourceNode')); + + $objectTree->expects($this->once()) + ->method('nodeExists') + ->with($this->identicalTo($destination)) + ->will($this->returnValue(true)); + $objectTree->expects($this->once()) + ->method('getNodeForPath') + ->with($this->identicalTo($source)) + ->will($this->returnValue($sourceNode)); + + /** @var $objectTree \OCA\DAV\Connector\Sabre\ObjectTree */ + $mountManager = \OC\Files\Filesystem::getMountManager(); + $objectTree->init($rootDir, $view, $mountManager); + $objectTree->move($source, $destination); + } } diff --git a/apps/dav/tests/unit/systemtag/systemtagmappingnode.php b/apps/dav/tests/unit/systemtag/systemtagmappingnode.php index 849f7c2fa54..aef62a1acb2 100644 --- a/apps/dav/tests/unit/systemtag/systemtagmappingnode.php +++ b/apps/dav/tests/unit/systemtag/systemtagmappingnode.php @@ -9,12 +9,8 @@ namespace OCA\DAV\Tests\Unit\SystemTag; use Sabre\DAV\Exception\NotFound; -use Sabre\DAV\Exception\MethodNotAllowed; -use Sabre\DAV\Exception\Conflict; - use OC\SystemTag\SystemTag; use OCP\SystemTag\TagNotFoundException; -use OCP\SystemTag\TagAlreadyExistsException; class SystemTagMappingNode extends SystemTagNode { diff --git a/apps/dav/tests/unit/systemtag/systemtagsbyidcollection.php b/apps/dav/tests/unit/systemtag/systemtagsbyidcollection.php index 104ce366034..fa9e42d04f9 100644 --- a/apps/dav/tests/unit/systemtag/systemtagsbyidcollection.php +++ b/apps/dav/tests/unit/systemtag/systemtagsbyidcollection.php @@ -11,7 +11,6 @@ namespace OCA\DAV\Tests\Unit\SystemTag; use OC\SystemTag\SystemTag; use OCP\SystemTag\TagNotFoundException; -use OCP\SystemTag\TagAlreadyExistsException; class SystemTagsByIdCollection extends \Test\TestCase { diff --git a/apps/dav/tests/unit/systemtag/systemtagsobjectmappingcollection.php b/apps/dav/tests/unit/systemtag/systemtagsobjectmappingcollection.php index 6e15bb78e7c..a9b34f5ae19 100644 --- a/apps/dav/tests/unit/systemtag/systemtagsobjectmappingcollection.php +++ b/apps/dav/tests/unit/systemtag/systemtagsobjectmappingcollection.php @@ -11,7 +11,6 @@ namespace OCA\DAV\Tests\Unit\SystemTag; use OC\SystemTag\SystemTag; use OCP\SystemTag\TagNotFoundException; -use OCP\SystemTag\TagAlreadyExistsException; class SystemTagsObjectMappingCollection extends \Test\TestCase { diff --git a/apps/encryption/l10n/es.js b/apps/encryption/l10n/es.js index 6b49b3917fd..1dadb88064a 100644 --- a/apps/encryption/l10n/es.js +++ b/apps/encryption/l10n/es.js @@ -32,6 +32,7 @@ OC.L10N.register( "The share will expire on %s." : "El objeto dejará de ser compartido el %s.", "Cheers!" : "¡Saludos!", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hola,\n<br><br>\nEl administrador ha habilitado el cifrado del lado servidor. Sus archivos serán cifrados usando como contraseña: <strong>%s</strong>\n<br><br>\nPor favor, identifíquese en la interfaz web, vaya a la sección 'Modulo básico de cifrado' de sus opciones personales y actualice su contraseña tecleando esta contraseña en el campo 'contraseña antigua' e introduciendo la nueva en su correspondiente campo.<br><br>", + "Encrypt the home storage" : "Encriptar el almacenamiento personal", "Enable recovery key" : "Activa la clave de recuperación", "Disable recovery key" : "Desactiva la clave de recuperación", "The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clave de recuperación es una clave de cifrado extra que se usa para cifrar ficheros. Permite la recuperación de los ficheros de un usuario si él o ella olvida su contraseña.", diff --git a/apps/encryption/l10n/es.json b/apps/encryption/l10n/es.json index 3f21b4c09a0..5cf2b12ce5f 100644 --- a/apps/encryption/l10n/es.json +++ b/apps/encryption/l10n/es.json @@ -30,6 +30,7 @@ "The share will expire on %s." : "El objeto dejará de ser compartido el %s.", "Cheers!" : "¡Saludos!", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Hola,\n<br><br>\nEl administrador ha habilitado el cifrado del lado servidor. Sus archivos serán cifrados usando como contraseña: <strong>%s</strong>\n<br><br>\nPor favor, identifíquese en la interfaz web, vaya a la sección 'Modulo básico de cifrado' de sus opciones personales y actualice su contraseña tecleando esta contraseña en el campo 'contraseña antigua' e introduciendo la nueva en su correspondiente campo.<br><br>", + "Encrypt the home storage" : "Encriptar el almacenamiento personal", "Enable recovery key" : "Activa la clave de recuperación", "Disable recovery key" : "Desactiva la clave de recuperación", "The recovery key is an extra encryption key that is used to encrypt files. It allows recovery of a user's files if the user forgets his or her password." : "La clave de recuperación es una clave de cifrado extra que se usa para cifrar ficheros. Permite la recuperación de los ficheros de un usuario si él o ella olvida su contraseña.", diff --git a/apps/encryption/l10n/et_EE.js b/apps/encryption/l10n/et_EE.js index 501772c2808..3a29b24cc54 100644 --- a/apps/encryption/l10n/et_EE.js +++ b/apps/encryption/l10n/et_EE.js @@ -34,6 +34,7 @@ OC.L10N.register( "New recovery key password" : "Uus taastevõtme parool", "Repeat new recovery key password" : "Korda uut taastevõtme parooli", "Change Password" : "Muuda parooli", + "ownCloud basic encryption module" : "ownCloud peamine küpteerimismoodul", "Your private key password no longer matches your log-in password." : "Sinu provaatvõtme parool ei kattu enam sinu sisselogimise parooliga.", "Set your old private key password to your current log-in password:" : "Pane oma vana privaatvõtme parooliks oma praegune sisselogimise parool.", " If you don't remember your old password you can ask your administrator to recover your files." : "Kui sa ei mäleta oma vana parooli, siis palu oma süsteemihalduril taastada ligipääs failidele.", diff --git a/apps/encryption/l10n/et_EE.json b/apps/encryption/l10n/et_EE.json index 14d4c59f9fc..916bc0667ad 100644 --- a/apps/encryption/l10n/et_EE.json +++ b/apps/encryption/l10n/et_EE.json @@ -32,6 +32,7 @@ "New recovery key password" : "Uus taastevõtme parool", "Repeat new recovery key password" : "Korda uut taastevõtme parooli", "Change Password" : "Muuda parooli", + "ownCloud basic encryption module" : "ownCloud peamine küpteerimismoodul", "Your private key password no longer matches your log-in password." : "Sinu provaatvõtme parool ei kattu enam sinu sisselogimise parooliga.", "Set your old private key password to your current log-in password:" : "Pane oma vana privaatvõtme parooliks oma praegune sisselogimise parool.", " If you don't remember your old password you can ask your administrator to recover your files." : "Kui sa ei mäleta oma vana parooli, siis palu oma süsteemihalduril taastada ligipääs failidele.", diff --git a/apps/encryption/l10n/pt_BR.js b/apps/encryption/l10n/pt_BR.js index 334bbf05282..80c35dea495 100644 --- a/apps/encryption/l10n/pt_BR.js +++ b/apps/encryption/l10n/pt_BR.js @@ -30,7 +30,7 @@ OC.L10N.register( "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Não é possível ler este arquivo, provavelmente este é um arquivo compartilhado. Por favor, pergunte o dono do arquivo para recompartilhar o arquivo com você.", "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'ownCloud basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Olá,\n\nO administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha '%s'.\n\nPor favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login.\n\n", "The share will expire on %s." : "O compartilhamento irá expirar em %s.", - "Cheers!" : "Saúde!", + "Cheers!" : "Saudações!", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Olá,<br><br>o administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha <strong>%s</strong>.<br><br>Por favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login..<br><br>", "Encrypt the home storage" : "Criptografar a pasta de armazenamento home", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Ativar essa opção de criptografia para todos os arquivos armazenados no armazenamento principal, caso contrário, apenas arquivos no armazenamento externo serão criptografados", diff --git a/apps/encryption/l10n/pt_BR.json b/apps/encryption/l10n/pt_BR.json index 794eb478c74..b3ce8c7238e 100644 --- a/apps/encryption/l10n/pt_BR.json +++ b/apps/encryption/l10n/pt_BR.json @@ -28,7 +28,7 @@ "Can not read this file, probably this is a shared file. Please ask the file owner to reshare the file with you." : "Não é possível ler este arquivo, provavelmente este é um arquivo compartilhado. Por favor, pergunte o dono do arquivo para recompartilhar o arquivo com você.", "Hey there,\n\nthe admin enabled server-side-encryption. Your files were encrypted using the password '%s'.\n\nPlease login to the web interface, go to the section 'ownCloud basic encryption module' of your personal settings and update your encryption password by entering this password into the 'old log-in password' field and your current login-password.\n\n" : "Olá,\n\nO administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha '%s'.\n\nPor favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login.\n\n", "The share will expire on %s." : "O compartilhamento irá expirar em %s.", - "Cheers!" : "Saúde!", + "Cheers!" : "Saudações!", "Hey there,<br><br>the admin enabled server-side-encryption. Your files were encrypted using the password <strong>%s</strong>.<br><br>Please login to the web interface, go to the section \"ownCloud basic encryption module\" of your personal settings and update your encryption password by entering this password into the \"old log-in password\" field and your current login-password.<br><br>" : "Olá,<br><br>o administrador habilitou criptografia-lado-servidor. Os seus arquivos foram criptografados usando a senha <strong>%s</strong>.<br><br>Por favor faça o login para a interface da Web, vá para a seção 'ownCloud módulo de criptografia básico' das suas definições pessoais e atualize sua senha de criptografia, inserindo esta senha no campo 'senha antiga de log-in' e sua atual senha-de-login..<br><br>", "Encrypt the home storage" : "Criptografar a pasta de armazenamento home", "Enabling this option encrypts all files stored on the main storage, otherwise only files on external storage will be encrypted" : "Ativar essa opção de criptografia para todos os arquivos armazenados no armazenamento principal, caso contrário, apenas arquivos no armazenamento externo serão criptografados", diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php index dbc0364a157..12e9008545a 100644 --- a/apps/encryption/lib/crypto/crypt.php +++ b/apps/encryption/lib/crypto/crypt.php @@ -30,7 +30,6 @@ use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Encryption\Exceptions\EncryptionFailedException; use OCA\Encryption\Exceptions\MultiKeyDecryptException; use OCA\Encryption\Exceptions\MultiKeyEncryptException; -use OCA\Encryption\Vendor\PBKDF2Fallback; use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\IConfig; use OCP\ILogger; @@ -293,28 +292,14 @@ class Crypt { $salt = hash('sha256', $uid . $instanceId . $instanceSecret, true); $keySize = $this->getKeySize($cipher); - if (function_exists('hash_pbkdf2')) { - $hash = hash_pbkdf2( - 'sha256', - $password, - $salt, - 100000, - $keySize, - true - ); - } else { - // fallback to 3rdparty lib for PHP <= 5.4. - // FIXME: Can be removed as soon as support for PHP 5.4 was dropped - $fallback = new PBKDF2Fallback(); - $hash = $fallback->pbkdf2( - 'sha256', - $password, - $salt, - 100000, - $keySize, - true - ); - } + $hash = hash_pbkdf2( + 'sha256', + $password, + $salt, + 100000, + $keySize, + true + ); return $hash; } diff --git a/apps/encryption/tests/lib/MigrationTest.php b/apps/encryption/tests/lib/MigrationTest.php index fc3d014345b..6df72ac1bf6 100644 --- a/apps/encryption/tests/lib/MigrationTest.php +++ b/apps/encryption/tests/lib/MigrationTest.php @@ -43,15 +43,18 @@ class MigrationTest extends \Test\TestCase { public static function setUpBeforeClass() { parent::setUpBeforeClass(); - \OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo'); - \OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo'); - \OC_User::createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo'); + \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER1, 'foo'); + \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER2, 'foo'); + \OC::$server->getUserManager()->createUser(self::TEST_ENCRYPTION_MIGRATION_USER3, 'foo'); } public static function tearDownAfterClass() { - \OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER1); - \OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER2); - \OC_User::deleteUser(self::TEST_ENCRYPTION_MIGRATION_USER3); + $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER1); + if ($user !== null) { $user->delete(); } + $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER2); + if ($user !== null) { $user->delete(); } + $user = \OC::$server->getUserManager()->get(self::TEST_ENCRYPTION_MIGRATION_USER3); + if ($user !== null) { $user->delete(); } parent::tearDownAfterClass(); } diff --git a/apps/encryption/vendor/pbkdf2fallback.php b/apps/encryption/vendor/pbkdf2fallback.php deleted file mode 100644 index ca579f8e7dc..00000000000 --- a/apps/encryption/vendor/pbkdf2fallback.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -/* Note; This class can be removed as soon as we drop PHP 5.4 support. - * - * - * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm). - * Copyright (c) 2013, Taylor Hornby - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -namespace OCA\Encryption\Vendor; - -class PBKDF2Fallback { - - /* - * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt - * $algorithm - The hash algorithm to use. Recommended: SHA256 - * $password - The password. - * $salt - A salt that is unique to the password. - * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000. - * $key_length - The length of the derived key in bytes. - * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise. - * Returns: A $key_length-byte key derived from the password and salt. - * - * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt - * - * This implementation of PBKDF2 was originally created by https://defuse.ca - * With improvements by http://www.variations-of-shadow.com - */ - public function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false) { - $algorithm = strtolower($algorithm); - if (!in_array($algorithm, hash_algos(), true)) - trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR); - if ($count <= 0 || $key_length <= 0) - trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR); - - if (function_exists("hash_pbkdf2")) { - // The output length is in NIBBLES (4-bits) if $raw_output is false! - if (!$raw_output) { - $key_length = $key_length * 2; - } - return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output); - } - - $hash_length = strlen(hash($algorithm, "", true)); - $block_count = ceil($key_length / $hash_length); - - $output = ""; - for ($i = 1; $i <= $block_count; $i++) { - // $i encoded as 4 bytes, big endian. - $last = $salt . pack("N", $i); - // first iteration - $last = $xorsum = hash_hmac($algorithm, $last, $password, true); - // perform the other $count - 1 iterations - for ($j = 1; $j < $count; $j++) { - $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true)); - } - $output .= $xorsum; - } - - if ($raw_output) - return substr($output, 0, $key_length); - else - return bin2hex(substr($output, 0, $key_length)); - } -} diff --git a/apps/federation/api/ocsauthapi.php b/apps/federation/api/ocsauthapi.php index 42d7113820d..b94550fd4f2 100644 --- a/apps/federation/api/ocsauthapi.php +++ b/apps/federation/api/ocsauthapi.php @@ -26,6 +26,7 @@ use OCA\Federation\DbHandler; use OCA\Federation\TrustedServers; use OCP\AppFramework\Http; use OCP\BackgroundJob\IJobList; +use OCP\ILogger; use OCP\IRequest; use OCP\Security\ISecureRandom; use OCP\Security\StringUtils; @@ -54,6 +55,9 @@ class OCSAuthAPI { /** @var DbHandler */ private $dbHandler; + /** @var ILogger */ + private $logger; + /** * OCSAuthAPI constructor. * @@ -62,19 +66,22 @@ class OCSAuthAPI { * @param IJobList $jobList * @param TrustedServers $trustedServers * @param DbHandler $dbHandler + * @param ILogger $logger */ public function __construct( IRequest $request, ISecureRandom $secureRandom, IJobList $jobList, TrustedServers $trustedServers, - DbHandler $dbHandler + DbHandler $dbHandler, + ILogger $logger ) { $this->request = $request; $this->secureRandom = $secureRandom; $this->jobList = $jobList; $this->trustedServers = $trustedServers; $this->dbHandler = $dbHandler; + $this->logger = $logger; } /** @@ -88,6 +95,7 @@ class OCSAuthAPI { $token = $this->request->getParam('token'); if ($this->trustedServers->isTrustedServer($url) === false) { + $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while requesting shared secret'); return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN); } @@ -95,6 +103,7 @@ class OCSAuthAPI { // token wins $localToken = $this->dbHandler->getToken($url); if (strcmp($localToken, $token) > 0) { + $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') presented lower token'); return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN); } @@ -120,10 +129,13 @@ class OCSAuthAPI { $url = $this->request->getParam('url'); $token = $this->request->getParam('token'); - if ( - $this->trustedServers->isTrustedServer($url) === false - || $this->isValidToken($url, $token) === false - ) { + if ($this->trustedServers->isTrustedServer($url) === false) { + $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while getting shared secret'); + return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN); + } + + if ($this->isValidToken($url, $token) === false) { + $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') didn\'t send a valid token (got ' . $token . ') while getting shared secret'); return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN); } @@ -139,7 +151,7 @@ class OCSAuthAPI { protected function isValidToken($url, $token) { $storedToken = $this->dbHandler->getToken($url); - return StringUtils::equals($storedToken, $token); + return hash_equals($storedToken, $token); } } diff --git a/apps/federation/appinfo/application.php b/apps/federation/appinfo/application.php index 172283536b4..45d88548b70 100644 --- a/apps/federation/appinfo/application.php +++ b/apps/federation/appinfo/application.php @@ -108,7 +108,8 @@ class Application extends \OCP\AppFramework\App { $server->getSecureRandom(), $server->getJobList(), $container->query('TrustedServers'), - $container->query('DbHandler') + $container->query('DbHandler'), + $server->getLogger() ); diff --git a/apps/federation/backgroundjob/getsharedsecret.php b/apps/federation/backgroundjob/getsharedsecret.php index eb55fa2d6ab..8aa8a08e07b 100644 --- a/apps/federation/backgroundjob/getsharedsecret.php +++ b/apps/federation/backgroundjob/getsharedsecret.php @@ -91,7 +91,7 @@ class GetSharedSecret extends QueuedJob{ $this->trustedServers = new TrustedServers( $this->dbHandler, \OC::$server->getHTTPClientService(), - \OC::$server->getLogger(), + $this->logger, $this->jobList, \OC::$server->getSecureRandom(), \OC::$server->getConfig() @@ -148,6 +148,7 @@ class GetSharedSecret extends QueuedJob{ } catch (ClientException $e) { $status = $e->getCode(); + $this->logger->logException($e); } // if we received a unexpected response we try again later diff --git a/apps/federation/backgroundjob/requestsharedsecret.php b/apps/federation/backgroundjob/requestsharedsecret.php index 24d8adada11..a1906d20823 100644 --- a/apps/federation/backgroundjob/requestsharedsecret.php +++ b/apps/federation/backgroundjob/requestsharedsecret.php @@ -60,6 +60,9 @@ class RequestSharedSecret extends QueuedJob { private $endPoint = '/ocs/v2.php/apps/federation/api/v1/request-shared-secret?format=json'; + /** @var ILogger */ + private $logger; + /** * RequestSharedSecret constructor. * @@ -80,13 +83,14 @@ class RequestSharedSecret extends QueuedJob { $this->jobList = $jobList ? $jobList : \OC::$server->getJobList(); $this->urlGenerator = $urlGenerator ? $urlGenerator : \OC::$server->getURLGenerator(); $this->dbHandler = $dbHandler ? $dbHandler : new DbHandler(\OC::$server->getDatabaseConnection(), \OC::$server->getL10N('federation')); + $this->logger = \OC::$server->getLogger(); if ($trustedServers) { $this->trustedServers = $trustedServers; } else { $this->trustedServers = new TrustedServers( $this->dbHandler, \OC::$server->getHTTPClientService(), - \OC::$server->getLogger(), + $this->logger, $this->jobList, \OC::$server->getSecureRandom(), \OC::$server->getConfig() @@ -142,6 +146,7 @@ class RequestSharedSecret extends QueuedJob { } catch (ClientException $e) { $status = $e->getCode(); + $this->logger->logException($e); } // if we received a unexpected response we try again later diff --git a/apps/federation/tests/api/ocsauthapitest.php b/apps/federation/tests/api/ocsauthapitest.php index a334686c24e..e6a95af8585 100644 --- a/apps/federation/tests/api/ocsauthapitest.php +++ b/apps/federation/tests/api/ocsauthapitest.php @@ -28,6 +28,7 @@ use OCA\Federation\API\OCSAuthAPI; use OCA\Federation\DbHandler; use OCA\Federation\TrustedServers; use OCP\AppFramework\Http; +use OCP\ILogger; use OCP\IRequest; use OCP\Security\ISecureRandom; use Test\TestCase; @@ -49,6 +50,9 @@ class OCSAuthAPITest extends TestCase { /** @var \PHPUnit_Framework_MockObject_MockObject | DbHandler */ private $dbHandler; + /** @var \PHPUnit_Framework_MockObject_MockObject | ILogger */ + private $logger; + /** @var OCSAuthApi */ private $ocsAuthApi; @@ -63,13 +67,16 @@ class OCSAuthAPITest extends TestCase { ->disableOriginalConstructor()->getMock(); $this->jobList = $this->getMockBuilder('OC\BackgroundJob\JobList') ->disableOriginalConstructor()->getMock(); + $this->logger = $this->getMockBuilder('OCP\ILogger') + ->disableOriginalConstructor()->getMock(); $this->ocsAuthApi = new OCSAuthAPI( $this->request, $this->secureRandom, $this->jobList, $this->trustedServers, - $this->dbHandler + $this->dbHandler, + $this->logger ); } @@ -136,7 +143,8 @@ class OCSAuthAPITest extends TestCase { $this->secureRandom, $this->jobList, $this->trustedServers, - $this->dbHandler + $this->dbHandler, + $this->logger ] )->setMethods(['isValidToken'])->getMock(); diff --git a/apps/files/admin.php b/apps/files/admin.php index f23f9b52698..a2092c600a7 100644 --- a/apps/files/admin.php +++ b/apps/files/admin.php @@ -33,7 +33,7 @@ $htaccessWorking=(getenv('htaccessWorking')=='true'); $upload_max_filesize = OC::$server->getIniWrapper()->getBytes('upload_max_filesize'); $post_max_size = OC::$server->getIniWrapper()->getBytes('post_max_size'); $maxUploadFilesize = OCP\Util::humanFileSize(min($upload_max_filesize, $post_max_size)); -if($_POST && OC_Util::isCallRegistered()) { +if($_POST && \OC::$server->getRequest()->passesCSRFCheck()) { if(isset($_POST['maxUploadSize'])) { if(($setMaxSize = OC_Files::setUploadLimit(OCP\Util::computerFileSize($_POST['maxUploadSize']))) !== false) { $maxUploadFilesize = OCP\Util::humanFileSize($setMaxSize); diff --git a/apps/files/appinfo/register_command.php b/apps/files/appinfo/register_command.php index 3042c259872..7e541ca26f2 100644 --- a/apps/files/appinfo/register_command.php +++ b/apps/files/appinfo/register_command.php @@ -20,5 +20,5 @@ * */ -$application->add(new OCA\Files\Command\Scan(OC_User::getManager())); +$application->add(new OCA\Files\Command\Scan(\OC::$server->getUserManager())); $application->add(new OCA\Files\Command\DeleteOrphanedFiles(\OC::$server->getDatabaseConnection())); diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php index 31ae555e041..7e00d8a2312 100644 --- a/apps/files/command/scan.php +++ b/apps/files/command/scan.php @@ -32,6 +32,7 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Helper\Table; class Scan extends Command { @@ -39,6 +40,15 @@ class Scan extends Command { * @var \OC\User\Manager $userManager */ private $userManager; + /** @var float */ + protected $execTime = 0; + /** @var int */ + protected $foldersCounter = 0; + /** @var int */ + protected $filesCounter = 0; + /** @var bool */ + protected $interrupted = false; + public function __construct(\OC\User\Manager $userManager) { $this->userManager = $userManager; @@ -64,7 +74,13 @@ class Scan extends Command { 'quiet', 'q', InputOption::VALUE_NONE, - 'suppress output' + 'suppress any output' + ) + ->addOption( + 'verbose', + '-v|vv|vvv', + InputOption::VALUE_NONE, + 'verbose the output' ) ->addOption( 'all', @@ -74,19 +90,31 @@ class Scan extends Command { ); } - protected function scanFiles($user, $path, $quiet, OutputInterface $output) { + protected function scanFiles($user, $path, $verbose, OutputInterface $output) { $scanner = new \OC\Files\Utils\Scanner($user, \OC::$server->getDatabaseConnection(), \OC::$server->getLogger()); - if (!$quiet) { + # printout and count + if ($verbose) { $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { $output->writeln("Scanning file <info>$path</info>"); + $this->filesCounter += 1; }); $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { $output->writeln("Scanning folder <info>$path</info>"); + $this->foldersCounter += 1; }); $scanner->listen('\OC\Files\Utils\Scanner', 'StorageNotAvailable', function (StorageNotAvailableException $e) use ($output) { $output->writeln("Error while scanning, storage not available (" . $e->getMessage() . ")"); }); + # count only + } else { + $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) { + $this->filesCounter += 1; + }); + $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) { + $this->foldersCounter += 1; + }); } + try { $scanner->scan($path); } catch (ForbiddenException $e) { @@ -95,6 +123,7 @@ class Scan extends Command { } } + protected function execute(InputInterface $input, OutputInterface $output) { $inputPath = $input->getOption('path'); if ($inputPath) { @@ -106,24 +135,173 @@ class Scan extends Command { } else { $users = $input->getArgument('user_id'); } - $quiet = $input->getOption('quiet'); - if (count($users) === 0) { $output->writeln("<error>Please specify the user id to scan, \"--all\" to scan for all users or \"--path=...\"</error>"); return; } + # no messaging level option means: no full printout but statistics + # $quiet means no print at all + # $verbose means full printout including statistics + # -q -v full stat + # 0 0 no yes + # 0 1 yes yes + # 1 -- no no (quiet overrules verbose) + $verbose = $input->getOption('verbose'); + $quiet = $input->getOption('quiet'); + # restrict the verbosity level to VERBOSITY_VERBOSE + if ($output->getVerbosity()>OutputInterface::VERBOSITY_VERBOSE) { + $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + } + if ($quiet) { + $verbose = false; + } + + $this->initTools(); + foreach ($users as $user) { if (is_object($user)) { $user = $user->getUID(); } $path = $inputPath ? $inputPath : '/' . $user; if ($this->userManager->userExists($user)) { - $this->scanFiles($user, $path, $quiet, $output); + # full: printout data if $verbose was set + $this->scanFiles($user, $path, $verbose, $output); } else { $output->writeln("<error>Unknown user $user</error>"); } } + + # stat: printout statistics if $quiet was not set + if (!$quiet) { + $this->presentStats($output); + } + + } + + + /** + * Checks if the command was interrupted by ctrl-c + */ + protected function checkForInterruption($output) { + if ($this->hasBeenInterrupted()) { + $this->presentResults($output); + exit; + } + } + + + /** + * Initialises some useful tools for the Command + */ + protected function initTools() { + // Start the timer + $this->execTime = -microtime(true); + // Convert PHP errors to exceptions + set_error_handler([$this, 'exceptionErrorHandler'], E_ALL); + + // Collect interrupts and notify the running command + pcntl_signal(SIGTERM, [$this, 'cancelOperation']); + pcntl_signal(SIGINT, [$this, 'cancelOperation']); + } + + + /** + * Changes the status of the command to "interrupted" + * + * Gives a chance to the command to properly terminate what it's doing + */ + private function cancelOperation() { + $this->interrupted = true; + } + + + /** + * Processes PHP errors as exceptions in order to be able to keep track of problems + * + * @see https://secure.php.net/manual/en/function.set-error-handler.php + * + * @param int $severity the level of the error raised + * @param string $message + * @param string $file the filename that the error was raised in + * @param int $line the line number the error was raised + * + * @throws \ErrorException + */ + public function exceptionErrorHandler($severity, $message, $file, $line) { + if (!(error_reporting() & $severity)) { + // This error code is not included in error_reporting + return; + } + throw new \ErrorException($message, 0, $severity, $file, $line); } + + + /** + * @return bool + */ + protected function hasBeenInterrupted() { + $cancelled = false; + pcntl_signal_dispatch(); + if ($this->interrupted) { + $cancelled = true; + } + + return $cancelled; + } + + + /** + * @param OutputInterface $output + */ + protected function presentStats(OutputInterface $output) { + // Stop the timer + $this->execTime += microtime(true); + $output->writeln(""); + + $headers = [ + 'Folders', 'Files', 'Elapsed time' + ]; + + $this->showSummary($headers, null, $output); + } + + + /** + * Shows a summary of operations + * + * @param string[] $headers + * @param string[] $rows + * @param OutputInterface $output + */ + protected function showSummary($headers, $rows, OutputInterface $output) { + $niceDate = $this->formatExecTime(); + if (!$rows) { + $rows = [ + $this->foldersCounter, + $this->filesCounter, + $niceDate, + ]; + } + $table = new Table($output); + $table + ->setHeaders($headers) + ->setRows([$rows]); + $table->render(); + } + + + /** + * Formats microtime into a human readable format + * + * @return string + */ + protected function formatExecTime() { + list($secs, $tens) = explode('.', sprintf("%.1f", ($this->execTime))); + $niceDate = date('H:i:s', $secs) . '.' . $tens; + + return $niceDate; + } + } diff --git a/apps/files/download.php b/apps/files/download.php index b0628e394be..b058f0ebf5b 100644 --- a/apps/files/download.php +++ b/apps/files/download.php @@ -39,7 +39,7 @@ if(!\OC\Files\Filesystem::file_exists($filename)) { exit; } -$ftype=\OC_Helper::getSecureMimeType(\OC\Files\Filesystem::getMimeType( $filename )); +$ftype=\OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType( $filename )); header('Content-Type:'.$ftype); OCP\Response::setContentDispositionHeader(basename($filename), 'attachment'); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 871a2149c88..67125f1570f 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -617,7 +617,16 @@ this.registerAction({ name: 'Delete', - displayName: t('files', 'Delete'), + displayName: function(context) { + var mountType = context.$file.attr('data-mounttype'); + var deleteTitle = t('files', 'Delete'); + if (mountType === 'external-root') { + deleteTitle = t('files', 'Disconnect storage'); + } else if (mountType === 'shared-root') { + deleteTitle = t('files', 'Unshare'); + } + return deleteTitle; + }, mime: 'all', order: 1000, // permission is READ because we show a hint instead if there is no permission @@ -668,8 +677,9 @@ * @typedef {Object} OCA.Files.FileAction * * @property {String} name identifier of the action - * @property {String} displayName display name of the action, defaults - * to the name given in name property + * @property {(String|OCA.Files.FileActions~displayNameFunction)} displayName + * display name string for the action, or function that returns the display name. + * Defaults to the name given in name property * @property {String} mime mime type * @property {int} permissions permissions * @property {(Function|String)} icon icon path to the icon or function @@ -703,6 +713,16 @@ */ /** + * Display name function for actions. + * The function returns the display name of the action using + * the given context information.. + * + * @callback OCA.Files.FileActions~displayNameFunction + * @param {OCA.Files.FileActionContext} context action context + * @return {String} display name + */ + + /** * Action handler function for file actions * * @callback OCA.Files.FileActions~actionHandler diff --git a/apps/files/js/fileactionsmenu.js b/apps/files/js/fileactionsmenu.js index 67cbb48c965..9e51d8f1f1f 100644 --- a/apps/files/js/fileactionsmenu.js +++ b/apps/files/js/fileactionsmenu.js @@ -81,6 +81,7 @@ * Renders the menu with the currently set items */ render: function() { + var self = this; var fileActions = this._context.fileActions; var actions = fileActions.getActions( fileActions.getCurrentMimeType(), @@ -100,6 +101,13 @@ (!defaultAction || actionSpec.name !== defaultAction.name) ); }); + items = _.map(items, function(item) { + if (_.isFunction(item.displayName)) { + item = _.extend({}, item); + item.displayName = item.displayName(self._context); + } + return item; + }); items = items.sort(function(actionA, actionB) { var orderA = actionA.order || 0; var orderB = actionB.order || 0; diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 672c39a8bb1..0dbf4f938bb 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -990,13 +990,16 @@ } if (fileData.mountType) { - // FIXME: HACK: detect shared-root - if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared') { - // if parent folder isn't share, assume the displayed folder is a share root - fileData.mountType = 'shared-root'; - } else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external') { - // if parent folder isn't external, assume the displayed folder is the external storage root - fileData.mountType = 'external-root'; + // dirInfo (parent) only exist for the "real" file list + if (this.dirInfo.id) { + // FIXME: HACK: detect shared-root + if (fileData.mountType === 'shared' && this.dirInfo.mountType !== 'shared' && this.dirInfo.mountType !== 'shared-root') { + // if parent folder isn't share, assume the displayed folder is a share root + fileData.mountType = 'shared-root'; + } else if (fileData.mountType === 'external' && this.dirInfo.mountType !== 'external' && this.dirInfo.mountType !== 'external-root') { + // if parent folder isn't external, assume the displayed folder is the external storage root + fileData.mountType = 'external-root'; + } } tr.attr('data-mounttype', fileData.mountType); } @@ -1065,7 +1068,7 @@ nameSpan.tooltip({placement: 'right'}); } // dirs can show the number of uploaded files - if (mime !== 'httpd/unix-directory') { + if (mime === 'httpd/unix-directory') { linkElem.append($('<span></span>').attr({ 'class': 'uploadtext', 'currentUploads': 0 @@ -1380,7 +1383,7 @@ * Returns list of webdav properties to request */ _getWebdavProperties: function() { - return this.filesClient.getPropfindProperties(); + return [].concat(this.filesClient.getPropfindProperties()); }, /** @@ -2069,7 +2072,7 @@ */ showFileBusyState: function(files, state) { var self = this; - if (!_.isArray(files)) { + if (!_.isArray(files) && !files.is) { files = [files]; } @@ -2077,10 +2080,13 @@ state = true; } - _.each(files, function($tr) { + _.each(files, function(fileName) { // jquery element already ? - if (!$tr.is) { - $tr = self.findFileEl($tr); + var $tr; + if (_.isString(fileName)) { + $tr = self.findFileEl(fileName); + } else { + $tr = $(fileName); } var $thumbEl = $tr.find('.thumbnail'); @@ -2465,6 +2471,7 @@ } }); fileUploadStart.on('fileuploadadd', function(e, data) { + console.log('XXXXXXX'); OC.Upload.log('filelist handle fileuploadadd', e, data); //finish delete if we are uploading a deleted file @@ -2484,8 +2491,7 @@ var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 1) { - var img = OC.imagePath('core', 'loading.gif'); - data.context.find('.thumbnail').css('background-image', 'url(' + img + ')'); + self.showFileBusyState(uploadText.closest('tr'), true); uploadText.text(translatedText); uploadText.show(); } else { @@ -2523,8 +2529,7 @@ uploadText.attr('currentUploads', currentUploads); var translatedText = n('files', 'Uploading %n file', 'Uploading %n files', currentUploads); if (currentUploads === 0) { - var img = OC.imagePath('core', 'filetypes/folder'); - data.context.find('.thumbnail').css('background-image', 'url(' + img + ')'); + self.showFileBusyState(uploadText.closest('tr'), false); uploadText.text(translatedText); uploadText.hide(); } else { @@ -2601,18 +2606,15 @@ } } }); - fileUploadStart.on('fileuploadstop', function(e, data) { - OC.Upload.log('filelist handle fileuploadstop', e, data); + fileUploadStart.on('fileuploadstop', function() { + OC.Upload.log('filelist handle fileuploadstop'); + + //cleanup uploading to a dir + var uploadText = self.$fileList.find('tr .uploadtext'); + self.showFileBusyState(uploadText.closest('tr'), false); + uploadText.fadeOut(); + uploadText.attr('currentUploads', 0); - //if user pressed cancel hide upload chrome - if (data.errorThrown === 'abort') { - //cleanup uploading to a dir - var uploadText = $('tr .uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder'); - uploadText.parents('td.filename').find('.thumbnail').css('background-image', 'url(' + img + ')'); - uploadText.fadeOut(); - uploadText.attr('currentUploads', 0); - } self.updateStorageStatistics(); }); fileUploadStart.on('fileuploadfail', function(e, data) { @@ -2621,9 +2623,8 @@ //if user pressed cancel hide upload chrome if (data.errorThrown === 'abort') { //cleanup uploading to a dir - var uploadText = $('tr .uploadtext'); - var img = OC.imagePath('core', 'filetypes/folder'); - uploadText.parents('td.filename').find('.thumbnail').css('background-image', 'url(' + img + ')'); + var uploadText = self.$fileList.find('tr .uploadtext'); + self.showFileBusyState(uploadText.closest('tr'), false); uploadText.fadeOut(); uploadText.attr('currentUploads', 0); } diff --git a/apps/files/l10n/ar.js b/apps/files/l10n/ar.js index 953267393b2..021c6b52b32 100644 --- a/apps/files/l10n/ar.js +++ b/apps/files/l10n/ar.js @@ -34,6 +34,7 @@ OC.L10N.register( "Download" : "تحميل", "Rename" : "إعادة التسمية", "Delete" : "حذف ", + "Unshare" : "إلغاء المشاركة", "Details" : "تفاصيل", "Select" : "اختار", "Pending" : "قيد الانتظار", diff --git a/apps/files/l10n/ar.json b/apps/files/l10n/ar.json index 245fe9a0b9e..53385598c24 100644 --- a/apps/files/l10n/ar.json +++ b/apps/files/l10n/ar.json @@ -32,6 +32,7 @@ "Download" : "تحميل", "Rename" : "إعادة التسمية", "Delete" : "حذف ", + "Unshare" : "إلغاء المشاركة", "Details" : "تفاصيل", "Select" : "اختار", "Pending" : "قيد الانتظار", diff --git a/apps/files/l10n/ast.js b/apps/files/l10n/ast.js index 8fc63c1ac35..637adcebc75 100644 --- a/apps/files/l10n/ast.js +++ b/apps/files/l10n/ast.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Descargar", "Rename" : "Renomar", "Delete" : "Desaniciar", + "Disconnect storage" : "Desconeutar almacenamientu", + "Unshare" : "Dexar de compartir", "Details" : "Detalles", "Select" : "Esbillar", "Pending" : "Pendiente", diff --git a/apps/files/l10n/ast.json b/apps/files/l10n/ast.json index 33c119771f7..352fbd9fac3 100644 --- a/apps/files/l10n/ast.json +++ b/apps/files/l10n/ast.json @@ -32,6 +32,8 @@ "Download" : "Descargar", "Rename" : "Renomar", "Delete" : "Desaniciar", + "Disconnect storage" : "Desconeutar almacenamientu", + "Unshare" : "Dexar de compartir", "Details" : "Detalles", "Select" : "Esbillar", "Pending" : "Pendiente", diff --git a/apps/files/l10n/az.js b/apps/files/l10n/az.js index cf7d81063ce..302bb051407 100644 --- a/apps/files/l10n/az.js +++ b/apps/files/l10n/az.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Yüklə", "Rename" : "Adı dəyiş", "Delete" : "Sil", + "Disconnect storage" : "Daşıyıcını ayır", + "Unshare" : "Paylaşımı durdur", "Details" : "Detallar", "Select" : "Seç", "Pending" : "Gözləmə", diff --git a/apps/files/l10n/az.json b/apps/files/l10n/az.json index f2c45fda2f7..b213c86cb2f 100644 --- a/apps/files/l10n/az.json +++ b/apps/files/l10n/az.json @@ -32,6 +32,8 @@ "Download" : "Yüklə", "Rename" : "Adı dəyiş", "Delete" : "Sil", + "Disconnect storage" : "Daşıyıcını ayır", + "Unshare" : "Paylaşımı durdur", "Details" : "Detallar", "Select" : "Seç", "Pending" : "Gözləmə", diff --git a/apps/files/l10n/bn_BD.js b/apps/files/l10n/bn_BD.js index b968a225da7..0ee56888df0 100644 --- a/apps/files/l10n/bn_BD.js +++ b/apps/files/l10n/bn_BD.js @@ -26,6 +26,7 @@ OC.L10N.register( "Download" : "ডাউনলোড", "Rename" : "পূনঃনামকরণ", "Delete" : "মুছে", + "Unshare" : "ভাগাভাগি বাতিল ", "Details" : "বিস্তারিত", "Pending" : "মুলতুবি", "Name" : "রাম", diff --git a/apps/files/l10n/bn_BD.json b/apps/files/l10n/bn_BD.json index bed559330ad..15ec2b4b826 100644 --- a/apps/files/l10n/bn_BD.json +++ b/apps/files/l10n/bn_BD.json @@ -24,6 +24,7 @@ "Download" : "ডাউনলোড", "Rename" : "পূনঃনামকরণ", "Delete" : "মুছে", + "Unshare" : "ভাগাভাগি বাতিল ", "Details" : "বিস্তারিত", "Pending" : "মুলতুবি", "Name" : "রাম", diff --git a/apps/files/l10n/bs.js b/apps/files/l10n/bs.js index 9da51ece871..278b6371771 100644 --- a/apps/files/l10n/bs.js +++ b/apps/files/l10n/bs.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Preuzmi", "Rename" : "Preimenuj", "Delete" : "Izbriši", + "Disconnect storage" : "Diskonektuj pohranu", + "Unshare" : "Prestani dijeliti", "Select" : "Izaberi", "Pending" : "Na čekanju", "Unable to determine date" : "Nemoguće odrediti datum", diff --git a/apps/files/l10n/bs.json b/apps/files/l10n/bs.json index 095fe47048d..678bedcbe36 100644 --- a/apps/files/l10n/bs.json +++ b/apps/files/l10n/bs.json @@ -32,6 +32,8 @@ "Download" : "Preuzmi", "Rename" : "Preimenuj", "Delete" : "Izbriši", + "Disconnect storage" : "Diskonektuj pohranu", + "Unshare" : "Prestani dijeliti", "Select" : "Izaberi", "Pending" : "Na čekanju", "Unable to determine date" : "Nemoguće odrediti datum", diff --git a/apps/files/l10n/de_AT.js b/apps/files/l10n/de_AT.js index d7a77f9ee40..783ff4aaa6b 100644 --- a/apps/files/l10n/de_AT.js +++ b/apps/files/l10n/de_AT.js @@ -5,6 +5,7 @@ OC.L10N.register( "Files" : "Dateien", "Download" : "Herunterladen", "Delete" : "Löschen", + "Unshare" : "Teilung zurücknehmen", "Details" : "Details", "New folder" : "Neuer Ordner", "Upload" : "Hochladen", diff --git a/apps/files/l10n/de_AT.json b/apps/files/l10n/de_AT.json index 7381a96665a..2d54751bdf6 100644 --- a/apps/files/l10n/de_AT.json +++ b/apps/files/l10n/de_AT.json @@ -3,6 +3,7 @@ "Files" : "Dateien", "Download" : "Herunterladen", "Delete" : "Löschen", + "Unshare" : "Teilung zurücknehmen", "Details" : "Details", "New folder" : "Neuer Ordner", "Upload" : "Hochladen", diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js index d65d062c75d..517799c1bcb 100644 --- a/apps/files/l10n/de_DE.js +++ b/apps/files/l10n/de_DE.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Herunterladen", "Rename" : "Umbenennen", "Delete" : "Löschen", + "Disconnect storage" : "Speicher trennen", + "Unshare" : "Freigabe aufheben", "Details" : "Details", "Select" : "Auswählen", "Pending" : "Ausstehend", diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json index 79467fc7642..90e499bec2f 100644 --- a/apps/files/l10n/de_DE.json +++ b/apps/files/l10n/de_DE.json @@ -32,6 +32,8 @@ "Download" : "Herunterladen", "Rename" : "Umbenennen", "Delete" : "Löschen", + "Disconnect storage" : "Speicher trennen", + "Unshare" : "Freigabe aufheben", "Details" : "Details", "Select" : "Auswählen", "Pending" : "Ausstehend", diff --git a/apps/files/l10n/el.js b/apps/files/l10n/el.js index f60e842199f..265381cd670 100644 --- a/apps/files/l10n/el.js +++ b/apps/files/l10n/el.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Λήψη", "Rename" : "Μετονομασία", "Delete" : "Διαγραφή", + "Disconnect storage" : "Αποσυνδεδεμένος αποθηκευτικός χώρος", + "Unshare" : "Διακοπή διαμοιρασμού", "Details" : "Λεπτομέρειες", "Select" : "Επιλογή", "Pending" : "Εκκρεμεί", diff --git a/apps/files/l10n/el.json b/apps/files/l10n/el.json index 15cb56c87d1..1f0d997b3da 100644 --- a/apps/files/l10n/el.json +++ b/apps/files/l10n/el.json @@ -32,6 +32,8 @@ "Download" : "Λήψη", "Rename" : "Μετονομασία", "Delete" : "Διαγραφή", + "Disconnect storage" : "Αποσυνδεδεμένος αποθηκευτικός χώρος", + "Unshare" : "Διακοπή διαμοιρασμού", "Details" : "Λεπτομέρειες", "Select" : "Επιλογή", "Pending" : "Εκκρεμεί", diff --git a/apps/files/l10n/en_GB.js b/apps/files/l10n/en_GB.js index a190c45e846..96c5acf9170 100644 --- a/apps/files/l10n/en_GB.js +++ b/apps/files/l10n/en_GB.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Download", "Rename" : "Rename", "Delete" : "Delete", + "Disconnect storage" : "Disconnect storage", + "Unshare" : "Unshare", "Details" : "Details", "Select" : "Select", "Pending" : "Pending", diff --git a/apps/files/l10n/en_GB.json b/apps/files/l10n/en_GB.json index ca8a60ec73b..4b0762f8b70 100644 --- a/apps/files/l10n/en_GB.json +++ b/apps/files/l10n/en_GB.json @@ -32,6 +32,8 @@ "Download" : "Download", "Rename" : "Rename", "Delete" : "Delete", + "Disconnect storage" : "Disconnect storage", + "Unshare" : "Unshare", "Details" : "Details", "Select" : "Select", "Pending" : "Pending", diff --git a/apps/files/l10n/eo.js b/apps/files/l10n/eo.js index dbdd0422911..398d2f669b0 100644 --- a/apps/files/l10n/eo.js +++ b/apps/files/l10n/eo.js @@ -28,6 +28,7 @@ OC.L10N.register( "Download" : "Elŝuti", "Rename" : "Alinomigi", "Delete" : "Forigi", + "Unshare" : "Malkunhavigi", "Details" : "Detaloj", "Select" : "Elekti", "Pending" : "Traktotaj", diff --git a/apps/files/l10n/eo.json b/apps/files/l10n/eo.json index b858ccece4d..2a76d0ab2c9 100644 --- a/apps/files/l10n/eo.json +++ b/apps/files/l10n/eo.json @@ -26,6 +26,7 @@ "Download" : "Elŝuti", "Rename" : "Alinomigi", "Delete" : "Forigi", + "Unshare" : "Malkunhavigi", "Details" : "Detaloj", "Select" : "Elekti", "Pending" : "Traktotaj", diff --git a/apps/files/l10n/es.js b/apps/files/l10n/es.js index 7bc628b8f9b..4b0ddbb7265 100644 --- a/apps/files/l10n/es.js +++ b/apps/files/l10n/es.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Descargar", "Rename" : "Renombrar", "Delete" : "Eliminar", + "Disconnect storage" : "Desconectar almacenamiento", + "Unshare" : "Dejar de compartir", "Details" : "Detalles", "Select" : "Seleccionar", "Pending" : "Pendiente", @@ -97,6 +99,7 @@ OC.L10N.register( "Maximum upload size" : "Tamaño máximo de subida", "max. possible: " : "máx. posible:", "Save" : "Guardar", + "Missing permissions to edit from here." : "Faltan permisos para poder editar desde aquí.", "Settings" : "Ajustes", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Use esta URL <a href=\"%s\" target=\"_blank\">para acceder via WebDAV</a>", diff --git a/apps/files/l10n/es.json b/apps/files/l10n/es.json index 27ebcb1da46..09411bc5b2a 100644 --- a/apps/files/l10n/es.json +++ b/apps/files/l10n/es.json @@ -32,6 +32,8 @@ "Download" : "Descargar", "Rename" : "Renombrar", "Delete" : "Eliminar", + "Disconnect storage" : "Desconectar almacenamiento", + "Unshare" : "Dejar de compartir", "Details" : "Detalles", "Select" : "Seleccionar", "Pending" : "Pendiente", @@ -95,6 +97,7 @@ "Maximum upload size" : "Tamaño máximo de subida", "max. possible: " : "máx. posible:", "Save" : "Guardar", + "Missing permissions to edit from here." : "Faltan permisos para poder editar desde aquí.", "Settings" : "Ajustes", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Use esta URL <a href=\"%s\" target=\"_blank\">para acceder via WebDAV</a>", diff --git a/apps/files/l10n/es_MX.js b/apps/files/l10n/es_MX.js index 0e4dbddeda9..9d4924d1d50 100644 --- a/apps/files/l10n/es_MX.js +++ b/apps/files/l10n/es_MX.js @@ -28,6 +28,7 @@ OC.L10N.register( "Download" : "Descargar", "Rename" : "Renombrar", "Delete" : "Eliminar", + "Unshare" : "Dejar de compartir", "Details" : "Detalles", "Pending" : "Pendiente", "Name" : "Nombre", diff --git a/apps/files/l10n/es_MX.json b/apps/files/l10n/es_MX.json index cb903ddd9dc..9abf30f0c7c 100644 --- a/apps/files/l10n/es_MX.json +++ b/apps/files/l10n/es_MX.json @@ -26,6 +26,7 @@ "Download" : "Descargar", "Rename" : "Renombrar", "Delete" : "Eliminar", + "Unshare" : "Dejar de compartir", "Details" : "Detalles", "Pending" : "Pendiente", "Name" : "Nombre", diff --git a/apps/files/l10n/et_EE.js b/apps/files/l10n/et_EE.js index f136bd300b1..f577fa663fc 100644 --- a/apps/files/l10n/et_EE.js +++ b/apps/files/l10n/et_EE.js @@ -34,12 +34,19 @@ OC.L10N.register( "Download" : "Lae alla", "Rename" : "Nimeta ümber", "Delete" : "Kustuta", + "Disconnect storage" : "Ühenda andmehoidla lahti.", + "Unshare" : "Lõpeta jagamine", "Details" : "Üksikasjad", "Select" : "Vali", "Pending" : "Ootel", "Unable to determine date" : "Kuupäeva tuvastamine ei õnnestunud", "This operation is forbidden" : "See toiming on keelatud", "This directory is unavailable, please check the logs or contact the administrator" : "See kaust pole saadaval. Palun kontrolli logifaile või võta ühendust administraatoriga", + "Could not move \"{file}\"" : "\"{file}\" liigutamine ebaõnnestus", + "{newName} already exists" : "{newName} on juba olemas", + "Could not rename \"{fileName}\"" : "\"{fileName}\" ümbernimetamine ebaõnnestus", + "Could not create file \"{file}\"" : "Faili \"{file}\" loomine ebaõnnestus", + "Error deleting file \"{fileName}\"." : "Tõrge faili \"{fileName}\" kustutamisel.", "No entries in this folder match '{filter}'" : "Ükski sissekanne ei kattu filtriga '{filter}'", "Name" : "Nimi", "Size" : "Suurus", @@ -62,6 +69,7 @@ OC.L10N.register( "Favorite" : "Lemmik", "Folder" : "Kaust", "New folder" : "Uus kaust", + "{newname} already exists" : "{newname} on juba olemas", "Upload" : "Lae üles", "An error occurred while trying to update the tags" : "Siltide uuendamisel tekkis tõrge", "A new file or folder has been <strong>created</strong>" : "Uus fail või kataloog on <strong>loodud</strong>", @@ -96,6 +104,7 @@ OC.L10N.register( "Currently scanning" : "Praegu skännimisel", "No favorites" : "Lemmikuid pole", "Files and folders you mark as favorite will show up here" : "Siin kuvatakse faile ja kaustasid, mille oled märkinud lemmikuteks", - "Text file" : "Tekstifail" + "Text file" : "Tekstifail", + "New text file.txt" : "Uus tekstifail.txt" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files/l10n/et_EE.json b/apps/files/l10n/et_EE.json index 6e1961770c9..078b8d2d6d7 100644 --- a/apps/files/l10n/et_EE.json +++ b/apps/files/l10n/et_EE.json @@ -32,12 +32,19 @@ "Download" : "Lae alla", "Rename" : "Nimeta ümber", "Delete" : "Kustuta", + "Disconnect storage" : "Ühenda andmehoidla lahti.", + "Unshare" : "Lõpeta jagamine", "Details" : "Üksikasjad", "Select" : "Vali", "Pending" : "Ootel", "Unable to determine date" : "Kuupäeva tuvastamine ei õnnestunud", "This operation is forbidden" : "See toiming on keelatud", "This directory is unavailable, please check the logs or contact the administrator" : "See kaust pole saadaval. Palun kontrolli logifaile või võta ühendust administraatoriga", + "Could not move \"{file}\"" : "\"{file}\" liigutamine ebaõnnestus", + "{newName} already exists" : "{newName} on juba olemas", + "Could not rename \"{fileName}\"" : "\"{fileName}\" ümbernimetamine ebaõnnestus", + "Could not create file \"{file}\"" : "Faili \"{file}\" loomine ebaõnnestus", + "Error deleting file \"{fileName}\"." : "Tõrge faili \"{fileName}\" kustutamisel.", "No entries in this folder match '{filter}'" : "Ükski sissekanne ei kattu filtriga '{filter}'", "Name" : "Nimi", "Size" : "Suurus", @@ -60,6 +67,7 @@ "Favorite" : "Lemmik", "Folder" : "Kaust", "New folder" : "Uus kaust", + "{newname} already exists" : "{newname} on juba olemas", "Upload" : "Lae üles", "An error occurred while trying to update the tags" : "Siltide uuendamisel tekkis tõrge", "A new file or folder has been <strong>created</strong>" : "Uus fail või kataloog on <strong>loodud</strong>", @@ -94,6 +102,7 @@ "Currently scanning" : "Praegu skännimisel", "No favorites" : "Lemmikuid pole", "Files and folders you mark as favorite will show up here" : "Siin kuvatakse faile ja kaustasid, mille oled märkinud lemmikuteks", - "Text file" : "Tekstifail" + "Text file" : "Tekstifail", + "New text file.txt" : "Uus tekstifail.txt" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files/l10n/eu.js b/apps/files/l10n/eu.js index 9add1516c46..d7b9935ac5e 100644 --- a/apps/files/l10n/eu.js +++ b/apps/files/l10n/eu.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Deskargatu", "Rename" : "Berrizendatu", "Delete" : "Ezabatu", + "Disconnect storage" : "Deskonektatu biltegia", + "Unshare" : "Ez elkarbanatu", "Details" : "Xehetasunak", "Select" : "hautatu", "Pending" : "Zain", diff --git a/apps/files/l10n/eu.json b/apps/files/l10n/eu.json index 96943f33a9a..71dc13b5741 100644 --- a/apps/files/l10n/eu.json +++ b/apps/files/l10n/eu.json @@ -32,6 +32,8 @@ "Download" : "Deskargatu", "Rename" : "Berrizendatu", "Delete" : "Ezabatu", + "Disconnect storage" : "Deskonektatu biltegia", + "Unshare" : "Ez elkarbanatu", "Details" : "Xehetasunak", "Select" : "hautatu", "Pending" : "Zain", diff --git a/apps/files/l10n/fa.js b/apps/files/l10n/fa.js index 46cbbc376ab..5f6c49e742a 100644 --- a/apps/files/l10n/fa.js +++ b/apps/files/l10n/fa.js @@ -34,6 +34,7 @@ OC.L10N.register( "Download" : "دانلود", "Rename" : "تغییرنام", "Delete" : "حذف", + "Unshare" : "لغو اشتراک", "Details" : "جزئیات", "Select" : "انتخاب", "Pending" : "در انتظار", diff --git a/apps/files/l10n/fa.json b/apps/files/l10n/fa.json index b9e32d69e37..a4d0add25ba 100644 --- a/apps/files/l10n/fa.json +++ b/apps/files/l10n/fa.json @@ -32,6 +32,7 @@ "Download" : "دانلود", "Rename" : "تغییرنام", "Delete" : "حذف", + "Unshare" : "لغو اشتراک", "Details" : "جزئیات", "Select" : "انتخاب", "Pending" : "در انتظار", diff --git a/apps/files/l10n/fi_FI.js b/apps/files/l10n/fi_FI.js index 8bebb81fd35..5e1327f7556 100644 --- a/apps/files/l10n/fi_FI.js +++ b/apps/files/l10n/fi_FI.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Lataa", "Rename" : "Nimeä uudelleen", "Delete" : "Poista", + "Disconnect storage" : "Katkaise yhteys tallennustilaan", + "Unshare" : "Peru jakaminen", "Details" : "Tiedot", "Select" : "Valitse", "Pending" : "Odottaa", diff --git a/apps/files/l10n/fi_FI.json b/apps/files/l10n/fi_FI.json index 64b0a886b73..baf6c5d4499 100644 --- a/apps/files/l10n/fi_FI.json +++ b/apps/files/l10n/fi_FI.json @@ -32,6 +32,8 @@ "Download" : "Lataa", "Rename" : "Nimeä uudelleen", "Delete" : "Poista", + "Disconnect storage" : "Katkaise yhteys tallennustilaan", + "Unshare" : "Peru jakaminen", "Details" : "Tiedot", "Select" : "Valitse", "Pending" : "Odottaa", diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js index 6af58e93094..16f95572933 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Télécharger", "Rename" : "Renommer", "Delete" : "Supprimer", + "Disconnect storage" : "Déconnecter ce support de stockage", + "Unshare" : "Ne plus partager", "Details" : "Détails", "Select" : "Sélectionner", "Pending" : "En attente", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index ba25b77e3df..3f8cdf95538 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -32,6 +32,8 @@ "Download" : "Télécharger", "Rename" : "Renommer", "Delete" : "Supprimer", + "Disconnect storage" : "Déconnecter ce support de stockage", + "Unshare" : "Ne plus partager", "Details" : "Détails", "Select" : "Sélectionner", "Pending" : "En attente", diff --git a/apps/files/l10n/ia.js b/apps/files/l10n/ia.js index a1548e6b732..8310334287f 100644 --- a/apps/files/l10n/ia.js +++ b/apps/files/l10n/ia.js @@ -10,6 +10,7 @@ OC.L10N.register( "Close" : "Clauder", "Download" : "Discargar", "Delete" : "Deler", + "Unshare" : "Leva compartir", "Name" : "Nomine", "Size" : "Dimension", "Modified" : "Modificate", diff --git a/apps/files/l10n/ia.json b/apps/files/l10n/ia.json index bbc3f4bc02a..eee4980e34a 100644 --- a/apps/files/l10n/ia.json +++ b/apps/files/l10n/ia.json @@ -8,6 +8,7 @@ "Close" : "Clauder", "Download" : "Discargar", "Delete" : "Deler", + "Unshare" : "Leva compartir", "Name" : "Nomine", "Size" : "Dimension", "Modified" : "Modificate", diff --git a/apps/files/l10n/id.js b/apps/files/l10n/id.js index 93ec698cfa4..07b33195d48 100644 --- a/apps/files/l10n/id.js +++ b/apps/files/l10n/id.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Unduh", "Rename" : "Ubah nama", "Delete" : "Hapus", + "Disconnect storage" : "Memutuskan penyimpaan", + "Unshare" : "Batalkan berbagi", "Details" : "Rincian", "Select" : "Pilih", "Pending" : "Tertunda", diff --git a/apps/files/l10n/id.json b/apps/files/l10n/id.json index dca3e7bd163..efe7c4559e7 100644 --- a/apps/files/l10n/id.json +++ b/apps/files/l10n/id.json @@ -32,6 +32,8 @@ "Download" : "Unduh", "Rename" : "Ubah nama", "Delete" : "Hapus", + "Disconnect storage" : "Memutuskan penyimpaan", + "Unshare" : "Batalkan berbagi", "Details" : "Rincian", "Select" : "Pilih", "Pending" : "Tertunda", diff --git a/apps/files/l10n/is.js b/apps/files/l10n/is.js index 967e205145f..68b0ad573a5 100644 --- a/apps/files/l10n/is.js +++ b/apps/files/l10n/is.js @@ -17,6 +17,7 @@ OC.L10N.register( "Download" : "Niðurhal", "Rename" : "Endurskýra", "Delete" : "Eyða", + "Unshare" : "Hætta deilingu", "Select" : "Velja", "Pending" : "Bíður", "Name" : "Nafn", diff --git a/apps/files/l10n/is.json b/apps/files/l10n/is.json index caff3b037d5..44153e94106 100644 --- a/apps/files/l10n/is.json +++ b/apps/files/l10n/is.json @@ -15,6 +15,7 @@ "Download" : "Niðurhal", "Rename" : "Endurskýra", "Delete" : "Eyða", + "Unshare" : "Hætta deilingu", "Select" : "Velja", "Pending" : "Bíður", "Name" : "Nafn", diff --git a/apps/files/l10n/it.js b/apps/files/l10n/it.js index 93a013492a2..d3a567e1d18 100644 --- a/apps/files/l10n/it.js +++ b/apps/files/l10n/it.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Scarica", "Rename" : "Rinomina", "Delete" : "Elimina", + "Disconnect storage" : "Disconnetti archiviazione", + "Unshare" : "Rimuovi condivisione", "Details" : "Dettagli", "Select" : "Seleziona", "Pending" : "In corso", diff --git a/apps/files/l10n/it.json b/apps/files/l10n/it.json index 25d3834b0a3..611de295147 100644 --- a/apps/files/l10n/it.json +++ b/apps/files/l10n/it.json @@ -32,6 +32,8 @@ "Download" : "Scarica", "Rename" : "Rinomina", "Delete" : "Elimina", + "Disconnect storage" : "Disconnetti archiviazione", + "Unshare" : "Rimuovi condivisione", "Details" : "Dettagli", "Select" : "Seleziona", "Pending" : "In corso", diff --git a/apps/files/l10n/km.js b/apps/files/l10n/km.js index f03aa9c4693..55080926fc3 100644 --- a/apps/files/l10n/km.js +++ b/apps/files/l10n/km.js @@ -10,6 +10,7 @@ OC.L10N.register( "Download" : "ទាញយក", "Rename" : "ប្ដូរឈ្មោះ", "Delete" : "លុប", + "Unshare" : "លែងចែករំលែក", "Details" : "ព័ត៌មានលម្អិត", "Pending" : "កំពុងរង់ចាំ", "Name" : "ឈ្មោះ", diff --git a/apps/files/l10n/km.json b/apps/files/l10n/km.json index f050b00b795..c1e9159ee88 100644 --- a/apps/files/l10n/km.json +++ b/apps/files/l10n/km.json @@ -8,6 +8,7 @@ "Download" : "ទាញយក", "Rename" : "ប្ដូរឈ្មោះ", "Delete" : "លុប", + "Unshare" : "លែងចែករំលែក", "Details" : "ព័ត៌មានលម្អិត", "Pending" : "កំពុងរង់ចាំ", "Name" : "ឈ្មោះ", diff --git a/apps/files/l10n/kn.js b/apps/files/l10n/kn.js index 840837bdad3..f9fb8977f93 100644 --- a/apps/files/l10n/kn.js +++ b/apps/files/l10n/kn.js @@ -25,6 +25,8 @@ OC.L10N.register( "Download" : "ಪ್ರತಿಯನ್ನು ಸ್ಥಳೀಯವಾಗಿ ಉಳಿಸಿಕೊಳ್ಳಿ", "Rename" : "ಮರುಹೆಸರಿಸು", "Delete" : "ಅಳಿಸಿ", + "Disconnect storage" : "ಸಂಗ್ರಹ ಸಾಧನವನ್ನು ತೆಗೆದುಹಾಕಿ", + "Unshare" : "ಹಂಚಿಕೆಯನ್ನು ಹಿಂತೆಗೆ", "Select" : "ಆಯ್ಕೆ ಮಾಡಿ", "Pending" : "ಬಾಕಿ ಇದೆ", "Unable to determine date" : "ಮುಕ್ತಾಯ ದಿನಾಂಕ ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", diff --git a/apps/files/l10n/kn.json b/apps/files/l10n/kn.json index d6345be732c..12446a3f8b4 100644 --- a/apps/files/l10n/kn.json +++ b/apps/files/l10n/kn.json @@ -23,6 +23,8 @@ "Download" : "ಪ್ರತಿಯನ್ನು ಸ್ಥಳೀಯವಾಗಿ ಉಳಿಸಿಕೊಳ್ಳಿ", "Rename" : "ಮರುಹೆಸರಿಸು", "Delete" : "ಅಳಿಸಿ", + "Disconnect storage" : "ಸಂಗ್ರಹ ಸಾಧನವನ್ನು ತೆಗೆದುಹಾಕಿ", + "Unshare" : "ಹಂಚಿಕೆಯನ್ನು ಹಿಂತೆಗೆ", "Select" : "ಆಯ್ಕೆ ಮಾಡಿ", "Pending" : "ಬಾಕಿ ಇದೆ", "Unable to determine date" : "ಮುಕ್ತಾಯ ದಿನಾಂಕ ನಿರ್ಧರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ", diff --git a/apps/files/l10n/ko.js b/apps/files/l10n/ko.js index 13e917e3cfa..e6d0f0a1b89 100644 --- a/apps/files/l10n/ko.js +++ b/apps/files/l10n/ko.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "다운로드", "Rename" : "이름 바꾸기", "Delete" : "삭제", + "Disconnect storage" : "저장소 연결 해제", + "Unshare" : "공유 해제", "Details" : "자세한 정보", "Select" : "선택", "Pending" : "대기 중", diff --git a/apps/files/l10n/ko.json b/apps/files/l10n/ko.json index 1e77ab1b9bc..e695b9cd42c 100644 --- a/apps/files/l10n/ko.json +++ b/apps/files/l10n/ko.json @@ -32,6 +32,8 @@ "Download" : "다운로드", "Rename" : "이름 바꾸기", "Delete" : "삭제", + "Disconnect storage" : "저장소 연결 해제", + "Unshare" : "공유 해제", "Details" : "자세한 정보", "Select" : "선택", "Pending" : "대기 중", diff --git a/apps/files/l10n/lt_LT.js b/apps/files/l10n/lt_LT.js index b4001e0c8ea..e93fb079d56 100644 --- a/apps/files/l10n/lt_LT.js +++ b/apps/files/l10n/lt_LT.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "Nepavyksta nustatyti datos", "This operation is forbidden" : "Ši operacija yra uždrausta", "This directory is unavailable, please check the logs or contact the administrator" : "Katalogas nepasiekiamas, prašome peržiūrėti žurnalo įrašus arba susisiekti su administratoriumi", + "Could not move \"{file}\", target exists" : "Nepavyko perkelti \"{file}\", toks jau egzistuoja", + "Could not move \"{file}\"" : "Nepavyko perkelti \"{file}\"", + "{newName} already exists" : "{newName} jau egzistuoja", + "Could not rename \"{fileName}\", it does not exist any more" : "Nepavyko pervadinti failo \"{fileName}\", nes jis jau nebeegzistuoja", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Pavadinimas \"{targetName}\" jau naudojamas aplanke \"{dir}\". Prašome pasirinkti kitokį pavadinimą.", + "Could not rename \"{fileName}\"" : "Nepavyko pervadinti failo \"{fileName}\"", + "Could not create file \"{file}\"" : "Nepavyko sukurti failo \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Nepavyko sukurti failo \"{file}\" - failas su tokiu pavadinimu jau egzistuoja", + "Could not create folder \"{dir}\"" : "Nepavyko sukurti aplanko \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Nepavyko sukurti aplanko \"{dir}\"- aplankas su tokiu pavadinimu jau egzistuoja", + "Error deleting file \"{fileName}\"." : "Klaida trinant failą \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nėra įrašų šiame aplanko atitikmeniui „{filter}“", "Name" : "Pavadinimas", "Size" : "Dydis", diff --git a/apps/files/l10n/lt_LT.json b/apps/files/l10n/lt_LT.json index ba8610da86b..e6724be98cf 100644 --- a/apps/files/l10n/lt_LT.json +++ b/apps/files/l10n/lt_LT.json @@ -38,6 +38,17 @@ "Unable to determine date" : "Nepavyksta nustatyti datos", "This operation is forbidden" : "Ši operacija yra uždrausta", "This directory is unavailable, please check the logs or contact the administrator" : "Katalogas nepasiekiamas, prašome peržiūrėti žurnalo įrašus arba susisiekti su administratoriumi", + "Could not move \"{file}\", target exists" : "Nepavyko perkelti \"{file}\", toks jau egzistuoja", + "Could not move \"{file}\"" : "Nepavyko perkelti \"{file}\"", + "{newName} already exists" : "{newName} jau egzistuoja", + "Could not rename \"{fileName}\", it does not exist any more" : "Nepavyko pervadinti failo \"{fileName}\", nes jis jau nebeegzistuoja", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Pavadinimas \"{targetName}\" jau naudojamas aplanke \"{dir}\". Prašome pasirinkti kitokį pavadinimą.", + "Could not rename \"{fileName}\"" : "Nepavyko pervadinti failo \"{fileName}\"", + "Could not create file \"{file}\"" : "Nepavyko sukurti failo \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Nepavyko sukurti failo \"{file}\" - failas su tokiu pavadinimu jau egzistuoja", + "Could not create folder \"{dir}\"" : "Nepavyko sukurti aplanko \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Nepavyko sukurti aplanko \"{dir}\"- aplankas su tokiu pavadinimu jau egzistuoja", + "Error deleting file \"{fileName}\"." : "Klaida trinant failą \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nėra įrašų šiame aplanko atitikmeniui „{filter}“", "Name" : "Pavadinimas", "Size" : "Dydis", diff --git a/apps/files/l10n/mk.js b/apps/files/l10n/mk.js index 92616a372a3..7a387afee66 100644 --- a/apps/files/l10n/mk.js +++ b/apps/files/l10n/mk.js @@ -54,6 +54,7 @@ OC.L10N.register( "Settings" : "Подесувања", "WebDAV" : "WebDAV", "Cancel upload" : "Откажи прикачување", + "No entries found in this folder" : "Нема ништо во оваа папка", "Upload too large" : "Фајлот кој се вчитува е преголем", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.", "Files are being scanned, please wait." : "Се скенираат датотеки, ве молам почекајте.", diff --git a/apps/files/l10n/mk.json b/apps/files/l10n/mk.json index 147707256c6..acf8f546d73 100644 --- a/apps/files/l10n/mk.json +++ b/apps/files/l10n/mk.json @@ -52,6 +52,7 @@ "Settings" : "Подесувања", "WebDAV" : "WebDAV", "Cancel upload" : "Откажи прикачување", + "No entries found in this folder" : "Нема ништо во оваа папка", "Upload too large" : "Фајлот кој се вчитува е преголем", "The files you are trying to upload exceed the maximum size for file uploads on this server." : "Датотеките кои се обидувате да ги подигнете ја надминуваат максималната големина за подигнување датотеки на овој сервер.", "Files are being scanned, please wait." : "Се скенираат датотеки, ве молам почекајте.", diff --git a/apps/files/l10n/nb_NO.js b/apps/files/l10n/nb_NO.js index 9d01ad626b7..7f933c2e6a0 100644 --- a/apps/files/l10n/nb_NO.js +++ b/apps/files/l10n/nb_NO.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Last ned", "Rename" : "Gi nytt navn", "Delete" : "Slett", + "Disconnect storage" : "Koble fra lagring", + "Unshare" : "Avslutt deling", "Details" : "Detaljer", "Select" : "Velg", "Pending" : "Ventende", diff --git a/apps/files/l10n/nb_NO.json b/apps/files/l10n/nb_NO.json index 32eb320296c..ca895007f62 100644 --- a/apps/files/l10n/nb_NO.json +++ b/apps/files/l10n/nb_NO.json @@ -32,6 +32,8 @@ "Download" : "Last ned", "Rename" : "Gi nytt navn", "Delete" : "Slett", + "Disconnect storage" : "Koble fra lagring", + "Unshare" : "Avslutt deling", "Details" : "Detaljer", "Select" : "Velg", "Pending" : "Ventende", diff --git a/apps/files/l10n/nl.js b/apps/files/l10n/nl.js index 4169c053269..d64dc68c474 100644 --- a/apps/files/l10n/nl.js +++ b/apps/files/l10n/nl.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Downloaden", "Rename" : "Naam wijzigen", "Delete" : "Verwijderen", + "Disconnect storage" : "Verbinding met opslag verbreken", + "Unshare" : "Stop met delen", "Details" : "Details", "Select" : "Selecteer", "Pending" : "In behandeling", diff --git a/apps/files/l10n/nl.json b/apps/files/l10n/nl.json index 07210b13829..1268cc10863 100644 --- a/apps/files/l10n/nl.json +++ b/apps/files/l10n/nl.json @@ -32,6 +32,8 @@ "Download" : "Downloaden", "Rename" : "Naam wijzigen", "Delete" : "Verwijderen", + "Disconnect storage" : "Verbinding met opslag verbreken", + "Unshare" : "Stop met delen", "Details" : "Details", "Select" : "Selecteer", "Pending" : "In behandeling", diff --git a/apps/files/l10n/nn_NO.js b/apps/files/l10n/nn_NO.js index efe3707c213..519387d6baf 100644 --- a/apps/files/l10n/nn_NO.js +++ b/apps/files/l10n/nn_NO.js @@ -28,6 +28,7 @@ OC.L10N.register( "Download" : "Last ned", "Rename" : "Endra namn", "Delete" : "Slett", + "Unshare" : "Udel", "Details" : "Detaljar", "Pending" : "Under vegs", "Name" : "Namn", diff --git a/apps/files/l10n/nn_NO.json b/apps/files/l10n/nn_NO.json index 929f8c0d7fe..a29e272ebac 100644 --- a/apps/files/l10n/nn_NO.json +++ b/apps/files/l10n/nn_NO.json @@ -26,6 +26,7 @@ "Download" : "Last ned", "Rename" : "Endra namn", "Delete" : "Slett", + "Unshare" : "Udel", "Details" : "Detaljar", "Pending" : "Under vegs", "Name" : "Namn", diff --git a/apps/files/l10n/oc.js b/apps/files/l10n/oc.js index a3088b663fd..a6f59679a18 100644 --- a/apps/files/l10n/oc.js +++ b/apps/files/l10n/oc.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Telecargar", "Rename" : "Renomenar", "Delete" : "Suprimir", + "Disconnect storage" : "Desconnectar aqueste supòrt d'emmagazinatge", + "Unshare" : "Partejar pas", "Details" : "Detalhs", "Select" : "Seleccionar", "Pending" : "En espèra", diff --git a/apps/files/l10n/oc.json b/apps/files/l10n/oc.json index c6048605696..61caa1724b5 100644 --- a/apps/files/l10n/oc.json +++ b/apps/files/l10n/oc.json @@ -32,6 +32,8 @@ "Download" : "Telecargar", "Rename" : "Renomenar", "Delete" : "Suprimir", + "Disconnect storage" : "Desconnectar aqueste supòrt d'emmagazinatge", + "Unshare" : "Partejar pas", "Details" : "Detalhs", "Select" : "Seleccionar", "Pending" : "En espèra", diff --git a/apps/files/l10n/pl.js b/apps/files/l10n/pl.js index 6478fbe5eb4..de1d8581243 100644 --- a/apps/files/l10n/pl.js +++ b/apps/files/l10n/pl.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Pobierz", "Rename" : "Zmień nazwę", "Delete" : "Usuń", + "Disconnect storage" : "Odłącz magazyn", + "Unshare" : "Zatrzymaj współdzielenie", "Details" : "Szczegóły", "Select" : "Wybierz", "Pending" : "Oczekujące", diff --git a/apps/files/l10n/pl.json b/apps/files/l10n/pl.json index 98a0592cc75..491991c6f61 100644 --- a/apps/files/l10n/pl.json +++ b/apps/files/l10n/pl.json @@ -32,6 +32,8 @@ "Download" : "Pobierz", "Rename" : "Zmień nazwę", "Delete" : "Usuń", + "Disconnect storage" : "Odłącz magazyn", + "Unshare" : "Zatrzymaj współdzielenie", "Details" : "Szczegóły", "Select" : "Wybierz", "Pending" : "Oczekujące", diff --git a/apps/files/l10n/pt_BR.js b/apps/files/l10n/pt_BR.js index 6619da366e9..2cf4a3dbc80 100644 --- a/apps/files/l10n/pt_BR.js +++ b/apps/files/l10n/pt_BR.js @@ -34,12 +34,25 @@ OC.L10N.register( "Download" : "Baixar", "Rename" : "Renomear", "Delete" : "Excluir", + "Disconnect storage" : "Desconectar armazenamento", + "Unshare" : "Descompartilhar", "Details" : "Detalhes", "Select" : "Selecionar", "Pending" : "Pendente", "Unable to determine date" : "Impossível determinar a data", "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor, verifique os logs ou entre em contato com o administrador", + "Could not move \"{file}\", target exists" : "Não foi possível mover o \"{file}\", o alvo já existe", + "Could not move \"{file}\"" : "Não foi possível mover \"{file}\"", + "{newName} already exists" : "{newName} já existe", + "Could not rename \"{fileName}\", it does not exist any more" : "Não foi possível renomear \"{fileName}\", ele já não existe", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "O nome \"{targetName}\" já está sendo usado na pasta \"{dir}\". Por favor escolha um outro nome.", + "Could not rename \"{fileName}\"" : "Não foi possível renomear \"{fileName}\"", + "Could not create file \"{file}\"" : "Não foi possível criar o arquivo \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Não foi possível criar o arquivo \"{file}\" porque ele já existe", + "Could not create folder \"{dir}\"" : "Não foi possível criar a pasta \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Não foi possível criar a pasta \"{dir}\" porque ela já existe", + "Error deleting file \"{fileName}\"." : "Ocorreu um erro ao apagar o arquivo \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nenhuma entrada nesta pasta coincide com '{filter}'", "Name" : "Nome", "Size" : "Tamanho", @@ -68,7 +81,7 @@ OC.L10N.register( "An error occurred while trying to update the tags" : "Ocorreu um erro enquanto tentava atualizar as etiquetas", "A new file or folder has been <strong>created</strong>" : "Um novo arquivo ou pasta foi <strong>criado</strong>", "A file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta foi <strong>modificado</strong>", - "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Limite de notificações sobre a criação e alterações em seus <strong>arquivos favoritos</strong> <em>(Stream apenas)</em>", + "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Limite de notificações sobre a criação e alterações em seus <strong>arquivos favoritos</strong> <em>(apenas Stream)</em>", "A file or folder has been <strong>deleted</strong>" : "Um arquivo ou pasta foi <strong>excluído</strong>", "A file or folder has been <strong>restored</strong>" : "Um arquivo ou pasta foi <strong>restautado</strong>", "You created %1$s" : "Você criou %1$s", @@ -103,7 +116,7 @@ OC.L10N.register( "Files are being scanned, please wait." : "Arquivos sendo escaneados, por favor aguarde.", "Currently scanning" : "Atualmente escaneando", "No favorites" : "Sem favoritos", - "Files and folders you mark as favorite will show up here" : "Arquivos e pastas que você marcou como favorito são mostrados aqui", + "Files and folders you mark as favorite will show up here" : "Arquivos e pastas que você marcou como favoritos são mostrados aqui", "Text file" : "Arquivo texto", "New text file.txt" : "Novo texto file.txt" }, diff --git a/apps/files/l10n/pt_BR.json b/apps/files/l10n/pt_BR.json index e656d9e0079..be96373df6a 100644 --- a/apps/files/l10n/pt_BR.json +++ b/apps/files/l10n/pt_BR.json @@ -32,12 +32,25 @@ "Download" : "Baixar", "Rename" : "Renomear", "Delete" : "Excluir", + "Disconnect storage" : "Desconectar armazenamento", + "Unshare" : "Descompartilhar", "Details" : "Detalhes", "Select" : "Selecionar", "Pending" : "Pendente", "Unable to determine date" : "Impossível determinar a data", "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor, verifique os logs ou entre em contato com o administrador", + "Could not move \"{file}\", target exists" : "Não foi possível mover o \"{file}\", o alvo já existe", + "Could not move \"{file}\"" : "Não foi possível mover \"{file}\"", + "{newName} already exists" : "{newName} já existe", + "Could not rename \"{fileName}\", it does not exist any more" : "Não foi possível renomear \"{fileName}\", ele já não existe", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "O nome \"{targetName}\" já está sendo usado na pasta \"{dir}\". Por favor escolha um outro nome.", + "Could not rename \"{fileName}\"" : "Não foi possível renomear \"{fileName}\"", + "Could not create file \"{file}\"" : "Não foi possível criar o arquivo \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Não foi possível criar o arquivo \"{file}\" porque ele já existe", + "Could not create folder \"{dir}\"" : "Não foi possível criar a pasta \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Não foi possível criar a pasta \"{dir}\" porque ela já existe", + "Error deleting file \"{fileName}\"." : "Ocorreu um erro ao apagar o arquivo \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nenhuma entrada nesta pasta coincide com '{filter}'", "Name" : "Nome", "Size" : "Tamanho", @@ -66,7 +79,7 @@ "An error occurred while trying to update the tags" : "Ocorreu um erro enquanto tentava atualizar as etiquetas", "A new file or folder has been <strong>created</strong>" : "Um novo arquivo ou pasta foi <strong>criado</strong>", "A file or folder has been <strong>changed</strong>" : "Um arquivo ou pasta foi <strong>modificado</strong>", - "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Limite de notificações sobre a criação e alterações em seus <strong>arquivos favoritos</strong> <em>(Stream apenas)</em>", + "Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>" : "Limite de notificações sobre a criação e alterações em seus <strong>arquivos favoritos</strong> <em>(apenas Stream)</em>", "A file or folder has been <strong>deleted</strong>" : "Um arquivo ou pasta foi <strong>excluído</strong>", "A file or folder has been <strong>restored</strong>" : "Um arquivo ou pasta foi <strong>restautado</strong>", "You created %1$s" : "Você criou %1$s", @@ -101,7 +114,7 @@ "Files are being scanned, please wait." : "Arquivos sendo escaneados, por favor aguarde.", "Currently scanning" : "Atualmente escaneando", "No favorites" : "Sem favoritos", - "Files and folders you mark as favorite will show up here" : "Arquivos e pastas que você marcou como favorito são mostrados aqui", + "Files and folders you mark as favorite will show up here" : "Arquivos e pastas que você marcou como favoritos são mostrados aqui", "Text file" : "Arquivo texto", "New text file.txt" : "Novo texto file.txt" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files/l10n/ro.js b/apps/files/l10n/ro.js index 54e7c505c9a..e9114f5531e 100644 --- a/apps/files/l10n/ro.js +++ b/apps/files/l10n/ro.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Descarcă", "Rename" : "Redenumește", "Delete" : "Șterge", + "Disconnect storage" : "Deconectează stocarea", + "Unshare" : "Nu mai partaja", "Details" : "Detalii", "Select" : "Alege", "Pending" : "În așteptare", diff --git a/apps/files/l10n/ro.json b/apps/files/l10n/ro.json index 3fe8ba180bb..ed4bf8e3851 100644 --- a/apps/files/l10n/ro.json +++ b/apps/files/l10n/ro.json @@ -32,6 +32,8 @@ "Download" : "Descarcă", "Rename" : "Redenumește", "Delete" : "Șterge", + "Disconnect storage" : "Deconectează stocarea", + "Unshare" : "Nu mai partaja", "Details" : "Detalii", "Select" : "Alege", "Pending" : "În așteptare", diff --git a/apps/files/l10n/ru.js b/apps/files/l10n/ru.js index f4b71563e6e..a6fb70944ec 100644 --- a/apps/files/l10n/ru.js +++ b/apps/files/l10n/ru.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Скачать", "Rename" : "Переименовать", "Delete" : "Удалить", + "Disconnect storage" : "Отсоединить хранилище", + "Unshare" : "Закрыть доступ", "Details" : "Подробно", "Select" : "Выбрать", "Pending" : "Ожидание", diff --git a/apps/files/l10n/ru.json b/apps/files/l10n/ru.json index 81dbf53184e..964a819535b 100644 --- a/apps/files/l10n/ru.json +++ b/apps/files/l10n/ru.json @@ -32,6 +32,8 @@ "Download" : "Скачать", "Rename" : "Переименовать", "Delete" : "Удалить", + "Disconnect storage" : "Отсоединить хранилище", + "Unshare" : "Закрыть доступ", "Details" : "Подробно", "Select" : "Выбрать", "Pending" : "Ожидание", diff --git a/apps/files/l10n/sl.js b/apps/files/l10n/sl.js index 240bce73af1..103b4861206 100644 --- a/apps/files/l10n/sl.js +++ b/apps/files/l10n/sl.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Prejmi", "Rename" : "Preimenuj", "Delete" : "Izbriši", + "Disconnect storage" : "Odklopi shrambo", + "Unshare" : "Prekini souporabo", "Details" : "Podrobnosti", "Select" : "Izberi", "Pending" : "V čakanju ...", diff --git a/apps/files/l10n/sl.json b/apps/files/l10n/sl.json index 64ad90f56bd..ef5720b8be3 100644 --- a/apps/files/l10n/sl.json +++ b/apps/files/l10n/sl.json @@ -32,6 +32,8 @@ "Download" : "Prejmi", "Rename" : "Preimenuj", "Delete" : "Izbriši", + "Disconnect storage" : "Odklopi shrambo", + "Unshare" : "Prekini souporabo", "Details" : "Podrobnosti", "Select" : "Izberi", "Pending" : "V čakanju ...", diff --git a/apps/files/l10n/sq.js b/apps/files/l10n/sq.js index 6811dc5791f..5a95ea92e3d 100644 --- a/apps/files/l10n/sq.js +++ b/apps/files/l10n/sq.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Shkarkoje", "Rename" : "Riemërtojeni", "Delete" : "Fshije", + "Disconnect storage" : "Shkëput hapësirën e memorizimit", + "Unshare" : "Hiqe ndarjen", "Details" : "Hollësi", "Select" : "Përzgjidhe", "Pending" : "Në pritje", diff --git a/apps/files/l10n/sq.json b/apps/files/l10n/sq.json index 45b9d6acb61..e70fffe8e5b 100644 --- a/apps/files/l10n/sq.json +++ b/apps/files/l10n/sq.json @@ -32,6 +32,8 @@ "Download" : "Shkarkoje", "Rename" : "Riemërtojeni", "Delete" : "Fshije", + "Disconnect storage" : "Shkëput hapësirën e memorizimit", + "Unshare" : "Hiqe ndarjen", "Details" : "Hollësi", "Select" : "Përzgjidhe", "Pending" : "Në pritje", diff --git a/apps/files/l10n/sr.js b/apps/files/l10n/sr.js index 80f81b1e028..6a97b5f282f 100644 --- a/apps/files/l10n/sr.js +++ b/apps/files/l10n/sr.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Преузми", "Rename" : "Преименуј", "Delete" : "Обриши", + "Disconnect storage" : "Искључи складиште", + "Unshare" : "Не дели", "Details" : "Подаци", "Select" : "Изабери", "Pending" : "На чекању", diff --git a/apps/files/l10n/sr.json b/apps/files/l10n/sr.json index f878b9c14d4..7f319f6ab48 100644 --- a/apps/files/l10n/sr.json +++ b/apps/files/l10n/sr.json @@ -32,6 +32,8 @@ "Download" : "Преузми", "Rename" : "Преименуј", "Delete" : "Обриши", + "Disconnect storage" : "Искључи складиште", + "Unshare" : "Не дели", "Details" : "Подаци", "Select" : "Изабери", "Pending" : "На чекању", diff --git a/apps/files/l10n/sr@latin.js b/apps/files/l10n/sr@latin.js index d7017005a17..ed166f5ee87 100644 --- a/apps/files/l10n/sr@latin.js +++ b/apps/files/l10n/sr@latin.js @@ -33,6 +33,8 @@ OC.L10N.register( "Download" : "Preuzmi", "Rename" : "Preimenuj", "Delete" : "Obriši", + "Disconnect storage" : "Isključi skladište", + "Unshare" : "Ne deli", "Details" : "Detaljnije", "Select" : "Izaberi", "Pending" : "Na čekanju", diff --git a/apps/files/l10n/sr@latin.json b/apps/files/l10n/sr@latin.json index 98356e441d6..a58df8712a7 100644 --- a/apps/files/l10n/sr@latin.json +++ b/apps/files/l10n/sr@latin.json @@ -31,6 +31,8 @@ "Download" : "Preuzmi", "Rename" : "Preimenuj", "Delete" : "Obriši", + "Disconnect storage" : "Isključi skladište", + "Unshare" : "Ne deli", "Details" : "Detaljnije", "Select" : "Izaberi", "Pending" : "Na čekanju", diff --git a/apps/files/l10n/sv.js b/apps/files/l10n/sv.js index f5e81760b0f..f6df4ee16d0 100644 --- a/apps/files/l10n/sv.js +++ b/apps/files/l10n/sv.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Ladda ner", "Rename" : "Byt namn", "Delete" : "Radera", + "Disconnect storage" : "Koppla bort lagring", + "Unshare" : "Sluta dela", "Details" : "Detaljer", "Select" : "Välj", "Pending" : "Väntar", diff --git a/apps/files/l10n/sv.json b/apps/files/l10n/sv.json index c624c34029c..143e35a1e0a 100644 --- a/apps/files/l10n/sv.json +++ b/apps/files/l10n/sv.json @@ -32,6 +32,8 @@ "Download" : "Ladda ner", "Rename" : "Byt namn", "Delete" : "Radera", + "Disconnect storage" : "Koppla bort lagring", + "Unshare" : "Sluta dela", "Details" : "Detaljer", "Select" : "Välj", "Pending" : "Väntar", diff --git a/apps/files/l10n/ug.js b/apps/files/l10n/ug.js index 55985f6a655..b587b590632 100644 --- a/apps/files/l10n/ug.js +++ b/apps/files/l10n/ug.js @@ -17,6 +17,7 @@ OC.L10N.register( "Download" : "چۈشۈر", "Rename" : "ئات ئۆزگەرت", "Delete" : "ئۆچۈر", + "Unshare" : "ھەمبەھىرلىمە", "Pending" : "كۈتۈۋاتىدۇ", "Name" : "ئاتى", "Size" : "چوڭلۇقى", diff --git a/apps/files/l10n/ug.json b/apps/files/l10n/ug.json index 716bf62afb9..dd2e9c98ee5 100644 --- a/apps/files/l10n/ug.json +++ b/apps/files/l10n/ug.json @@ -15,6 +15,7 @@ "Download" : "چۈشۈر", "Rename" : "ئات ئۆزگەرت", "Delete" : "ئۆچۈر", + "Unshare" : "ھەمبەھىرلىمە", "Pending" : "كۈتۈۋاتىدۇ", "Name" : "ئاتى", "Size" : "چوڭلۇقى", diff --git a/apps/files/l10n/uk.js b/apps/files/l10n/uk.js index 8d43949d8ff..7da8b160afd 100644 --- a/apps/files/l10n/uk.js +++ b/apps/files/l10n/uk.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "Завантажити", "Rename" : "Перейменувати", "Delete" : "Видалити", + "Disconnect storage" : "Від’єднати сховище", + "Unshare" : "Закрити спільний доступ", "Details" : "Деталі", "Select" : "Оберіть", "Pending" : "Очікування", diff --git a/apps/files/l10n/uk.json b/apps/files/l10n/uk.json index d7381e0cfa7..15dabf456f8 100644 --- a/apps/files/l10n/uk.json +++ b/apps/files/l10n/uk.json @@ -32,6 +32,8 @@ "Download" : "Завантажити", "Rename" : "Перейменувати", "Delete" : "Видалити", + "Disconnect storage" : "Від’єднати сховище", + "Unshare" : "Закрити спільний доступ", "Details" : "Деталі", "Select" : "Оберіть", "Pending" : "Очікування", diff --git a/apps/files/l10n/ur_PK.js b/apps/files/l10n/ur_PK.js index bac2c3b614b..094511db425 100644 --- a/apps/files/l10n/ur_PK.js +++ b/apps/files/l10n/ur_PK.js @@ -5,6 +5,7 @@ OC.L10N.register( "Close" : "بند ", "Download" : "ڈاؤن لوڈ،", "Delete" : "حذف کریں", + "Unshare" : "شئیرنگ ختم کریں", "Name" : "اسم", "Save" : "حفظ", "Settings" : "ترتیبات" diff --git a/apps/files/l10n/ur_PK.json b/apps/files/l10n/ur_PK.json index be36293b913..3c859bb452d 100644 --- a/apps/files/l10n/ur_PK.json +++ b/apps/files/l10n/ur_PK.json @@ -3,6 +3,7 @@ "Close" : "بند ", "Download" : "ڈاؤن لوڈ،", "Delete" : "حذف کریں", + "Unshare" : "شئیرنگ ختم کریں", "Name" : "اسم", "Save" : "حفظ", "Settings" : "ترتیبات" diff --git a/apps/files/l10n/vi.js b/apps/files/l10n/vi.js index 4af893ab66a..83dd03f48f8 100644 --- a/apps/files/l10n/vi.js +++ b/apps/files/l10n/vi.js @@ -30,6 +30,7 @@ OC.L10N.register( "Download" : "Tải về", "Rename" : "Sửa tên", "Delete" : "Xóa", + "Unshare" : "Bỏ chia sẻ", "Details" : "Chi tiết", "Select" : "Chọn", "Pending" : "Đang chờ", diff --git a/apps/files/l10n/vi.json b/apps/files/l10n/vi.json index ebffb827525..abc83963873 100644 --- a/apps/files/l10n/vi.json +++ b/apps/files/l10n/vi.json @@ -28,6 +28,7 @@ "Download" : "Tải về", "Rename" : "Sửa tên", "Delete" : "Xóa", + "Unshare" : "Bỏ chia sẻ", "Details" : "Chi tiết", "Select" : "Chọn", "Pending" : "Đang chờ", diff --git a/apps/files/l10n/zh_TW.js b/apps/files/l10n/zh_TW.js index 09829db3bcc..0283c91b656 100644 --- a/apps/files/l10n/zh_TW.js +++ b/apps/files/l10n/zh_TW.js @@ -34,6 +34,8 @@ OC.L10N.register( "Download" : "下載", "Rename" : "重新命名", "Delete" : "刪除", + "Disconnect storage" : "斷開儲存空間連接", + "Unshare" : "取消分享", "Details" : "詳細資料", "Select" : "選擇", "Pending" : "等候中", diff --git a/apps/files/l10n/zh_TW.json b/apps/files/l10n/zh_TW.json index 6f2c396ad18..4969b3d5bfb 100644 --- a/apps/files/l10n/zh_TW.json +++ b/apps/files/l10n/zh_TW.json @@ -32,6 +32,8 @@ "Download" : "下載", "Rename" : "重新命名", "Delete" : "刪除", + "Disconnect storage" : "斷開儲存空間連接", + "Unshare" : "取消分享", "Details" : "詳細資料", "Select" : "選擇", "Pending" : "等候中", diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index 9a4e8d59786..4f960518839 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -66,15 +66,15 @@ class Helper { */ public static function determineIcon($file) { if($file['type'] === 'dir') { - $icon = \OC_Helper::mimetypeIcon('dir'); + $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir'); // TODO: move this part to the client side, using mountType if ($file->isShared()) { - $icon = \OC_Helper::mimetypeIcon('dir-shared'); + $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir-shared'); } elseif ($file->isMounted()) { - $icon = \OC_Helper::mimetypeIcon('dir-external'); + $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon('dir-external'); } }else{ - $icon = \OC_Helper::mimetypeIcon($file->getMimetype()); + $icon = \OC::$server->getMimeTypeDetector()->mimeTypeIcon($file->getMimetype()); } return substr($icon, 0, -3) . 'svg'; diff --git a/apps/files/tests/backgroundjob/ScanFilesTest.php b/apps/files/tests/backgroundjob/ScanFilesTest.php index 907cad64f9e..087696f0cfc 100644 --- a/apps/files/tests/backgroundjob/ScanFilesTest.php +++ b/apps/files/tests/backgroundjob/ScanFilesTest.php @@ -24,7 +24,6 @@ use Test\TestCase; use OCP\IConfig; use OCP\IUserManager; use OCA\Files\BackgroundJob\ScanFiles; -use OCP\ILogger; /** * Class ScanFilesTest diff --git a/apps/files/tests/controller/apicontrollertest.php b/apps/files/tests/controller/apicontrollertest.php index fb728d5eff0..bc66e4641b9 100644 --- a/apps/files/tests/controller/apicontrollertest.php +++ b/apps/files/tests/controller/apicontrollertest.php @@ -92,6 +92,7 @@ class ApiControllerTest extends TestCase { [ 'mtime' => 55, 'mimetype' => 'application/pdf', + 'permissions' => 31, 'size' => 1234, 'etag' => 'MyEtag', ], @@ -111,7 +112,7 @@ class ApiControllerTest extends TestCase { 'parentId' => null, 'mtime' => 55000, 'name' => 'root.txt', - 'permissions' => null, + 'permissions' => 31, 'mimetype' => 'application/pdf', 'size' => 1234, 'type' => 'file', @@ -139,6 +140,7 @@ class ApiControllerTest extends TestCase { [ 'mtime' => 55, 'mimetype' => 'application/pdf', + 'permissions' => 31, 'size' => 1234, 'etag' => 'MyEtag', ], @@ -155,6 +157,7 @@ class ApiControllerTest extends TestCase { [ 'mtime' => 999, 'mimetype' => 'application/binary', + 'permissions' => 31, 'size' => 9876, 'etag' => 'SubEtag', ], @@ -174,7 +177,7 @@ class ApiControllerTest extends TestCase { 'parentId' => null, 'mtime' => 55000, 'name' => 'root.txt', - 'permissions' => null, + 'permissions' => 31, 'mimetype' => 'application/pdf', 'size' => 1234, 'type' => 'file', @@ -191,7 +194,7 @@ class ApiControllerTest extends TestCase { 'parentId' => null, 'mtime' => 999000, 'name' => 'root.txt', - 'permissions' => null, + 'permissions' => 31, 'mimetype' => 'application/binary', 'size' => 9876, 'type' => 'file', diff --git a/apps/files/tests/js/fileactionsmenuSpec.js b/apps/files/tests/js/fileactionsmenuSpec.js index 747a746a602..3028db2b3ac 100644 --- a/apps/files/tests/js/fileactionsmenuSpec.js +++ b/apps/files/tests/js/fileactionsmenuSpec.js @@ -20,7 +20,7 @@ */ describe('OCA.Files.FileActionsMenu tests', function() { - var fileList, fileActions, menu, actionStub, $tr; + var fileList, fileActions, menu, actionStub, menuContext, $tr; beforeEach(function() { // init horrible parameters @@ -80,7 +80,7 @@ describe('OCA.Files.FileActionsMenu tests', function() { }; $tr = fileList.add(fileData); - var menuContext = { + menuContext = { $file: $tr, fileList: fileList, fileActions: fileActions, @@ -189,6 +189,22 @@ describe('OCA.Files.FileActionsMenu tests', function() { var yactionIndex = menu.$el.find('a[data-action=Yaction]').closest('li').index(); expect(wactionIndex).toBeLessThan(yactionIndex); }); + it('calls displayName function', function() { + var displayNameStub = sinon.stub().returns('Test'); + + fileActions.registerAction({ + name: 'Something', + displayName: displayNameStub, + mime: 'text/plain', + permissions: OC.PERMISSION_ALL + }); + + menu.render(); + + expect(displayNameStub.calledOnce).toEqual(true); + expect(displayNameStub.calledWith(menuContext)).toEqual(true); + expect(menu.$el.find('a[data-action=Something]').text()).toEqual('Test'); + }); }); describe('action handler', function() { diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 9f7ad50bc60..9dfcdfead7f 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -2402,6 +2402,21 @@ describe('OCA.Files.FileList tests', function() { expect(ev.result).not.toEqual(false); expect(uploadData.targetDir).toEqual('/a/b'); }); + it('renders upload indicator element for folders only', function() { + fileList.add({ + name: 'afolder', + type: 'dir', + mime: 'httpd/unix-directory' + }); + fileList.add({ + name: 'afile.txt', + type: 'file', + mime: 'text/plain' + }); + + expect(fileList.findFileEl('afolder').find('.uploadtext').length).toEqual(1); + expect(fileList.findFileEl('afile.txt').find('.uploadtext').length).toEqual(0); + }); }); }); describe('Handling errors', function () { @@ -2534,4 +2549,34 @@ describe('OCA.Files.FileList tests', function() { expect(newFileMenuStub.notCalled).toEqual(true); }); }); + describe('mount type detection', function() { + function testMountType(dirInfoId, dirInfoMountType, inputMountType, expectedMountType) { + var $tr; + fileList.dirInfo.id = dirInfoId; + fileList.dirInfo.mountType = dirInfoMountType; + $tr = fileList.add({ + type: 'dir', + mimetype: 'httpd/unix-directory', + name: 'test dir', + mountType: inputMountType + }); + + expect($tr.attr('data-mounttype')).toEqual(expectedMountType); + } + + it('leaves mount type as is if no parent exists', function() { + testMountType(null, null, 'external', 'external'); + testMountType(null, null, 'shared', 'shared'); + }); + it('detects share root if parent exists', function() { + testMountType(123, null, 'shared', 'shared-root'); + testMountType(123, 'shared', 'shared', 'shared'); + testMountType(123, 'shared-root', 'shared', 'shared'); + }); + it('detects external storage root if parent exists', function() { + testMountType(123, null, 'external', 'external-root'); + testMountType(123, 'external', 'external', 'external'); + testMountType(123, 'external-root', 'external', 'external'); + }); + }); }); diff --git a/apps/files/tests/service/tagservice.php b/apps/files/tests/service/tagservice.php index 36da3edc61e..b93dedd0efd 100644 --- a/apps/files/tests/service/tagservice.php +++ b/apps/files/tests/service/tagservice.php @@ -54,7 +54,7 @@ class TagServiceTest extends \Test\TestCase { protected function setUp() { parent::setUp(); $this->user = $this->getUniqueId('user'); - \OC_User::createUser($this->user, 'test'); + \OC::$server->getUserManager()->createUser($this->user, 'test'); \OC_User::setUserId($this->user); \OC_Util::setupFS($this->user); /** @@ -82,7 +82,8 @@ class TagServiceTest extends \Test\TestCase { protected function tearDown() { \OC_User::setUserId(''); - \OC_User::deleteUser($this->user); + $user = \OC::$server->getUserManager()->get($this->user); + if ($user !== null) { $user->delete(); } } public function testUpdateFileTags() { diff --git a/apps/files_external/appinfo/database.xml b/apps/files_external/appinfo/database.xml index 27918bf9819..2c3615a4d4c 100644 --- a/apps/files_external/appinfo/database.xml +++ b/apps/files_external/appinfo/database.xml @@ -80,14 +80,12 @@ <length>64</length> </field> <index> - <name>mount_id_app_index</name> <field> <name>mount_id</name> <sorting>ascending</sorting> </field> </index> <index> - <name>applicable_value_index</name> <field> <name>type</name> <sorting>ascending</sorting> @@ -98,7 +96,6 @@ </field> </index> <index> - <name>applicable_value_mount_index</name> <unique>true</unique> <field> <name>type</name> @@ -147,14 +144,12 @@ </field> <index> - <name>config_mount_id</name> <field> <name>mount_id</name> <sorting>ascending</sorting> </field> </index> <index> - <name>config_mount_key</name> <unique>true</unique> <field> <name>mount_id</name> @@ -199,7 +194,6 @@ </field> <index> - <name>option_mount_id</name> <field> <name>mount_id</name> <sorting>ascending</sorting> diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index f6d583d0a5a..355d9feb4b9 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -14,7 +14,7 @@ <admin>admin-external-storage</admin> </documentation> <rememberlogin>false</rememberlogin> - <version>0.5.0</version> + <version>0.5.1</version> <types> <filesystem/> </types> diff --git a/apps/files_external/l10n/cs_CZ.js b/apps/files_external/l10n/cs_CZ.js index 94c8194e328..13ca22abcc9 100644 --- a/apps/files_external/l10n/cs_CZ.js +++ b/apps/files_external/l10n/cs_CZ.js @@ -36,6 +36,14 @@ OC.L10N.register( "(group)" : "(skupina)", "Admin defined" : "Nastaveno administrátorem", "Saved" : "Uloženo", + "Empty response from the server" : "Prázdná odpověď serveru", + "Couldn't access. Please logout and login to activate this mount point" : "Nelze připojit. Pro aktivaci tohoto přípojného bodu se prosím odhlašte a znovu přihlašte", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Nelze obdržet informaci z ownCloud serveru: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Nelze obdržet seznam vzdálených přípojných bodů: {type}", + "There was an error with message: " : "Došlo k chybě s tímto hlášením:", + "External mount error" : "Chyba vzdáleného úložiště", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Nelze obdržet seznam síťových úložišť systému Windows: prázdná odpověď serveru", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Některá z nastavených vzdálených úložišť nejsou připojena. Pro více informací prosím klikněte na červenou šipku(y)", "Access key" : "Přístupový klíč", "Secret key" : "Tajný klíč", "Builtin" : "Zabudované", diff --git a/apps/files_external/l10n/cs_CZ.json b/apps/files_external/l10n/cs_CZ.json index 4adba2f4d04..bfcd3a30f46 100644 --- a/apps/files_external/l10n/cs_CZ.json +++ b/apps/files_external/l10n/cs_CZ.json @@ -34,6 +34,14 @@ "(group)" : "(skupina)", "Admin defined" : "Nastaveno administrátorem", "Saved" : "Uloženo", + "Empty response from the server" : "Prázdná odpověď serveru", + "Couldn't access. Please logout and login to activate this mount point" : "Nelze připojit. Pro aktivaci tohoto přípojného bodu se prosím odhlašte a znovu přihlašte", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Nelze obdržet informaci z ownCloud serveru: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Nelze obdržet seznam vzdálených přípojných bodů: {type}", + "There was an error with message: " : "Došlo k chybě s tímto hlášením:", + "External mount error" : "Chyba vzdáleného úložiště", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Nelze obdržet seznam síťových úložišť systému Windows: prázdná odpověď serveru", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Některá z nastavených vzdálených úložišť nejsou připojena. Pro více informací prosím klikněte na červenou šipku(y)", "Access key" : "Přístupový klíč", "Secret key" : "Tajný klíč", "Builtin" : "Zabudované", diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js index d4d00e7f83e..18f67db420f 100644 --- a/apps/files_external/l10n/de.js +++ b/apps/files_external/l10n/de.js @@ -10,10 +10,12 @@ OC.L10N.register( "Storage with id \"%i\" not found" : "Der Speicher mit der ID „%i“ wurde nicht gefunden", "Invalid mount point" : "Ungültiger mount point", "Invalid storage backend \"%s\"" : "Ungültiges Speicher-Backend „%s“", + "%s" : "%s", "Personal" : "Persönlich", "System" : "System", "Grant access" : "Zugriff gestatten", "Access granted" : "Zugriff gestattet", + "Error configuring OAuth1" : "Fehler beim konfigurieren von OAuth1", "Error configuring OAuth2" : "Fehler beim Einrichten von OAuth2", "Generate keys" : "Schlüssel erzeugen", "Error generating key pair" : "Fehler beim Erzeugen des Schlüsselpaares", @@ -25,7 +27,9 @@ OC.L10N.register( "Every time the filesystem is used" : "Immer, wenn das Dateisystem benutzt wird", "All users. Type to select user or group." : "Alle Benutzer. Benutzer oder Gruppe zur Auswahl eingeben.", "(group)" : "(group)", + "Admin defined" : "Administrator festlegen", "Saved" : "Gespeichert", + "There was an error with message: " : "Es ist ein Fehler mit folgender Meldung aufgetreten:", "Access key" : "Zugangsschlüssel", "Secret key" : "Geheimer Schlüssel", "None" : "Keine", diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json index a65f58b0fdb..cae7672260e 100644 --- a/apps/files_external/l10n/de.json +++ b/apps/files_external/l10n/de.json @@ -8,10 +8,12 @@ "Storage with id \"%i\" not found" : "Der Speicher mit der ID „%i“ wurde nicht gefunden", "Invalid mount point" : "Ungültiger mount point", "Invalid storage backend \"%s\"" : "Ungültiges Speicher-Backend „%s“", + "%s" : "%s", "Personal" : "Persönlich", "System" : "System", "Grant access" : "Zugriff gestatten", "Access granted" : "Zugriff gestattet", + "Error configuring OAuth1" : "Fehler beim konfigurieren von OAuth1", "Error configuring OAuth2" : "Fehler beim Einrichten von OAuth2", "Generate keys" : "Schlüssel erzeugen", "Error generating key pair" : "Fehler beim Erzeugen des Schlüsselpaares", @@ -23,7 +25,9 @@ "Every time the filesystem is used" : "Immer, wenn das Dateisystem benutzt wird", "All users. Type to select user or group." : "Alle Benutzer. Benutzer oder Gruppe zur Auswahl eingeben.", "(group)" : "(group)", + "Admin defined" : "Administrator festlegen", "Saved" : "Gespeichert", + "There was an error with message: " : "Es ist ein Fehler mit folgender Meldung aufgetreten:", "Access key" : "Zugangsschlüssel", "Secret key" : "Geheimer Schlüssel", "None" : "Keine", diff --git a/apps/files_external/l10n/el.js b/apps/files_external/l10n/el.js index 294ec9da6ff..6de2ebb2101 100644 --- a/apps/files_external/l10n/el.js +++ b/apps/files_external/l10n/el.js @@ -17,6 +17,7 @@ OC.L10N.register( "Unsatisfied backend parameters" : "Ελλιπείς παράμετροι συστήματος", "Unsatisfied authentication mechanism parameters" : "Ελλιπείς παράμετροι μηχανισμού πιστοποίησης", "Insufficient data: %s" : "Μη επαρκή δεδομένα: %s", + "%s" : "%s", "Personal" : "Προσωπικά", "System" : "Σύστημα", "Grant access" : "Παροχή πρόσβασης", diff --git a/apps/files_external/l10n/el.json b/apps/files_external/l10n/el.json index 431e81c3d7a..1c5d7fb6dfc 100644 --- a/apps/files_external/l10n/el.json +++ b/apps/files_external/l10n/el.json @@ -15,6 +15,7 @@ "Unsatisfied backend parameters" : "Ελλιπείς παράμετροι συστήματος", "Unsatisfied authentication mechanism parameters" : "Ελλιπείς παράμετροι μηχανισμού πιστοποίησης", "Insufficient data: %s" : "Μη επαρκή δεδομένα: %s", + "%s" : "%s", "Personal" : "Προσωπικά", "System" : "Σύστημα", "Grant access" : "Παροχή πρόσβασης", diff --git a/apps/files_external/l10n/es.js b/apps/files_external/l10n/es.js index 6d8bf0d3139..5aaa3884fd0 100644 --- a/apps/files_external/l10n/es.js +++ b/apps/files_external/l10n/es.js @@ -1,8 +1,8 @@ OC.L10N.register( "files_external", { - "Fetching request tokens failed. Verify that your app key and secret are correct." : "Fallo al acceder a los tokens solicitados. Verfique que su clave de app y la clave secreta son correctas.", - "Fetching access tokens failed. Verify that your app key and secret are correct." : "Fallo al acceder a los tokens solicitados. Verfique que su clave de app y la clave secreta son correctas.", + "Fetching request tokens failed. Verify that your app key and secret are correct." : "Falló al acceder a los tokens solicitados. Verifique que su clave de app y la clave secreta sean correctas.", + "Fetching access tokens failed. Verify that your app key and secret are correct." : "Falló al acceder a los tokens solicitados. Verifique que su clave de app y la clave secreta sean correctas.", "Please provide a valid app key and secret." : "Por favor facilite una clave de app y una clave secreta válidas.", "Step 1 failed. Exception: %s" : "El paso 1 falló. Excepción: %s", "Step 2 failed. Exception: %s" : "El paso 2 falló. Excepción: %s", @@ -36,6 +36,7 @@ OC.L10N.register( "(group)" : "(grupo)", "Admin defined" : "Admin definido", "Saved" : "Guardado", + "There was an error with message: " : "Hubo un error con el mensaje:", "Access key" : "Clave de acceso", "Secret key" : "Clave secreta", "None" : "Ninguno", @@ -100,6 +101,7 @@ OC.L10N.register( "Add storage" : "Añadir almacenamiento", "Advanced settings" : "Configuración avanzada", "Delete" : "Eliminar", + "Allow users to mount external storage" : "Permitir a los usuarios montar un almacenamiento externo", "Allow users to mount the following external storage" : "Permitir a los usuarios montar el siguiente almacenamiento externo" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/es.json b/apps/files_external/l10n/es.json index bf0624e96db..60722de1f41 100644 --- a/apps/files_external/l10n/es.json +++ b/apps/files_external/l10n/es.json @@ -1,6 +1,6 @@ { "translations": { - "Fetching request tokens failed. Verify that your app key and secret are correct." : "Fallo al acceder a los tokens solicitados. Verfique que su clave de app y la clave secreta son correctas.", - "Fetching access tokens failed. Verify that your app key and secret are correct." : "Fallo al acceder a los tokens solicitados. Verfique que su clave de app y la clave secreta son correctas.", + "Fetching request tokens failed. Verify that your app key and secret are correct." : "Falló al acceder a los tokens solicitados. Verifique que su clave de app y la clave secreta sean correctas.", + "Fetching access tokens failed. Verify that your app key and secret are correct." : "Falló al acceder a los tokens solicitados. Verifique que su clave de app y la clave secreta sean correctas.", "Please provide a valid app key and secret." : "Por favor facilite una clave de app y una clave secreta válidas.", "Step 1 failed. Exception: %s" : "El paso 1 falló. Excepción: %s", "Step 2 failed. Exception: %s" : "El paso 2 falló. Excepción: %s", @@ -34,6 +34,7 @@ "(group)" : "(grupo)", "Admin defined" : "Admin definido", "Saved" : "Guardado", + "There was an error with message: " : "Hubo un error con el mensaje:", "Access key" : "Clave de acceso", "Secret key" : "Clave secreta", "None" : "Ninguno", @@ -98,6 +99,7 @@ "Add storage" : "Añadir almacenamiento", "Advanced settings" : "Configuración avanzada", "Delete" : "Eliminar", + "Allow users to mount external storage" : "Permitir a los usuarios montar un almacenamiento externo", "Allow users to mount the following external storage" : "Permitir a los usuarios montar el siguiente almacenamiento externo" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/et_EE.js b/apps/files_external/l10n/et_EE.js index fa22b4c6591..e21ce7e39ae 100644 --- a/apps/files_external/l10n/et_EE.js +++ b/apps/files_external/l10n/et_EE.js @@ -7,10 +7,14 @@ OC.L10N.register( "Storage with id \"%i\" not found" : "Salvestuskohta ID-ga \"%i\" ei leitud", "Invalid mount point" : "Vigane ühenduspunkt", "Invalid storage backend \"%s\"" : "Vigane salvestuskoha taustsüsteem \"%s\"", + "Unsatisfied backend parameters" : "Rahuldamata taustarakenduse parameetrid", + "%s" : "%s", "Personal" : "Isiklik", "System" : "Süsteem", "Grant access" : "Anna ligipääs", "Access granted" : "Ligipääs on antud", + "Error configuring OAuth1" : "OAuth1 seadistamise tõrge", + "Error configuring OAuth2" : "OAuth2 seadistamise tõrge", "Generate keys" : "Loo võtmed", "Error generating key pair" : "Viga võtmepaari loomisel", "Enable encryption" : "Luba krüpteerimine", @@ -21,18 +25,26 @@ OC.L10N.register( "Every time the filesystem is used" : "Iga kord, kui failisüsteemi kasutatakse", "All users. Type to select user or group." : "Kõik kasutajad. Kirjuta, et valida kasutaja või grupp.", "(group)" : "(grupp)", + "Admin defined" : "Admini poolt määratud", "Saved" : "Salvestatud", + "Couldn't get the list of external mount points: {type}" : "Välise ühenduspunkti hankimine ebaõnnestus: {type}", + "There was an error with message: " : "Sõnumiga tekkis tõrge:", + "External mount error" : "Välise seostamise tõrge", + "Access key" : "Ligipääsuvõti", + "Secret key" : "Salavõti", "Builtin" : "Sisseehitatud", "None" : "Pole", "OAuth1" : "OAuth1", "App key" : "Rakenduse võti", "App secret" : "Rakenduse salasõna", + "OAuth2" : "OAuth2", "Client ID" : "Kliendi ID", "Client secret" : "Kliendi salasõna", "OpenStack" : "OpenStack", "Username" : "Kasutajanimi", "Password" : "Parool", "API key" : "API võti", + "RSA public key" : "RSA avalik võti", "Public key" : "Avalik võti", "Amazon S3" : "Amazon S3", "Bucket" : "Korv", diff --git a/apps/files_external/l10n/et_EE.json b/apps/files_external/l10n/et_EE.json index 37e7cc282ce..ad7b8622be8 100644 --- a/apps/files_external/l10n/et_EE.json +++ b/apps/files_external/l10n/et_EE.json @@ -5,10 +5,14 @@ "Storage with id \"%i\" not found" : "Salvestuskohta ID-ga \"%i\" ei leitud", "Invalid mount point" : "Vigane ühenduspunkt", "Invalid storage backend \"%s\"" : "Vigane salvestuskoha taustsüsteem \"%s\"", + "Unsatisfied backend parameters" : "Rahuldamata taustarakenduse parameetrid", + "%s" : "%s", "Personal" : "Isiklik", "System" : "Süsteem", "Grant access" : "Anna ligipääs", "Access granted" : "Ligipääs on antud", + "Error configuring OAuth1" : "OAuth1 seadistamise tõrge", + "Error configuring OAuth2" : "OAuth2 seadistamise tõrge", "Generate keys" : "Loo võtmed", "Error generating key pair" : "Viga võtmepaari loomisel", "Enable encryption" : "Luba krüpteerimine", @@ -19,18 +23,26 @@ "Every time the filesystem is used" : "Iga kord, kui failisüsteemi kasutatakse", "All users. Type to select user or group." : "Kõik kasutajad. Kirjuta, et valida kasutaja või grupp.", "(group)" : "(grupp)", + "Admin defined" : "Admini poolt määratud", "Saved" : "Salvestatud", + "Couldn't get the list of external mount points: {type}" : "Välise ühenduspunkti hankimine ebaõnnestus: {type}", + "There was an error with message: " : "Sõnumiga tekkis tõrge:", + "External mount error" : "Välise seostamise tõrge", + "Access key" : "Ligipääsuvõti", + "Secret key" : "Salavõti", "Builtin" : "Sisseehitatud", "None" : "Pole", "OAuth1" : "OAuth1", "App key" : "Rakenduse võti", "App secret" : "Rakenduse salasõna", + "OAuth2" : "OAuth2", "Client ID" : "Kliendi ID", "Client secret" : "Kliendi salasõna", "OpenStack" : "OpenStack", "Username" : "Kasutajanimi", "Password" : "Parool", "API key" : "API võti", + "RSA public key" : "RSA avalik võti", "Public key" : "Avalik võti", "Amazon S3" : "Amazon S3", "Bucket" : "Korv", diff --git a/apps/files_external/l10n/fi_FI.js b/apps/files_external/l10n/fi_FI.js index b56ad195ce6..c358a2b3260 100644 --- a/apps/files_external/l10n/fi_FI.js +++ b/apps/files_external/l10n/fi_FI.js @@ -25,6 +25,8 @@ OC.L10N.register( "(group)" : "(ryhmä)", "Admin defined" : "Ylläpitäjän määrittämä", "Saved" : "Tallennettu", + "Empty response from the server" : "Tyhjä vastaus palvelimelta", + "There was an error with message: " : "Tapahtui virhe viestillä:", "Builtin" : "Sisäänrakennettu", "None" : "Ei mitään", "OAuth1" : "OAuth1", @@ -39,6 +41,7 @@ OC.L10N.register( "Rackspace" : "Rackspace", "API key" : "API-avain", "Username and password" : "Käyttäjätunnus ja salasana", + "Session credentials" : "Istunnon tunnistetiedot", "RSA public key" : "Julkinen RSA-avain", "Public key" : "Julkinen avain", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/fi_FI.json b/apps/files_external/l10n/fi_FI.json index 9ff2748b7f6..4e72d861d6f 100644 --- a/apps/files_external/l10n/fi_FI.json +++ b/apps/files_external/l10n/fi_FI.json @@ -23,6 +23,8 @@ "(group)" : "(ryhmä)", "Admin defined" : "Ylläpitäjän määrittämä", "Saved" : "Tallennettu", + "Empty response from the server" : "Tyhjä vastaus palvelimelta", + "There was an error with message: " : "Tapahtui virhe viestillä:", "Builtin" : "Sisäänrakennettu", "None" : "Ei mitään", "OAuth1" : "OAuth1", @@ -37,6 +39,7 @@ "Rackspace" : "Rackspace", "API key" : "API-avain", "Username and password" : "Käyttäjätunnus ja salasana", + "Session credentials" : "Istunnon tunnistetiedot", "RSA public key" : "Julkinen RSA-avain", "Public key" : "Julkinen avain", "Amazon S3" : "Amazon S3", diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js index 8d5f7e911b7..eb99f0aaeba 100644 --- a/apps/files_external/l10n/fr.js +++ b/apps/files_external/l10n/fr.js @@ -36,6 +36,14 @@ OC.L10N.register( "(group)" : "(groupe)", "Admin defined" : "Défini par l'administrateur", "Saved" : "Sauvegardé", + "Empty response from the server" : "Réponse vide du serveur", + "Couldn't access. Please logout and login to activate this mount point" : "Impossible d'accéder. Veuillez vous déconnecter et vous reconnecter pour activer ce point de montage.", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossible d'obtenir l'information depuis le serveur ownCloud : {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Impossible de récupérer la liste des points de montage externes : {type}", + "There was an error with message: " : "Il y a eu une erreur avec le message :", + "External mount error" : "Erreur de montage externe", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossible d'obtenir la liste des points de montage des disques réseaux Windows : Réponse vide du serveur", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Certains points de montage externes configurés ne sont pas connectés. Veuillez cliquer sur la(les) ligne(s) rouge(s) pour plus d'informations", "Access key" : "Clé d'accès", "Secret key" : "Clé secrète", "Builtin" : "Intégré", diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json index cae66119a4f..a399872c208 100644 --- a/apps/files_external/l10n/fr.json +++ b/apps/files_external/l10n/fr.json @@ -34,6 +34,14 @@ "(group)" : "(groupe)", "Admin defined" : "Défini par l'administrateur", "Saved" : "Sauvegardé", + "Empty response from the server" : "Réponse vide du serveur", + "Couldn't access. Please logout and login to activate this mount point" : "Impossible d'accéder. Veuillez vous déconnecter et vous reconnecter pour activer ce point de montage.", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossible d'obtenir l'information depuis le serveur ownCloud : {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Impossible de récupérer la liste des points de montage externes : {type}", + "There was an error with message: " : "Il y a eu une erreur avec le message :", + "External mount error" : "Erreur de montage externe", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossible d'obtenir la liste des points de montage des disques réseaux Windows : Réponse vide du serveur", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Certains points de montage externes configurés ne sont pas connectés. Veuillez cliquer sur la(les) ligne(s) rouge(s) pour plus d'informations", "Access key" : "Clé d'accès", "Secret key" : "Clé secrète", "Builtin" : "Intégré", diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js index daca1d3a896..85fb70cf1a7 100644 --- a/apps/files_external/l10n/it.js +++ b/apps/files_external/l10n/it.js @@ -36,6 +36,15 @@ OC.L10N.register( "(group)" : "(gruppo)", "Admin defined" : "Definito dall'amministratore", "Saved" : "Salvato", + "Empty response from the server" : "Risposta vuota dal server", + "Couldn't access. Please logout and login to activate this mount point" : "Impossibile accedere. Termina la sessione e accedi nuovamente per attivare questo punto di mount", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossibile ottenere le informazioni dal server ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Impossibile ottenere l'elenco dei punti di mount esterni: {type}", + "There was an error with message: " : "Si è verificato un errore con il messaggio:", + "External mount error" : "Errore di mount esterno", + "goto-external-storage" : "goto-external-storage", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossibile ottenere l'elenco dei punti di mount delle unità di rete Windows: risposta vuota dal server", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alcuni dei punti di mount esterni configurati non sono connessi. Fai clic sulle righe rosse per ulteriori informazioni", "Access key" : "Chiave di accesso", "Secret key" : "Chiave segreta", "Builtin" : "Integrata", diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json index 67f7fe3595b..08da52362d6 100644 --- a/apps/files_external/l10n/it.json +++ b/apps/files_external/l10n/it.json @@ -34,6 +34,15 @@ "(group)" : "(gruppo)", "Admin defined" : "Definito dall'amministratore", "Saved" : "Salvato", + "Empty response from the server" : "Risposta vuota dal server", + "Couldn't access. Please logout and login to activate this mount point" : "Impossibile accedere. Termina la sessione e accedi nuovamente per attivare questo punto di mount", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Impossibile ottenere le informazioni dal server ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Impossibile ottenere l'elenco dei punti di mount esterni: {type}", + "There was an error with message: " : "Si è verificato un errore con il messaggio:", + "External mount error" : "Errore di mount esterno", + "goto-external-storage" : "goto-external-storage", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Impossibile ottenere l'elenco dei punti di mount delle unità di rete Windows: risposta vuota dal server", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alcuni dei punti di mount esterni configurati non sono connessi. Fai clic sulle righe rosse per ulteriori informazioni", "Access key" : "Chiave di accesso", "Secret key" : "Chiave segreta", "Builtin" : "Integrata", diff --git a/apps/files_external/l10n/ja.js b/apps/files_external/l10n/ja.js index 5518f6afa78..d99eca69d2b 100644 --- a/apps/files_external/l10n/ja.js +++ b/apps/files_external/l10n/ja.js @@ -17,6 +17,7 @@ OC.L10N.register( "Unsatisfied backend parameters" : "バックエンドのためのパラメーターが不十分です。", "Unsatisfied authentication mechanism parameters" : "認証のためのパラメータが不十分です", "Insufficient data: %s" : "データが不足しています: %s", + "%s" : "%s", "Personal" : "個人", "System" : "システム", "Grant access" : "アクセスを許可", @@ -35,6 +36,13 @@ OC.L10N.register( "(group)" : "(グループ)", "Admin defined" : "管理者設定済", "Saved" : "保存されました", + "Empty response from the server" : "サーバーから空の応答がありました", + "Couldn't access. Please logout and login to activate this mount point" : "アクセス出来ませんでした。このマウントポイントを有効にするには一度ログアウトしてからログインしてください。", + "Couldn't get the information from the ownCloud server: {code} {type}" : "ownCloud サーバーから情報を取得出来ませんでした。: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "外部マウントポイントのリストを取得出来ませんでした。: {type}", + "There was an error with message: " : "メッセージ付きのエラーが発生しました:", + "External mount error" : "外部マウントエラー", + "goto-external-storage" : "外部ストレージに行く", "Access key" : "アクセスキー", "Secret key" : "シークレットキー", "Builtin" : "ビルトイン", diff --git a/apps/files_external/l10n/ja.json b/apps/files_external/l10n/ja.json index 8134ed16cd5..2675c3e18e5 100644 --- a/apps/files_external/l10n/ja.json +++ b/apps/files_external/l10n/ja.json @@ -15,6 +15,7 @@ "Unsatisfied backend parameters" : "バックエンドのためのパラメーターが不十分です。", "Unsatisfied authentication mechanism parameters" : "認証のためのパラメータが不十分です", "Insufficient data: %s" : "データが不足しています: %s", + "%s" : "%s", "Personal" : "個人", "System" : "システム", "Grant access" : "アクセスを許可", @@ -33,6 +34,13 @@ "(group)" : "(グループ)", "Admin defined" : "管理者設定済", "Saved" : "保存されました", + "Empty response from the server" : "サーバーから空の応答がありました", + "Couldn't access. Please logout and login to activate this mount point" : "アクセス出来ませんでした。このマウントポイントを有効にするには一度ログアウトしてからログインしてください。", + "Couldn't get the information from the ownCloud server: {code} {type}" : "ownCloud サーバーから情報を取得出来ませんでした。: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "外部マウントポイントのリストを取得出来ませんでした。: {type}", + "There was an error with message: " : "メッセージ付きのエラーが発生しました:", + "External mount error" : "外部マウントエラー", + "goto-external-storage" : "外部ストレージに行く", "Access key" : "アクセスキー", "Secret key" : "シークレットキー", "Builtin" : "ビルトイン", diff --git a/apps/files_external/l10n/nl.js b/apps/files_external/l10n/nl.js index 5051689216f..3200988ec2b 100644 --- a/apps/files_external/l10n/nl.js +++ b/apps/files_external/l10n/nl.js @@ -36,6 +36,15 @@ OC.L10N.register( "(group)" : "(groep)", "Admin defined" : "Beheerder gedefinieerd", "Saved" : "Bewaard", + "Empty response from the server" : "Lege reactie van de server", + "Couldn't access. Please logout and login to activate this mount point" : "Geen toegang. Log uit en opnieuw in om dit koppelpunt te activeren", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Kon geen informatie van de ownCloud server krijgen: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Kon geen overzicht met externe koppelpunten krijgen: {type}", + "There was an error with message: " : "Er was een fout met de volgende melding:", + "External mount error" : "Extern koppelpunt fout", + "goto-external-storage" : "goto-external-storage", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kon geen overzicht met Windows netwerk koppelpunten krijgen: lege reactie van de server", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Sommige van de geconfigureerde koppelpunten zijn niet verbonden. Klok op de rode rij(en) voor meer informatie", "Access key" : "Access Key", "Secret key" : "Geheime sleutel", "Builtin" : "Ingebouwd", diff --git a/apps/files_external/l10n/nl.json b/apps/files_external/l10n/nl.json index d8a254bad1b..09b912445c1 100644 --- a/apps/files_external/l10n/nl.json +++ b/apps/files_external/l10n/nl.json @@ -34,6 +34,15 @@ "(group)" : "(groep)", "Admin defined" : "Beheerder gedefinieerd", "Saved" : "Bewaard", + "Empty response from the server" : "Lege reactie van de server", + "Couldn't access. Please logout and login to activate this mount point" : "Geen toegang. Log uit en opnieuw in om dit koppelpunt te activeren", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Kon geen informatie van de ownCloud server krijgen: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Kon geen overzicht met externe koppelpunten krijgen: {type}", + "There was an error with message: " : "Er was een fout met de volgende melding:", + "External mount error" : "Extern koppelpunt fout", + "goto-external-storage" : "goto-external-storage", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Kon geen overzicht met Windows netwerk koppelpunten krijgen: lege reactie van de server", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Sommige van de geconfigureerde koppelpunten zijn niet verbonden. Klok op de rode rij(en) voor meer informatie", "Access key" : "Access Key", "Secret key" : "Geheime sleutel", "Builtin" : "Ingebouwd", diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index 0d4f04ae226..296d51836ca 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -17,6 +17,7 @@ OC.L10N.register( "Unsatisfied backend parameters" : "Parâmetros de back-end não-atendidos", "Unsatisfied authentication mechanism parameters" : "Parâmetros de mecanismos de autenticação não satisfeitos", "Insufficient data: %s" : "Dados insuficientes: %s", + "%s" : "%s", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Permitir acesso", @@ -35,6 +36,15 @@ OC.L10N.register( "(group)" : "(grupo)", "Admin defined" : "Definido pelo administrador", "Saved" : "Salvo", + "Empty response from the server" : "Resposta vazia a partir do servidor", + "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível acessar. Por favor, desconectar e conectar novamente para ativar este ponto de montagem", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível obter as informações do servidor ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Não foi possível obter a lista de pontos de montagem externos: {type}", + "There was an error with message: " : "Houve um erro com a mensagem:", + "External mount error" : "Erro de montagem externa", + "goto-external-storage" : "ir-montagem-externa", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", "Access key" : "Chave da acesso", "Secret key" : "Chave secreta", "Builtin" : "Construídas em", diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index d49e818ea0f..5e4aed0b191 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -15,6 +15,7 @@ "Unsatisfied backend parameters" : "Parâmetros de back-end não-atendidos", "Unsatisfied authentication mechanism parameters" : "Parâmetros de mecanismos de autenticação não satisfeitos", "Insufficient data: %s" : "Dados insuficientes: %s", + "%s" : "%s", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Permitir acesso", @@ -33,6 +34,15 @@ "(group)" : "(grupo)", "Admin defined" : "Definido pelo administrador", "Saved" : "Salvo", + "Empty response from the server" : "Resposta vazia a partir do servidor", + "Couldn't access. Please logout and login to activate this mount point" : "Não foi possível acessar. Por favor, desconectar e conectar novamente para ativar este ponto de montagem", + "Couldn't get the information from the ownCloud server: {code} {type}" : "Não foi possível obter as informações do servidor ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "Não foi possível obter a lista de pontos de montagem externos: {type}", + "There was an error with message: " : "Houve um erro com a mensagem:", + "External mount error" : "Erro de montagem externa", + "goto-external-storage" : "ir-montagem-externa", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível obter a lista unidades de pontos de montagem da rede do Windows: resposta vazia a partir do servidor", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor clique na linha vermelha(s) para mais informações", "Access key" : "Chave da acesso", "Secret key" : "Chave secreta", "Builtin" : "Construídas em", diff --git a/apps/files_external/l10n/sq.js b/apps/files_external/l10n/sq.js index a708bca92d0..3e2de744b15 100644 --- a/apps/files_external/l10n/sq.js +++ b/apps/files_external/l10n/sq.js @@ -36,6 +36,14 @@ OC.L10N.register( "(group)" : "(grup)", "Admin defined" : "Përcaktuar nga përgjegjësi", "Saved" : "U ruajt", + "Empty response from the server" : "Përgjigje e zbrazët prej shërbyesit", + "Couldn't access. Please logout and login to activate this mount point" : "S’fut dot. Ju lutemi, dilni dhe hyni që të aktivizohet kjo pikë montimi", + "Couldn't get the information from the ownCloud server: {code} {type}" : "S’u morën dot të dhëna nga shërbyesi ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "S’u mor dot lista e pikave të jashtme të montimit: {type}", + "There was an error with message: " : "Pati një gabim me këtë mesazh:", + "External mount error" : "Gabim i jashtëm montimi", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "S’u mor dot lista e pikave të montimit Windows network drive: përgjigje e zbrazët nga shërbyesi", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Disa nga pikat e jashtme të formësuara të montimit s’janë të lidhura. Ju lutemi, klikoni në shigjetën(at) e kuqe për më tepër të dhëna", "Access key" : "Kyç hyrjesh", "Secret key" : "Kyç i fshehtë", "Builtin" : "I brendshëm", diff --git a/apps/files_external/l10n/sq.json b/apps/files_external/l10n/sq.json index 655a419c389..f184caf061e 100644 --- a/apps/files_external/l10n/sq.json +++ b/apps/files_external/l10n/sq.json @@ -34,6 +34,14 @@ "(group)" : "(grup)", "Admin defined" : "Përcaktuar nga përgjegjësi", "Saved" : "U ruajt", + "Empty response from the server" : "Përgjigje e zbrazët prej shërbyesit", + "Couldn't access. Please logout and login to activate this mount point" : "S’fut dot. Ju lutemi, dilni dhe hyni që të aktivizohet kjo pikë montimi", + "Couldn't get the information from the ownCloud server: {code} {type}" : "S’u morën dot të dhëna nga shërbyesi ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "S’u mor dot lista e pikave të jashtme të montimit: {type}", + "There was an error with message: " : "Pati një gabim me këtë mesazh:", + "External mount error" : "Gabim i jashtëm montimi", + "Couldn't get the list of Windows network drive mount points: empty response from the server" : "S’u mor dot lista e pikave të montimit Windows network drive: përgjigje e zbrazët nga shërbyesi", + "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Disa nga pikat e jashtme të formësuara të montimit s’janë të lidhura. Ju lutemi, klikoni në shigjetën(at) e kuqe për më tepër të dhëna", "Access key" : "Kyç hyrjesh", "Secret key" : "Kyç i fshehtë", "Builtin" : "I brendshëm", diff --git a/apps/files_external/l10n/th_TH.js b/apps/files_external/l10n/th_TH.js index bb9c2dc909d..97a64e78e40 100644 --- a/apps/files_external/l10n/th_TH.js +++ b/apps/files_external/l10n/th_TH.js @@ -36,6 +36,13 @@ OC.L10N.register( "(group)" : "(กลุ่ม)", "Admin defined" : "ถูกกำหนดโดยผู้ดูแลระบบ", "Saved" : "บันทึกแล้ว", + "Empty response from the server" : "ไม่มีการตอบสนองจากเซิร์ฟเวอร์", + "Couldn't access. Please logout and login to activate this mount point" : "ไม่สามารถเข้าถึง กรุณออกจากระบบและาเข้าสู่ระบบใหม่เพื่อเปิดใช้งานจุดเชื่อมต่อนี้", + "Couldn't get the information from the ownCloud server: {code} {type}" : "ไม่สามารถรับข้อมูลจากเซิร์ฟเวอร์ ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "ไม่สามารถรับรายชื่อของจุดเชื่อมต่อภายนอก: {type}", + "There was an error with message: " : "มีข้อความแสดงข้อผิดพลาด", + "External mount error" : "การติดจากตั้งภายนอกเกิดข้อผิดพลาด", + "goto-external-storage" : "ไปยังพื้นที่จัดเก็บข้อมูลภายนอก", "Access key" : "คีย์การเข้าถึง", "Secret key" : "คีย์ลับ", "Builtin" : "ในตัว", diff --git a/apps/files_external/l10n/th_TH.json b/apps/files_external/l10n/th_TH.json index f38d99ae88b..de569c9d61f 100644 --- a/apps/files_external/l10n/th_TH.json +++ b/apps/files_external/l10n/th_TH.json @@ -34,6 +34,13 @@ "(group)" : "(กลุ่ม)", "Admin defined" : "ถูกกำหนดโดยผู้ดูแลระบบ", "Saved" : "บันทึกแล้ว", + "Empty response from the server" : "ไม่มีการตอบสนองจากเซิร์ฟเวอร์", + "Couldn't access. Please logout and login to activate this mount point" : "ไม่สามารถเข้าถึง กรุณออกจากระบบและาเข้าสู่ระบบใหม่เพื่อเปิดใช้งานจุดเชื่อมต่อนี้", + "Couldn't get the information from the ownCloud server: {code} {type}" : "ไม่สามารถรับข้อมูลจากเซิร์ฟเวอร์ ownCloud: {code} {type}", + "Couldn't get the list of external mount points: {type}" : "ไม่สามารถรับรายชื่อของจุดเชื่อมต่อภายนอก: {type}", + "There was an error with message: " : "มีข้อความแสดงข้อผิดพลาด", + "External mount error" : "การติดจากตั้งภายนอกเกิดข้อผิดพลาด", + "goto-external-storage" : "ไปยังพื้นที่จัดเก็บข้อมูลภายนอก", "Access key" : "คีย์การเข้าถึง", "Secret key" : "คีย์ลับ", "Builtin" : "ในตัว", diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 7a869847a63..f29e08dad25 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -90,6 +90,7 @@ class OC_Mount_Config { $userStoragesService->setUser($user); foreach ($userGlobalStoragesService->getStorages() as $storage) { + /** @var \OCA\Files_external\Lib\StorageConfig $storage */ $mountPoint = '/'.$uid.'/files'.$storage->getMountPoint(); $mountEntry = self::prepareMountPointEntry($storage, false); foreach ($mountEntry['options'] as &$option) { @@ -267,26 +268,6 @@ class OC_Mount_Config { } /** - * Write the mount points to the config file - * - * @param string|null $user If not null, personal for $user, otherwise system - * @param array $data Mount points - */ - public static function writeData($user, $data) { - if (isset($user)) { - $file = \OC::$server->getUserManager()->get($user)->getHome() . '/mount.json'; - } else { - $config = \OC::$server->getConfig(); - $datadir = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/'); - $file = $config->getSystemValue('mount_file', $datadir . '/mount.json'); - } - - $content = json_encode($data, JSON_PRETTY_PRINT); - @file_put_contents($file, $content); - @chmod($file, 0640); - } - - /** * Get backend dependency message * TODO: move into AppFramework along with templates * @@ -397,39 +378,9 @@ class OC_Mount_Config { } /** - * Merges mount points - * - * @param array $data Existing mount points - * @param array $mountPoint New mount point - * @param string $mountType - * @return array - */ - private static function mergeMountPoints($data, $mountPoint, $mountType) { - $applicable = key($mountPoint); - $mountPath = key($mountPoint[$applicable]); - if (isset($data[$mountType])) { - if (isset($data[$mountType][$applicable])) { - // Merge priorities - if (isset($data[$mountType][$applicable][$mountPath]) - && isset($data[$mountType][$applicable][$mountPath]['priority']) - && !isset($mountPoint[$applicable][$mountPath]['priority']) - ) { - $mountPoint[$applicable][$mountPath]['priority'] - = $data[$mountType][$applicable][$mountPath]['priority']; - } - $data[$mountType][$applicable] - = array_merge($data[$mountType][$applicable], $mountPoint[$applicable]); - } else { - $data[$mountType] = array_merge($data[$mountType], $mountPoint); - } - } else { - $data[$mountType] = $mountPoint; - } - return $data; - } - - /** * Returns the encryption cipher + * + * @return AES */ private static function getCipher() { $cipher = new AES(AES::MODE_CBC); @@ -441,6 +392,9 @@ class OC_Mount_Config { * Computes a hash based on the given configuration. * This is mostly used to find out whether configurations * are the same. + * + * @param array $config + * @return string */ public static function makeConfigHash($config) { $data = json_encode( diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php index 4e37e6a4004..4f68c3c7fde 100644 --- a/apps/files_external/lib/config/configadapter.php +++ b/apps/files_external/lib/config/configadapter.php @@ -114,7 +114,7 @@ class ConfigAdapter implements IMountProvider { * @return \OCP\Files\Mount\IMountPoint[] */ public function getMountsForUser(IUser $user, IStorageFactory $loader) { - $this->migrator->migrateUser(); + $this->migrator->migrateUser($user); $mounts = []; diff --git a/apps/files_external/lib/personalmount.php b/apps/files_external/lib/personalmount.php index 26f68ba32db..34ae516ea5e 100644 --- a/apps/files_external/lib/personalmount.php +++ b/apps/files_external/lib/personalmount.php @@ -35,12 +35,12 @@ class PersonalMount extends MountPoint implements MoveableMount { protected $storagesService; /** @var int */ - protected $storageId; + protected $numericStorageId; /** * @param UserStoragesService $storagesService * @param int $storageId - * @param string|\OC\Files\Storage\Storage $storage + * @param \OCP\Files\Storage $storage * @param string $mountpoint * @param array $arguments (optional) configuration for the storage backend * @param \OCP\Files\Storage\IStorageFactory $loader @@ -57,7 +57,7 @@ class PersonalMount extends MountPoint implements MoveableMount { ) { parent::__construct($storage, $mountpoint, $arguments, $loader, $mountOptions); $this->storagesService = $storagesService; - $this->storageId = $storageId; + $this->numericStorageId = $storageId; } /** @@ -67,7 +67,7 @@ class PersonalMount extends MountPoint implements MoveableMount { * @return bool */ public function moveMount($target) { - $storage = $this->storagesService->getStorage($this->storageId); + $storage = $this->storagesService->getStorage($this->numericStorageId); // remove "/$user/files" prefix $targetParts = explode('/', trim($target, '/'), 3); $storage->setMountPoint($targetParts[2]); @@ -82,7 +82,7 @@ class PersonalMount extends MountPoint implements MoveableMount { * @return bool */ public function removeMount() { - $this->storagesService->removeStorage($this->storageId); + $this->storagesService->removeStorage($this->numericStorageId); return true; } } diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index a94840ead59..80b44a4cbdf 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -33,6 +33,7 @@ use Icewind\SMB\Exception\Exception; use Icewind\SMB\Exception\NotFoundException; use Icewind\SMB\NativeServer; use Icewind\SMB\Server; +use Icewind\Streams\CallbackWrapper; use Icewind\Streams\IteratorDirectory; use OC\Files\Filesystem; @@ -189,7 +190,10 @@ class SMB extends Common { return $this->share->read($fullPath); case 'w': case 'wb': - return $this->share->write($fullPath); + $source = $this->share->write($fullPath); + return CallBackWrapper::wrap($source, null, null, function () use ($fullPath) { + unset($this->statCache[$fullPath]); + }); case 'a': case 'ab': case 'r+': @@ -219,7 +223,8 @@ class SMB extends Common { } $source = fopen($tmpFile, $mode); $share = $this->share; - return CallBackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) { + return CallbackWrapper::wrap($source, null, null, function () use ($tmpFile, $fullPath, $share) { + unset($this->statCache[$fullPath]); $share->put($tmpFile, $fullPath); unlink($tmpFile); }); diff --git a/apps/files_external/migration/dummyusersession.php b/apps/files_external/migration/dummyusersession.php new file mode 100644 index 00000000000..9ffbfd6309f --- /dev/null +++ b/apps/files_external/migration/dummyusersession.php @@ -0,0 +1,51 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @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\Files_external\Migration; + +use OCP\IUser; +use OCP\IUserSession; + +class DummyUserSession implements IUserSession { + + /** + * @var IUser + */ + private $user; + + public function login($user, $password) { + } + + public function logout() { + } + + public function setUser($user) { + $this->user = $user; + } + + public function getUser() { + return $this->user; + } + + public function isLoggedIn() { + return !is_null($this->user); + } +} diff --git a/apps/files_external/migration/storagemigrator.php b/apps/files_external/migration/storagemigrator.php index c8e323121ea..b469205ac55 100644 --- a/apps/files_external/migration/storagemigrator.php +++ b/apps/files_external/migration/storagemigrator.php @@ -32,6 +32,7 @@ use OCA\Files_external\Service\UserStoragesService; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; +use OCP\IUser; use OCP\IUserSession; /** @@ -49,11 +50,6 @@ class StorageMigrator { private $dbConfig; /** - * @var IUserSession - */ - private $userSession; - - /** * @var IConfig */ private $config; @@ -73,7 +69,6 @@ class StorageMigrator { * * @param BackendService $backendService * @param DBConfigService $dbConfig - * @param IUserSession $userSession * @param IConfig $config * @param IDBConnection $connection * @param ILogger $logger @@ -81,14 +76,12 @@ class StorageMigrator { public function __construct( BackendService $backendService, DBConfigService $dbConfig, - IUserSession $userSession, IConfig $config, IDBConnection $connection, ILogger $logger ) { $this->backendService = $backendService; $this->dbConfig = $dbConfig; - $this->userSession = $userSession; $this->config = $config; $this->connection = $connection; $this->logger = $logger; @@ -121,14 +114,18 @@ class StorageMigrator { /** * Migrate personal storages configured by the current user + * + * @param IUser $user */ - public function migrateUser() { - $userId = $this->userSession->getUser()->getUID(); + public function migrateUser(IUser $user) { + $dummySession = new DummyUserSession(); + $dummySession->setUser($user); + $userId = $user->getUID(); $userVersion = $this->config->getUserValue($userId, 'files_external', 'config_version', '0.0.0'); if (version_compare($userVersion, '0.5.0', '<')) { $this->config->setUserValue($userId, 'files_external', 'config_version', '0.5.0'); - $legacyService = new UserLegacyStoragesService($this->backendService, $this->userSession); - $storageService = new UserStoragesService($this->backendService, $this->dbConfig, $this->userSession); + $legacyService = new UserLegacyStoragesService($this->backendService, $dummySession); + $storageService = new UserStoragesService($this->backendService, $this->dbConfig, $dummySession); $this->migrate($legacyService, $storageService); } diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 9be6498435c..97f79c13324 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -185,6 +185,9 @@ abstract class StoragesService { */ abstract public function getVisibilityType(); + /** + * @return integer + */ protected function getType() { return DBConfigService::MOUNT_TYPE_ADMIN; } diff --git a/apps/files_external/service/userglobalstoragesservice.php b/apps/files_external/service/userglobalstoragesservice.php index cb49f0f6726..e58815f8a79 100644 --- a/apps/files_external/service/userglobalstoragesservice.php +++ b/apps/files_external/service/userglobalstoragesservice.php @@ -89,6 +89,9 @@ class UserGlobalStoragesService extends GlobalStoragesService { throw new \DomainException('UserGlobalStoragesService writing disallowed'); } + /** + * @param integer $id + */ public function removeStorage($id) { throw new \DomainException('UserGlobalStoragesService writing disallowed'); } diff --git a/apps/files_external/tests/env/entrypoint.sh b/apps/files_external/tests/env/entrypoint.sh new file mode 100755 index 00000000000..6dd6ef23fb6 --- /dev/null +++ b/apps/files_external/tests/env/entrypoint.sh @@ -0,0 +1,274 @@ +#!/bin/bash +set -e + +: ${CLUSTER:=ceph} +: ${RGW_NAME:=$(hostname -s)} +: ${MON_NAME:=$(hostname -s)} +: ${RGW_CIVETWEB_PORT:=80} +: ${OSD_SIZE:=100} + +: ${KEYSTONE_ADMIN_TOKEN:=admin} +: ${KEYSTONE_ADMIN_PORT:=35357} +: ${KEYSTONE_PUBLIC_PORT:=5001} + +: ${KEYSTONE_SERVICE:=${CLUSTER}} +: ${KEYSTONE_ENDPOINT_REGION:=region} + +: ${KEYSTONE_ADMIN_USER:=admin} +: ${KEYSTONE_ADMIN_TENANT:=admin} +: ${KEYSTONE_ADMIN_PASS:=admin} + +ip_address=$(head -n1 /etc/hosts | cut -d" " -f1) +: ${MON_IP:=${ip_address}} +subnet=$(ip route | grep "src ${ip_address}" | cut -d" " -f1) +: ${CEPH_NETWORK:=${subnet}} + +####### +# MON # +####### + +if [ ! -n "$CEPH_NETWORK" ]; then + echo "ERROR- CEPH_NETWORK must be defined as the name of the network for the OSDs" + exit 1 +fi + +if [ ! -n "$MON_IP" ]; then + echo "ERROR- MON_IP must be defined as the IP address of the monitor" + exit 1 +fi + +# bootstrap MON +if [ ! -e /etc/ceph/ceph.conf ]; then + fsid=$(uuidgen) + cat <<ENDHERE >/etc/ceph/${CLUSTER}.conf +[global] +fsid = $fsid +mon initial members = ${MON_NAME} +mon host = ${MON_IP} +auth cluster required = cephx +auth service required = cephx +auth client required = cephx +osd crush chooseleaf type = 0 +osd journal size = 100 +osd pool default pg num = 8 +osd pool default pgp num = 8 +osd pool default size = 1 +public network = ${CEPH_NETWORK} +cluster network = ${CEPH_NETWORK} +debug ms = 1 + +[mon] +debug mon = 20 +debug paxos = 20 +debug auth = 20 + +[osd] +debug osd = 20 +debug filestore = 20 +debug journal = 20 +debug monc = 20 + +[mds] +debug mds = 20 +debug mds balancer = 20 +debug mds log = 20 +debug mds migrator = 20 + +[client.radosgw.gateway] +rgw keystone url = http://${MON_IP}:${KEYSTONE_ADMIN_PORT} +rgw keystone admin token = ${KEYSTONE_ADMIN_TOKEN} +rgw keystone accepted roles = _member_ +ENDHERE + + # Generate administrator key + ceph-authtool /etc/ceph/${CLUSTER}.client.admin.keyring --create-keyring --gen-key -n client.admin --set-uid=0 --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow' + + # Generate the mon. key + ceph-authtool /etc/ceph/${CLUSTER}.mon.keyring --create-keyring --gen-key -n mon. --cap mon 'allow *' + + # Generate initial monitor map + monmaptool --create --add ${MON_NAME} ${MON_IP} --fsid ${fsid} /etc/ceph/monmap +fi + +# If we don't have a monitor keyring, this is a new monitor +if [ ! -e /var/lib/ceph/mon/${CLUSTER}-${MON_NAME}/keyring ]; then + + if [ ! -e /etc/ceph/${CLUSTER}.client.admin.keyring ]; then + echo "ERROR- /etc/ceph/${CLUSTER}.client.admin.keyring must exist; get it from your existing mon" + exit 2 + fi + + if [ ! -e /etc/ceph/${CLUSTER}.mon.keyring ]; then + echo "ERROR- /etc/ceph/${CLUSTER}.mon.keyring must exist. You can extract it from your current monitor by running 'ceph auth get mon. -o /tmp/${CLUSTER}.mon.keyring'" + exit 3 + fi + + if [ ! -e /etc/ceph/monmap ]; then + echo "ERROR- /etc/ceph/monmap must exist. You can extract it from your current monitor by running 'ceph mon getmap -o /tmp/monmap'" + exit 4 + fi + + # Import the client.admin keyring and the monitor keyring into a new, temporary one + ceph-authtool /tmp/${CLUSTER}.mon.keyring --create-keyring --import-keyring /etc/ceph/${CLUSTER}.client.admin.keyring + ceph-authtool /tmp/${CLUSTER}.mon.keyring --import-keyring /etc/ceph/${CLUSTER}.mon.keyring + + # Make the monitor directory + mkdir -p /var/lib/ceph/mon/${CLUSTER}-${MON_NAME} + + # Prepare the monitor daemon's directory with the map and keyring + ceph-mon --mkfs -i ${MON_NAME} --monmap /etc/ceph/monmap --keyring /tmp/${CLUSTER}.mon.keyring + + # Clean up the temporary key + rm /tmp/${CLUSTER}.mon.keyring +fi + +# start MON +ceph-mon -i ${MON_NAME} --public-addr ${MON_IP}:6789 + +# change replica size +ceph osd pool set rbd size 1 + + +####### +# OSD # +####### + +if [ ! -e /var/lib/ceph/osd/${CLUSTER}-0/keyring ]; then + # bootstrap OSD + mkdir -p /var/lib/ceph/osd/${CLUSTER}-0 + # skip btrfs HACK if btrfs is already in place + if [ "$(stat -f /var/lib/ceph/osd/${CLUSTER}-0 2>/dev/null | grep btrfs | wc -l)" == "0" ]; then + # HACK create btrfs loopback device + echo "creating osd storage image" + dd if=/dev/zero of=/tmp/osddata bs=1M count=${OSD_SIZE} + mkfs.btrfs /tmp/osddata + echo "mounting via loopback" + mount -o loop /tmp/osddata /var/lib/ceph/osd/${CLUSTER}-0 + echo "now mounted:" + mount + # end HACK + fi + echo "creating osd" + ceph osd create + echo "creating osd filesystem" + ceph-osd -i 0 --mkfs + echo "creating osd keyring" + ceph auth get-or-create osd.0 osd 'allow *' mon 'allow profile osd' -o /var/lib/ceph/osd/${CLUSTER}-0/keyring + echo "configuring osd crush" + ceph osd crush add 0 1 root=default host=$(hostname -s) + echo "adding osd keyring" + ceph-osd -i 0 -k /var/lib/ceph/osd/${CLUSTER}-0/keyring +fi + +# start OSD +echo "starting osd" +ceph-osd --cluster=${CLUSTER} -i 0 + +#sleep 10 + +####### +# MDS # +####### + +if [ ! -e /var/lib/ceph/mds/${CLUSTER}-0/keyring ]; then + # create ceph filesystem + echo "creating osd pool" + ceph osd pool create cephfs_data 8 + echo "creating osd pool metadata" + ceph osd pool create cephfs_metadata 8 + echo "creating cephfs" + ceph fs new cephfs cephfs_metadata cephfs_data + + # bootstrap MDS + mkdir -p /var/lib/ceph/mds/${CLUSTER}-0 + echo "creating mds auth" + ceph auth get-or-create mds.0 mds 'allow' osd 'allow *' mon 'allow profile mds' > /var/lib/ceph/mds/${CLUSTER}-0/keyring +fi + +# start MDS +echo "starting mds" +ceph-mds --cluster=${CLUSTER} -i 0 + +#sleep 10 + + +####### +# RGW # +####### + +if [ ! -e /var/lib/ceph/radosgw/${RGW_NAME}/keyring ]; then + # bootstrap RGW + mkdir -p /var/lib/ceph/radosgw/${RGW_NAME} + echo "creating rgw auth" + ceph auth get-or-create client.radosgw.gateway osd 'allow rwx' mon 'allow rw' -o /var/lib/ceph/radosgw/${RGW_NAME}/keyring +fi + +# start RGW +echo "starting rgw" +radosgw -c /etc/ceph/ceph.conf -n client.radosgw.gateway -k /var/lib/ceph/radosgw/${RGW_NAME}/keyring --rgw-socket-path="" --rgw-frontends="civetweb port=${RGW_CIVETWEB_PORT}" + + +####### +# API # +####### + +# start ceph-rest-api +echo "starting rest api" +ceph-rest-api -n client.admin & + +############ +# Keystone # +############ + +if [ ! -e /etc/keystone/${CLUSTER}.conf ]; then + cat <<ENDHERE > /etc/keystone/${CLUSTER}.conf +[DEFAULT] +admin_token=${KEYSTONE_ADMIN_TOKEN} +admin_port=${KEYSTONE_ADMIN_PORT} +public_port=${KEYSTONE_PUBLIC_PORT} + +[database] +connection = sqlite:////var/lib/keystone/keystone.db +ENDHERE + + # start Keystone + echo "starting keystone" + keystone-all --config-file /etc/keystone/${CLUSTER}.conf & + + # wait until up + while ! nc ${MON_IP} ${KEYSTONE_ADMIN_PORT} </dev/null; do + sleep 1 + done + + export OS_SERVICE_TOKEN=${KEYSTONE_ADMIN_TOKEN} + export OS_SERVICE_ENDPOINT=http://${MON_IP}:${KEYSTONE_ADMIN_PORT}/v2.0 + + echo "creating keystone service ${KEYSTONE_SERVICE}" + keystone service-create --name ${KEYSTONE_SERVICE} --type object-store + echo "creating keystone endpoint ${KEYSTONE_SERVICE}" + keystone endpoint-create --service ${KEYSTONE_SERVICE} \ + --region ${KEYSTONE_ENDPOINT_REGION} \ + --publicurl http://${MON_IP}:${RGW_CIVETWEB_PORT}/swift/v1 \ + --internalurl http://${MON_IP}:${RGW_CIVETWEB_PORT}/swift/v1 \ + --adminurl http://${MON_IP}:${RGW_CIVETWEB_PORT}/swift/v1 + + echo "creating keystone user ${KEYSTONE_ADMIN_USER}" + keystone user-create --name=${KEYSTONE_ADMIN_USER} --pass=${KEYSTONE_ADMIN_PASS} --email=dev@null.com + echo "creating keystone tenant ${KEYSTONE_ADMIN_TENANT}" + keystone tenant-create --name=${KEYSTONE_ADMIN_TENANT} --description=admin + echo "adding keystone role _member_" + keystone user-role-add --user=${KEYSTONE_ADMIN_USER} --tenant=${KEYSTONE_ADMIN_TENANT} --role=_member_ + + echo "creating keystone role admin" + keystone role-create --name=admin + echo "adding keystone role admin" + keystone user-role-add --user=${KEYSTONE_ADMIN_USER} --tenant=${KEYSTONE_ADMIN_TENANT} --role=admin +fi + + +######### +# WATCH # +######### + +echo "watching ceph" +exec ceph -w diff --git a/apps/files_external/tests/env/start-smb-windows.sh b/apps/files_external/tests/env/start-smb-windows.sh index 9453b4eb3e7..a23c879dd96 100755 --- a/apps/files_external/tests/env/start-smb-windows.sh +++ b/apps/files_external/tests/env/start-smb-windows.sh @@ -19,7 +19,7 @@ user=smb-test password=!owncloud123 host=WIN-9GTFAS08C15 -if ! "$thisFolder"/env/wait-for-connection ${host} 445 0; then +if ! "$thisFolder"/env/wait-for-connection ${host} 445; then echo "[ERROR] Server not reachable" >&2 exit 1 fi diff --git a/apps/files_external/tests/env/start-swift-ceph.sh b/apps/files_external/tests/env/start-swift-ceph.sh index 936bb667e94..f3707aa39f6 100755 --- a/apps/files_external/tests/env/start-swift-ceph.sh +++ b/apps/files_external/tests/env/start-swift-ceph.sh @@ -25,7 +25,7 @@ echo "Fetch recent ${docker_image} docker image" docker pull ${docker_image} # retrieve current folder to place the config in the parent folder -thisFolder=`echo $0 | replace "env/start-swift-ceph.sh" ""` +thisFolder=`echo $0 | sed 's#env/start-swift-ceph\.sh##'` if [ -z "$thisFolder" ]; then thisFolder="." @@ -38,6 +38,7 @@ pass=testing tenant=testenant region=testregion service=testceph +endpointFolder="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" container=`docker run -d \ -e KEYSTONE_PUBLIC_PORT=${port} \ @@ -46,7 +47,10 @@ container=`docker run -d \ -e KEYSTONE_ADMIN_TENANT=${tenant} \ -e KEYSTONE_ENDPOINT_REGION=${region} \ -e KEYSTONE_SERVICE=${service} \ - ${docker_image}` + -e OSD_SIZE=300 \ + -v ${endpointFolder}/entrypoint.sh:/entrypoint.sh \ + --privileged \ + --entrypoint /entrypoint.sh ${docker_image}` host=`docker inspect --format="{{.NetworkSettings.IPAddress}}" $container` @@ -57,8 +61,9 @@ echo "${docker_image} container: $container" echo $container >> $thisFolder/dockerContainerCeph.$EXECUTOR_NUMBER.swift echo -n "Waiting for ceph initialization" -if ! "$thisFolder"/env/wait-for-connection ${host} 80 60; then - echo "[ERROR] Waited 60 seconds, no response" >&2 +if ! "$thisFolder"/env/wait-for-connection ${host} 80 600; then + echo "[ERROR] Waited 600 seconds, no response" >&2 + docker logs $container exit 1 fi sleep 1 diff --git a/apps/files_external/tests/personalmounttest.php b/apps/files_external/tests/personalmounttest.php new file mode 100644 index 00000000000..b56d69aa9be --- /dev/null +++ b/apps/files_external/tests/personalmounttest.php @@ -0,0 +1,50 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @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\Files_external\Tests; + +use OC\Files\Mount\Manager; +use OCA\Files_External\Lib\PersonalMount; +use Test\TestCase; + +class PersonalMountTest extends TestCase { + public function testFindByStorageId() { + /** @var \OCA\Files_External\Service\UserStoragesService $storageService */ + $storageService = $this->getMockBuilder('\OCA\Files_External\Service\UserStoragesService') + ->disableOriginalConstructor() + ->getMock(); + + $storage = $this->getMockBuilder('\OC\Files\Storage\Storage') + ->disableOriginalConstructor() + ->getMock(); + + $storage->expects($this->any()) + ->method('getId') + ->will($this->returnValue('dummy')); + + $mount = new PersonalMount($storageService, 10, $storage, '/foo'); + + $mountManager = new Manager(); + $mountManager->addMount($mount); + + $this->assertEquals([$mount], $mountManager->findByStorageId('dummy')); + } +} diff --git a/apps/files_external/tests/service/globalstoragesservicetest.php b/apps/files_external/tests/service/globalstoragesservicetest.php index 7c77616563c..e620b05a51e 100644 --- a/apps/files_external/tests/service/globalstoragesservicetest.php +++ b/apps/files_external/tests/service/globalstoragesservicetest.php @@ -23,7 +23,6 @@ namespace OCA\Files_external\Tests\Service; use \OC\Files\Filesystem; -use OCA\Files_External\Service\DBConfigService; use \OCA\Files_external\Service\GlobalStoragesService; use \OCA\Files_external\NotFoundException; use \OCA\Files_external\Lib\StorageConfig; diff --git a/apps/files_sharing/ajax/external.php b/apps/files_sharing/ajax/external.php index 0f8a3d56cf0..2ba1cb470c2 100644 --- a/apps/files_sharing/ajax/external.php +++ b/apps/files_sharing/ajax/external.php @@ -40,6 +40,7 @@ if (OCA\Files_Sharing\Helper::isIncomingServer2serverShareEnabled() === false) { $token = $_POST['token']; $remote = $_POST['remote']; $owner = $_POST['owner']; +$ownerDisplayName = $_POST['ownerDisplayName']; $name = $_POST['name']; $password = $_POST['password']; @@ -49,6 +50,14 @@ if(!\OCP\Util::isValidFileName($name)) { exit(); } +$currentUser = \OC::$server->getUserSession()->getUser()->getUID(); +$currentServer = \OC::$server->getURLGenerator()->getAbsoluteURL('/'); +if (\OC\Share\Helper::isSameUserOnSameServer($owner, $remote, $currentUser, $currentServer )) { + \OCP\JSON::error(array('data' => array('message' => $l->t('Not allowed to create a federated share with the same user server')))); + exit(); +} + + $externalManager = new \OCA\Files_Sharing\External\Manager( \OC::$server->getDatabaseConnection(), \OC\Files\Filesystem::getMountManager(), @@ -68,7 +77,7 @@ if (substr($remote, 0, 5) === 'https') { } } -$mount = $externalManager->addShare($remote, $token, $password, $name, $owner, true); +$mount = $externalManager->addShare($remote, $token, $password, $name, $ownerDisplayName, true); /** * @var \OCA\Files_Sharing\External\Storage $storage diff --git a/apps/files_sharing/api/local.php b/apps/files_sharing/api/local.php index aaafafb269f..5b2f2e06e75 100644 --- a/apps/files_sharing/api/local.php +++ b/apps/files_sharing/api/local.php @@ -64,9 +64,10 @@ class Local { if ($shares === false) { return new \OC_OCS_Result(null, 404, 'could not get shares'); } else { + $mimetypeDetector = \OC::$server->getMimeTypeDetector(); foreach ($shares as &$share) { if ($share['item_type'] === 'file' && isset($share['path'])) { - $share['mimetype'] = \OC_Helper::getFileNameMimeType($share['path']); + $share['mimetype'] = $mimetypeDetector->detectPath($share['path']); if (\OC::$server->getPreviewManager()->isMimeSupported($share['mimetype'])) { $share['isPreviewAvailable'] = true; } @@ -227,9 +228,10 @@ class Local { private static function getFilesSharedWithMe() { try { $shares = \OCP\Share::getItemsSharedWith('file'); + $mimetypeDetector = \OC::$server->getMimeTypeDetector(); foreach ($shares as &$share) { if ($share['item_type'] === 'file') { - $share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']); + $share['mimetype'] = $mimetypeDetector->detectPath($share['file_target']); if (\OC::$server->getPreviewManager()->isMimeSupported($share['mimetype'])) { $share['isPreviewAvailable'] = true; } diff --git a/apps/files_sharing/js/external.js b/apps/files_sharing/js/external.js index f658de307ab..45a6ef02758 100644 --- a/apps/files_sharing/js/external.js +++ b/apps/files_sharing/js/external.js @@ -19,7 +19,7 @@ */ OCA.Sharing.showAddExternalDialog = function (share, passwordProtected, callback) { var remote = share.remote; - var owner = share.owner; + var owner = share.ownerDisplayName || share.owner; var name = share.name; var remoteClean = (remote.substr(0, 8) === 'https://') ? remote.substr(8) : remote.substr(7); @@ -92,6 +92,7 @@ remote: share.remote, token: share.token, owner: share.owner, + ownerDisplayName: share.ownerDisplayName || share.owner, name: share.name, password: password}, function(result) { if (result.status === 'error') { diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 70c1ba5c0c2..af808447381 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -242,9 +242,10 @@ OCA.Sharing.PublicApp = { var remote = $(this).find('input[type="text"]').val(); var token = $('#sharingToken').val(); var owner = $('#save').data('owner'); + var ownerDisplayName = $('#save').data('owner-display-name'); var name = $('#save').data('name'); var isProtected = $('#save').data('protected') ? 1 : 0; - OCA.Sharing.PublicApp._saveToOwnCloud(remote, token, owner, name, isProtected); + OCA.Sharing.PublicApp._saveToOwnCloud(remote, token, owner, ownerDisplayName, name, isProtected); }); $('#remote_address').on("keyup paste", function() { @@ -291,7 +292,7 @@ OCA.Sharing.PublicApp = { this.fileList.changeDirectory(params.path || params.dir, false, true); }, - _saveToOwnCloud: function (remote, token, owner, name, isProtected) { + _saveToOwnCloud: function (remote, token, owner, ownerDisplayName, name, isProtected) { var location = window.location.protocol + '//' + window.location.host + OC.webroot; if(remote.substr(-1) !== '/') { @@ -299,7 +300,7 @@ OCA.Sharing.PublicApp = { }; var url = remote + 'index.php/apps/files#' + 'remote=' + encodeURIComponent(location) // our location is the remote for the other server - + "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) + "&name=" + encodeURIComponent(name) + "&protected=" + isProtected; + + "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) +"&ownerDisplayName=" + encodeURIComponent(ownerDisplayName) + "&name=" + encodeURIComponent(name) + "&protected=" + isProtected; if (remote.indexOf('://') > 0) { diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 3d105f283d8..f8d89828f4d 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -45,6 +45,9 @@ if (fileData.type === 'file') { // files can't be shared with delete permissions sharePermissions = sharePermissions & ~OC.PERMISSION_DELETE; + + // create permissions don't mean anything for files + sharePermissions = sharePermissions & ~OC.PERMISSION_CREATE; } tr.attr('data-share-permissions', sharePermissions); if (fileData.shareOwner) { diff --git a/apps/files_sharing/l10n/es.js b/apps/files_sharing/l10n/es.js index 650fb5c524a..e77c4b974f7 100644 --- a/apps/files_sharing/l10n/es.js +++ b/apps/files_sharing/l10n/es.js @@ -52,6 +52,7 @@ OC.L10N.register( "Shared by %2$s" : "Compartido por %2$s", "Shared via public link" : "Compartido vía enlace público", "Shares" : "Compartidos", + "You received %2$s as a remote share from %1$s" : "Ha recibido %2$s como un recurso compartido de %1$s", "Accept" : "Aceptar", "Decline" : "Denegar", "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartirlo conmigo a través de mi ID Nube Federada #ownCloud, ver %s", diff --git a/apps/files_sharing/l10n/es.json b/apps/files_sharing/l10n/es.json index 6f963ab8fee..96fb368f76b 100644 --- a/apps/files_sharing/l10n/es.json +++ b/apps/files_sharing/l10n/es.json @@ -50,6 +50,7 @@ "Shared by %2$s" : "Compartido por %2$s", "Shared via public link" : "Compartido vía enlace público", "Shares" : "Compartidos", + "You received %2$s as a remote share from %1$s" : "Ha recibido %2$s como un recurso compartido de %1$s", "Accept" : "Aceptar", "Decline" : "Denegar", "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartirlo conmigo a través de mi ID Nube Federada #ownCloud, ver %s", diff --git a/apps/files_sharing/l10n/et_EE.js b/apps/files_sharing/l10n/et_EE.js index 6d02bdd6ced..2fcce251407 100644 --- a/apps/files_sharing/l10n/et_EE.js +++ b/apps/files_sharing/l10n/et_EE.js @@ -30,6 +30,8 @@ OC.L10N.register( "You shared %1$s with group %2$s" : "Jagasid %1$s %2$s grupiga", "%2$s shared %1$s with you" : "%2$s jagas sinuga %1$s", "You shared %1$s via link" : "Jagasid %1$s lingiga", + "Downloaded via public link" : "Alla laetud avalikult lingilt", + "Shared with %2$s" : "Jagatud kasutajaga %2$s", "Shares" : "Jagamised", "Accept" : "Nõustu", "Decline" : "Lükka tagasi", diff --git a/apps/files_sharing/l10n/et_EE.json b/apps/files_sharing/l10n/et_EE.json index 481a75210ee..34ddd9f1b15 100644 --- a/apps/files_sharing/l10n/et_EE.json +++ b/apps/files_sharing/l10n/et_EE.json @@ -28,6 +28,8 @@ "You shared %1$s with group %2$s" : "Jagasid %1$s %2$s grupiga", "%2$s shared %1$s with you" : "%2$s jagas sinuga %1$s", "You shared %1$s via link" : "Jagasid %1$s lingiga", + "Downloaded via public link" : "Alla laetud avalikult lingilt", + "Shared with %2$s" : "Jagatud kasutajaga %2$s", "Shares" : "Jagamised", "Accept" : "Nõustu", "Decline" : "Lükka tagasi", diff --git a/apps/files_sharing/l10n/fi_FI.js b/apps/files_sharing/l10n/fi_FI.js index a87d6156d40..accd43020ff 100644 --- a/apps/files_sharing/l10n/fi_FI.js +++ b/apps/files_sharing/l10n/fi_FI.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "Palvelimelta-palvelimelle-jakaminen ei ole käytössä tällä palvelimella", "The mountpoint name contains invalid characters." : "Liitospisteen nimi sisältää virheellisiä merkkejä.", + "Not allowed to create a federated share with the same user server" : "Saman käyttäjäpalvelimen kanssa ei ole sallittua luoda federoitua jakoa", "Invalid or untrusted SSL certificate" : "Virheellinen tai ei-luotettu SSL-varmenne", "Could not authenticate to remote share, password might be wrong" : "Tunnistautuminen etäjakoa kohtaan epäonnistui. Salasana saattaa olla väärä", "Storage not valid" : "Tallennustila ei ole kelvollinen", diff --git a/apps/files_sharing/l10n/fi_FI.json b/apps/files_sharing/l10n/fi_FI.json index 172b8476092..e321d64732f 100644 --- a/apps/files_sharing/l10n/fi_FI.json +++ b/apps/files_sharing/l10n/fi_FI.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "Palvelimelta-palvelimelle-jakaminen ei ole käytössä tällä palvelimella", "The mountpoint name contains invalid characters." : "Liitospisteen nimi sisältää virheellisiä merkkejä.", + "Not allowed to create a federated share with the same user server" : "Saman käyttäjäpalvelimen kanssa ei ole sallittua luoda federoitua jakoa", "Invalid or untrusted SSL certificate" : "Virheellinen tai ei-luotettu SSL-varmenne", "Could not authenticate to remote share, password might be wrong" : "Tunnistautuminen etäjakoa kohtaan epäonnistui. Salasana saattaa olla väärä", "Storage not valid" : "Tallennustila ei ole kelvollinen", diff --git a/apps/files_sharing/l10n/it.js b/apps/files_sharing/l10n/it.js index 8cd66b34d24..39f7ced4bbd 100644 --- a/apps/files_sharing/l10n/it.js +++ b/apps/files_sharing/l10n/it.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "La condivisione tra server non è abilitata su questo server", "The mountpoint name contains invalid characters." : "Il nome del punto di mount contiene caratteri non validi.", + "Not allowed to create a federated share with the same user server" : "Non è consentito creare una condivisione federata con lo stesso server dell'utente", "Invalid or untrusted SSL certificate" : "Certificato SSL non valido o non attendibile", "Could not authenticate to remote share, password might be wrong" : "Impossibile autenticarsi sulla condivisione remota, la password potrebbe essere errata", "Storage not valid" : "Archiviazione non valida", diff --git a/apps/files_sharing/l10n/it.json b/apps/files_sharing/l10n/it.json index b93dafd7555..f40fbb4b232 100644 --- a/apps/files_sharing/l10n/it.json +++ b/apps/files_sharing/l10n/it.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "La condivisione tra server non è abilitata su questo server", "The mountpoint name contains invalid characters." : "Il nome del punto di mount contiene caratteri non validi.", + "Not allowed to create a federated share with the same user server" : "Non è consentito creare una condivisione federata con lo stesso server dell'utente", "Invalid or untrusted SSL certificate" : "Certificato SSL non valido o non attendibile", "Could not authenticate to remote share, password might be wrong" : "Impossibile autenticarsi sulla condivisione remota, la password potrebbe essere errata", "Storage not valid" : "Archiviazione non valida", diff --git a/apps/files_sharing/l10n/ja.js b/apps/files_sharing/l10n/ja.js index e40acc0104a..e3427ea1f7b 100644 --- a/apps/files_sharing/l10n/ja.js +++ b/apps/files_sharing/l10n/ja.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "このサーバーでは、サーバー間の共有が有効ではありません", "The mountpoint name contains invalid characters." : "マウントポイント名 に不正な文字列が含まれています。", + "Not allowed to create a federated share with the same user server" : "同じユーザーのサーバーでフェデレーション共有を作成することは出来ません", "Invalid or untrusted SSL certificate" : "無効または信頼できないSSL証明書", "Could not authenticate to remote share, password might be wrong" : "リモート共有が認証できませんでした,パスワードが間違っているかもしれません", "Storage not valid" : "ストレージが無効です", diff --git a/apps/files_sharing/l10n/ja.json b/apps/files_sharing/l10n/ja.json index 989a723b2a8..9dc64e2b3ec 100644 --- a/apps/files_sharing/l10n/ja.json +++ b/apps/files_sharing/l10n/ja.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "このサーバーでは、サーバー間の共有が有効ではありません", "The mountpoint name contains invalid characters." : "マウントポイント名 に不正な文字列が含まれています。", + "Not allowed to create a federated share with the same user server" : "同じユーザーのサーバーでフェデレーション共有を作成することは出来ません", "Invalid or untrusted SSL certificate" : "無効または信頼できないSSL証明書", "Could not authenticate to remote share, password might be wrong" : "リモート共有が認証できませんでした,パスワードが間違っているかもしれません", "Storage not valid" : "ストレージが無効です", diff --git a/apps/files_sharing/l10n/lt_LT.js b/apps/files_sharing/l10n/lt_LT.js index 93848326605..517cc0915be 100644 --- a/apps/files_sharing/l10n/lt_LT.js +++ b/apps/files_sharing/l10n/lt_LT.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "Serveris - serveris dalinimasis neįjungtas šiame serveryje", "The mountpoint name contains invalid characters." : "Prijungimo taškas su neleistinais simboliais.", + "Not allowed to create a federated share with the same user server" : "Neleidžiama dalintis tarp serverių, nes vartotojas tame pačiame serveryje.", "Invalid or untrusted SSL certificate" : "Netinkamas arba nepatikimas SSL sertifikatas", "Could not authenticate to remote share, password might be wrong" : "Nepavyko identifikuotis serveryje, gal netinkamas slaptažodis", "Storage not valid" : "Talpykla negalioja", diff --git a/apps/files_sharing/l10n/lt_LT.json b/apps/files_sharing/l10n/lt_LT.json index 6d777024052..923971c336b 100644 --- a/apps/files_sharing/l10n/lt_LT.json +++ b/apps/files_sharing/l10n/lt_LT.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "Serveris - serveris dalinimasis neįjungtas šiame serveryje", "The mountpoint name contains invalid characters." : "Prijungimo taškas su neleistinais simboliais.", + "Not allowed to create a federated share with the same user server" : "Neleidžiama dalintis tarp serverių, nes vartotojas tame pačiame serveryje.", "Invalid or untrusted SSL certificate" : "Netinkamas arba nepatikimas SSL sertifikatas", "Could not authenticate to remote share, password might be wrong" : "Nepavyko identifikuotis serveryje, gal netinkamas slaptažodis", "Storage not valid" : "Talpykla negalioja", diff --git a/apps/files_sharing/l10n/mk.js b/apps/files_sharing/l10n/mk.js index a5de7fb5c09..5be22894b99 100644 --- a/apps/files_sharing/l10n/mk.js +++ b/apps/files_sharing/l10n/mk.js @@ -1,20 +1,44 @@ OC.L10N.register( "files_sharing", { + "Server to server sharing is not enabled on this server" : "Не е овозможено споделувањето од сервер на сервер на вашиот сервер", + "Invalid or untrusted SSL certificate" : "SSL сертификат кој е невалиден или недоверлив", + "Could not authenticate to remote share, password might be wrong" : "Не можам да се автентицирам на оддалеченото споделевање, веројатно лозинката не е исправна", + "Storage not valid" : "Сториџот не е валиден", + "Couldn't add remote share" : "Не можам да додадам оддалечено споделување", "Shared with you" : "Споделено со тебе", "Shared with others" : "Сподели со останатите", "Shared by link" : "Споделено со врска", + "Nothing shared with you yet" : "Сеуште ништо не е споделено со вас", + "Nothing shared yet" : "Уште ништо не е споделено", + "No shared links" : "Нема споделени врски/линкови", + "Remote share" : "Оддалечено споделување", + "Remote share password" : "Лозинка за оддалечаното споделување", "Cancel" : "Откажи", + "Add remote share" : "Додади оддалечно споделување", + "You can upload into this folder" : "Можете да прикачите во оваа папка", + "Invalid ownCloud url" : "Неисправен ownCloud url", "Shared by" : "Споделено од", "Sharing" : "Споделување", "A file or folder has been <strong>shared</strong>" : "Датотека или фолдер беше <strong>споделен</strong>", "You shared %1$s with %2$s" : "Вие споделивте %1$s со %2$s", "You shared %1$s with group %2$s" : "Вие споделивте %1$s со групата %2$s", "%2$s shared %1$s with you" : "%2$s споделено %1$s со вас", + "Downloaded via public link" : "Преземи преку јавен линк", + "Shared with %2$s" : "Споделено со %2$s", + "Shared with group %2$s" : "Споделено со група %2$s", + "Shared with %3$s by %2$s" : "Споделено со %3$s од %2$s", + "Shared with group %3$s by %2$s" : "Споделено со група %3$s од %2$s", + "Shared via link by %2$s" : "Споделено со врска/линк од %2$s", + "Shared by %2$s" : "Споделено од %2$s", + "Shared via public link" : "Споделено со јавна врска/линк", "Shares" : "Споделувања", + "Accept" : "Прифати", + "Decline" : "Одбиј", "This share is password-protected" : "Ова споделување е заштитено со лозинка", "The password is wrong. Try again." : "Лозинката е грешна. Обиди се повторно.", "Password" : "Лозинка", + "No entries found in this folder" : "Нема ништо во оваа папка", "Name" : "Име", "Share time" : "Сподели време", "Sorry, this link doesn’t seem to work anymore." : "Извенете, но овој линк изгледа дека повеќе не функционира.", @@ -23,8 +47,17 @@ OC.L10N.register( "the link expired" : "времетраењето на линкот е изминато", "sharing is disabled" : "споделувањето не е дозволено", "For more info, please ask the person who sent this link." : "За повеќе информации, прашајте го лицето кое ви ја испратила врската.", + "Add to your ownCloud" : "Додади во вашиот ownCloud", "Download" : "Преземи", "Download %s" : "Преземи %s", - "Direct link" : "Директна врска" + "Direct link" : "Директна врска", + "Federated Cloud Sharing" : "Федерирано клауд споделување", + "Open documentation" : "Отвори ја документацијата", + "Federated Cloud" : "Федериран клауд", + "Your Federated Cloud ID:" : "Вашиот федериран Cloud ID:", + "Share it:" : "Сподели го:", + "Add to your website" : "Додади на твојот веб сајт", + "Share with me via ownCloud" : "Сподели со мене преку ownCloud", + "HTML Code:" : "HTML код:" }, "nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;"); diff --git a/apps/files_sharing/l10n/mk.json b/apps/files_sharing/l10n/mk.json index ad7eff6078b..40bcf9f8bde 100644 --- a/apps/files_sharing/l10n/mk.json +++ b/apps/files_sharing/l10n/mk.json @@ -1,18 +1,42 @@ { "translations": { + "Server to server sharing is not enabled on this server" : "Не е овозможено споделувањето од сервер на сервер на вашиот сервер", + "Invalid or untrusted SSL certificate" : "SSL сертификат кој е невалиден или недоверлив", + "Could not authenticate to remote share, password might be wrong" : "Не можам да се автентицирам на оддалеченото споделевање, веројатно лозинката не е исправна", + "Storage not valid" : "Сториџот не е валиден", + "Couldn't add remote share" : "Не можам да додадам оддалечено споделување", "Shared with you" : "Споделено со тебе", "Shared with others" : "Сподели со останатите", "Shared by link" : "Споделено со врска", + "Nothing shared with you yet" : "Сеуште ништо не е споделено со вас", + "Nothing shared yet" : "Уште ништо не е споделено", + "No shared links" : "Нема споделени врски/линкови", + "Remote share" : "Оддалечено споделување", + "Remote share password" : "Лозинка за оддалечаното споделување", "Cancel" : "Откажи", + "Add remote share" : "Додади оддалечно споделување", + "You can upload into this folder" : "Можете да прикачите во оваа папка", + "Invalid ownCloud url" : "Неисправен ownCloud url", "Shared by" : "Споделено од", "Sharing" : "Споделување", "A file or folder has been <strong>shared</strong>" : "Датотека или фолдер беше <strong>споделен</strong>", "You shared %1$s with %2$s" : "Вие споделивте %1$s со %2$s", "You shared %1$s with group %2$s" : "Вие споделивте %1$s со групата %2$s", "%2$s shared %1$s with you" : "%2$s споделено %1$s со вас", + "Downloaded via public link" : "Преземи преку јавен линк", + "Shared with %2$s" : "Споделено со %2$s", + "Shared with group %2$s" : "Споделено со група %2$s", + "Shared with %3$s by %2$s" : "Споделено со %3$s од %2$s", + "Shared with group %3$s by %2$s" : "Споделено со група %3$s од %2$s", + "Shared via link by %2$s" : "Споделено со врска/линк од %2$s", + "Shared by %2$s" : "Споделено од %2$s", + "Shared via public link" : "Споделено со јавна врска/линк", "Shares" : "Споделувања", + "Accept" : "Прифати", + "Decline" : "Одбиј", "This share is password-protected" : "Ова споделување е заштитено со лозинка", "The password is wrong. Try again." : "Лозинката е грешна. Обиди се повторно.", "Password" : "Лозинка", + "No entries found in this folder" : "Нема ништо во оваа папка", "Name" : "Име", "Share time" : "Сподели време", "Sorry, this link doesn’t seem to work anymore." : "Извенете, но овој линк изгледа дека повеќе не функционира.", @@ -21,8 +45,17 @@ "the link expired" : "времетраењето на линкот е изминато", "sharing is disabled" : "споделувањето не е дозволено", "For more info, please ask the person who sent this link." : "За повеќе информации, прашајте го лицето кое ви ја испратила врската.", + "Add to your ownCloud" : "Додади во вашиот ownCloud", "Download" : "Преземи", "Download %s" : "Преземи %s", - "Direct link" : "Директна врска" + "Direct link" : "Директна врска", + "Federated Cloud Sharing" : "Федерирано клауд споделување", + "Open documentation" : "Отвори ја документацијата", + "Federated Cloud" : "Федериран клауд", + "Your Federated Cloud ID:" : "Вашиот федериран Cloud ID:", + "Share it:" : "Сподели го:", + "Add to your website" : "Додади на твојот веб сајт", + "Share with me via ownCloud" : "Сподели со мене преку ownCloud", + "HTML Code:" : "HTML код:" },"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;" }
\ No newline at end of file diff --git a/apps/files_sharing/l10n/nl.js b/apps/files_sharing/l10n/nl.js index 7b31b7c73e2..523213f6d9b 100644 --- a/apps/files_sharing/l10n/nl.js +++ b/apps/files_sharing/l10n/nl.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "Server met server delen is niet geactiveerd op deze server", "The mountpoint name contains invalid characters." : "De naam van het mountpoint bevat ongeldige karakters.", + "Not allowed to create a federated share with the same user server" : "Het is niet toegestaan om een gefedereerde share met dezelfde gebruikersserver te maken", "Invalid or untrusted SSL certificate" : "Ongeldig of onvertrouwd SSL-certificaat", "Could not authenticate to remote share, password might be wrong" : "Kon niet authenticeren bij externe share, misschien verkeerd wachtwoord", "Storage not valid" : "Opslag ongeldig", diff --git a/apps/files_sharing/l10n/nl.json b/apps/files_sharing/l10n/nl.json index 92ba1c04268..e6501e874ea 100644 --- a/apps/files_sharing/l10n/nl.json +++ b/apps/files_sharing/l10n/nl.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "Server met server delen is niet geactiveerd op deze server", "The mountpoint name contains invalid characters." : "De naam van het mountpoint bevat ongeldige karakters.", + "Not allowed to create a federated share with the same user server" : "Het is niet toegestaan om een gefedereerde share met dezelfde gebruikersserver te maken", "Invalid or untrusted SSL certificate" : "Ongeldig of onvertrouwd SSL-certificaat", "Could not authenticate to remote share, password might be wrong" : "Kon niet authenticeren bij externe share, misschien verkeerd wachtwoord", "Storage not valid" : "Opslag ongeldig", diff --git a/apps/files_sharing/l10n/pt_BR.js b/apps/files_sharing/l10n/pt_BR.js index d2dac6d5955..2faf1455480 100644 --- a/apps/files_sharing/l10n/pt_BR.js +++ b/apps/files_sharing/l10n/pt_BR.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "Compartilhamento de servidor para servidor não está habilitado no servidor", "The mountpoint name contains invalid characters." : "O nome do ponto de montagem contém caracteres inválidos.", + "Not allowed to create a federated share with the same user server" : "Não permitido para criar um compartilhamento associado com o mesmo servidor do usuário", "Invalid or untrusted SSL certificate" : "Certificado SSL inválido ou não confiável", "Could not authenticate to remote share, password might be wrong" : "Não foi possível autenticação com o compartilhamento remoto, a senha deve estar errada", "Storage not valid" : "Armazenamento não válido", @@ -27,8 +28,8 @@ OC.L10N.register( "Shared by" : "Compartilhado por", "Sharing" : "Compartilhamento", "A file or folder has been <strong>shared</strong>" : "Um arquivo ou pasta foi <strong>compartilhado</strong> ", - "A file or folder was shared from <strong>another server</strong>" : "Um arquivo ou pasta foi compartilhada a partir de <strong>outro servidor</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "Um arquivo ou pasta compartilhada publicamente foi <strong>baixado</strong>", + "A file or folder was shared from <strong>another server</strong>" : "Um arquivo ou pasta foi compartilhado a partir de <strong>outro servidor</strong>", + "A public shared file or folder was <strong>downloaded</strong>" : "Um arquivo ou pasta compartilhado publicamente foi <strong>baixado</strong>", "You received a new remote share %2$s from %1$s" : "Você recebeu um novo conpartilhamento remoto %2$s de %1$s", "You received a new remote share from %s" : "Você recebeu um novo compartilhamento remoto de %s", "%1$s accepted remote share %2$s" : "%1$s aceitou o compartilhamento remoto %2$s", @@ -55,14 +56,14 @@ OC.L10N.register( "You received %2$s as a remote share from %1$s" : "Você recebeu %2$s como um compartilhamento remoto de %1$s", "Accept" : "Aceitar", "Decline" : "Rejeitar", - "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartilhe comigo através do meu #ownCloud Nuvem Federados ID, veja %s", - "Share with me through my #ownCloud Federated Cloud ID" : "Compartilhe comigo através do meu #ownCloud Nuvem Federados ID", + "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartilhe comigo através do meu #ownCloud Nuvem ID Associada, veja %s", + "Share with me through my #ownCloud Federated Cloud ID" : "Compartilhe comigo através do meu #ownCloud Nuvem ID Associada", "This share is password-protected" : "Este compartilhamento esta protegido por senha", "The password is wrong. Try again." : "Senha incorreta. Tente novamente.", "Password" : "Senha", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", "Name" : "Nome", - "Share time" : "Tempo de compartilhamento", + "Share time" : "Data compartilhado", "Sorry, this link doesn’t seem to work anymore." : "Desculpe, este link parece não mais funcionar.", "Reasons might be:" : "As razões podem ser:", "the item was removed" : "o item foi removido", @@ -73,12 +74,12 @@ OC.L10N.register( "Download" : "Baixar", "Download %s" : "Baixar %s", "Direct link" : "Link direto", - "Federated Cloud Sharing" : "Compartilhamento de Nuvem Conglomerada", + "Federated Cloud Sharing" : "Compartilhamento de Nuvem Associada", "Open documentation" : "Abrir documentação", "Allow users on this server to send shares to other servers" : "Permitir que os usuários deste servidor enviem compartilhamentos para outros servidores", "Allow users on this server to receive shares from other servers" : "Permitir que os usuários nesse servidor recebam compartilhamentos de outros servidores", - "Federated Cloud" : "Nuvem Conglomerada", - "Your Federated Cloud ID:" : "Seu Federados Nuvem ID:", + "Federated Cloud" : "Nuvem Associada", + "Your Federated Cloud ID:" : "Sua ID na Nuvem Associada:", "Share it:" : "Compartilhe:", "Add to your website" : "Adicione ao seu website", "Share with me via ownCloud" : "Compartilhe comigo via ownCloud", diff --git a/apps/files_sharing/l10n/pt_BR.json b/apps/files_sharing/l10n/pt_BR.json index e64c07ff8e0..51e6e9e6f0a 100644 --- a/apps/files_sharing/l10n/pt_BR.json +++ b/apps/files_sharing/l10n/pt_BR.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "Compartilhamento de servidor para servidor não está habilitado no servidor", "The mountpoint name contains invalid characters." : "O nome do ponto de montagem contém caracteres inválidos.", + "Not allowed to create a federated share with the same user server" : "Não permitido para criar um compartilhamento associado com o mesmo servidor do usuário", "Invalid or untrusted SSL certificate" : "Certificado SSL inválido ou não confiável", "Could not authenticate to remote share, password might be wrong" : "Não foi possível autenticação com o compartilhamento remoto, a senha deve estar errada", "Storage not valid" : "Armazenamento não válido", @@ -25,8 +26,8 @@ "Shared by" : "Compartilhado por", "Sharing" : "Compartilhamento", "A file or folder has been <strong>shared</strong>" : "Um arquivo ou pasta foi <strong>compartilhado</strong> ", - "A file or folder was shared from <strong>another server</strong>" : "Um arquivo ou pasta foi compartilhada a partir de <strong>outro servidor</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "Um arquivo ou pasta compartilhada publicamente foi <strong>baixado</strong>", + "A file or folder was shared from <strong>another server</strong>" : "Um arquivo ou pasta foi compartilhado a partir de <strong>outro servidor</strong>", + "A public shared file or folder was <strong>downloaded</strong>" : "Um arquivo ou pasta compartilhado publicamente foi <strong>baixado</strong>", "You received a new remote share %2$s from %1$s" : "Você recebeu um novo conpartilhamento remoto %2$s de %1$s", "You received a new remote share from %s" : "Você recebeu um novo compartilhamento remoto de %s", "%1$s accepted remote share %2$s" : "%1$s aceitou o compartilhamento remoto %2$s", @@ -53,14 +54,14 @@ "You received %2$s as a remote share from %1$s" : "Você recebeu %2$s como um compartilhamento remoto de %1$s", "Accept" : "Aceitar", "Decline" : "Rejeitar", - "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartilhe comigo através do meu #ownCloud Nuvem Federados ID, veja %s", - "Share with me through my #ownCloud Federated Cloud ID" : "Compartilhe comigo através do meu #ownCloud Nuvem Federados ID", + "Share with me through my #ownCloud Federated Cloud ID, see %s" : "Compartilhe comigo através do meu #ownCloud Nuvem ID Associada, veja %s", + "Share with me through my #ownCloud Federated Cloud ID" : "Compartilhe comigo através do meu #ownCloud Nuvem ID Associada", "This share is password-protected" : "Este compartilhamento esta protegido por senha", "The password is wrong. Try again." : "Senha incorreta. Tente novamente.", "Password" : "Senha", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", "Name" : "Nome", - "Share time" : "Tempo de compartilhamento", + "Share time" : "Data compartilhado", "Sorry, this link doesn’t seem to work anymore." : "Desculpe, este link parece não mais funcionar.", "Reasons might be:" : "As razões podem ser:", "the item was removed" : "o item foi removido", @@ -71,12 +72,12 @@ "Download" : "Baixar", "Download %s" : "Baixar %s", "Direct link" : "Link direto", - "Federated Cloud Sharing" : "Compartilhamento de Nuvem Conglomerada", + "Federated Cloud Sharing" : "Compartilhamento de Nuvem Associada", "Open documentation" : "Abrir documentação", "Allow users on this server to send shares to other servers" : "Permitir que os usuários deste servidor enviem compartilhamentos para outros servidores", "Allow users on this server to receive shares from other servers" : "Permitir que os usuários nesse servidor recebam compartilhamentos de outros servidores", - "Federated Cloud" : "Nuvem Conglomerada", - "Your Federated Cloud ID:" : "Seu Federados Nuvem ID:", + "Federated Cloud" : "Nuvem Associada", + "Your Federated Cloud ID:" : "Sua ID na Nuvem Associada:", "Share it:" : "Compartilhe:", "Add to your website" : "Adicione ao seu website", "Share with me via ownCloud" : "Compartilhe comigo via ownCloud", diff --git a/apps/files_sharing/l10n/sq.js b/apps/files_sharing/l10n/sq.js index 413214d58de..e25c170df9c 100644 --- a/apps/files_sharing/l10n/sq.js +++ b/apps/files_sharing/l10n/sq.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "Ndarja mes shërbyesish s’është e aktivizuar në këtë shërbyes", "The mountpoint name contains invalid characters." : "Emri i pikës së montimit përmban shenja të pavlefshme.", + "Not allowed to create a federated share with the same user server" : "Nuk i lejohet të krijojë një ndarje të federuar me të njëjtin shërbyes përdoruesi", "Invalid or untrusted SSL certificate" : "Dëshmi SSL e pavlefshme ose e pabesuar", "Could not authenticate to remote share, password might be wrong" : "S’bëri dot mirëfilltësimin te ndarja e largët, fjalëkalimi mund të jetë i gabuar", "Storage not valid" : "Depozitë jo e vlefshme", @@ -33,6 +34,7 @@ OC.L10N.register( "You received a new remote share from %s" : "Morët një ndarje të largët nga %s", "%1$s accepted remote share %2$s" : "%1$s pranoi ndarjen e largët %2$s", "%1$s declined remote share %2$s" : "%1$s hodhi tej ndarjen e largët %2$s", + "%1$s unshared %2$s from you" : "%1$s shndau me ju %2$s", "Public shared folder %1$s was downloaded" : "U shkarkua dosja e ndarë publikisht %1$s", "Public shared file %1$s was downloaded" : "U shkarkua kartela e ndarë publikisht %1$s", "You shared %1$s with %2$s" : "Ndatë %1$s me %2$s", @@ -72,9 +74,11 @@ OC.L10N.register( "Download" : "Shkarko", "Download %s" : "Shkarko %s", "Direct link" : "Lidhje e drejtpërdrejtë", + "Federated Cloud Sharing" : "Ndarje Në Re e Federuar ", "Open documentation" : "Hap dokumentimin", "Allow users on this server to send shares to other servers" : "Lejoju përdoruesve në këtë shërbyes të dërgojnë ndarje në shërbyes të tjerë", "Allow users on this server to receive shares from other servers" : "Lejoju përdoruesve në këtë shërbyes të marrin ndarje nga shërbyes të tjerë", + "Federated Cloud" : "Ndarje e Federuar", "Your Federated Cloud ID:" : "ID-ja juaj Federated Cloud:", "Share it:" : "Ndajeni:", "Add to your website" : "Shtojeni te sajti juaj", diff --git a/apps/files_sharing/l10n/sq.json b/apps/files_sharing/l10n/sq.json index 8722966f5da..d16d1710a55 100644 --- a/apps/files_sharing/l10n/sq.json +++ b/apps/files_sharing/l10n/sq.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "Ndarja mes shërbyesish s’është e aktivizuar në këtë shërbyes", "The mountpoint name contains invalid characters." : "Emri i pikës së montimit përmban shenja të pavlefshme.", + "Not allowed to create a federated share with the same user server" : "Nuk i lejohet të krijojë një ndarje të federuar me të njëjtin shërbyes përdoruesi", "Invalid or untrusted SSL certificate" : "Dëshmi SSL e pavlefshme ose e pabesuar", "Could not authenticate to remote share, password might be wrong" : "S’bëri dot mirëfilltësimin te ndarja e largët, fjalëkalimi mund të jetë i gabuar", "Storage not valid" : "Depozitë jo e vlefshme", @@ -31,6 +32,7 @@ "You received a new remote share from %s" : "Morët një ndarje të largët nga %s", "%1$s accepted remote share %2$s" : "%1$s pranoi ndarjen e largët %2$s", "%1$s declined remote share %2$s" : "%1$s hodhi tej ndarjen e largët %2$s", + "%1$s unshared %2$s from you" : "%1$s shndau me ju %2$s", "Public shared folder %1$s was downloaded" : "U shkarkua dosja e ndarë publikisht %1$s", "Public shared file %1$s was downloaded" : "U shkarkua kartela e ndarë publikisht %1$s", "You shared %1$s with %2$s" : "Ndatë %1$s me %2$s", @@ -70,9 +72,11 @@ "Download" : "Shkarko", "Download %s" : "Shkarko %s", "Direct link" : "Lidhje e drejtpërdrejtë", + "Federated Cloud Sharing" : "Ndarje Në Re e Federuar ", "Open documentation" : "Hap dokumentimin", "Allow users on this server to send shares to other servers" : "Lejoju përdoruesve në këtë shërbyes të dërgojnë ndarje në shërbyes të tjerë", "Allow users on this server to receive shares from other servers" : "Lejoju përdoruesve në këtë shërbyes të marrin ndarje nga shërbyes të tjerë", + "Federated Cloud" : "Ndarje e Federuar", "Your Federated Cloud ID:" : "ID-ja juaj Federated Cloud:", "Share it:" : "Ndajeni:", "Add to your website" : "Shtojeni te sajti juaj", diff --git a/apps/files_sharing/l10n/th_TH.js b/apps/files_sharing/l10n/th_TH.js index b1400469af8..b0022e9d959 100644 --- a/apps/files_sharing/l10n/th_TH.js +++ b/apps/files_sharing/l10n/th_TH.js @@ -3,6 +3,7 @@ OC.L10N.register( { "Server to server sharing is not enabled on this server" : "เซิร์ฟเวอร์ไปยังแชร์เซิร์ฟเวอร์ไม่ได้เปิดใช้งานบนเซิร์ฟเวอร์นี้", "The mountpoint name contains invalid characters." : "ชื่อจุดเชื่อมต่อมีตัวอักษรที่ไม่ถูกต้อง", + "Not allowed to create a federated share with the same user server" : "ไม่อนุญาตให้สร้างแชร์ในเครือกับเซิร์ฟเวอร์ที่มีผู้ใช้เดียวกัน", "Invalid or untrusted SSL certificate" : "ใบรับรอง SSL ไม่ถูกต้องหรือไม่น่าเชื่อถือ", "Could not authenticate to remote share, password might be wrong" : "ไม่สามารถรับรองความถูกต้องจากการแชร์ระยะไกลรหัสผ่านอาจจะผิด", "Storage not valid" : "การจัดเก็บข้อมูลไม่ถูกต้อง", diff --git a/apps/files_sharing/l10n/th_TH.json b/apps/files_sharing/l10n/th_TH.json index a9ee8d6eb53..d973ad6ba19 100644 --- a/apps/files_sharing/l10n/th_TH.json +++ b/apps/files_sharing/l10n/th_TH.json @@ -1,6 +1,7 @@ { "translations": { "Server to server sharing is not enabled on this server" : "เซิร์ฟเวอร์ไปยังแชร์เซิร์ฟเวอร์ไม่ได้เปิดใช้งานบนเซิร์ฟเวอร์นี้", "The mountpoint name contains invalid characters." : "ชื่อจุดเชื่อมต่อมีตัวอักษรที่ไม่ถูกต้อง", + "Not allowed to create a federated share with the same user server" : "ไม่อนุญาตให้สร้างแชร์ในเครือกับเซิร์ฟเวอร์ที่มีผู้ใช้เดียวกัน", "Invalid or untrusted SSL certificate" : "ใบรับรอง SSL ไม่ถูกต้องหรือไม่น่าเชื่อถือ", "Could not authenticate to remote share, password might be wrong" : "ไม่สามารถรับรองความถูกต้องจากการแชร์ระยะไกลรหัสผ่านอาจจะผิด", "Storage not valid" : "การจัดเก็บข้อมูลไม่ถูกต้อง", diff --git a/apps/files_sharing/lib/controllers/sharecontroller.php b/apps/files_sharing/lib/controllers/sharecontroller.php index fe7b159449c..e28019c358c 100644 --- a/apps/files_sharing/lib/controllers/sharecontroller.php +++ b/apps/files_sharing/lib/controllers/sharecontroller.php @@ -181,6 +181,7 @@ class ShareController extends Controller { $shareTmpl = []; $shareTmpl['displayName'] = User::getDisplayName($shareOwner); + $shareTmpl['owner'] = $shareOwner; $shareTmpl['filename'] = $file; $shareTmpl['directory_path'] = $linkItem['file_target']; $shareTmpl['mimetype'] = Filesystem::getMimeType($originalSharePath); diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php index 2a0d827e064..36ff4f0c226 100644 --- a/apps/files_sharing/lib/external/storage.php +++ b/apps/files_sharing/lib/external/storage.php @@ -265,4 +265,12 @@ class Storage extends DAV implements ISharedStorage { list(, $remote) = explode('://', $this->remote, 2); return $this->remoteUser . '@' . $remote; } + + public function isSharable($path) { + if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) { + return false; + } + return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE); + } + } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index cda3f564d5f..38f79762dc6 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -257,7 +257,7 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { } public function isSharable($path) { - if (\OCP\Util::isSharingDisabledForUser()) { + if (\OCP\Util::isSharingDisabledForUser() || !\OC\Share\Share::isResharingAllowed()) { return false; } return ($this->getPermissions($path) & \OCP\Constants::PERMISSION_SHARE); diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index 26044cc1c8e..2d165a5df04 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -58,6 +58,47 @@ class Shared_Updater { */ static public function renameHook($params) { self::renameChildren($params['oldpath'], $params['newpath']); + self::moveShareToShare($params['newpath']); + } + + /** + * Fix for https://github.com/owncloud/core/issues/20769 + * + * The owner is allowed to move their files (if they are shared) into a receiving folder + * In this case we need to update the parent of the moved share. Since they are + * effectively handing over ownership of the file the rest of the code needs to know + * they need to build up the reshare tree. + * + * @param string $path + */ + static private function moveShareToShare($path) { + $userFolder = \OC::$server->getUserFolder(); + $src = $userFolder->get($path); + + $type = $src instanceof \OCP\Files\File ? 'file' : 'folder'; + $shares = \OCP\Share::getItemShared($type, $src->getId()); + + // If the path we move is not a share we don't care + if (empty($shares)) { + return; + } + + // Check if the destination is inside a share + $mountManager = \OC::$server->getMountManager(); + $dstMount = $mountManager->find($src->getPath()); + if (!($dstMount instanceof \OCA\Files_Sharing\SharedMount)) { + return; + } + + $parenShare = $dstMount->getShare(); + + foreach ($shares as $share) { + $qb = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $qb->update('share') + ->set('parent', $qb->createNamedParameter($parenShare['id'])) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share['id']))) + ->execute(); + } } /** diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index b5dd653d718..aa1f926ea35 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -41,7 +41,7 @@ $thumbSize = 1024; <input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename"> <input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype"> <input type="hidden" name="previewSupported" value="<?php p($_['previewSupported'] ? 'true' : 'false'); ?>" id="previewSupported"> -<input type="hidden" name="mimetypeIcon" value="<?php p(OC_Helper::mimetypeIcon($_['mimetype'])); ?>" id="mimetypeIcon"> +<input type="hidden" name="mimetypeIcon" value="<?php p(\OC::$server->getMimeTypeDetector()->mimeTypeIcon($_['mimetype'])); ?>" id="mimetypeIcon"> <input type="hidden" name="filesize" value="<?php p($_['nonHumanFileSize']); ?>" id="filesize"> <input type="hidden" name="maxSizeAnimateGif" value="<?php p($_['maxSizeAnimateGif']); ?>" id="maxSizeAnimateGif"> @@ -72,7 +72,7 @@ $thumbSize = 1024; if ($_['server2serversharing']) { ?> <span id="save" data-protected="<?php p($_['protected']) ?>" - data-owner="<?php p($_['displayName']) ?>" data-name="<?php p($_['filename']) ?>"> + data-owner-display-name="<?php p($_['displayName']) ?>" data-owner="<?php p($_['owner']) ?>" data-name="<?php p($_['filename']) ?>"> <button id="save-button"><?php p($l->t('Add to your ownCloud')) ?></button> <form class="save-form hidden" action="#"> <input type="text" id="remote_address" placeholder="example.com/owncloud"/> diff --git a/apps/files_sharing/tests/controller/sharecontroller.php b/apps/files_sharing/tests/controller/sharecontroller.php index ccef4263c2b..398538f0943 100644 --- a/apps/files_sharing/tests/controller/sharecontroller.php +++ b/apps/files_sharing/tests/controller/sharecontroller.php @@ -78,7 +78,7 @@ class ShareControllerTest extends \Test\TestCase { // Create a dummy user $this->user = \OC::$server->getSecureRandom()->getLowStrengthGenerator()->generate(12, ISecureRandom::CHAR_LOWER); - \OC_User::createUser($this->user, $this->user); + \OC::$server->getUserManager()->createUser($this->user, $this->user); \OC_Util::tearDownFS(); $this->loginAsUser($this->user); @@ -98,7 +98,8 @@ class ShareControllerTest extends \Test\TestCase { \OC_Util::tearDownFS(); \OC_User::setUserId(''); Filesystem::tearDown(); - \OC_User::deleteUser($this->user); + $user = \OC::$server->getUserManager()->get($this->user); + if ($user !== null) { $user->delete(); } \OC_User::setIncognitoMode(false); \OC::$server->getSession()->set('public_link_authenticated', ''); @@ -168,6 +169,7 @@ class ShareControllerTest extends \Test\TestCase { $response = $this->shareController->showShare($this->token); $sharedTmplParams = array( 'displayName' => $this->user, + 'owner' => $this->user, 'filename' => 'file1.txt', 'directory_path' => '/file1.txt', 'mimetype' => 'text/plain', diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php index de9ce565394..5a917fd1c67 100644 --- a/apps/files_sharing/tests/etagpropagation.php +++ b/apps/files_sharing/tests/etagpropagation.php @@ -34,31 +34,7 @@ use OC\Files\View; * * @package OCA\Files_sharing\Tests */ -class EtagPropagation extends TestCase { - /** - * @var \OC\Files\View - */ - private $rootView; - protected $fileIds = []; // [$user=>[$path=>$id]] - protected $fileEtags = []; // [$id=>$etag] - - public static function setUpBeforeClass() { - parent::setUpBeforeClass(); - \OCA\Files_Sharing\Helper::registerHooks(); - } - - protected function setUp() { - parent::setUp(); - $this->setUpShares(); - } - - protected function tearDown() { - \OC_Hook::clear('OC_Filesystem', 'post_write'); - \OC_Hook::clear('OC_Filesystem', 'post_delete'); - \OC_Hook::clear('OC_Filesystem', 'post_rename'); - \OC_Hook::clear('OCP\Share', 'post_update_permissions'); - parent::tearDown(); - } +class EtagPropagation extends PropagationTestCase { /** * "user1" is the admin who shares a folder "sub1/sub2/folder" with "user2" and "user3" @@ -67,7 +43,7 @@ class EtagPropagation extends TestCase { * "user2" reshares the subdir "sub1/sub2/folder/inside" with "user4" * "user4" puts the received "inside" folder into "sub1/sub2/inside" (this is to check if it propagates across multiple subfolders) */ - private function setUpShares() { + protected function setUpShares() { $this->fileIds[self::TEST_FILES_SHARING_API_USER1] = []; $this->fileIds[self::TEST_FILES_SHARING_API_USER2] = []; $this->fileIds[self::TEST_FILES_SHARING_API_USER3] = []; @@ -136,58 +112,6 @@ class EtagPropagation extends TestCase { } } - /** - * @param string[] $users - * @param string $subPath - */ - private function assertEtagsChanged($users, $subPath = '') { - $oldUser = \OC::$server->getUserSession()->getUser(); - foreach ($users as $user) { - $this->loginAsUser($user); - $id = $this->fileIds[$user][$subPath]; - $path = $this->rootView->getPath($id); - $etag = $this->rootView->getFileInfo($path)->getEtag(); - $this->assertNotEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has changed'); - $this->fileEtags[$id] = $etag; - } - $this->loginAsUser($oldUser->getUID()); - } - - /** - * @param string[] $users - * @param string $subPath - */ - private function assertEtagsNotChanged($users, $subPath = '') { - $oldUser = \OC::$server->getUserSession()->getUser(); - foreach ($users as $user) { - $this->loginAsUser($user); - $id = $this->fileIds[$user][$subPath]; - $path = $this->rootView->getPath($id); - $etag = $this->rootView->getFileInfo($path)->getEtag(); - $this->assertEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has not changed'); - $this->fileEtags[$id] = $etag; - } - $this->loginAsUser($oldUser->getUID()); - } - - /** - * Assert that the etags for the root, /sub1 and /sub1/sub2 have changed - * - * @param string[] $users - */ - private function assertEtagsForFoldersChanged($users) { - $this->assertEtagsChanged($users); - - $this->assertEtagsChanged($users, 'sub1'); - $this->assertEtagsChanged($users, 'sub1/sub2'); - } - - private function assertAllUnchanged() { - $users = [self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, - self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]; - $this->assertEtagsNotChanged($users); - } - public function testOwnerWritesToShare() { $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); Filesystem::file_put_contents('/sub1/sub2/folder/asd.txt', 'bar'); diff --git a/apps/files_sharing/tests/groupetagpropagation.php b/apps/files_sharing/tests/groupetagpropagation.php new file mode 100644 index 00000000000..804d064eadb --- /dev/null +++ b/apps/files_sharing/tests/groupetagpropagation.php @@ -0,0 +1,104 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @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\Files_sharing\Tests; + +use OC\Files\Filesystem; +use OC\Files\View; + +/** + * @group DB + * + * @package OCA\Files_sharing\Tests + */ +class GroupEtagPropagation extends PropagationTestCase { + /** + * "user1" creates /test, /test/sub and shares with group1 + * "user2" (in group1) reshares /test with group2 and reshared /test/sub with group3 + * "user3" (in group 2) + * "user4" (in group 3) + */ + protected function setUpShares() { + $this->fileIds[self::TEST_FILES_SHARING_API_USER1] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER2] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER3] = []; + $this->fileIds[self::TEST_FILES_SHARING_API_USER4] = []; + + $this->rootView = new View(''); + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); + $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); + $view1->mkdir('/test/sub'); + $folderInfo = $view1->getFileInfo('/test'); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group1', 31); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1][''] = $view1->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['test'] = $view1->getFileInfo('test')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER1]['test/sub'] = $view1->getFileInfo('test/sub')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER2); + $view2 = new View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $folderInfo = $view2->getFileInfo('/test'); + $subFolderInfo = $view2->getFileInfo('/test/sub'); + \OCP\Share::shareItem('folder', $folderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group2', 31); + \OCP\Share::shareItem('folder', $subFolderInfo->getId(), \OCP\Share::SHARE_TYPE_GROUP, 'group3', 31); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2][''] = $view2->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['test'] = $view2->getFileInfo('test')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER2]['test/sub'] = $view2->getFileInfo('test/sub')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + $view3 = new View('/' . self::TEST_FILES_SHARING_API_USER3 . '/files'); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3][''] = $view3->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['test'] = $view3->getFileInfo('test')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER3]['test/sub'] = $view3->getFileInfo('test/sub')->getId(); + + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + $view4 = new View('/' . self::TEST_FILES_SHARING_API_USER4 . '/files'); + $this->fileIds[self::TEST_FILES_SHARING_API_USER4][''] = $view4->getFileInfo('')->getId(); + $this->fileIds[self::TEST_FILES_SHARING_API_USER4]['sub'] = $view4->getFileInfo('sub')->getId(); + + foreach ($this->fileIds as $user => $ids) { + $this->loginAsUser($user); + foreach ($ids as $id) { + $path = $this->rootView->getPath($id); + $this->fileEtags[$id] = $this->rootView->getFileInfo($path)->getEtag(); + } + } + } + + public function testGroupReShareRecipientWrites() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER3); + + Filesystem::file_put_contents('/test/sub/file.txt', 'asd'); + + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchanged(); + } + + public function testGroupReShareSubFolderRecipientWrites() { + $this->loginAsUser(self::TEST_FILES_SHARING_API_USER4); + + Filesystem::file_put_contents('/sub/file.txt', 'asd'); + + $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]); + + $this->assertAllUnchanged(); + } +} diff --git a/apps/files_sharing/tests/js/externalSpec.js b/apps/files_sharing/tests/js/externalSpec.js index 255f0fc3a48..362df49252b 100644 --- a/apps/files_sharing/tests/js/externalSpec.js +++ b/apps/files_sharing/tests/js/externalSpec.js @@ -67,6 +67,7 @@ describe('OCA.Sharing external tests', function() { remote: 'http://example.com/owncloud', token: 'abcdefg', owner: 'theowner', + ownerDisplayName: 'The Generous Owner', name: 'the share name' }; }); @@ -88,6 +89,7 @@ describe('OCA.Sharing external tests', function() { remote: 'http://example.com/owncloud', token: 'abcdefg', owner: 'theowner', + ownerDisplayName: 'The Generous Owner', name: 'the share name', password: '' }); @@ -104,6 +106,7 @@ describe('OCA.Sharing external tests', function() { remote: 'http://example.com/owncloud', token: 'abcdefg', owner: 'theowner', + ownerDisplayName: 'The Generous Owner', name: 'the share name', password: 'thepassword' }); @@ -148,6 +151,7 @@ describe('OCA.Sharing external tests', function() { remote: 'http://example.com/owncloud', token: 'abcdefg', owner: 'theowner', + ownerDisplayName: 'The Generous Owner', name: 'the share name' }; }); diff --git a/apps/files_sharing/tests/js/publicAppSpec.js b/apps/files_sharing/tests/js/publicAppSpec.js index 74f008025e1..2aaf758f3e3 100644 --- a/apps/files_sharing/tests/js/publicAppSpec.js +++ b/apps/files_sharing/tests/js/publicAppSpec.js @@ -89,7 +89,8 @@ describe('OCA.Sharing.PublicApp tests', function() { it('Uses public webdav endpoint', function() { expect(fakeServer.requests.length).toEqual(1); expect(fakeServer.requests[0].method).toEqual('PROPFIND'); - expect(fakeServer.requests[0].url).toEqual('https://sh4tok@example.com/owncloud/public.php/webdav/subdir'); + expect(fakeServer.requests[0].url).toEqual('https://example.com/owncloud/public.php/webdav/subdir'); + expect(fakeServer.requests[0].requestHeaders.Authorization).toEqual('Basic c2g0dG9rOm51bGw='); }); describe('Download Url', function() { diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js index fdc9de49c17..0b0676a19e6 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -712,7 +712,7 @@ describe('OCA.Sharing.FileList tests', function() { $tr = fileList.$el.find('tr:first'); expect(parseInt($tr.attr('data-share-permissions'), 10)) - .toEqual(OC.PERMISSION_ALL - OC.PERMISSION_SHARE - OC.PERMISSION_DELETE); + .toEqual(OC.PERMISSION_ALL - OC.PERMISSION_SHARE - OC.PERMISSION_DELETE - OC.PERMISSION_CREATE); }); }); }); diff --git a/apps/files_sharing/tests/propagationtestcase.php b/apps/files_sharing/tests/propagationtestcase.php new file mode 100644 index 00000000000..f397c1fb7a0 --- /dev/null +++ b/apps/files_sharing/tests/propagationtestcase.php @@ -0,0 +1,103 @@ +<?php +/** + * @author Robin Appelman <icewind@owncloud.com> + * + * @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\Files_sharing\Tests; + +abstract class PropagationTestCase extends TestCase { + /** + * @var \OC\Files\View + */ + protected $rootView; + protected $fileIds = []; // [$user=>[$path=>$id]] + protected $fileEtags = []; // [$id=>$etag] + + public static function setUpBeforeClass() { + parent::setUpBeforeClass(); + \OCA\Files_Sharing\Helper::registerHooks(); + } + + protected function setUp() { + parent::setUp(); + $this->setUpShares(); + } + + protected function tearDown() { + \OC_Hook::clear('OC_Filesystem', 'post_write'); + \OC_Hook::clear('OC_Filesystem', 'post_delete'); + \OC_Hook::clear('OC_Filesystem', 'post_rename'); + \OC_Hook::clear('OCP\Share', 'post_update_permissions'); + parent::tearDown(); + } + + abstract protected function setUpShares(); + + /** + * @param string[] $users + * @param string $subPath + */ + protected function assertEtagsChanged($users, $subPath = '') { + $oldUser = \OC::$server->getUserSession()->getUser(); + foreach ($users as $user) { + $this->loginAsUser($user); + $id = $this->fileIds[$user][$subPath]; + $path = $this->rootView->getPath($id); + $etag = $this->rootView->getFileInfo($path)->getEtag(); + $this->assertNotEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has changed'); + $this->fileEtags[$id] = $etag; + } + $this->loginAsUser($oldUser->getUID()); + } + + /** + * @param string[] $users + * @param string $subPath + */ + protected function assertEtagsNotChanged($users, $subPath = '') { + $oldUser = \OC::$server->getUserSession()->getUser(); + foreach ($users as $user) { + $this->loginAsUser($user); + $id = $this->fileIds[$user][$subPath]; + $path = $this->rootView->getPath($id); + $etag = $this->rootView->getFileInfo($path)->getEtag(); + $this->assertEquals($this->fileEtags[$id], $etag, 'Failed asserting that the etag for "' . $subPath . '" of user ' . $user . ' has not changed'); + $this->fileEtags[$id] = $etag; + } + $this->loginAsUser($oldUser->getUID()); + } + + /** + * Assert that the etags for the root, /sub1 and /sub1/sub2 have changed + * + * @param string[] $users + */ + protected function assertEtagsForFoldersChanged($users) { + $this->assertEtagsChanged($users); + + $this->assertEtagsChanged($users, 'sub1'); + $this->assertEtagsChanged($users, 'sub1/sub2'); + } + + protected function assertAllUnchanged() { + $users = [self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2, + self::TEST_FILES_SHARING_API_USER3, self::TEST_FILES_SHARING_API_USER4]; + $this->assertEtagsNotChanged($users); + } +} diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php index dc5b8ed79d9..c4037c7c42e 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -84,9 +84,15 @@ abstract class TestCase extends \Test\TestCase { $groupBackend = new \OC_Group_Dummy(); $groupBackend->createGroup(self::TEST_FILES_SHARING_API_GROUP1); $groupBackend->createGroup('group'); + $groupBackend->createGroup('group1'); + $groupBackend->createGroup('group2'); + $groupBackend->createGroup('group3'); $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER1, 'group'); $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group'); $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group'); + $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, 'group1'); + $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER3, 'group2'); + $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER4, 'group3'); $groupBackend->addToGroup(self::TEST_FILES_SHARING_API_USER2, self::TEST_FILES_SHARING_API_GROUP1); \OC_Group::useBackend($groupBackend); @@ -111,9 +117,12 @@ abstract class TestCase extends \Test\TestCase { public static function tearDownAfterClass() { // cleanup users - \OC_User::deleteUser(self::TEST_FILES_SHARING_API_USER1); - \OC_User::deleteUser(self::TEST_FILES_SHARING_API_USER2); - \OC_User::deleteUser(self::TEST_FILES_SHARING_API_USER3); + $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER1); + if ($user !== null) { $user->delete(); } + $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER2); + if ($user !== null) { $user->delete(); } + $user = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER3); + if ($user !== null) { $user->delete(); } // delete group \OC_Group::deleteGroup(self::TEST_FILES_SHARING_API_GROUP1); @@ -143,7 +152,7 @@ abstract class TestCase extends \Test\TestCase { } if ($create) { - \OC_User::createUser($user, $password); + \OC::$server->getUserManager()->createUser($user, $password); \OC_Group::createGroup('group'); \OC_Group::addToGroup($user, 'group'); } diff --git a/apps/files_trashbin/ajax/preview.php b/apps/files_trashbin/ajax/preview.php index 49d6d93f574..ecb4971253f 100644 --- a/apps/files_trashbin/ajax/preview.php +++ b/apps/files_trashbin/ajax/preview.php @@ -62,7 +62,7 @@ try{ $fileName = substr($fileName, 0, $i); } } - $mimetype = \OC_Helper::getFileNameMimeType($fileName); + $mimetype = \OC::$server->getMimeTypeDetector()->detectPath($fileName); } $preview->setMimetype($mimetype); $preview->setMaxX($maxX); diff --git a/apps/files_trashbin/l10n/mk.js b/apps/files_trashbin/l10n/mk.js index 6acb933aa80..8a4b16c52d8 100644 --- a/apps/files_trashbin/l10n/mk.js +++ b/apps/files_trashbin/l10n/mk.js @@ -9,6 +9,7 @@ OC.L10N.register( "Delete permanently" : "Трајно избришани", "Error" : "Грешка", "restored" : "повратени", + "No entries found in this folder" : "Нема ништо во оваа папка", "Name" : "Име", "Deleted" : "Избришан" }, diff --git a/apps/files_trashbin/l10n/mk.json b/apps/files_trashbin/l10n/mk.json index a9948f49ff6..2ec0b3c9500 100644 --- a/apps/files_trashbin/l10n/mk.json +++ b/apps/files_trashbin/l10n/mk.json @@ -7,6 +7,7 @@ "Delete permanently" : "Трајно избришани", "Error" : "Грешка", "restored" : "повратени", + "No entries found in this folder" : "Нема ништо во оваа папка", "Name" : "Име", "Deleted" : "Избришан" },"pluralForm" :"nplurals=2; plural=(n % 10 == 1 && n % 100 != 11) ? 0 : 1;" diff --git a/apps/files_trashbin/l10n/pt_BR.js b/apps/files_trashbin/l10n/pt_BR.js index b1a3768a3bd..806200ea651 100644 --- a/apps/files_trashbin/l10n/pt_BR.js +++ b/apps/files_trashbin/l10n/pt_BR.js @@ -11,8 +11,8 @@ OC.L10N.register( "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor, verifique os logs ou entre em contato com o administrador", "restored" : "restaurado", - "No deleted files" : "Aquivos não removidos", - "You will be able to recover deleted files from here" : "Você pode recuperar arquivos removidos daqui", + "No deleted files" : "Nenhum arquivo excluído", + "You will be able to recover deleted files from here" : "Neste local, você será capaz de recuperar arquivos excluídos", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", "Select all" : "Selecionar tudo", "Name" : "Nome", diff --git a/apps/files_trashbin/l10n/pt_BR.json b/apps/files_trashbin/l10n/pt_BR.json index db2dc331d5d..177a377bdfd 100644 --- a/apps/files_trashbin/l10n/pt_BR.json +++ b/apps/files_trashbin/l10n/pt_BR.json @@ -9,8 +9,8 @@ "This operation is forbidden" : "Esta operação é proibida", "This directory is unavailable, please check the logs or contact the administrator" : "Este diretório não está disponível, por favor, verifique os logs ou entre em contato com o administrador", "restored" : "restaurado", - "No deleted files" : "Aquivos não removidos", - "You will be able to recover deleted files from here" : "Você pode recuperar arquivos removidos daqui", + "No deleted files" : "Nenhum arquivo excluído", + "You will be able to recover deleted files from here" : "Neste local, você será capaz de recuperar arquivos excluídos", "No entries found in this folder" : "Nenhuma entrada foi encontrada nesta pasta", "Select all" : "Selecionar tudo", "Name" : "Nome", diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php index d14e97285c5..0ccf15cd2bc 100644 --- a/apps/files_trashbin/lib/helper.php +++ b/apps/files_trashbin/lib/helper.php @@ -87,7 +87,7 @@ class Helper $i = array( 'name' => $id, 'mtime' => $timestamp, - 'mimetype' => $view->is_dir($dir . '/' . $entryName) ? 'httpd/unix-directory' : \OC_Helper::getFileNameMimeType($id), + 'mimetype' => $view->is_dir($dir . '/' . $entryName) ? 'httpd/unix-directory' : \OC::$server->getMimeTypeDetector()->detectPath($id), 'type' => $view->is_dir($dir . '/' . $entryName) ? 'dir' : 'file', 'directory' => ($dir === '/') ? '' : $dir, 'size' => $size, diff --git a/apps/files_trashbin/lib/storage.php b/apps/files_trashbin/lib/storage.php index 0e42df1e967..becde5e635b 100644 --- a/apps/files_trashbin/lib/storage.php +++ b/apps/files_trashbin/lib/storage.php @@ -26,6 +26,7 @@ namespace OCA\Files_Trashbin; use OC\Files\Filesystem; use OC\Files\Storage\Wrapper\Wrapper; +use OC\Files\View; use OCP\IUserManager; class Storage extends Wrapper { @@ -151,8 +152,8 @@ class Storage extends Wrapper { $normalized = Filesystem::normalizePath($this->mountPoint . '/' . $path); $result = true; - if (!isset($this->deletedFiles[$normalized])) { - $view = Filesystem::getView(); + $view = Filesystem::getView(); + if (!isset($this->deletedFiles[$normalized]) && $view instanceof View) { $this->deletedFiles[$normalized] = $normalized; if ($filesPath = $view->getRelativePath($normalized)) { $filesPath = trim($filesPath, '/'); diff --git a/apps/files_trashbin/tests/storage.php b/apps/files_trashbin/tests/storage.php index 3ebbbc3ec9d..30735fe7bc3 100644 --- a/apps/files_trashbin/tests/storage.php +++ b/apps/files_trashbin/tests/storage.php @@ -75,7 +75,8 @@ class Storage extends \Test\TestCase { protected function tearDown() { \OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin'); $this->logout(); - \OC_User::deleteUser($this->user); + $user = \OC::$server->getUserManager()->get($this->user); + if ($user !== null) { $user->delete(); } \OC_Hook::clear(); parent::tearDown(); } @@ -531,4 +532,17 @@ class Storage extends \Test\TestCase { ['/schiesbn/', '/test.txt', false, false], ]; } + + /** + * Test that deleting a file doesn't error when nobody is logged in + */ + public function testSingleStorageDeleteFileLoggedOut() { + $this->logout(); + + if (!$this->userView->file_exists('test.txt')) { + $this->markTestSkipped('Skipping since the current home storage backend requires the user to logged in'); + } else { + $this->userView->unlink('test.txt'); + } + } } diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php index db7e7e6e840..9c19b67a904 100644 --- a/apps/files_trashbin/tests/trashbin.php +++ b/apps/files_trashbin/tests/trashbin.php @@ -88,7 +88,8 @@ class Test_Trashbin extends \Test\TestCase { public static function tearDownAfterClass() { // cleanup test user - \OC_User::deleteUser(self::TEST_TRASHBIN_USER1); + $user = \OC::$server->getUserManager()->get(self::TEST_TRASHBIN_USER1); + if ($user !== null) { $user->delete(); } \OC::$server->getConfig()->setSystemValue('trashbin_retention_obligation', self::$rememberRetentionObligation); @@ -636,7 +637,7 @@ class Test_Trashbin extends \Test\TestCase { public static function loginHelper($user, $create = false) { if ($create) { try { - \OC_User::createUser($user, $user); + \OC::$server->getUserManager()->createUser($user, $user); } catch(\Exception $e) { // catch username is already being used from previous aborted runs } diff --git a/apps/files_versions/ajax/preview.php b/apps/files_versions/ajax/preview.php index 0da518f3eaa..f21911abc9b 100644 --- a/apps/files_versions/ajax/preview.php +++ b/apps/files_versions/ajax/preview.php @@ -48,7 +48,7 @@ if($maxX === 0 || $maxY === 0) { try { list($user, $file) = \OCA\Files_Versions\Storage::getUidAndFilename($file); $preview = new \OC\Preview($user, 'files_versions', $file.'.v'.$version); - $mimetype = \OC_Helper::getFileNameMimeType($file); + $mimetype = \OC::$server->getMimeTypeDetector()->detectPath($file); $preview->setMimetype($mimetype); $preview->setMaxX($maxX); $preview->setMaxY($maxY); diff --git a/apps/files_versions/command/expire.php b/apps/files_versions/command/expire.php index 5db33f38772..88723690603 100644 --- a/apps/files_versions/command/expire.php +++ b/apps/files_versions/command/expire.php @@ -35,16 +35,6 @@ class Expire implements ICommand { private $fileName; /** - * @var int|null - */ - private $versionsSize; - - /** - * @var int - */ - private $neededSpace = 0; - - /** * @var string */ private $user; @@ -52,14 +42,10 @@ class Expire implements ICommand { /** * @param string $user * @param string $fileName - * @param int|null $versionsSize - * @param int $neededSpace */ - function __construct($user, $fileName, $versionsSize = null, $neededSpace = 0) { + function __construct($user, $fileName) { $this->user = $user; $this->fileName = $fileName; - $this->versionsSize = $versionsSize; - $this->neededSpace = $neededSpace; } @@ -71,7 +57,7 @@ class Expire implements ICommand { } \OC_Util::setupFS($this->user); - Storage::expire($this->fileName, $this->versionsSize, $this->neededSpace); + Storage::expire($this->fileName); \OC_Util::tearDownFS(); } } diff --git a/apps/files_versions/download.php b/apps/files_versions/download.php index 22a218f472a..d3c38f3d4e1 100644 --- a/apps/files_versions/download.php +++ b/apps/files_versions/download.php @@ -35,7 +35,7 @@ $versionName = '/'.$uid.'/files_versions/'.$filename.'.v'.$revision; $view = new OC\Files\View('/'); -$ftype = \OC_Helper::getSecureMimeType($view->getMimeType('/'.$uid.'/files/'.$filename)); +$ftype = \OC::$server->getMimeTypeDetector()->getSecureMimeType($view->getMimeType('/'.$uid.'/files/'.$filename)); header('Content-Type:'.$ftype); OCP\Response::setContentDispositionHeader(basename($filename), 'attachment'); diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index 29876b3e38a..21b5e9e0e7b 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -168,14 +168,7 @@ class Storage { // create all parent folders self::createMissingDirectories($filename, $users_view); - $versionsSize = self::getVersionsSize($uid); - - // assumption: we need filesize($filename) for the new version + - // some more free space for the modified file which might be - // 1.5 times as large as the current version -> 2.5 - $neededSpace = $files_view->filesize($filename) * 2.5; - - self::scheduleExpire($uid, $filename, $versionsSize, $neededSpace); + self::scheduleExpire($uid, $filename); // store a new version of a file $mtime = $users_view->filemtime('files/' . $filename); @@ -634,14 +627,12 @@ class Storage { * * @param string $uid owner of the file * @param string $fileName file/folder for which to schedule expiration - * @param int|null $versionsSize current versions size - * @param int $neededSpace requested versions size */ - private static function scheduleExpire($uid, $fileName, $versionsSize = null, $neededSpace = 0) { + private static function scheduleExpire($uid, $fileName) { // let the admin disable auto expire $expiration = self::getExpiration(); if ($expiration->isEnabled()) { - $command = new Expire($uid, $fileName, $versionsSize, $neededSpace); + $command = new Expire($uid, $fileName); \OC::$server->getCommandBus()->push($command); } } @@ -650,11 +641,9 @@ class Storage { * Expire versions which exceed the quota * * @param string $filename - * @param int|null $versionsSize - * @param int $offset * @return bool|int|null */ - public static function expire($filename, $versionsSize = null, $offset = 0) { + public static function expire($filename) { $config = \OC::$server->getConfig(); $expiration = self::getExpiration(); @@ -680,9 +669,7 @@ class Storage { } // make sure that we have the current size of the version history - if ( $versionsSize === null ) { - $versionsSize = self::getVersionsSize($uid); - } + $versionsSize = self::getVersionsSize($uid); // calculate available space for version history // subtract size of files and current versions size from quota @@ -692,12 +679,12 @@ class Storage { $rootInfo = $files_view->getFileInfo('/', false); $free = $quota - $rootInfo['size']; // remaining free space for user if ($free > 0) { - $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions + $availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions } else { - $availableSpace = $free - $versionsSize - $offset; + $availableSpace = $free - $versionsSize; } } else { - $availableSpace = $quota - $offset; + $availableSpace = $quota; } } else { $availableSpace = PHP_INT_MAX; diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index ffc98c2e98c..ee4978ff784 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -61,8 +61,10 @@ class Test_Files_Versioning extends \Test\TestCase { public static function tearDownAfterClass() { // cleanup test user - \OC_User::deleteUser(self::TEST_VERSIONS_USER); - \OC_User::deleteUser(self::TEST_VERSIONS_USER2); + $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER); + if ($user !== null) { $user->delete(); } + $user = \OC::$server->getUserManager()->get(self::TEST_VERSIONS_USER2); + if ($user !== null) { $user->delete(); } parent::tearDownAfterClass(); } diff --git a/apps/user_ldap/appinfo/update.php b/apps/user_ldap/appinfo/update.php index 7d358e5b268..7a61101f578 100644 --- a/apps/user_ldap/appinfo/update.php +++ b/apps/user_ldap/appinfo/update.php @@ -21,21 +21,5 @@ * */ -$installedVersion = \OC::$server->getConfig()->getAppValue('user_ldap', 'installed_version'); - -if (version_compare($installedVersion, '0.6.1', '<')) { - \OC::$server->getConfig()->setAppValue('user_ldap', 'enforce_home_folder_naming_rule', false); -} - -if(version_compare($installedVersion, '0.6.2', '<')) { - // Remove LDAP case insensitive setting from DB as it is no longer beeing used. - $helper = new \OCA\user_ldap\lib\Helper(); - $prefixes = $helper->getServerConfigurationPrefixes(); - - foreach($prefixes as $prefix) { - \OC::$server->getConfig()->deleteAppValue('user_ldap', $prefix . "ldap_nocase"); - } -} - OCP\Backgroundjob::registerJob('OCA\user_ldap\lib\Jobs'); OCP\Backgroundjob::registerJob('\OCA\User_LDAP\Jobs\CleanUp'); diff --git a/apps/user_ldap/js/wizard/wizardTabGeneric.js b/apps/user_ldap/js/wizard/wizardTabGeneric.js index 60e7cd2ad9e..8940a8468a0 100644 --- a/apps/user_ldap/js/wizard/wizardTabGeneric.js +++ b/apps/user_ldap/js/wizard/wizardTabGeneric.js @@ -198,9 +198,13 @@ OCA = OCA || {}; return; } - // deal with text area + // special cases: deal with text area and multiselect if ($element.is('textarea') && $.isArray(value)) { value = value.join("\n"); + } else if($element.hasClass(this.multiSelectPluginClass)) { + if(!_.isArray(value)) { + value = value.split("\n"); + } } if ($element.is('span')) { diff --git a/apps/user_ldap/l10n/et_EE.js b/apps/user_ldap/l10n/et_EE.js index aa0727c9bc1..649950716b6 100644 --- a/apps/user_ldap/l10n/et_EE.js +++ b/apps/user_ldap/l10n/et_EE.js @@ -18,18 +18,22 @@ OC.L10N.register( "Select groups" : "Vali grupid", "Select object classes" : "Vali objekti klassid", "Please check the credentials, they seem to be wrong." : "Palu nkontrolli kasutajaandmeid, need näivad olevat valed.", + "Could not detect Base DN, please enter it manually." : "BaasDN-i tuvastamine ebaõnnestus. Palun sisesta see käsitsi.", "{nthServer}. Server" : "{nthServer}. Server", "Do you really want to delete the current Server Configuration?" : "Oled kindel, et tahad kustutada praegust serveri seadistust?", "Confirm Deletion" : "Kinnita kustutamine", + "Error while clearing the mappings." : "Tõrgeseose eemaldamisel.", "Mode switch" : "Režiimi lüliti", "Select attributes" : "Vali atribuudid", "User found and settings verified." : "Kasutaja leiti ja seaded on kontrollitud.", + "Please provide a login name to test against" : "Palun sisesta kasutajanimi, mida testida", "_%s group found_::_%s groups found_" : ["%s grupp leitud","%s gruppi leitud"], "_%s user found_::_%s users found_" : ["%s kasutaja leitud","%s kasutajat leitud"], "Could not find the desired feature" : "Ei suuda leida soovitud funktsioonaalsust", "Invalid Host" : "Vigane server", "Server" : "Server", "Users" : "Kasutajad", + "Login Attributes" : "Sisselogimise andmed", "Groups" : "Grupid", "Test Configuration" : "Testi seadistust", "Help" : "Abiinfo", @@ -43,6 +47,8 @@ OC.L10N.register( "LDAP Filter:" : "LDAP filter:", "The filter specifies which LDAP groups shall have access to the %s instance." : "Filter määrab millised LDAP grupid saavad ligipääsu sellele %s instantsile.", "Verify settings and count groups" : "Kontrolli seadeid ja loe grupid üle", + "LDAP / AD Username:" : "LDAP / AD kasutajanimi:", + "LDAP / AD Email Address:" : "LDAP / AD e-posti aadress:", "Other Attributes:" : "Muud atribuudid:", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Määrab sisselogimisel kasutatava filtri. %%uid asendab sisselogimistegevuses kasutajanime. Näide: \"uid=%%uid\"", "Test Loginname" : "Testi kasutajanime", diff --git a/apps/user_ldap/l10n/et_EE.json b/apps/user_ldap/l10n/et_EE.json index 5d8fbe2f81b..04b22459b18 100644 --- a/apps/user_ldap/l10n/et_EE.json +++ b/apps/user_ldap/l10n/et_EE.json @@ -16,18 +16,22 @@ "Select groups" : "Vali grupid", "Select object classes" : "Vali objekti klassid", "Please check the credentials, they seem to be wrong." : "Palu nkontrolli kasutajaandmeid, need näivad olevat valed.", + "Could not detect Base DN, please enter it manually." : "BaasDN-i tuvastamine ebaõnnestus. Palun sisesta see käsitsi.", "{nthServer}. Server" : "{nthServer}. Server", "Do you really want to delete the current Server Configuration?" : "Oled kindel, et tahad kustutada praegust serveri seadistust?", "Confirm Deletion" : "Kinnita kustutamine", + "Error while clearing the mappings." : "Tõrgeseose eemaldamisel.", "Mode switch" : "Režiimi lüliti", "Select attributes" : "Vali atribuudid", "User found and settings verified." : "Kasutaja leiti ja seaded on kontrollitud.", + "Please provide a login name to test against" : "Palun sisesta kasutajanimi, mida testida", "_%s group found_::_%s groups found_" : ["%s grupp leitud","%s gruppi leitud"], "_%s user found_::_%s users found_" : ["%s kasutaja leitud","%s kasutajat leitud"], "Could not find the desired feature" : "Ei suuda leida soovitud funktsioonaalsust", "Invalid Host" : "Vigane server", "Server" : "Server", "Users" : "Kasutajad", + "Login Attributes" : "Sisselogimise andmed", "Groups" : "Grupid", "Test Configuration" : "Testi seadistust", "Help" : "Abiinfo", @@ -41,6 +45,8 @@ "LDAP Filter:" : "LDAP filter:", "The filter specifies which LDAP groups shall have access to the %s instance." : "Filter määrab millised LDAP grupid saavad ligipääsu sellele %s instantsile.", "Verify settings and count groups" : "Kontrolli seadeid ja loe grupid üle", + "LDAP / AD Username:" : "LDAP / AD kasutajanimi:", + "LDAP / AD Email Address:" : "LDAP / AD e-posti aadress:", "Other Attributes:" : "Muud atribuudid:", "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "Määrab sisselogimisel kasutatava filtri. %%uid asendab sisselogimistegevuses kasutajanime. Näide: \"uid=%%uid\"", "Test Loginname" : "Testi kasutajanime", diff --git a/apps/user_ldap/l10n/ko.js b/apps/user_ldap/l10n/ko.js index c08dd0e2a93..0ed95377acb 100644 --- a/apps/user_ldap/l10n/ko.js +++ b/apps/user_ldap/l10n/ko.js @@ -24,6 +24,7 @@ OC.L10N.register( "Could not detect Base DN, please enter it manually." : "기본 DN을 자동으로 감지할 수 없습니다. 직접 입력하십시오.", "{nthServer}. Server" : "{nthServer}. 서버", "No object found in the given Base DN. Please revise." : "입력한 기본 DN에서 객체를 찾을 수 없습니다. 다시 입력하십시오.", + "More than 1,000 directory entries available." : "디렉터리 항목이 1,000개 이상 존재합니다.", " entries available within the provided Base DN" : "개(지정한 DN의 기본 항목 수)", "An error occurred. Please check the Base DN, as well as connection settings and credentials." : "오류가 발생했습니다. 기본 DN, 연결 설정, 인증 정보를 확인하십시오.", "Do you really want to delete the current Server Configuration?" : "현재 서버 설정을 지우시겠습니까?", diff --git a/apps/user_ldap/l10n/ko.json b/apps/user_ldap/l10n/ko.json index 59037ab0a2b..3fccad0577c 100644 --- a/apps/user_ldap/l10n/ko.json +++ b/apps/user_ldap/l10n/ko.json @@ -22,6 +22,7 @@ "Could not detect Base DN, please enter it manually." : "기본 DN을 자동으로 감지할 수 없습니다. 직접 입력하십시오.", "{nthServer}. Server" : "{nthServer}. 서버", "No object found in the given Base DN. Please revise." : "입력한 기본 DN에서 객체를 찾을 수 없습니다. 다시 입력하십시오.", + "More than 1,000 directory entries available." : "디렉터리 항목이 1,000개 이상 존재합니다.", " entries available within the provided Base DN" : "개(지정한 DN의 기본 항목 수)", "An error occurred. Please check the Base DN, as well as connection settings and credentials." : "오류가 발생했습니다. 기본 DN, 연결 설정, 인증 정보를 확인하십시오.", "Do you really want to delete the current Server Configuration?" : "현재 서버 설정을 지우시겠습니까?", diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 667f1076235..693a420a74d 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -1277,6 +1277,54 @@ class Access extends LDAPUtility implements user\IUserTools { } /** + * reverse lookup of a DN given a known UUID + * + * @param string $uuid + * @return string + * @throws \Exception + */ + public function getUserDnByUuid($uuid) { + $uuidOverride = $this->connection->ldapExpertUUIDUserAttr; + $filter = $this->connection->ldapUserFilter; + $base = $this->connection->ldapBaseUsers; + + if($this->connection->ldapUuidUserAttribute === 'auto' && empty($uuidOverride)) { + // Sacrebleu! The UUID attribute is unknown :( We need first an + // existing DN to be able to reliably detect it. + $result = $this->search($filter, $base, ['dn'], 1); + if(!isset($result[0]) || !isset($result[0]['dn'])) { + throw new \Exception('Cannot determine UUID attribute'); + } + $dn = $result[0]['dn'][0]; + if(!$this->detectUuidAttribute($dn, true)) { + throw new \Exception('Cannot determine UUID attribute'); + } + } else { + // The UUID attribute is either known or an override is given. + // By calling this method we ensure that $this->connection->$uuidAttr + // is definitely set + if(!$this->detectUuidAttribute('', true)) { + throw new \Exception('Cannot determine UUID attribute'); + } + } + + $uuidAttr = $this->connection->ldapUuidUserAttribute; + if($uuidAttr === 'guid' || $uuidAttr === 'objectguid') { + $uuid = $this->formatGuid2ForFilterUser($uuid); + } + + $filter = $uuidAttr . '=' . $uuid; + $result = $this->searchUsers($filter, ['dn'], 2); + if(is_array($result) && isset($result[0]) && isset($result[0]['dn']) && count($result) === 1) { + // we put the count into account to make sure that this is + // really unique + return $result[0]['dn'][0]; + } + + throw new \Exception('Cannot determine UUID attribute'); + } + + /** * auto-detects the directory's UUID attribute * @param string $dn a known DN used to check against * @param bool $isUser @@ -1379,6 +1427,53 @@ class Access extends LDAPUtility implements user\IUserTools { } /** + * the first three blocks of the string-converted GUID happen to be in + * reverse order. In order to use it in a filter, this needs to be + * corrected. Furthermore the dashes need to be replaced and \\ preprended + * to every two hax figures. + * + * If an invalid string is passed, it will be returned without change. + * + * @param string $guid + * @return string + */ + public function formatGuid2ForFilterUser($guid) { + if(!is_string($guid)) { + throw new \InvalidArgumentException('String expected'); + } + $blocks = explode('-', $guid); + if(count($blocks) !== 5) { + /* + * Why not throw an Exception instead? This method is a utility + * called only when trying to figure out whether a "missing" known + * LDAP user was or was not renamed on the LDAP server. And this + * even on the use case that a reverse lookup is needed (UUID known, + * not DN), i.e. when finding users (search dialog, users page, + * login, …) this will not be fired. This occurs only if shares from + * a users are supposed to be mounted who cannot be found. Throwing + * an exception here would kill the experience for a valid, acting + * user. Instead we write a log message. + */ + \OC::$server->getLogger()->info( + 'Passed string does not resemble a valid GUID. Known UUID ' . + '({uuid}) probably does not match UUID configuration.', + [ 'app' => 'user_ldap', 'uuid' => $guid ] + ); + return $guid; + } + for($i=0; $i < 3; $i++) { + $pairs = str_split($blocks[$i], 2); + $pairs = array_reverse($pairs); + $blocks[$i] = implode('', $pairs); + } + for($i=0; $i < 5; $i++) { + $pairs = str_split($blocks[$i], 2); + $blocks[$i] = '\\' . implode('\\', $pairs); + } + return implode('', $blocks); + } + + /** * gets a SID of the domain of the given dn * @param string $dn * @return string|bool diff --git a/apps/user_ldap/lib/mapping/abstractmapping.php b/apps/user_ldap/lib/mapping/abstractmapping.php index f0f0f6df75e..c3d38ce8b71 100644 --- a/apps/user_ldap/lib/mapping/abstractmapping.php +++ b/apps/user_ldap/lib/mapping/abstractmapping.php @@ -158,7 +158,7 @@ abstract class AbstractMapping { } /** - * Gets the name based on the provided LDAP DN. + * Gets the name based on the provided LDAP UUID. * @param string $uuid * @return string|false */ @@ -167,6 +167,16 @@ abstract class AbstractMapping { } /** + * Gets the UUID based on the provided LDAP DN + * @param string $dn + * @return false|string + * @throws \Exception + */ + public function getUUIDByDN($dn) { + return $this->getXbyY('directory_uuid', 'ldap_dn', $dn); + } + + /** * gets a piece of the mapping list * @param int $offset * @param int $limit diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php index 7593371d85d..3aec2a5ce57 100644 --- a/apps/user_ldap/tests/user_ldap.php +++ b/apps/user_ldap/tests/user_ldap.php @@ -70,14 +70,26 @@ class Test_User_Ldap_Direct extends \Test\TestCase { array($lw, null, null)); $this->configMock = $this->getMock('\OCP\IConfig'); - $um = new \OCA\user_ldap\lib\user\Manager( + + $offlineUser = $this->getMockBuilder('\OCA\user_ldap\lib\user\OfflineUser') + ->disableOriginalConstructor() + ->getMock(); + + $um = $this->getMockBuilder('\OCA\user_ldap\lib\user\Manager') + ->setMethods(['getDeletedUser']) + ->setConstructorArgs([ $this->configMock, $this->getMock('\OCA\user_ldap\lib\FilesystemHelper'), $this->getMock('\OCA\user_ldap\lib\LogWrapper'), $this->getMock('\OCP\IAvatarManager'), $this->getMock('\OCP\Image'), $this->getMock('\OCP\IDBConnection') - ); + ]) + ->getMock(); + + $um->expects($this->any()) + ->method('getDeletedUser') + ->will($this->returnValue($offlineUser)); $access = $this->getMock('\OCA\user_ldap\lib\Access', $accMethods, @@ -661,6 +673,44 @@ class Test_User_Ldap_Direct extends \Test\TestCase { $this->assertFalse($result); } + /** + * @expectedException \OC\User\NoUserException + */ + public function testGetHomeDeletedUser() { + $access = $this->getAccessMock(); + $backend = new UserLDAP($access, $this->getMock('\OCP\IConfig')); + $this->prepareMockForUserExists($access); + + $access->connection->expects($this->any()) + ->method('__get') + ->will($this->returnCallback(function($name) { + if($name === 'homeFolderNamingRule') { + return 'attr:testAttribute'; + } + return null; + })); + + $access->expects($this->any()) + ->method('readAttribute') + ->will($this->returnValue([])); + + $userMapper = $this->getMockBuilder('\OCA\User_LDAP\Mapping\UserMapping') + ->disableOriginalConstructor() + ->getMock(); + + $access->expects($this->any()) + ->method('getUserMapper') + ->will($this->returnValue($userMapper)); + + $this->configMock->expects($this->any()) + ->method('getUserValue') + ->will($this->returnValue(true)); + + //no path at all – triggers OC default behaviour + $result = $backend->getHome('newyorker'); + $this->assertFalse($result); + } + private function prepareAccessForGetDisplayName(&$access) { $access->connection->expects($this->any()) ->method('__get') @@ -686,6 +736,14 @@ class Test_User_Ldap_Direct extends \Test\TestCase { return false; } })); + + $userMapper = $this->getMockBuilder('\OCA\User_LDAP\Mapping\UserMapping') + ->disableOriginalConstructor() + ->getMock(); + + $access->expects($this->any()) + ->method('getUserMapper') + ->will($this->returnValue($userMapper)); } public function testGetDisplayName() { diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php index 0097dda89b5..a266be7b7f7 100644 --- a/apps/user_ldap/user_ldap.php +++ b/apps/user_ldap/user_ldap.php @@ -30,6 +30,7 @@ namespace OCA\user_ldap; +use OC\User\NoUserException; use OCA\user_ldap\lib\BackendUtility; use OCA\user_ldap\lib\Access; use OCA\user_ldap\lib\user\OfflineUser; @@ -190,15 +191,18 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn /** * checks whether a user is still available on LDAP + * * @param string|\OCA\User_LDAP\lib\user\User $user either the ownCloud user * name or an instance of that user * @return bool + * @throws \Exception + * @throws \OC\ServerNotAvailableException */ public function userExistsOnLDAP($user) { if(is_string($user)) { $user = $this->access->userManager->get($user); } - if(!$user instanceof User) { + if(is_null($user)) { return false; } @@ -209,7 +213,22 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn if(is_null($lcr)) { throw new \Exception('No LDAP Connection to server ' . $this->access->connection->ldapHost); } - return false; + + try { + $uuid = $this->access->getUserMapper()->getUUIDByDN($dn); + if(!$uuid) { + return false; + } + $newDn = $this->access->getUserDnByUuid($uuid); + $this->access->getUserMapper()->setDNbyUUID($newDn, $uuid); + return true; + } catch (\Exception $e) { + return false; + } + } + + if($user instanceof OfflineUser) { + $user->unmark(); } return true; @@ -274,10 +293,13 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } /** - * get the user's home directory - * @param string $uid the username - * @return string|bool - */ + * get the user's home directory + * + * @param string $uid the username + * @return bool|string + * @throws NoUserException + * @throws \Exception + */ public function getHome($uid) { if(isset($this->homesToKill[$uid]) && !empty($this->homesToKill[$uid])) { //a deleted user who needs some clean up @@ -295,6 +317,15 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn } $user = $this->access->userManager->get($uid); + if(is_null($user) || ($user instanceof OfflineUser && !$this->userExistsOnLDAP($user->getOCName()))) { + throw new NoUserException($uid . ' is not a valid user anymore'); + } + if($user instanceof OfflineUser) { + // apparently this user survived the userExistsOnLDAP check, + // we request the user instance again in order to retrieve a User + // instance instead + $user = $this->access->userManager->get($uid); + } $path = $user->getHomePath(); $this->access->cacheUserHome($uid, $path); |