diff options
54 files changed, 708 insertions, 170 deletions
diff --git a/apps/dav/lib/CalDAV/Calendar.php b/apps/dav/lib/CalDAV/Calendar.php index d1eff1aeaa3..a216e4e078b 100644 --- a/apps/dav/lib/CalDAV/Calendar.php +++ b/apps/dav/lib/CalDAV/Calendar.php @@ -30,6 +30,12 @@ use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; +/** + * Class Calendar + * + * @package OCA\DAV\CalDAV + * @property BackendInterface|CalDavBackend $caldavBackend + */ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { public function __construct(BackendInterface $caldavBackend, $calendarInfo, IL10N $l10n) { @@ -61,11 +67,13 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { * @param array $add * @param array $remove * @return void + * @throws Forbidden */ - function updateShares(array $add, array $remove) { - /** @var CalDavBackend $calDavBackend */ - $calDavBackend = $this->caldavBackend; - $calDavBackend->updateShares($this, $add, $remove); + public function updateShares(array $add, array $remove) { + if ($this->isShared()) { + throw new Forbidden(); + } + $this->caldavBackend->updateShares($this, $add, $remove); } /** @@ -80,10 +88,11 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { * * @return array */ - function getShares() { - /** @var CalDavBackend $calDavBackend */ - $calDavBackend = $this->caldavBackend; - return $calDavBackend->getShares($this->getResourceId()); + public function getShares() { + if ($this->isShared()) { + return []; + } + return $this->caldavBackend->getShares($this->getResourceId()); } /** @@ -100,7 +109,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { return $this->calendarInfo['principaluri']; } - function getACL() { + public function getACL() { $acl = [ [ 'privilege' => '{DAV:}read', @@ -136,27 +145,29 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { ]; } - /** @var CalDavBackend $calDavBackend */ - $calDavBackend = $this->caldavBackend; - return $calDavBackend->applyShareAcl($this->getResourceId(), $acl); + if ($this->isShared()) { + return $acl; + } + + return $this->caldavBackend->applyShareAcl($this->getResourceId(), $acl); } - function getChildACL() { + public function getChildACL() { return $this->getACL(); } - function getOwner() { + public function getOwner() { if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal'])) { return $this->calendarInfo['{http://owncloud.org/ns}owner-principal']; } return parent::getOwner(); } - function delete() { + public function delete() { if (isset($this->calendarInfo['{http://owncloud.org/ns}owner-principal']) && $this->calendarInfo['{http://owncloud.org/ns}owner-principal'] !== $this->calendarInfo['principaluri']) { $principal = 'principal:' . parent::getOwner(); - $shares = $this->getShares(); + $shares = $this->caldavBackend->getShares($this->getResourceId()); $shares = array_filter($shares, function($share) use ($principal){ return $share['href'] === $principal; }); @@ -164,9 +175,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { throw new Forbidden(); } - /** @var CalDavBackend $calDavBackend */ - $calDavBackend = $this->caldavBackend; - $calDavBackend->updateShares($this, [], [ + $this->caldavBackend->updateShares($this, [], [ 'href' => $principal ]); return; @@ -174,7 +183,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { parent::delete(); } - function propPatch(PropPatch $propPatch) { + public function propPatch(PropPatch $propPatch) { // parent::propPatch will only update calendars table // if calendar is shared, changes have to be made to the properties table if (!$this->isShared()) { @@ -182,7 +191,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { } } - function getChild($name) { + public function getChild($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); @@ -190,7 +199,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { throw new NotFound('Calendar object not found'); } - if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { throw new NotFound('Calendar object not found'); } @@ -200,12 +209,12 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { } - function getChildren() { + public function getChildren() { $objs = $this->caldavBackend->getCalendarObjects($this->calendarInfo['id']); $children = []; foreach ($objs as $obj) { - if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { continue; } $obj['acl'] = $this->getChildACL(); @@ -215,12 +224,12 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { } - function getMultipleChildren(array $paths) { + public function getMultipleChildren(array $paths) { $objs = $this->caldavBackend->getMultipleCalendarObjects($this->calendarInfo['id'], $paths); $children = []; foreach ($objs as $obj) { - if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { continue; } $obj['acl'] = $this->getChildACL(); @@ -230,19 +239,19 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { } - function childExists($name) { + public function childExists($name) { $obj = $this->caldavBackend->getCalendarObject($this->calendarInfo['id'], $name); if (!$obj) { return false; } - if ($this->isShared() && $obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE) { + if ($obj['classification'] === CalDavBackend::CLASSIFICATION_PRIVATE && $this->isShared()) { return false; } return true; } - function calendarQuery(array $filters) { + public function calendarQuery(array $filters) { $uris = $this->caldavBackend->calendarQuery($this->calendarInfo['id'], $filters); if ($this->isShared()) { @@ -258,7 +267,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { * @param boolean $value * @return string|null */ - function setPublishStatus($value) { + public function setPublishStatus($value) { $publicUri = $this->caldavBackend->setPublishStatus($value, $this); $this->calendarInfo['publicuri'] = $publicUri; return $publicUri; @@ -267,7 +276,7 @@ class Calendar extends \Sabre\CalDAV\Calendar implements IShareable { /** * @return mixed $value */ - function getPublishStatus() { + public function getPublishStatus() { return $this->caldavBackend->getPublishStatus($this); } diff --git a/apps/dav/lib/CardDAV/AddressBook.php b/apps/dav/lib/CardDAV/AddressBook.php index 1c13ac00aec..eb5bebaa2ee 100644 --- a/apps/dav/lib/CardDAV/AddressBook.php +++ b/apps/dav/lib/CardDAV/AddressBook.php @@ -29,6 +29,12 @@ use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\PropPatch; +/** + * Class AddressBook + * + * @package OCA\DAV\CardDAV + * @property BackendInterface|CardDavBackend $carddavBackend + */ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { /** @@ -41,8 +47,8 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { public function __construct(BackendInterface $carddavBackend, array $addressBookInfo, IL10N $l10n) { parent::__construct($carddavBackend, $addressBookInfo); - if ($this->getName() === CardDavBackend::PERSONAL_ADDRESSBOOK_URI && - $this->addressBookInfo['{DAV:}displayname'] === CardDavBackend::PERSONAL_ADDRESSBOOK_NAME) { + if ($this->addressBookInfo['{DAV:}displayname'] === CardDavBackend::PERSONAL_ADDRESSBOOK_NAME && + $this->getName() === CardDavBackend::PERSONAL_ADDRESSBOOK_URI) { $this->addressBookInfo['{DAV:}displayname'] = $l10n->t('Contacts'); } } @@ -64,11 +70,13 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { * @param array $add * @param array $remove * @return void + * @throws Forbidden */ - function updateShares(array $add, array $remove) { - /** @var CardDavBackend $carddavBackend */ - $carddavBackend = $this->carddavBackend; - $carddavBackend->updateShares($this, $add, $remove); + public function updateShares(array $add, array $remove) { + if ($this->isShared()) { + throw new Forbidden(); + } + $this->carddavBackend->updateShares($this, $add, $remove); } /** @@ -83,13 +91,14 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { * * @return array */ - function getShares() { - /** @var CardDavBackend $carddavBackend */ - $carddavBackend = $this->carddavBackend; - return $carddavBackend->getShares($this->getResourceId()); + public function getShares() { + if ($this->isShared()) { + return []; + } + return $this->carddavBackend->getShares($this->getResourceId()); } - function getACL() { + public function getACL() { $acl = [ [ 'privilege' => '{DAV:}read', @@ -123,16 +132,18 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { ]; } - /** @var CardDavBackend $carddavBackend */ - $carddavBackend = $this->carddavBackend; - return $carddavBackend->applyShareAcl($this->getResourceId(), $acl); + if ($this->isShared()) { + return $acl; + } + + return $this->carddavBackend->applyShareAcl($this->getResourceId(), $acl); } - function getChildACL() { + public function getChildACL() { return $this->getACL(); } - function getChild($name) { + public function getChild($name) { $obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name); if (!$obj) { @@ -150,17 +161,17 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { return $this->addressBookInfo['id']; } - function getOwner() { + public function getOwner() { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal']; } return parent::getOwner(); } - function delete() { + public function delete() { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { $principal = 'principal:' . parent::getOwner(); - $shares = $this->getShares(); + $shares = $this->carddavBackend->getShares($this->getResourceId()); $shares = array_filter($shares, function($share) use ($principal){ return $share['href'] === $principal; }); @@ -168,9 +179,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { throw new Forbidden(); } - /** @var CardDavBackend $cardDavBackend */ - $cardDavBackend = $this->carddavBackend; - $cardDavBackend->updateShares($this, [], [ + $this->carddavBackend->updateShares($this, [], [ 'href' => $principal ]); return; @@ -178,7 +187,7 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { parent::delete(); } - function propPatch(PropPatch $propPatch) { + public function propPatch(PropPatch $propPatch) { if (isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { throw new Forbidden(); } @@ -186,10 +195,15 @@ class AddressBook extends \Sabre\CardDAV\AddressBook implements IShareable { } public function getContactsGroups() { - /** @var CardDavBackend $cardDavBackend */ - $cardDavBackend = $this->carddavBackend; + return $this->carddavBackend->collectCardProperties($this->getResourceId(), 'CATEGORIES'); + } + + private function isShared() { + if (!isset($this->addressBookInfo['{http://owncloud.org/ns}owner-principal'])) { + return false; + } - return $cardDavBackend->collectCardProperties($this->getResourceId(), 'CATEGORIES'); + return $this->addressBookInfo['{http://owncloud.org/ns}owner-principal'] !== $this->addressBookInfo['principaluri']; } private function canWrite() { diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 22ef232dac4..63ca03b0d3d 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -143,8 +143,6 @@ class CalDavBackendTest extends AbstractCalDavBackendTest { $this->assertAcl(self::UNIT_TEST_USER, '{DAV:}write', $acl); $this->assertAccess($userCanRead, self::UNIT_TEST_USER1, '{DAV:}read', $acl); $this->assertAccess($userCanWrite, self::UNIT_TEST_USER1, '{DAV:}write', $acl); - $this->assertAccess($groupCanRead, self::UNIT_TEST_GROUP, '{DAV:}read', $acl); - $this->assertAccess($groupCanWrite, self::UNIT_TEST_GROUP, '{DAV:}write', $acl); $this->assertEquals(self::UNIT_TEST_USER, $calendar->getOwner()); // test acls on the child @@ -178,8 +176,6 @@ EOD; $this->assertAcl(self::UNIT_TEST_USER, '{DAV:}write', $acl); $this->assertAccess($userCanRead, self::UNIT_TEST_USER1, '{DAV:}read', $acl); $this->assertAccess($userCanWrite, self::UNIT_TEST_USER1, '{DAV:}write', $acl); - $this->assertAccess($groupCanRead, self::UNIT_TEST_GROUP, '{DAV:}read', $acl); - $this->assertAccess($groupCanWrite, self::UNIT_TEST_GROUP, '{DAV:}write', $acl); // delete the address book $this->dispatcher->expects($this->at(0)) diff --git a/apps/dav/tests/unit/CardDAV/AddressBookTest.php b/apps/dav/tests/unit/CardDAV/AddressBookTest.php index 22992d564f6..132fa4796db 100644 --- a/apps/dav/tests/unit/CardDAV/AddressBookTest.php +++ b/apps/dav/tests/unit/CardDAV/AddressBookTest.php @@ -40,6 +40,7 @@ class AddressBookTest extends TestCase { ]); $calendarInfo = [ '{http://owncloud.org/ns}owner-principal' => 'user1', + '{DAV:}displayname' => 'Test address book', 'principaluri' => 'user2', 'id' => 666, 'uri' => 'default', @@ -61,6 +62,7 @@ class AddressBookTest extends TestCase { ]); $calendarInfo = [ '{http://owncloud.org/ns}owner-principal' => 'user1', + '{DAV:}displayname' => 'Test address book', 'principaluri' => 'user2', 'id' => 666, 'uri' => 'default', @@ -78,6 +80,7 @@ class AddressBookTest extends TestCase { $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock(); $calendarInfo = [ '{http://owncloud.org/ns}owner-principal' => 'user1', + '{DAV:}displayname' => 'Test address book', 'principaluri' => 'user2', 'id' => 666, 'uri' => 'default', @@ -95,6 +98,7 @@ class AddressBookTest extends TestCase { $backend = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock(); $backend->expects($this->any())->method('applyShareAcl')->willReturnArgument(1); $calendarInfo = [ + '{DAV:}displayname' => 'Test address book', 'principaluri' => 'user2', 'id' => 666, 'uri' => 'default' diff --git a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php index 062ef72dbf0..a6f0384cc38 100644 --- a/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php +++ b/apps/dav/tests/unit/CardDAV/ContactsManagerTest.php @@ -39,7 +39,7 @@ class ContactsManagerTest extends TestCase { /** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backEnd */ $backEnd = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock(); $backEnd->method('getAddressBooksForUser')->willReturn([ - ['uri' => 'default'], + ['{DAV:}displayname' => 'Test address book', 'uri' => 'default'], ]); $l = $this->createMock(IL10N::class); diff --git a/apps/files_external/l10n/pl.js b/apps/files_external/l10n/pl.js index 3bcf24d26c0..d35058891e3 100644 --- a/apps/files_external/l10n/pl.js +++ b/apps/files_external/l10n/pl.js @@ -38,6 +38,7 @@ OC.L10N.register( "Credentials saved" : "Poświadczenia zapisane", "Credentials saving failed" : "Poświadczenia oszczędności nie powiodło się", "Credentials required" : "Wymagane poświadczenia", + "Storage with ID \"%d\" not found" : "Nie znaleziono magazynu o ID \"%d\"", "Invalid backend or authentication mechanism class" : "Nieprawidłowy mechanizm uwierzytelniania powrotu lub klasy", "Invalid mount point" : "Nieprawidłowy punkt montowania", "Objectstore forbidden" : "Zabroniony obiekt sklepu", @@ -48,6 +49,7 @@ OC.L10N.register( "Unsatisfied authentication mechanism parameters" : "Niepoprawne parametry mechanizmu uwierzytelnienia", "Insufficient data: %s" : "Niewystarczające dane: %s", "%s" : "%s", + "Storage with ID \"%d\" is not user editable" : "Magazyn o ID \"%d\" nie może być edytowany przez użytkownika", "Access key" : "Klucz dostępu", "Secret key" : "Sekretny klucz", "Builtin" : "Wbudowane", diff --git a/apps/files_external/l10n/pl.json b/apps/files_external/l10n/pl.json index dd78d06dcbb..3de59414e30 100644 --- a/apps/files_external/l10n/pl.json +++ b/apps/files_external/l10n/pl.json @@ -36,6 +36,7 @@ "Credentials saved" : "Poświadczenia zapisane", "Credentials saving failed" : "Poświadczenia oszczędności nie powiodło się", "Credentials required" : "Wymagane poświadczenia", + "Storage with ID \"%d\" not found" : "Nie znaleziono magazynu o ID \"%d\"", "Invalid backend or authentication mechanism class" : "Nieprawidłowy mechanizm uwierzytelniania powrotu lub klasy", "Invalid mount point" : "Nieprawidłowy punkt montowania", "Objectstore forbidden" : "Zabroniony obiekt sklepu", @@ -46,6 +47,7 @@ "Unsatisfied authentication mechanism parameters" : "Niepoprawne parametry mechanizmu uwierzytelnienia", "Insufficient data: %s" : "Niewystarczające dane: %s", "%s" : "%s", + "Storage with ID \"%d\" is not user editable" : "Magazyn o ID \"%d\" nie może być edytowany przez użytkownika", "Access key" : "Klucz dostępu", "Secret key" : "Sekretny klucz", "Builtin" : "Wbudowane", diff --git a/apps/files_external/lib/Command/Config.php b/apps/files_external/lib/Command/Config.php index 362f0a0f2b5..5df6f7ee650 100644 --- a/apps/files_external/lib/Command/Config.php +++ b/apps/files_external/lib/Command/Config.php @@ -91,9 +91,9 @@ class Config extends Base { } else { $value = $mount->getBackendOption($key); } - if (!is_string($value)) { // show bools and objects correctly - $value = json_encode($value); - } + if (!is_string($value) && json_decode(json_encode($value)) === $value) { // show bools and objects correctly + $value = json_encode($value); + } $output->writeln($value); } @@ -105,9 +105,9 @@ class Config extends Base { */ protected function setOption(StorageConfig $mount, $key, $value, OutputInterface $output) { $decoded = json_decode($value, true); - if (!is_null($decoded)) { - $value = $decoded; - } + if (!is_null($decoded) && json_encode($decoded) === $value) { + $value = $decoded; + } if ($key === 'mountpoint' || $key === 'mount_point') { $mount->setMountPoint($value); } else { diff --git a/apps/files_sharing/css/sharetabview.scss b/apps/files_sharing/css/sharetabview.scss index 6fd2b9b3165..82f0ffa5822 100644 --- a/apps/files_sharing/css/sharetabview.scss +++ b/apps/files_sharing/css/sharetabview.scss @@ -40,6 +40,17 @@ padding: 18px 0 18px 36px; } +/* fix clickable area because in the share tab popover the label is inside the actual menu item*/ +#shareWithList .popovermenu .shareOption { + padding-right: 0 !important; +} +/* fix clickable area because in the share tab popover the label is inside the actual menu item*/ +.shareTabView .popovermenu label { + width: 100%; + display: inline-block; + padding: 0 10px 0 0 !important; +} + .shareTabView label { white-space: nowrap; } diff --git a/apps/files_sharing/js/files_drop.js b/apps/files_sharing/js/files_drop.js index 64051844d03..ddfcfcd3d8b 100644 --- a/apps/files_sharing/js/files_drop.js +++ b/apps/files_sharing/js/files_drop.js @@ -20,9 +20,11 @@ var Drop = { /** @type {Function} **/ _template: undefined, - - initialize: function () { - + + addFileToUpload: function(e, data) { + var errors = []; + var output = this.template(); + var filesClient = new OC.Files.Client({ host: OC.getHost(), port: OC.getPort(), @@ -32,7 +34,45 @@ root: OC.getRootPath() + '/public.php/webdav', useHTTPS: OC.getProtocol() === 'https' }); - + + var name = data.files[0].name; + try { + // FIXME: not so elegant... need to refactor that method to return a value + Files.isFileNameValid(name); + } + catch (errorMessage) { + OC.Notification.show(errorMessage, {type: 'error'}); + return false; + } + var base = OC.getProtocol() + '://' + OC.getHost(); + data.url = base + OC.getRootPath() + '/public.php/webdav/' + encodeURI(name); + + data.multipart = false; + + if (!data.headers) { + data.headers = {}; + } + + var userName = filesClient.getUserName(); + var password = filesClient.getPassword(); + if (userName) { + // copy username/password from DAV client + data.headers['Authorization'] = + 'Basic ' + btoa(userName + ':' + (password || '')); + } + + $('#drop-upload-done-indicator').addClass('hidden'); + $('#drop-upload-progress-indicator').removeClass('hidden'); + _.each(data['files'], function(file) { + $('#public-upload ul').append(output({isUploading: true, name: escapeHTML(file.name)})); + $('[data-toggle="tooltip"]').tooltip(); + data.submit(); + }); + + return true; + }, + + initialize: function () { $(document).bind('drop dragover', function (e) { // Prevent the default browser drop action: e.preventDefault(); @@ -43,35 +83,9 @@ dropZone: $('#public-upload'), sequentialUploads: true, add: function(e, data) { - var errors = []; - - var name = data.files[0].name; - - var base = OC.getProtocol() + '://' + OC.getHost(); - data.url = base + OC.getRootPath() + '/public.php/webdav/' + encodeURI(name); - - data.multipart = false; - - if (!data.headers) { - data.headers = {}; - } - - var userName = filesClient.getUserName(); - var password = filesClient.getPassword(); - if (userName) { - // copy username/password from DAV client - data.headers['Authorization'] = - 'Basic ' + btoa(userName + ':' + (password || '')); - } - - $('#drop-upload-done-indicator').addClass('hidden'); - $('#drop-upload-progress-indicator').removeClass('hidden'); - _.each(data['files'], function(file) { - $('#public-upload ul').append(output({isUploading: true, name: escapeHTML(file.name)})); - $('[data-toggle="tooltip"]').tooltip(); - data.submit(); - }); - + Drop.addFileToUpload(e, data); + //we return true to keep trying to upload next file even + //if addFileToUpload did not like the privious one return true; }, done: function(e, data) { @@ -116,15 +130,13 @@ } }; + OCA.FilesSharingDrop = Drop; + $(document).ready(function() { if($('#upload-only-interface').val() === "1") { $('.avatardiv').avatar($('#sharingUserId').val(), 128, true); } - OCA.Files_Sharing_Drop = Drop; - OCA.Files_Sharing_Drop.initialize(); + OCA.FilesSharingDrop.initialize(); }); - - })(jQuery); - diff --git a/apps/files_sharing/tests/js/fileDropSpec.js b/apps/files_sharing/tests/js/fileDropSpec.js new file mode 100644 index 00000000000..22bb95878b4 --- /dev/null +++ b/apps/files_sharing/tests/js/fileDropSpec.js @@ -0,0 +1,98 @@ +/** + * + * @copyright Copyright (c) 2017, Artur Neumann (info@individual-it.net) + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +describe("files Drop tests", function() { + //some testing data + var sharingToken = "fVCiSMhScgWfiuv"; + var testFiles = [ + { name: 'test.txt', expectedValidationResult: true }, + { name: 'testनेपाल.txt', expectedValidationResult: true }, + { name: 'test.part', expectedValidationResult: false }, + { name: 'test.filepart', expectedValidationResult: false }, + { name: '.', expectedValidationResult: false }, + { name: '..', expectedValidationResult: false }, + ]; + + //this pre/post positions should not change the result of the file name validation + var prePostPositions = [""," "," "," "]; + + //use the testFiles and the pre/post positions to generate more testing data + var replicatedTestFiles = []; + prePostPositions.map(function (prePostPosition) { + testFiles.map(function (testFile) { + replicatedTestFiles.push( + { + name: testFile.name + prePostPosition, + expectedValidationResult: testFile.expectedValidationResult + } + ); + replicatedTestFiles.push( + { + name: prePostPosition + testFile.name, + expectedValidationResult: testFile.expectedValidationResult + } + ); + replicatedTestFiles.push( + { + name: prePostPosition + testFile.name + prePostPosition, + expectedValidationResult: testFile.expectedValidationResult + } + ); + }); + }); + + beforeEach (function () { + //fake input for the sharing token + $('#testArea').append( + '<input name="sharingToken" value="" id="sharingToken" type="hidden">' + ); + }); + + + replicatedTestFiles.map(function (testFile) { + it("validates the filenames correctly", function() { + data = { + 'submit': function() {}, + 'files': [testFile] + } + expect(OCA.FilesSharingDrop.addFileToUpload('',data)). + toBe( + testFile.expectedValidationResult, + 'wrongly validated file named "'+testFile.name+'"' + ); + }); + + if (testFile.expectedValidationResult === true) { + it("should set correct PUT URL, Auth header and submit", function () { + data = { + 'submit': sinon.stub(), + 'files': [testFile] + } + $('#sharingToken').val(sharingToken); + + OCA.FilesSharingDrop.addFileToUpload('',data); + expect(data.submit.calledOnce).toEqual(true); + expect(data.url).toContain("/public.php/webdav/" + encodeURI(testFile.name)); + expect(data.headers['Authorization']).toEqual('Basic ' + btoa(sharingToken+":")); + }); + } + }); +}); diff --git a/apps/sharebymail/css/settings-admin.css b/apps/sharebymail/css/settings-admin.css new file mode 100644 index 00000000000..c7bfb122f3e --- /dev/null +++ b/apps/sharebymail/css/settings-admin.css @@ -0,0 +1,3 @@ +#ncShareByMailSettings p { + padding-top: 10px; +} diff --git a/apps/sharebymail/js/settings-admin.js b/apps/sharebymail/js/settings-admin.js index 7b431233032..35a0e9855ac 100644 --- a/apps/sharebymail/js/settings-admin.js +++ b/apps/sharebymail/js/settings-admin.js @@ -24,7 +24,15 @@ $(function() { if ($(this).is(':checked')) { status = 'yes'; } - OC.AppConfig.setValue('sharebymail', 'sendpasswordmail', status); + OCP.AppConfig.setValue('sharebymail', 'sendpasswordmail', status); + }); + + $('#enforcePasswordProtection').on('change', function() { + var status = 'no'; + if ($(this).is(':checked')) { + status = 'yes'; + } + OCP.AppConfig.setValue('sharebymail', 'enforcePasswordProtection', status); }); }); diff --git a/apps/sharebymail/lib/Activity.php b/apps/sharebymail/lib/Activity.php index 1c2f37dc382..acc3e59f923 100644 --- a/apps/sharebymail/lib/Activity.php +++ b/apps/sharebymail/lib/Activity.php @@ -58,6 +58,8 @@ class Activity implements IProvider { const SUBJECT_SHARED_EMAIL_SELF = 'shared_with_email_self'; const SUBJECT_SHARED_EMAIL_BY = 'shared_with_email_by'; + const SUBJECT_SHARED_EMAIL_PASSWORD_SEND = 'shared_with_email_password_send'; + const SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF = 'shared_with_email_password_send_self'; /** * @param IFactory $languageFactory @@ -119,15 +121,26 @@ class Activity implements IProvider { ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_BY) { $event->setParsedSubject($this->l->t('Shared with %1$s by %2$s', [ - $parsedParameters['email']['name'], - $parsedParameters['actor']['name'], - ])) + $parsedParameters['email']['name'], + $parsedParameters['actor']['name'], + ])) ->setRichSubject($this->l->t('Shared with {email} by {actor}'), [ 'email' => $parsedParameters['email'], 'actor' => $parsedParameters['actor'], ]) ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); - + } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND) { + $event->setParsedSubject($this->l->t('Password for mail share send to %1$s', [ + $parsedParameters['email']['name'] + ])) + ->setRichSubject($this->l->t('Password for mail share send to {email}'), [ + 'email' => $parsedParameters['email'] + ]) + ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); + } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF) { + $event->setParsedSubject($this->l->t('Password for mail share send to you')) + ->setRichSubject($this->l->t('Password for mail share send to you')) + ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } else { throw new \InvalidArgumentException(); } @@ -153,12 +166,26 @@ class Activity implements IProvider { ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_BY) { $event->setParsedSubject($this->l->t('%3$s shared %1$s with %2$s by mail', [ - $parsedParameters['file']['path'], - $parsedParameters['email']['name'], - $parsedParameters['actor']['name'], - ])) + $parsedParameters['file']['path'], + $parsedParameters['email']['name'], + $parsedParameters['actor']['name'], + ])) ->setRichSubject($this->l->t('{actor} shared {file} with {email} by mail'), $parsedParameters) ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); + } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND) { + $event->setParsedSubject($this->l->t('Password to access %1$s was send to %2s', [ + $parsedParameters['file']['path'], + $parsedParameters['email']['name'] + ])) + ->setRichSubject($this->l->t('Password to access {file} was send to {email}'), $parsedParameters) + ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); + } else if ($event->getSubject() === self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF) { + $event->setParsedSubject( + $this->l->t('Password to access %1$s was send to you', + [$parsedParameters['file']['path']])) + ->setRichSubject($this->l->t('Password to access {file} was send to you'), $parsedParameters) + ->setIcon($this->url->getAbsoluteURL($this->url->imagePath('core', 'actions/share.svg'))); + } else { throw new \InvalidArgumentException(); } @@ -182,6 +209,15 @@ class Activity implements IProvider { 'email' => $this->generateEmailParameter($parameters[1]), 'actor' => $this->generateUserParameter($parameters[2]), ]; + case self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND: + return [ + 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), + 'email' => $this->generateEmailParameter($parameters[1]), + ]; + case self::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF: + return [ + 'file' => $this->generateFileParameter((int) $event->getObjectId(), $parameters[0]), + ]; } throw new \InvalidArgumentException(); } diff --git a/apps/sharebymail/lib/AppInfo/Application.php b/apps/sharebymail/lib/AppInfo/Application.php index 98febf9dad7..12419a8c3d9 100644 --- a/apps/sharebymail/lib/AppInfo/Application.php +++ b/apps/sharebymail/lib/AppInfo/Application.php @@ -32,7 +32,8 @@ class Application extends App { public function __construct(array $urlParams = array()) { parent::__construct('sharebymail', $urlParams); - $settings = new Settings(); + $settingsManager = \OC::$server->query(Settings\SettingsManager::class); + $settings = new Settings($settingsManager); /** register capabilities */ $container = $this->getContainer(); @@ -40,6 +41,7 @@ class Application extends App { /** register hooks */ Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareProvider'); + Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareByMailSettings'); } } diff --git a/apps/sharebymail/lib/Settings.php b/apps/sharebymail/lib/Settings.php index 4ab1622425b..e032bc43ff1 100644 --- a/apps/sharebymail/lib/Settings.php +++ b/apps/sharebymail/lib/Settings.php @@ -23,8 +23,17 @@ namespace OCA\ShareByMail; +use OCA\ShareByMail\Settings\SettingsManager; + class Settings { + /** @var SettingsManager */ + private $settingsManager; + + public function __construct(SettingsManager $settingsManager) { + $this->settingsManager = $settingsManager; + } + /** * announce that the share-by-mail share provider is enabled * @@ -35,4 +44,10 @@ class Settings { $array['shareByMailEnabled'] = true; $settings['array']['oc_appconfig'] = json_encode($array); } + + public function announceShareByMailSettings(array $settings) { + $array = json_decode($settings['array']['oc_appconfig'], true); + $array['shareByMail']['enforcePasswordProtection'] = $this->settingsManager->enforcePasswordProtection(); + $settings['array']['oc_appconfig'] = json_encode($array); + } } diff --git a/apps/sharebymail/lib/Settings/Admin.php b/apps/sharebymail/lib/Settings/Admin.php index b6e7e5d3b4a..93a8d3aafa4 100644 --- a/apps/sharebymail/lib/Settings/Admin.php +++ b/apps/sharebymail/lib/Settings/Admin.php @@ -40,7 +40,8 @@ class Admin implements ISettings { public function getForm() { $parameters = [ - 'sendPasswordMail' => $this->settingsManager->sendPasswordByMail() + 'sendPasswordMail' => $this->settingsManager->sendPasswordByMail(), + 'enforcePasswordProtection' => $this->settingsManager->enforcePasswordProtection() ]; return new TemplateResponse('sharebymail', 'settings-admin', $parameters, ''); diff --git a/apps/sharebymail/lib/Settings/SettingsManager.php b/apps/sharebymail/lib/Settings/SettingsManager.php index 205b253f337..2b35e5833a7 100644 --- a/apps/sharebymail/lib/Settings/SettingsManager.php +++ b/apps/sharebymail/lib/Settings/SettingsManager.php @@ -30,7 +30,9 @@ class SettingsManager { /** @var IConfig */ private $config; - private $defaultSetting = 'yes'; + private $sendPasswordByMailDefault = 'yes'; + + private $enforcePasswordProtectionDefault = 'no'; public function __construct(IConfig $config) { $this->config = $config; @@ -42,8 +44,18 @@ class SettingsManager { * @return bool */ public function sendPasswordByMail() { - $sendPasswordByMail = $this->config->getAppValue('sharebymail', 'sendpasswordmail', $this->defaultSetting); + $sendPasswordByMail = $this->config->getAppValue('sharebymail', 'sendpasswordmail', $this->sendPasswordByMailDefault); return $sendPasswordByMail === 'yes'; } + /** + * do we require a share by mail to be password protected + * + * @return bool + */ + public function enforcePasswordProtection() { + $enforcePassword = $this->config->getAppValue('sharebymail', 'enforcePasswordProtection', $this->enforcePasswordProtectionDefault); + return $enforcePassword === 'yes'; + } + } diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 767bdc86a4a..7e0f7c5071e 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -21,6 +21,7 @@ namespace OCA\ShareByMail; +use OC\CapabilitiesManager; use OC\HintException; use OC\Share20\Exception\InvalidShare; use OCA\ShareByMail\Settings\SettingsManager; @@ -37,12 +38,12 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\IUserManager; use OCP\Mail\IMailer; +use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OC\Share20\Share; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; -use OCP\Template; /** * Class ShareByMail @@ -84,6 +85,12 @@ class ShareByMailProvider implements IShareProvider { /** @var Defaults */ private $defaults; + /** @var IHasher */ + private $hasher; + + /** @var CapabilitiesManager */ + private $capabilitiesManager; + /** * Return the identifier of this provider. * @@ -107,6 +114,8 @@ class ShareByMailProvider implements IShareProvider { * @param IManager $activityManager * @param SettingsManager $settingsManager * @param Defaults $defaults + * @param IHasher $hasher + * @param CapabilitiesManager $capabilitiesManager */ public function __construct( IDBConnection $connection, @@ -119,7 +128,9 @@ class ShareByMailProvider implements IShareProvider { IURLGenerator $urlGenerator, IManager $activityManager, SettingsManager $settingsManager, - Defaults $defaults + Defaults $defaults, + IHasher $hasher, + CapabilitiesManager $capabilitiesManager ) { $this->dbConnection = $connection; $this->secureRandom = $secureRandom; @@ -132,6 +143,8 @@ class ShareByMailProvider implements IShareProvider { $this->activityManager = $activityManager; $this->settingsManager = $settingsManager; $this->defaults = $defaults; + $this->hasher = $hasher; + $this->capabilitiesManager = $capabilitiesManager; } /** @@ -156,19 +169,80 @@ class ShareByMailProvider implements IShareProvider { throw new \Exception($message_t); } + // if the admin enforces a password for all mail shares we create a + // random password and send it to the recipient + $password = ''; + $passwordEnforced = $this->settingsManager->enforcePasswordProtection(); + if ($passwordEnforced) { + $password = $this->autoGeneratePassword($share); + } + $shareId = $this->createMailShare($share); - $this->createActivity($share); + $send = $this->sendPassword($share, $password); + if ($passwordEnforced && $send === false) { + $this->sendPasswordToOwner($share, $password); + } + + $this->createShareActivity($share); $data = $this->getRawShare($shareId); + return $this->createShareObject($data); } /** + * auto generate password in case of password enforcement on mail shares + * + * @param IShare $share + * @return string + * @throws \Exception + */ + protected function autoGeneratePassword($share) { + $initiatorUser = $this->userManager->get($share->getSharedBy()); + $initiatorEMailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; + $allowPasswordByMail = $this->settingsManager->sendPasswordByMail(); + + if ($initiatorEMailAddress === null && !$allowPasswordByMail) { + throw new \Exception( + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") + ); + } + + $passwordPolicy = $this->getPasswordPolicy(); + $passwordCharset = ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS; + $passwordLength = 8; + if (!empty($passwordPolicy)) { + $passwordLength = (int)$passwordPolicy['minLength'] > 0 ? (int)$passwordPolicy['minLength'] : $passwordLength; + $passwordCharset .= $passwordPolicy['enforceSpecialCharacters'] ? ISecureRandom::CHAR_SYMBOLS : ''; + } + + $password = $this->secureRandom->generate($passwordLength, $passwordCharset); + + $share->setPassword($this->hasher->hash($password)); + + return $password; + } + + /** + * get password policy + * + * @return array + */ + protected function getPasswordPolicy() { + $capabilities = $this->capabilitiesManager->getCapabilities(); + if (isset($capabilities['password_policy'])) { + return $capabilities['password_policy']; + } + + return []; + } + + /** * create activity if a file/folder was shared by mail * * @param IShare $share */ - protected function createActivity(IShare $share) { + protected function createShareActivity(IShare $share) { $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); @@ -197,6 +271,37 @@ class ShareByMailProvider implements IShareProvider { } /** + * create activity if a file/folder was shared by mail + * + * @param IShare $share + * @param string $sharedWith + * @param bool $sendToSelf + */ + protected function createPasswordSendActivity(IShare $share, $sharedWith, $sendToSelf) { + + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); + + if ($sendToSelf) { + $this->publishActivity( + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND_SELF, + [$userFolder->getRelativePath($share->getNode()->getPath())], + $share->getSharedBy(), + $share->getNode()->getId(), + $userFolder->getRelativePath($share->getNode()->getPath()) + ); + } else { + $this->publishActivity( + Activity::SUBJECT_SHARED_EMAIL_PASSWORD_SEND, + [$userFolder->getRelativePath($share->getNode()->getPath()), $sharedWith], + $share->getSharedBy(), + $share->getNode()->getId(), + $userFolder->getRelativePath($share->getNode()->getPath()) + ); + } + } + + + /** * publish activity if a file/folder was shared by mail * * @param $subject @@ -230,7 +335,8 @@ class ShareByMailProvider implements IShareProvider { $share->getSharedBy(), $share->getShareOwner(), $share->getPermissions(), - $share->getToken() + $share->getToken(), + $share->getPassword() ); try { @@ -287,18 +393,15 @@ class ShareByMailProvider implements IShareProvider { $emailTemplate->addHeader(); $emailTemplate->addHeading($this->l->t('%s shared »%s« with you', [$ownerDisplayName, $filename]), false); - if ($owner === $initiator) { $text = $this->l->t('%s shared »%s« with you.', [$ownerDisplayName, $filename]); } else { $text= $this->l->t('%s shared »%s« with you on behalf of %s.', [$ownerDisplayName, $filename, $initiator]); } - $emailTemplate->addBodyText( $text . ' ' . $this->l->t('Click the button below to open it.'), $text ); - $emailTemplate->addBodyButton( $this->l->t('Open »%s«', [$filename]), $link @@ -336,52 +439,109 @@ class ShareByMailProvider implements IShareProvider { /** * send password to recipient of a mail share * - * @param string $filename - * @param string $initiator - * @param string $shareWith + * @param IShare $share + * @param string $password + * @return bool */ - protected function sendPassword($filename, $initiator, $shareWith, $password) { + protected function sendPassword(IShare $share, $password) { - if ($this->settingsManager->sendPasswordByMail() === false) { - return; + $filename = $share->getNode()->getName(); + $initiator = $share->getSharedBy(); + $shareWith = $share->getSharedWith(); + + if ($password === '' || $this->settingsManager->sendPasswordByMail() === false) { + return false; } $initiatorUser = $this->userManager->get($initiator); $initiatorDisplayName = ($initiatorUser instanceof IUser) ? $initiatorUser->getDisplayName() : $initiator; + $initiatorEmailAddress = ($initiatorUser instanceof IUser) ? $initiatorUser->getEMailAddress() : null; + $subject = (string)$this->l->t('Password to access »%s« shared to you by %s', [$filename, $initiatorDisplayName]); + $plainBodyPart = $this->l->t("%s shared »%s« with you.\nYou should have already received a separate mail with a link to access it.\n", [$initiatorDisplayName, $filename]); + $htmlBodyPart = $this->l->t('%s shared »%s« with you. You should have already received a separate mail with a link to access it.', [$initiatorDisplayName, $filename]); $message = $this->mailer->createMessage(); $emailTemplate = $this->mailer->createEMailTemplate(); - $emailTemplate->addHeader(); - $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename])); - - $emailTemplate->addBodyText($this->l->t( - '%s shared »%s« with you. You should have already received a separate mail with a link to access it.', - [$initiatorDisplayName, $filename] - )); + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); + $emailTemplate->addBodyText($htmlBodyPart, $plainBodyPart); $emailTemplate->addBodyText($this->l->t('It is protected with the following password: %s', [$password])); - $emailTemplate->addFooter(); + if ($initiatorEmailAddress !== null) { + $message->setFrom([$initiatorEmailAddress => $initiatorDisplayName]); + } $message->setTo([$shareWith]); $message->setSubject($subject); $message->setBody($emailTemplate->renderText(), 'text/plain'); $message->setHtmlBody($emailTemplate->renderHtml()); $this->mailer->send($message); + $this->createPasswordSendActivity($share, $shareWith, false); + + return true; } + /** + * send auto generated password to the owner. This happens if the admin enforces + * a password for mail shares and forbid to send the password by mail to the recipient + * + * @param IShare $share + * @param string $password + * @return bool + * @throws \Exception + */ + protected function sendPasswordToOwner(IShare $share, $password) { + + $filename = $share->getNode()->getName(); + $initiator = $this->userManager->get($share->getSharedBy()); + $initiatorEMailAddress = ($initiator instanceof IUser) ? $initiator->getEMailAddress() : null; + $initiatorDisplayName = ($initiator instanceof IUser) ? $initiator->getDisplayName() : $share->getSharedBy(); + $shareWith = $share->getSharedWith(); + + if ($initiatorEMailAddress === null) { + throw new \Exception( + $this->l->t("We can't send you the auto-generated password. Please set a valid email address in your personal settings and try again.") + ); + } + + $subject = (string)$this->l->t('Password to access »%s« shared with %s', [$filename, $shareWith]); + $bodyPart = $this->l->t("You just shared »%s« with %s. The share was already send to the recipient. Due to the security policies defined by the administrator of %s each share needs to be protected by password and it is not allowed to send the password directly to the recipient. Therefore you need to forward the password manually to the recipient.", [$filename, $shareWith, $this->defaults->getName()]); + + $message = $this->mailer->createMessage(); + $emailTemplate = $this->mailer->createEMailTemplate(); + + $emailTemplate->addHeader(); + $emailTemplate->addHeading($this->l->t('Password to access »%s«', [$filename]), false); + $emailTemplate->addBodyText($bodyPart); + $emailTemplate->addBodyText($this->l->t('This is the password: %s', [$password])); + $emailTemplate->addBodyText($this->l->t('You can choose a different password at any time in the share dialog.')); + $emailTemplate->addFooter(); + + if ($initiatorEMailAddress) { + $message->setFrom([$initiatorEMailAddress => $initiatorDisplayName]); + } + $message->setTo([$initiatorEMailAddress => $initiatorDisplayName]); + $message->setSubject($subject); + $message->setBody($emailTemplate->renderText(), 'text/plain'); + $message->setHtmlBody($emailTemplate->renderHtml()); + $this->mailer->send($message); + + $this->createPasswordSendActivity($share, $shareWith, true); + + return true; + } /** * generate share token * * @return string */ - protected function generateToken() { + protected function generateToken($size = 15) { $token = $this->secureRandom->generate( - 15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); + $size, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); return $token; } @@ -422,7 +582,7 @@ class ShareByMailProvider implements IShareProvider { * @param string $token * @return int */ - protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { + protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $password) { $qb = $this->dbConnection->getQueryBuilder(); $qb->insert('share') ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) @@ -434,6 +594,7 @@ class ShareByMailProvider implements IShareProvider { ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) ->setValue('permissions', $qb->createNamedParameter($permissions)) ->setValue('token', $qb->createNamedParameter($token)) + ->setValue('password', $qb->createNamedParameter($password)) ->setValue('stime', $qb->createNamedParameter(time())); /* @@ -463,7 +624,7 @@ class ShareByMailProvider implements IShareProvider { $validPassword = $plainTextPassword !== null && $plainTextPassword !== ''; if($validPassword && $originalShare->getPassword() !== $share->getPassword()) { - $this->sendPassword($share->getNode()->getName(), $share->getSharedBy(), $share->getSharedWith(), $plainTextPassword); + $this->sendPassword($share, $plainTextPassword); } /* * We allow updating the permissions and password of mail shares diff --git a/apps/sharebymail/templates/settings-admin.php b/apps/sharebymail/templates/settings-admin.php index c4e41086063..3af98741e52 100644 --- a/apps/sharebymail/templates/settings-admin.php +++ b/apps/sharebymail/templates/settings-admin.php @@ -4,6 +4,7 @@ use OCA\Federation\TrustedServers; /** @var \OCP\IL10N $l */ script('sharebymail', 'settings-admin'); +style('sharebymail', 'settings-admin'); ?> <div id="ncShareByMailSettings" class="section"> <h2><?php p($l->t('Share by mail')); ?></h2> @@ -11,7 +12,9 @@ script('sharebymail', 'settings-admin'); <p> <input id="sendPasswordMail" type="checkbox" class="checkbox" <?php if($_['sendPasswordMail']) p('checked'); ?> /> - <label for="sendPasswordMail"><?php p($l->t('Send password by mail')); ?></label> + <label for="sendPasswordMail"><?php p($l->t('Send password by mail')); ?></label><br/> + <input id="enforcePasswordProtection" type="checkbox" class="checkbox" <?php if($_['enforcePasswordProtection']) p('checked'); ?> /> + <label for="enforcePasswordProtection"><?php p($l->t('Enforce password protection')); ?></label> </p> </div> diff --git a/apps/sharebymail/tests/SettingsTest.php b/apps/sharebymail/tests/SettingsTest.php index f415421b0cf..8b2fc200d57 100644 --- a/apps/sharebymail/tests/SettingsTest.php +++ b/apps/sharebymail/tests/SettingsTest.php @@ -24,6 +24,7 @@ namespace OCA\ShareByMail\Tests; use OCA\ShareByMail\Settings; +use OCA\ShareByMail\Settings\SettingsManager; use Test\TestCase; class SettingsTest extends TestCase { @@ -31,10 +32,15 @@ class SettingsTest extends TestCase { /** @var Settings */ private $instance; + /** @var SettingsManager | \PHPUnit_Framework_MockObject_MockObject */ + private $settingsManager; + public function setUp() { parent::setUp(); - $this->instance = new Settings(); + $this->settingsManager = $this->getMockBuilder(SettingsManager::class) + ->disableOriginalConstructor()->getMock(); + $this->instance = new Settings($this->settingsManager); } public function testAnnounceShareProvider() { @@ -61,4 +67,30 @@ class SettingsTest extends TestCase { $this->assertSame($after, $before); } + + public function testAnnounceShareByMailSettings() { + $this->settingsManager->expects($this->once())->method('enforcePasswordProtection')->willReturn(true); + $before = [ + 'oc_appconfig' => + json_encode([ + 'key1' => 'value1', + 'key2' => 'value2' + ]), + 'oc_foo' => 'oc_bar' + ]; + + $after = [ + 'oc_appconfig' => + json_encode([ + 'key1' => 'value1', + 'key2' => 'value2', + 'shareByMail' => ['enforcePasswordProtection' => true] + ]), + 'oc_foo' => 'oc_bar' + ]; + + $this->instance->announceShareByMailSettings(['array' => &$before]); + $this->assertSame($after, $before); + } + } diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php index 581ca9b1b95..269f8e8f414 100644 --- a/apps/sharebymail/tests/ShareByMailProviderTest.php +++ b/apps/sharebymail/tests/ShareByMailProviderTest.php @@ -23,10 +23,12 @@ namespace OCA\ShareByMail\Tests; +use OC\CapabilitiesManager; use OC\Mail\Message; use OCA\ShareByMail\Settings\SettingsManager; use OCA\ShareByMail\ShareByMailProvider; use OCP\Defaults; +use OCP\Files\File; use OCP\Files\IRootFolder; use OCP\IDBConnection; use OCP\IL10N; @@ -36,6 +38,7 @@ use OCP\IUser; use OCP\IUserManager; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; +use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OCP\Share\IManager; use OCP\Share\IShare; @@ -88,6 +91,12 @@ class ShareByMailProviderTest extends TestCase { /** @var Defaults|\PHPUnit_Framework_MockObject_MockObject */ private $defaults; + /** @var IHasher | \PHPUnit_Framework_MockObject_MockObject */ + private $hasher; + + /** @var CapabilitiesManager | \PHPUnit_Framework_MockObject_MockObject */ + private $capabilitiesManager; + public function setUp() { parent::setUp(); @@ -109,6 +118,8 @@ class ShareByMailProviderTest extends TestCase { $this->activityManager = $this->getMockBuilder('OCP\Activity\IManager')->getMock(); $this->settingsManager = $this->getMockBuilder(SettingsManager::class)->disableOriginalConstructor()->getMock(); $this->defaults = $this->createMock(Defaults::class); + $this->hasher = $this->getMockBuilder(IHasher::class)->getMock(); + $this->capabilitiesManager = $this->getMockBuilder(CapabilitiesManager::class)->disableOriginalConstructor()->getMock(); $this->userManager->expects($this->any())->method('userExists')->willReturn(true); } @@ -134,7 +145,9 @@ class ShareByMailProviderTest extends TestCase { $this->urlGenerator, $this->activityManager, $this->settingsManager, - $this->defaults + $this->defaults, + $this->hasher, + $this->capabilitiesManager ] ); @@ -154,7 +167,9 @@ class ShareByMailProviderTest extends TestCase { $this->urlGenerator, $this->activityManager, $this->settingsManager, - $this->defaults + $this->defaults, + $this->hasher, + $this->capabilitiesManager ); } @@ -167,15 +182,22 @@ class ShareByMailProviderTest extends TestCase { public function testCreate() { $share = $this->getMockBuilder('\OCP\Share\IShare')->getMock(); - $share->expects($this->once())->method('getSharedWith')->willReturn('user1'); + $share->expects($this->any())->method('getSharedWith')->willReturn('user1'); + + $node = $this->getMockBuilder(File::class)->getMock(); + $node->expects($this->any())->method('getName')->willReturn('filename'); - $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createActivity']); + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject', 'createShareActivity', 'sendPassword']); $instance->expects($this->once())->method('getSharedWith')->willReturn([]); $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); - $instance->expects($this->once())->method('createActivity')->with($share); + $instance->expects($this->once())->method('createShareActivity')->with($share); $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn('rawShare'); $instance->expects($this->once())->method('createShareObject')->with('rawShare')->willReturn('shareObject'); + $instance->expects($this->any())->method('sendPassword')->willReturn(true); + $share->expects($this->any())->method('getNode')->willReturn($node); + $this->settingsManager->expects($this->any())->method('enforcePasswordProtection')->willReturn(false); + $this->settingsManager->expects($this->any())->method('sendPasswordByMail')->willReturn(true); $this->assertSame('shareObject', $instance->create($share) @@ -273,6 +295,7 @@ class ShareByMailProviderTest extends TestCase { $uidOwner = 'user2'; $permissions = 1; $token = 'token'; + $password = 'password'; $instance = $this->getInstance(); @@ -286,7 +309,8 @@ class ShareByMailProviderTest extends TestCase { $sharedBy, $uidOwner, $permissions, - $token + $token, + $password ] ); @@ -305,6 +329,7 @@ class ShareByMailProviderTest extends TestCase { $this->assertSame($uidOwner, $result[0]['uid_owner']); $this->assertSame($permissions, (int)$result[0]['permissions']); $this->assertSame($token, $result[0]['token']); + $this->assertSame($password, $result[0]['password']); } @@ -636,7 +661,7 @@ class ShareByMailProviderTest extends TestCase { $userManager = \OC::$server->getUserManager(); $rootFolder = \OC::$server->getRootFolder(); - $provider = $this->getInstance(['sendMailNotification', 'createActivity']); + $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); $u1 = $userManager->createUser('testFed', md5(time())); $u2 = $userManager->createUser('testFed2', md5(time())); @@ -678,7 +703,7 @@ class ShareByMailProviderTest extends TestCase { $userManager = \OC::$server->getUserManager(); $rootFolder = \OC::$server->getRootFolder(); - $provider = $this->getInstance(['sendMailNotification', 'createActivity']); + $provider = $this->getInstance(['sendMailNotification', 'createShareActivity']); $u1 = $userManager->createUser('testFed', md5(time())); $u2 = $userManager->createUser('testFed2', md5(time())); diff --git a/apps/user_ldap/l10n/de.js b/apps/user_ldap/l10n/de.js index 6fc8957a2c5..5c900a76025 100644 --- a/apps/user_ldap/l10n/de.js +++ b/apps/user_ldap/l10n/de.js @@ -65,8 +65,10 @@ OC.L10N.register( "Edit LDAP Query" : "LDAP-Abfrage bearbeiten", "LDAP Filter:" : "LDAP-Filter:", "The filter specifies which LDAP groups shall have access to the %s instance." : "Der Filter bestimmt, welche LDAP-Gruppen Zugriff auf die %s-Instanz haben sollen.", + "Verify settings and count the groups" : "Einstellungen überprüfen und die Gruppen zählen", "When logging in, %s will find the user based on the following attributes:" : "Beim Anmelden wird %s den Nutzer basierend auf folgenden Attributen finden:", "LDAP / AD Username:" : "LDAP-/AD-Benutzername:", + "Allows login against the LDAP / AD username, which is either uid or sAMAccountName and will be detected." : "Erlaubt das Anmelden gegen den LDAP / AD Nutzernamen, welcher entweder eine UID oder sAMAccount-Name ist und automatisch erkannt wird.", "LDAP / AD Email Address:" : "LDAP-/AD-E-Mail-Adresse:", "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "Erlaubt das Anmelden gegen ein E-Mail Attribut. Mail und mailPrimaryAddress sind erlaubt.", "Other Attributes:" : "Andere Attribute:", @@ -75,9 +77,11 @@ OC.L10N.register( "Verify settings" : "Einstellungen überprüfen", "1. Server" : "1. Server", "%s. Server:" : "%s. Server:", + "Add a new configuration" : "Neue Konfiguration hinzufügen", "Copy current configuration into new directory binding" : "Aktuelle Konfiguration in eine neues Verzeichnis-Bind kopieren", "Delete the current configuration" : "Aktuelle Konfiguration löschen", "Host" : "Host", + "You can omit the protocol, unless you require SSL. If so, start with ldaps://" : "Du kannst das Protokoll auslassen, es sei denn, du benötigst SSL. In diesem Fall beginne mit ldaps://", "Port" : "Port", "Detect Port" : "Port ermitteln", "User DN" : "Benutzer-DN", diff --git a/apps/user_ldap/l10n/de.json b/apps/user_ldap/l10n/de.json index 076b0200b97..400f2f416ed 100644 --- a/apps/user_ldap/l10n/de.json +++ b/apps/user_ldap/l10n/de.json @@ -63,8 +63,10 @@ "Edit LDAP Query" : "LDAP-Abfrage bearbeiten", "LDAP Filter:" : "LDAP-Filter:", "The filter specifies which LDAP groups shall have access to the %s instance." : "Der Filter bestimmt, welche LDAP-Gruppen Zugriff auf die %s-Instanz haben sollen.", + "Verify settings and count the groups" : "Einstellungen überprüfen und die Gruppen zählen", "When logging in, %s will find the user based on the following attributes:" : "Beim Anmelden wird %s den Nutzer basierend auf folgenden Attributen finden:", "LDAP / AD Username:" : "LDAP-/AD-Benutzername:", + "Allows login against the LDAP / AD username, which is either uid or sAMAccountName and will be detected." : "Erlaubt das Anmelden gegen den LDAP / AD Nutzernamen, welcher entweder eine UID oder sAMAccount-Name ist und automatisch erkannt wird.", "LDAP / AD Email Address:" : "LDAP-/AD-E-Mail-Adresse:", "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "Erlaubt das Anmelden gegen ein E-Mail Attribut. Mail und mailPrimaryAddress sind erlaubt.", "Other Attributes:" : "Andere Attribute:", @@ -73,9 +75,11 @@ "Verify settings" : "Einstellungen überprüfen", "1. Server" : "1. Server", "%s. Server:" : "%s. Server:", + "Add a new configuration" : "Neue Konfiguration hinzufügen", "Copy current configuration into new directory binding" : "Aktuelle Konfiguration in eine neues Verzeichnis-Bind kopieren", "Delete the current configuration" : "Aktuelle Konfiguration löschen", "Host" : "Host", + "You can omit the protocol, unless you require SSL. If so, start with ldaps://" : "Du kannst das Protokoll auslassen, es sei denn, du benötigst SSL. In diesem Fall beginne mit ldaps://", "Port" : "Port", "Detect Port" : "Port ermitteln", "User DN" : "Benutzer-DN", diff --git a/apps/user_ldap/l10n/fr.js b/apps/user_ldap/l10n/fr.js index da18f011538..03c9a609712 100644 --- a/apps/user_ldap/l10n/fr.js +++ b/apps/user_ldap/l10n/fr.js @@ -68,6 +68,7 @@ OC.L10N.register( "Verify settings and count the groups" : "Vérifier les paramètres et compter les groupes", "When logging in, %s will find the user based on the following attributes:" : "Au login, %s cherchera l'utilisateur sur base de ces attributs :", "LDAP / AD Username:" : "Nom d'utilisateur LDAP / AD :", + "Allows login against the LDAP / AD username, which is either uid or sAMAccountName and will be detected." : "Autorise le login avec le nom d'utilisateur LDAP / AD, ce qui est soit uid ou sAMAccountName et il sera détecté.", "LDAP / AD Email Address:" : "Adresse mail LDAP / AD :", "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "Autoriser le login avec une adresse mail. Mail et mailPrimaryAddress sont autorisés.", "Other Attributes:" : "Autres attributs :", @@ -80,6 +81,7 @@ OC.L10N.register( "Copy current configuration into new directory binding" : "Copier la configuration actuelle vers une nouvelle", "Delete the current configuration" : "Supprimer la configuration actuelle", "Host" : "Hôte", + "You can omit the protocol, unless you require SSL. If so, start with ldaps://" : "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas, préfixez avec ldaps://", "Port" : "Port", "Detect Port" : "Détecter le port", "User DN" : "DN Utilisateur", diff --git a/apps/user_ldap/l10n/fr.json b/apps/user_ldap/l10n/fr.json index cec6ea8d1e8..05276581399 100644 --- a/apps/user_ldap/l10n/fr.json +++ b/apps/user_ldap/l10n/fr.json @@ -66,6 +66,7 @@ "Verify settings and count the groups" : "Vérifier les paramètres et compter les groupes", "When logging in, %s will find the user based on the following attributes:" : "Au login, %s cherchera l'utilisateur sur base de ces attributs :", "LDAP / AD Username:" : "Nom d'utilisateur LDAP / AD :", + "Allows login against the LDAP / AD username, which is either uid or sAMAccountName and will be detected." : "Autorise le login avec le nom d'utilisateur LDAP / AD, ce qui est soit uid ou sAMAccountName et il sera détecté.", "LDAP / AD Email Address:" : "Adresse mail LDAP / AD :", "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "Autoriser le login avec une adresse mail. Mail et mailPrimaryAddress sont autorisés.", "Other Attributes:" : "Autres attributs :", @@ -78,6 +79,7 @@ "Copy current configuration into new directory binding" : "Copier la configuration actuelle vers une nouvelle", "Delete the current configuration" : "Supprimer la configuration actuelle", "Host" : "Hôte", + "You can omit the protocol, unless you require SSL. If so, start with ldaps://" : "Vous pouvez omettre le protocole, sauf si vous avez besoin de SSL. Dans ce cas, préfixez avec ldaps://", "Port" : "Port", "Detect Port" : "Détecter le port", "User DN" : "DN Utilisateur", diff --git a/apps/workflowengine/l10n/de.js b/apps/workflowengine/l10n/de.js index 87b81a330e3..bd0c130b3d6 100644 --- a/apps/workflowengine/l10n/de.js +++ b/apps/workflowengine/l10n/de.js @@ -1,7 +1,9 @@ OC.L10N.register( "workflowengine", { + "Saved" : "Gespeichert", "Saving failed:" : "Speichern fehlgeschlagen:", + "File MIME type" : "Datei Medientyp", "is" : "ist", "is not" : "ist nicht", "matches" : "entspricht", diff --git a/apps/workflowengine/l10n/de.json b/apps/workflowengine/l10n/de.json index eb2ea0310d1..c3bc4f34adc 100644 --- a/apps/workflowengine/l10n/de.json +++ b/apps/workflowengine/l10n/de.json @@ -1,5 +1,7 @@ { "translations": { + "Saved" : "Gespeichert", "Saving failed:" : "Speichern fehlgeschlagen:", + "File MIME type" : "Datei Medientyp", "is" : "ist", "is not" : "ist nicht", "matches" : "entspricht", diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 1ead631db4d..16ab904ad43 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -29,6 +29,7 @@ isMailShareAllowed: oc_appconfig.shareByMailEnabled !== undefined, defaultExpireDate: oc_appconfig.core.defaultExpireDate, isResharingAllowed: oc_appconfig.core.resharingAllowed, + isPasswordForMailSharesRequired: (oc_appconfig.shareByMail === undefined) ? false : oc_appconfig.shareByMail.enforcePasswordProtection, allowGroupSharing: oc_appconfig.core.allowGroupSharing }, diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index 75e56a23f58..6017714b305 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -85,7 +85,7 @@ '</li>' + '{{#each social}}' + '<li>' + - '<a href="#" class="shareOption menuitem pop-up" data-url="{{url}}">' + + '<a href="#" class="shareOption menuitem pop-up" data-url="{{url}}" data-window="{{newWindow}}">' + '<span class="icon {{iconClass}}"' + '></span><span>{{label}}' + '</span>' + @@ -424,7 +424,8 @@ url: url, label: t('core', 'Share to {name}', {name: model.get('name')}), name: model.get('name'), - iconClass: model.get('iconClass') + iconClass: model.get('iconClass'), + newWindow: model.get('newWindow') }); }); @@ -515,14 +516,19 @@ event.stopPropagation(); var url = $(event.currentTarget).data('url'); + var newWindow = $(event.currentTarget).data('window'); $(event.currentTarget).tooltip('hide'); if (url) { - var width = 600; - var height = 400; - var left = (screen.width/2)-(width/2); - var top = (screen.height/2)-(height/2); + if (newWindow === true) { + var width = 600; + var height = 400; + var left = (screen.width / 2) - (width / 2); + var top = (screen.height / 2) - (height / 2); - window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left); + window.open(url, 'name', 'width=' + width + ', height=' + height + ', top=' + top + ', left=' + left); + } else { + window.location.href = url; + } } } diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 6903dd57c33..3a481e53dde 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -100,7 +100,7 @@ '{{/if}}' + '<li>' + '<span class="shareOption menuitem">' + - '<input id="password-{{cid}}-{{shareId}}" type="checkbox" name="password" class="password checkbox" {{#if isPasswordSet}}checked="checked"{{/if}}" />' + + '<input id="password-{{cid}}-{{shareId}}" type="checkbox" name="password" class="password checkbox" {{#if isPasswordSet}}checked="checked"{{/if}}{{#if isPasswordSet}}{{#if isPasswordForMailSharesRequired}}disabled=""{{/if}}{{/if}}" />' + '<label for="password-{{cid}}-{{shareId}}">{{passwordLabel}}</label>' + '<div class="passwordContainer-{{cid}}-{{shareId}} {{#unless isPasswordSet}}hidden{{/unless}}">' + ' <label for="passwordField-{{cid}}-{{shareId}}" class="hidden-visually" value="{{password}}">{{passwordLabel}}</label>' + @@ -268,6 +268,7 @@ crudsLabel: t('core', 'Access control'), triangleSImage: OC.imagePath('core', 'actions/triangle-s'), isResharingAllowed: this.configModel.get('isResharingAllowed'), + isPasswordForMailSharesRequired: this.configModel.get('isPasswordForMailSharesRequired'), sharePermissionPossible: this.model.sharePermissionPossible(), editPermissionPossible: this.model.editPermissionPossible(), createPermissionPossible: this.model.createPermissionPossible(), diff --git a/core/js/sharesocialmanager.js b/core/js/sharesocialmanager.js index c1db48dda62..c0e10a47481 100644 --- a/core/js/sharesocialmanager.js +++ b/core/js/sharesocialmanager.js @@ -36,7 +36,9 @@ /** Name to show in the tooltip */ name: null, /** Icon class to display */ - iconClass: null + iconClass: null, + /** Open in new windows */ + newWindow: true } }); diff --git a/core/l10n/pl.js b/core/l10n/pl.js index 995b1031a10..514f697a292 100644 --- a/core/l10n/pl.js +++ b/core/l10n/pl.js @@ -72,6 +72,7 @@ OC.L10N.register( "No files in here" : "Nie ma tu żadnych plików", "Choose" : "Wybierz", "Error loading file picker template: {error}" : "Błąd podczas ładowania pliku wybranego szablonu: {error}", + "OK" : "OK", "Error loading message template: {error}" : "Błąd podczas ładowania szablonu wiadomości: {error}", "read-only" : "tylko odczyt", "_{count} file conflict_::_{count} file conflicts_" : ["{count} konfliktów plików","{count} konfliktów plików","{count} konfliktów plików","{count} konfliktów plików"], @@ -116,6 +117,7 @@ OC.L10N.register( "Expiration" : "Wygaśnięcie", "Expiration date" : "Data wygaśnięcia", "Choose a password for the public link" : "Wybierz hasło dla linku publicznego", + "Choose a password for the public link or press \"Enter ↵\"" : "Wybierz hasło dla publicznego linka lub wciśnij \"Enter ↵\"", "Copied!" : "Skopiowano!", "Copy" : "Skopiuj", "Not supported!" : "Brak wsparcia!", diff --git a/core/l10n/pl.json b/core/l10n/pl.json index becec1a4154..2a36e8bee29 100644 --- a/core/l10n/pl.json +++ b/core/l10n/pl.json @@ -70,6 +70,7 @@ "No files in here" : "Nie ma tu żadnych plików", "Choose" : "Wybierz", "Error loading file picker template: {error}" : "Błąd podczas ładowania pliku wybranego szablonu: {error}", + "OK" : "OK", "Error loading message template: {error}" : "Błąd podczas ładowania szablonu wiadomości: {error}", "read-only" : "tylko odczyt", "_{count} file conflict_::_{count} file conflicts_" : ["{count} konfliktów plików","{count} konfliktów plików","{count} konfliktów plików","{count} konfliktów plików"], @@ -114,6 +115,7 @@ "Expiration" : "Wygaśnięcie", "Expiration date" : "Data wygaśnięcia", "Choose a password for the public link" : "Wybierz hasło dla linku publicznego", + "Choose a password for the public link or press \"Enter ↵\"" : "Wybierz hasło dla publicznego linka lub wciśnij \"Enter ↵\"", "Copied!" : "Skopiowano!", "Copy" : "Skopiuj", "Not supported!" : "Brak wsparcia!", diff --git a/lib/l10n/de.js b/lib/l10n/de.js index cae14af7cfc..a4c9547373a 100644 --- a/lib/l10n/de.js +++ b/lib/l10n/de.js @@ -36,11 +36,13 @@ OC.L10N.register( "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die App in den App-Einstellungen aktivieren oder den Administrator kontaktieren.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", + "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Personal" : "Persönlich", @@ -63,6 +65,7 @@ OC.L10N.register( "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter details of an existing account." : "Du mußt Daten eines existierenden Kontos angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", @@ -155,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", + "Username must not consist of dots only" : "Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort angegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "User disabled" : "Nutzer deaktiviert", @@ -198,6 +202,7 @@ OC.L10N.register( "Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration", + "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stelle sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speichern nicht erlaubt. %s", diff --git a/lib/l10n/de.json b/lib/l10n/de.json index 39ee5435aaa..7b2051ab3bf 100644 --- a/lib/l10n/de.json +++ b/lib/l10n/de.json @@ -34,11 +34,13 @@ "_%n hour ago_::_%n hours ago_" : ["Vor %n Stunde","Vor %n Stunden"], "_%n minute ago_::_%n minutes ago_" : ["Vor %n Minute","Vor %n Minuten"], "seconds ago" : "Gerade eben", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Das Modul mit der ID: %s existiert nicht. Bitte die App in den App-Einstellungen aktivieren oder den Administrator kontaktieren.", "File name is a reserved word" : "Der Dateiname ist ein reserviertes Wort", "File name contains at least one invalid character" : "Der Dateiname enthält mindestens ein ungültiges Zeichen", "File name is too long" : "Dateiname ist zu lang", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", "Empty filename is not allowed" : "Ein leerer Dateiname ist nicht erlaubt", + "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch versandte E-Mail, bitte nicht antworten.", "Help" : "Hilfe", "Apps" : "Apps", "Personal" : "Persönlich", @@ -61,6 +63,7 @@ "Oracle username and/or password not valid" : "Oracle-Benutzername und/oder -Passwort ungültig", "DB Error: \"%s\"" : "DB-Fehler: „%s“", "Offending command was: \"%s\"" : "Fehlerhafter Befehl war: „%s“", + "You need to enter details of an existing account." : "Du mußt Daten eines existierenden Kontos angeben.", "Offending command was: \"%s\", name: %s, password: %s" : "Fehlerhafter Befehl war: „%s“, Name: %s, Passwort: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL-Benutzername und/oder -Passwort ungültig", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X wird nicht unterstützt und %s wird auf dieser Plattform nicht richtig funktionieren. Die Benutzung erfolgt auf eigene Gefahr!", @@ -153,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Folgende Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“ und „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Der Benutzername enthält Leerzeichen am Anfang oder am Ende", + "Username must not consist of dots only" : "Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort angegeben werden", "The username is already being used" : "Dieser Benutzername existiert bereits", "User disabled" : "Nutzer deaktiviert", @@ -196,6 +200,7 @@ "Your data directory is readable by other users" : "Dein Datenverzeichnis kann von anderen Benutzern gelesen werden", "Your data directory must be an absolute path" : "Dein Datenverzeichnis muss einen eindeutigen Pfad haben", "Check the value of \"datadirectory\" in your configuration" : "Überprüfe bitte die Angabe unter „datadirectory“ in Deiner Konfiguration", + "Your data directory is invalid" : "Dein Datenverzeichnis ist ungültig", "Please check that the data directory contains a file \".ocdata\" in its root." : "Bitte stelle sicher, dass das Datenverzeichnis auf seiner ersten Ebene eine Datei namens „.ocdata“ enthält.", "Could not obtain lock type %d on \"%s\"." : "Sperrtyp %d auf „%s“ konnte nicht ermittelt werden.", "Storage unauthorized. %s" : "Speichern nicht erlaubt. %s", diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js index 369d8c31bdf..c8c8bcf1086 100644 --- a/lib/l10n/de_DE.js +++ b/lib/l10n/de_DE.js @@ -158,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Nur die folgenden Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“, and „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Benutzername enthält Leerzeichen am Anfang oder Ende", + "Username must not consist of dots only" : "Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Der Benutzername existiert bereits", "User disabled" : "Nutzer deaktiviert", diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json index 134d7425e70..1e3a96b7808 100644 --- a/lib/l10n/de_DE.json +++ b/lib/l10n/de_DE.json @@ -156,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Nur die folgenden Zeichen sind im Benutzernamen erlaubt: „a-z“, „A-Z“, „0-9“, and „_.@-'“", "A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden", "Username contains whitespace at the beginning or at the end" : "Benutzername enthält Leerzeichen am Anfang oder Ende", + "Username must not consist of dots only" : "Benutzername darf nicht nur aus Punkten bestehen", "A valid password must be provided" : "Es muss ein gültiges Passwort eingegeben werden", "The username is already being used" : "Der Benutzername existiert bereits", "User disabled" : "Nutzer deaktiviert", diff --git a/lib/l10n/fr.js b/lib/l10n/fr.js index fe107a3c9c5..86a8fb3f84a 100644 --- a/lib/l10n/fr.js +++ b/lib/l10n/fr.js @@ -65,6 +65,7 @@ OC.L10N.register( "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", + "You need to enter details of an existing account." : "Vous devez indiquer les détails d'un compte existant.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", @@ -157,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", \"_@-\" et \".\" (le point)", "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Username contains whitespace at the beginning or at the end" : "Le nom d'utilisateur contient des espaces au début ou à la fin", + "Username must not consist of dots only" : "Le nom d'utilisateur ne doit pas être composé uniquement de points", "A valid password must be provided" : "Un mot de passe valide doit être saisi", "The username is already being used" : "Ce nom d'utilisateur est déjà utilisé", "User disabled" : "Utilisateur désactivé", diff --git a/lib/l10n/fr.json b/lib/l10n/fr.json index 4c6f4124f6e..8a3037cfd24 100644 --- a/lib/l10n/fr.json +++ b/lib/l10n/fr.json @@ -63,6 +63,7 @@ "Oracle username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base Oracle non valide(s)", "DB Error: \"%s\"" : "Erreur de la base de données : \"%s\"", "Offending command was: \"%s\"" : "La requête en cause est : \"%s\"", + "You need to enter details of an existing account." : "Vous devez indiquer les détails d'un compte existant.", "Offending command was: \"%s\", name: %s, password: %s" : "La requête en cause est : \"%s\", nom : %s, mot de passe : %s", "PostgreSQL username and/or password not valid" : "Nom d'utilisateur et/ou mot de passe de la base PostgreSQL non valide(s)", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X n'est pas pris en charge et %s ne fonctionnera pas correctement sur cette plate-forme. Son utilisation est à vos risques et périls !", @@ -155,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Seuls les caractères suivants sont autorisés dans un nom d'utilisateur : \"a-z\", \"A-Z\", \"0-9\", \"_@-\" et \".\" (le point)", "A valid username must be provided" : "Un nom d'utilisateur valide doit être saisi", "Username contains whitespace at the beginning or at the end" : "Le nom d'utilisateur contient des espaces au début ou à la fin", + "Username must not consist of dots only" : "Le nom d'utilisateur ne doit pas être composé uniquement de points", "A valid password must be provided" : "Un mot de passe valide doit être saisi", "The username is already being used" : "Ce nom d'utilisateur est déjà utilisé", "User disabled" : "Utilisateur désactivé", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index ade962bca47..d690d655d60 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -36,11 +36,13 @@ OC.L10N.register( "_%n hour ago_::_%n hours ago_" : ["%n godzinę temu","%n godzin temu","%n godzin temu","%n godzin temu"], "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Proszę włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "File name is a reserved word" : "Nazwa pliku jest zarezerwowana", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "Dot files are not allowed" : "Pliki z kropką są nie dozwolone", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", + "This is an automatically sent email, please do not reply." : "To jest automatycznie wysłany e-mail, proszę nie odpowiadać na niego.", "Help" : "Pomoc", "Apps" : "Aplikacje", "Personal" : "Osobiste", @@ -63,6 +65,7 @@ OC.L10N.register( "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", "DB Error: \"%s\"" : "Błąd DB: \"%s\"", "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", + "You need to enter details of an existing account." : "Musisz wprowadzić szczegóły istniejącego konta.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", @@ -155,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "W nazwie użytkownika dozwolone są tylko następujące znaki : \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"", "A valid username must be provided" : "Należy podać prawidłową nazwę użytkownika", "Username contains whitespace at the beginning or at the end" : "Nazwa użytkownika zawiera spację na początku albo na końcu", + "Username must not consist of dots only" : "Nazwa użytkownika nie może się składać tylko z kropek", "A valid password must be provided" : "Należy podać prawidłowe hasło", "The username is already being used" : "Ta nazwa użytkownika jest już używana", "User disabled" : "Użytkownik zablokowany", @@ -198,6 +202,7 @@ OC.L10N.register( "Your data directory is readable by other users" : "Twój katalog z danymi mogą czytać inni użytkownicy", "Your data directory must be an absolute path" : "Twój katalog z danymi musi być ścieżką absolutną", "Check the value of \"datadirectory\" in your configuration" : "Sprawdź wartość \"datadirectory\" w swojej konfiguracji", + "Your data directory is invalid" : "Twój katalog z danymi jest nieprawidłowy", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sprawdź, czy katalog danych zawiera plik \".ocdata\".", "Could not obtain lock type %d on \"%s\"." : "Nie można uzyskać blokady typu %d na \"%s\".", "Storage unauthorized. %s" : "Magazyn nieautoryzowany. %s", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index 8dc4fa0bf76..a46ec2da2cf 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -34,11 +34,13 @@ "_%n hour ago_::_%n hours ago_" : ["%n godzinę temu","%n godzin temu","%n godzin temu","%n godzin temu"], "_%n minute ago_::_%n minutes ago_" : ["%n minute temu","%n minut temu","%n minut temu","%n minut temu"], "seconds ago" : "sekund temu", + "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Proszę włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "File name is a reserved word" : "Nazwa pliku jest zarezerwowana", "File name contains at least one invalid character" : "Nazwa pliku zawiera co najmniej jeden nieprawidłowy znak", "File name is too long" : "Nazwa pliku zbyt długa", "Dot files are not allowed" : "Pliki z kropką są nie dozwolone", "Empty filename is not allowed" : "Pusta nazwa nie jest dozwolona.", + "This is an automatically sent email, please do not reply." : "To jest automatycznie wysłany e-mail, proszę nie odpowiadać na niego.", "Help" : "Pomoc", "Apps" : "Aplikacje", "Personal" : "Osobiste", @@ -61,6 +63,7 @@ "Oracle username and/or password not valid" : "Oracle: Nazwa użytkownika i/lub hasło jest niepoprawne", "DB Error: \"%s\"" : "Błąd DB: \"%s\"", "Offending command was: \"%s\"" : "Niepoprawna komenda: \"%s\"", + "You need to enter details of an existing account." : "Musisz wprowadzić szczegóły istniejącego konta.", "Offending command was: \"%s\", name: %s, password: %s" : "Niepoprawne polecania: \"%s\", nazwa: %s, hasło: %s", "PostgreSQL username and/or password not valid" : "PostgreSQL: Nazwa użytkownika i/lub hasło jest niepoprawne", "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "Mac OS X nie jest wspierany i %s nie będzie działać poprawnie na tej platformie. Używasz na własne ryzyko!", @@ -153,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "W nazwie użytkownika dozwolone są tylko następujące znaki : \"a-z\", \"A-Z\", \"0-9\" i \"_.@-'\"", "A valid username must be provided" : "Należy podać prawidłową nazwę użytkownika", "Username contains whitespace at the beginning or at the end" : "Nazwa użytkownika zawiera spację na początku albo na końcu", + "Username must not consist of dots only" : "Nazwa użytkownika nie może się składać tylko z kropek", "A valid password must be provided" : "Należy podać prawidłowe hasło", "The username is already being used" : "Ta nazwa użytkownika jest już używana", "User disabled" : "Użytkownik zablokowany", @@ -196,6 +200,7 @@ "Your data directory is readable by other users" : "Twój katalog z danymi mogą czytać inni użytkownicy", "Your data directory must be an absolute path" : "Twój katalog z danymi musi być ścieżką absolutną", "Check the value of \"datadirectory\" in your configuration" : "Sprawdź wartość \"datadirectory\" w swojej konfiguracji", + "Your data directory is invalid" : "Twój katalog z danymi jest nieprawidłowy", "Please check that the data directory contains a file \".ocdata\" in its root." : "Sprawdź, czy katalog danych zawiera plik \".ocdata\".", "Could not obtain lock type %d on \"%s\"." : "Nie można uzyskać blokady typu %d na \"%s\".", "Storage unauthorized. %s" : "Magazyn nieautoryzowany. %s", diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js index 389d620bcbf..4b58876ddbc 100644 --- a/lib/l10n/pt_BR.js +++ b/lib/l10n/pt_BR.js @@ -158,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Um nome de usuário válido deve ser fornecido", "Username contains whitespace at the beginning or at the end" : "O nome de usuário contém espaço em branco no início ou no fim", + "Username must not consist of dots only" : "Nome do usuário não pode consistir de pontos somente", "A valid password must be provided" : "Uma senha válida deve ser fornecida", "The username is already being used" : "Este nome de usuário já está em uso", "User disabled" : "Usuário desativado", diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json index f26f42c301b..580af8db048 100644 --- a/lib/l10n/pt_BR.json +++ b/lib/l10n/pt_BR.json @@ -156,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"", "A valid username must be provided" : "Um nome de usuário válido deve ser fornecido", "Username contains whitespace at the beginning or at the end" : "O nome de usuário contém espaço em branco no início ou no fim", + "Username must not consist of dots only" : "Nome do usuário não pode consistir de pontos somente", "A valid password must be provided" : "Uma senha válida deve ser fornecida", "The username is already being used" : "Este nome de usuário já está em uso", "User disabled" : "Usuário desativado", diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js index 1cf5f406d97..27864e2ec10 100644 --- a/lib/l10n/ru.js +++ b/lib/l10n/ru.js @@ -158,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В составе имени пользователя допускаются следующие символы: \"a-z\", \"A-Z\", \"0-9\" и \"_.@-'\"", "A valid username must be provided" : "Укажите допустимое имя пользователя", "Username contains whitespace at the beginning or at the end" : "Имя пользователя содержит пробел в начале или в конце", + "Username must not consist of dots only" : "Имя пользователя должно состоять не только из точек", "A valid password must be provided" : "Укажите допустимый пароль", "The username is already being used" : "Имя пользователя уже используется", "User disabled" : "Пользователь отключен", diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json index 16352c7e746..cdd827031ee 100644 --- a/lib/l10n/ru.json +++ b/lib/l10n/ru.json @@ -156,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В составе имени пользователя допускаются следующие символы: \"a-z\", \"A-Z\", \"0-9\" и \"_.@-'\"", "A valid username must be provided" : "Укажите допустимое имя пользователя", "Username contains whitespace at the beginning or at the end" : "Имя пользователя содержит пробел в начале или в конце", + "Username must not consist of dots only" : "Имя пользователя должно состоять не только из точек", "A valid password must be provided" : "Укажите допустимый пароль", "The username is already being used" : "Имя пользователя уже используется", "User disabled" : "Пользователь отключен", diff --git a/lib/l10n/tr.js b/lib/l10n/tr.js index 49e3b463289..676e62f3cab 100644 --- a/lib/l10n/tr.js +++ b/lib/l10n/tr.js @@ -158,6 +158,7 @@ OC.L10N.register( "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Kullanıcı adında yalnız şu karakterler kullanılabilir: \"a-z\", \"A-Z\", \"0-9\", ve \"_.@-'\"", "A valid username must be provided" : "Geçerli bir kullanıcı adı yazmalısınız", "Username contains whitespace at the beginning or at the end" : "Kullanıcı adının başı ya da sonunda boşluk var", + "Username must not consist of dots only" : "Kullanıcı adı yalnız noktalardan oluşamaz", "A valid password must be provided" : "Geçerli bir parola yazmalısınız", "The username is already being used" : "Bu kullanıcı adı zaten var", "User disabled" : "Kullanıcı devre dışı", diff --git a/lib/l10n/tr.json b/lib/l10n/tr.json index 85ce981745f..02b2d17cc50 100644 --- a/lib/l10n/tr.json +++ b/lib/l10n/tr.json @@ -156,6 +156,7 @@ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Kullanıcı adında yalnız şu karakterler kullanılabilir: \"a-z\", \"A-Z\", \"0-9\", ve \"_.@-'\"", "A valid username must be provided" : "Geçerli bir kullanıcı adı yazmalısınız", "Username contains whitespace at the beginning or at the end" : "Kullanıcı adının başı ya da sonunda boşluk var", + "Username must not consist of dots only" : "Kullanıcı adı yalnız noktalardan oluşamaz", "A valid password must be provided" : "Geçerli bir parola yazmalısınız", "The username is already being used" : "Bu kullanıcı adı zaten var", "User disabled" : "Kullanıcı devre dışı", diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 6e59629153e..3afd38c579f 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -1233,7 +1233,7 @@ class Manager implements IManager { //Get node for the owner $userFolder = $this->rootFolder->getUserFolder($owner); - if (!$userFolder->isSubNode($path)) { + if ($path->getId() !== $userFolder->getId() && !$userFolder->isSubNode($path)) { $path = $userFolder->getById($path->getId())[0]; } @@ -1245,7 +1245,12 @@ class Manager implements IManager { if ($currentAccess) { $ownerPath = $path->getPath(); - list(, , , $ownerPath) = explode('/', $ownerPath, 4); + $ownerPath = explode('/', $ownerPath, 4); + if (count($ownerPath) < 4) { + $ownerPath = ''; + } else { + $ownerPath = $ownerPath[3]; + } $al['users'][$owner] = [ 'node_id' => $path->getId(), 'node_path' => '/' . $ownerPath, diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index beb3a0965d6..c79f58f6ba6 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -23,6 +23,7 @@ */ namespace OC\Share20; +use OC\CapabilitiesManager; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; @@ -160,7 +161,9 @@ class ProviderFactory implements IProviderFactory { $this->serverContainer->getURLGenerator(), $this->serverContainer->getActivityManager(), $settingsManager, - $this->serverContainer->query(Defaults::class) + $this->serverContainer->query(Defaults::class), + $this->serverContainer->getHasher(), + $this->serverContainer->query(CapabilitiesManager::class) ); } diff --git a/settings/l10n/pl.js b/settings/l10n/pl.js index cfd95acbab2..37d4e699ad7 100644 --- a/settings/l10n/pl.js +++ b/settings/l10n/pl.js @@ -4,6 +4,9 @@ OC.L10N.register( "{actor} changed your password" : "{actor} zmienił twoje hasło", "You changed your password" : "Zmieniłeś/-aś swoje hasło", "Your password was reset by an administrator" : "Twoj hasło zostało zresetowane przez administratora", + "{actor} changed your email address" : "{actor} zmienił twój adres e-mail", + "You changed your email address" : "Zmieniłeś/-aś swój adres e-mail", + "Your email address was changed by an administrator" : "Twój adres e-mail został zmieniony przez administratora", "Your <strong>password</strong> or <strong>email</strong> was modified" : "Twoje <strong>hasło</strong> lub <strong>e-mail</strong> zostal zmieniony", "Enabled" : "Włączone", "Not enabled" : "Nie włączone", @@ -14,6 +17,7 @@ OC.L10N.register( "Authentication error" : "Błąd uwierzytelniania", "Please provide an admin recovery password; otherwise, all user data will be lost." : "Proszę podać hasło odzyskiwania administratora; w innym przypadku wszystkie dane zostaną utracone.", "Wrong admin recovery password. Please check the password and try again." : "Błędne hasło odzyskiwania. Sprawdź hasło i spróbuj ponownie.", + "Backend doesn't support password change, but the user's encryption key was updated." : "Backend nie wspiera zmiany hasła, ale klucz szyfrujący użytkownika został zaktualizowany.", "installing and updating apps via the app store or Federated Cloud Sharing" : "instalacji i aktualizacji aplikacji za pośrednictwem sklepu z aplikacjami lub udziałem Stowarzyszonej Chmury", "Federated Cloud Sharing" : "Dzielenie się ze Stowarzyszoną Chmurą", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL używa starej %s wersji (%s). Proszę zaktualizować swój system operacyjny albo funkcje takie jak %s nie będą działały niezawodnie.", @@ -22,6 +26,10 @@ OC.L10N.register( "Group already exists." : "Grupa już istnieje.", "Unable to add group." : "Nie można dodać grupy.", "Unable to delete group." : "Nie można usunąć grupy.", + "Invalid SMTP password." : "Błędne hasło SMTP.", + "Well done, %s!" : "Dobra robota, %s!", + "If you received this email, the email configuration seems to be correct." : "Jeśli otrzymałeś/-aś tego e-maila to wydaje się, że konfiguracja e-maili jest prawidłowa.", + "Email setting test" : "Testuj ustawienia e-maili", "Mail could not be sent. Check your mail server log" : "E-mail nie mógł zostać wysłany. Sprawdź logi swojego serwera poczty", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Wystąpił błąd podczas wysyłania wiadomości e-mail. Proszę zmienić swoje ustawienia. (Error: %s)", "You need to set your user email before being able to send test emails." : "Musisz najpierw ustawić użytkownika e-mail, aby móc wysyłać wiadomości testowe.", @@ -68,6 +76,7 @@ OC.L10N.register( "Migration in progress. Please wait until the migration is finished" : "Trwa migracja. Proszę poczekać, aż migracja dobiegnie końca.", "Migration started …" : "Migracja rozpoczęta...", "Not saved" : "Nie zapisany", + "Sending…" : "Wysyłam...", "Email sent" : "E-mail wysłany", "Official" : "Oficjalny", "All" : "Wszystkie", @@ -90,6 +99,7 @@ OC.L10N.register( "Updating...." : "Aktualizacja w toku...", "Error while updating app" : "Błąd podczas aktualizacji aplikacji", "Updated" : "Zaktualizowano", + "Uninstalling …" : "Odinstalowuję...", "Error while uninstalling app" : "Błąd przy odinstalowywaniu aplikacji", "Uninstall" : "Odinstaluj", "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "Aplikacja została włączona i musi być aktualizowana. Zostaniesz przekierowany na stronę aktualizacji za 5 sekund.", @@ -209,6 +219,7 @@ OC.L10N.register( "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dzieje się tak prawdopodobnie przez cache lub akcelerator taki jak Zend OPcache lub eAccelerator.", "Your database does not run with \"READ COMMITTED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Twoja baza danych nie działa z poziomem izolacji transakcji \"READ COMMITTED\". Może to powodować problemy kiedy wiele akcji będzie wykonywanych równolegle.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s jest zainstalowany poniżej wersji %2$s. Zalecamy podniesienie %1$s do wersji nowszej ze względu na stabilność i wydajność.", + "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with MIME type detection." : "Brakuje modułu PHP 'fileinfo'. Silnie zalecamy włączenie tego modułu, aby osiągać lepsze wyniki w wykrywaniu typów plików MIME.", "Transactional file locking is disabled, this might lead to issues with race conditions. Enable 'filelocking.enabled' in config.php to avoid these problems. See the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation ↗</a> for more information." : "Transakcyjne blokowanie plików jest wyłączone. Może to powodować problemy w działaniu. Włącz 'filelocking.enabled' w config.php, aby rozwiązać te problemy. Sprawdź <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">dokumentację ↗</a>, aby uzyskać więcej informacji.", "System locale can not be set to a one which supports UTF-8." : "Ustawienia regionalne systemu nie można ustawić na jeden, który obsługuje UTF-8.", "This means that there might be problems with certain characters in file names." : "Oznacza to, że mogą być problemy z niektórymi znakami w nazwach plików.", diff --git a/settings/l10n/pl.json b/settings/l10n/pl.json index 563be5d8c7a..ed43bbaed61 100644 --- a/settings/l10n/pl.json +++ b/settings/l10n/pl.json @@ -2,6 +2,9 @@ "{actor} changed your password" : "{actor} zmienił twoje hasło", "You changed your password" : "Zmieniłeś/-aś swoje hasło", "Your password was reset by an administrator" : "Twoj hasło zostało zresetowane przez administratora", + "{actor} changed your email address" : "{actor} zmienił twój adres e-mail", + "You changed your email address" : "Zmieniłeś/-aś swój adres e-mail", + "Your email address was changed by an administrator" : "Twój adres e-mail został zmieniony przez administratora", "Your <strong>password</strong> or <strong>email</strong> was modified" : "Twoje <strong>hasło</strong> lub <strong>e-mail</strong> zostal zmieniony", "Enabled" : "Włączone", "Not enabled" : "Nie włączone", @@ -12,6 +15,7 @@ "Authentication error" : "Błąd uwierzytelniania", "Please provide an admin recovery password; otherwise, all user data will be lost." : "Proszę podać hasło odzyskiwania administratora; w innym przypadku wszystkie dane zostaną utracone.", "Wrong admin recovery password. Please check the password and try again." : "Błędne hasło odzyskiwania. Sprawdź hasło i spróbuj ponownie.", + "Backend doesn't support password change, but the user's encryption key was updated." : "Backend nie wspiera zmiany hasła, ale klucz szyfrujący użytkownika został zaktualizowany.", "installing and updating apps via the app store or Federated Cloud Sharing" : "instalacji i aktualizacji aplikacji za pośrednictwem sklepu z aplikacjami lub udziałem Stowarzyszonej Chmury", "Federated Cloud Sharing" : "Dzielenie się ze Stowarzyszoną Chmurą", "cURL is using an outdated %s version (%s). Please update your operating system or features such as %s will not work reliably." : "cURL używa starej %s wersji (%s). Proszę zaktualizować swój system operacyjny albo funkcje takie jak %s nie będą działały niezawodnie.", @@ -20,6 +24,10 @@ "Group already exists." : "Grupa już istnieje.", "Unable to add group." : "Nie można dodać grupy.", "Unable to delete group." : "Nie można usunąć grupy.", + "Invalid SMTP password." : "Błędne hasło SMTP.", + "Well done, %s!" : "Dobra robota, %s!", + "If you received this email, the email configuration seems to be correct." : "Jeśli otrzymałeś/-aś tego e-maila to wydaje się, że konfiguracja e-maili jest prawidłowa.", + "Email setting test" : "Testuj ustawienia e-maili", "Mail could not be sent. Check your mail server log" : "E-mail nie mógł zostać wysłany. Sprawdź logi swojego serwera poczty", "A problem occurred while sending the email. Please revise your settings. (Error: %s)" : "Wystąpił błąd podczas wysyłania wiadomości e-mail. Proszę zmienić swoje ustawienia. (Error: %s)", "You need to set your user email before being able to send test emails." : "Musisz najpierw ustawić użytkownika e-mail, aby móc wysyłać wiadomości testowe.", @@ -66,6 +74,7 @@ "Migration in progress. Please wait until the migration is finished" : "Trwa migracja. Proszę poczekać, aż migracja dobiegnie końca.", "Migration started …" : "Migracja rozpoczęta...", "Not saved" : "Nie zapisany", + "Sending…" : "Wysyłam...", "Email sent" : "E-mail wysłany", "Official" : "Oficjalny", "All" : "Wszystkie", @@ -88,6 +97,7 @@ "Updating...." : "Aktualizacja w toku...", "Error while updating app" : "Błąd podczas aktualizacji aplikacji", "Updated" : "Zaktualizowano", + "Uninstalling …" : "Odinstalowuję...", "Error while uninstalling app" : "Błąd przy odinstalowywaniu aplikacji", "Uninstall" : "Odinstaluj", "The app has been enabled but needs to be updated. You will be redirected to the update page in 5 seconds." : "Aplikacja została włączona i musi być aktualizowana. Zostaniesz przekierowany na stronę aktualizacji za 5 sekund.", @@ -207,6 +217,7 @@ "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Dzieje się tak prawdopodobnie przez cache lub akcelerator taki jak Zend OPcache lub eAccelerator.", "Your database does not run with \"READ COMMITTED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Twoja baza danych nie działa z poziomem izolacji transakcji \"READ COMMITTED\". Może to powodować problemy kiedy wiele akcji będzie wykonywanych równolegle.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s jest zainstalowany poniżej wersji %2$s. Zalecamy podniesienie %1$s do wersji nowszej ze względu na stabilność i wydajność.", + "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with MIME type detection." : "Brakuje modułu PHP 'fileinfo'. Silnie zalecamy włączenie tego modułu, aby osiągać lepsze wyniki w wykrywaniu typów plików MIME.", "Transactional file locking is disabled, this might lead to issues with race conditions. Enable 'filelocking.enabled' in config.php to avoid these problems. See the <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">documentation ↗</a> for more information." : "Transakcyjne blokowanie plików jest wyłączone. Może to powodować problemy w działaniu. Włącz 'filelocking.enabled' w config.php, aby rozwiązać te problemy. Sprawdź <a target=\"_blank\" rel=\"noreferrer\" href=\"%s\">dokumentację ↗</a>, aby uzyskać więcej informacji.", "System locale can not be set to a one which supports UTF-8." : "Ustawienia regionalne systemu nie można ustawić na jeden, który obsługuje UTF-8.", "This means that there might be problems with certain characters in file names." : "Oznacza to, że mogą być problemy z niektórymi znakami w nazwach plików.", diff --git a/tests/karma.config.js b/tests/karma.config.js index 91052f62cd2..014a8709615 100644 --- a/tests/karma.config.js +++ b/tests/karma.config.js @@ -56,7 +56,8 @@ module.exports = function(config) { 'apps/files_sharing/js/share.js', 'apps/files_sharing/js/sharebreadcrumbview.js', 'apps/files_sharing/js/public.js', - 'apps/files_sharing/js/sharetabview.js' + 'apps/files_sharing/js/sharetabview.js', + 'apps/files_sharing/js/files_drop.js' ], testFiles: ['apps/files_sharing/tests/js/*.js'] }, diff --git a/tests/lib/Share20/ManagerTest.php b/tests/lib/Share20/ManagerTest.php index 42308a9d6a6..7de73421d3e 100644 --- a/tests/lib/Share20/ManagerTest.php +++ b/tests/lib/Share20/ManagerTest.php @@ -2767,8 +2767,7 @@ class ManagerTest extends \Test\TestCase { $node->expects($this->once()) ->method('getOwner') ->willReturn($owner); - $node->expects($this->once()) - ->method('getId') + $node->method('getId') ->willReturn(42); $userFolder = $this->createMock(Folder::class); |