diff options
35 files changed, 376 insertions, 189 deletions
diff --git a/apps/encryption/js/encryption.js b/apps/encryption/js/encryption.js index 7ed49f77311..ea6a5596f24 100644 --- a/apps/encryption/js/encryption.js +++ b/apps/encryption/js/encryption.js @@ -15,7 +15,7 @@ if (!OC.Encryption) { */ OC.Encryption = { displayEncryptionWarning: function () { - if (!OC.Notification.isHidden()) { + if (!OC.currentUser || !OC.Notification.isHidden()) { return; } diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 9e55e428057..e4bf791761d 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -581,7 +581,9 @@ a.action>img { #fileList tr:hover a.action, #fileList a.action.permanent, #fileList tr:focus a.action, -#fileList a.action.permanent +#fileList a.action.permanent, +#fileList tr:hover a.action.no-permission:hover, +#fileList tr:focus a.action.no-permission:focus /*#fileList .name:focus .action*/ { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index b335f1f6432..1956fda0077 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -288,9 +288,15 @@ } else if (mountType === 'shared-root') { deleteTitle = t('files', 'Unshare'); } + var cssClasses = 'action delete icon-delete'; + if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) { + // add css class no-permission to delete icon + cssClasses += ' no-permission'; + deleteTitle = t('files', 'No permission to delete'); + } var $actionLink = $('<a href="#" original-title="' + escapeHTML(deleteTitle) + - '" class="action delete icon-delete">' + + '" class="' +cssClasses + '">' + '<span class="hidden-visually">' + escapeHTML(deleteTitle) + '</span>' + '</a>' ); @@ -426,12 +432,17 @@ name: 'Delete', displayName: '', mime: 'all', - permissions: OC.PERMISSION_DELETE, + // permission is READ because we show a hint instead if there is no permission + permissions: OC.PERMISSION_READ, icon: function() { return OC.imagePath('core', 'actions/delete'); }, render: _.bind(this._renderDeleteAction, this), actionHandler: function(fileName, context) { + // if there is no permission to delete do nothing + if((context.$file.data('permissions') & OC.PERMISSION_DELETE) === 0) { + return; + } context.fileList.do_delete(fileName, context.dir); $('.tipsy').remove(); } diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index 828aec9b6b9..53fa8707674 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -157,6 +157,48 @@ describe('OCA.Files.FileActions tests', function() { expect(deleteStub.getCall(0).args[1]).toEqual('/somepath/dir'); deleteStub.restore(); }); + it('shows delete hint when no permission to delete', function() { + var deleteStub = sinon.stub(fileList, 'do_delete'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + path: '/somepath/dir', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456', + permissions: OC.PERMISSION_READ + }; + var $tr = fileList.add(fileData); + FileActions.display($tr.find('td.filename'), true, fileList); + + var $action = $tr.find('.action.delete'); + + expect($action.hasClass('no-permission')).toEqual(true); + deleteStub.restore(); + }); + it('shows delete hint not when permission to delete', function() { + var deleteStub = sinon.stub(fileList, 'do_delete'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + path: '/somepath/dir', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456', + permissions: OC.PERMISSION_DELETE + }; + var $tr = fileList.add(fileData); + FileActions.display($tr.find('td.filename'), true, fileList); + + var $action = $tr.find('.action.delete'); + + expect($action.hasClass('no-permission')).toEqual(false); + deleteStub.restore(); + }); it('passes context to action handler', function() { var actionStub = sinon.stub(); var fileData = { diff --git a/apps/files_external/3rdparty/composer.json b/apps/files_external/3rdparty/composer.json index 8992220d843..9680d92e548 100644 --- a/apps/files_external/3rdparty/composer.json +++ b/apps/files_external/3rdparty/composer.json @@ -6,7 +6,7 @@ "vendor-dir": "." }, "require": { - "icewind/smb": "1.0.0", + "icewind/smb": "1.0.1", "icewind/streams": "0.2" } } diff --git a/apps/files_external/3rdparty/composer.lock b/apps/files_external/3rdparty/composer.lock index ffd5c7c3c65..84fbb046d5a 100644 --- a/apps/files_external/3rdparty/composer.lock +++ b/apps/files_external/3rdparty/composer.lock @@ -4,24 +4,24 @@ "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "2554253c9f91c67cd0db753a69105bc0", + "hash": "7b46d64e33feb600c5f0ec830b211e6f", "packages": [ { "name": "icewind/smb", - "version": "v1.0.0", + "version": "v1.0.1", "source": { "type": "git", "url": "https://github.com/icewind1991/SMB.git", - "reference": "4f5d9db3a9397e30476f92eb753751b54d6d4fa5" + "reference": "8041bc1960bf2da94e60b88b34e5c78300eac476" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/4f5d9db3a9397e30476f92eb753751b54d6d4fa5", - "reference": "4f5d9db3a9397e30476f92eb753751b54d6d4fa5", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476", + "reference": "8041bc1960bf2da94e60b88b34e5c78300eac476", "shasum": "" }, "require": { - "icewind/streams": "0.2.x", + "icewind/streams": "0.2.*", "php": ">=5.3" }, "require-dev": { @@ -45,7 +45,7 @@ } ], "description": "php wrapper for smbclient and libsmbclient-php", - "time": "2015-04-10 12:10:08" + "time": "2015-04-20 11:16:24" }, { "name": "icewind/streams", diff --git a/apps/files_external/3rdparty/composer/installed.json b/apps/files_external/3rdparty/composer/installed.json index 441ed1f14fa..42e8fdd29db 100644 --- a/apps/files_external/3rdparty/composer/installed.json +++ b/apps/files_external/3rdparty/composer/installed.json @@ -43,27 +43,27 @@ }, { "name": "icewind/smb", - "version": "v1.0.0", - "version_normalized": "1.0.0.0", + "version": "v1.0.1", + "version_normalized": "1.0.1.0", "source": { "type": "git", "url": "https://github.com/icewind1991/SMB.git", - "reference": "4f5d9db3a9397e30476f92eb753751b54d6d4fa5" + "reference": "8041bc1960bf2da94e60b88b34e5c78300eac476" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/icewind1991/SMB/zipball/4f5d9db3a9397e30476f92eb753751b54d6d4fa5", - "reference": "4f5d9db3a9397e30476f92eb753751b54d6d4fa5", + "url": "https://api.github.com/repos/icewind1991/SMB/zipball/8041bc1960bf2da94e60b88b34e5c78300eac476", + "reference": "8041bc1960bf2da94e60b88b34e5c78300eac476", "shasum": "" }, "require": { - "icewind/streams": "0.2.x", + "icewind/streams": "0.2.*", "php": ">=5.3" }, "require-dev": { "satooshi/php-coveralls": "dev-master" }, - "time": "2015-04-10 12:10:08", + "time": "2015-04-20 11:16:24", "type": "library", "installation-source": "source", "autoload": { diff --git a/apps/files_external/3rdparty/icewind/smb/composer.json b/apps/files_external/3rdparty/icewind/smb/composer.json index 92f4280d775..593eb728716 100644 --- a/apps/files_external/3rdparty/icewind/smb/composer.json +++ b/apps/files_external/3rdparty/icewind/smb/composer.json @@ -10,7 +10,7 @@ ], "require" : { "php": ">=5.3", - "icewind/streams": "0.2.x" + "icewind/streams": "0.2.*" }, "require-dev": { "satooshi/php-coveralls" : "dev-master" diff --git a/apps/files_external/3rdparty/icewind/smb/src/Parser.php b/apps/files_external/3rdparty/icewind/smb/src/Parser.php index 8b4de7825e4..6af70143c52 100644 --- a/apps/files_external/3rdparty/icewind/smb/src/Parser.php +++ b/apps/files_external/3rdparty/icewind/smb/src/Parser.php @@ -91,7 +91,6 @@ class Parser { $mode = 0; $size = 0; foreach ($output as $line) { - list($name, $value) = explode(':', $line, 2); // A line = explode statement may not fill all array elements // properly. May happen when accessing non Windows Fileservers $words = explode(':', $line, 2); diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js index f63553fe50a..4b6b3b7d305 100644 --- a/apps/files_external/l10n/fr.js +++ b/apps/files_external/l10n/fr.js @@ -37,7 +37,7 @@ OC.L10N.register( "Password (required for OpenStack Object Storage)" : "Mot de passe (requis pour OpenStack Object Storage)", "Service Name (required for OpenStack Object Storage)" : "Nom du service (requis pour le stockage OpenStack)", "URL of identity endpoint (required for OpenStack Object Storage)" : "URL du point d'accès d'identité (requis pour le stockage OpenStack)", - "Timeout of HTTP requests in seconds" : "Temps maximal de requête HTTP en seconde", + "Timeout of HTTP requests in seconds" : "Délai d'attente maximal des requêtes HTTP en secondes", "Share" : "Partager", "SMB / CIFS using OC login" : "SMB / CIFS en utilisant les identifiants OC", "Username as share" : "Nom d'utilisateur comme nom de partage", diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json index af99c2c935a..add46dd3021 100644 --- a/apps/files_external/l10n/fr.json +++ b/apps/files_external/l10n/fr.json @@ -35,7 +35,7 @@ "Password (required for OpenStack Object Storage)" : "Mot de passe (requis pour OpenStack Object Storage)", "Service Name (required for OpenStack Object Storage)" : "Nom du service (requis pour le stockage OpenStack)", "URL of identity endpoint (required for OpenStack Object Storage)" : "URL du point d'accès d'identité (requis pour le stockage OpenStack)", - "Timeout of HTTP requests in seconds" : "Temps maximal de requête HTTP en seconde", + "Timeout of HTTP requests in seconds" : "Délai d'attente maximal des requêtes HTTP en secondes", "Share" : "Partager", "SMB / CIFS using OC login" : "SMB / CIFS en utilisant les identifiants OC", "Username as share" : "Nom d'utilisateur comme nom de partage", diff --git a/apps/files_external/tests/js/mountsfilelistSpec.js b/apps/files_external/tests/js/mountsfilelistSpec.js index a4e4fec1177..c7ea819d2fe 100644 --- a/apps/files_external/tests/js/mountsfilelistSpec.js +++ b/apps/files_external/tests/js/mountsfilelistSpec.js @@ -128,7 +128,7 @@ describe('OCA.External.FileList tests', function() { '?dir=/another%20mount%20points/sftp%20mount' ); expect($tr.find('.nametext').text().trim()).toEqual('sftp mount'); - expect($tr.find('.column-scope').text().trim()).toEqual('System'); + expect($tr.find('.column-scope > span').text().trim()).toEqual('System'); expect($tr.find('.column-backend').text().trim()).toEqual('SFTP'); $tr = $rows.eq(1); diff --git a/apps/files_sharing/l10n/fr.js b/apps/files_sharing/l10n/fr.js index 74f31dba7cf..6fd7bf4a9d9 100644 --- a/apps/files_sharing/l10n/fr.js +++ b/apps/files_sharing/l10n/fr.js @@ -28,7 +28,7 @@ OC.L10N.register( "Shared by" : "Partagé par", "A file or folder has been <strong>shared</strong>" : "Un fichier ou un répertoire a été <strong>partagé</strong>", "A file or folder was shared from <strong>another server</strong>" : "Un fichier ou un répertoire a été partagé depuis <strong>un autre serveur</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé a été <strong>téléchargé</strong>", + "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé publiquement a été <strong>téléchargé</strong>", "You received a new remote share from %s" : "Vous avez reçu un partage distant de %s", "%1$s accepted remote share %2$s" : "%1$s a accepté le partage distant %2$s", "%1$s declined remote share %2$s" : "%1$s a refusé le partage distant %2$s", diff --git a/apps/files_sharing/l10n/fr.json b/apps/files_sharing/l10n/fr.json index d7ed1692d6f..07629def1d3 100644 --- a/apps/files_sharing/l10n/fr.json +++ b/apps/files_sharing/l10n/fr.json @@ -26,7 +26,7 @@ "Shared by" : "Partagé par", "A file or folder has been <strong>shared</strong>" : "Un fichier ou un répertoire a été <strong>partagé</strong>", "A file or folder was shared from <strong>another server</strong>" : "Un fichier ou un répertoire a été partagé depuis <strong>un autre serveur</strong>", - "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé a été <strong>téléchargé</strong>", + "A public shared file or folder was <strong>downloaded</strong>" : "Un fichier ou un répertoire partagé publiquement a été <strong>téléchargé</strong>", "You received a new remote share from %s" : "Vous avez reçu un partage distant de %s", "%1$s accepted remote share %2$s" : "%1$s a accepté le partage distant %2$s", "%1$s declined remote share %2$s" : "%1$s a refusé le partage distant %2$s", diff --git a/apps/files_sharing/l10n/gl.js b/apps/files_sharing/l10n/gl.js index 9eeb97cde86..11cc0348f34 100644 --- a/apps/files_sharing/l10n/gl.js +++ b/apps/files_sharing/l10n/gl.js @@ -21,7 +21,7 @@ OC.L10N.register( "Remote share password" : "Contrasinal da compartición remota", "Cancel" : "Cancelar", "Add remote share" : "Engadir unha compartición remota", - "You can upload into this folder" : "Pode envialo envialo a este cartafol", + "You can upload into this folder" : "Pode envialo a este cartafol", "No ownCloud installation (7 or higher) found at {remote}" : "Non se atopa unha instalación de ownCloud (7 ou superior) en {remote}", "Invalid ownCloud url" : "URL incorrecto do ownCloud", "Share" : "Compartir", diff --git a/apps/files_sharing/l10n/gl.json b/apps/files_sharing/l10n/gl.json index 4b222329227..01972ddee16 100644 --- a/apps/files_sharing/l10n/gl.json +++ b/apps/files_sharing/l10n/gl.json @@ -19,7 +19,7 @@ "Remote share password" : "Contrasinal da compartición remota", "Cancel" : "Cancelar", "Add remote share" : "Engadir unha compartición remota", - "You can upload into this folder" : "Pode envialo envialo a este cartafol", + "You can upload into this folder" : "Pode envialo a este cartafol", "No ownCloud installation (7 or higher) found at {remote}" : "Non se atopa unha instalación de ownCloud (7 ou superior) en {remote}", "Invalid ownCloud url" : "URL incorrecto do ownCloud", "Share" : "Compartir", diff --git a/apps/files_sharing/lib/readonlycache.php b/apps/files_sharing/lib/readonlycache.php deleted file mode 100644 index c7640f896f4..00000000000 --- a/apps/files_sharing/lib/readonlycache.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * @author Joas Schilling <nickvergessen@owncloud.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <icewind@owncloud.com> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OCA\Files_Sharing; - -use OC\Files\Cache\Cache; - -class ReadOnlyCache extends Cache { - public function get($path) { - $data = parent::get($path); - if ($data !== false) { - $data['permissions'] &= (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_SHARE); - } - return $data; - } - - public function getFolderContents($path) { - $content = parent::getFolderContents($path); - foreach ($content as &$data) { - $data['permissions'] &= (\OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_SHARE); - } - return $content; - } -} diff --git a/apps/files_sharing/lib/readonlywrapper.php b/apps/files_sharing/lib/readonlywrapper.php index 067000ff47c..a5d84f7f5a2 100644 --- a/apps/files_sharing/lib/readonlywrapper.php +++ b/apps/files_sharing/lib/readonlywrapper.php @@ -23,7 +23,9 @@ namespace OCA\Files_Sharing; +use OC\Files\Cache\Wrapper\CachePermissionsMask; use OC\Files\Storage\Wrapper\Wrapper; +use OCP\Constants; class ReadOnlyWrapper extends Wrapper { public function isUpdatable($path) { @@ -66,6 +68,7 @@ class ReadOnlyWrapper extends Wrapper { if (!$storage) { $storage = $this; } - return new ReadOnlyCache($storage); + $sourceCache = $this->storage->getCache($path, $storage); + return new CachePermissionsMask($sourceCache, Constants::PERMISSION_READ | Constants::PERMISSION_SHARE); } } diff --git a/apps/files_sharing/publicwebdav.php b/apps/files_sharing/publicwebdav.php index 3a961f5d757..3be464c64f0 100644 --- a/apps/files_sharing/publicwebdav.php +++ b/apps/files_sharing/publicwebdav.php @@ -56,7 +56,8 @@ $server->addPlugin(new \OC\Connector\Sabre\ExceptionLoggerPlugin('webdav', \OC:: // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $authBackend) { $share = $authBackend->getShare(); - $owner = $share['uid_owner']; + $rootShare = \OCP\Share::resolveReShare($share); + $owner = $rootShare['uid_owner']; $isWritable = $share['permissions'] & (\OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_CREATE); $fileId = $share['file_source']; diff --git a/apps/files_sharing/tests/readonlycache.php b/apps/files_sharing/tests/readonlycache.php deleted file mode 100644 index 5da200fa78f..00000000000 --- a/apps/files_sharing/tests/readonlycache.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php -/** - * @author Olivier Paroz <owncloud@interfasys.ch> - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OCA\Files_Sharing\Tests; - -class ReadOnlyCache extends TestCase { - - /** @var \OC\Files\Storage\Storage */ - protected $storage; - - /** @var \OC\Files\Storage\StorageFactory */ - protected $loader; - - /** @var \OC\Files\Mount\MountPoint */ - protected $readOnlyMount; - - /** @var \OCA\Files_Sharing\ReadOnlyWrapper */ - protected $readOnlyStorage; - - /** @var \OC\Files\Cache\Cache */ - protected $readOnlyCache; - - protected function setUp() { - parent::setUp(); - - $this->view->mkdir('readonly'); - $this->view->file_put_contents('readonly/foo.txt', 'foo'); - $this->view->file_put_contents('readonly/bar.txt', 'bar'); - - list($this->storage) = $this->view->resolvePath(''); - $this->loader = new \OC\Files\Storage\StorageFactory(); - $this->readOnlyMount = new \OC\Files\Mount\MountPoint($this->storage, - '/readonly', [[]], $this->loader); - $this->readOnlyStorage = $this->loader->getInstance($this->readOnlyMount, - '\OCA\Files_Sharing\ReadOnlyWrapper', ['storage' => $this->storage]); - - $this->readOnlyCache = $this->readOnlyStorage->getCache(); - } - - public function testSetup() { - $this->assertTrue($this->view->file_exists('/readonly/foo.txt')); - - $perms = $this->readOnlyStorage->getPermissions('files/readonly/foo.txt'); - $this->assertEquals(17, $perms); - - $this->assertFalse($this->readOnlyStorage->unlink('files/readonly/foo.txt')); - $this->assertTrue($this->readOnlyStorage->file_exists('files/readonly/foo.txt')); - - $this->assertInstanceOf('\OCA\Files_Sharing\ReadOnlyCache', $this->readOnlyCache); - } - - public function testGetWhenFileExists() { - $result = $this->readOnlyCache->get('files/readonly/foo.txt'); - $this->assertNotEmpty($result); - } - - public function testGetWhenFileDoesNotExist() { - $result = $this->readOnlyCache->get('files/readonly/proof does not exist.md'); - $this->assertFalse($result); - } - - public function testGetFolderContentsWhenFolderExists() { - $results = $this->readOnlyCache->getFolderContents('files/readonly'); - $this->assertNotEmpty($results); - - foreach ($results as $result) { - $this->assertNotEmpty($result); - } - } - - public function testGetFolderContentsWhenFolderDoesNotExist() { - $results = $this->readOnlyCache->getFolderContents('files/iamaghost'); - $this->assertEmpty($results); - } - -} diff --git a/apps/files_trashbin/command/expire.php b/apps/files_trashbin/command/expire.php index f0526d42830..e617fa47c90 100644 --- a/apps/files_trashbin/command/expire.php +++ b/apps/files_trashbin/command/expire.php @@ -52,5 +52,6 @@ class Expire implements ICommand { \OC_Util::tearDownFS(); \OC_Util::setupFS($this->user); Trashbin::expire($this->trashBinSize, $this->user); + \OC_Util::tearDownFS(); } } diff --git a/core/command/user/add.php b/core/command/user/add.php index 60c70bf13dd..c52ec4d0090 100644 --- a/core/command/user/add.php +++ b/core/command/user/add.php @@ -119,7 +119,7 @@ class Add extends Command { ); if ($user instanceof IUser) { - $output->writeln('The user "' . $user->getUID() . '" was created successfully'); + $output->writeln('<info>The user "' . $user->getUID() . '" was created successfully</info>'); } else { $output->writeln('<error>An error occurred while creating the user</error>'); return 1; diff --git a/core/command/user/delete.php b/core/command/user/delete.php index 53952ceb9e1..8cac03157eb 100644 --- a/core/command/user/delete.php +++ b/core/command/user/delete.php @@ -22,19 +22,20 @@ namespace OC\Core\Command\User; +use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; class Delete extends Command { - /** @var \OC\User\Manager */ + /** @var IUserManager */ protected $userManager; /** - * @param \OC\User\Manager $userManager + * @param IUserManager $userManager */ - public function __construct(\OC\User\Manager $userManager) { + public function __construct(IUserManager $userManager) { $this->userManager = $userManager; parent::__construct(); } @@ -51,11 +52,17 @@ class Delete extends Command { } protected function execute(InputInterface $input, OutputInterface $output) { - $wasSuccessful = $this->userManager->get($input->getArgument('uid'))->delete(); - if($wasSuccessful === true) { - $output->writeln('The specified user was deleted'); + $user = $this->userManager->get($input->getArgument('uid')); + if (is_null($user)) { + $output->writeln('<error>User does not exist</error>'); return; } + + if ($user->delete()) { + $output->writeln('<info>The specified user was deleted</info>'); + return; + } + $output->writeln('<error>The specified could not be deleted. Please check the logs.</error>'); } } diff --git a/core/command/user/lastseen.php b/core/command/user/lastseen.php index 308b3c67ddb..92fcb1d449b 100644 --- a/core/command/user/lastseen.php +++ b/core/command/user/lastseen.php @@ -22,12 +22,24 @@ namespace OC\Core\Command\User; +use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Input\InputArgument; class LastSeen extends Command { + /** @var IUserManager */ + protected $userManager; + + /** + * @param IUserManager $userManager + */ + public function __construct(IUserManager $userManager) { + $this->userManager = $userManager; + parent::__construct(); + } + protected function configure() { $this ->setName('user:lastseen') @@ -40,10 +52,9 @@ class LastSeen extends Command { } protected function execute(InputInterface $input, OutputInterface $output) { - $userManager = \OC::$server->getUserManager(); - $user = $userManager->get($input->getArgument('uid')); + $user = $this->userManager->get($input->getArgument('uid')); if(is_null($user)) { - $output->writeln('User does not exist'); + $output->writeln('<error>User does not exist</error>'); return; } diff --git a/core/command/user/report.php b/core/command/user/report.php index 13bb5df69f8..5d89c0ae549 100644 --- a/core/command/user/report.php +++ b/core/command/user/report.php @@ -23,11 +23,23 @@ namespace OC\Core\Command\User; +use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; class Report extends Command { + /** @var IUserManager */ + protected $userManager; + + /** + * @param IUserManager $userManager + */ + public function __construct(IUserManager $userManager) { + $this->userManager = $userManager; + parent::__construct(); + } + protected function configure() { $this ->setName('user:report') @@ -35,6 +47,7 @@ class Report extends Command { } protected function execute(InputInterface $input, OutputInterface $output) { + /** @var \Symfony\Component\Console\Helper\TableHelper $table */ $table = $this->getHelperSet()->get('table'); $table->setHeaders(array('User Report', '')); $userCountArray = $this->countUsers(); @@ -61,8 +74,7 @@ class Report extends Command { } private function countUsers() { - $userManager = \OC::$server->getUserManager(); - return $userManager->countUsers(); + return $this->userManager->countUsers(); } private function countUserDirectories() { diff --git a/core/command/user/resetpassword.php b/core/command/user/resetpassword.php index 3e16c8f79a5..7c405592114 100644 --- a/core/command/user/resetpassword.php +++ b/core/command/user/resetpassword.php @@ -23,6 +23,7 @@ namespace OC\Core\Command\User; +use OCP\IUserManager; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; @@ -31,10 +32,10 @@ use Symfony\Component\Console\Output\OutputInterface; class ResetPassword extends Command { - /** @var \OC\User\Manager */ + /** @var IUserManager */ protected $userManager; - public function __construct(\OC\User\Manager $userManager) { + public function __construct(IUserManager $userManager) { $this->userManager = $userManager; parent::__construct(); } @@ -60,10 +61,10 @@ class ResetPassword extends Command { protected function execute(InputInterface $input, OutputInterface $output) { $username = $input->getArgument('user'); - /** @var $user \OC\User\User */ + /** @var $user \OCP\IUser */ $user = $this->userManager->get($username); if (is_null($user)) { - $output->writeln("<error>There is no user called " . $username . "</error>"); + $output->writeln('<error>User does not exist</error>'); return 1; } diff --git a/core/css/icons.css b/core/css/icons.css index 0f602515883..e44f9880052 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -56,7 +56,9 @@ background-image: url('../img/actions/confirm.svg'); } -.icon-delete { +.icon-delete, +.icon-delete.no-permission:hover, +.icon-delete.no-permission:focus { background-image: url('../img/actions/delete.svg'); } .icon-delete:hover, diff --git a/core/register_command.php b/core/register_command.php index 701fb10d1ba..75c0245a924 100644 --- a/core/register_command.php +++ b/core/register_command.php @@ -42,11 +42,11 @@ if (\OC::$server->getConfig()->getSystemValue('installed', false)) { $application->add(new OC\Core\Command\App\Enable()); $application->add(new OC\Core\Command\App\ListApps()); $application->add(new OC\Core\Command\Maintenance\Repair($repair, \OC::$server->getConfig())); - $application->add(new OC\Core\Command\User\Report()); - $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); - $application->add(new OC\Core\Command\User\LastSeen()); - $application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\User\Add(\OC::$server->getUserManager(), \OC::$server->getGroupManager())); + $application->add(new OC\Core\Command\User\Delete(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\LastSeen(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\Report(\OC::$server->getUserManager())); + $application->add(new OC\Core\Command\User\ResetPassword(\OC::$server->getUserManager())); $application->add(new OC\Core\Command\Background\Cron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\WebCron(\OC::$server->getConfig())); $application->add(new OC\Core\Command\Background\Ajax(\OC::$server->getConfig())); diff --git a/lib/private/app/codechecker.php b/lib/private/app/codechecker.php index c8c66d6dcab..75db9ab3560 100644 --- a/lib/private/app/codechecker.php +++ b/lib/private/app/codechecker.php @@ -99,7 +99,7 @@ class CodeChecker extends BasicEmitter { $excludes = array_map(function($item) use ($folder) { return $folder . '/' . $item; - }, ['vendor', '3rdparty', '.git', 'l10n']); + }, ['vendor', '3rdparty', '.git', 'l10n', 'tests', 'test']); $iterator = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS); $iterator = new RecursiveCallbackFilterIterator($iterator, function($item) use ($folder, $excludes){ diff --git a/settings/admin.php b/settings/admin.php index 94e48aab14e..f08c3cd53b6 100644 --- a/settings/admin.php +++ b/settings/admin.php @@ -123,6 +123,9 @@ $databaseOverload = (strpos(\OCP\Config::getSystemValue('dbtype'), 'sqlite') !== $template->assign('databaseOverload', $databaseOverload); $template->assign('cronErrors', $appConfig->getValue('core', 'cronErrors')); +// warn if php is not setup properly to get system variables with getenv +$template->assign('getenvServerNotWorking', empty(getenv('PATH'))); + // warn if Windows is used $template->assign('WindowsWarning', OC_Util::runningOnWindows()); diff --git a/settings/js/apps.js b/settings/js/apps.js index 1bd7ffdf790..58a0a31ccd8 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -321,8 +321,10 @@ OC.Settings.Apps = OC.Settings.Apps || { var img= $('<img class="app-icon"/>').attr({ src: entry.icon}); var a=$('<a></a>').attr('href', entry.href); var filename=$('<span></span>'); + var loading = $('<div class="icon-loading-dark"></div>').css('display', 'none'); filename.text(entry.name); a.prepend(filename); + a.prepend(loading); a.prepend(img); li.append(a); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index 4f60f7c16d9..587a3b6c66b 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -67,6 +67,16 @@ if ($_['mail_smtpmode'] == 'qmail') { <h2><?php p($l->t('Security & setup warnings'));?></h2> <ul> <?php +// is php setup properly to query system environment variables like getenv('PATH') +if ($_['getenvServerNotWorking']) { +?> + <li> + <?php p($l->t('php does not seem to be setup properly to query system environment variables. The test with getenv("PATH") only returns an empty response.')); ?><br> + <?php p($l->t('Please check the installation documentation for php configuration notes and the php configuration of your server, especially when using php-fpm.')); ?> + </li> +<?php +} + // is read only config enabled if ($_['readOnlyConfigEnabled']) { ?> diff --git a/tests/core/command/user/deletetest.php b/tests/core/command/user/deletetest.php new file mode 100644 index 00000000000..bfcf031b719 --- /dev/null +++ b/tests/core/command/user/deletetest.php @@ -0,0 +1,106 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace Tests\Core\Command\User; + + +use OC\Core\Command\User\Delete; +use Test\TestCase; + +class DeleteTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $userManager = $this->userManager = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IUserManager $userManager */ + $this->command = new Delete($userManager); + } + + + public function validUserLastSeen() { + return [ + [true, 'The specified user was deleted'], + [false, 'The specified could not be deleted'], + ]; + } + + /** + * @dataProvider validUserLastSeen + * + * @param bool $deleteSuccess + * @param string $expectedString + */ + public function testValidUser($deleteSuccess, $expectedString) { + $user = $this->getMock('OCP\IUser'); + $user->expects($this->once()) + ->method('delete') + ->willReturn($deleteSuccess); + + $this->userManager->expects($this->once()) + ->method('get') + ->with('user') + ->willReturn($user); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('uid') + ->willReturn('user'); + + $this->consoleOutput->expects($this->once()) + ->method('writeln') + ->with($this->stringContains($expectedString)); + + \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } + + public function testInvalidUser() { + $this->userManager->expects($this->once()) + ->method('get') + ->with('user') + ->willReturn(null); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('uid') + ->willReturn('user'); + + $this->consoleOutput->expects($this->once()) + ->method('writeln') + ->with($this->stringContains('User does not exist')); + + \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +} diff --git a/tests/core/command/user/lastseentest.php b/tests/core/command/user/lastseentest.php new file mode 100644 index 00000000000..7eda6fb27ed --- /dev/null +++ b/tests/core/command/user/lastseentest.php @@ -0,0 +1,105 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace Tests\Core\Command\User; + + +use OC\Core\Command\User\LastSeen; +use Test\TestCase; + +class LastSeenTest extends TestCase { + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $userManager; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleInput; + /** @var \PHPUnit_Framework_MockObject_MockObject */ + protected $consoleOutput; + + /** @var \Symfony\Component\Console\Command\Command */ + protected $command; + + protected function setUp() { + parent::setUp(); + + $userManager = $this->userManager = $this->getMockBuilder('OCP\IUserManager') + ->disableOriginalConstructor() + ->getMock(); + $this->consoleInput = $this->getMock('Symfony\Component\Console\Input\InputInterface'); + $this->consoleOutput = $this->getMock('Symfony\Component\Console\Output\OutputInterface'); + + /** @var \OCP\IUserManager $userManager */ + $this->command = new LastSeen($userManager); + } + + public function validUserLastSeen() { + return [ + [0, 'never logged in'], + [time(), 'last login'], + ]; + } + + /** + * @dataProvider validUserLastSeen + * + * @param int $lastSeen + * @param string $expectedString + */ + public function testValidUser($lastSeen, $expectedString) { + $user = $this->getMock('OCP\IUser'); + $user->expects($this->once()) + ->method('getLastLogin') + ->willReturn($lastSeen); + + $this->userManager->expects($this->once()) + ->method('get') + ->with('user') + ->willReturn($user); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('uid') + ->willReturn('user'); + + $this->consoleOutput->expects($this->once()) + ->method('writeln') + ->with($this->stringContains($expectedString)); + + \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } + + public function testInvalidUser() { + $this->userManager->expects($this->once()) + ->method('get') + ->with('user') + ->willReturn(null); + + $this->consoleInput->expects($this->once()) + ->method('getArgument') + ->with('uid') + ->willReturn('user'); + + $this->consoleOutput->expects($this->once()) + ->method('writeln') + ->with($this->stringContains('User does not exist')); + + \Test_Helper::invokePrivate($this->command, 'execute', [$this->consoleInput, $this->consoleOutput]); + } +} diff --git a/tests/lib/files/stream/encryption.php b/tests/lib/files/stream/encryption.php index 25e7e54335e..1da208282e6 100644 --- a/tests/lib/files/stream/encryption.php +++ b/tests/lib/files/stream/encryption.php @@ -148,6 +148,8 @@ class Encryption extends \Test\TestCase { $stream = $this->getStream($fileName, 'r', 6); $this->assertEquals('foobar', fread($stream, 100)); fclose($stream); + + unlink($fileName); } public function testSeek() { @@ -161,6 +163,8 @@ class Encryption extends \Test\TestCase { $stream = $this->getStream($fileName, 'r', 9); $this->assertEquals('foofoobar', fread($stream, 100)); fclose($stream); + + unlink($fileName); } function dataFilesProvider() { @@ -198,6 +202,8 @@ class Encryption extends \Test\TestCase { fclose($stream); $this->assertEquals($expectedData, $data); + + unlink($fileName); } /** |