diff options
48 files changed, 610 insertions, 158 deletions
diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index a4e13709559..dc47416cca8 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -168,20 +168,19 @@ class FilesPlugin extends ServerPlugin { */ function checkMove($source, $destination) { $sourceNode = $this->tree->getNodeForPath($source); - if ($sourceNode instanceof FutureFile) { + if (!$sourceNode instanceof Node) { return; } list($sourceDir,) = \Sabre\HTTP\URLUtil::splitPath($source); list($destinationDir,) = \Sabre\HTTP\URLUtil::splitPath($destination); if ($sourceDir !== $destinationDir) { - $sourceFileInfo = $this->fileView->getFileInfo($source); - - if ($sourceFileInfo === false) { + $sourceNodeFileInfo = $sourceNode->getFileInfo(); + if (is_null($sourceNodeFileInfo)) { throw new NotFound($source . ' does not exist'); } - if (!$sourceFileInfo->isDeletable()) { + if (!$sourceNodeFileInfo->isDeletable()) { throw new Forbidden($source . " cannot be deleted"); } } diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index 04640bbde4f..c41b5137fde 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -347,4 +347,8 @@ abstract class Node implements \Sabre\DAV\INode { public function changeLock($type) { $this->fileView->changeLock($this->path, $type); } + + public function getFileInfo() { + return $this->info; + } } diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 87fec17bf6b..80f284e470e 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -24,6 +24,7 @@ */ namespace OCA\DAV\Tests\unit\Connector\Sabre; +use OCA\DAV\Connector\Sabre\FilesPlugin; use OCP\Files\StorageNotAvailableException; use Sabre\DAV\PropFind; use Sabre\DAV\PropPatch; @@ -36,16 +37,16 @@ use Test\TestCase; * See the COPYING-README file. */ class FilesPluginTest extends TestCase { - const GETETAG_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::GETETAG_PROPERTYNAME; - const FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::FILEID_PROPERTYNAME; - const INTERNAL_FILEID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; - const SIZE_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::SIZE_PROPERTYNAME; - const PERMISSIONS_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::PERMISSIONS_PROPERTYNAME; - const LASTMODIFIED_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::LASTMODIFIED_PROPERTYNAME; - const DOWNLOADURL_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DOWNLOADURL_PROPERTYNAME; - const OWNER_ID_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_ID_PROPERTYNAME; - const OWNER_DISPLAY_NAME_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; - const DATA_FINGERPRINT_PROPERTYNAME = \OCA\DAV\Connector\Sabre\FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; + const GETETAG_PROPERTYNAME = FilesPlugin::GETETAG_PROPERTYNAME; + const FILEID_PROPERTYNAME = FilesPlugin::FILEID_PROPERTYNAME; + const INTERNAL_FILEID_PROPERTYNAME = FilesPlugin::INTERNAL_FILEID_PROPERTYNAME; + const SIZE_PROPERTYNAME = FilesPlugin::SIZE_PROPERTYNAME; + const PERMISSIONS_PROPERTYNAME = FilesPlugin::PERMISSIONS_PROPERTYNAME; + const LASTMODIFIED_PROPERTYNAME = FilesPlugin::LASTMODIFIED_PROPERTYNAME; + const DOWNLOADURL_PROPERTYNAME = FilesPlugin::DOWNLOADURL_PROPERTYNAME; + const OWNER_ID_PROPERTYNAME = FilesPlugin::OWNER_ID_PROPERTYNAME; + const OWNER_DISPLAY_NAME_PROPERTYNAME = FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME; + const DATA_FINGERPRINT_PROPERTYNAME = FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME; /** * @var \Sabre\DAV\Server | \PHPUnit_Framework_MockObject_MockObject @@ -58,7 +59,7 @@ class FilesPluginTest extends TestCase { private $tree; /** - * @var \OCA\DAV\Connector\Sabre\FilesPlugin + * @var FilesPlugin */ private $plugin; @@ -84,11 +85,11 @@ class FilesPluginTest extends TestCase { ->disableOriginalConstructor() ->getMock(); $this->config = $this->getMock('\OCP\IConfig'); - $this->config->method('getSystemValue') + $this->config->expects($this->any())->method('getSystemValue') ->with($this->equalTo('data-fingerprint'), $this->equalTo('')) ->willReturn('my_fingerprint'); - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->plugin = new FilesPlugin( $this->tree, $this->view, $this->config @@ -263,7 +264,7 @@ class FilesPluginTest extends TestCase { } public function testGetPublicPermissions() { - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->plugin = new FilesPlugin( $this->tree, $this->view, $this->config, @@ -331,7 +332,7 @@ class FilesPluginTest extends TestCase { $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Directory') ->disableOriginalConstructor() ->getMock(); - $node->method('getPath')->willReturn('/'); + $node->expects($this->any())->method('getPath')->willReturn('/'); $propFind = new PropFind( '/', @@ -432,11 +433,16 @@ class FilesPluginTest extends TestCase { ->method('isDeletable') ->willReturn(false); - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') ->willReturn($fileInfoFolderATestTXT); + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } @@ -448,11 +454,16 @@ class FilesPluginTest extends TestCase { ->method('isDeletable') ->willReturn(true); - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') ->willReturn($fileInfoFolderATestTXT); + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); + $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } @@ -461,10 +472,15 @@ class FilesPluginTest extends TestCase { * @expectedExceptionMessage FolderA/test.txt does not exist */ public function testMoveSrcNotExist() { - $this->view->expects($this->once()) + $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\Node') + ->disableOriginalConstructor() + ->getMock(); + $node->expects($this->once()) ->method('getFileInfo') - ->with('FolderA/test.txt') - ->willReturn(false); + ->willReturn(null); + + $this->tree->expects($this->once())->method('getNodeForPath') + ->willReturn($node); $this->plugin->checkMove('FolderA/test.txt', 'test.txt'); } diff --git a/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php new file mode 100644 index 00000000000..4f481cba557 --- /dev/null +++ b/apps/dav/tests/unit/Connector/Sabre/RequestTest/DeleteTest.php @@ -0,0 +1,59 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Robin Appelman <icewind@owncloud.com> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Tests\unit\Connector\Sabre\RequestTest; + +use OC\Connector\Sabre\Exception\FileLocked; +use OCP\AppFramework\Http; +use OCP\Lock\ILockingProvider; + +/** + * Class DeleteTest + * + * @group DB + * + * @package OCA\DAV\Tests\unit\Connector\Sabre\RequestTest + */ +class DeleteTest extends RequestTest { + public function testBasicUpload() { + $user = $this->getUniqueID(); + $view = $this->setupUser($user, 'pass'); + + $view->file_put_contents('foo.txt', 'asd'); + $mount = $view->getMount('foo.txt'); + $internalPath = $view->getAbsolutePath(); + + // create a ghost file + $mount->getStorage()->unlink($mount->getInternalPath($internalPath)); + + // cache entry still exists + $this->assertInstanceOf('\OCP\Files\FileInfo', $view->getFileInfo('foo.txt')); + + $response = $this->request($view, $user, 'pass', 'DELETE', '/foo.txt'); + + $this->assertEquals(Http::STATUS_NO_CONTENT, $response->getStatus()); + + // no longer in the cache + $this->assertFalse($view->getFileInfo('foo.txt')); + } +} diff --git a/apps/federatedfilesharing/l10n/ar.js b/apps/federatedfilesharing/l10n/ar.js new file mode 100644 index 00000000000..451663a7b6b --- /dev/null +++ b/apps/federatedfilesharing/l10n/ar.js @@ -0,0 +1,6 @@ +OC.L10N.register( + "federatedfilesharing", + { + "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح" +}, +"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;"); diff --git a/apps/federatedfilesharing/l10n/ar.json b/apps/federatedfilesharing/l10n/ar.json new file mode 100644 index 00000000000..630ab95f64b --- /dev/null +++ b/apps/federatedfilesharing/l10n/ar.json @@ -0,0 +1,4 @@ +{ "translations": { + "Invalid Federated Cloud ID" : "معرّف السحابة المتحدة غير صالح" +},"pluralForm" :"nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;" +}
\ No newline at end of file diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 7a3d78f9663..fbfa510e07e 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -53,6 +53,9 @@ this.$showHiddenFiles = $('input#showhiddenfilesToggle'); var showHidden = $('#showHiddenFiles').val() === "1"; this.$showHiddenFiles.prop('checked', showHidden); + if ($('#fileNotFound').val() === "1") { + OC.Notification.showTemporary(t('files', 'File could not be found')); + } this._filesConfig = new OC.Backbone.Model({ showhidden: showHidden diff --git a/apps/files/l10n/da.js b/apps/files/l10n/da.js index e1013bcafbb..e585ccacc1c 100644 --- a/apps/files/l10n/da.js +++ b/apps/files/l10n/da.js @@ -32,6 +32,8 @@ OC.L10N.register( "Could not get result from server." : "Kunne ikke hente resultat fra server.", "Uploading..." : "Uploader...", "..." : "...", + "Any moment now..." : "Når som helst...", + "Soon..." : "Snart...", "File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "Actions" : "Handlinger", "Download" : "Download", @@ -47,6 +49,8 @@ OC.L10N.register( "This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren", "Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn", "Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"", + "{newName} already exists" : "{newName} eksistere allerede", + "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"", "No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'", "Name" : "Navn", "Size" : "Størrelse", @@ -68,6 +72,7 @@ OC.L10N.register( "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Favorited" : "Gjort til foretrukken", "Favorite" : "Foretrukken", + "Local link" : "Lokalt link", "Folder" : "Mappe", "New folder" : "Ny Mappe", "{newname} already exists" : "{newname} eksistere allerede", @@ -97,6 +102,7 @@ OC.L10N.register( "Save" : "Gem", "Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted", "Settings" : "Indstillinger", + "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", diff --git a/apps/files/l10n/da.json b/apps/files/l10n/da.json index 3162770cc40..499533d77ba 100644 --- a/apps/files/l10n/da.json +++ b/apps/files/l10n/da.json @@ -30,6 +30,8 @@ "Could not get result from server." : "Kunne ikke hente resultat fra server.", "Uploading..." : "Uploader...", "..." : "...", + "Any moment now..." : "Når som helst...", + "Soon..." : "Snart...", "File upload is in progress. Leaving the page now will cancel the upload." : "Fil upload kører. Hvis du forlader siden nu, vil uploadet blive annuleret.", "Actions" : "Handlinger", "Download" : "Download", @@ -45,6 +47,8 @@ "This directory is unavailable, please check the logs or contact the administrator" : "Denne mappe er utilgængelig, tjek venligst loggene eller kontakt administratoren", "Could not move \"{file}\", target exists" : "Kunne ikke flytte \"{file}\" - der findes allerede en fil med dette navn", "Could not move \"{file}\"" : "Kunne ikke flytte \"{file}\"", + "{newName} already exists" : "{newName} eksistere allerede", + "Error deleting file \"{fileName}\"." : "Fejl under sletning af filen \"{fileName}\"", "No entries in this folder match '{filter}'" : "Der er ingen poster i denne mappe, der matcher '{filter}'", "Name" : "Navn", "Size" : "Størrelse", @@ -66,6 +70,7 @@ "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"], "Favorited" : "Gjort til foretrukken", "Favorite" : "Foretrukken", + "Local link" : "Lokalt link", "Folder" : "Mappe", "New folder" : "Ny Mappe", "{newname} already exists" : "{newname} eksistere allerede", @@ -95,6 +100,7 @@ "Save" : "Gem", "Missing permissions to edit from here." : "Rettighed mangler til at redigere på dette sted", "Settings" : "Indstillinger", + "Show hidden files" : "Vis skjulte filer", "WebDAV" : "WebDAV", "No files in here" : "Her er ingen filer", "Upload some content or sync with your devices!" : "Overfør indhold eller synkronisér med dine enheder!", diff --git a/apps/files/lib/Controller/ViewController.php b/apps/files/lib/Controller/ViewController.php index 1b0903b41d3..18b6cf719c5 100644 --- a/apps/files/lib/Controller/ViewController.php +++ b/apps/files/lib/Controller/ViewController.php @@ -27,9 +27,9 @@ namespace OCA\Files\Controller; use OC\AppFramework\Http\Request; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\ContentSecurityPolicy; -use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Files\NotFoundException; use OCP\IConfig; use OCP\IL10N; use OCP\INavigationManager; @@ -37,7 +37,6 @@ use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserSession; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use OCP\AppFramework\Http\NotFoundResponse; use OCP\Files\Folder; use OCP\App\IAppManager; @@ -142,11 +141,15 @@ class ViewController extends Controller { * @param string $view * @param string $fileid * @return TemplateResponse - * @throws \OCP\Files\NotFoundException */ public function index($dir = '', $view = '', $fileid = null) { + $fileNotFound = false; if ($fileid !== null) { - return $this->showFile($fileid); + try { + return $this->showFile($fileid); + } catch (NotFoundException $e) { + $fileNotFound = true; + } } $nav = new \OCP\Template('files', 'appnavigation', ''); @@ -245,6 +248,7 @@ class ViewController extends Controller { $params['defaultFileSortingDirection'] = $this->config->getUserValue($user, 'files', 'file_sorting_direction', 'asc'); $showHidden = (bool) $this->config->getUserValue($this->userSession->getUser()->getUID(), 'files', 'show_hidden', false); $params['showHiddenFiles'] = $showHidden ? 1 : 0; + $params['fileNotFound'] = $fileNotFound ? 1 : 0; $params['appNavigation'] = $nav; $params['appContents'] = $contentItems; $this->navigationManager->setActiveEntry('files_index'); @@ -265,40 +269,37 @@ class ViewController extends Controller { * Redirects to the file list and highlight the given file id * * @param string $fileId file id to show - * @return Response redirect response or not found response + * @return RedirectResponse redirect response or not found response + * @throws \OCP\Files\NotFoundException * * @NoCSRFRequired * @NoAdminRequired */ public function showFile($fileId) { - try { - $uid = $this->userSession->getUser()->getUID(); - $baseFolder = $this->rootFolder->get($uid . '/files/'); - $files = $baseFolder->getById($fileId); - $params = []; + $uid = $this->userSession->getUser()->getUID(); + $baseFolder = $this->rootFolder->get($uid . '/files/'); + $files = $baseFolder->getById($fileId); + $params = []; - if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) { - $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/'); - $files = $baseFolder->getById($fileId); - $params['view'] = 'trashbin'; - } + if (empty($files) && $this->appManager->isEnabledForUser('files_trashbin')) { + $baseFolder = $this->rootFolder->get($uid . '/files_trashbin/files/'); + $files = $baseFolder->getById($fileId); + $params['view'] = 'trashbin'; + } - if (!empty($files)) { - $file = current($files); - if ($file instanceof Folder) { - // set the full path to enter the folder - $params['dir'] = $baseFolder->getRelativePath($file->getPath()); - } else { - // set parent path as dir - $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath()); - // and scroll to the entry - $params['scrollto'] = $file->getName(); - } - return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params)); + if (!empty($files)) { + $file = current($files); + if ($file instanceof Folder) { + // set the full path to enter the folder + $params['dir'] = $baseFolder->getRelativePath($file->getPath()); + } else { + // set parent path as dir + $params['dir'] = $baseFolder->getRelativePath($file->getParent()->getPath()); + // and scroll to the entry + $params['scrollto'] = $file->getName(); } - } catch (\OCP\Files\NotFoundException $e) { - return new NotFoundResponse(); + return new RedirectResponse($this->urlGenerator->linkToRoute('files.view.index', $params)); } - return new NotFoundResponse(); + throw new \OCP\Files\NotFoundException(); } } diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 7281edd3aec..42ce941a4a5 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -14,6 +14,7 @@ <input type="hidden" name="usedSpacePercent" id="usedSpacePercent" value="<?php p($_['usedSpacePercent']); ?>" /> <input type="hidden" name="owner" id="owner" value="<?php p($_['owner']); ?>" /> <input type="hidden" name="ownerDisplayName" id="ownerDisplayName" value="<?php p($_['ownerDisplayName']); ?>" /> +<input type="hidden" name="fileNotFound" id="fileNotFound" value="<?php p($_['fileNotFound']); ?>"" /> <?php if (!$_['isPublic']) :?> <input type="hidden" name="mailNotificationEnabled" id="mailNotificationEnabled" value="<?php p($_['mailNotificationEnabled']) ?>" /> <input type="hidden" name="mailPublicNotificationEnabled" id="mailPublicNotificationEnabled" value="<?php p($_['mailPublicNotificationEnabled']) ?>" /> diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index 049f44fc0af..34c40ecea5c 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -26,6 +26,7 @@ namespace OCA\Files\Tests\Controller; use OCA\Files\Controller\ViewController; use OCP\AppFramework\Http; +use OCP\Files\NotFoundException; use OCP\IUser; use OCP\Template; use Test\TestCase; @@ -259,7 +260,8 @@ class ViewControllerTest extends TestCase { 'isPublic' => false, 'defaultFileSorting' => 'name', 'defaultFileSortingDirection' => 'asc', - 'showHiddenFiles' => false, + 'showHiddenFiles' => 0, + 'fileNotFound' => 0, 'mailNotificationEnabled' => 'no', 'mailPublicNotificationEnabled' => 'no', 'allowShareWithLink' => 'yes', @@ -410,11 +412,14 @@ class ViewControllerTest extends TestCase { ->with(123) ->will($this->returnValue([])); - $expected = new Http\NotFoundResponse(); if ($useShowFile) { - $this->assertEquals($expected, $this->viewController->showFile(123)); + $this->setExpectedException('OCP\Files\NotFoundException'); + $this->viewController->showFile(123); } else { - $this->assertEquals($expected, $this->viewController->index('/whatever', '', '123')); + $response = $this->viewController->index('MyDir', 'MyView', '123'); + $this->assertInstanceOf('OCP\AppFramework\Http\TemplateResponse', $response); + $params = $response->getParams(); + $this->assertEquals(1, $params['fileNotFound']); } } diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js index 9d3998d903b..68f183a36b3 100644 --- a/apps/files_external/l10n/pt_PT.js +++ b/apps/files_external/l10n/pt_PT.js @@ -7,13 +7,13 @@ OC.L10N.register( "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s", "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s", "External storage" : "Armazenamento Externo", - "Dropbox App Configuration" : "Configuração da app Dropbox", - "Google Drive App Configuration" : "Configuração da app Google Drive", + "Dropbox App Configuration" : "Configuração da aplicação Dropbox", + "Google Drive App Configuration" : "Configuração da aplicação Google Drive", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Conceder acesso", - "Error configuring OAuth1" : "Erro de configuração OAuth1", - "Error configuring OAuth2" : "Erro de configuração OAuth2", + "Error configuring OAuth1" : "Erro ao configurar OAuth1", + "Error configuring OAuth2" : "Erro ao configurar OAuth2", "Generate keys" : "Gerar chaves", "Error generating key pair" : "Erro ao gerar chave par", "All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.", @@ -27,14 +27,14 @@ OC.L10N.register( "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", - "external-storage" : "Armazenamento Externo", + "external-storage" : "armazenamento externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", "Username" : "Nome de utilizador", "Password" : "Palavra-passe", "Credentials saved" : "Credenciais guardadas", - "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials saving failed" : "Falha ao guardar as credenciais", "Credentials required" : "Credenciais necessárias", "Save" : "Guardar", "Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"", @@ -49,7 +49,7 @@ OC.L10N.register( "Insufficient data: %s" : "Dados insuficientes: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador", - "Access key" : "Código de acesso", + "Access key" : "Chave de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", "None" : "Nenhum", diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json index fab0f05a830..93922c67762 100644 --- a/apps/files_external/l10n/pt_PT.json +++ b/apps/files_external/l10n/pt_PT.json @@ -5,13 +5,13 @@ "Step 1 failed. Exception: %s" : "Passo 1 falhou. Exceção: %s", "Step 2 failed. Exception: %s" : "Passo 2 falhou. Exceção: %s", "External storage" : "Armazenamento Externo", - "Dropbox App Configuration" : "Configuração da app Dropbox", - "Google Drive App Configuration" : "Configuração da app Google Drive", + "Dropbox App Configuration" : "Configuração da aplicação Dropbox", + "Google Drive App Configuration" : "Configuração da aplicação Google Drive", "Personal" : "Pessoal", "System" : "Sistema", "Grant access" : "Conceder acesso", - "Error configuring OAuth1" : "Erro de configuração OAuth1", - "Error configuring OAuth2" : "Erro de configuração OAuth2", + "Error configuring OAuth1" : "Erro ao configurar OAuth1", + "Error configuring OAuth2" : "Erro ao configurar OAuth2", "Generate keys" : "Gerar chaves", "Error generating key pair" : "Erro ao gerar chave par", "All users. Type to select user or group." : "Todos os utilizadores. Digite para selecionar o utilizador ou grupo.", @@ -25,14 +25,14 @@ "Couldn't get the list of external mount points: {type}" : "Não foi possível conseguir a lista de pontos de montagem externos: {type}", "There was an error with message: " : "Houve um erro com a mensagem:", "External mount error" : "Erro de montagem externa", - "external-storage" : "Armazenamento Externo", + "external-storage" : "armazenamento externo", "Couldn't get the list of Windows network drive mount points: empty response from the server" : "Não foi possível conseguir a lista de pontos de montagem Windows na rede: resposta vazia do servidor", "Some of the configured external mount points are not connected. Please click on the red row(s) for more information" : "Alguns dos pontos de montagem externos configurados não estão conectados. Por favor, clique na fila vermelha para mais informação", "Please enter the credentials for the {mount} mount" : "Por favor, introduza as credenciais para {mount}", "Username" : "Nome de utilizador", "Password" : "Palavra-passe", "Credentials saved" : "Credenciais guardadas", - "Credentials saving failed" : "Falha ao salvar credenciais", + "Credentials saving failed" : "Falha ao guardar as credenciais", "Credentials required" : "Credenciais necessárias", "Save" : "Guardar", "Storage with id \"%i\" not found" : "Não foi encontrado o armazenamento com a id. \"%i\"", @@ -47,7 +47,7 @@ "Insufficient data: %s" : "Dados insuficientes: %s", "%s" : "%s", "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador", - "Access key" : "Código de acesso", + "Access key" : "Chave de acesso", "Secret key" : "Código secreto", "Builtin" : "Integrado", "None" : "Nenhum", diff --git a/apps/files_external/lib/Lib/Storage/Google.php b/apps/files_external/lib/Lib/Storage/Google.php index 2a1ff768e2c..49fde7d066f 100644 --- a/apps/files_external/lib/Lib/Storage/Google.php +++ b/apps/files_external/lib/Lib/Storage/Google.php @@ -97,6 +97,9 @@ class Google extends \OC\Files\Storage\Common { private function getDriveFile($path) { // Remove leading and trailing slashes $path = trim($path, '/'); + if ($path === '.') { + $path = ''; + } if (isset($this->driveFiles[$path])) { return $this->driveFiles[$path]; } else if ($path === '') { @@ -138,7 +141,7 @@ class Google extends \OC\Files\Storage\Common { if ($pos !== false) { $pathWithoutExt = substr($path, 0, $pos); $file = $this->getDriveFile($pathWithoutExt); - if ($file) { + if ($file && $this->isGoogleDocFile($file)) { // Switch cached Google_Service_Drive_DriveFile to the correct index unset($this->driveFiles[$pathWithoutExt]); $this->driveFiles[$path] = $file; @@ -208,6 +211,17 @@ class Google extends \OC\Files\Storage\Common { } } + /** + * Returns whether the given drive file is a Google Doc file + * + * @param \Google_Service_Drive_DriveFile + * + * @return true if the file is a Google Doc file, false otherwise + */ + private function isGoogleDocFile($file) { + return $this->getGoogleDocExtension($file->getMimeType()) !== ''; + } + public function mkdir($path) { if (!$this->is_dir($path)) { $parentFolder = $this->getDriveFile(dirname($path)); diff --git a/apps/files_external/lib/Lib/Storage/SMB.php b/apps/files_external/lib/Lib/Storage/SMB.php index c08b730b02e..9f74aa881e0 100644 --- a/apps/files_external/lib/Lib/Storage/SMB.php +++ b/apps/files_external/lib/Lib/Storage/SMB.php @@ -181,6 +181,26 @@ class SMB extends \OC\Files\Storage\Common { } /** + * @param string $path1 the old name + * @param string $path2 the new name + * @return bool + */ + public function rename($path1, $path2) { + try { + $this->remove($path2); + $path1 = $this->buildPath($path1); + $path2 = $this->buildPath($path2); + return $this->share->rename($path1, $path2); + } catch (NotFoundException $e) { + return false; + } catch (ForbiddenException $e) { + return false; + } catch (ConnectException $e) { + throw new StorageNotAvailableException($e->getMessage(), $e->getCode(), $e); + } + } + + /** * check if a file or folder has been updated since $time * * @param string $path diff --git a/apps/files_external/tests/Storage/GoogleTest.php b/apps/files_external/tests/Storage/GoogleTest.php index 7684fec8429..eb19cc463b1 100644 --- a/apps/files_external/tests/Storage/GoogleTest.php +++ b/apps/files_external/tests/Storage/GoogleTest.php @@ -60,4 +60,13 @@ class GoogleTest extends \Test\Files\Storage\Storage { parent::tearDown(); } + + public function testSameNameAsFolderWithExtension() { + $this->assertTrue($this->instance->mkdir('testsamename')); + $this->assertEquals(13, $this->instance->file_put_contents('testsamename.txt', 'some contents')); + $this->assertEquals('some contents', $this->instance->file_get_contents('testsamename.txt')); + $this->assertTrue($this->instance->is_dir('testsamename')); + $this->assertTrue($this->instance->unlink('testsamename.txt')); + $this->assertTrue($this->instance->rmdir('testsamename')); + } } diff --git a/apps/files_sharing/lib/Cache.php b/apps/files_sharing/lib/Cache.php index 82d885a8ef3..a0519cadbaa 100644 --- a/apps/files_sharing/lib/Cache.php +++ b/apps/files_sharing/lib/Cache.php @@ -81,7 +81,7 @@ class Cache extends CacheJail { } protected function formatCacheEntry($entry) { - $path = $entry['path']; + $path = isset($entry['path']) ? $entry['path'] : ''; $entry = parent::formatCacheEntry($entry); $sharePermissions = $this->storage->getPermissions($path); if (isset($entry['permissions'])) { diff --git a/apps/files_sharing/lib/Controllers/ShareController.php b/apps/files_sharing/lib/Controllers/ShareController.php index d6f497ed38e..96c0a0ca556 100644 --- a/apps/files_sharing/lib/Controllers/ShareController.php +++ b/apps/files_sharing/lib/Controllers/ShareController.php @@ -321,7 +321,7 @@ class ShareController extends Controller { * The OC_Util methods require a view. This just uses the node API */ $freeSpace = $share->getNode()->getStorage()->free_space($share->getNode()->getInternalPath()); - if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN) { + if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { $freeSpace = max($freeSpace, 0); } else { $freeSpace = (INF > 0) ? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 diff --git a/apps/systemtags/l10n/fr.js b/apps/systemtags/l10n/fr.js index 93c7d364b24..387bc45643c 100644 --- a/apps/systemtags/l10n/fr.js +++ b/apps/systemtags/l10n/fr.js @@ -7,20 +7,20 @@ OC.L10N.register( "Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer", "No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées", "<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées", - "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s", + "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s", "%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s", - "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s", + "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s", "%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s", - "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s", + "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s", "%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s", - "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s", - "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s", - "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s", - "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s", - "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s", - "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s", - "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s", - "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s", + "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s", + "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s", + "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s", + "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s", + "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s", + "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s", + "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s", + "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", "No files in here" : "Aucun fichier", diff --git a/apps/systemtags/l10n/fr.json b/apps/systemtags/l10n/fr.json index 94254c93ab0..66b4d246dac 100644 --- a/apps/systemtags/l10n/fr.json +++ b/apps/systemtags/l10n/fr.json @@ -5,20 +5,20 @@ "Please select tags to filter by" : "Veuillez sélectionner les étiquettes par lesquelles filtrer", "No files found for the selected tags" : "Aucun fichier pour les étiquettes sélectionnées", "<strong>System tags</strong> for a file have been modified" : "<strong>Les étiquettes systèmes</strong> pour un fichier ont été modifiées", - "You assigned system tag %3$s" : "Vous avez attribué l'étiquette système %3$s", + "You assigned system tag %3$s" : "Vous avez attribué l'étiquette collaborative %3$s", "%1$s assigned system tag %3$s" : "%1$s a attribué l'étiquette système %3$s", - "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette système %3$s", + "You unassigned system tag %3$s" : "Vous avez retiré l'étiquette collaborative %3$s", "%1$s unassigned system tag %3$s" : "%1$s a retiré l'étiquette système %3$s", - "You created system tag %2$s" : "Vous avez créé l'étiquette système %2$s", + "You created system tag %2$s" : "Vous avez créé l'étiquette collaborative %2$s", "%1$s created system tag %2$s" : "%1$s a créé l'étiquette système %2$s", - "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette système %2$s", - "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette système %2$s", - "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette système %3$s en %2$s", - "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette système %3$s en %2$s", - "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette système %3$s à %2$s", - "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette système %3$s à %2$s", - "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette système %3$s de %2$s", - "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette système %3$s à %2$s", + "You deleted system tag %2$s" : "Vous avez supprimé l'étiquette collaborative %2$s", + "%1$s deleted system tag %2$s" : "%1$s a supprimé l'étiquette collaborative %2$s", + "You updated system tag %3$s to %2$s" : "Vous avez renommé l'étiquette collaborative %3$s en %2$s", + "%1$s updated system tag %3$s to %2$s" : "%1$s a renommé l'étiquette collaborative %3$s en %2$s", + "You assigned system tag %3$s to %2$s" : "Vous avez attribué l'étiquette collaborative %3$s à %2$s", + "%1$s assigned system tag %3$s to %2$s" : "%1$s a attribué l'étiquette collaborative %3$s à %2$s", + "You unassigned system tag %3$s from %2$s" : "Vous avez retiré l'étiquette collaborative %3$s à %2$s", + "%1$s unassigned system tag %3$s from %2$s" : "%1$s a retiré l'étiquette collaborative %3$s à %2$s", "%s (restricted)" : "%s (restreint)", "%s (invisible)" : "%s (invisible)", "No files in here" : "Aucun fichier", diff --git a/apps/user_ldap/l10n/tr.js b/apps/user_ldap/l10n/tr.js index 5daf34cc58e..bef7664ab94 100644 --- a/apps/user_ldap/l10n/tr.js +++ b/apps/user_ldap/l10n/tr.js @@ -13,6 +13,7 @@ OC.L10N.register( " Could not set configuration %s" : "%s yapılandırması ayarlanamadı", "Action does not exist" : "Eylem mevcut değil", "The Base DN appears to be wrong" : "Base DN yanlış gibi görünüyor", + "Testing configuration…" : "Yapılandırma sınanıyor...", "Configuration incorrect" : "Yapılandırma geçersiz", "Configuration incomplete" : "Yapılandırma tamamlanmamış", "Configuration OK" : "Yapılandırma tamam", diff --git a/apps/user_ldap/l10n/tr.json b/apps/user_ldap/l10n/tr.json index 183ab05a12e..3edb0f9fc67 100644 --- a/apps/user_ldap/l10n/tr.json +++ b/apps/user_ldap/l10n/tr.json @@ -11,6 +11,7 @@ " Could not set configuration %s" : "%s yapılandırması ayarlanamadı", "Action does not exist" : "Eylem mevcut değil", "The Base DN appears to be wrong" : "Base DN yanlış gibi görünüyor", + "Testing configuration…" : "Yapılandırma sınanıyor...", "Configuration incorrect" : "Yapılandırma geçersiz", "Configuration incomplete" : "Yapılandırma tamamlanmamış", "Configuration OK" : "Yapılandırma tamam", diff --git a/build/integration/features/bootstrap/BasicStructure.php b/build/integration/features/bootstrap/BasicStructure.php index b8fb516fada..9248b2cd252 100644 --- a/build/integration/features/bootstrap/BasicStructure.php +++ b/build/integration/features/bootstrap/BasicStructure.php @@ -261,6 +261,17 @@ trait BasicStructure { } /** + * @Given User :user modifies text of :filename with text :text + * @param string $user + * @param string $filename + * @param string $text + */ + public function modifyTextOfFile($user, $filename, $text) { + self::removeFile("../../data/$user/files", "$filename"); + file_put_contents("../../data/$user/files" . "$filename", "$text"); + } + + /** * @BeforeSuite */ public static function addFilesToSkeleton(){ diff --git a/build/integration/features/bootstrap/FederationContext.php b/build/integration/features/bootstrap/FederationContext.php index 2809c6974fa..55f3a55da0d 100644 --- a/build/integration/features/bootstrap/FederationContext.php +++ b/build/integration/features/bootstrap/FederationContext.php @@ -12,7 +12,7 @@ require __DIR__ . '/../../vendor/autoload.php'; */ class FederationContext implements Context, SnippetAcceptingContext { - use Sharing; + use WebDav; /** * @Given /^User "([^"]*)" from server "(LOCAL|REMOTE)" shares "([^"]*)" with user "([^"]*)" from server "(LOCAL|REMOTE)"$/ diff --git a/build/integration/federation_features/federated.feature b/build/integration/federation_features/federated.feature index acd1f91e908..8bf8e921b0f 100644 --- a/build/integration/federation_features/federated.feature +++ b/build/integration/federation_features/federated.feature @@ -120,6 +120,67 @@ Feature: federated | share_with | user2 | | share_with_displayname | user2 | + Scenario: Overwrite a federated shared file as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt" + And Downloading file "/textfile0 (2).txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared folder as recipient + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA" + When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt" + And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8" + Then Downloaded content should be "BLABLABLA" + + Scenario: Overwrite a federated shared file as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/textfile0 (2).txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/textfile0 (2).txt" + When Downloading file "/textfile0 (2).txt" with range "bytes=0-4" + Then Downloaded content should be "AAAAA" + + Scenario: Overwrite a federated shared folder as recipient using old chunking + Given Using server "REMOTE" + And user "user1" exists + And user "user2" exists + And Using server "LOCAL" + And user "user0" exists + And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE" + And User "user1" from server "REMOTE" accepts last pending share + And Using server "REMOTE" + And As an "user1" + And user "user1" uploads chunk file "1" of "3" with "AAAAA" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "2" of "3" with "BBBBB" to "/PARENT (2)/textfile0.txt" + And user "user1" uploads chunk file "3" of "3" with "CCCCC" to "/PARENT (2)/textfile0.txt" + When Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=3-13" + Then Downloaded content should be "AABBBBBCCCC" diff --git a/core/Application.php b/core/Application.php index 25e2fa76273..a87917b626a 100644 --- a/core/Application.php +++ b/core/Application.php @@ -120,7 +120,8 @@ class Application extends App { $c->query('AppName'), $c->query('Request'), $c->query('UserManager'), - $c->query('OC\Authentication\Token\DefaultTokenProvider'), + $c->query('ServerContainer')->query('OC\Authentication\Token\IProvider'), + $c->query('TwoFactorAuthManager'), $c->query('SecureRandom') ); }); diff --git a/core/Command/Encryption/DecryptAll.php b/core/Command/Encryption/DecryptAll.php index 8d7d26f3d23..d060918a506 100644 --- a/core/Command/Encryption/DecryptAll.php +++ b/core/Command/Encryption/DecryptAll.php @@ -111,7 +111,8 @@ class DecryptAll extends Command { $this->addArgument( 'user', InputArgument::OPTIONAL, - 'user for which you want to decrypt all files (optional)' + 'user for which you want to decrypt all files (optional)', + '' ); } @@ -127,8 +128,16 @@ class DecryptAll extends Command { return; } + $uid = $input->getArgument('user'); + //FIXME WHEN https://github.com/owncloud/core/issues/24994 is fixed + if ($uid === null) { + $message = 'your ownCloud'; + } else { + $message = "$uid's account"; + } + $output->writeln("\n"); - $output->writeln('You are about to start to decrypt all files stored in your ownCloud.'); + $output->writeln("You are about to start to decrypt all files stored in $message."); $output->writeln('It will depend on the encryption module and your setup if this is possible.'); $output->writeln('Depending on the number and size of your files this can take some time'); $output->writeln('Please make sure that no user access his files during this process!'); @@ -140,6 +149,7 @@ class DecryptAll extends Command { $result = $this->decryptAll->decryptAll($input, $output, $user); if ($result === false) { $output->writeln(' aborted.'); + $output->writeln('Server side encryption remains enabled'); $this->config->setAppValue('core', 'encryption_enabled', 'yes'); } $this->resetSingleUserAndTrashbin(); diff --git a/core/Controller/TokenController.php b/core/Controller/TokenController.php index 42cc29bad10..13b1db9044a 100644 --- a/core/Controller/TokenController.php +++ b/core/Controller/TokenController.php @@ -1,4 +1,5 @@ <?php + /** * @author Christoph Wurst <christoph@owncloud.com> * @@ -23,22 +24,27 @@ namespace OC\Core\Controller; use OC\AppFramework\Http; use OC\Authentication\Token\DefaultTokenProvider; +use OC\Authentication\Token\IProvider; use OC\Authentication\Token\IToken; -use OC\User\Manager; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; +use OC\User\Manager as UserManager; +use OCA\User_LDAP\User\Manager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\Http\Response; use OCP\IRequest; use OCP\Security\ISecureRandom; class TokenController extends Controller { - /** @var Manager */ + /** @var UserManager */ private $userManager; - /** @var DefaultTokenProvider */ + /** @var IProvider */ private $tokenProvider; + /** @var TwoFactorAuthManager */ + private $twoFactorAuthManager; + /** @var ISecureRandom */ private $secureRandom; @@ -49,12 +55,12 @@ class TokenController extends Controller { * @param DefaultTokenProvider $tokenProvider * @param ISecureRandom $secureRandom */ - public function __construct($appName, IRequest $request, Manager $userManager, DefaultTokenProvider $tokenProvider, - ISecureRandom $secureRandom) { + public function __construct($appName, IRequest $request, UserManager $userManager, IProvider $tokenProvider, TwoFactorAuthManager $twoFactorAuthManager, ISecureRandom $secureRandom) { parent::__construct($appName, $request); $this->userManager = $userManager; $this->tokenProvider = $tokenProvider; $this->secureRandom = $secureRandom; + $this->twoFactorAuthManager = $twoFactorAuthManager; } /** @@ -70,18 +76,26 @@ class TokenController extends Controller { */ public function generateToken($user, $password, $name = 'unknown client') { if (is_null($user) || is_null($password)) { - $response = new Response(); + $response = new JSONResponse(); $response->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY); return $response; } - $loginResult = $this->userManager->checkPassword($user, $password); - if ($loginResult === false) { - $response = new Response(); + $loginName = $user; + $user = $this->userManager->checkPassword($loginName, $password); + if ($user === false) { + $response = new JSONResponse(); $response->setStatus(Http::STATUS_UNAUTHORIZED); return $response; } + + if ($this->twoFactorAuthManager->isTwoFactorAuthenticated($user)) { + $resp = new JSONResponse(); + $resp->setStatus(Http::STATUS_UNAUTHORIZED); + return $resp; + } + $token = $this->secureRandom->generate(128); - $this->tokenProvider->generateToken($token, $loginResult->getUID(), $user, $password, $name, IToken::PERMANENT_TOKEN); + $this->tokenProvider->generateToken($token, $user->getUID(), $loginName, $password, $name, IToken::PERMANENT_TOKEN); return [ 'token' => $token, ]; diff --git a/core/img/background.jpg b/core/img/background.jpg Binary files differindex a4ede839cb5..ab1d7e6d7a8 100644 --- a/core/img/background.jpg +++ b/core/img/background.jpg diff --git a/core/img/favicon.ico b/core/img/favicon.ico Binary files differindex 96bee5656a9..2e75f57f00d 100644 --- a/core/img/favicon.ico +++ b/core/img/favicon.ico diff --git a/lib/l10n/ar.js b/lib/l10n/ar.js index 21a67883ac3..a7a3e4e4c31 100644 --- a/lib/l10n/ar.js +++ b/lib/l10n/ar.js @@ -1,18 +1,46 @@ OC.L10N.register( "lib", { + "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!", + "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config", + "See %s" : "أنظر %s", + "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة", + "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.", + "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.", + "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.", + "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s", + "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s", + "The library %s is not available." : "مكتبة %s غير متوفرة.", "Unknown filetype" : "نوع الملف غير معروف", "Invalid image" : "الصورة غير صالحة", "today" : "اليوم", "yesterday" : "يوم أمس", + "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"], "last month" : "الشهر الماضي", + "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"], "last year" : "السنةالماضية", "seconds ago" : "منذ ثواني", + "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات", + "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات", + "File name is a reserved word" : "اسم الملف كلمة محجوزة", + "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح", + "File name is too long" : "اسم الملف طويل جداً", "App directory already exists" : "مجلد التطبيق موجود مسبقا", "Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s", + "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s", "No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج", + "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http", + "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي", "Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة", + "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق", "App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml", + "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.", + "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق", + "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud", + "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات", + "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.", "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", @@ -23,9 +51,20 @@ OC.L10N.register( "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", + "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!", + "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.", "Set an admin username." : "اعداد اسم مستخدم للمدير", "Set an admin password." : "اعداد كلمة مرور للمدير", + "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s", + "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح", "%s shared »%s« with you" : "%s شارك »%s« معك", + "%s via %s" : "%s عبر %s", + "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود", + "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s", + "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك", + "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود", + "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s", "Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"", "Apps" : "التطبيقات", "A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح", diff --git a/lib/l10n/ar.json b/lib/l10n/ar.json index eb91606bb52..86284a03538 100644 --- a/lib/l10n/ar.json +++ b/lib/l10n/ar.json @@ -1,16 +1,44 @@ { "translations": { + "Cannot write into \"config\" directory!" : "الكتابة في مجلد \"config\" غير ممكنة!", + "This can usually be fixed by giving the webserver write access to the config directory" : "يمكن حل هذا عادة بإعطاء خادم الوب صلاحية الكتابة في مجلد config", + "See %s" : "أنظر %s", + "Sample configuration detected" : "تم اكتشاف إعدادات عيّنة", + "PHP %s or higher is required." : "إصدار PHP %s أو أحدث منه مطلوب.", + "PHP with a version lower than %s is required." : "PHP الإصدار %s أو أقل مطلوب.", + "%sbit or higher PHP required." : "مكتبات PHP ذات %s بت أو أعلى مطلوبة.", + "Following databases are supported: %s" : "قواعد البيانات التالية مدعومة: %s", + "The command line tool %s could not be found" : "لم يتم العثور على أداة سطر الأوامر %s", + "The library %s is not available." : "مكتبة %s غير متوفرة.", "Unknown filetype" : "نوع الملف غير معروف", "Invalid image" : "الصورة غير صالحة", "today" : "اليوم", "yesterday" : "يوم أمس", + "_%n day ago_::_%n days ago_" : ["قبل ساعات","قبل يوم","قبل يومين","قبل %n يوماً","قبل %n يوماً","قبل %n يوماً"], "last month" : "الشهر الماضي", + "_%n month ago_::_%n months ago_" : ["قبل عدة أيام","قبل شهر","قبل شهرين","قبل %n شهراً","قبل %n شهراً","قبل %n شهراً"], "last year" : "السنةالماضية", "seconds ago" : "منذ ثواني", + "Empty filename is not allowed" : "لا يسمح بأسماء فارغة للملفات", + "4-byte characters are not supported in file names" : "المحارف ذات 4 بايت غير مسموح بها في أسماء الملفات", + "File name is a reserved word" : "اسم الملف كلمة محجوزة", + "File name contains at least one invalid character" : "اسم الملف به ، على الأقل ، حرف غير صالح", + "File name is too long" : "اسم الملف طويل جداً", "App directory already exists" : "مجلد التطبيق موجود مسبقا", "Can't create app folder. Please fix permissions. %s" : "لا يمكن إنشاء مجلد التطبيق. يرجى تعديل الصلاحيات. %s", + "Archive does not contain a directory named %s" : "الأرشيف لا يحتوي مجلداً اسمه %s", "No source specified when installing app" : "لم يتم تحديد المصدر عن تثبيت البرنامج", + "No href specified when installing app from http" : "لم يتم تحديد href عند تثبيت التطبيق من http", + "No path specified when installing app from local file" : "لم يتم تحديد مسار عند تثبيت التطبيق من ملف محلّي", "Archives of type %s are not supported" : "الأرشيفات من نوع %s غير مدعومة", + "Failed to open archive when installing app" : "فشل فتح الأرشيف أثناء تثبيت التطبيق", "App does not provide an info.xml file" : "التطبيق لا يتوفر على ملف info.xml", + "App cannot be installed because appinfo file cannot be read." : "لا يمكن تثبيت التطبيق لأن ملف appinfo غير ممكنة قراءته.", + "Signature could not get checked. Please contact the app developer and check your admin screen." : "لم يتم التحقق من التوقيع. فضلاً اتصل بمطوّر التطبيق و تحقق من شاشة الإدارة في حسابك.", + "App can't be installed because of not allowed code in the App" : "لم يتم تثبيت التطبيق لوجود شفرة غير مسموح بها في التطبيق", + "App can't be installed because it is not compatible with this version of ownCloud" : "لم يتم تثبيت التطبيق لأنه غير متوافق مع هذا الإصدار من ownCloud", + "App can't be installed because it contains the <shipped>true</shipped> tag which is not allowed for non shipped apps" : "لم يتم تثبيت التطبيق لأن به علامة <shipped>true</shipped> التي لايسمح بها في التطبيقات غير المشحونة", + "App can't be installed because the version in info.xml is not the same as the version reported from the app store" : "لم يتم تثبيت التطبيق لأن الإصدار في info.xml مختلف عن الإصدار المذكور في متجر التطبيقات", + "%s enter the database username and name." : "%s أدخِل اسم قاعدة البيانات واسم مستخدمها.", "%s enter the database username." : "%s ادخل اسم المستخدم الخاص بقاعدة البيانات.", "%s enter the database name." : "%s ادخل اسم فاعدة البيانات", "%s you may not use dots in the database name" : "%s لا يسمح لك باستخدام نقطه (.) في اسم قاعدة البيانات", @@ -21,9 +49,20 @@ "You need to enter either an existing account or the administrator." : "انت بحاجة لكتابة اسم مستخدم موجود أو حساب المدير.", "Offending command was: \"%s\", name: %s, password: %s" : "الأمر المخالف كان : \"%s\", اسم المستخدم : %s, كلمة المرور: %s", "PostgreSQL username and/or password not valid" : "اسم المستخدم / أو كلمة المرور الخاصة بـPostgreSQL غير صحيحة", + "Mac OS X is not supported and %s will not work properly on this platform. Use it at your own risk! " : "نظام ماك الإصدار X غير مدعوم و %s لن يعمل بشكل صحيح على هذه المنصة. استخدمه على مسؤوليتك!", + "For the best results, please consider using a GNU/Linux server instead." : "فضلاً ضع في الاعتبار استخدام نظام GNU/Linux بدل الأنظمة الأخرى للحصول على أفضل النتائج.", + "Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP." : "فضلاً إحذف إعداد open_basedir من ملف php.ini لديك أو حوّل إلى PHP إصدار 64 بت.", "Set an admin username." : "اعداد اسم مستخدم للمدير", "Set an admin password." : "اعداد كلمة مرور للمدير", + "Can't create or write into the data directory %s" : "لا يمكن الإنشاء أو الكتابة في مجلد البيانات %s", + "Invalid Federated Cloud ID" : "معرّف سحابة الاتحاد غير صالح", "%s shared »%s« with you" : "%s شارك »%s« معك", + "%s via %s" : "%s عبر %s", + "Sharing %s failed, because the file does not exist" : "فشلت مشاركة %s فالملف غير موجود", + "You are not allowed to share %s" : "أنت غير مسموح لك أن تشارك %s", + "Sharing %s failed, because you can not share with yourself" : "فشلت مشاركة %s لأنك لايمكنك المشاركة مع نفسك", + "Sharing %s failed, because the user %s does not exist" : "فشلت مشاركة %s لأن المستخدم %s غير موجود", + "Share type %s is not valid for %s" : "مشاركة النوع %s غير صالحة لـ %s", "Could not find category \"%s\"" : "تعذر العثور على المجلد \"%s\"", "Apps" : "التطبيقات", "A valid username must be provided" : "يجب ادخال اسم مستخدم صحيح", diff --git a/lib/l10n/ro.js b/lib/l10n/ro.js index 5c5b7844cf7..de13cd796d6 100644 --- a/lib/l10n/ro.js +++ b/lib/l10n/ro.js @@ -29,6 +29,7 @@ OC.L10N.register( "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.", "Empty filename is not allowed" : "Nu este permis fișier fără nume", "Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise", + "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor", "File name is a reserved word" : "Numele fișierului este un cuvânt rezervat", "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", @@ -36,6 +37,8 @@ OC.L10N.register( "Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s", "Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s", "No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației", + "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet", + "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local", "Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate", "Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației", "App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml", @@ -51,10 +54,12 @@ OC.L10N.register( "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", + "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", "Invalid Federated Cloud ID" : "ID invalid cloud federalizat", "%s shared »%s« with you" : "%s Partajat »%s« cu tine de", + "%s via %s" : "%s via %s", "You are not allowed to share %s" : "Nu există permisiunea de partajare %s", "Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s", "Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator", diff --git a/lib/l10n/ro.json b/lib/l10n/ro.json index 566f6d01123..9149650a015 100644 --- a/lib/l10n/ro.json +++ b/lib/l10n/ro.json @@ -27,6 +27,7 @@ "Module with id: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modulul cu id-ul %s nu există. Activează-l în setările tale de aplicație sau contactează-ți administratorul.", "Empty filename is not allowed" : "Nu este permis fișier fără nume", "Dot files are not allowed" : "Fișierele care încep cu caracterul punct nu sunt permise", + "4-byte characters are not supported in file names" : "Caracterele stocate în 4 octeți nu sunt suportate în denumirile fișierelor", "File name is a reserved word" : "Numele fișierului este un cuvânt rezervat", "File name contains at least one invalid character" : "Numele fișierului conține măcar un caracter invalid", "File name is too long" : "Numele fișierului este prea lung", @@ -34,6 +35,8 @@ "Can't create app folder. Please fix permissions. %s" : "Nu se poate crea directorul de aplicație. Repară permisiunile. %s", "Archive does not contain a directory named %s" : "Arhiva nu conține vreun director cu numele %s", "No source specified when installing app" : "Nu a fost specificată vreo sursă la instalarea aplicației", + "No href specified when installing app from http" : "Nu s-a specificat adresa la instalarea aplicației dintr-o sursă de pe Internet", + "No path specified when installing app from local file" : "Nu s-a specificat vreo cale la instalarea aplicației de pe un fișier local", "Archives of type %s are not supported" : "Arhivele de tip %s nu sunt suportate", "Failed to open archive when installing app" : "Deschiderea arhivei a eșuat în timpul instalării aplicației", "App does not provide an info.xml file" : "Aplicația nu furnizează un fișier info.xml", @@ -49,10 +52,12 @@ "DB Error: \"%s\"" : "Eroare Bază de Date: \"%s\"", "Offending command was: \"%s\"" : "Comanda cauză a fost: \"%s\"", "PostgreSQL username and/or password not valid" : "Nume utilizator și/sau parolă PostgreSQL greșită", + "For the best results, please consider using a GNU/Linux server instead." : "Pentru cele mai bune rezultate, ia în calcul folosirea unui server care rulează un sistem de operare GNU/Linux.", "Set an admin username." : "Setează un nume de administrator.", "Set an admin password." : "Setează o parolă de administrator.", "Invalid Federated Cloud ID" : "ID invalid cloud federalizat", "%s shared »%s« with you" : "%s Partajat »%s« cu tine de", + "%s via %s" : "%s via %s", "You are not allowed to share %s" : "Nu există permisiunea de partajare %s", "Sharing %s failed, because this item is already shared with %s" : "Partajarea %s a eșuat deoarece acest element este deja partajat cu %s", "Not allowed to create a federated share with the same user" : "Nu este permisă crearea unei partajări federalizate cu acelaşi utilizator", diff --git a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php index 04b98c6c5a0..7746d6be915 100644 --- a/lib/private/Authentication/Token/DefaultTokenCleanupJob.php +++ b/lib/private/Authentication/Token/DefaultTokenCleanupJob.php @@ -28,6 +28,7 @@ class DefaultTokenCleanupJob extends Job { protected function run($argument) { /* @var $provider DefaultTokenProvider */ + // TODO: add OC\Authentication\Token\IProvider::invalidateOldTokens and query interface $provider = OC::$server->query('OC\Authentication\Token\DefaultTokenProvider'); $provider->invalidateOldTokens(); } diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php index 8676bc09575..34a3e1bff91 100644 --- a/lib/private/Encryption/DecryptAll.php +++ b/lib/private/Encryption/DecryptAll.php @@ -80,7 +80,7 @@ class DecryptAll { $this->input = $input; $this->output = $output; - if (!empty($user) && $this->userManager->userExists($user) === false) { + if ($user !== '' && $this->userManager->userExists($user) === false) { $this->output->writeln('User "' . $user . '" does not exist. Please check the username and try again'); return false; } @@ -141,7 +141,7 @@ class DecryptAll { $this->output->writeln("\n"); $userList = []; - if (empty($user)) { + if ($user === '') { $fetchUsersProgress = new ProgressBar($this->output); $fetchUsersProgress->setFormat(" %message% \n [%bar%]"); diff --git a/lib/private/Files/Cache/Updater.php b/lib/private/Files/Cache/Updater.php index 820941abae1..4e17c4d778d 100644 --- a/lib/private/Files/Cache/Updater.php +++ b/lib/private/Files/Cache/Updater.php @@ -231,7 +231,10 @@ class Updater implements IUpdater { $parentId = $this->cache->getParentId($internalPath); $parent = dirname($internalPath); if ($parentId != -1) { - $this->cache->update($parentId, array('storage_mtime' => $this->storage->filemtime($parent))); + $mtime = $this->storage->filemtime($parent); + if ($mtime !== false) { + $this->cache->update($parentId, array('storage_mtime' => $mtime)); + } } } } diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index cec6a42a2c0..0c1b69108d4 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -643,6 +643,9 @@ abstract class Common implements Storage, ILockingStorage { $data = []; $data['mimetype'] = $this->getMimeType($path); $data['mtime'] = $this->filemtime($path); + if ($data['mtime'] === false) { + $data['mtime'] = time(); + } if ($data['mimetype'] == 'httpd/unix-directory') { $data['size'] = -1; //unknown } else { diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index b07e26a3358..acd4c3b4838 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -157,7 +157,7 @@ class Local extends \OC\Files\Storage\Common { public function filemtime($path) { clearstatcache($this->getSourcePath($path)); - return filemtime($this->getSourcePath($path)); + return $this->file_exists($path) ? filemtime($this->getSourcePath($path)) : false; } public function touch($path, $mtime = null) { @@ -188,7 +188,7 @@ class Local extends \OC\Files\Storage\Common { return ''; } - $handle = fopen($fileName,'rb'); + $handle = fopen($fileName, 'rb'); $content = fread($handle, $fileSize); fclose($handle); return $content; @@ -377,7 +377,7 @@ class Local extends \OC\Files\Storage\Common { * @return bool */ public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - if($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')){ + if ($sourceStorage->instanceOfStorage('\OC\Files\Storage\Local')) { /** * @var \OC\Files\Storage\Local $sourceStorage */ diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index f738542ea8c..e9daa123470 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -337,10 +337,17 @@ class View { return $this->removeMount($mount, $absolutePath); } if ($this->is_dir($path)) { - return $this->basicOperation('rmdir', $path, array('delete')); + $result = $this->basicOperation('rmdir', $path, array('delete')); } else { - return false; + $result = false; + } + + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); } + return $result; } /** @@ -429,7 +436,7 @@ class View { /** * @param string $path - * @param int $from + * @param int $from * @param int $to * @return bool|mixed * @throws \OCP\Files\InvalidPathException @@ -441,18 +448,18 @@ class View { $handle = $this->fopen($path, 'rb'); if ($handle) { if (fseek($handle, $from) === 0) { - $chunkSize = 8192; // 8 kB chunks - $end = $to + 1; - while (!feof($handle) && ftell($handle) < $end) { - $len = $end-ftell($handle); - if ($len > $chunkSize) { - $len = $chunkSize; + $chunkSize = 8192; // 8 kB chunks + $end = $to + 1; + while (!feof($handle) && ftell($handle) < $end) { + $len = $end - ftell($handle); + if ($len > $chunkSize) { + $len = $chunkSize; + } + echo fread($handle, $len); + flush(); } - echo fread($handle, $len); - flush(); - } - $size = ftell($handle) - $from; - return $size; + $size = ftell($handle) - $from; + return $size; } throw new \OCP\Files\UnseekableException('fseek error'); @@ -679,7 +686,15 @@ class View { if ($mount and $mount->getInternalPath($absolutePath) === '') { return $this->removeMount($mount, $absolutePath); } - return $this->basicOperation('unlink', $path, array('delete')); + $result = $this->basicOperation('unlink', $path, array('delete')); + if (!$result && !$this->file_exists($path)) { //clear ghost files from the cache on delete + $storage = $mount->getStorage(); + $internalPath = $mount->getInternalPath($absolutePath); + $storage->getUpdater()->remove($internalPath); + return true; + } else { + return $result; + } } /** diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php index 25cfff3036b..f107d47faf7 100644 --- a/lib/private/legacy/helper.php +++ b/lib/private/legacy/helper.php @@ -469,7 +469,7 @@ class OC_Helper { */ public static function freeSpace($dir) { $freeSpace = \OC\Files\Filesystem::free_space($dir); - if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN) { + if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { $freeSpace = max($freeSpace, 0); return $freeSpace; } else { diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 03f471bc3b4..e0fccbd9539 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -14,7 +14,7 @@ var UserList = { availableGroups: [], offset: 0, usersToLoad: 10, //So many users will be loaded when user scrolls down - initialUsersToLoad: 250, //initial number of users to load + initialUsersToLoad: 50, //initial number of users to load currentGid: '', filter: '', diff --git a/settings/l10n/sv.js b/settings/l10n/sv.js index d056027b43e..aa5e3a8ef48 100644 --- a/settings/l10n/sv.js +++ b/settings/l10n/sv.js @@ -137,6 +137,7 @@ OC.L10N.register( "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "Läs-bara konfigureringen har blivit aktiv. Detta förhindrar att några konfigureringar kan sättas via web-gränssnittet.", "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställd för att rensa inline doc block. Detta kommer att göra flera kärnapplikationer otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", + "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Din databas kör inte \"READ COMMITED\" tansaktionsisoleringsnvån. Detta kan orsaka problem när multipla aktioner körs parallellt.", "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Din server använder Microsoft Windows. Vi rekommenderar starkt Linux för en optimal användarerfarenhet.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s under version %2$s är installerad, för stabilitet och prestanda rekommenderar vi uppdatering till en nyare %1$s version.", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "PHP-modulen 'fileinfo' saknas. Vi rekommenderar starkt att aktivera den här modulen för att kunna upptäcka korrekt mime-typ.", @@ -266,6 +267,7 @@ OC.L10N.register( "Current password" : "Nuvarande lösenord", "New password" : "Nytt lösenord", "Change password" : "Ändra lösenord", + "These are the web, desktop and mobile clients currently logged in to your ownCloud." : "Dessa webbläsare,pc och mobila klienter är för tillfället inloggade på din ownCloud.", "Browser" : "Webbläsare", "Most recent activity" : "Senaste aktivitet", "You've linked these devices." : "Du har länkat dessa enheter.", diff --git a/settings/l10n/sv.json b/settings/l10n/sv.json index 61c30b1b0e3..0cf69401b31 100644 --- a/settings/l10n/sv.json +++ b/settings/l10n/sv.json @@ -135,6 +135,7 @@ "The Read-Only config has been enabled. This prevents setting some configurations via the web-interface. Furthermore, the file needs to be made writable manually for every update." : "Läs-bara konfigureringen har blivit aktiv. Detta förhindrar att några konfigureringar kan sättas via web-gränssnittet.", "PHP is apparently setup to strip inline doc blocks. This will make several core apps inaccessible." : "PHP är tydligen inställd för att rensa inline doc block. Detta kommer att göra flera kärnapplikationer otillgängliga.", "This is probably caused by a cache/accelerator such as Zend OPcache or eAccelerator." : "Detta orsakas troligtvis av en cache/accelerator som t ex Zend OPchache eller eAccelerator.", + "Your database does not run with \"READ COMMITED\" transaction isolation level. This can cause problems when multiple actions are executed in parallel." : "Din databas kör inte \"READ COMMITED\" tansaktionsisoleringsnvån. Detta kan orsaka problem när multipla aktioner körs parallellt.", "Your server is running on Microsoft Windows. We highly recommend Linux for optimal user experience." : "Din server använder Microsoft Windows. Vi rekommenderar starkt Linux för en optimal användarerfarenhet.", "%1$s below version %2$s is installed, for stability and performance reasons we recommend updating to a newer %1$s version." : "%1$s under version %2$s är installerad, för stabilitet och prestanda rekommenderar vi uppdatering till en nyare %1$s version.", "The PHP module 'fileinfo' is missing. We strongly recommend to enable this module to get best results with mime-type detection." : "PHP-modulen 'fileinfo' saknas. Vi rekommenderar starkt att aktivera den här modulen för att kunna upptäcka korrekt mime-typ.", @@ -264,6 +265,7 @@ "Current password" : "Nuvarande lösenord", "New password" : "Nytt lösenord", "Change password" : "Ändra lösenord", + "These are the web, desktop and mobile clients currently logged in to your ownCloud." : "Dessa webbläsare,pc och mobila klienter är för tillfället inloggade på din ownCloud.", "Browser" : "Webbläsare", "Most recent activity" : "Senaste aktivitet", "You've linked these devices." : "Du har länkat dessa enheter.", diff --git a/tests/Core/Controller/TokenControllerTest.php b/tests/Core/Controller/TokenControllerTest.php index 386140a8a4f..b6b54b14fad 100644 --- a/tests/Core/Controller/TokenControllerTest.php +++ b/tests/Core/Controller/TokenControllerTest.php @@ -23,8 +23,9 @@ namespace Tests\Core\Controller; use OC\AppFramework\Http; +use OC\Authentication\Token\IToken; use OC\Core\Controller\TokenController; -use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\JSONResponse; use Test\TestCase; class TokenControllerTest extends TestCase { @@ -34,6 +35,7 @@ class TokenControllerTest extends TestCase { private $request; private $userManager; private $tokenProvider; + private $twoFactorAuthManager; private $secureRandom; protected function setUp() { @@ -43,17 +45,17 @@ class TokenControllerTest extends TestCase { $this->userManager = $this->getMockBuilder('\OC\User\Manager') ->disableOriginalConstructor() ->getMock(); - $this->tokenProvider = $this->getMockBuilder('\OC\Authentication\Token\DefaultTokenProvider') + $this->tokenProvider = $this->getMock('\OC\Authentication\Token\IProvider'); + $this->twoFactorAuthManager = $this->getMockBuilder('\OC\Authentication\TwoFactorAuth\Manager') ->disableOriginalConstructor() ->getMock(); $this->secureRandom = $this->getMock('\OCP\Security\ISecureRandom'); - $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider, - $this->secureRandom); + $this->tokenController = new TokenController('core', $this->request, $this->userManager, $this->tokenProvider, $this->twoFactorAuthManager, $this->secureRandom); } public function testWithoutCredentials() { - $expected = new Response(); + $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_UNPROCESSABLE_ENTITY); $actual = $this->tokenController->generateToken(null, null); @@ -66,7 +68,7 @@ class TokenControllerTest extends TestCase { ->method('checkPassword') ->with('john', 'passme') ->will($this->returnValue(false)); - $expected = new Response(); + $expected = new JSONResponse(); $expected->setStatus(Http::STATUS_UNAUTHORIZED); $actual = $this->tokenController->generateToken('john', 'passme'); @@ -83,13 +85,17 @@ class TokenControllerTest extends TestCase { $user->expects($this->once()) ->method('getUID') ->will($this->returnValue('john')); + $this->twoFactorAuthManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(false)); $this->secureRandom->expects($this->once()) ->method('generate') ->with(128) ->will($this->returnValue('verysecurerandomtoken')); $this->tokenProvider->expects($this->once()) ->method('generateToken') - ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', \OC\Authentication\Token\IToken::PERMANENT_TOKEN); + ->with('verysecurerandomtoken', 'john', 'john', '123456', 'unknown client', IToken::PERMANENT_TOKEN); $expected = [ 'token' => 'verysecurerandomtoken' ]; @@ -99,4 +105,24 @@ class TokenControllerTest extends TestCase { $this->assertEquals($expected, $actual); } + public function testWithValidCredentialsBut2faEnabled() { + $user = $this->getMock('\OCP\IUser'); + $this->userManager->expects($this->once()) + ->method('checkPassword') + ->with('john', '123456') + ->will($this->returnValue($user)); + $this->twoFactorAuthManager->expects($this->once()) + ->method('isTwoFactorAuthenticated') + ->with($user) + ->will($this->returnValue(true)); + $this->secureRandom->expects($this->never()) + ->method('generate'); + $expected = new JSONResponse(); + $expected->setStatus(Http::STATUS_UNAUTHORIZED); + + $actual = $this->tokenController->generateToken('john', '123456'); + + $this->assertEquals($expected, $actual); + } + } diff --git a/tests/lib/Encryption/DecryptAllTest.php b/tests/lib/Encryption/DecryptAllTest.php index ffcbbc74a99..d7cf2fb7baf 100644 --- a/tests/lib/Encryption/DecryptAllTest.php +++ b/tests/lib/Encryption/DecryptAllTest.php @@ -86,13 +86,25 @@ class DecryptAllTest extends TestCase { $this->invokePrivate($this->instance, 'output', [$this->outputInterface]); } + public function dataDecryptAll() { + return [ + [true, 'user1', true], + [false, 'user1', true], + [true, '0', true], + [false, '0', true], + [true, '', false], + ]; + } + /** - * @dataProvider dataTrueFalse + * @dataProvider dataDecryptAll * @param bool $prepareResult + * @param string $user + * @param bool $userExistsChecked */ - public function testDecryptAll($prepareResult, $user) { + public function testDecryptAll($prepareResult, $user, $userExistsChecked) { - if (!empty($user)) { + if ($userExistsChecked) { $this->userManager->expects($this->once())->method('userExists')->willReturn(true); } else { $this->userManager->expects($this->never())->method('userExists'); @@ -125,15 +137,6 @@ class DecryptAllTest extends TestCase { $instance->decryptAll($this->inputInterface, $this->outputInterface, $user); } - public function dataTrueFalse() { - return [ - [true, 'user1'], - [false, 'user1'], - [true, ''], - [true, null] - ]; - } - /** * test decrypt all call with a user who doesn't exists */ @@ -147,8 +150,16 @@ class DecryptAllTest extends TestCase { ); } + public function dataTrueFalse() { + return [ + [true], + [false], + ]; + } + /** * @dataProvider dataTrueFalse + * @param bool $success */ public function testPrepareEncryptionModules($success) { diff --git a/tests/lib/Files/ViewTest.php b/tests/lib/Files/ViewTest.php index 2c27bb64a70..59b17b83958 100644 --- a/tests/lib/Files/ViewTest.php +++ b/tests/lib/Files/ViewTest.php @@ -2417,7 +2417,7 @@ class ViewTest extends \Test\TestCase { $content = $view->getDirectoryContent('', $filter); - $files = array_map(function(FileInfo $info) { + $files = array_map(function (FileInfo $info) { return $info->getName(); }, $content); sort($files); @@ -2444,4 +2444,53 @@ class ViewTest extends \Test\TestCase { $data = $view->getFileInfo('.'); $this->assertEquals('', $data->getChecksum()); } + + public function testDeleteGhostFile() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->file_put_contents('foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->unlink('foo.txt'); + + $this->assertTrue($cache->inCache('foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->unlink('foo.txt'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } + + public function testDeleteGhostFolder() { + $storage = new Temporary(array()); + $scanner = $storage->getScanner(); + $cache = $storage->getCache(); + $storage->mkdir('foo'); + $storage->file_put_contents('foo/foo.txt', 'bar'); + \OC\Files\Filesystem::mount($storage, array(), '/test/'); + $scanner->scan(''); + + $storage->rmdir('foo'); + + $this->assertTrue($cache->inCache('foo')); + $this->assertTrue($cache->inCache('foo/foo.txt')); + + $view = new \OC\Files\View('/test'); + $rootInfo = $view->getFileInfo(''); + $this->assertEquals(3, $rootInfo->getSize()); + $view->rmdir('foo'); + $newInfo = $view->getFileInfo(''); + + $this->assertFalse($cache->inCache('foo')); + $this->assertFalse($cache->inCache('foo/foo.txt')); + $this->assertNotEquals($rootInfo->getEtag(), $newInfo->getEtag()); + $this->assertEquals(0, $newInfo->getSize()); + } } |