diff options
182 files changed, 1747 insertions, 1537 deletions
diff --git a/3rdparty b/3rdparty -Subproject d24fd874b6e23c64ad67a3e05f51e1ee5745cf8 +Subproject 6c70738949fbb2add7850b1f55fb7f10524c68e diff --git a/apps/dav/lib/caldav/caldavbackend.php b/apps/dav/lib/caldav/caldavbackend.php index 99338650793..d912f209d46 100644 --- a/apps/dav/lib/caldav/caldavbackend.php +++ b/apps/dav/lib/caldav/caldavbackend.php @@ -26,8 +26,8 @@ use Sabre\CalDAV\Backend\SchedulingSupport; use Sabre\CalDAV\Backend\SubscriptionSupport; use Sabre\CalDAV\Backend\SyncSupport; use Sabre\CalDAV\Plugin; -use Sabre\CalDAV\Property\ScheduleCalendarTransp; -use Sabre\CalDAV\Property\SupportedCalendarComponentSet; +use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp; +use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV; use Sabre\DAV\Exception\Forbidden; use Sabre\VObject\DateTimeParser; diff --git a/apps/dav/lib/carddav/addressbook.php b/apps/dav/lib/carddav/addressbook.php index e50f6f4adf6..eff1ad321e5 100644 --- a/apps/dav/lib/carddav/addressbook.php +++ b/apps/dav/lib/carddav/addressbook.php @@ -3,13 +3,9 @@ namespace OCA\DAV\CardDAV; use OCA\DAV\CardDAV\Sharing\IShareableAddressBook; -use OCP\IUserManager; class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddressBook { - /** @var IUserManager */ - private $userManager; - public function __construct(CardDavBackend $carddavBackend, array $addressBookInfo) { parent::__construct($carddavBackend, $addressBookInfo); } @@ -55,4 +51,4 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareableAddres $carddavBackend = $this->carddavBackend; $carddavBackend->getShares($this->getName()); } -}
\ No newline at end of file +} diff --git a/apps/dav/lib/carddav/useraddressbooks.php b/apps/dav/lib/carddav/useraddressbooks.php index adbb0292fa7..5f618a0ece3 100644 --- a/apps/dav/lib/carddav/useraddressbooks.php +++ b/apps/dav/lib/carddav/useraddressbooks.php @@ -2,7 +2,7 @@ namespace OCA\DAV\CardDAV; -class UserAddressBooks extends \Sabre\CardDAV\UserAddressBooks { +class UserAddressBooks extends \Sabre\CardDAV\AddressBookHome { /** * Returns a list of addressbooks diff --git a/apps/dav/lib/connector/sabre/auth.php b/apps/dav/lib/connector/sabre/auth.php index 27f6704ba2c..655152a2cc1 100644 --- a/apps/dav/lib/connector/sabre/auth.php +++ b/apps/dav/lib/connector/sabre/auth.php @@ -35,6 +35,8 @@ use OCP\IUserSession; use Sabre\DAV\Auth\Backend\AbstractBasic; use Sabre\DAV\Exception\NotAuthenticated; use Sabre\DAV\Exception\ServiceUnavailable; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; class Auth extends AbstractBasic { const DAV_AUTHENTICATED = 'AUTHENTICATED_TO_DAV_BACKEND'; @@ -122,22 +124,15 @@ class Auth extends AbstractBasic { } /** - * Override function here. We want to cache authentication cookies - * in the syncing client to avoid HTTP-401 roundtrips. - * If the sync client supplies the cookies, then OC_User::isLoggedIn() - * will return true and we can see this WebDAV request as already authenticated, - * even if there are no HTTP Basic Auth headers. - * In other case, just fallback to the parent implementation. - * - * @param \Sabre\DAV\Server $server - * @param string $realm - * @return bool - * @throws ServiceUnavailable + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array * @throws NotAuthenticated + * @throws ServiceUnavailable */ - public function authenticate(\Sabre\DAV\Server $server, $realm) { + function check(RequestInterface $request, ResponseInterface $response) { try { - $result = $this->auth($server, $realm); + $result = $this->auth($request, $response); return $result; } catch (NotAuthenticated $e) { throw $e; @@ -149,11 +144,11 @@ class Auth extends AbstractBasic { } /** - * @param \Sabre\DAV\Server $server - * @param string $realm - * @return bool + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array */ - private function auth(\Sabre\DAV\Server $server, $realm) { + private function auth(RequestInterface $request, ResponseInterface $response) { if (\OC_User::handleApacheAuth() || ($this->userSession->isLoggedIn() && is_null($this->session->get(self::DAV_AUTHENTICATED))) ) { @@ -161,16 +156,16 @@ class Auth extends AbstractBasic { \OC_Util::setupFS($user); $this->currentUser = $user; $this->session->close(); - return true; + return [true, $this->principalPrefix . $user]; } - if ($server->httpRequest->getHeader('X-Requested-With') === 'XMLHttpRequest') { + if ($request->getHeader('X-Requested-With') === 'XMLHttpRequest') { // do not re-authenticate over ajax, use dummy auth name to prevent browser popup - $server->httpResponse->addHeader('WWW-Authenticate','DummyBasic realm="' . $realm . '"'); - $server->httpResponse->setStatus(401); + $response->addHeader('WWW-Authenticate','DummyBasic realm="' . $this->realm . '"'); + $response->setStatus(401); throw new \Sabre\DAV\Exception\NotAuthenticated('Cannot authenticate over ajax calls'); } - return parent::authenticate($server, $realm); + return parent::check($request, $response); } } diff --git a/apps/dav/lib/connector/sabre/fakelockerplugin.php b/apps/dav/lib/connector/sabre/fakelockerplugin.php index b9d1a30a041..b75e7f137d8 100644 --- a/apps/dav/lib/connector/sabre/fakelockerplugin.php +++ b/apps/dav/lib/connector/sabre/fakelockerplugin.php @@ -22,9 +22,9 @@ namespace OCA\DAV\Connector\Sabre; use Sabre\DAV\Locks\LockInfo; -use Sabre\DAV\Property\LockDiscovery; -use Sabre\DAV\Property\SupportedLock; use Sabre\DAV\ServerPlugin; +use Sabre\DAV\Xml\Property\LockDiscovery; +use Sabre\DAV\Xml\Property\SupportedLock; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; use Sabre\DAV\PropFind; @@ -122,12 +122,6 @@ class FakeLockerPlugin extends ServerPlugin { */ public function fakeLockProvider(RequestInterface $request, ResponseInterface $response) { - $dom = new \DOMDocument('1.0', 'utf-8'); - $prop = $dom->createElementNS('DAV:', 'd:prop'); - $dom->appendChild($prop); - - $lockDiscovery = $dom->createElementNS('DAV:', 'd:lockdiscovery'); - $prop->appendChild($lockDiscovery); $lockInfo = new LockInfo(); $lockInfo->token = md5($request->getPath()); @@ -135,10 +129,12 @@ class FakeLockerPlugin extends ServerPlugin { $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; $lockInfo->timeout = 1800; - $lockObj = new LockDiscovery([$lockInfo]); - $lockObj->serialize($this->server, $lockDiscovery); + $body = $this->server->xml->write('{DAV:}prop', [ + '{DAV:}lockdiscovery' => + new LockDiscovery([$lockInfo]) + ]); - $response->setBody($dom->saveXML()); + $response->setBody($body); return false; } diff --git a/apps/dav/lib/connector/sabre/taglist.php b/apps/dav/lib/connector/sabre/taglist.php index 177cc23e805..1b32d4b1047 100644 --- a/apps/dav/lib/connector/sabre/taglist.php +++ b/apps/dav/lib/connector/sabre/taglist.php @@ -22,82 +22,100 @@ namespace OCA\DAV\Connector\Sabre; -use Sabre\DAV; +use Sabre\Xml\Element; +use Sabre\Xml\Reader; +use Sabre\Xml\Writer; /** * TagList property * * This property contains multiple "tag" elements, each containing a tag name. */ -class TagList extends DAV\Property { +class TagList implements Element { const NS_OWNCLOUD = 'http://owncloud.org/ns'; - /** - * tags - * - * @var array - */ - private $tags; - - /** - * @param array $tags - */ - public function __construct(array $tags) { - $this->tags = $tags; - } - - /** - * Returns the tags - * - * @return array - */ - public function getTags() { - - return $this->tags; - - } - - /** - * Serializes this property. - * - * @param DAV\Server $server - * @param \DOMElement $dom - * @return void - */ - public function serialize(DAV\Server $server,\DOMElement $dom) { - - $prefix = $server->xmlNamespaces[self::NS_OWNCLOUD]; - - foreach($this->tags as $tag) { - - $elem = $dom->ownerDocument->createElement($prefix . ':tag'); - $elem->appendChild($dom->ownerDocument->createTextNode($tag)); - - $dom->appendChild($elem); - } - - } - - /** - * Unserializes this property from a DOM Element - * - * This method returns an instance of this class. - * It will only decode tag values. - * - * @param \DOMElement $dom - * @param array $propertyMap - * @return \OCA\DAV\Connector\Sabre\TagList - */ - static function unserialize(\DOMElement $dom, array $propertyMap) { - - $tags = array(); - foreach($dom->childNodes as $child) { - if (DAV\XMLUtil::toClarkNotation($child)==='{' . self::NS_OWNCLOUD . '}tag') { - $tags[] = $child->textContent; - } - } - return new self($tags); - - } - + /** + * tags + * + * @var array + */ + private $tags; + + /** + * @param array $tags + */ + public function __construct(array $tags) { + $this->tags = $tags; + } + + /** + * Returns the tags + * + * @return array + */ + public function getTags() { + + return $this->tags; + + } + + /** + * The deserialize method is called during xml parsing. + * + * This method is called statictly, this is because in theory this method + * may be used as a type of constructor, or factory method. + * + * Often you want to return an instance of the current class, but you are + * free to return other data as well. + * + * You are responsible for advancing the reader to the next element. Not + * doing anything will result in a never-ending loop. + * + * If you just want to skip parsing for this element altogether, you can + * just call $reader->next(); + * + * $reader->parseInnerTree() will parse the entire sub-tree, and advance to + * the next element. + * + * @param Reader $reader + * @return mixed + */ + static function xmlDeserialize(Reader $reader) { + $tags = []; + + foreach ($reader->parseInnerTree() as $elem) { + if ($elem['name'] === '{' . self::NS_OWNCLOUD . '}tag') { + $tags[] = $elem['value']; + } + } + return new self($tags); + } + + /** + * The xmlSerialize metod is called during xml writing. + * + * Use the $writer argument to write its own xml serialization. + * + * An important note: do _not_ create a parent element. Any element + * implementing XmlSerializble should only ever write what's considered + * its 'inner xml'. + * + * The parent of the current element is responsible for writing a + * containing element. + * + * This allows serializers to be re-used for different element names. + * + * If you are opening new elements, you must also close them again. + * + * @param Writer $writer + * @return void + */ + function xmlSerialize(Writer $writer) { + + foreach ($this->tags as $tag) { + $writer->startElement(self::NS_OWNCLOUD . ':tag'); + $writer->writeElement($tag); + $writer->endElement(); + } + } } diff --git a/apps/dav/tests/unit/caldav/caldavbackendtest.php b/apps/dav/tests/unit/caldav/caldavbackendtest.php index 258c5627ad9..e9483a47a78 100644 --- a/apps/dav/tests/unit/caldav/caldavbackendtest.php +++ b/apps/dav/tests/unit/caldav/caldavbackendtest.php @@ -23,9 +23,9 @@ namespace Tests\Connector\Sabre; use DateTime; use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; -use Sabre\CalDAV\Property\SupportedCalendarComponentSet; -use Sabre\DAV\Property\Href; +use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet; use Sabre\DAV\PropPatch; +use Sabre\DAV\Xml\Property\Href; use Test\TestCase; /** diff --git a/apps/dav/tests/unit/connector/sabre/FakeLockerPluginTest.php b/apps/dav/tests/unit/connector/sabre/FakeLockerPluginTest.php index dfe8cc220a3..8539e9c06ee 100644 --- a/apps/dav/tests/unit/connector/sabre/FakeLockerPluginTest.php +++ b/apps/dav/tests/unit/connector/sabre/FakeLockerPluginTest.php @@ -21,6 +21,7 @@ namespace OCA\DAV\Tests\Unit\Connector\Sabre; use OCA\DAV\Connector\Sabre\FakeLockerPlugin; +use Sabre\HTTP\Response; use Test\TestCase; /** @@ -141,20 +142,19 @@ class FakeLockerPluginTest extends TestCase { public function testFakeLockProvider() { $request = $this->getMock('\Sabre\HTTP\RequestInterface'); - $response = $this->getMock('\Sabre\HTTP\ResponseInterface'); + $response = new Response(); $server = $this->getMock('\Sabre\DAV\Server'); $this->fakeLockerPlugin->initialize($server); $request->expects($this->exactly(2)) ->method('getPath') ->will($this->returnValue('MyPath')); - $response->expects($this->once()) - ->method('setBody') - ->with('<?xml version="1.0" encoding="utf-8"?> -<d:prop xmlns:d="DAV:"><d:lockdiscovery><d:activelock><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:lockroot><d:href>MyPath</d:href></d:lockroot><d:depth>infinity</d:depth><d:timeout>Second-1800</d:timeout><d:locktoken><d:href>opaquelocktoken:fe4f7f2437b151fbcb4e9f5c8118c6b1</d:href></d:locktoken><d:owner/></d:activelock></d:lockdiscovery></d:prop> -'); $this->assertSame(false, $this->fakeLockerPlugin->fakeLockProvider($request, $response)); + + $expectedXml = '<?xml version="1.0" encoding="utf-8"?><d:prop xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns"><d:lockdiscovery><d:activelock><d:lockscope><d:exclusive/></d:lockscope><d:locktype><d:write/></d:locktype><d:lockroot><d:href>MyPath</d:href></d:lockroot><d:depth>infinity</d:depth><d:timeout>Second-1800</d:timeout><d:locktoken><d:href>opaquelocktoken:fe4f7f2437b151fbcb4e9f5c8118c6b1</d:href></d:locktoken><d:owner/></d:activelock></d:lockdiscovery></d:prop>'; + + $this->assertXmlStringEqualsXmlString($expectedXml, $response->getBody()); } public function testFakeUnlockProvider() { diff --git a/apps/dav/tests/unit/connector/sabre/auth.php b/apps/dav/tests/unit/connector/sabre/auth.php index 4c060ff04bb..595bd441617 100644 --- a/apps/dav/tests/unit/connector/sabre/auth.php +++ b/apps/dav/tests/unit/connector/sabre/auth.php @@ -249,9 +249,12 @@ class Auth extends TestCase { } public function testAuthenticateAlreadyLoggedIn() { - $server = $this->getMockBuilder('\Sabre\DAV\Server') - ->disableOriginalConstructor() - ->getMock(); + $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface') + ->disableOriginalConstructor() + ->getMock(); + $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface') + ->disableOriginalConstructor() + ->getMock(); $this->userSession ->expects($this->once()) ->method('isLoggedIn') @@ -275,13 +278,10 @@ class Auth extends TestCase { ->expects($this->once()) ->method('close'); - $this->assertTrue($this->auth->authenticate($server, 'TestRealm')); + $response = $this->auth->check($request, $response); + $this->assertEquals([true, 'principals/MyWrongDavUser'], $response); } - /** - * @expectedException \Sabre\DAV\Exception\NotAuthenticated - * @expectedExceptionMessage No basic authentication headers were found - */ public function testAuthenticateNoBasicAuthenticateHeadersProvided() { $server = $this->getMockBuilder('\Sabre\DAV\Server') ->disableOriginalConstructor() @@ -292,7 +292,8 @@ class Auth extends TestCase { $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface') ->disableOriginalConstructor() ->getMock(); - $this->auth->authenticate($server, 'TestRealm'); + $response = $this->auth->check($server->httpRequest, $server->httpResponse); + $this->assertEquals([false, 'No \'Authorization: Basic\' header found. Either the client didn\'t send one, or the server is mis-configured'], $response); } /** @@ -300,21 +301,20 @@ class Auth extends TestCase { * @expectedExceptionMessage Cannot authenticate over ajax calls */ public function testAuthenticateNoBasicAuthenticateHeadersProvidedWithAjax() { - $server = $this->getMockBuilder('\Sabre\DAV\Server') + /** @var \Sabre\HTTP\RequestInterface $httpRequest */ + $httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface') ->disableOriginalConstructor() ->getMock(); - $server->httpRequest = $this->getMockBuilder('\Sabre\HTTP\RequestInterface') + /** @var \Sabre\HTTP\ResponseInterface $httpResponse */ + $httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface') ->disableOriginalConstructor() ->getMock(); - $server->httpResponse = $this->getMockBuilder('\Sabre\HTTP\ResponseInterface') - ->disableOriginalConstructor() - ->getMock(); - $server->httpRequest + $httpRequest ->expects($this->once()) ->method('getHeader') ->with('X-Requested-With') ->will($this->returnValue('XMLHttpRequest')); - $this->auth->authenticate($server, 'TestRealm'); + $this->auth->check($httpRequest, $httpResponse); } public function testAuthenticateValidCredentials() { @@ -352,13 +352,10 @@ class Auth extends TestCase { ->expects($this->exactly(2)) ->method('getUser') ->will($this->returnValue($user)); - $this->assertTrue($this->auth->authenticate($server, 'TestRealm')); + $response = $this->auth->check($server->httpRequest, $server->httpResponse); + $this->assertEquals([true, 'principals/username'], $response); } - /** - * @expectedException \Sabre\DAV\Exception\NotAuthenticated - * @expectedExceptionMessage Username or password does not match - */ public function testAuthenticateInvalidCredentials() { $server = $this->getMockBuilder('\Sabre\DAV\Server') ->disableOriginalConstructor() @@ -384,6 +381,7 @@ class Auth extends TestCase { ->method('login') ->with('username', 'password') ->will($this->returnValue(false)); - $this->auth->authenticate($server, 'TestRealm'); + $response = $this->auth->check($server->httpRequest, $server->httpResponse); + $this->assertEquals([false, 'Username or password was incorrect'], $response); } } diff --git a/apps/dav/tests/unit/connector/sabre/requesttest/auth.php b/apps/dav/tests/unit/connector/sabre/requesttest/auth.php index 02b64ab070b..3caa019af8d 100644 --- a/apps/dav/tests/unit/connector/sabre/requesttest/auth.php +++ b/apps/dav/tests/unit/connector/sabre/requesttest/auth.php @@ -9,6 +9,8 @@ namespace OCA\DAV\Tests\Unit\Connector\Sabre\RequestTest; use Sabre\DAV\Auth\Backend\BackendInterface; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; class Auth implements BackendInterface { /** @@ -32,18 +34,35 @@ class Auth implements BackendInterface { $this->password = $password; } - /** - * Authenticates the user based on the current request. + * When this method is called, the backend must check if authentication was + * successful. + * + * The returned value must be one of the following + * + * [true, "principals/username"] + * [false, "reason for failure"] + * + * If authentication was successful, it's expected that the authentication + * backend returns a so-called principal url. + * + * Examples of a principal url: * - * If authentication is successful, true must be returned. - * If authentication fails, an exception must be thrown. + * principals/admin + * principals/user1 + * principals/users/joe + * principals/uid/123457 * - * @param \Sabre\DAV\Server $server - * @param string $realm - * @return boolean|null + * If you don't use WebDAV ACL (RFC3744) we recommend that you simply + * return a string such as: + * + * principals/users/[username] + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return array */ - function authenticate(\Sabre\DAV\Server $server, $realm) { + function check(RequestInterface $request, ResponseInterface $response) { $userSession = \OC::$server->getUserSession(); $result = $userSession->login($this->user, $this->password); if ($result) { @@ -52,18 +71,33 @@ class Auth implements BackendInterface { \OC_Util::setupFS($user); //trigger creation of user home and /files folder \OC::$server->getUserFolder($user); + return [true, "principals/$user"]; } - return $result; + return [false, "login failed"]; } /** - * Returns information about the currently logged in username. + * This method is called when a user could not be authenticated, and + * authentication was required for the current request. + * + * This gives you the opportunity to set authentication headers. The 401 + * status code will already be set. + * + * In this case of Basic Auth, this would for example mean that the + * following header needs to be set: + * + * $response->addHeader('WWW-Authenticate', 'Basic realm=SabreDAV'); * - * If nobody is currently logged in, this method should return null. + * Keep in mind that in the case of multiple authentication backends, other + * WWW-Authenticate headers may already have been set, and you'll want to + * append your own WWW-Authenticate header instead of overwriting the + * existing one. * - * @return string + * @param RequestInterface $request + * @param ResponseInterface $response + * @return void */ - function getCurrentUser() { - return $this->user; + function challenge(RequestInterface $request, ResponseInterface $response) { + // TODO: Implement challenge() method. } } diff --git a/apps/federation/appinfo/app.php b/apps/federation/appinfo/app.php index 9ed00f23866..8cc77885d6f 100644 --- a/apps/federation/appinfo/app.php +++ b/apps/federation/appinfo/app.php @@ -23,3 +23,4 @@ namespace OCA\Federation\AppInfo; $app = new Application(); $app->registerSettings(); +$app->registerHooks(); diff --git a/apps/federation/appinfo/application.php b/apps/federation/appinfo/application.php index 350b140b4dd..172283536b4 100644 --- a/apps/federation/appinfo/application.php +++ b/apps/federation/appinfo/application.php @@ -22,14 +22,15 @@ namespace OCA\Federation\AppInfo; use OCA\Federation\API\OCSAuthAPI; -use OCA\Federation\Controller\AuthController; use OCA\Federation\Controller\SettingsController; use OCA\Federation\DbHandler; +use OCA\Federation\Hooks; use OCA\Federation\Middleware\AddServerMiddleware; use OCA\Federation\TrustedServers; use OCP\API; use OCP\App; use OCP\AppFramework\IAppContainer; +use OCP\Util; class Application extends \OCP\AppFramework\App { @@ -127,4 +128,21 @@ class Application extends \OCP\AppFramework\App { } + /** + * listen to federated_share_added hooks to auto-add new servers to the + * list of trusted servers. + */ + public function registerHooks() { + + $container = $this->getContainer(); + $hooksManager = new Hooks($container->query('TrustedServers')); + + Util::connectHook( + 'OCP\Share', + 'federated_share_added', + $hooksManager, + 'addServerHook' + ); + } + } diff --git a/apps/federation/lib/dbhandler.php b/apps/federation/lib/dbhandler.php index 61ba5c87cfd..7606593f780 100644 --- a/apps/federation/lib/dbhandler.php +++ b/apps/federation/lib/dbhandler.php @@ -68,6 +68,7 @@ class DbHandler { */ public function addServer($url) { $hash = $this->hash($url); + $url = rtrim($url, '/'); $query = $this->connection->getQueryBuilder(); $query->insert($this->dbTable) ->values( diff --git a/apps/federation/lib/hooks.php b/apps/federation/lib/hooks.php new file mode 100644 index 00000000000..4bf5be4e5b6 --- /dev/null +++ b/apps/federation/lib/hooks.php @@ -0,0 +1,50 @@ +<?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\Federation; + + + +class Hooks { + + /** @var TrustedServers */ + private $trustedServers; + + public function __construct(TrustedServers $trustedServers) { + $this->trustedServers = $trustedServers; + } + + /** + * add servers to the list of trusted servers once a federated share was established + * + * @param array $params + */ + public function addServerHook($params) { + if ( + $this->trustedServers->getAutoAddServers() === true && + $this->trustedServers->isTrustedServer($params['server']) === false + ) { + $this->trustedServers->addServer($params['server']); + } + } + +} diff --git a/apps/federation/tests/lib/dbhandlertest.php b/apps/federation/tests/lib/dbhandlertest.php index e47df092f8c..123eaaee450 100644 --- a/apps/federation/tests/lib/dbhandlertest.php +++ b/apps/federation/tests/lib/dbhandlertest.php @@ -67,17 +67,33 @@ class DbHandlerTest extends TestCase { $query->execute(); } - public function testAddServer() { - $id = $this->dbHandler->addServer('server1'); + /** + * @dataProvider dataTestAddServer + * + * @param string $url passed to the method + * @param string $expectedUrl the url we expect to be written to the db + * @param string $expectedHash the hash value we expect to be written to the db + */ + public function testAddServer($url, $expectedUrl, $expectedHash) { + $id = $this->dbHandler->addServer($url); $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable); $result = $query->execute()->fetchAll(); $this->assertSame(1, count($result)); - $this->assertSame('server1', $result[0]['url']); + $this->assertSame($expectedUrl, $result[0]['url']); $this->assertSame($id, (int)$result[0]['id']); + $this->assertSame($expectedHash, $result[0]['url_hash']); $this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']); } + public function dataTestAddServer() { + return [ + ['http://owncloud.org', 'http://owncloud.org', md5('owncloud.org')], + ['https://owncloud.org', 'https://owncloud.org', md5('owncloud.org')], + ['http://owncloud.org/', 'http://owncloud.org', md5('owncloud.org')], + ]; + } + public function testRemove() { $id1 = $this->dbHandler->addServer('server1'); $id2 = $this->dbHandler->addServer('server2'); diff --git a/apps/federation/tests/lib/hookstest.php b/apps/federation/tests/lib/hookstest.php new file mode 100644 index 00000000000..5b19c167456 --- /dev/null +++ b/apps/federation/tests/lib/hookstest.php @@ -0,0 +1,79 @@ +<?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\Federation\Tests\lib; + + +use OCA\Federation\Hooks; +use OCA\Federation\TrustedServers; +use Test\TestCase; + +class HooksTest extends TestCase { + + /** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers */ + private $trustedServers; + + /** @var Hooks */ + private $hooks; + + public function setUp() { + parent::setUp(); + + $this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers') + ->disableOriginalConstructor()->getMock(); + + $this->hooks = new Hooks($this->trustedServers); + } + + /** + * @dataProvider dataTestAddServerHook + * + * @param bool $autoAddEnabled is auto-add enabled + * @param bool $isTrustedServer is the server already in the list of trusted servers + * @param bool $addServer should the server be added + */ + public function testAddServerHook($autoAddEnabled, $isTrustedServer, $addServer) { + $this->trustedServers->expects($this->any())->method('getAutoAddServers') + ->willReturn($autoAddEnabled); + $this->trustedServers->expects($this->any())->method('isTrustedServer') + ->with('url')->willReturn($isTrustedServer); + + if ($addServer) { + $this->trustedServers->expects($this->once())->method('addServer') + ->with('url'); + } else { + $this->trustedServers->expects($this->never())->method('addServer'); + } + + $this->hooks->addServerHook(['server' => 'url']); + + } + + public function dataTestAddServerHook() { + return [ + [true, true, false], + [false, true, false], + [true, false, true], + [false, false, false], + ]; + } +} diff --git a/apps/files/l10n/cs_CZ.js b/apps/files/l10n/cs_CZ.js index 9e1135347ec..8c40cbcd771 100644 --- a/apps/files/l10n/cs_CZ.js +++ b/apps/files/l10n/cs_CZ.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "Nelze určit datum", "This operation is forbidden" : "Tato operace je zakázána", "This directory is unavailable, please check the logs or contact the administrator" : "Tento adresář není dostupný, zkontrolujte prosím logy nebo kontaktujte svého správce systému", + "Could not move \"{file}\", target exists" : "Nelze přesunout \"{file}\", cíl existuje", + "Could not move \"{file}\"" : "Nelze přesunout \"{file}\"", + "{newName} already exists" : "{newName} již existuje", + "Could not rename \"{fileName}\", it does not exist any more" : "Nelze přejmenovat \"{fileName}\", již neexistuje", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Jméno \"{targetName}\" je již použito v adresáři \"{dir}\". Vyberte prosím jiné jméno.", + "Could not rename \"{fileName}\"" : "Nelze přejmenovat \"{fileName}\"", + "Could not create file \"{file}\"" : "Nelze vytvořit soubor \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Nelze vytvořit soubor \"{file}\", protože již existuje", + "Could not create folder \"{dir}\"" : "Nelze vytvořit adresář \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Nelze vytvořit adresář \"{dir}\", protože již existuje", + "Error deleting file \"{fileName}\"." : "Chyba mazání souboru \"{fileName}\".", "No entries in this folder match '{filter}'" : "V tomto adresáři nic nesouhlasí s '{filter}'", "Name" : "Název", "Size" : "Velikost", diff --git a/apps/files/l10n/cs_CZ.json b/apps/files/l10n/cs_CZ.json index 04bf4cf7e9e..70b61ec784a 100644 --- a/apps/files/l10n/cs_CZ.json +++ b/apps/files/l10n/cs_CZ.json @@ -38,6 +38,17 @@ "Unable to determine date" : "Nelze určit datum", "This operation is forbidden" : "Tato operace je zakázána", "This directory is unavailable, please check the logs or contact the administrator" : "Tento adresář není dostupný, zkontrolujte prosím logy nebo kontaktujte svého správce systému", + "Could not move \"{file}\", target exists" : "Nelze přesunout \"{file}\", cíl existuje", + "Could not move \"{file}\"" : "Nelze přesunout \"{file}\"", + "{newName} already exists" : "{newName} již existuje", + "Could not rename \"{fileName}\", it does not exist any more" : "Nelze přejmenovat \"{fileName}\", již neexistuje", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Jméno \"{targetName}\" je již použito v adresáři \"{dir}\". Vyberte prosím jiné jméno.", + "Could not rename \"{fileName}\"" : "Nelze přejmenovat \"{fileName}\"", + "Could not create file \"{file}\"" : "Nelze vytvořit soubor \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Nelze vytvořit soubor \"{file}\", protože již existuje", + "Could not create folder \"{dir}\"" : "Nelze vytvořit adresář \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Nelze vytvořit adresář \"{dir}\", protože již existuje", + "Error deleting file \"{fileName}\"." : "Chyba mazání souboru \"{fileName}\".", "No entries in this folder match '{filter}'" : "V tomto adresáři nic nesouhlasí s '{filter}'", "Name" : "Název", "Size" : "Velikost", diff --git a/apps/files/l10n/el.js b/apps/files/l10n/el.js index abc3e5331d8..f60e842199f 100644 --- a/apps/files/l10n/el.js +++ b/apps/files/l10n/el.js @@ -40,6 +40,13 @@ OC.L10N.register( "Unable to determine date" : "Αδυναμία προσδιορισμού ημερομηνίας ", "This operation is forbidden" : "Αυτή η ενέργεια δεν επιτρέπεται", "This directory is unavailable, please check the logs or contact the administrator" : "Ο κατάλογος δεν είναι διαθέσιμος, παρακαλώ ελέγξτε τα αρχεία καταγραφής ή επικοινωνήστε με το διαχειριστή", + "Could not move \"{file}\", target exists" : "Αδυναμία μετακίνησης του \"{file}\", υπάρχει ήδη αρχείο με αυτό το όνομα", + "Could not move \"{file}\"" : "Αδυναμία μετακίνησης του \"{file}\"", + "{newName} already exists" : "Το {newname} υπάρχει ήδη", + "Could not rename \"{fileName}\"" : "Αδυναμία μετονομασίας του \"{fileName}\"", + "Could not create file \"{file}\"" : "Αδυναμία δημιουργίας του \"{file}\"", + "Could not create folder \"{dir}\"" : "Αδυναμία δημιουργίας του φακέλου \"{dir}\"", + "Error deleting file \"{fileName}\"." : "Αδυναμία διαγραφής του \"{fileName}\".", "No entries in this folder match '{filter}'" : "Δεν ταιριάζουν καταχωρήσεις σε αυτόν το φάκελο '{filter}'", "Name" : "Όνομα", "Size" : "Μέγεθος", diff --git a/apps/files/l10n/el.json b/apps/files/l10n/el.json index f77aa9e373d..15cb56c87d1 100644 --- a/apps/files/l10n/el.json +++ b/apps/files/l10n/el.json @@ -38,6 +38,13 @@ "Unable to determine date" : "Αδυναμία προσδιορισμού ημερομηνίας ", "This operation is forbidden" : "Αυτή η ενέργεια δεν επιτρέπεται", "This directory is unavailable, please check the logs or contact the administrator" : "Ο κατάλογος δεν είναι διαθέσιμος, παρακαλώ ελέγξτε τα αρχεία καταγραφής ή επικοινωνήστε με το διαχειριστή", + "Could not move \"{file}\", target exists" : "Αδυναμία μετακίνησης του \"{file}\", υπάρχει ήδη αρχείο με αυτό το όνομα", + "Could not move \"{file}\"" : "Αδυναμία μετακίνησης του \"{file}\"", + "{newName} already exists" : "Το {newname} υπάρχει ήδη", + "Could not rename \"{fileName}\"" : "Αδυναμία μετονομασίας του \"{fileName}\"", + "Could not create file \"{file}\"" : "Αδυναμία δημιουργίας του \"{file}\"", + "Could not create folder \"{dir}\"" : "Αδυναμία δημιουργίας του φακέλου \"{dir}\"", + "Error deleting file \"{fileName}\"." : "Αδυναμία διαγραφής του \"{fileName}\".", "No entries in this folder match '{filter}'" : "Δεν ταιριάζουν καταχωρήσεις σε αυτόν το φάκελο '{filter}'", "Name" : "Όνομα", "Size" : "Μέγεθος", diff --git a/apps/files/l10n/fi_FI.js b/apps/files/l10n/fi_FI.js index 948ea7c8a71..8bebb81fd35 100644 --- a/apps/files/l10n/fi_FI.js +++ b/apps/files/l10n/fi_FI.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "Päivämäärän määrittäminen epäonnistui", "This operation is forbidden" : "Tämä toiminto on kielletty", "This directory is unavailable, please check the logs or contact the administrator" : "Hakemisto ei ole käytettävissä. Tarkista lokit tai ole yhteydessä ylläpitoon.", + "Could not move \"{file}\", target exists" : "Tiedoston \"{file}\" siirtäminen ei onnistunut, kohde on olemassa", + "Could not move \"{file}\"" : "Tiedoston \"{file}\" siirtäminen ei onnistunut", + "{newName} already exists" : "{newName} on jo olemassa", + "Could not rename \"{fileName}\", it does not exist any more" : "Tiedoston \"{fileName}\" nimeäminen uudelleen ei onnistunut, koska sitä ei ole enää olemassa", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Nimi \"{targetName}\" on jo käytössä kansiossa \"{dir}\". Valitse toinen nimi.", + "Could not rename \"{fileName}\"" : "Tiedoston \"{fileName}\" nimeäminen uudelleen ei onnistunut", + "Could not create file \"{file}\"" : "Tiedostoa \"{file}\" ei voitu luoda", + "Could not create file \"{file}\" because it already exists" : "Tiedostoa \"{file}\" ei voitu luoda, koska se on jo olemassa", + "Could not create folder \"{dir}\"" : "Kansiota \"{dir}\" ei voitu luoda", + "Could not create folder \"{dir}\" because it already exists" : "Kansiota \"{dir}\" ei voitu luoda, koska se on jo olemassa", + "Error deleting file \"{fileName}\"." : "Virhe poistaessa tiedostoa \"{fileName}\".", "No entries in this folder match '{filter}'" : "Mikään tässä kansiossa ei vastaa suodatusta '{filter}'", "Name" : "Nimi", "Size" : "Koko", @@ -89,6 +100,7 @@ OC.L10N.register( "max. possible: " : "suurin mahdollinen:", "Save" : "Tallenna", "With PHP-FPM it might take 5 minutes for changes to be applied." : "PHP-FPM:tä käyttäen muutoksien voimaantulossa saattaa kestää 5 minuuttia.", + "Missing permissions to edit from here." : "Käyttöoikeudet eivät riitä tätä kautta muokkaamiseen.", "Settings" : "Asetukset", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Käytä tätä osoitetta <a href=\"%s\" target=\"_blank\">käyttääksesi tiedostojasi WebDAVin kautta</a>", diff --git a/apps/files/l10n/fi_FI.json b/apps/files/l10n/fi_FI.json index 940a95840b7..64b0a886b73 100644 --- a/apps/files/l10n/fi_FI.json +++ b/apps/files/l10n/fi_FI.json @@ -38,6 +38,17 @@ "Unable to determine date" : "Päivämäärän määrittäminen epäonnistui", "This operation is forbidden" : "Tämä toiminto on kielletty", "This directory is unavailable, please check the logs or contact the administrator" : "Hakemisto ei ole käytettävissä. Tarkista lokit tai ole yhteydessä ylläpitoon.", + "Could not move \"{file}\", target exists" : "Tiedoston \"{file}\" siirtäminen ei onnistunut, kohde on olemassa", + "Could not move \"{file}\"" : "Tiedoston \"{file}\" siirtäminen ei onnistunut", + "{newName} already exists" : "{newName} on jo olemassa", + "Could not rename \"{fileName}\", it does not exist any more" : "Tiedoston \"{fileName}\" nimeäminen uudelleen ei onnistunut, koska sitä ei ole enää olemassa", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Nimi \"{targetName}\" on jo käytössä kansiossa \"{dir}\". Valitse toinen nimi.", + "Could not rename \"{fileName}\"" : "Tiedoston \"{fileName}\" nimeäminen uudelleen ei onnistunut", + "Could not create file \"{file}\"" : "Tiedostoa \"{file}\" ei voitu luoda", + "Could not create file \"{file}\" because it already exists" : "Tiedostoa \"{file}\" ei voitu luoda, koska se on jo olemassa", + "Could not create folder \"{dir}\"" : "Kansiota \"{dir}\" ei voitu luoda", + "Could not create folder \"{dir}\" because it already exists" : "Kansiota \"{dir}\" ei voitu luoda, koska se on jo olemassa", + "Error deleting file \"{fileName}\"." : "Virhe poistaessa tiedostoa \"{fileName}\".", "No entries in this folder match '{filter}'" : "Mikään tässä kansiossa ei vastaa suodatusta '{filter}'", "Name" : "Nimi", "Size" : "Koko", @@ -87,6 +98,7 @@ "max. possible: " : "suurin mahdollinen:", "Save" : "Tallenna", "With PHP-FPM it might take 5 minutes for changes to be applied." : "PHP-FPM:tä käyttäen muutoksien voimaantulossa saattaa kestää 5 minuuttia.", + "Missing permissions to edit from here." : "Käyttöoikeudet eivät riitä tätä kautta muokkaamiseen.", "Settings" : "Asetukset", "WebDAV" : "WebDAV", "Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Käytä tätä osoitetta <a href=\"%s\" target=\"_blank\">käyttääksesi tiedostojasi WebDAVin kautta</a>", diff --git a/apps/files/l10n/fr.js b/apps/files/l10n/fr.js index a8a6e04a3a5..6af58e93094 100644 --- a/apps/files/l10n/fr.js +++ b/apps/files/l10n/fr.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "Impossible de déterminer la date", "This operation is forbidden" : "Cette opération est interdite", "This directory is unavailable, please check the logs or contact the administrator" : "Ce répertoire n'est pas disponible. Consultez les logs ou contactez votre administrateur", + "Could not move \"{file}\", target exists" : "Impossible de déplacer \"{file}\", la cible existe", + "Could not move \"{file}\"" : "Impossible de déplacer \"{file}\"", + "{newName} already exists" : "{newName} existe déjà", + "Could not rename \"{fileName}\", it does not exist any more" : "Impossible de renommer \"{file}\", il n'existe plus", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{targetName}\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un nom différent.", + "Could not rename \"{fileName}\"" : "Impossible de renommer \"{fileName}\"", + "Could not create file \"{file}\"" : "Impossible de créer le fichier \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Impossible de créer le fichier \"{file}\" car il existe déjà", + "Could not create folder \"{dir}\"" : "Impossible de créer le dossier \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Impossible de créer le dossier \"{dir}\" car il existe déjà", + "Error deleting file \"{fileName}\"." : "Erreur pendant la suppression du fichier \"{fileName}\".", "No entries in this folder match '{filter}'" : "Aucune entrée de ce dossier ne correspond à '{filter}'", "Name" : "Nom", "Size" : "Taille", diff --git a/apps/files/l10n/fr.json b/apps/files/l10n/fr.json index 86c1fe44c09..ba25b77e3df 100644 --- a/apps/files/l10n/fr.json +++ b/apps/files/l10n/fr.json @@ -38,6 +38,17 @@ "Unable to determine date" : "Impossible de déterminer la date", "This operation is forbidden" : "Cette opération est interdite", "This directory is unavailable, please check the logs or contact the administrator" : "Ce répertoire n'est pas disponible. Consultez les logs ou contactez votre administrateur", + "Could not move \"{file}\", target exists" : "Impossible de déplacer \"{file}\", la cible existe", + "Could not move \"{file}\"" : "Impossible de déplacer \"{file}\"", + "{newName} already exists" : "{newName} existe déjà", + "Could not rename \"{fileName}\", it does not exist any more" : "Impossible de renommer \"{file}\", il n'existe plus", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Le nom \"{targetName}\" est déjà utilisé dans le dossier \"{dir}\". Merci de choisir un nom différent.", + "Could not rename \"{fileName}\"" : "Impossible de renommer \"{fileName}\"", + "Could not create file \"{file}\"" : "Impossible de créer le fichier \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Impossible de créer le fichier \"{file}\" car il existe déjà", + "Could not create folder \"{dir}\"" : "Impossible de créer le dossier \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Impossible de créer le dossier \"{dir}\" car il existe déjà", + "Error deleting file \"{fileName}\"." : "Erreur pendant la suppression du fichier \"{fileName}\".", "No entries in this folder match '{filter}'" : "Aucune entrée de ce dossier ne correspond à '{filter}'", "Name" : "Nom", "Size" : "Taille", diff --git a/apps/files/l10n/it.js b/apps/files/l10n/it.js index abf2e3b723f..93a013492a2 100644 --- a/apps/files/l10n/it.js +++ b/apps/files/l10n/it.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "Impossibile determinare la data", "This operation is forbidden" : "Questa operazione è vietata", "This directory is unavailable, please check the logs or contact the administrator" : "Questa cartella non è disponibile, controlla i log o contatta l'amministratore", + "Could not move \"{file}\", target exists" : "Impossibile spostare \"{file}\", la destinazione esiste già", + "Could not move \"{file}\"" : "Impossibile spostare \"{file}\"", + "{newName} already exists" : "{newName} esiste già", + "Could not rename \"{fileName}\", it does not exist any more" : "Impossibile rinominare \"{fileName}\", non esiste più", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Il nome \"{targetName}\" è attualmente in uso nella cartella \"{dir}\". Scegli un nome diverso.", + "Could not rename \"{fileName}\"" : "Impossibile rinominare \"{fileName}\"", + "Could not create file \"{file}\"" : "Impossibile creare il file \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Impossibile creare il file \"{file}\" poiché esiste già", + "Could not create folder \"{dir}\"" : "Impossibile creare la cartella \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Impossibile creare la cartella \"{dir}\" poiché esiste già", + "Error deleting file \"{fileName}\"." : "Errore durante l'eliminazione del file \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nessuna voce in questa cartella corrisponde a '{filter}'", "Name" : "Nome", "Size" : "Dimensione", diff --git a/apps/files/l10n/it.json b/apps/files/l10n/it.json index 89a6f9a7364..25d3834b0a3 100644 --- a/apps/files/l10n/it.json +++ b/apps/files/l10n/it.json @@ -38,6 +38,17 @@ "Unable to determine date" : "Impossibile determinare la data", "This operation is forbidden" : "Questa operazione è vietata", "This directory is unavailable, please check the logs or contact the administrator" : "Questa cartella non è disponibile, controlla i log o contatta l'amministratore", + "Could not move \"{file}\", target exists" : "Impossibile spostare \"{file}\", la destinazione esiste già", + "Could not move \"{file}\"" : "Impossibile spostare \"{file}\"", + "{newName} already exists" : "{newName} esiste già", + "Could not rename \"{fileName}\", it does not exist any more" : "Impossibile rinominare \"{fileName}\", non esiste più", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Il nome \"{targetName}\" è attualmente in uso nella cartella \"{dir}\". Scegli un nome diverso.", + "Could not rename \"{fileName}\"" : "Impossibile rinominare \"{fileName}\"", + "Could not create file \"{file}\"" : "Impossibile creare il file \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "Impossibile creare il file \"{file}\" poiché esiste già", + "Could not create folder \"{dir}\"" : "Impossibile creare la cartella \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "Impossibile creare la cartella \"{dir}\" poiché esiste già", + "Error deleting file \"{fileName}\"." : "Errore durante l'eliminazione del file \"{fileName}\".", "No entries in this folder match '{filter}'" : "Nessuna voce in questa cartella corrisponde a '{filter}'", "Name" : "Nome", "Size" : "Dimensione", diff --git a/apps/files/l10n/sq.js b/apps/files/l10n/sq.js index f2db47ddf98..6811dc5791f 100644 --- a/apps/files/l10n/sq.js +++ b/apps/files/l10n/sq.js @@ -40,6 +40,17 @@ OC.L10N.register( "Unable to determine date" : "S’arrihet të përcaktohet data", "This operation is forbidden" : "Ky veprim është i ndaluar", "This directory is unavailable, please check the logs or contact the administrator" : "Kjo drejtori nuk kapet, ju lutemi, kontrolloni regjistrat ose lidhuni me përgjegjësin", + "Could not move \"{file}\", target exists" : "S’u lëviz dot \"{file}\", objektivi ekziston", + "Could not move \"{file}\"" : "S’u lëviz dot \"{file}\"", + "{newName} already exists" : "{newName} ekziston tashmë", + "Could not rename \"{fileName}\", it does not exist any more" : "S’u riemërtua dot \"{fileName}\", s’ekziston më", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Emri \"{targetName}\" është tashmë i përdorur te dosja \"{dir}\". Ju lutemi, zgjidhni një emër tjetër.", + "Could not rename \"{fileName}\"" : "S’u riemërtua dot \"{fileName}\"", + "Could not create file \"{file}\"" : "S’u krijua dot kartela \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "S’u krijua dot kartela \"{file}\" ngaqë ka një të tillë", + "Could not create folder \"{dir}\"" : "S’u krijua dot dosja \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "S’u krijua dot dosja \"{dir}\" ngaqë ka një të tillë", + "Error deleting file \"{fileName}\"." : "Gabim në fshirjen e kartelës \"{fileName}\".", "No entries in this folder match '{filter}'" : "Në këtë dosje s’ka zëra me përputhje me '{filter}'", "Name" : "Emër", "Size" : "Madhësi", diff --git a/apps/files/l10n/sq.json b/apps/files/l10n/sq.json index 473a4b18d2d..45b9d6acb61 100644 --- a/apps/files/l10n/sq.json +++ b/apps/files/l10n/sq.json @@ -38,6 +38,17 @@ "Unable to determine date" : "S’arrihet të përcaktohet data", "This operation is forbidden" : "Ky veprim është i ndaluar", "This directory is unavailable, please check the logs or contact the administrator" : "Kjo drejtori nuk kapet, ju lutemi, kontrolloni regjistrat ose lidhuni me përgjegjësin", + "Could not move \"{file}\", target exists" : "S’u lëviz dot \"{file}\", objektivi ekziston", + "Could not move \"{file}\"" : "S’u lëviz dot \"{file}\"", + "{newName} already exists" : "{newName} ekziston tashmë", + "Could not rename \"{fileName}\", it does not exist any more" : "S’u riemërtua dot \"{fileName}\", s’ekziston më", + "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "Emri \"{targetName}\" është tashmë i përdorur te dosja \"{dir}\". Ju lutemi, zgjidhni një emër tjetër.", + "Could not rename \"{fileName}\"" : "S’u riemërtua dot \"{fileName}\"", + "Could not create file \"{file}\"" : "S’u krijua dot kartela \"{file}\"", + "Could not create file \"{file}\" because it already exists" : "S’u krijua dot kartela \"{file}\" ngaqë ka një të tillë", + "Could not create folder \"{dir}\"" : "S’u krijua dot dosja \"{dir}\"", + "Could not create folder \"{dir}\" because it already exists" : "S’u krijua dot dosja \"{dir}\" ngaqë ka një të tillë", + "Error deleting file \"{fileName}\"." : "Gabim në fshirjen e kartelës \"{fileName}\".", "No entries in this folder match '{filter}'" : "Në këtë dosje s’ka zëra me përputhje me '{filter}'", "Name" : "Emër", "Size" : "Madhësi", diff --git a/apps/files_external/appinfo/register_command.php b/apps/files_external/appinfo/register_command.php new file mode 100644 index 00000000000..a436dc95005 --- /dev/null +++ b/apps/files_external/appinfo/register_command.php @@ -0,0 +1,34 @@ +<?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/> + * + */ + + +use OCA\Files_External\Command\ListCommand; + +$userManager = OC::$server->getUserManager(); +$userSession = OC::$server->getUserSession(); + +$app = \OC_Mount_Config::$app; + +$globalStorageService = $app->getContainer()->query('\OCA\Files_external\Service\GlobalStoragesService'); +$userStorageService = $app->getContainer()->query('\OCA\Files_external\Service\UserStoragesService'); + +/** @var Symfony\Component\Console\Application $application */ +$application->add(new ListCommand($globalStorageService, $userStorageService, $userSession, $userManager)); diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php index 39ded1dc2ec..e66c010a8cf 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -36,6 +36,7 @@ namespace OCA\Files_External\AppInfo; 'resources' => array( 'global_storages' => array('url' => '/globalstorages'), 'user_storages' => array('url' => '/userstorages'), + 'user_global_storages' => array('url' => '/userglobalstorages'), ), 'routes' => array( array( diff --git a/apps/files_external/command/listcommand.php b/apps/files_external/command/listcommand.php new file mode 100644 index 00000000000..4c027ffcb8e --- /dev/null +++ b/apps/files_external/command/listcommand.php @@ -0,0 +1,231 @@ +<?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\Command; + +use OC\Core\Command\Base; +use OCA\Files_external\Lib\StorageConfig; +use OCA\Files_external\Service\GlobalStoragesService; +use OCA\Files_external\Service\UserStoragesService; +use OCP\IUserManager; +use OCP\IUserSession; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\Table; +use Symfony\Component\Console\Helper\TableHelper; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class ListCommand extends Base { + /** + * @var GlobalStoragesService + */ + private $globalService; + + /** + * @var UserStoragesService + */ + private $userService; + + /** + * @var IUserSession + */ + private $userSession; + + /** + * @var IUserManager + */ + private $userManager; + + function __construct(GlobalStoragesService $globalService, UserStoragesService $userService, IUserSession $userSession, IUserManager $userManager) { + parent::__construct(); + $this->globalService = $globalService; + $this->userService = $userService; + $this->userSession = $userSession; + $this->userManager = $userManager; + } + + protected function configure() { + $this + ->setName('files_external:list') + ->setDescription('List configured mounts') + ->addArgument( + 'user_id', + InputArgument::OPTIONAL, + 'user id to list the personal mounts for, if no user is provided admin mounts will be listed' + )->addOption( + 'show-password', + null, + InputOption::VALUE_NONE, + 'show passwords and secrets' + )->addOption( + 'full', + null, + InputOption::VALUE_NONE, + 'don\'t truncate long values in table output' + ); + parent::configure(); + } + + protected function execute(InputInterface $input, OutputInterface $output) { + $userId = $input->getArgument('user_id'); + if (!empty($userId)) { + $user = $this->userManager->get($userId); + if (is_null($user)) { + $output->writeln("<error>user $userId not found</error>"); + return; + } + $this->userSession->setUser($user); + $storageService = $this->userService; + } else { + $storageService = $this->globalService; + } + + /** @var $mounts StorageConfig[] */ + $mounts = $storageService->getAllStorages(); + + if (count($mounts) === 0) { + if ($userId) { + $output->writeln("<info>No mounts configured by $userId</info>"); + } else { + $output->writeln("<info>No admin mounts configured</info>"); + } + return; + } + + $headers = ['Mount ID', 'Mount Point', 'Storage', 'Authentication Type', 'Configuration', 'Options']; + + if (!$userId) { + $headers[] = 'Applicable Users'; + $headers[] = 'Applicable Groups'; + } + + if (!$input->getOption('show-password')) { + $hideKeys = ['password', 'refresh_token', 'token', 'client_secret', 'public_key', 'private_key']; + foreach ($mounts as $mount) { + $config = $mount->getBackendOptions(); + foreach ($config as $key => $value) { + if (in_array($key, $hideKeys)) { + $mount->setBackendOption($key, '***'); + } + } + } + } + + $outputType = $input->getOption('output'); + if ($outputType === self::OUTPUT_FORMAT_JSON || $outputType === self::OUTPUT_FORMAT_JSON_PRETTY) { + $keys = array_map(function ($header) { + return strtolower(str_replace(' ', '_', $header)); + }, $headers); + + $pairs = array_map(function (StorageConfig $config) use ($keys, $userId) { + $values = [ + $config->getId(), + $config->getMountPoint(), + $config->getBackend()->getStorageClass(), + $config->getAuthMechanism()->getScheme(), + $config->getBackendOptions(), + $config->getMountOptions() + ]; + if (!$userId) { + $values[] = $config->getApplicableUsers(); + $values[] = $config->getApplicableGroups(); + } + + return array_combine($keys, $values); + }, $mounts); + if ($outputType === self::OUTPUT_FORMAT_JSON) { + $output->writeln(json_encode(array_values($pairs))); + } else { + $output->writeln(json_encode(array_values($pairs), JSON_PRETTY_PRINT)); + } + } else { + $full = $input->getOption('full'); + $defaultMountOptions = [ + 'encrypt' => true, + 'previews' => true, + 'filesystem_check_changes' => 1 + ]; + $rows = array_map(function (StorageConfig $config) use ($userId, $defaultMountOptions, $full) { + $storageConfig = $config->getBackendOptions(); + $keys = array_keys($storageConfig); + $values = array_values($storageConfig); + + if (!$full) { + $values = array_map(function ($value) { + if (is_string($value) && strlen($value) > 32) { + return substr($value, 0, 6) . '...' . substr($value, -6, 6); + } else { + return $value; + } + }, $values); + } + + $configStrings = array_map(function ($key, $value) { + return $key . ': ' . json_encode($value); + }, $keys, $values); + $configString = implode(', ', $configStrings); + + $mountOptions = $config->getMountOptions(); + // hide defaults + foreach ($mountOptions as $key => $value) { + if ($value === $defaultMountOptions[$key]) { + unset($mountOptions[$key]); + } + } + $keys = array_keys($mountOptions); + $values = array_values($mountOptions); + + $optionsStrings = array_map(function ($key, $value) { + return $key . ': ' . json_encode($value); + }, $keys, $values); + $optionsString = implode(', ', $optionsStrings); + + $values = [ + $config->getId(), + $config->getMountPoint(), + $config->getBackend()->getText(), + $config->getAuthMechanism()->getText(), + $configString, + $optionsString + ]; + + if (!$userId) { + $applicableUsers = implode(', ', $config->getApplicableUsers()); + $applicableGroups = implode(', ', $config->getApplicableGroups()); + if ($applicableUsers === '' && $applicableGroups === '') { + $applicableUsers = 'All'; + } + $values[] = $applicableUsers; + $values[] = $applicableGroups; + } + + return $values; + }, $mounts); + + $table = new Table($output); + $table->setHeaders($headers); + $table->setRows($rows); + $table->render(); + } + } +} diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 048f3588ed7..c66bd902d8d 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -256,6 +256,20 @@ abstract class StoragesController extends Controller { } /** + * Get all storage entries + * + * @return DataResponse + */ + public function index() { + $storages = $this->service->getAllStorages(); + + return new DataResponse( + $storages, + Http::STATUS_OK + ); + } + + /** * Get an external storage entry. * * @param int $id storage id diff --git a/apps/files_external/controller/userglobalstoragescontroller.php b/apps/files_external/controller/userglobalstoragescontroller.php new file mode 100644 index 00000000000..c6f777763e8 --- /dev/null +++ b/apps/files_external/controller/userglobalstoragescontroller.php @@ -0,0 +1,121 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@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\Controller; + +use \OCP\IRequest; +use \OCP\IL10N; +use \OCP\AppFramework\Http\DataResponse; +use \OCP\AppFramework\Controller; +use \OCP\AppFramework\Http; +use \OCA\Files_external\Service\UserGlobalStoragesService; +use \OCA\Files_external\NotFoundException; +use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\Backend\Backend; + +/** + * User global storages controller + */ +class UserGlobalStoragesController extends StoragesController { + /** + * Creates a new user global storages controller. + * + * @param string $AppName application name + * @param IRequest $request request object + * @param IL10N $l10n l10n service + * @param UserGlobalStoragesService $userGlobalStoragesService storage service + */ + public function __construct( + $AppName, + IRequest $request, + IL10N $l10n, + UserGlobalStoragesService $userGlobalStoragesService + ) { + parent::__construct( + $AppName, + $request, + $l10n, + $userGlobalStoragesService + ); + } + + /** + * Get all storage entries + * + * @return DataResponse + * + * @NoAdminRequired + */ + public function index() { + $storages = $this->service->getUniqueStorages(); + + // remove configuration data, this must be kept private + foreach ($storages as $storage) { + $this->sanitizeStorage($storage); + } + + return new DataResponse( + $storages, + Http::STATUS_OK + ); + } + + /** + * Get an external storage entry. + * + * @param int $id storage id + * @return DataResponse + * + * @NoAdminRequired + */ + public function show($id) { + try { + $storage = $this->service->getStorage($id); + + $this->updateStorageStatus($storage); + } catch (NotFoundException $e) { + return new DataResponse( + [ + 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id)) + ], + Http::STATUS_NOT_FOUND + ); + } + + $this->sanitizeStorage($storage); + + return new DataResponse( + $storage, + Http::STATUS_OK + ); + } + + /** + * Remove sensitive data from a StorageConfig before returning it to the user + * + * @param StorageConfig $storage + */ + protected function sanitizeStorage(StorageConfig $storage) { + $storage->setBackendOptions([]); + $storage->setMountOptions([]); + } + +} diff --git a/apps/files_external/js/public_key.js b/apps/files_external/js/public_key.js index a8546067452..5f9658381f0 100644 --- a/apps/files_external/js/public_key.js +++ b/apps/files_external/js/public_key.js @@ -1,10 +1,16 @@ $(document).ready(function() { - OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme) { + OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) { if (scheme === 'publickey') { var config = $tr.find('.configuration'); if ($(config).find('[name="public_key_generate"]').length === 0) { setupTableRow($tr, config); + onCompletion.then(function() { + // If there's no private key, build one + if (0 === $(config).find('[data-parameter="private_key"]').val().length) { + generateKeys($tr); + } + }); } } }); @@ -22,10 +28,6 @@ $(document).ready(function() { .attr('value', t('files_external', 'Generate keys')) .attr('name', 'public_key_generate') ); - // If there's no private key, build one - if (0 === $(config).find('[data-parameter="private_key"]').val().length) { - generateKeys(tr); - } } function generateKeys(tr) { @@ -33,7 +35,7 @@ $(document).ready(function() { $.post(OC.filePath('files_external', 'ajax', 'public_key.php'), {}, function(result) { if (result && result.status === 'success') { - $(config).find('[data-parameter="public_key"]').val(result.data.public_key); + $(config).find('[data-parameter="public_key"]').val(result.data.public_key).keyup(); $(config).find('[data-parameter="private_key"]').val(result.data.private_key); OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() { // Nothing to do diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index a839f396b9b..f712ecf4328 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -623,36 +623,7 @@ MountConfigListView.prototype = _.extend({ this._allBackends = this.$el.find('.selectBackend').data('configurations'); this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms'); - //initialize hidden input field with list of users and groups - this.$el.find('tr:not(#addMountPoint)').each(function(i,tr) { - var $tr = $(tr); - var $applicable = $tr.find('.applicable'); - if ($applicable.length > 0) { - var groups = $applicable.data('applicable-groups'); - var groupsId = []; - $.each(groups, function () { - groupsId.push(this + '(group)'); - }); - var users = $applicable.data('applicable-users'); - if (users.indexOf('all') > -1 || users === '') { - $tr.find('.applicableUsers').val(''); - } else { - $tr.find('.applicableUsers').val(groupsId.concat(users).join(',')); - } - } - }); - - addSelect2(this.$el.find('tr:not(#addMountPoint) .applicableUsers'), this._userListLimit); - this.$el.tooltip({ - selector: '.status span', - container: 'body' - }); - this._initEvents(); - - this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { - self.recheckStorageConfig($(tr)); - }); }, /** @@ -661,7 +632,7 @@ MountConfigListView.prototype = _.extend({ */ whenSelectBackend: function(callback) { this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { - var backend = $(tr).find('.backend').data('class'); + var backend = $(tr).find('.backend').data('identifier'); callback($(tr), backend); }); this.on('selectBackend', callback); @@ -725,65 +696,41 @@ MountConfigListView.prototype = _.extend({ _onSelectBackend: function(event) { var $target = $(event.target); - var $el = this.$el; var $tr = $target.closest('tr'); - $el.find('tbody').append($tr.clone()); - $el.find('tbody tr').last().find('.mountPoint input').val(''); - $tr.data('constructing', true); - var selected = $target.find('option:selected').text(); - var backend = $target.val(); - $tr.find('.backend').text(selected); - if ($tr.find('.mountPoint input').val() === '') { - $tr.find('.mountPoint input').val(this._suggestMountPoint(selected)); - } - $tr.addClass(backend); - $tr.find('.backend').data('class', backend); - var backendConfiguration = this._allBackends[backend]; - var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>'); - $.each(this._allAuthMechanisms, function(authClass, authMechanism) { - if (backendConfiguration['authSchemes'][authMechanism['scheme']]) { - selectAuthMechanism.append( - $('<option value="'+authClass+'" data-scheme="'+authMechanism['scheme']+'">'+authMechanism['name']+'</option>') - ); - } - }); - $tr.find('td.authentication').append(selectAuthMechanism); + var storageConfig = new this._storageConfigClass(); + storageConfig.mountPoint = $tr.find('.mountPoint input').val(); + storageConfig.backend = $target.val(); + $tr.find('.mountPoint input').val(''); - var $td = $tr.find('td.configuration'); - $.each(backendConfiguration['configuration'], _.partial(this.writeParameterInput, $td)); + var onCompletion = jQuery.Deferred(); + $tr = this.newStorage(storageConfig, onCompletion); + onCompletion.resolve(); - this.trigger('selectBackend', $tr, backend); - - selectAuthMechanism.trigger('change'); // generate configuration parameters for auth mechanism - - var priorityEl = $('<input type="hidden" class="priority" value="' + backendConfiguration['priority'] + '" />'); - $tr.append(priorityEl); - $td.children().not('[type=hidden]').first().focus(); - - // FIXME default backend mount options - $tr.find('input.mountOptions').val(JSON.stringify({ - 'encrypt': true, - 'previews': true, - 'filesystem_check_changes': 1 - })); - - $tr.find('td').last().attr('class', 'remove'); - $tr.find('td.mountOptionsToggle').removeClass('hidden'); - $tr.find('td').last().removeAttr('style'); - $tr.removeAttr('id'); - $target.remove(); - addSelect2($tr.find('.applicableUsers'), this._userListLimit); - - $tr.removeData('constructing'); + $tr.find('td.configuration').children().not('[type=hidden]').first().focus(); this.saveStorageConfig($tr); }, _onSelectAuthMechanism: function(event) { var $target = $(event.target); var $tr = $target.closest('tr'); - var authMechanism = $target.val(); + + var onCompletion = jQuery.Deferred(); + this.configureAuthMechanism($tr, authMechanism, onCompletion); + onCompletion.resolve(); + + this.saveStorageConfig($tr); + }, + + /** + * Configure the storage config with a new authentication mechanism + * + * @param {jQuery} $tr config row + * @param {string} authMechanism + * @param {jQuery.Deferred} onCompletion + */ + configureAuthMechanism: function($tr, authMechanism, onCompletion) { var authMechanismConfiguration = this._allAuthMechanisms[authMechanism]; var $td = $tr.find('td.configuration'); $td.find('.auth-param').remove(); @@ -793,15 +740,172 @@ MountConfigListView.prototype = _.extend({ )); this.trigger('selectAuthMechanism', - $tr, authMechanism, authMechanismConfiguration['scheme'] + $tr, authMechanism, authMechanismConfiguration['scheme'], onCompletion ); + }, + + /** + * Create a config row for a new storage + * + * @param {StorageConfig} storageConfig storage config to pull values from + * @param {jQuery.Deferred} onCompletion + * @return {jQuery} created row + */ + newStorage: function(storageConfig, onCompletion) { + var mountPoint = storageConfig.mountPoint; + var backend = this._allBackends[storageConfig.backend]; + + // FIXME: Replace with a proper Handlebar template + var $tr = this.$el.find('tr#addMountPoint'); + this.$el.find('tbody').append($tr.clone()); + + $tr.find('td').last().attr('class', 'remove'); + $tr.find('td.mountOptionsToggle').removeClass('hidden'); + $tr.find('td').last().removeAttr('style'); + $tr.removeAttr('id'); + $tr.find('select#selectBackend'); + addSelect2($tr.find('.applicableUsers'), this._userListLimit); + + if (storageConfig.id) { + $tr.data('id', storageConfig.id); + } + + $tr.find('.backend').text(backend.name); + if (mountPoint === '') { + mountPoint = this._suggestMountPoint(backend.name); + } + $tr.find('.mountPoint input').val(mountPoint); + $tr.addClass(backend.identifier); + $tr.find('.backend').data('identifier', backend.identifier); + + var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>'); + $.each(this._allAuthMechanisms, function(authIdentifier, authMechanism) { + if (backend.authSchemes[authMechanism.scheme]) { + selectAuthMechanism.append( + $('<option value="'+authMechanism.identifier+'" data-scheme="'+authMechanism.scheme+'">'+authMechanism.name+'</option>') + ); + } + }); + if (storageConfig.authMechanism) { + selectAuthMechanism.val(storageConfig.authMechanism); + } else { + storageConfig.authMechanism = selectAuthMechanism.val(); + } + $tr.find('td.authentication').append(selectAuthMechanism); - if ($tr.data('constructing') !== true) { - // row is ready, trigger recheck - this.saveStorageConfig($tr); + var $td = $tr.find('td.configuration'); + $.each(backend.configuration, _.partial(this.writeParameterInput, $td)); + + this.trigger('selectBackend', $tr, backend.identifier, onCompletion); + this.configureAuthMechanism($tr, storageConfig.authMechanism, onCompletion); + + if (storageConfig.backendOptions) { + $td.children().each(function() { + var input = $(this); + var val = storageConfig.backendOptions[input.data('parameter')]; + if (val !== undefined) { + input.val(storageConfig.backendOptions[input.data('parameter')]); + highlightInput(input); + } + }); + } + + var applicable = []; + if (storageConfig.applicableUsers) { + applicable = applicable.concat(storageConfig.applicableUsers); + } + if (storageConfig.applicableGroups) { + applicable = applicable.concat( + _.map(storageConfig.applicableGroups, function(group) { + return group+'(group)'; + }) + ); } + $tr.find('.applicableUsers').val(applicable).trigger('change'); + + var priorityEl = $('<input type="hidden" class="priority" value="' + backend.priority + '" />'); + $tr.append(priorityEl); + + if (storageConfig.mountOptions) { + $tr.find('input.mountOptions').val(JSON.stringify(storageConfig.mountOptions)); + } else { + // FIXME default backend mount options + $tr.find('input.mountOptions').val(JSON.stringify({ + 'encrypt': true, + 'previews': true, + 'filesystem_check_changes': 1 + })); + } + + return $tr; }, + /** + * Load storages into config rows + */ + loadStorages: function() { + var self = this; + + if (this._isPersonal) { + // load userglobal storages + $.ajax({ + type: 'GET', + url: OC.generateUrl('apps/files_external/userglobalstorages'), + contentType: 'application/json', + success: function(result) { + var onCompletion = jQuery.Deferred(); + $.each(result, function(i, storageParams) { + storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash + var storageConfig = new self._storageConfigClass(); + _.extend(storageConfig, storageParams); + var $tr = self.newStorage(storageConfig, onCompletion); + + // userglobal storages must be at the top of the list + $tr.detach(); + self.$el.prepend($tr); + + var $authentication = $tr.find('.authentication'); + $authentication.text($authentication.find('select option:selected').text()); + + // userglobal storages do not expose configuration data + $tr.find('.configuration').text(t('files_external', 'Admin defined')); + + // disable any other inputs + $tr.find('.mountOptionsToggle, .remove').empty(); + $tr.find('input, select, button').attr('disabled', 'disabled'); + }); + onCompletion.resolve(); + } + }); + } + + var url = this._storageConfigClass.prototype._url; + + $.ajax({ + type: 'GET', + url: OC.generateUrl(url), + contentType: 'application/json', + success: function(result) { + var onCompletion = jQuery.Deferred(); + $.each(result, function(i, storageParams) { + storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash + var storageConfig = new self._storageConfigClass(); + _.extend(storageConfig, storageParams); + var $tr = self.newStorage(storageConfig, onCompletion); + self.recheckStorageConfig($tr); + }); + onCompletion.resolve(); + } + }); + }, + + /** + * @param {jQuery} $td + * @param {string} parameter + * @param {string} placeholder + * @param {Array} classes + * @return {jQuery} newly created input + */ writeParameterInput: function($td, parameter, placeholder, classes) { classes = $.isArray(classes) ? classes : []; classes.push('added'); @@ -822,6 +926,7 @@ MountConfigListView.prototype = _.extend({ } highlightInput(newElement); $td.append(newElement); + return newElement; }, /** @@ -831,14 +936,14 @@ MountConfigListView.prototype = _.extend({ * @return {OCA.External.StorageConfig} storage model instance */ getStorageConfig: function($tr) { - var storageId = parseInt($tr.attr('data-id'), 10); + var storageId = $tr.data('id'); if (!storageId) { // new entry storageId = null; } var storage = new this._storageConfigClass(storageId); storage.mountPoint = $tr.find('.mountPoint input').val(); - storage.backend = $tr.find('.backend').data('class'); + storage.backend = $tr.find('.backend').data('identifier'); storage.authMechanism = $tr.find('.selectAuthMechanism').val(); var classOptions = {}; @@ -951,8 +1056,8 @@ MountConfigListView.prototype = _.extend({ if (concurrentTimer === undefined || $tr.data('save-timer') === concurrentTimer ) { - self.updateStatus($tr, result.status, result.statusMessage); - $tr.attr('data-id', result.id); + self.updateStatus($tr, result.status); + $tr.data('id', result.id); if (_.isFunction(callback)) { callback(storage); @@ -1054,12 +1159,12 @@ MountConfigListView.prototype = _.extend({ } return defaultMountPoint + append; }, - + /** * Toggles the mount options dropdown * * @param {Object} $tr configuration row - */ + */ _showMountOptionsDropdown: function($tr) { if (this._preventNextDropdown) { // prevented because the click was on the toggle @@ -1106,6 +1211,7 @@ $(document).ready(function() { var mountConfigListView = new MountConfigListView($('#externalStorage'), { encryptionEnabled: encryptionEnabled }); + mountConfigListView.loadStorages(); $('#sslCertificate').on('click', 'td.remove>img', function() { var $tr = $(this).closest('tr'); diff --git a/apps/files_external/l10n/ast.js b/apps/files_external/l10n/ast.js index b663657e029..f67309631e8 100644 --- a/apps/files_external/l10n/ast.js +++ b/apps/files_external/l10n/ast.js @@ -53,8 +53,8 @@ OC.L10N.register( "Folder name" : "Nome de la carpeta", "Configuration" : "Configuración", "Available for" : "Disponible pa", - "Delete" : "Desaniciar", "Add storage" : "Amestar almacenamientu", + "Delete" : "Desaniciar", "Allow users to mount the following external storage" : "Permitir a los usuarios montar el siguiente almacenamientu esternu" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/ast.json b/apps/files_external/l10n/ast.json index 0e98fe5a24a..b4bc4355bc4 100644 --- a/apps/files_external/l10n/ast.json +++ b/apps/files_external/l10n/ast.json @@ -51,8 +51,8 @@ "Folder name" : "Nome de la carpeta", "Configuration" : "Configuración", "Available for" : "Disponible pa", - "Delete" : "Desaniciar", "Add storage" : "Amestar almacenamientu", + "Delete" : "Desaniciar", "Allow users to mount the following external storage" : "Permitir a los usuarios montar el siguiente almacenamientu esternu" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/az.js b/apps/files_external/l10n/az.js index 700e36a4aef..42c9a496185 100644 --- a/apps/files_external/l10n/az.js +++ b/apps/files_external/l10n/az.js @@ -57,9 +57,9 @@ OC.L10N.register( "Folder name" : "Qovluq adı", "Configuration" : "Konfiqurasiya", "Available for" : "Üçün mövcuddur", + "Add storage" : "Deponu əlavə et", "Advanced settings" : "İrəliləmiş quraşdırmalar", "Delete" : "Sil", - "Add storage" : "Deponu əlavə et", "Allow users to mount the following external storage" : "Göstərilən kənar deponun bərkidilməsi üçün istifadəçilərə izin ver" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/az.json b/apps/files_external/l10n/az.json index 6cccabb2dd6..4e01cdf954c 100644 --- a/apps/files_external/l10n/az.json +++ b/apps/files_external/l10n/az.json @@ -55,9 +55,9 @@ "Folder name" : "Qovluq adı", "Configuration" : "Konfiqurasiya", "Available for" : "Üçün mövcuddur", + "Add storage" : "Deponu əlavə et", "Advanced settings" : "İrəliləmiş quraşdırmalar", "Delete" : "Sil", - "Add storage" : "Deponu əlavə et", "Allow users to mount the following external storage" : "Göstərilən kənar deponun bərkidilməsi üçün istifadəçilərə izin ver" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/bg_BG.js b/apps/files_external/l10n/bg_BG.js index f14b13d2a0e..cc52682f956 100644 --- a/apps/files_external/l10n/bg_BG.js +++ b/apps/files_external/l10n/bg_BG.js @@ -59,9 +59,9 @@ OC.L10N.register( "Folder name" : "Име на папката", "Configuration" : "Настройки", "Available for" : "Достъпно за", + "Add storage" : "Добави дисково пространство", "Advanced settings" : "Разширени настройки", "Delete" : "Изтрий", - "Add storage" : "Добави дисково пространство", "Allow users to mount the following external storage" : "Разреши на потребителите да прикачват следното външно дисково пространство" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/bg_BG.json b/apps/files_external/l10n/bg_BG.json index 6cce8fd5cfa..df3fe1c20e8 100644 --- a/apps/files_external/l10n/bg_BG.json +++ b/apps/files_external/l10n/bg_BG.json @@ -57,9 +57,9 @@ "Folder name" : "Име на папката", "Configuration" : "Настройки", "Available for" : "Достъпно за", + "Add storage" : "Добави дисково пространство", "Advanced settings" : "Разширени настройки", "Delete" : "Изтрий", - "Add storage" : "Добави дисково пространство", "Allow users to mount the following external storage" : "Разреши на потребителите да прикачват следното външно дисково пространство" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/ca.js b/apps/files_external/l10n/ca.js index b14057ea420..56c5f72abf9 100644 --- a/apps/files_external/l10n/ca.js +++ b/apps/files_external/l10n/ca.js @@ -66,9 +66,9 @@ OC.L10N.register( "Folder name" : "Nom de la carpeta", "Configuration" : "Configuració", "Available for" : "Disponible per", + "Add storage" : "Afegeix emmagatzemament", "Advanced settings" : "Configuració avançada", "Delete" : "Esborra", - "Add storage" : "Afegeix emmagatzemament", "Allow users to mount the following external storage" : "Permet als usuaris muntar els dispositius externs següents" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/ca.json b/apps/files_external/l10n/ca.json index cce51970c18..7a166c3c011 100644 --- a/apps/files_external/l10n/ca.json +++ b/apps/files_external/l10n/ca.json @@ -64,9 +64,9 @@ "Folder name" : "Nom de la carpeta", "Configuration" : "Configuració", "Available for" : "Disponible per", + "Add storage" : "Afegeix emmagatzemament", "Advanced settings" : "Configuració avançada", "Delete" : "Esborra", - "Add storage" : "Afegeix emmagatzemament", "Allow users to mount the following external storage" : "Permet als usuaris muntar els dispositius externs següents" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/cs_CZ.js b/apps/files_external/l10n/cs_CZ.js index 3e9a14f9998..5cf807f7fb8 100644 --- a/apps/files_external/l10n/cs_CZ.js +++ b/apps/files_external/l10n/cs_CZ.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Ověření", "Configuration" : "Nastavení", "Available for" : "Dostupné pro", + "Add storage" : "Přidat úložiště", "Advanced settings" : "Pokročilá nastavení", "Delete" : "Smazat", - "Add storage" : "Přidat úložiště", "Allow users to mount external storage" : "Povolit uživatelům připojení externího úložiště", "Allow users to mount the following external storage" : "Povolit uživatelů připojit následující externí úložiště" }, diff --git a/apps/files_external/l10n/cs_CZ.json b/apps/files_external/l10n/cs_CZ.json index 4c6f940666e..a9ceedbf2f4 100644 --- a/apps/files_external/l10n/cs_CZ.json +++ b/apps/files_external/l10n/cs_CZ.json @@ -97,9 +97,9 @@ "Authentication" : "Ověření", "Configuration" : "Nastavení", "Available for" : "Dostupné pro", + "Add storage" : "Přidat úložiště", "Advanced settings" : "Pokročilá nastavení", "Delete" : "Smazat", - "Add storage" : "Přidat úložiště", "Allow users to mount external storage" : "Povolit uživatelům připojení externího úložiště", "Allow users to mount the following external storage" : "Povolit uživatelů připojit následující externí úložiště" },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" diff --git a/apps/files_external/l10n/da.js b/apps/files_external/l10n/da.js index ba5d572a206..dc86b6f1c06 100644 --- a/apps/files_external/l10n/da.js +++ b/apps/files_external/l10n/da.js @@ -98,9 +98,9 @@ OC.L10N.register( "Authentication" : "Godkendelse", "Configuration" : "Opsætning", "Available for" : "Tilgængelig for", + "Add storage" : "Tilføj lager", "Advanced settings" : "Avancerede indstillinger", "Delete" : "Slet", - "Add storage" : "Tilføj lager", "Allow users to mount the following external storage" : "Tillad brugere at montere følgende som eksternt lager" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/da.json b/apps/files_external/l10n/da.json index c819040bc74..bb7961fbb2b 100644 --- a/apps/files_external/l10n/da.json +++ b/apps/files_external/l10n/da.json @@ -96,9 +96,9 @@ "Authentication" : "Godkendelse", "Configuration" : "Opsætning", "Available for" : "Tilgængelig for", + "Add storage" : "Tilføj lager", "Advanced settings" : "Avancerede indstillinger", "Delete" : "Slet", - "Add storage" : "Tilføj lager", "Allow users to mount the following external storage" : "Tillad brugere at montere følgende som eksternt lager" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js index 19468ead63f..8fbbee567a7 100644 --- a/apps/files_external/l10n/de.js +++ b/apps/files_external/l10n/de.js @@ -85,9 +85,9 @@ OC.L10N.register( "Authentication" : "Authentifizierung", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount external storage" : "Benutzern erlauben, externen Speicher einzubinden", "Allow users to mount the following external storage" : "Benutzern erlauben, den oder die folgenden externen Speicher einzubinden:" }, diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json index fa9c7068e7b..02e495251a8 100644 --- a/apps/files_external/l10n/de.json +++ b/apps/files_external/l10n/de.json @@ -83,9 +83,9 @@ "Authentication" : "Authentifizierung", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount external storage" : "Benutzern erlauben, externen Speicher einzubinden", "Allow users to mount the following external storage" : "Benutzern erlauben, den oder die folgenden externen Speicher einzubinden:" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js index 610f737a4a8..7187e4d97d4 100644 --- a/apps/files_external/l10n/de_DE.js +++ b/apps/files_external/l10n/de_DE.js @@ -67,9 +67,9 @@ OC.L10N.register( "Folder name" : "Ordnername", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount the following external storage" : "Benutzern erlauben, den oder die folgenden externen Speicher einzubinden:" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json index fa21fea8f4a..c91d78eb0d2 100644 --- a/apps/files_external/l10n/de_DE.json +++ b/apps/files_external/l10n/de_DE.json @@ -65,9 +65,9 @@ "Folder name" : "Ordnername", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount the following external storage" : "Benutzern erlauben, den oder die folgenden externen Speicher einzubinden:" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/el.js b/apps/files_external/l10n/el.js index a83c241af7a..294ec9da6ff 100644 --- a/apps/files_external/l10n/el.js +++ b/apps/files_external/l10n/el.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Πιστοποίηση", "Configuration" : "Ρυθμίσεις", "Available for" : "Διαθέσιμο για", + "Add storage" : "Προσθηκη αποθηκευσης", "Advanced settings" : "Ρυθμίσεις για προχωρημένους", "Delete" : "Διαγραφή", - "Add storage" : "Προσθηκη αποθηκευσης", "Allow users to mount external storage" : "Να επιτρέπεται στους χρήστες η σύνδεση εξωτερικού χώρου", "Allow users to mount the following external storage" : "Χορήγηση άδειας στους χρήστες να συνδέσουν τα παρακάτω εξωτερικά μέσα αποθήκευσης" }, diff --git a/apps/files_external/l10n/el.json b/apps/files_external/l10n/el.json index 32f223cf8c0..431e81c3d7a 100644 --- a/apps/files_external/l10n/el.json +++ b/apps/files_external/l10n/el.json @@ -97,9 +97,9 @@ "Authentication" : "Πιστοποίηση", "Configuration" : "Ρυθμίσεις", "Available for" : "Διαθέσιμο για", + "Add storage" : "Προσθηκη αποθηκευσης", "Advanced settings" : "Ρυθμίσεις για προχωρημένους", "Delete" : "Διαγραφή", - "Add storage" : "Προσθηκη αποθηκευσης", "Allow users to mount external storage" : "Να επιτρέπεται στους χρήστες η σύνδεση εξωτερικού χώρου", "Allow users to mount the following external storage" : "Χορήγηση άδειας στους χρήστες να συνδέσουν τα παρακάτω εξωτερικά μέσα αποθήκευσης" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/en_GB.js b/apps/files_external/l10n/en_GB.js index 9829bd3e8c7..9efc720eb18 100644 --- a/apps/files_external/l10n/en_GB.js +++ b/apps/files_external/l10n/en_GB.js @@ -67,9 +67,9 @@ OC.L10N.register( "Folder name" : "Folder name", "Configuration" : "Configuration", "Available for" : "Available for", + "Add storage" : "Add storage", "Advanced settings" : "Advanced settings", "Delete" : "Delete", - "Add storage" : "Add storage", "Allow users to mount the following external storage" : "Allow users to mount the following external storage" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/en_GB.json b/apps/files_external/l10n/en_GB.json index 88467528431..87deeec5989 100644 --- a/apps/files_external/l10n/en_GB.json +++ b/apps/files_external/l10n/en_GB.json @@ -65,9 +65,9 @@ "Folder name" : "Folder name", "Configuration" : "Configuration", "Available for" : "Available for", + "Add storage" : "Add storage", "Advanced settings" : "Advanced settings", "Delete" : "Delete", - "Add storage" : "Add storage", "Allow users to mount the following external storage" : "Allow users to mount the following external storage" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/eo.js b/apps/files_external/l10n/eo.js index ce3dd21c012..ddab2360a6d 100644 --- a/apps/files_external/l10n/eo.js +++ b/apps/files_external/l10n/eo.js @@ -38,8 +38,8 @@ OC.L10N.register( "Folder name" : "Dosierujnomo", "Configuration" : "Agordo", "Available for" : "Disponebla por", - "Delete" : "Forigi", "Add storage" : "Aldoni memorilon", + "Delete" : "Forigi", "Allow users to mount the following external storage" : "Permesi uzantojn munti la jenajn malenajn memorilojn" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/eo.json b/apps/files_external/l10n/eo.json index d8c620d24c2..ec91f40abac 100644 --- a/apps/files_external/l10n/eo.json +++ b/apps/files_external/l10n/eo.json @@ -36,8 +36,8 @@ "Folder name" : "Dosierujnomo", "Configuration" : "Agordo", "Available for" : "Disponebla por", - "Delete" : "Forigi", "Add storage" : "Aldoni memorilon", + "Delete" : "Forigi", "Allow users to mount the following external storage" : "Permesi uzantojn munti la jenajn malenajn memorilojn" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/es.js b/apps/files_external/l10n/es.js index 5415e8963cd..46de520f55e 100644 --- a/apps/files_external/l10n/es.js +++ b/apps/files_external/l10n/es.js @@ -85,9 +85,9 @@ OC.L10N.register( "Authentication" : "Autenticación", "Configuration" : "Configuración", "Available for" : "Disponible para", + "Add storage" : "Añadir almacenamiento", "Advanced settings" : "Configuración avanzada", "Delete" : "Eliminar", - "Add storage" : "Añadir almacenamiento", "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 10cbf0c974a..3748040fdd4 100644 --- a/apps/files_external/l10n/es.json +++ b/apps/files_external/l10n/es.json @@ -83,9 +83,9 @@ "Authentication" : "Autenticación", "Configuration" : "Configuración", "Available for" : "Disponible para", + "Add storage" : "Añadir almacenamiento", "Advanced settings" : "Configuración avanzada", "Delete" : "Eliminar", - "Add storage" : "Añadir almacenamiento", "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/es_AR.js b/apps/files_external/l10n/es_AR.js index fd242104c8c..7fb87f1a1d3 100644 --- a/apps/files_external/l10n/es_AR.js +++ b/apps/files_external/l10n/es_AR.js @@ -22,7 +22,7 @@ OC.L10N.register( "External Storage" : "Almacenamiento externo", "Folder name" : "Nombre de la carpeta", "Configuration" : "Configuración", - "Delete" : "Borrar", - "Add storage" : "Añadir almacenamiento" + "Add storage" : "Añadir almacenamiento", + "Delete" : "Borrar" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/es_AR.json b/apps/files_external/l10n/es_AR.json index d9e91a3af47..9fb735f7a3a 100644 --- a/apps/files_external/l10n/es_AR.json +++ b/apps/files_external/l10n/es_AR.json @@ -20,7 +20,7 @@ "External Storage" : "Almacenamiento externo", "Folder name" : "Nombre de la carpeta", "Configuration" : "Configuración", - "Delete" : "Borrar", - "Add storage" : "Añadir almacenamiento" + "Add storage" : "Añadir almacenamiento", + "Delete" : "Borrar" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/es_MX.js b/apps/files_external/l10n/es_MX.js index 9682e360a58..c805ce16662 100644 --- a/apps/files_external/l10n/es_MX.js +++ b/apps/files_external/l10n/es_MX.js @@ -21,7 +21,7 @@ OC.L10N.register( "External Storage" : "Almacenamiento externo", "Folder name" : "Nombre de la carpeta", "Configuration" : "Configuración", - "Delete" : "Eliminar", - "Add storage" : "Añadir almacenamiento" + "Add storage" : "Añadir almacenamiento", + "Delete" : "Eliminar" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/es_MX.json b/apps/files_external/l10n/es_MX.json index 81b2f408d11..1df9bf70436 100644 --- a/apps/files_external/l10n/es_MX.json +++ b/apps/files_external/l10n/es_MX.json @@ -19,7 +19,7 @@ "External Storage" : "Almacenamiento externo", "Folder name" : "Nombre de la carpeta", "Configuration" : "Configuración", - "Delete" : "Eliminar", - "Add storage" : "Añadir almacenamiento" + "Add storage" : "Añadir almacenamiento", + "Delete" : "Eliminar" },"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 ae66daa37a6..fa22b4c6591 100644 --- a/apps/files_external/l10n/et_EE.js +++ b/apps/files_external/l10n/et_EE.js @@ -77,9 +77,9 @@ OC.L10N.register( "Authentication" : "Autentimine", "Configuration" : "Seadistamine", "Available for" : "Saadaval", + "Add storage" : "Lisa andmehoidla", "Advanced settings" : "Lisavalikud", "Delete" : "Kustuta", - "Add storage" : "Lisa andmehoidla", "Allow users to mount the following external storage" : "Võimalda kasutajatel ühendada järgmist välist andmehoidlat" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/et_EE.json b/apps/files_external/l10n/et_EE.json index f2cc31e46e1..37e7cc282ce 100644 --- a/apps/files_external/l10n/et_EE.json +++ b/apps/files_external/l10n/et_EE.json @@ -75,9 +75,9 @@ "Authentication" : "Autentimine", "Configuration" : "Seadistamine", "Available for" : "Saadaval", + "Add storage" : "Lisa andmehoidla", "Advanced settings" : "Lisavalikud", "Delete" : "Kustuta", - "Add storage" : "Lisa andmehoidla", "Allow users to mount the following external storage" : "Võimalda kasutajatel ühendada järgmist välist andmehoidlat" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/eu.js b/apps/files_external/l10n/eu.js index 599229a92b3..58742552e76 100644 --- a/apps/files_external/l10n/eu.js +++ b/apps/files_external/l10n/eu.js @@ -52,8 +52,8 @@ OC.L10N.register( "Folder name" : "Karpetaren izena", "Configuration" : "Konfigurazioa", "Available for" : "Hauentzat eskuragarri", - "Delete" : "Ezabatu", "Add storage" : "Gehitu biltegiratzea", + "Delete" : "Ezabatu", "Allow users to mount the following external storage" : "Baimendu erabiltzaileak hurrengo kanpo biltegiratzeak muntatzen" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/eu.json b/apps/files_external/l10n/eu.json index 5a568893060..f039441b464 100644 --- a/apps/files_external/l10n/eu.json +++ b/apps/files_external/l10n/eu.json @@ -50,8 +50,8 @@ "Folder name" : "Karpetaren izena", "Configuration" : "Konfigurazioa", "Available for" : "Hauentzat eskuragarri", - "Delete" : "Ezabatu", "Add storage" : "Gehitu biltegiratzea", + "Delete" : "Ezabatu", "Allow users to mount the following external storage" : "Baimendu erabiltzaileak hurrengo kanpo biltegiratzeak muntatzen" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/fa.js b/apps/files_external/l10n/fa.js index a18f7cdcf5c..0c1076fc994 100644 --- a/apps/files_external/l10n/fa.js +++ b/apps/files_external/l10n/fa.js @@ -57,8 +57,8 @@ OC.L10N.register( "Authentication" : "احراز هویت", "Configuration" : "پیکربندی", "Available for" : "در دسترس برای", + "Add storage" : "اضافه کردن حافظه", "Advanced settings" : "تنظیمات پیشرفته", - "Delete" : "حذف", - "Add storage" : "اضافه کردن حافظه" + "Delete" : "حذف" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/fa.json b/apps/files_external/l10n/fa.json index 4bc16f72f54..056bad259a6 100644 --- a/apps/files_external/l10n/fa.json +++ b/apps/files_external/l10n/fa.json @@ -55,8 +55,8 @@ "Authentication" : "احراز هویت", "Configuration" : "پیکربندی", "Available for" : "در دسترس برای", + "Add storage" : "اضافه کردن حافظه", "Advanced settings" : "تنظیمات پیشرفته", - "Delete" : "حذف", - "Add storage" : "اضافه کردن حافظه" + "Delete" : "حذف" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/fi_FI.js b/apps/files_external/l10n/fi_FI.js index f91dec93310..5a2c3a9aa66 100644 --- a/apps/files_external/l10n/fi_FI.js +++ b/apps/files_external/l10n/fi_FI.js @@ -76,9 +76,9 @@ OC.L10N.register( "Authentication" : "Tunnistautuminen", "Configuration" : "Asetukset", "Available for" : "Saatavuus", + "Add storage" : "Lisää tallennustila", "Advanced settings" : "Lisäasetukset", "Delete" : "Poista", - "Add storage" : "Lisää tallennustila", "Allow users to mount external storage" : "Salli käyttäjien liittää erillisiä tallennustiloja", "Allow users to mount the following external storage" : "Salli käyttäjien liittää seuraavat erilliset tallennusvälineet" }, diff --git a/apps/files_external/l10n/fi_FI.json b/apps/files_external/l10n/fi_FI.json index a2033b4c578..d2a8cf16278 100644 --- a/apps/files_external/l10n/fi_FI.json +++ b/apps/files_external/l10n/fi_FI.json @@ -74,9 +74,9 @@ "Authentication" : "Tunnistautuminen", "Configuration" : "Asetukset", "Available for" : "Saatavuus", + "Add storage" : "Lisää tallennustila", "Advanced settings" : "Lisäasetukset", "Delete" : "Poista", - "Add storage" : "Lisää tallennustila", "Allow users to mount external storage" : "Salli käyttäjien liittää erillisiä tallennustiloja", "Allow users to mount the following external storage" : "Salli käyttäjien liittää seuraavat erilliset tallennusvälineet" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js index 1389cd1233b..7126864a9e7 100644 --- a/apps/files_external/l10n/fr.js +++ b/apps/files_external/l10n/fr.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Authentification", "Configuration" : "Configuration", "Available for" : "Disponible pour", + "Add storage" : "Ajouter un support de stockage", "Advanced settings" : "Paramètres avancés", "Delete" : "Supprimer", - "Add storage" : "Ajouter un support de stockage", "Allow users to mount external storage" : "Autoriser les utilisateurs à monter des espaces de stockage externes", "Allow users to mount the following external storage" : "Autoriser les utilisateurs à monter les stockages externes suivants" }, diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json index 9d078bf13fc..f6cdce79c67 100644 --- a/apps/files_external/l10n/fr.json +++ b/apps/files_external/l10n/fr.json @@ -97,9 +97,9 @@ "Authentication" : "Authentification", "Configuration" : "Configuration", "Available for" : "Disponible pour", + "Add storage" : "Ajouter un support de stockage", "Advanced settings" : "Paramètres avancés", "Delete" : "Supprimer", - "Add storage" : "Ajouter un support de stockage", "Allow users to mount external storage" : "Autoriser les utilisateurs à monter des espaces de stockage externes", "Allow users to mount the following external storage" : "Autoriser les utilisateurs à monter les stockages externes suivants" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files_external/l10n/gl.js b/apps/files_external/l10n/gl.js index 5827a6ab57f..044b7c34df1 100644 --- a/apps/files_external/l10n/gl.js +++ b/apps/files_external/l10n/gl.js @@ -67,9 +67,9 @@ OC.L10N.register( "Folder name" : "Nome do cartafol", "Configuration" : "Configuración", "Available for" : "Dispoñíbel para", + "Add storage" : "Engadir almacenamento", "Advanced settings" : "Axustes avanzados", "Delete" : "Eliminar", - "Add storage" : "Engadir almacenamento", "Allow users to mount the following external storage" : "Permitirlle aos usuarios montar o seguinte almacenamento externo" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/gl.json b/apps/files_external/l10n/gl.json index 29dde23c329..143b9a53b43 100644 --- a/apps/files_external/l10n/gl.json +++ b/apps/files_external/l10n/gl.json @@ -65,9 +65,9 @@ "Folder name" : "Nome do cartafol", "Configuration" : "Configuración", "Available for" : "Dispoñíbel para", + "Add storage" : "Engadir almacenamento", "Advanced settings" : "Axustes avanzados", "Delete" : "Eliminar", - "Add storage" : "Engadir almacenamento", "Allow users to mount the following external storage" : "Permitirlle aos usuarios montar o seguinte almacenamento externo" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/hr.js b/apps/files_external/l10n/hr.js index 4ab71f76ffb..8c632eba518 100644 --- a/apps/files_external/l10n/hr.js +++ b/apps/files_external/l10n/hr.js @@ -50,8 +50,8 @@ OC.L10N.register( "Folder name" : "Naziv mape", "Configuration" : "Konfiguracija", "Available for" : "Dostupno za", - "Delete" : "Izbrišite", "Add storage" : "Dodajte spremište", + "Delete" : "Izbrišite", "Allow users to mount the following external storage" : "Dopustite korisnicima postavljanje sljedećeg vanjskog spremišta" }, "nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;"); diff --git a/apps/files_external/l10n/hr.json b/apps/files_external/l10n/hr.json index e8b8dcd3f61..610a42d46e2 100644 --- a/apps/files_external/l10n/hr.json +++ b/apps/files_external/l10n/hr.json @@ -48,8 +48,8 @@ "Folder name" : "Naziv mape", "Configuration" : "Konfiguracija", "Available for" : "Dostupno za", - "Delete" : "Izbrišite", "Add storage" : "Dodajte spremište", + "Delete" : "Izbrišite", "Allow users to mount the following external storage" : "Dopustite korisnicima postavljanje sljedećeg vanjskog spremišta" },"pluralForm" :"nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/hu_HU.js b/apps/files_external/l10n/hu_HU.js index 0a307f8a609..964c28285fd 100644 --- a/apps/files_external/l10n/hu_HU.js +++ b/apps/files_external/l10n/hu_HU.js @@ -39,7 +39,7 @@ OC.L10N.register( "Folder name" : "Mappanév", "Configuration" : "Beállítások", "Available for" : "Elérhető számukra", - "Delete" : "Törlés", - "Add storage" : "Tároló becsatolása" + "Add storage" : "Tároló becsatolása", + "Delete" : "Törlés" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/hu_HU.json b/apps/files_external/l10n/hu_HU.json index 4012c0eb490..7942028d038 100644 --- a/apps/files_external/l10n/hu_HU.json +++ b/apps/files_external/l10n/hu_HU.json @@ -37,7 +37,7 @@ "Folder name" : "Mappanév", "Configuration" : "Beállítások", "Available for" : "Elérhető számukra", - "Delete" : "Törlés", - "Add storage" : "Tároló becsatolása" + "Add storage" : "Tároló becsatolása", + "Delete" : "Törlés" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/id.js b/apps/files_external/l10n/id.js index b01e13e6085..1029ff8e3c7 100644 --- a/apps/files_external/l10n/id.js +++ b/apps/files_external/l10n/id.js @@ -98,9 +98,9 @@ OC.L10N.register( "Authentication" : "Otentikasi", "Configuration" : "Konfigurasi", "Available for" : "Tersedia untuk", + "Add storage" : "Tambahkan penyimpanan", "Advanced settings" : "Pengaturan Lanjutan", "Delete" : "Hapus", - "Add storage" : "Tambahkan penyimpanan", "Allow users to mount the following external storage" : "Izinkan pengguna untuk mengaitkan penyimpanan eksternal berikut" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/id.json b/apps/files_external/l10n/id.json index 383850199ac..acaf37381bc 100644 --- a/apps/files_external/l10n/id.json +++ b/apps/files_external/l10n/id.json @@ -96,9 +96,9 @@ "Authentication" : "Otentikasi", "Configuration" : "Konfigurasi", "Available for" : "Tersedia untuk", + "Add storage" : "Tambahkan penyimpanan", "Advanced settings" : "Pengaturan Lanjutan", "Delete" : "Hapus", - "Add storage" : "Tambahkan penyimpanan", "Allow users to mount the following external storage" : "Izinkan pengguna untuk mengaitkan penyimpanan eksternal berikut" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js index 8e126abe93b..babb189e54b 100644 --- a/apps/files_external/l10n/it.js +++ b/apps/files_external/l10n/it.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Autenticazione", "Configuration" : "Configurazione", "Available for" : "Disponibile per", + "Add storage" : "Aggiungi archiviazione", "Advanced settings" : "Impostazioni avanzate", "Delete" : "Elimina", - "Add storage" : "Aggiungi archiviazione", "Allow users to mount external storage" : "Consenti agli utenti di montare archiviazioni esterne", "Allow users to mount the following external storage" : "Consenti agli utenti di montare la seguente archiviazione esterna" }, diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json index e21cf5973d2..6935c4d53d3 100644 --- a/apps/files_external/l10n/it.json +++ b/apps/files_external/l10n/it.json @@ -97,9 +97,9 @@ "Authentication" : "Autenticazione", "Configuration" : "Configurazione", "Available for" : "Disponibile per", + "Add storage" : "Aggiungi archiviazione", "Advanced settings" : "Impostazioni avanzate", "Delete" : "Elimina", - "Add storage" : "Aggiungi archiviazione", "Allow users to mount external storage" : "Consenti agli utenti di montare archiviazioni esterne", "Allow users to mount the following external storage" : "Consenti agli utenti di montare la seguente archiviazione esterna" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/ja.js b/apps/files_external/l10n/ja.js index 4481c7fdd2e..3dcae3f8afc 100644 --- a/apps/files_external/l10n/ja.js +++ b/apps/files_external/l10n/ja.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "認証", "Configuration" : "設定", "Available for" : "利用可能", + "Add storage" : "ストレージを追加", "Advanced settings" : "詳細設定", "Delete" : "削除", - "Add storage" : "ストレージを追加", "Allow users to mount external storage" : "ユーザーに外部ストレージの接続を許可する", "Allow users to mount the following external storage" : "ユーザーに以下の外部ストレージのマウントを許可する" }, diff --git a/apps/files_external/l10n/ja.json b/apps/files_external/l10n/ja.json index 5573c11fe84..4aaaac43a5a 100644 --- a/apps/files_external/l10n/ja.json +++ b/apps/files_external/l10n/ja.json @@ -97,9 +97,9 @@ "Authentication" : "認証", "Configuration" : "設定", "Available for" : "利用可能", + "Add storage" : "ストレージを追加", "Advanced settings" : "詳細設定", "Delete" : "削除", - "Add storage" : "ストレージを追加", "Allow users to mount external storage" : "ユーザーに外部ストレージの接続を許可する", "Allow users to mount the following external storage" : "ユーザーに以下の外部ストレージのマウントを許可する" },"pluralForm" :"nplurals=1; plural=0;" diff --git a/apps/files_external/l10n/ka_GE.js b/apps/files_external/l10n/ka_GE.js index 824295cb93d..e82c778862e 100644 --- a/apps/files_external/l10n/ka_GE.js +++ b/apps/files_external/l10n/ka_GE.js @@ -21,7 +21,7 @@ OC.L10N.register( "External Storage" : "ექსტერნალ საცავი", "Folder name" : "ფოლდერის სახელი", "Configuration" : "კონფიგურაცია", - "Delete" : "წაშლა", - "Add storage" : "საცავის დამატება" + "Add storage" : "საცავის დამატება", + "Delete" : "წაშლა" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/ka_GE.json b/apps/files_external/l10n/ka_GE.json index 73ad2cfd0c5..a706d42225b 100644 --- a/apps/files_external/l10n/ka_GE.json +++ b/apps/files_external/l10n/ka_GE.json @@ -19,7 +19,7 @@ "External Storage" : "ექსტერნალ საცავი", "Folder name" : "ფოლდერის სახელი", "Configuration" : "კონფიგურაცია", - "Delete" : "წაშლა", - "Add storage" : "საცავის დამატება" + "Add storage" : "საცავის დამატება", + "Delete" : "წაშლა" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/km.js b/apps/files_external/l10n/km.js index 7a5c6cb86c8..e5aad9697ae 100644 --- a/apps/files_external/l10n/km.js +++ b/apps/files_external/l10n/km.js @@ -19,7 +19,7 @@ OC.L10N.register( "External Storage" : "ឃ្លាំងផ្ទុកខាងក្រៅ", "Folder name" : "ឈ្មោះថត", "Configuration" : "ការកំណត់សណ្ឋាន", - "Delete" : "លុប", - "Add storage" : "បន្ថែមឃ្លាំងផ្ទុក" + "Add storage" : "បន្ថែមឃ្លាំងផ្ទុក", + "Delete" : "លុប" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/km.json b/apps/files_external/l10n/km.json index 0375b5bfee2..71213394d53 100644 --- a/apps/files_external/l10n/km.json +++ b/apps/files_external/l10n/km.json @@ -17,7 +17,7 @@ "External Storage" : "ឃ្លាំងផ្ទុកខាងក្រៅ", "Folder name" : "ឈ្មោះថត", "Configuration" : "ការកំណត់សណ្ឋាន", - "Delete" : "លុប", - "Add storage" : "បន្ថែមឃ្លាំងផ្ទុក" + "Add storage" : "បន្ថែមឃ្លាំងផ្ទុក", + "Delete" : "លុប" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/ko.js b/apps/files_external/l10n/ko.js index 9028a3e2986..0eaac8aee4d 100644 --- a/apps/files_external/l10n/ko.js +++ b/apps/files_external/l10n/ko.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "인증", "Configuration" : "설정", "Available for" : "다음으로 사용 가능", + "Add storage" : "저장소 추가", "Advanced settings" : "고급 설정", "Delete" : "삭제", - "Add storage" : "저장소 추가", "Allow users to mount external storage" : "사용자가 외부 저장소를 마운트하도록 허용", "Allow users to mount the following external storage" : "사용자가 다음 외부 저장소를 마운트할 수 있도록 허용" }, diff --git a/apps/files_external/l10n/ko.json b/apps/files_external/l10n/ko.json index d27fdfcae30..7f63ba793e7 100644 --- a/apps/files_external/l10n/ko.json +++ b/apps/files_external/l10n/ko.json @@ -97,9 +97,9 @@ "Authentication" : "인증", "Configuration" : "설정", "Available for" : "다음으로 사용 가능", + "Add storage" : "저장소 추가", "Advanced settings" : "고급 설정", "Delete" : "삭제", - "Add storage" : "저장소 추가", "Allow users to mount external storage" : "사용자가 외부 저장소를 마운트하도록 허용", "Allow users to mount the following external storage" : "사용자가 다음 외부 저장소를 마운트할 수 있도록 허용" },"pluralForm" :"nplurals=1; plural=0;" diff --git a/apps/files_external/l10n/lt_LT.js b/apps/files_external/l10n/lt_LT.js index 78d03a865f2..0efbfa333b8 100644 --- a/apps/files_external/l10n/lt_LT.js +++ b/apps/files_external/l10n/lt_LT.js @@ -23,7 +23,7 @@ OC.L10N.register( "External Storage" : "Išorinės saugyklos", "Folder name" : "Katalogo pavadinimas", "Configuration" : "Konfigūracija", - "Delete" : "Ištrinti", - "Add storage" : "Pridėti saugyklą" + "Add storage" : "Pridėti saugyklą", + "Delete" : "Ištrinti" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/files_external/l10n/lt_LT.json b/apps/files_external/l10n/lt_LT.json index fcb1f1f39bd..13c1543748f 100644 --- a/apps/files_external/l10n/lt_LT.json +++ b/apps/files_external/l10n/lt_LT.json @@ -21,7 +21,7 @@ "External Storage" : "Išorinės saugyklos", "Folder name" : "Katalogo pavadinimas", "Configuration" : "Konfigūracija", - "Delete" : "Ištrinti", - "Add storage" : "Pridėti saugyklą" + "Add storage" : "Pridėti saugyklą", + "Delete" : "Ištrinti" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/lv.js b/apps/files_external/l10n/lv.js index 6590706fa2a..d6733a1d9c0 100644 --- a/apps/files_external/l10n/lv.js +++ b/apps/files_external/l10n/lv.js @@ -21,7 +21,7 @@ OC.L10N.register( "External Storage" : "Ārējā krātuve", "Folder name" : "Mapes nosaukums", "Configuration" : "Konfigurācija", - "Delete" : "Dzēst", - "Add storage" : "Pievienot krātuvi" + "Add storage" : "Pievienot krātuvi", + "Delete" : "Dzēst" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);"); diff --git a/apps/files_external/l10n/lv.json b/apps/files_external/l10n/lv.json index 4e27db77737..57fe7cbc048 100644 --- a/apps/files_external/l10n/lv.json +++ b/apps/files_external/l10n/lv.json @@ -19,7 +19,7 @@ "External Storage" : "Ārējā krātuve", "Folder name" : "Mapes nosaukums", "Configuration" : "Konfigurācija", - "Delete" : "Dzēst", - "Add storage" : "Pievienot krātuvi" + "Add storage" : "Pievienot krātuvi", + "Delete" : "Dzēst" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/nb_NO.js b/apps/files_external/l10n/nb_NO.js index ecafac048c0..0e9e2dd24ce 100644 --- a/apps/files_external/l10n/nb_NO.js +++ b/apps/files_external/l10n/nb_NO.js @@ -98,9 +98,9 @@ OC.L10N.register( "Authentication" : "Autentisering", "Configuration" : "Konfigurasjon", "Available for" : "Tilgjengelig for", + "Add storage" : "Legg til lagringsplass", "Advanced settings" : "Avanserte innstillinger", "Delete" : "Slett", - "Add storage" : "Legg til lagringsplass", "Allow users to mount the following external storage" : "Tillat brukere å koble opp følgende eksterne lagring" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/nb_NO.json b/apps/files_external/l10n/nb_NO.json index 9a7a2ae6287..ddf5221e955 100644 --- a/apps/files_external/l10n/nb_NO.json +++ b/apps/files_external/l10n/nb_NO.json @@ -96,9 +96,9 @@ "Authentication" : "Autentisering", "Configuration" : "Konfigurasjon", "Available for" : "Tilgjengelig for", + "Add storage" : "Legg til lagringsplass", "Advanced settings" : "Avanserte innstillinger", "Delete" : "Slett", - "Add storage" : "Legg til lagringsplass", "Allow users to mount the following external storage" : "Tillat brukere å koble opp følgende eksterne lagring" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/nds.js b/apps/files_external/l10n/nds.js index f7f1da30c60..b9417b4a4d5 100644 --- a/apps/files_external/l10n/nds.js +++ b/apps/files_external/l10n/nds.js @@ -93,9 +93,9 @@ OC.L10N.register( "Authentication" : "Authentifizierung", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount the following external storage" : "Erlaube Benutzern folgenden externen Speicher einzuhängen" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/nds.json b/apps/files_external/l10n/nds.json index bfadfc0c123..dd1ca770ebd 100644 --- a/apps/files_external/l10n/nds.json +++ b/apps/files_external/l10n/nds.json @@ -91,9 +91,9 @@ "Authentication" : "Authentifizierung", "Configuration" : "Konfiguration", "Available for" : "Verfügbar für", + "Add storage" : "Speicher hinzufügen", "Advanced settings" : "Erweiterte Einstellungen", "Delete" : "Löschen", - "Add storage" : "Speicher hinzufügen", "Allow users to mount the following external storage" : "Erlaube Benutzern folgenden externen Speicher einzuhängen" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/nl.js b/apps/files_external/l10n/nl.js index 57481ce4176..cb015b104cb 100644 --- a/apps/files_external/l10n/nl.js +++ b/apps/files_external/l10n/nl.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Authenticatie", "Configuration" : "Configuratie", "Available for" : "Beschikbaar voor", + "Add storage" : "Toevoegen opslag", "Advanced settings" : "Geavanceerde instellingen", "Delete" : "Verwijder", - "Add storage" : "Toevoegen opslag", "Allow users to mount external storage" : "Sta gebruikers toe om een externe opslag aan te koppelen", "Allow users to mount the following external storage" : "Sta gebruikers toe de volgende externe opslag aan te koppelen" }, diff --git a/apps/files_external/l10n/nl.json b/apps/files_external/l10n/nl.json index fc80c3bbb2c..1a80aa31e30 100644 --- a/apps/files_external/l10n/nl.json +++ b/apps/files_external/l10n/nl.json @@ -97,9 +97,9 @@ "Authentication" : "Authenticatie", "Configuration" : "Configuratie", "Available for" : "Beschikbaar voor", + "Add storage" : "Toevoegen opslag", "Advanced settings" : "Geavanceerde instellingen", "Delete" : "Verwijder", - "Add storage" : "Toevoegen opslag", "Allow users to mount external storage" : "Sta gebruikers toe om een externe opslag aan te koppelen", "Allow users to mount the following external storage" : "Sta gebruikers toe de volgende externe opslag aan te koppelen" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/oc.js b/apps/files_external/l10n/oc.js index a45aebf81ba..a212c9a5179 100644 --- a/apps/files_external/l10n/oc.js +++ b/apps/files_external/l10n/oc.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Autentificacion", "Configuration" : "Configuracion", "Available for" : "Disponible per", + "Add storage" : "Apondre un supòrt d'emmagazinatge", "Advanced settings" : "Paramètres avançats", "Delete" : "Suprimir", - "Add storage" : "Apondre un supòrt d'emmagazinatge", "Allow users to mount external storage" : "Autorizar los utilizaires a montar l'espaci d'emmagazinatge extèrne", "Allow users to mount the following external storage" : "Autorizar los utilizaires a montar los emmagazinatges extèrnes seguents" }, diff --git a/apps/files_external/l10n/oc.json b/apps/files_external/l10n/oc.json index c49326a955f..a7d51dd8ad0 100644 --- a/apps/files_external/l10n/oc.json +++ b/apps/files_external/l10n/oc.json @@ -97,9 +97,9 @@ "Authentication" : "Autentificacion", "Configuration" : "Configuracion", "Available for" : "Disponible per", + "Add storage" : "Apondre un supòrt d'emmagazinatge", "Advanced settings" : "Paramètres avançats", "Delete" : "Suprimir", - "Add storage" : "Apondre un supòrt d'emmagazinatge", "Allow users to mount external storage" : "Autorizar los utilizaires a montar l'espaci d'emmagazinatge extèrne", "Allow users to mount the following external storage" : "Autorizar los utilizaires a montar los emmagazinatges extèrnes seguents" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files_external/l10n/pl.js b/apps/files_external/l10n/pl.js index 1f7cc6b1979..99de2703433 100644 --- a/apps/files_external/l10n/pl.js +++ b/apps/files_external/l10n/pl.js @@ -67,9 +67,9 @@ OC.L10N.register( "Folder name" : "Nazwa folderu", "Configuration" : "Konfiguracja", "Available for" : "Dostępne przez", + "Add storage" : "Dodaj zasoby dyskowe", "Advanced settings" : "Ustawienia zaawansowane", "Delete" : "Usuń", - "Add storage" : "Dodaj zasoby dyskowe", "Allow users to mount the following external storage" : "Pozwól użytkownikom montować następujące zewnętrzne zasoby dyskowe" }, "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/files_external/l10n/pl.json b/apps/files_external/l10n/pl.json index 5b5059a5d18..258811f2d81 100644 --- a/apps/files_external/l10n/pl.json +++ b/apps/files_external/l10n/pl.json @@ -65,9 +65,9 @@ "Folder name" : "Nazwa folderu", "Configuration" : "Konfiguracja", "Available for" : "Dostępne przez", + "Add storage" : "Dodaj zasoby dyskowe", "Advanced settings" : "Ustawienia zaawansowane", "Delete" : "Usuń", - "Add storage" : "Dodaj zasoby dyskowe", "Allow users to mount the following external storage" : "Pozwól użytkownikom montować następujące zewnętrzne zasoby dyskowe" },"pluralForm" :"nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index 92bc16355d3..34fe07ae3d4 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Autenticação", "Configuration" : "Configuração", "Available for" : "Disponível para", + "Add storage" : "Adicionar Armazenamento", "Advanced settings" : "Configurações avançadas", "Delete" : "Excluir", - "Add storage" : "Adicionar Armazenamento", "Allow users to mount external storage" : "Permitir que usuários montem armazenamento externo", "Allow users to mount the following external storage" : "Permitir que usuários montem o seguinte armazenamento externo" }, diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index bb6e0a96cff..df028cfc91c 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -97,9 +97,9 @@ "Authentication" : "Autenticação", "Configuration" : "Configuração", "Available for" : "Disponível para", + "Add storage" : "Adicionar Armazenamento", "Advanced settings" : "Configurações avançadas", "Delete" : "Excluir", - "Add storage" : "Adicionar Armazenamento", "Allow users to mount external storage" : "Permitir que usuários montem armazenamento externo", "Allow users to mount the following external storage" : "Permitir que usuários montem o seguinte armazenamento externo" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js index 4e8b3245e66..f6c3254de0b 100644 --- a/apps/files_external/l10n/pt_PT.js +++ b/apps/files_external/l10n/pt_PT.js @@ -92,9 +92,9 @@ OC.L10N.register( "Authentication" : "Autenticação", "Configuration" : "Configuração", "Available for" : "Disponível para ", + "Add storage" : "Adicionar armazenamento", "Advanced settings" : "Definições avançadas", "Delete" : "Apagar", - "Add storage" : "Adicionar armazenamento", "Allow users to mount the following external storage" : "Permitir que os utilizadores montem o seguinte armazenamento externo" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json index 51333429106..ec22394470e 100644 --- a/apps/files_external/l10n/pt_PT.json +++ b/apps/files_external/l10n/pt_PT.json @@ -90,9 +90,9 @@ "Authentication" : "Autenticação", "Configuration" : "Configuração", "Available for" : "Disponível para ", + "Add storage" : "Adicionar armazenamento", "Advanced settings" : "Definições avançadas", "Delete" : "Apagar", - "Add storage" : "Adicionar armazenamento", "Allow users to mount the following external storage" : "Permitir que os utilizadores montem o seguinte armazenamento externo" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/ro.js b/apps/files_external/l10n/ro.js index d8ba040824c..cc3a065a21d 100644 --- a/apps/files_external/l10n/ro.js +++ b/apps/files_external/l10n/ro.js @@ -29,8 +29,8 @@ OC.L10N.register( "External Storage" : "Stocare externă", "Folder name" : "Denumire director", "Configuration" : "Configurație", - "Delete" : "Șterge", "Add storage" : "Adauga stocare", + "Delete" : "Șterge", "Allow users to mount the following external storage" : "Permite utilizatorilor să monteze următoarea unitate de stocare" }, "nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));"); diff --git a/apps/files_external/l10n/ro.json b/apps/files_external/l10n/ro.json index cbe2826def4..2f1f8e32883 100644 --- a/apps/files_external/l10n/ro.json +++ b/apps/files_external/l10n/ro.json @@ -27,8 +27,8 @@ "External Storage" : "Stocare externă", "Folder name" : "Denumire director", "Configuration" : "Configurație", - "Delete" : "Șterge", "Add storage" : "Adauga stocare", + "Delete" : "Șterge", "Allow users to mount the following external storage" : "Permite utilizatorilor să monteze următoarea unitate de stocare" },"pluralForm" :"nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));" }
\ No newline at end of file diff --git a/apps/files_external/l10n/ru.js b/apps/files_external/l10n/ru.js index 5550ea780ab..edcfc6af6c0 100644 --- a/apps/files_external/l10n/ru.js +++ b/apps/files_external/l10n/ru.js @@ -98,9 +98,9 @@ OC.L10N.register( "Authentication" : "Авторизация", "Configuration" : "Конфигурация", "Available for" : "Доступно для", + "Add storage" : "Добавить хранилище", "Advanced settings" : "Расширенные настройки", "Delete" : "Удалить", - "Add storage" : "Добавить хранилище", "Allow users to mount the following external storage" : "Разрешить пользователям монтировать следующие сервисы хранения данных" }, "nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"); diff --git a/apps/files_external/l10n/ru.json b/apps/files_external/l10n/ru.json index 52792a3f09a..b85e04d3667 100644 --- a/apps/files_external/l10n/ru.json +++ b/apps/files_external/l10n/ru.json @@ -96,9 +96,9 @@ "Authentication" : "Авторизация", "Configuration" : "Конфигурация", "Available for" : "Доступно для", + "Add storage" : "Добавить хранилище", "Advanced settings" : "Расширенные настройки", "Delete" : "Удалить", - "Add storage" : "Добавить хранилище", "Allow users to mount the following external storage" : "Разрешить пользователям монтировать следующие сервисы хранения данных" },"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/sk_SK.js b/apps/files_external/l10n/sk_SK.js index 41f9c866196..7485625455c 100644 --- a/apps/files_external/l10n/sk_SK.js +++ b/apps/files_external/l10n/sk_SK.js @@ -97,9 +97,9 @@ OC.L10N.register( "Authentication" : "Autentifikácia", "Configuration" : "Nastavenia", "Available for" : "K dispozícii pre", + "Add storage" : "Pridať úložisko", "Advanced settings" : "Rozšírené nastavenia", "Delete" : "Zmazať", - "Add storage" : "Pridať úložisko", "Allow users to mount the following external storage" : "Povoliť používateľom pripojiť tieto externé úložiská" }, "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;"); diff --git a/apps/files_external/l10n/sk_SK.json b/apps/files_external/l10n/sk_SK.json index 4072136fa53..9ed60285b2d 100644 --- a/apps/files_external/l10n/sk_SK.json +++ b/apps/files_external/l10n/sk_SK.json @@ -95,9 +95,9 @@ "Authentication" : "Autentifikácia", "Configuration" : "Nastavenia", "Available for" : "K dispozícii pre", + "Add storage" : "Pridať úložisko", "Advanced settings" : "Rozšírené nastavenia", "Delete" : "Zmazať", - "Add storage" : "Pridať úložisko", "Allow users to mount the following external storage" : "Povoliť používateľom pripojiť tieto externé úložiská" },"pluralForm" :"nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/sl.js b/apps/files_external/l10n/sl.js index f9513a5f11c..846f1d0095a 100644 --- a/apps/files_external/l10n/sl.js +++ b/apps/files_external/l10n/sl.js @@ -67,9 +67,9 @@ OC.L10N.register( "Folder name" : "Ime mape", "Configuration" : "Nastavitve", "Available for" : "Na voljo za", + "Add storage" : "Dodaj shrambo", "Advanced settings" : "Napredne nastavitve", "Delete" : "Izbriši", - "Add storage" : "Dodaj shrambo", "Allow users to mount the following external storage" : "Dovoli uporabnikom priklapljanje navedenih zunanjih shramb." }, "nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);"); diff --git a/apps/files_external/l10n/sl.json b/apps/files_external/l10n/sl.json index ca97f27b234..aa7145180e4 100644 --- a/apps/files_external/l10n/sl.json +++ b/apps/files_external/l10n/sl.json @@ -65,9 +65,9 @@ "Folder name" : "Ime mape", "Configuration" : "Nastavitve", "Available for" : "Na voljo za", + "Add storage" : "Dodaj shrambo", "Advanced settings" : "Napredne nastavitve", "Delete" : "Izbriši", - "Add storage" : "Dodaj shrambo", "Allow users to mount the following external storage" : "Dovoli uporabnikom priklapljanje navedenih zunanjih shramb." },"pluralForm" :"nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/sq.js b/apps/files_external/l10n/sq.js index f8e5c5a27dc..2085b48b411 100644 --- a/apps/files_external/l10n/sq.js +++ b/apps/files_external/l10n/sq.js @@ -97,9 +97,9 @@ OC.L10N.register( "Authentication" : "Mirëfilltësim", "Configuration" : "Formësim", "Available for" : "E gatshme për", + "Add storage" : "Shtoni depozitë", "Advanced settings" : "Rregullime të mëtejshme", "Delete" : "Fshije", - "Add storage" : "Shtoni depozitë", "Allow users to mount external storage" : "Lejoju përdoruesve të montojnë depozita të jashtme", "Allow users to mount the following external storage" : "Lejoju përdoruesve të montojnë depozitën e jashtme vijuese" }, diff --git a/apps/files_external/l10n/sq.json b/apps/files_external/l10n/sq.json index 0da8e9406bb..165607b0444 100644 --- a/apps/files_external/l10n/sq.json +++ b/apps/files_external/l10n/sq.json @@ -95,9 +95,9 @@ "Authentication" : "Mirëfilltësim", "Configuration" : "Formësim", "Available for" : "E gatshme për", + "Add storage" : "Shtoni depozitë", "Advanced settings" : "Rregullime të mëtejshme", "Delete" : "Fshije", - "Add storage" : "Shtoni depozitë", "Allow users to mount external storage" : "Lejoju përdoruesve të montojnë depozita të jashtme", "Allow users to mount the following external storage" : "Lejoju përdoruesve të montojnë depozitën e jashtme vijuese" },"pluralForm" :"nplurals=2; plural=(n != 1);" diff --git a/apps/files_external/l10n/sr.js b/apps/files_external/l10n/sr.js index 02596d8ab22..add0cc4dab7 100644 --- a/apps/files_external/l10n/sr.js +++ b/apps/files_external/l10n/sr.js @@ -66,9 +66,9 @@ OC.L10N.register( "Folder name" : "Назив фасцикле", "Configuration" : "Подешавање", "Available for" : "Доступно за", + "Add storage" : "Додај складиште", "Advanced settings" : "Напредне поставке", "Delete" : "Обриши", - "Add storage" : "Додај складиште", "Allow users to mount the following external storage" : "Дозволи корисницима да монтирају следећа спољашња складишта" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/files_external/l10n/sr.json b/apps/files_external/l10n/sr.json index f1c12eeb0f7..3e7ac487280 100644 --- a/apps/files_external/l10n/sr.json +++ b/apps/files_external/l10n/sr.json @@ -64,9 +64,9 @@ "Folder name" : "Назив фасцикле", "Configuration" : "Подешавање", "Available for" : "Доступно за", + "Add storage" : "Додај складиште", "Advanced settings" : "Напредне поставке", "Delete" : "Обриши", - "Add storage" : "Додај складиште", "Allow users to mount the following external storage" : "Дозволи корисницима да монтирају следећа спољашња складишта" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/sr@latin.js b/apps/files_external/l10n/sr@latin.js index 880a33c1614..0420189571c 100644 --- a/apps/files_external/l10n/sr@latin.js +++ b/apps/files_external/l10n/sr@latin.js @@ -48,8 +48,8 @@ OC.L10N.register( "Folder name" : "Ime fascikle", "Configuration" : "Podešavanje", "Available for" : "Dostupno za", - "Delete" : "Obriši", "Add storage" : "Dodaj skladište", + "Delete" : "Obriši", "Allow users to mount the following external storage" : "Omogući korisnicima da namontiraju sledeće spoljašnje skladište" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/files_external/l10n/sr@latin.json b/apps/files_external/l10n/sr@latin.json index 760100867eb..3320ab2c863 100644 --- a/apps/files_external/l10n/sr@latin.json +++ b/apps/files_external/l10n/sr@latin.json @@ -46,8 +46,8 @@ "Folder name" : "Ime fascikle", "Configuration" : "Podešavanje", "Available for" : "Dostupno za", - "Delete" : "Obriši", "Add storage" : "Dodaj skladište", + "Delete" : "Obriši", "Allow users to mount the following external storage" : "Omogući korisnicima da namontiraju sledeće spoljašnje skladište" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/sv.js b/apps/files_external/l10n/sv.js index 09786533e87..f20b96caed8 100644 --- a/apps/files_external/l10n/sv.js +++ b/apps/files_external/l10n/sv.js @@ -53,8 +53,8 @@ OC.L10N.register( "Folder name" : "Mappnamn", "Configuration" : "Konfiguration", "Available for" : "Tillgänglig för", - "Delete" : "Radera", "Add storage" : "Lägg till lagring", + "Delete" : "Radera", "Allow users to mount the following external storage" : "Tillåt användare att montera följande extern lagring" }, "nplurals=2; plural=(n != 1);"); diff --git a/apps/files_external/l10n/sv.json b/apps/files_external/l10n/sv.json index ddb7439cb8d..cd3dc13296d 100644 --- a/apps/files_external/l10n/sv.json +++ b/apps/files_external/l10n/sv.json @@ -51,8 +51,8 @@ "Folder name" : "Mappnamn", "Configuration" : "Konfiguration", "Available for" : "Tillgänglig för", - "Delete" : "Radera", "Add storage" : "Lägg till lagring", + "Delete" : "Radera", "Allow users to mount the following external storage" : "Tillåt användare att montera följande extern lagring" },"pluralForm" :"nplurals=2; plural=(n != 1);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/th_TH.js b/apps/files_external/l10n/th_TH.js index 55611a5531d..022fb9b55e3 100644 --- a/apps/files_external/l10n/th_TH.js +++ b/apps/files_external/l10n/th_TH.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "รับรองความถูกต้อง", "Configuration" : "การกำหนดค่า", "Available for" : "สามารถใช้ได้สำหรับ", + "Add storage" : "เพิ่มพื้นที่จัดเก็บข้อมูล", "Advanced settings" : "ตั้งค่าขั้นสูง", "Delete" : "ลบ", - "Add storage" : "เพิ่มพื้นที่จัดเก็บข้อมูล", "Allow users to mount external storage" : "อนุญาตให้ผู้ใช้ติดตั้งการจัดเก็บข้อมูลภายนอก", "Allow users to mount the following external storage" : "อนุญาตให้ผู้ใช้ติดตั้งจัดเก็บข้อมูลภายนอกต่อไปนี้" }, diff --git a/apps/files_external/l10n/th_TH.json b/apps/files_external/l10n/th_TH.json index 2db63df9df2..5b3713f0ed1 100644 --- a/apps/files_external/l10n/th_TH.json +++ b/apps/files_external/l10n/th_TH.json @@ -97,9 +97,9 @@ "Authentication" : "รับรองความถูกต้อง", "Configuration" : "การกำหนดค่า", "Available for" : "สามารถใช้ได้สำหรับ", + "Add storage" : "เพิ่มพื้นที่จัดเก็บข้อมูล", "Advanced settings" : "ตั้งค่าขั้นสูง", "Delete" : "ลบ", - "Add storage" : "เพิ่มพื้นที่จัดเก็บข้อมูล", "Allow users to mount external storage" : "อนุญาตให้ผู้ใช้ติดตั้งการจัดเก็บข้อมูลภายนอก", "Allow users to mount the following external storage" : "อนุญาตให้ผู้ใช้ติดตั้งจัดเก็บข้อมูลภายนอกต่อไปนี้" },"pluralForm" :"nplurals=1; plural=0;" diff --git a/apps/files_external/l10n/tr.js b/apps/files_external/l10n/tr.js index 9d46012454b..78f9df04ec6 100644 --- a/apps/files_external/l10n/tr.js +++ b/apps/files_external/l10n/tr.js @@ -99,9 +99,9 @@ OC.L10N.register( "Authentication" : "Kimlik Doğrulama", "Configuration" : "Yapılandırma", "Available for" : "Kullanabilenler", + "Add storage" : "Depo ekle", "Advanced settings" : "Gelişmiş ayarlar", "Delete" : "Sil", - "Add storage" : "Depo ekle", "Allow users to mount external storage" : "Kullanıcılara harici depolama bağlama izin ver", "Allow users to mount the following external storage" : "Kullanıcıların aşağıdaki harici depolamayı bağlamalarına izin ver" }, diff --git a/apps/files_external/l10n/tr.json b/apps/files_external/l10n/tr.json index 17c76b1916e..a798f4a7427 100644 --- a/apps/files_external/l10n/tr.json +++ b/apps/files_external/l10n/tr.json @@ -97,9 +97,9 @@ "Authentication" : "Kimlik Doğrulama", "Configuration" : "Yapılandırma", "Available for" : "Kullanabilenler", + "Add storage" : "Depo ekle", "Advanced settings" : "Gelişmiş ayarlar", "Delete" : "Sil", - "Add storage" : "Depo ekle", "Allow users to mount external storage" : "Kullanıcılara harici depolama bağlama izin ver", "Allow users to mount the following external storage" : "Kullanıcıların aşağıdaki harici depolamayı bağlamalarına izin ver" },"pluralForm" :"nplurals=2; plural=(n > 1);" diff --git a/apps/files_external/l10n/uk.js b/apps/files_external/l10n/uk.js index f8ca8dc360c..702b3e328ce 100644 --- a/apps/files_external/l10n/uk.js +++ b/apps/files_external/l10n/uk.js @@ -61,9 +61,9 @@ OC.L10N.register( "Folder name" : "Ім'я теки", "Configuration" : "Налаштування", "Available for" : "Доступний для", + "Add storage" : "Додати сховище", "Advanced settings" : "Розширені налаштування", "Delete" : "Видалити", - "Add storage" : "Додати сховище", "Allow users to mount the following external storage" : "Дозволити користувачам монтувати наступні зовнішні сховища" }, "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); diff --git a/apps/files_external/l10n/uk.json b/apps/files_external/l10n/uk.json index c34d0d2c82f..cddda62118f 100644 --- a/apps/files_external/l10n/uk.json +++ b/apps/files_external/l10n/uk.json @@ -59,9 +59,9 @@ "Folder name" : "Ім'я теки", "Configuration" : "Налаштування", "Available for" : "Доступний для", + "Add storage" : "Додати сховище", "Advanced settings" : "Розширені налаштування", "Delete" : "Видалити", - "Add storage" : "Додати сховище", "Allow users to mount the following external storage" : "Дозволити користувачам монтувати наступні зовнішні сховища" },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }
\ No newline at end of file diff --git a/apps/files_external/l10n/vi.js b/apps/files_external/l10n/vi.js index f3fdb39136c..65b3d492429 100644 --- a/apps/files_external/l10n/vi.js +++ b/apps/files_external/l10n/vi.js @@ -21,7 +21,7 @@ OC.L10N.register( "External Storage" : "Lưu trữ ngoài", "Folder name" : "Tên thư mục", "Configuration" : "Cấu hình", - "Delete" : "Xóa", - "Add storage" : "Thêm bộ nhớ" + "Add storage" : "Thêm bộ nhớ", + "Delete" : "Xóa" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/vi.json b/apps/files_external/l10n/vi.json index fdba39fc95e..031dddee8e3 100644 --- a/apps/files_external/l10n/vi.json +++ b/apps/files_external/l10n/vi.json @@ -19,7 +19,7 @@ "External Storage" : "Lưu trữ ngoài", "Folder name" : "Tên thư mục", "Configuration" : "Cấu hình", - "Delete" : "Xóa", - "Add storage" : "Thêm bộ nhớ" + "Add storage" : "Thêm bộ nhớ", + "Delete" : "Xóa" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/zh_CN.js b/apps/files_external/l10n/zh_CN.js index 4b99fc170a2..ca35a97bb53 100644 --- a/apps/files_external/l10n/zh_CN.js +++ b/apps/files_external/l10n/zh_CN.js @@ -40,8 +40,8 @@ OC.L10N.register( "Folder name" : "目录名称", "Configuration" : "配置", "Available for" : "可用于", - "Delete" : "删除", "Add storage" : "增加存储", + "Delete" : "删除", "Allow users to mount the following external storage" : "允许用户挂载以下外部存储" }, "nplurals=1; plural=0;"); diff --git a/apps/files_external/l10n/zh_CN.json b/apps/files_external/l10n/zh_CN.json index fddc688c5c2..53c1df78899 100644 --- a/apps/files_external/l10n/zh_CN.json +++ b/apps/files_external/l10n/zh_CN.json @@ -38,8 +38,8 @@ "Folder name" : "目录名称", "Configuration" : "配置", "Available for" : "可用于", - "Delete" : "删除", "Add storage" : "增加存储", + "Delete" : "删除", "Allow users to mount the following external storage" : "允许用户挂载以下外部存储" },"pluralForm" :"nplurals=1; plural=0;" }
\ No newline at end of file diff --git a/apps/files_external/l10n/zh_TW.js b/apps/files_external/l10n/zh_TW.js index 61f224e4ffe..56182dc68c2 100644 --- a/apps/files_external/l10n/zh_TW.js +++ b/apps/files_external/l10n/zh_TW.js @@ -93,9 +93,9 @@ OC.L10N.register( "Authentication" : "驗證", "Configuration" : "設定", "Available for" : "可用的", + "Add storage" : "增加儲存區", "Advanced settings" : "進階設定", "Delete" : "刪除", - "Add storage" : "增加儲存區", "Allow users to mount external storage" : "允許使用者能自行掛載外部儲存", "Allow users to mount the following external storage" : "允許使用者自行掛載以下的外部儲存" }, diff --git a/apps/files_external/l10n/zh_TW.json b/apps/files_external/l10n/zh_TW.json index b9aa234e89e..629544bdaaf 100644 --- a/apps/files_external/l10n/zh_TW.json +++ b/apps/files_external/l10n/zh_TW.json @@ -91,9 +91,9 @@ "Authentication" : "驗證", "Configuration" : "設定", "Available for" : "可用的", + "Add storage" : "增加儲存區", "Advanced settings" : "進階設定", "Delete" : "刪除", - "Add storage" : "增加儲存區", "Allow users to mount external storage" : "允許使用者能自行掛載外部儲存", "Allow users to mount the following external storage" : "允許使用者自行掛載以下的外部儲存" },"pluralForm" :"nplurals=1; plural=0;" diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index df15c3bd258..05196a58905 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -32,31 +32,11 @@ $appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService'); -OCP\Util::addScript('files_external', 'settings'); -OCP\Util::addStyle('files_external', 'settings'); - -$backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); -$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); -foreach ($backends as $backend) { - if ($backend->getCustomJs()) { - \OCP\Util::addScript('files_external', $backend->getCustomJs()); - } -} -foreach ($authMechanisms as $authMechanism) { - if ($authMechanism->getCustomJs()) { - \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); - } -} - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); -$tmpl->assign('isAdminPage', false); +$tmpl->assign('visibilityType', BackendService::VISIBILITY_PERSONAL); $tmpl->assign('storages', $userStoragesService->getStorages()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); -$tmpl->assign('backends', $backends); -$tmpl->assign('authMechanisms', $authMechanisms); +$tmpl->assign('backends', $backendService->getAvailableBackends()); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); return $tmpl->fetchPage(); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 03ed363bdb2..50d47d667fd 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -35,40 +35,15 @@ $appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService'); -OCP\Util::addScript('files_external', 'settings'); -OCP\Util::addStyle('files_external', 'settings'); - \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); -$backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isVisibleFor(BackendService::VISIBILITY_ADMIN); -}); -$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isVisibleFor(BackendService::VISIBILITY_ADMIN); -}); -foreach ($backends as $backend) { - if ($backend->getCustomJs()) { - \OCP\Util::addScript('files_external', $backend->getCustomJs()); - } -} -foreach ($authMechanisms as $authMechanism) { - if ($authMechanism->getCustomJs()) { - \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); - } -} - -$userBackends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); -$tmpl->assign('isAdminPage', true); +$tmpl->assign('visibilityType', BackendService::VISIBILITY_ADMIN); $tmpl->assign('storages', $globalStoragesService->getStorages()); -$tmpl->assign('backends', $backends); -$tmpl->assign('authMechanisms', $authMechanisms); -$tmpl->assign('userBackends', $userBackends); +$tmpl->assign('backends', $backendService->getAvailableBackends()); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); return $tmpl->fetchPage(); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index cebf6cc4de0..f7caf3d2caa 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -3,6 +3,21 @@ use \OCA\Files_External\Lib\DefinitionParameter; use \OCA\Files_External\Service\BackendService; + script('files_external', 'settings'); + style('files_external', 'settings'); + + // load custom JS + foreach ($_['backends'] as $backend) { + if ($backend->getCustomJs()) { + script('files_external', $backend->getCustomJs()); + } + } + foreach ($_['authMechanisms'] as $authMechanism) { + if ($authMechanism->getCustomJs()) { + script('files_external', $authMechanism->getCustomJs()); + } + } + function writeParameterInput($parameter, $options, $classes = []) { $value = ''; if (isset($options[$parameter->getName()])) { @@ -56,7 +71,7 @@ <form id="files_external" class="section" data-encryption-enabled="<?php echo $_['encryptionEnabled']?'true': 'false'; ?>"> <h2><?php p($l->t('External Storage')); ?></h2> <?php if (isset($_['dependencies']) and ($_['dependencies']<>'')) print_unescaped(''.$_['dependencies'].''); ?> - <table id="externalStorage" class="grid" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'> + <table id="externalStorage" class="grid" data-admin='<?php print_unescaped(json_encode($_['visibilityType'] === BackendService::VISIBILITY_ADMIN)); ?>'> <thead> <tr> <th></th> @@ -64,79 +79,12 @@ <th><?php p($l->t('External storage')); ?></th> <th><?php p($l->t('Authentication')); ?></th> <th><?php p($l->t('Configuration')); ?></th> - <?php if ($_['isAdminPage']) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?> <th> </th> <th> </th> </tr> </thead> <tbody> - <?php foreach ($_['storages'] as $storage): ?> - <tr class="<?php p($storage->getBackend()->getIdentifier()); ?>" data-id="<?php p($storage->getId()); ?>"> - <td class="status"> - <span></span> - </td> - <td class="mountPoint"><input type="text" name="mountPoint" - value="<?php p(ltrim($storage->getMountPoint(), '/')); ?>" - data-mountpoint="<?php p(ltrim($storage->getMountPoint(), '/')); ?>" - placeholder="<?php p($l->t('Folder name')); ?>" /> - </td> - <td class="backend" data-class="<?php p($storage->getBackend()->getIdentifier()); ?>"><?php p($storage->getBackend()->getText()); ?> - </td> - <td class="authentication"> - <select class="selectAuthMechanism"> - <?php - $authSchemes = $storage->getBackend()->getAuthSchemes(); - $authMechanisms = array_filter($_['authMechanisms'], function($mech) use ($authSchemes) { - return isset($authSchemes[$mech->getScheme()]); - }); - ?> - <?php foreach ($authMechanisms as $mech): ?> - <option value="<?php p($mech->getIdentifier()); ?>" data-scheme="<?php p($mech->getScheme());?>" - <?php if ($mech->getIdentifier() === $storage->getAuthMechanism()->getIdentifier()): ?>selected<?php endif; ?> - ><?php p($mech->getText()); ?></option> - <?php endforeach; ?> - </select> - </td> - <td class="configuration"> - <?php - $options = $storage->getBackendOptions(); - foreach ($storage->getBackend()->getParameters() as $parameter) { - writeParameterInput($parameter, $options); - } - foreach ($storage->getAuthMechanism()->getParameters() as $parameter) { - writeParameterInput($parameter, $options, ['auth-param']); - } - ?> - </td> - <?php if ($_['isAdminPage']): ?> - <td class="applicable" - align="right" - data-applicable-groups='<?php print_unescaped(json_encode($storage->getApplicableGroups())); ?>' - data-applicable-users='<?php print_unescaped(json_encode($storage->getApplicableUsers())); ?>'> - <input type="hidden" class="applicableUsers" style="width:20em;" value=""/> - </td> - <?php endif; ?> - <td class="mountOptionsToggle"> - <img - class="svg action" - title="<?php p($l->t('Advanced settings')); ?>" - alt="<?php p($l->t('Advanced settings')); ?>" - src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>" - /> - <input type="hidden" class="mountOptions" value="<?php p(json_encode($storage->getMountOptions())); ?>" /> - <?php if ($_['isAdminPage']): ?> - <input type="hidden" class="priority" value="<?php p($storage->getPriority()); ?>" /> - <?php endif; ?> - </td> - <td class="remove"> - <img alt="<?php p($l->t('Delete')); ?>" - title="<?php p($l->t('Delete')); ?>" - class="svg action" - src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" - /> - </td> - </tr> - <?php endforeach; ?> <tr id="addMountPoint"> <td class="status"> <span></span> @@ -151,7 +99,9 @@ <?php p($l->t('Add storage')); ?> </option> <?php - $sortedBackends = $_['backends']; + $sortedBackends = array_filter($_['backends'], function($backend) use ($_) { + return $backend->isVisibleFor($_['visibilityType']); + }); uasort($sortedBackends, function($a, $b) { return strcasecmp($a->getText(), $b->getText()); }); @@ -164,7 +114,7 @@ </td> <td class="authentication" data-mechanisms='<?php p(json_encode($_['authMechanisms'])); ?>'></td> <td class="configuration"></td> - <?php if ($_['isAdminPage']): ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?> <td class="applicable" align="right"> <input type="hidden" class="applicableUsers" style="width:20em;" value="" /> </td> @@ -189,7 +139,7 @@ </table> <br /> - <?php if ($_['isAdminPage']): ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?> <br /> <input type="checkbox" name="allowUserMounting" id="allowUserMounting" class="checkbox" value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> /> @@ -197,7 +147,12 @@ <p id="userMountingBackends"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>> <?php p($l->t('Allow users to mount the following external storage')); ?><br /> - <?php $i = 0; foreach ($_['userBackends'] as $backend): ?> + <?php + $userBackends = array_filter($_['backends'], function($backend) { + return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL); + }); + ?> + <?php $i = 0; foreach ($userBackends as $backend): ?> <?php if ($deprecateTo = $backend->getDeprecateTo()): ?> <input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" /> <?php else: ?> diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 67a81277124..3d0168898ca 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -54,7 +54,8 @@ describe('OCA.External.Settings tests', function() { // within the DOM by the server template $('#externalStorage .selectBackend:first').data('configurations', { '\\OC\\TestBackend': { - 'backend': 'Test Backend Name', + 'identifier': '\\OC\\TestBackend', + 'name': 'Test Backend', 'configuration': { 'field1': 'Display Name 1', 'field2': '&Display Name 2' @@ -65,7 +66,8 @@ describe('OCA.External.Settings tests', function() { 'priority': 11 }, '\\OC\\AnotherTestBackend': { - 'backend': 'Another Test Backend Name', + 'identifier': '\\OC\\AnotherTestBackend', + 'name': 'Another Test Backend', 'configuration': { 'field1': 'Display Name 1', 'field2': '&Display Name 2' @@ -80,6 +82,7 @@ describe('OCA.External.Settings tests', function() { $('#externalStorage #addMountPoint .authentication:first').data('mechanisms', { 'mechanism1': { + 'identifier': 'mechanism1', 'name': 'Mechanism 1', 'configuration': { }, diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 03e448be0af..5f56340d254 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -42,7 +42,6 @@ $l = \OC::$server->getL10N('files_sharing'); $application = new Application(); $application->registerMountProviders(); -$application->setupPropagation(); \OCP\App::registerAdmin('files_sharing', 'settings-admin'); \OCP\App::registerPersonal('files_sharing', 'settings-personal'); diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php index 545a9425083..ffe3a6a513f 100644 --- a/apps/files_sharing/appinfo/application.php +++ b/apps/files_sharing/appinfo/application.php @@ -27,8 +27,6 @@ namespace OCA\Files_Sharing\AppInfo; use OCA\Files_Sharing\Helper; use OCA\Files_Sharing\MountProvider; -use OCA\Files_Sharing\Propagation\PropagationManager; -use OCA\Files_Sharing\Propagation\GroupPropagationManager; use OCP\AppFramework\App; use OC\AppFramework\Utility\SimpleContainer; use OCA\Files_Sharing\Controllers\ExternalSharesController; @@ -116,8 +114,7 @@ class Application extends App { /** @var \OCP\IServerContainer $server */ $server = $c->query('ServerContainer'); return new MountProvider( - $server->getConfig(), - $c->query('PropagationManager') + $server->getConfig() ); }); @@ -132,25 +129,6 @@ class Application extends App { ); }); - $container->registerService('PropagationManager', function (IContainer $c) { - /** @var \OCP\IServerContainer $server */ - $server = $c->query('ServerContainer'); - return new PropagationManager( - $server->getUserSession(), - $server->getConfig() - ); - }); - - $container->registerService('GroupPropagationManager', function (IContainer $c) { - /** @var \OCP\IServerContainer $server */ - $server = $c->query('ServerContainer'); - return new GroupPropagationManager( - $server->getUserSession(), - $server->getGroupManager(), - $c->query('PropagationManager') - ); - }); - /* * Register capabilities */ @@ -164,11 +142,4 @@ class Application extends App { $mountProviderCollection->registerProvider($this->getContainer()->query('MountProvider')); $mountProviderCollection->registerProvider($this->getContainer()->query('ExternalMountProvider')); } - - public function setupPropagation() { - $propagationManager = $this->getContainer()->query('PropagationManager'); - \OCP\Util::connectHook('OC_Filesystem', 'setup', $propagationManager, 'globalSetup'); - - $this->getContainer()->query('GroupPropagationManager')->globalSetup(); - } } diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 82691129926..9b2e2c00f60 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -286,8 +286,12 @@ OCA.Sharing.PublicApp = { _saveToOwnCloud: function (remote, token, owner, name, isProtected) { var location = window.location.protocol + '//' + window.location.host + OC.webroot; + + if(remote.substr(-1) !== '/') { + remote += '/' + }; - var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location) // our location is the remote for the other server + 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; diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php index 93e2cdb540b..020f55c5157 100644 --- a/apps/files_sharing/lib/external/manager.php +++ b/apps/files_sharing/lib/external/manager.php @@ -192,6 +192,8 @@ class Manager { $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid)); $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept'); + \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $share['remote']]); + //FIXME $this->scrapNotification($share['remote_id']); return true; } diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php index 458e7f2619b..74a2a3ff4d6 100644 --- a/apps/files_sharing/lib/mountprovider.php +++ b/apps/files_sharing/lib/mountprovider.php @@ -24,7 +24,6 @@ namespace OCA\Files_Sharing; use OC\Files\Filesystem; use OC\User\NoUserException; -use OCA\Files_Sharing\Propagation\PropagationManager; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; @@ -37,17 +36,10 @@ class MountProvider implements IMountProvider { protected $config; /** - * @var \OCA\Files_Sharing\Propagation\PropagationManager - */ - protected $propagationManager; - - /** * @param \OCP\IConfig $config - * @param \OCA\Files_Sharing\Propagation\PropagationManager $propagationManager */ - public function __construct(IConfig $config, PropagationManager $propagationManager) { + public function __construct(IConfig $config) { $this->config = $config; - $this->propagationManager = $propagationManager; } @@ -60,21 +52,15 @@ class MountProvider implements IMountProvider { */ public function getMountsForUser(IUser $user, IStorageFactory $storageFactory) { $shares = \OCP\Share::getItemsSharedWithUser('file', $user->getUID()); - $propagator = $this->propagationManager->getSharePropagator($user->getUID()); - $propagator->propagateDirtyMountPoints($shares); $shares = array_filter($shares, function ($share) { return $share['permissions'] > 0; }); $shares = array_map(function ($share) use ($user, $storageFactory) { - // for updating etags for the share owner when we make changes to this share. - $ownerPropagator = $this->propagationManager->getChangePropagator($share['uid_owner']); return new SharedMount( '\OC\Files\Storage\Shared', '/' . $user->getUID() . '/' . $share['file_target'], array( - 'propagationManager' => $this->propagationManager, - 'propagator' => $ownerPropagator, 'share' => $share, 'user' => $user->getUID() ), diff --git a/apps/files_sharing/lib/propagation/changewatcher.php b/apps/files_sharing/lib/propagation/changewatcher.php deleted file mode 100644 index e61c161da19..00000000000 --- a/apps/files_sharing/lib/propagation/changewatcher.php +++ /dev/null @@ -1,110 +0,0 @@ -<?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\Propagation; - -use OC\Files\Cache\ChangePropagator; -use OC\Files\Filesystem; -use OC\Files\View; -use OCA\Files_Sharing\SharedMount; - -/** - * Watch for changes made in a shared mount and propagate the changes to the share owner - */ -class ChangeWatcher { - /** - * The user view for the logged in user - * - * @var \OC\Files\View - */ - private $baseView; - - /** - * @var RecipientPropagator - */ - private $recipientPropagator; - - /** - * @param \OC\Files\View $baseView the view for the logged in user - * @param RecipientPropagator $recipientPropagator - */ - public function __construct(View $baseView, RecipientPropagator $recipientPropagator) { - $this->baseView = $baseView; - $this->recipientPropagator = $recipientPropagator; - } - - - public function writeHook($params) { - $path = $params['path']; - $fullPath = $this->baseView->getAbsolutePath($path); - $mount = $this->baseView->getMount($path); - if ($mount instanceof SharedMount) { - $this->propagateForOwner($mount->getShare(), $mount->getInternalPath($fullPath), $mount->getOwnerPropagator()); - } - $info = $this->baseView->getFileInfo($path); - if ($info) { - // trigger propagation if the subject of the write hook is shared. - // if a parent folder of $path is shared the propagation will be triggered from the change propagator hooks - $this->recipientPropagator->propagateById($info->getId()); - } - } - - public function renameHook($params) { - $path1 = $params['oldpath']; - $path2 = $params['newpath']; - $fullPath1 = $this->baseView->getAbsolutePath($path1); - $fullPath2 = $this->baseView->getAbsolutePath($path2); - $mount1 = $this->baseView->getMount($path1); - $mount2 = $this->baseView->getMount($path2); - if ($mount1 instanceof SharedMount and $mount1->getInternalPath($fullPath1) !== '') { - $this->propagateForOwner($mount1->getShare(), $mount1->getInternalPath($fullPath1), $mount1->getOwnerPropagator()); - } - if ($mount1 !== $mount2 and $mount2 instanceof SharedMount and $mount2->getInternalPath($fullPath2) !== '') { - $this->propagateForOwner($mount2->getShare(), $mount2->getInternalPath($fullPath2), $mount2->getOwnerPropagator()); - } - } - - /** - * @param array $share - * @param string $internalPath - * @param \OC\Files\Cache\ChangePropagator $propagator - */ - private function propagateForOwner($share, $internalPath, ChangePropagator $propagator) { - // note that we have already set up the filesystem for the owner when mounting the share - $view = new View('/' . $share['uid_owner'] . '/files'); - - $shareRootPath = $view->getPath($share['item_source']); - if (!is_null($shareRootPath)) { - $path = $shareRootPath . '/' . $internalPath; - $path = Filesystem::normalizePath($path); - $propagator->addChange($path); - $propagator->propagateChanges(); - } - } - - public function permissionsHook($params) { - $share = $params['share']; - - if ($share['item_type'] === 'file' || $share['item_type'] === 'folder') { - $this->recipientPropagator->markDirty($share, microtime(true)); - } - } -} diff --git a/apps/files_sharing/lib/propagation/grouppropagationmanager.php b/apps/files_sharing/lib/propagation/grouppropagationmanager.php deleted file mode 100644 index ba550dccec3..00000000000 --- a/apps/files_sharing/lib/propagation/grouppropagationmanager.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php -/** - * @author Vincent Petry <pvince81@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\Propagation; - -use OC\Files\Filesystem; -use OC\Files\View; -use OCP\IConfig; -use OCP\IUserSession; -use OCP\IGroup; -use OCP\IUser; -use OCP\IGroupManager; -use OCA\Files_Sharing\Propagation\PropagationManager; - -/** - * Propagate changes on group changes - */ -class GroupPropagationManager { - /** - * @var \OCP\IUserSession - */ - private $userSession; - - /** - * @var \OCP\IGroupManager - */ - private $groupManager; - - /** - * @var PropagationManager - */ - private $propagationManager; - - /** - * Items shared with a given user. - * Key is user id and value is an array of shares. - * - * @var array - */ - private $userShares = []; - - public function __construct(IUserSession $userSession, IGroupManager $groupManager, PropagationManager $propagationManager) { - $this->userSession = $userSession; - $this->groupManager = $groupManager; - $this->propagationManager = $propagationManager; - } - - public function onPreProcessUser(IGroup $group, IUser $targetUser) { - $this->userShares[$targetUser->getUID()] = $this->getUserShares($targetUser->getUID()); - } - - public function onPostAddUser(IGroup $group, IUser $targetUser) { - $targetUserId = $targetUser->getUID(); - $sharesAfter = $this->getUserShares($targetUserId); - - $this->propagateSharesDiff($targetUserId, $sharesAfter, $this->userShares[$targetUserId]); - unset($this->userShares[$targetUserId]); - } - - public function onPostRemoveUser(IGroup $group, IUser $targetUser) { - $targetUserId = $targetUser->getUID(); - $sharesAfter = $this->getUserShares($targetUserId); - - $this->propagateSharesDiff($targetUserId, $this->userShares[$targetUserId], $sharesAfter); - unset($this->userShares[$targetUserId]); - } - - private function getUserShares($targetUserId) { - return \OCP\Share::getItemsSharedWithUser('file', $targetUserId); - } - - /** - * Propagate etag for the shares that are in $shares1 but not in $shares2. - * - * @param string $targetUserId user id for which to propagate shares - * @param array $shares1 - * @param array $shares2 - */ - private function propagateSharesDiff($targetUserId, $shares1, $shares2) { - $sharesToPropagate = array_udiff( - $shares1, - $shares2, - function($share1, $share2) { - return ($share2['id'] - $share1['id']); - } - ); - - \OC\Files\Filesystem::initMountPoints($targetUserId); - $this->propagationManager->propagateSharesToUser($sharesToPropagate, $targetUserId); - } - - /** - * To be called from setupFS trough a hook - * - * Sets up listening to changes made to shares owned by the current user - */ - public function globalSetup() { - $user = $this->userSession->getUser(); - if (!$user) { - return; - } - - $this->groupManager->listen('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']); - $this->groupManager->listen('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']); - $this->groupManager->listen('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']); - $this->groupManager->listen('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']); - } - - public function tearDown() { - $this->groupManager->removeListener('\OC\Group', 'preAddUser', [$this, 'onPreProcessUser']); - $this->groupManager->removeListener('\OC\Group', 'postAddUser', [$this, 'onPostAddUser']); - $this->groupManager->removeListener('\OC\Group', 'preRemoveUser', [$this, 'onPreProcessUser']); - $this->groupManager->removeListener('\OC\Group', 'postRemoveUser', [$this, 'onPostRemoveUser']); - } -} diff --git a/apps/files_sharing/lib/propagation/propagationmanager.php b/apps/files_sharing/lib/propagation/propagationmanager.php deleted file mode 100644 index aac9428240d..00000000000 --- a/apps/files_sharing/lib/propagation/propagationmanager.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php -/** - * @author Robin Appelman <icewind@owncloud.com> - * @author Vincent Petry <pvince81@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\Propagation; - -use OC\Files\Filesystem; -use OC\Files\View; -use OCP\IConfig; -use OCP\IUserSession; -use OCP\Util; - - -/** - * Keep track of all change and share propagators by owner - */ -class PropagationManager { - /** - * @var \OCP\IUserSession - */ - private $userSession; - - /** - * @var \OCP\IConfig - */ - private $config; - - /** - * Change propagators for share owner - * - * @var \OC\Files\Cache\ChangePropagator[] - */ - private $changePropagators = []; - - /** - * Recipient propagators - * - * @var \OCA\Files_Sharing\Propagation\RecipientPropagator[] - */ - private $sharePropagators = []; - - public function __construct(IUserSession $userSession, IConfig $config) { - $this->userSession = $userSession; - $this->config = $config; - } - - /** - * @param string $user - * @return \OC\Files\Cache\ChangePropagator - */ - public function getChangePropagator($user) { - $activeUser = $this->userSession->getUser(); - - // for the local user we want to propagator from the active view, not any cached one - if ($activeUser && $activeUser->getUID() === $user && Filesystem::getView() instanceof View) { - // it's important that we take the existing propagator here to make sure we can listen to external changes - $this->changePropagators[$user] = Filesystem::getView()->getUpdater()->getPropagator(); - } - if (isset($this->changePropagators[$user])) { - return $this->changePropagators[$user]; - } - $view = new View('/' . $user . '/files'); - $this->changePropagators[$user] = $view->getUpdater()->getPropagator(); - return $this->changePropagators[$user]; - } - - /** - * Propagates etag changes for the given shares to the given user - * - * @param array array of shares for which to trigger etag change - * @param string $user - */ - public function propagateSharesToUser($shares, $user) { - $changePropagator = $this->getChangePropagator($user); - foreach ($shares as $share) { - $changePropagator->addChange($share['file_target']); - } - $time = microtime(true); - $changePropagator->propagateChanges(floor($time)); - } - - /** - * @param string $user - * @return \OCA\Files_Sharing\Propagation\RecipientPropagator - */ - public function getSharePropagator($user) { - if (isset($this->sharePropagators[$user])) { - return $this->sharePropagators[$user]; - } - $this->sharePropagators[$user] = new RecipientPropagator($user, $this->getChangePropagator($user), $this->config, $this); - return $this->sharePropagators[$user]; - } - - /** - * Attach the recipient propagator for $user to the change propagator of a share owner to mark shares as dirty when the owner makes a change to a share - * - * @param string $shareOwner - * @param string $user - */ - public function listenToOwnerChanges($shareOwner, $user) { - $sharePropagator = $this->getSharePropagator($user); - $ownerPropagator = $this->getChangePropagator($shareOwner); - $sharePropagator->attachToPropagator($ownerPropagator, $shareOwner); - } - - /** - * To be called from setupFS trough a hook - * - * Sets up listening to changes made to shares owned by the current user - */ - public function globalSetup() { - $user = $this->userSession->getUser(); - if (!$user) { - return; - } - $recipientPropagator = $this->getSharePropagator($user->getUID()); - $watcher = new ChangeWatcher(Filesystem::getView(), $recipientPropagator); - - // for marking shares owned by the active user as dirty when a file inside them changes - $this->listenToOwnerChanges($user->getUID(), $user->getUID()); - Util::connectHook('OC_Filesystem', 'post_write', $watcher, 'writeHook'); - Util::connectHook('OC_Filesystem', 'post_delete', $watcher, 'writeHook'); - Util::connectHook('OC_Filesystem', 'post_rename', $watcher, 'renameHook'); - Util::connectHook('OCP\Share', 'post_update_permissions', $watcher, 'permissionsHook'); - } -} diff --git a/apps/files_sharing/lib/propagation/recipientpropagator.php b/apps/files_sharing/lib/propagation/recipientpropagator.php deleted file mode 100644 index 5eacf4c0f6e..00000000000 --- a/apps/files_sharing/lib/propagation/recipientpropagator.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php -/** - * @author Lukas Reschke <lukas@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @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\Propagation; - -use OC\Files\Cache\ChangePropagator; -use OC\Files\View; -use OC\Share\Share; -use OCP\Files\NotFoundException; - -/** - * Propagate etags for share recipients - */ -class RecipientPropagator { - /** - * @var string - */ - protected $userId; - - /** - * @var \OC\Files\Cache\ChangePropagator - */ - protected $changePropagator; - - /** - * @var \OCP\IConfig - */ - protected $config; - - /** - * @var PropagationManager - */ - private $manager; - - /** - * @param string $userId current user, must match the propagator's - * user - * @param \OC\Files\Cache\ChangePropagator $changePropagator change propagator - * initialized with a view for $user - * @param \OCP\IConfig $config - * @param PropagationManager $manager - */ - public function __construct($userId, $changePropagator, $config, PropagationManager $manager) { - $this->userId = $userId; - $this->changePropagator = $changePropagator; - $this->config = $config; - $this->manager = $manager; - } - - /** - * Propagate the etag changes for all shares marked as dirty and mark the shares as clean - * - * @param array $shares the shares for the users - * @param int $time - */ - public function propagateDirtyMountPoints(array $shares, $time = null) { - if ($time === null) { - $time = microtime(true); - } - $dirtyShares = $this->getDirtyShares($shares); - foreach ($dirtyShares as $share) { - $this->changePropagator->addChange($share['file_target']); - } - if (count($dirtyShares)) { - $this->config->setUserValue($this->userId, 'files_sharing', 'last_propagate', $time); - $this->changePropagator->propagateChanges(floor($time)); - } - } - - /** - * Get all shares we need to update the etag for - * - * @param array $shares the shares for the users - * @return string[] - */ - protected function getDirtyShares($shares) { - $dirty = []; - $userTime = $this->config->getUserValue($this->userId, 'files_sharing', 'last_propagate', 0); - foreach ($shares as $share) { - $updateTime = $this->config->getAppValue('files_sharing', $share['id'], 0); - if ($updateTime >= $userTime) { - $dirty[] = $share; - } - } - return $dirty; - } - - /** - * @param array $share - * @param float $time - */ - public function markDirty($share, $time = null) { - if ($time === null) { - $time = microtime(true); - } - $this->config->setAppValue('files_sharing', $share['id'], $time); - } - - /** - * Listen on the propagator for updates made to shares owned by a user - * - * @param \OC\Files\Cache\ChangePropagator $propagator - * @param string $owner - */ - public function attachToPropagator(ChangePropagator $propagator, $owner) { - $propagator->listen('\OC\Files', 'propagate', function ($path, $entry) use ($owner) { - $this->propagateById($entry['fileid']); - }); - } - - protected $propagatingIds = []; - - /** - * @param int $id - */ - public function propagateById($id) { - if (isset($this->propagatingIds[$id])) { - return; - } - $this->propagatingIds[$id] = true; - $shares = Share::getAllSharesForFileId($id); - foreach ($shares as $share) { - // propagate down the share tree - $this->markDirty($share, microtime(true)); - - // propagate up the share tree - if ($share['share_with'] === $this->userId) { - $user = $share['uid_owner']; - $view = new View('/' . $user . '/files'); - - try { - $path = $view->getPath($share['file_source']); - } catch (NotFoundException $e) { - $path = null; - } - - $watcher = new ChangeWatcher($view, $this->manager->getSharePropagator($user)); - $watcher->writeHook(['path' => $path]); - } - } - - unset($this->propagatingIds[$id]); - } -} diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index a1387957867..275fea97c7f 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -39,11 +39,6 @@ class SharedMount extends MountPoint implements MoveableMount { protected $storage = null; /** - * @var \OC\Files\Cache\ChangePropagator - */ - protected $ownerPropagator; - - /** * @var \OC\Files\View */ private $recipientView; @@ -54,8 +49,6 @@ class SharedMount extends MountPoint implements MoveableMount { private $user; public function __construct($storage, $mountpoint, $arguments = null, $loader = null) { - // first update the mount point before creating the parent - $this->ownerPropagator = $arguments['propagator']; $this->user = $arguments['user']; $this->recipientView = new View('/' . $this->user . '/files'); $newMountPoint = $this->verifyMountPoint($arguments['share']); @@ -201,11 +194,4 @@ class SharedMount extends MountPoint implements MoveableMount { public function getShare() { return $this->getStorage()->getShare(); } - - /** - * @return \OC\Files\Cache\ChangePropagator - */ - public function getOwnerPropagator() { - return $this->ownerPropagator; - } } diff --git a/apps/files_sharing/lib/sharedpropagator.php b/apps/files_sharing/lib/sharedpropagator.php new file mode 100644 index 00000000000..fcb4b92dd33 --- /dev/null +++ b/apps/files_sharing/lib/sharedpropagator.php @@ -0,0 +1,43 @@ +<?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; + +use OC\Files\Cache\Propagator; + +class SharedPropagator extends Propagator { + /** + * @var \OC\Files\Storage\Shared + */ + protected $storage; + + /** + * @param string $internalPath + * @param int $time + * @return array[] all propagated entries + */ + public function propagateChange($internalPath, $time) { + $source = $this->storage->getSourcePath($internalPath); + /** @var \OC\Files\Storage\Storage $storage */ + list($storage, $sourceInternalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->getPropagator()->propagateChange($sourceInternalPath, $time); + } +} diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 18e02844179..4807b5ee738 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -51,11 +51,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { private $ownerView; /** - * @var \OCA\Files_Sharing\Propagation\PropagationManager - */ - private $propagationManager; - - /** * @var string */ private $user; @@ -65,7 +60,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { public function __construct($arguments) { $this->share = $arguments['share']; $this->ownerView = $arguments['ownerView']; - $this->propagationManager = $arguments['propagationManager']; $this->user = $arguments['user']; } @@ -75,9 +69,6 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { } $this->initialized = true; Filesystem::initMountPoints($this->share['uid_owner']); - - // for updating our etags when changes are made to the share from the owners side (probably indirectly by us trough another share) - $this->propagationManager->listenToOwnerChanges($this->share['uid_owner'], $this->user); } /** @@ -571,6 +562,13 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage { return new \OC\Files\Cache\Shared_Watcher($storage); } + public function getPropagator($storage = null) { + if (!$storage) { + $storage = $this; + } + return new \OCA\Files_Sharing\SharedPropagator($storage); + } + public function getOwner($path) { if ($path == '') { $path = $this->getMountPoint(); diff --git a/apps/files_sharing/tests/etagpropagation.php b/apps/files_sharing/tests/etagpropagation.php index 1abf04df84f..2a33732d63f 100644 --- a/apps/files_sharing/tests/etagpropagation.php +++ b/apps/files_sharing/tests/etagpropagation.php @@ -193,7 +193,8 @@ class EtagPropagation extends TestCase { public function testOwnerWritesToSingleFileShare() { $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - Filesystem::file_put_contents('/foo.txt', 'bar'); + Filesystem::file_put_contents('/foo.txt', 'longer_bar'); + Filesystem::touch('/foo.txt', time() - 1); $this->assertEtagsNotChanged([self::TEST_FILES_SHARING_API_USER4, self::TEST_FILES_SHARING_API_USER3]); $this->assertEtagsChanged([self::TEST_FILES_SHARING_API_USER1, self::TEST_FILES_SHARING_API_USER2]); diff --git a/apps/files_sharing/tests/grouppropagationmanager.php b/apps/files_sharing/tests/grouppropagationmanager.php deleted file mode 100644 index ea32ca4f7ec..00000000000 --- a/apps/files_sharing/tests/grouppropagationmanager.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php -/** - * @author Vincent Petry <pvince81@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\View; -use OCP\IGroupManager; -use OCP\IGroup; -use OCP\IUser; -use OCP\Share; -use OCA\Files_Sharing\Propagation\GroupPropagationManager; -use OCA\Files_Sharing\Propagation\PropagationManager; - -class GroupPropagationManagerTest extends TestCase { - - /** - * @var GroupPropagationManager - */ - private $groupPropagationManager; - - /** - * @var IGroupManager - */ - private $groupManager; - - /** - * @var PropagationManager - */ - private $propagationManager; - - /** - * @var IGroup - */ - private $recipientGroup; - - /** - * @var IUser - */ - private $recipientUser; - - /** - * @var array - */ - private $fileInfo; - - protected function setUp() { - parent::setUp(); - - $user = $this->getMockBuilder('\OCP\IUser') - ->disableOriginalConstructor() - ->getMock(); - $user->method('getUID')->willReturn(self::TEST_FILES_SHARING_API_USER1); - $userSession = $this->getMockBuilder('\OCP\IUserSession') - ->disableOriginalConstructor() - ->getMock(); - $userSession->method('getUser')->willReturn(selF::TEST_FILES_SHARING_API_USER1); - - $this->propagationManager = $this->getMockBuilder('OCA\Files_Sharing\Propagation\PropagationManager') - ->disableOriginalConstructor() - ->getMock(); - - $this->groupManager = \OC::$server->getGroupManager(); - $this->groupPropagationManager = new GroupPropagationManager( - $userSession, - $this->groupManager, - $this->propagationManager - ); - $this->groupPropagationManager->globalSetup(); - - // since the sharing code is not mockable, we have to create a real folder - $this->loginAsUser(self::TEST_FILES_SHARING_API_USER1); - $view1 = new View('/' . self::TEST_FILES_SHARING_API_USER1 . '/files'); - $view1->mkdir('/folder'); - - $this->fileInfo = $view1->getFileInfo('/folder'); - - $this->recipientGroup = $this->groupManager->get(self::TEST_FILES_SHARING_API_GROUP1); - $this->recipientUser = \OC::$server->getUserManager()->get(self::TEST_FILES_SHARING_API_USER3); - - Share::shareItem( - 'folder', - $this->fileInfo['fileid'], - Share::SHARE_TYPE_GROUP, - $this->recipientGroup->getGID(), - \OCP\Constants::PERMISSION_READ - ); - - $this->loginAsUser($this->recipientUser->getUID()); - } - - protected function tearDown() { - $this->groupPropagationManager->tearDown(); - $this->recipientGroup->removeUser($this->recipientUser); - parent::tearDown(); - } - - public function testPropagateWhenAddedToGroup() { - $this->propagationManager->expects($this->once()) - ->method('propagateSharesToUser') - ->with($this->callback(function($shares) { - if (count($shares) !== 1) { - return false; - } - $share = array_values($shares)[0]; - return $share['file_source'] === $this->fileInfo['fileid'] && - $share['share_with'] === $this->recipientGroup->getGID() && - $share['file_target'] === '/folder'; - }), $this->recipientUser->getUID()); - - $this->recipientGroup->addUser($this->recipientUser); - } - - public function testPropagateWhenRemovedFromGroup() { - $this->recipientGroup->addUser($this->recipientUser); - - $this->propagationManager->expects($this->once()) - ->method('propagateSharesToUser') - ->with($this->callback(function($shares) { - if (count($shares) !== 1) { - return false; - } - $share = array_values($shares)[0]; - return $share['file_source'] === $this->fileInfo['fileid'] && - $share['share_with'] === $this->recipientGroup->getGID() && - $share['file_target'] === '/folder'; - }), $this->recipientUser->getUID()); - - $this->recipientGroup->removeUser($this->recipientUser); - } - - public function testPropagateWhenRemovedFromGroupWithSubdirTarget() { - $this->recipientGroup->addUser($this->recipientUser); - - // relogin to refresh mount points - $this->loginAsUser($this->recipientUser->getUID()); - $recipientView = new View('/' . $this->recipientUser->getUID() . '/files'); - - $this->assertTrue($recipientView->mkdir('sub')); - $this->assertTrue($recipientView->rename('folder', 'sub/folder')); - - $this->propagationManager->expects($this->once()) - ->method('propagateSharesToUser') - ->with($this->callback(function($shares) { - if (count($shares) !== 1) { - return false; - } - $share = array_values($shares)[0]; - return $share['file_source'] === $this->fileInfo['fileid'] && - $share['share_with'] === $this->recipientGroup->getGID() && - $share['file_target'] === '/sub/folder'; - }), $this->recipientUser->getUID()); - - $this->recipientGroup->removeUser($this->recipientUser); - } -} diff --git a/apps/files_sharing/tests/testcase.php b/apps/files_sharing/tests/testcase.php index c91734a5b03..6a72a34149a 100644 --- a/apps/files_sharing/tests/testcase.php +++ b/apps/files_sharing/tests/testcase.php @@ -61,7 +61,6 @@ abstract class TestCase extends \Test\TestCase { $application = new Application(); $application->registerMountProviders(); - $application->setupPropagation(); // reset backend \OC_User::clearBackends(); diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php index e28b854ca1f..934f831ef74 100644 --- a/apps/files_trashbin/tests/trashbin.php +++ b/apps/files_trashbin/tests/trashbin.php @@ -65,7 +65,6 @@ class Test_Trashbin extends \Test\TestCase { \OC::registerShareHooks(); $application = new \OCA\Files_Sharing\AppInfo\Application(); $application->registerMountProviders(); - $application->setupPropagation(); //disable encryption \OC_App::disable('encryption'); diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index b9bc0932a84..6ebbf830e70 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -51,7 +51,6 @@ class Test_Files_Versioning extends \Test\TestCase { $application = new \OCA\Files_Sharing\AppInfo\Application(); $application->registerMountProviders(); - $application->setupPropagation(); // create test user self::loginHelper(self::TEST_VERSIONS_USER2, true); diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 7be91186c16..42e57e8296e 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -864,14 +864,13 @@ class Access extends LDAPUtility implements user\IUserTools { * @param bool $pagedSearchOK whether a paged search has been executed * @param bool $skipHandling required for paged search when cookies to * prior results need to be gained - * @return array|false array with the search result as first value and pagedSearchOK as - * second | false if not successful + * @return bool cookie validity, true if we have more pages, false otherwise. */ private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) { + $cookie = null; if($pagedSearchOK) { $cr = $this->connection->getConnectionResource(); foreach($sr as $key => $res) { - $cookie = null; if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) { $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie); } @@ -892,6 +891,12 @@ class Access extends LDAPUtility implements user\IUserTools { \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO); } } + /* ++ Fixing RHDS searches with pages with zero results ++ + * Return cookie status. If we don't have more pages, with RHDS + * cookie is null, with openldap cookie is an empty string and + * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0 + */ + return !empty($cookie) || $cookie === '0'; } /** @@ -920,7 +925,6 @@ class Access extends LDAPUtility implements user\IUserTools { $this->connection->getConnectionResource(); do { - $continue = false; $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset); if($search === false) { @@ -928,12 +932,20 @@ class Access extends LDAPUtility implements user\IUserTools { } list($sr, $pagedSearchOK) = $search; - $count = $this->countEntriesInSearchResults($sr, $limitPerPage, $continue); + /* ++ Fixing RHDS searches with pages with zero results ++ + * countEntriesInSearchResults() method signature changed + * by removing $limit and &$hasHitLimit parameters + */ + $count = $this->countEntriesInSearchResults($sr); $counter += $count; - $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage, + $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage, $offset, $pagedSearchOK, $skipHandling); $offset += $limitPerPage; + /* ++ Fixing RHDS searches with pages with zero results ++ + * Continue now depends on $hasMorePages value + */ + $continue = $pagedSearchOK && $hasMorePages; } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter)); return $counter; @@ -941,20 +953,15 @@ class Access extends LDAPUtility implements user\IUserTools { /** * @param array $searchResults - * @param int $limit - * @param bool $hasHitLimit * @return int */ - private function countEntriesInSearchResults($searchResults, $limit, &$hasHitLimit) { + private function countEntriesInSearchResults($searchResults) { $cr = $this->connection->getConnectionResource(); $counter = 0; foreach($searchResults as $res) { $count = intval($this->ldap->countEntries($cr, $res)); $counter += $count; - if($count > 0 && $count === $limit) { - $hasHitLimit = true; - } } return $counter; @@ -975,38 +982,53 @@ class Access extends LDAPUtility implements user\IUserTools { //otherwise search will fail $limit = null; } - $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); - if($search === false) { - return array(); - } - list($sr, $pagedSearchOK) = $search; - $cr = $this->connection->getConnectionResource(); - if($skipHandling) { - //i.e. result do not need to be fetched, we just need the cookie - //thus pass 1 or any other value as $iFoundItems because it is not - //used - $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit, - $offset, $pagedSearchOK, - $skipHandling); - return array(); - } + /* ++ Fixing RHDS searches with pages with zero results ++ + * As we can have pages with zero results and/or pages with less + * than $limit results but with a still valid server 'cookie', + * loops through until we get $continue equals true and + * $findings['count'] < $limit + */ + $findings = array(); + $savedoffset = $offset; + do { + $continue = false; + $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); + if($search === false) { + return array(); + } + list($sr, $pagedSearchOK) = $search; + $cr = $this->connection->getConnectionResource(); - // Do the server-side sorting - foreach(array_reverse($attr) as $sortAttr){ - foreach($sr as $searchResource) { - $this->ldap->sort($cr, $searchResource, $sortAttr); + if($skipHandling) { + //i.e. result do not need to be fetched, we just need the cookie + //thus pass 1 or any other value as $iFoundItems because it is not + //used + $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit, + $offset, $pagedSearchOK, + $skipHandling); + return array(); } - } - $findings = array(); - foreach($sr as $res) { - $findings = array_merge($findings, $this->ldap->getEntries($cr , $res )); - } + // Do the server-side sorting + foreach(array_reverse($attr) as $sortAttr){ + foreach($sr as $searchResource) { + $this->ldap->sort($cr, $searchResource, $sortAttr); + } + } + + + foreach($sr as $res) { + $findings = array_merge($findings, $this->ldap->getEntries($cr , $res )); + } - $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'], - $limit, $offset, $pagedSearchOK, + $continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'], + $limit, $offset, $pagedSearchOK, $skipHandling); + $offset += $limit; + } while ($continue && $pagedSearchOK && $findings['count'] < $limit); + // reseting offset + $offset = $savedoffset; // if we're here, probably no connection resource is returned. // to make ownCloud behave nicely, we simply give back an empty array. @@ -1594,7 +1616,13 @@ class Access extends LDAPUtility implements user\IUserTools { } } - } else if($this->connection->hasPagedResultSupport && $limit === 0) { + /* ++ Fixing RHDS searches with pages with zero results ++ + * We coudn't get paged searches working with our RHDS for login ($limit = 0), + * due to pages with zero results. + * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination + * if we don't have a previous paged search. + */ + } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) { // a search without limit was requested. However, if we do use // Paged Search once, we always must do it. This requires us to // initialize it with the configured page size. diff --git a/core/js/files/iedavclient.js b/core/js/files/iedavclient.js index bc6bce2f9ae..4fd9a3a3bf4 100644 --- a/core/js/files/iedavclient.js +++ b/core/js/files/iedavclient.js @@ -145,10 +145,7 @@ var k = 0; for (k = 0; k < propNode.childNodes.length; k++) { var prop = propNode.childNodes[k]; - var value = prop.textContent || prop.text; - if (prop.childNodes && prop.childNodes.length > 0 && prop.childNodes[0].nodeType === 1) { - value = prop.childNodes; - } + var value = this._parsePropNode(prop); propStat.properties['{' + prop.namespaceURI + '}' + (prop.localName || prop.baseName)] = value; } diff --git a/core/vendor/davclient.js/lib/client.js b/core/vendor/davclient.js/lib/client.js index 121b5dcab5f..18bbf13f3cd 100644 --- a/core/vendor/davclient.js/lib/client.js +++ b/core/vendor/davclient.js/lib/client.js @@ -148,6 +148,33 @@ dav.Client.prototype = { }, + /** + * Parses a property node. + * + * Either returns a string if the node only contains text, or returns an + * array of non-text subnodes. + * + * @param {Object} propNode node to parse + * @return {string|Array} text content as string or array of subnodes, excluding text nodes + */ + _parsePropNode: function(propNode) { + var content = null; + if (propNode.childNodes && propNode.childNodes.length > 0) { + var subNodes = []; + // filter out text nodes + for (var j = 0; j < propNode.childNodes.length; j++) { + var node = propNode.childNodes[j]; + if (node.nodeType === 1) { + subNodes.push(node); + } + } + if (subNodes.length) { + content = subNodes; + } + } + + return content || propNode.textContent || propNode.text; + }, /** * Parses a multi-status response body. @@ -197,11 +224,7 @@ dav.Client.prototype = { var propNode = propIterator.iterateNext(); while(propNode) { - var content = propNode.textContent; - if (propNode.childNodes && propNode.childNodes.length > 0 && propNode.childNodes[0].nodeType === 1) { - content = propNode.childNodes; - } - + var content = this._parsePropNode(propNode); propStat.properties['{' + propNode.namespaceURI + '}' + propNode.localName] = content; propNode = propIterator.iterateNext(); diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index 40477243324..e6110c1925d 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -160,6 +160,7 @@ class Cache { } else { //fix types $data['fileid'] = (int)$data['fileid']; + $data['parent'] = (int)$data['parent']; $data['size'] = 0 + $data['size']; $data['mtime'] = (int)$data['mtime']; $data['storage_mtime'] = (int)$data['storage_mtime']; @@ -391,12 +392,17 @@ class Cache { if ($file === '') { return -1; } else { - $parent = dirname($file); - if ($parent === '.') { - $parent = ''; - } - return $this->getId($parent); + $parent = $this->getParentPath($file); + return (int) $this->getId($parent); + } + } + + private function getParentPath($path) { + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; } + return $parent; } /** diff --git a/lib/private/files/cache/changepropagator.php b/lib/private/files/cache/changepropagator.php index 9696a82257e..2a48eb13c59 100644 --- a/lib/private/files/cache/changepropagator.php +++ b/lib/private/files/cache/changepropagator.php @@ -22,6 +22,7 @@ namespace OC\Files\Cache; +use OC\Files\Filesystem; use OC\Hooks\BasicEmitter; /** @@ -61,23 +62,30 @@ class ChangePropagator extends BasicEmitter { * @param int $time (optional) the mtime to set for the folders, if not set the current time is used */ public function propagateChanges($time = null) { - $parents = $this->getAllParents(); - $this->changedFiles = array(); + $changes = $this->getChanges(); + $this->changedFiles = []; if (!$time) { $time = time(); } - foreach ($parents as $parent) { + foreach ($changes as $change) { /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ - list($storage, $internalPath) = $this->view->resolvePath($parent); + $absolutePath = $this->view->getAbsolutePath($change); + $mount = $this->view->getMount($change); + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); if ($storage) { - $cache = $storage->getCache(); - $entry = $cache->get($internalPath); - $cache->update($entry['fileid'], array('mtime' => max($time, $entry['mtime']), 'etag' => $storage->getETag($internalPath))); - $this->emit('\OC\Files', 'propagate', [$parent, $entry]); + $propagator = $storage->getPropagator(); + $propagatedEntries = $propagator->propagateChange($internalPath, $time); + + foreach ($propagatedEntries as $entry) { + $absolutePath = Filesystem::normalizePath($mount->getMountPoint() . '/' . $entry['path']); + $relativePath = $this->view->getRelativePath($absolutePath); + $this->emit('\OC\Files', 'propagate', [$relativePath, $entry]); + } } } } diff --git a/lib/private/files/cache/propagator.php b/lib/private/files/cache/propagator.php new file mode 100644 index 00000000000..bd11cef5990 --- /dev/null +++ b/lib/private/files/cache/propagator.php @@ -0,0 +1,66 @@ +<?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 OC\Files\Cache; + +/** + * Propagate etags and mtimes within the storage + */ +class Propagator { + /** + * @var \OC\Files\Storage\Storage + */ + protected $storage; + + /** + * @param \OC\Files\Storage\Storage $storage + */ + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + } + + + /** + * @param string $internalPath + * @param int $time + * @return array[] all propagated entries + */ + public function propagateChange($internalPath, $time) { + $cache = $this->storage->getCache($internalPath); + + $parentId = $cache->getParentId($internalPath); + $propagatedEntries = []; + while ($parentId !== -1) { + $entry = $cache->get($parentId); + $propagatedEntries[] = $entry; + if (!$entry) { + return $propagatedEntries; + } + $mtime = max($time, $entry['mtime']); + + $cache->update($parentId, ['mtime' => $mtime, 'etag' => $this->storage->getETag($entry['path'])]); + + $parentId = $entry['parent']; + } + + return $propagatedEntries; + } +} diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index bb810dd45ed..5b5e8697004 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -62,6 +62,11 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { private $owner; /** + * @var string[] + */ + private $childEtags = []; + + /** * @param string|boolean $path * @param Storage\Storage $storage * @param string $internalPath @@ -93,6 +98,8 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function offsetGet($offset) { if ($offset === 'type') { return $this->getType(); + } else if ($offset === 'etag') { + return $this->getEtag(); } elseif (isset($this->data[$offset])) { return $this->data[$offset]; } else { @@ -153,7 +160,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { * @return string */ public function getEtag() { - return $this->data['etag']; + if (count($this->childEtags) > 0) { + $combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags); + return md5($combinedEtag); + } else { + return $this->data['etag']; + } } /** @@ -285,4 +297,26 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function getOwner() { return $this->owner; } + + /** + * Add a cache entry which is the child of this folder + * + * Sets the size, etag and size to for cross-storage childs + * + * @param array $data cache entry for the child + * @param string $entryPath full path of the child entry + */ + public function addSubEntry($data, $entryPath) { + $this->data['size'] += isset($data['size']) ? $data['size'] : 0; + if (isset($data['mtime'])) { + $this->data['mtime'] = max($this->data['mtime'], $data['mtime']); + } + if (isset($data['etag'])) { + // prefix the etag with the relative path of the subentry to propagate etag on mount moves + $relativeEntryPath = substr($entryPath, strlen($this->getPath())); + // attach the permissions to propagate etag on permision changes of submounts + $permissions = isset($data['permissions']) ? $data['permissions'] : 0; + $this->childEtags[] = $relativeEntryPath . '/' . $data['etag'] . $permissions; + } + } } diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 8e4958a930d..3772b442b59 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -37,6 +37,7 @@ namespace OC\Files\Storage; use OC\Files\Cache\Cache; +use OC\Files\Cache\Propagator; use OC\Files\Cache\Scanner; use OC\Files\Filesystem; use OC\Files\Cache\Watcher; @@ -64,6 +65,7 @@ abstract class Common implements Storage { protected $cache; protected $scanner; protected $watcher; + protected $propagator; protected $storageCache; protected $mountOptions = []; @@ -345,6 +347,22 @@ abstract class Common implements Storage { return $this->watcher; } + /** + * get a propagator instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Propagator + */ + public function getPropagator($storage = null) { + if (!$storage) { + $storage = $this; + } + if (!isset($this->propagator)) { + $this->propagator = new Propagator($storage); + } + return $this->propagator; + } + public function getStorageCache($storage = null) { if (!$storage) { $storage = $this; diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php index dcde7b8029b..9147f572461 100644 --- a/lib/private/files/storage/dav.php +++ b/lib/private/files/storage/dav.php @@ -47,6 +47,7 @@ use OCP\Files\StorageNotAvailableException; use OCP\Util; use Sabre\DAV\Client; use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\Xml\Property\ResourceType; use Sabre\HTTP\ClientException; use Sabre\HTTP\ClientHttpException; @@ -137,7 +138,7 @@ class DAV extends Common { $this->client->setThrowExceptions(true); if ($this->secure === true && $this->certPath) { - $this->client->addTrustedCertificates($this->certPath); + $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); } } @@ -280,7 +281,8 @@ class DAV extends Common { $response = $this->propfind($path); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType = $response["{DAV:}resourcetype"]->resourceType; + /** @var ResourceType[] $response */ + $responseType = $response["{DAV:}resourcetype"]->getValue(); } return (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; } catch (ClientHttpException $e) { @@ -554,7 +556,8 @@ class DAV extends Common { $response = $this->propfind($path); $responseType = array(); if (isset($response["{DAV:}resourcetype"])) { - $responseType = $response["{DAV:}resourcetype"]->resourceType; + /** @var ResourceType[] $response */ + $responseType = $response["{DAV:}resourcetype"]->getValue(); } $type = (count($responseType) > 0 and $responseType[0] == "{DAV:}collection") ? 'dir' : 'file'; if ($type == 'dir') { diff --git a/lib/private/files/storage/storage.php b/lib/private/files/storage/storage.php index f46ac544b56..3399c717890 100644 --- a/lib/private/files/storage/storage.php +++ b/lib/private/files/storage/storage.php @@ -68,6 +68,14 @@ interface Storage extends \OCP\Files\Storage { public function getWatcher($path = '', $storage = null); /** + * get a propagator instance for the cache + * + * @param \OC\Files\Storage\Storage (optional) the storage to pass to the watcher + * @return \OC\Files\Cache\Propagator + */ + public function getPropagator($storage = null); + + /** * @return \OC\Files\Cache\Storage */ public function getStorageCache(); diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php index 048738170db..5327b94211b 100644 --- a/lib/private/files/storage/wrapper/wrapper.php +++ b/lib/private/files/storage/wrapper/wrapper.php @@ -430,6 +430,13 @@ class Wrapper implements \OC\Files\Storage\Storage { return $this->storage->getWatcher($path, $storage); } + public function getPropagator($storage = null) { + if (!$storage) { + $storage = $this; + } + return $this->storage->getPropagator($storage); + } + /** * @return \OC\Files\Cache\Storage */ diff --git a/lib/private/files/stream/encryption.php b/lib/private/files/stream/encryption.php index 4c328993ef7..585a697c5d1 100644 --- a/lib/private/files/stream/encryption.php +++ b/lib/private/files/stream/encryption.php @@ -167,7 +167,7 @@ class Encryption extends Wrapper { ) )); - return self::wrapSource($source, $mode, $context, 'ocencryption', $wrapper); + return self::wrapSource($source, $context, 'ocencryption', $wrapper, $mode); } /** @@ -181,7 +181,7 @@ class Encryption extends Wrapper { * @return resource * @throws \BadMethodCallException */ - protected static function wrapSource($source, $mode, $context, $protocol, $class) { + protected static function wrapSource($source, $context, $protocol, $class, $mode = 'r+') { try { stream_wrapper_register($protocol, $class); if (@rewinddir($source) === false) { diff --git a/lib/private/files/view.php b/lib/private/files/view.php index cee4b182425..3ad9d36a384 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -707,10 +707,6 @@ class View { } else if ($result) { if ($internalPath1 !== '') { // dont do a cache update for moved mounts $this->updater->rename($path1, $path2); - } else { // only do etag propagation - $this->getUpdater()->getPropagator()->addChange($path1); - $this->getUpdater()->getPropagator()->addChange($path2); - $this->getUpdater()->getPropagator()->propagateChanges(); } } @@ -1179,6 +1175,48 @@ class View { } /** + * Get file info from cache + * + * If the file is not in cached it will be scanned + * If the file has changed on storage the cache will be updated + * + * @param \OC\Files\Storage\Storage $storage + * @param string $internalPath + * @param string $relativePath + * @return array|bool + */ + private function getCacheEntry($storage, $internalPath, $relativePath) { + $cache = $storage->getCache($internalPath); + $data = $cache->get($internalPath); + $watcher = $storage->getWatcher($internalPath); + + try { + // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data + if (!$data || $data['size'] === -1) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + if (!$storage->file_exists($internalPath)) { + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + return false; + } + $scanner = $storage->getScanner($internalPath); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { + $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); + $watcher->update($internalPath, $data); + $this->updater->propagate($relativePath); + $data = $cache->get($internalPath); + $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); + } + } catch (LockedException $e) { + // if the file is locked we just use the old cache info + } + + return $data; + } + + /** * get the filesystem info * * @param string $path @@ -1189,9 +1227,8 @@ class View { */ public function getFileInfo($path, $includeMountPoints = true) { $this->assertPathLength($path); - $data = array(); if (!Filesystem::isValidPath($path)) { - return $data; + return false; } if (Cache\Scanner::isPartialFile($path)) { return $this->getPartFileInfo($path); @@ -1202,48 +1239,27 @@ class View { $mount = Filesystem::getMountManager()->find($path); $storage = $mount->getStorage(); $internalPath = $mount->getInternalPath($path); - $data = null; if ($storage) { - $cache = $storage->getCache($internalPath); + $data = $this->getCacheEntry($storage, $internalPath, $relativePath); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); + if(!is_array($data)) { + return false; + } - try { - // if the file is not in the cache or needs to be updated, trigger the scanner and reload the data - if (!$data) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - return false; - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } else if (!Cache\Scanner::isPartialFile($internalPath) && $watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($relativePath, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($relativePath, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if ($mount instanceof MoveableMount && $internalPath === '') { + $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; } + $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); + $info = new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + if ($data and isset($data['fileid'])) { - // upgrades from oc6 or lower might not have the permissions set in the file cache - if ($data['permissions'] === 0) { - $data['permissions'] = $storage->getPermissions($data['path']); - $cache->update($data['fileid'], array('permissions' => $data['permissions'])); - } if ($includeMountPoints and $data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mount points to the folder $extOnly = ($includeMountPoints === 'ext'); - $mountPoints = Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); + $mounts = Filesystem::getMountManager()->findIn($path); + foreach ($mounts as $mount) { + $subStorage = $mount->getStorage(); if ($subStorage) { // exclude shared storage ? if ($extOnly && $subStorage instanceof \OC\Files\Storage\Shared) { @@ -1251,22 +1267,16 @@ class View { } $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); - $data['size'] += isset($rootEntry['size']) ? $rootEntry['size'] : 0; + $info->addSubEntry($rootEntry, $mount->getMountPoint()); } } } } - } - if (!$data) { - return false; - } - if ($mount instanceof MoveableMount && $internalPath === '') { - $data['permissions'] |= \OCP\Constants::PERMISSION_DELETE; + return $info; } - $owner = $this->getUserObjectForOwner($storage->getOwner($internalPath)); - return new FileInfo($path, $storage, $internalPath, $data, $mount, $owner); + return false; } /** @@ -1278,9 +1288,8 @@ class View { */ public function getDirectoryContent($directory, $mimetype_filter = '') { $this->assertPathLength($directory); - $result = array(); if (!Filesystem::isValidPath($directory)) { - return $result; + return []; } $path = $this->getAbsolutePath($directory); $path = Filesystem::normalizePath($path); @@ -1291,50 +1300,25 @@ class View { $cache = $storage->getCache($internalPath); $user = \OC_User::getUser(); - /** - * @var \OC\Files\FileInfo[] $files - */ - $files = array(); + $data = $this->getCacheEntry($storage, $internalPath, $directory); - $data = $cache->get($internalPath); - $watcher = $storage->getWatcher($internalPath); - try { - if (!$data or $data['size'] === -1) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - if (!$storage->file_exists($internalPath)) { - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - return array(); - } - $scanner = $storage->getScanner($internalPath); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } else if ($watcher->needsUpdate($internalPath, $data)) { - $this->lockFile($directory, ILockingProvider::LOCK_SHARED); - $watcher->update($internalPath, $data); - $this->updater->propagate($path); - $data = $cache->get($internalPath); - $this->unlockFile($directory, ILockingProvider::LOCK_SHARED); - } - } catch (LockedException $e) { - // if the file is locked we just use the old cache info + if (!is_array($data) || !isset($data['fileid'])) { + return []; } $folderId = $data['fileid']; $contents = $cache->getFolderContentsById($folderId); //TODO: mimetype_filter - foreach ($contents as $content) { - if ($content['permissions'] === 0) { - $content['permissions'] = $storage->getPermissions($content['path']); - $cache->update($content['fileid'], array('permissions' => $content['permissions'])); - } - // if sharing was disabled for the user we remove the share permissions + /** + * @var \OC\Files\FileInfo[] $files + */ + $files = array_map(function (array $content) use ($path, $storage, $mount) { if (\OCP\Util::isSharingDisabledForUser()) { $content['permissions'] = $content['permissions'] & ~\OCP\Constants::PERMISSION_SHARE; } $owner = $this->getUserObjectForOwner($storage->getOwner($content['path'])); - $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); - } + return new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content, $mount, $owner); + }, $contents); //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mounts = Filesystem::getMountManager()->findIn($path); @@ -1345,7 +1329,8 @@ class View { if ($subStorage) { $subCache = $subStorage->getCache(''); - if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) { + $rootEntry = $subCache->get(''); + if (!$rootEntry) { $subScanner = $subStorage->getScanner(''); try { $subScanner->scanFile(''); @@ -1363,17 +1348,17 @@ class View { ); continue; } + $rootEntry = $subCache->get(''); } - $rootEntry = $subCache->get(''); if ($rootEntry) { $relativePath = trim(substr($mountPoint, $dirLength), '/'); if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; + if ($entry->getName() === $entryName) { + $entry->addSubEntry($rootEntry, $mountPoint); } } } else { //mountpoint in this folder, add an entry for it @@ -1410,23 +1395,23 @@ class View { } if ($mimetype_filter) { - foreach ($files as $file) { + $files = array_filter($files, function (FileInfo $file) use ($mimetype_filter) { if (strpos($mimetype_filter, '/')) { - if ($file['mimetype'] === $mimetype_filter) { + if ($file->getMimetype() === $mimetype_filter) { $result[] = $file; } } else { - if ($file['mimepart'] === $mimetype_filter) { + if ($file->getMimePart() === $mimetype_filter) { $result[] = $file; } } - } - } else { - $result = $files; + }); } - } - return $result; + return $files; + } else { + return []; + } } /** diff --git a/lib/private/notification/inotification.php b/lib/private/notification/inotification.php index b79b7f6d9d3..0187abab152 100644 --- a/lib/private/notification/inotification.php +++ b/lib/private/notification/inotification.php @@ -76,10 +76,11 @@ interface INotification { /** * @param string $type - * @param int $id + * @param string $id * @return $this - * @throws \InvalidArgumentException if the object type or id are invalid + * @throws \InvalidArgumentException if the object type or id is invalid * @since 8.2.0 + * @changed 9.0.0 Type of $id changed to string */ public function setObject($type, $id); @@ -90,8 +91,9 @@ interface INotification { public function getObjectType(); /** - * @return int + * @return string * @since 8.2.0 + * @changed 9.0.0 Return type changed to string */ public function getObjectId(); diff --git a/lib/private/notification/notification.php b/lib/private/notification/notification.php index a22d5446f45..70964fc0366 100644 --- a/lib/private/notification/notification.php +++ b/lib/private/notification/notification.php @@ -35,7 +35,7 @@ class Notification implements INotification { /** @var string */ protected $objectType; - /** @var int */ + /** @var string */ protected $objectId; /** @var string */ @@ -83,7 +83,7 @@ class Notification implements INotification { $this->dateTime = new \DateTime(); $this->dateTime->setTimestamp(0); $this->objectType = ''; - $this->objectId = 0; + $this->objectId = ''; $this->subject = ''; $this->subjectParameters = []; $this->subjectParsed = ''; @@ -164,10 +164,11 @@ class Notification implements INotification { /** * @param string $type - * @param int $id + * @param string $id * @return $this * @throws \InvalidArgumentException if the object type or id is invalid * @since 8.2.0 + * @changed 9.0.0 Type of $id changed to string */ public function setObject($type, $id) { if (!is_string($type) || $type === '' || isset($type[64])) { @@ -175,10 +176,10 @@ class Notification implements INotification { } $this->objectType = $type; - if (!is_int($id)) { + if (!is_int($id) && (!is_string($id) || $id === '' || isset($id[64]))) { throw new \InvalidArgumentException('The given object id is invalid'); } - $this->objectId = $id; + $this->objectId = (string) $id; return $this; } @@ -191,8 +192,9 @@ class Notification implements INotification { } /** - * @return int + * @return string * @since 8.2.0 + * @changed 9.0.0 Return type changed to string */ public function getObjectId() { return $this->objectId; @@ -443,7 +445,7 @@ class Notification implements INotification { && $this->getObjectType() !== '' && - $this->getObjectId() !== 0 + $this->getObjectId() !== '' ; } } diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 6aac0d6264d..1308c99f804 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -2566,7 +2566,10 @@ class Share extends Constants { $result = self::tryHttpPost($url, $fields); $status = json_decode($result['result'], true); - return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + if ($result['success'] && $status['ocs']['meta']['statuscode'] === 100) { + \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); + return true; + } } diff --git a/lib/public/files/node.php b/lib/public/files/node.php index 866b9b6f61f..aa1115f8c28 100644 --- a/lib/public/files/node.php +++ b/lib/public/files/node.php @@ -225,12 +225,4 @@ interface Node extends FileInfo { * @since 6.0.0 */ public function getName(); - - /** - * Get the file owner - * - * @since 9.0.0 - * @return string - */ - public function getOwner(); } diff --git a/settings/l10n/fr.js b/settings/l10n/fr.js index e3c2a792389..805b5c05d5c 100644 --- a/settings/l10n/fr.js +++ b/settings/l10n/fr.js @@ -12,12 +12,10 @@ OC.L10N.register( "Log" : "Log", "Tips & tricks" : "Trucs et astuces", "Updates" : "Mises à jour", - "Authentication error" : "Erreur d'authentification", - "Your full name has been changed." : "Votre nom complet a été modifié.", - "Unable to change full name" : "Impossible de changer le nom complet", "Couldn't remove app." : "Impossible de supprimer l'application.", "Language changed" : "Langue changée", "Invalid request" : "Requête non valide", + "Authentication error" : "Erreur d'authentification", "Admins can't remove themself from the admin group" : "Les administrateurs ne peuvent pas se retirer eux-mêmes du groupe admin", "Unable to add user to group %s" : "Impossible d'ajouter l'utilisateur au groupe %s", "Unable to remove user from group %s" : "Impossible de supprimer l'utilisateur du groupe %s", @@ -53,6 +51,8 @@ OC.L10N.register( "Invalid user" : "Utilisateur non valable", "Unable to change mail address" : "Impossible de modifier l'adresse e-mail", "Email saved" : "E-mail sauvegardé", + "Your full name has been changed." : "Votre nom complet a été modifié.", + "Unable to change full name" : "Impossible de changer le nom complet", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Êtes-vous vraiment sûr de vouloir ajouter \"{domain}\" comme domaine de confiance ?", "Add trusted domain" : "Ajouter un domaine de confiance", "Migration in progress. Please wait until the migration is finished" : "Migration en cours. Veuillez attendre que celle-ci se termine", @@ -192,6 +192,7 @@ OC.L10N.register( "More" : "Plus", "Less" : "Moins", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "La taille du fichier journal excède 100 Mo. Le télécharger peut prendre un certain temps!", + "What to log" : "Ce qu'il faut journaliser", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite est actuellement utilisé comme gestionnaire de base de données. Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "L'utilisation de SQLite est particulièrement déconseillée si vous utilisez le client de bureau pour synchroniser vos données.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pour migrer vers un autre type de base de données, utilisez la ligne de commande : 'occ db:convert-type' ou consultez la <a target=\"_blank\" href=\"%s\">documentation ↗</a>.", diff --git a/settings/l10n/fr.json b/settings/l10n/fr.json index a0722035b7e..61c3129a8c1 100644 --- a/settings/l10n/fr.json +++ b/settings/l10n/fr.json @@ -10,12 +10,10 @@ "Log" : "Log", "Tips & tricks" : "Trucs et astuces", "Updates" : "Mises à jour", - "Authentication error" : "Erreur d'authentification", - "Your full name has been changed." : "Votre nom complet a été modifié.", - "Unable to change full name" : "Impossible de changer le nom complet", "Couldn't remove app." : "Impossible de supprimer l'application.", "Language changed" : "Langue changée", "Invalid request" : "Requête non valide", + "Authentication error" : "Erreur d'authentification", "Admins can't remove themself from the admin group" : "Les administrateurs ne peuvent pas se retirer eux-mêmes du groupe admin", "Unable to add user to group %s" : "Impossible d'ajouter l'utilisateur au groupe %s", "Unable to remove user from group %s" : "Impossible de supprimer l'utilisateur du groupe %s", @@ -51,6 +49,8 @@ "Invalid user" : "Utilisateur non valable", "Unable to change mail address" : "Impossible de modifier l'adresse e-mail", "Email saved" : "E-mail sauvegardé", + "Your full name has been changed." : "Votre nom complet a été modifié.", + "Unable to change full name" : "Impossible de changer le nom complet", "Are you really sure you want add \"{domain}\" as trusted domain?" : "Êtes-vous vraiment sûr de vouloir ajouter \"{domain}\" comme domaine de confiance ?", "Add trusted domain" : "Ajouter un domaine de confiance", "Migration in progress. Please wait until the migration is finished" : "Migration en cours. Veuillez attendre que celle-ci se termine", @@ -190,6 +190,7 @@ "More" : "Plus", "Less" : "Moins", "The logfile is bigger than 100 MB. Downloading it may take some time!" : "La taille du fichier journal excède 100 Mo. Le télécharger peut prendre un certain temps!", + "What to log" : "Ce qu'il faut journaliser", "SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite est actuellement utilisé comme gestionnaire de base de données. Pour des installations plus volumineuses, nous vous conseillons d'utiliser un autre gestionnaire de base de données.", "Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "L'utilisation de SQLite est particulièrement déconseillée si vous utilisez le client de bureau pour synchroniser vos données.", "To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Pour migrer vers un autre type de base de données, utilisez la ligne de commande : 'occ db:convert-type' ou consultez la <a target=\"_blank\" href=\"%s\">documentation ↗</a>.", diff --git a/tests/lib/files/cache/changepropagator.php b/tests/lib/files/cache/changepropagator.php index 1b56da5e97c..9108330eb9b 100644 --- a/tests/lib/files/cache/changepropagator.php +++ b/tests/lib/files/cache/changepropagator.php @@ -94,4 +94,37 @@ class ChangePropagator extends \Test\TestCase { $this->assertEquals(100, $time - $cache->get('foo')['mtime']); $this->assertEquals(100, $time - $cache->get('foo/bar')['mtime']); } + + public function testPropagateCrossStorage() { + $storage = new Temporary(); + $this->view->mkdir('/foo'); + Filesystem::mount($storage, [], $this->view->getAbsolutePath('/foo/submount')); + $this->view->mkdir('/foo/submount/bar'); + $this->view->file_put_contents('/foo/submount/bar/sad.txt', 'qwerty'); + + $oldInfo1 = $this->view->getFileInfo('/'); + $oldInfo2 = $this->view->getFileInfo('/foo'); + $oldInfo3 = $this->view->getFileInfo('/foo/submount'); + $oldInfo4 = $this->view->getFileInfo('/foo/submount/bar'); + + $time = time() + 50; + + $this->propagator->addChange('/foo/submount/bar/sad.txt'); + $this->propagator->propagateChanges($time); + + $newInfo1 = $this->view->getFileInfo('/'); + $newInfo2 = $this->view->getFileInfo('/foo'); + $newInfo3 = $this->view->getFileInfo('/foo/submount'); + $newInfo4 = $this->view->getFileInfo('/foo/submount/bar'); + + $this->assertEquals($newInfo1->getMTime(), $time); + $this->assertEquals($newInfo2->getMTime(), $time); + $this->assertEquals($newInfo3->getMTime(), $time); + $this->assertEquals($newInfo4->getMTime(), $time); + + $this->assertNotSame($oldInfo1->getEtag(), $newInfo1->getEtag()); + $this->assertNotSame($oldInfo2->getEtag(), $newInfo2->getEtag()); + $this->assertNotSame($oldInfo3->getEtag(), $newInfo3->getEtag()); + $this->assertNotSame($oldInfo4->getEtag(), $newInfo3->getEtag()); + } } diff --git a/tests/lib/files/cache/updaterlegacy.php b/tests/lib/files/cache/updaterlegacy.php index f4d52e9a390..c1a0d3d8230 100644 --- a/tests/lib/files/cache/updaterlegacy.php +++ b/tests/lib/files/cache/updaterlegacy.php @@ -10,6 +10,7 @@ namespace Test\Files\Cache; use \OC\Files\Filesystem as Filesystem; use OC\Files\Storage\Temporary; +use OC\Files\View; class UpdaterLegacy extends \Test\TestCase { /** @@ -111,7 +112,8 @@ class UpdaterLegacy extends \Test\TestCase { $storage2->getScanner()->scan(''); //initialize etags $cache2 = $storage2->getCache(); Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); - $folderCachedData = $this->cache->get('folder'); + $view = new View('/' . self::$user . '/files'); + $folderCachedData = $view->getFileInfo('folder'); $substorageCachedData = $cache2->get(''); Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); $this->assertTrue($cache2->inCache('foo.txt')); @@ -124,7 +126,7 @@ class UpdaterLegacy extends \Test\TestCase { $this->assertInternalType('string', $cachedData['etag']); $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); - $cachedData = $this->cache->get('folder'); + $cachedData = $view->getFileInfo('folder'); $this->assertInternalType('string', $folderCachedData['etag']); $this->assertInternalType('string', $cachedData['etag']); $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); @@ -168,8 +170,9 @@ class UpdaterLegacy extends \Test\TestCase { $cache2 = $storage2->getCache(); Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); + $view = new View('/' . self::$user . '/files'); $this->assertTrue($cache2->inCache('foo.txt')); - $folderCachedData = $this->cache->get('folder'); + $folderCachedData = $view->getFileInfo('folder'); $substorageCachedData = $cache2->get(''); Filesystem::unlink('folder/substorage/foo.txt'); $this->assertFalse($cache2->inCache('foo.txt')); @@ -180,7 +183,7 @@ class UpdaterLegacy extends \Test\TestCase { $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); $this->assertGreaterThanOrEqual($substorageCachedData['mtime'], $cachedData['mtime']); - $cachedData = $this->cache->get('folder'); + $cachedData = $view->getFileInfo('folder'); $this->assertInternalType('string', $folderCachedData['etag']); $this->assertInternalType('string', $cachedData['etag']); $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); @@ -222,8 +225,9 @@ class UpdaterLegacy extends \Test\TestCase { $cache2 = $storage2->getCache(); Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); + $view = new View('/' . self::$user . '/files'); $this->assertTrue($cache2->inCache('foo.txt')); - $folderCachedData = $this->cache->get('folder'); + $folderCachedData = $view->getFileInfo('folder'); $substorageCachedData = $cache2->get(''); $fooCachedData = $cache2->get('foo.txt'); Filesystem::rename('folder/substorage/foo.txt', 'folder/substorage/bar.txt'); @@ -240,7 +244,7 @@ class UpdaterLegacy extends \Test\TestCase { // rename can cause mtime change - invalid assert // $this->assertEquals($mtime, $cachedData['mtime']); - $cachedData = $this->cache->get('folder'); + $cachedData = $view->getFileInfo('folder'); $this->assertInternalType('string', $folderCachedData['etag']); $this->assertInternalType('string', $cachedData['etag']); $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); @@ -287,35 +291,4 @@ class UpdaterLegacy extends \Test\TestCase { $this->assertEquals($time, $cachedData['mtime']); } - public function testTouchWithMountPoints() { - $storage2 = new \OC\Files\Storage\Temporary(array()); - $cache2 = $storage2->getCache(); - Filesystem::mount($storage2, array(), '/' . self::$user . '/files/folder/substorage'); - Filesystem::file_put_contents('folder/substorage/foo.txt', 'asd'); - $this->assertTrue($cache2->inCache('foo.txt')); - $folderCachedData = $this->cache->get('folder'); - $substorageCachedData = $cache2->get(''); - $fooCachedData = $cache2->get('foo.txt'); - $cachedData = $cache2->get('foo.txt'); - $time = 1371006070; - $this->cache->put('folder', ['mtime' => $time - 100]); - Filesystem::touch('folder/substorage/foo.txt', $time); - $cachedData = $cache2->get('foo.txt'); - $this->assertInternalType('string', $fooCachedData['etag']); - $this->assertInternalType('string', $cachedData['etag']); - $this->assertNotSame($fooCachedData['etag'], $cachedData['etag']); - $this->assertEquals($time, $cachedData['mtime']); - - $cachedData = $cache2->get(''); - $this->assertInternalType('string', $substorageCachedData['etag']); - $this->assertInternalType('string', $cachedData['etag']); - $this->assertNotSame($substorageCachedData['etag'], $cachedData['etag']); - - $cachedData = $this->cache->get('folder'); - $this->assertInternalType('string', $folderCachedData['etag']); - $this->assertInternalType('string', $cachedData['etag']); - $this->assertNotSame($folderCachedData['etag'], $cachedData['etag']); - $this->assertEquals($time, $cachedData['mtime']); - } - } diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php index 1a11b29cf00..192768d04af 100644 --- a/tests/lib/files/etagtest.php +++ b/tests/lib/files/etagtest.php @@ -27,7 +27,6 @@ class EtagTest extends \Test\TestCase { \OC_Hook::clear('OC_Filesystem', 'setup'); $application = new \OCA\Files_Sharing\AppInfo\Application(); $application->registerMountProviders(); - $application->setupPropagation(); \OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); \OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); diff --git a/tests/lib/notification/notificationtest.php b/tests/lib/notification/notificationtest.php index da3ada440e2..91fa1de8b42 100644 --- a/tests/lib/notification/notificationtest.php +++ b/tests/lib/notification/notificationtest.php @@ -176,22 +176,23 @@ class NotificationTest extends TestCase { public function dataSetObject() { return [ - ['a', 1], - [str_repeat('a', 64), time()], + ['a', '21', '21'], + [str_repeat('a', 64), 42, '42'], ]; } /** * @dataProvider dataSetObject * @param string $type - * @param int $id + * @param int|string $id + * @param string $exptectedId */ - public function testSetObject($type, $id) { + public function testSetObject($type, $id, $exptectedId) { $this->assertSame('', $this->notification->getObjectType()); - $this->assertSame(0, $this->notification->getObjectId()); + $this->assertSame('', $this->notification->getObjectId()); $this->assertSame($this->notification, $this->notification->setObject($type, $id)); $this->assertSame($type, $this->notification->getObjectType()); - $this->assertSame($id, $this->notification->getObjectId()); + $this->assertSame($exptectedId, $this->notification->getObjectId()); } public function dataSetObjectTypeInvalid() { @@ -210,7 +211,14 @@ class NotificationTest extends TestCase { } public function dataSetObjectIdInvalid() { - return $this->dataInvalidInt(); + return [ + [true], + [false], + [''], + [str_repeat('a', 64 + 1)], + [[]], + [[str_repeat('a', 64 + 1)]], + ]; } /** @@ -560,12 +568,12 @@ class NotificationTest extends TestCase { public function dataIsValidCommon() { return [ - ['', '', 0, '', 0, false], - ['app', '', 0, '', 0, false], - ['app', 'user', 0, '', 0, false], - ['app', 'user', time(), '', 0, false], - ['app', 'user', time(), 'type', 0, false], - ['app', 'user', time(), 'type', 42, true], + ['', '', 0, '', '', false], + ['app', '', 0, '', '', false], + ['app', 'user', 0, '', '', false], + ['app', 'user', time(), '', '', false], + ['app', 'user', time(), 'type', '', false], + ['app', 'user', time(), 'type', '42', true], ]; } @@ -576,7 +584,7 @@ class NotificationTest extends TestCase { * @param string $user * @param int $timestamp * @param string $objectType - * @param int $objectId + * @param string $objectId * @param bool $expected */ public function testIsValidCommon($app, $user, $timestamp, $objectType, $objectId, $expected) { |