diff options
55 files changed, 1342 insertions, 217 deletions
diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 009cb355ba7..731dd7a23e7 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -358,6 +358,10 @@ table td.filename form { font-size:14px; margin-left:48px; margin-right:48px; } padding: 28px 14px 19px !important; } +#fileList .action.action-share-notification span, img, a { + cursor: default !important; +} + a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } /* Actions for selected files */ diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index b9cd9816d4c..2edb45f544c 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -244,7 +244,7 @@ this.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); }, function (filename) { - var url = OCA.Files.Files.getDownloadUrl(filename, fileList.getCurrentDirectory()); + var url = fileList.getDownloadUrl(filename, fileList.getCurrentDirectory()); if (url) { OC.redirect(url); } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 38766e2b801..3dcd9dd3eaa 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1184,10 +1184,22 @@ event.preventDefault(); try { var newName = input.val(); + input.tipsy('hide'); + form.remove(); + if (newName !== oldname) { checkInput(); - // mark as loading + // mark as loading (temp element) td.css('background-image', 'url('+ OC.imagePath('core', 'loading.gif') + ')'); + tr.attr('data-file', newName); + var basename = newName; + if (newName.indexOf('.') > 0 && tr.data('type') !== 'dir') { + basename = newName.substr(0, newName.lastIndexOf('.')); + } + td.find('a.name span.nametext').text(basename); + td.children('a.name').show(); + tr.find('.fileactions, .action').addClass('hidden'); + $.ajax({ url: OC.filePath('files','ajax','rename.php'), data: { @@ -1207,30 +1219,17 @@ // reinsert row self.files.splice(tr.index(), 1); tr.remove(); - self.add(fileInfo); + self.add(fileInfo, {updateSummary: false}); + self.$fileList.trigger($.Event('fileActionsReady')); } }); + } else { + // add back the old file info when cancelled + self.files.splice(tr.index(), 1); + tr.remove(); + self.add(oldFileInfo, {updateSummary: false}); + self.$fileList.trigger($.Event('fileActionsReady')); } - input.tipsy('hide'); - tr.data('renaming',false); - tr.attr('data-file', newName); - var path = td.children('a.name').attr('href'); - // FIXME this will fail if the path contains the filename. - td.children('a.name').attr('href', path.replace(encodeURIComponent(oldname), encodeURIComponent(newName))); - var basename = newName; - if (newName.indexOf('.') > 0 && tr.data('type') !== 'dir') { - basename = newName.substr(0, newName.lastIndexOf('.')); - } - td.find('a.name span.nametext').text(basename); - if (newName.indexOf('.') > 0 && tr.data('type') !== 'dir') { - if ( ! td.find('a.name span.extension').exists() ) { - td.find('a.name span.nametext').append('<span class="extension"></span>'); - } - td.find('a.name span.extension').text(newName.substr(newName.lastIndexOf('.'))); - } - form.remove(); - self.fileActions.display( tr.find('td.filename'), true); - td.children('a.name').show(); } catch (error) { input.attr('title', error); input.tipsy({gravity: 'w', trigger: 'manual'}); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index bfc983c7483..a3dc5b255a1 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -468,6 +468,22 @@ describe('OCA.Files.FileList tests', function() { }); }); describe('Renaming files', function() { + function doCancelRename() { + var $input; + for (var i = 0; i < testFiles.length; i++) { + fileList.add(testFiles[i]); + } + + // trigger rename prompt + fileList.rename('One.txt'); + $input = fileList.$fileList.find('input.filename'); + // keep same name + $input.val('One.txt'); + // trigger submit because triggering blur doesn't work in all browsers + $input.closest('form').trigger('submit'); + + expect(fakeServer.requests.length).toEqual(0); + } function doRename() { var $input, request; @@ -486,12 +502,6 @@ describe('OCA.Files.FileList tests', function() { request = fakeServer.requests[0]; expect(request.url.substr(0, request.url.indexOf('?'))).toEqual(OC.webroot + '/index.php/apps/files/ajax/rename.php'); expect(OC.parseQueryString(request.url)).toEqual({'dir': '/subdir', newname: 'Tu_after_three.txt', file: 'One.txt'}); - - // element is renamed before the request finishes - expect(fileList.findFileEl('One.txt').length).toEqual(0); - expect(fileList.findFileEl('Tu_after_three.txt').length).toEqual(1); - // input is gone - expect(fileList.$fileList.find('input.filename').length).toEqual(0); } it('Inserts renamed file entry at correct position if rename ajax call suceeded', function() { doRename(); @@ -542,22 +552,51 @@ describe('OCA.Files.FileList tests', function() { $tr = fileList.findFileEl('Tu_after_three.txt'); expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=Tu_after_three.txt'); }); - // FIXME: fix this in the source code! - xit('Correctly updates file link after rename when path has same name', function() { - var $tr; - // evil case: because of buggy code - $('#dir').val('/One.txt/subdir'); + it('Triggers "fileActionsReady" event after rename', function() { + var handler = sinon.stub(); + fileList.$fileList.on('fileActionsReady', handler); doRename(); - + expect(handler.notCalled).toEqual(true); fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ status: 'success', data: { name: 'Tu_after_three.txt' } })); + expect(handler.calledOnce).toEqual(true); + expect(fileList.$fileList.find('.test').length).toEqual(0); + }); + it('Leaves the summary alone when reinserting renamed element', function() { + var $summary = $('#filestable .summary'); + doRename(); + fakeServer.requests[0].respond(200, {'Content-Type': 'application/json'}, JSON.stringify({ + status: 'success', + data: { + name: 'Tu_after_three.txt' + } + })); + expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); + }); + it('Leaves the summary alone when cancel renaming', function() { + var $summary = $('#filestable .summary'); + doCancelRename(); + expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); + }); + it('Hides actions while rename in progress', function() { + var $tr; + doRename(); + // element is renamed before the request finishes $tr = fileList.findFileEl('Tu_after_three.txt'); - expect($tr.find('a.name').attr('href')).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=One.txt'); + expect($tr.length).toEqual(1); + expect(fileList.findFileEl('One.txt').length).toEqual(0); + // file actions are hidden + expect($tr.find('.action').hasClass('hidden')).toEqual(true); + expect($tr.find('.fileactions').hasClass('hidden')).toEqual(true); + + // input and form are gone + expect(fileList.$fileList.find('input.filename').length).toEqual(0); + expect(fileList.$fileList.find('form').length).toEqual(0); }); }); describe('Moving files', function() { diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index c1ccb927df5..6e375f99a8e 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -81,7 +81,7 @@ class Hooks { // Check if first-run file migration has already been performed
$ready = false;
$migrationStatus = $util->getMigrationStatus();
- if ($migrationStatus === Util::MIGRATION_OPEN) {
+ if ($migrationStatus === Util::MIGRATION_OPEN && $session !== false) {
$ready = $util->beginMigration();
} elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) {
// refuse login as long as the initial encryption is running
@@ -222,10 +222,14 @@ class Hooks { $util = new Util($view, $user);
$recoveryPassword = isset($params['recoveryPassword']) ? $params['recoveryPassword'] : null;
+ // we generate new keys if...
+ // ...we have a recovery password and the user enabled the recovery key
+ // ...encryption was activated for the first time (no keys exists)
+ // ...the user doesn't have any files
if (($util->recoveryEnabledForUser() && $recoveryPassword)
- || !$util->userKeysExists()) {
+ || !$util->userKeysExists()
+ || !$view->file_exists($user . '/files')) {
- $recoveryPassword = $params['recoveryPassword'];
$newUserPassword = $params['password'];
// make sure that the users home is mounted
diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index fcb369c7238..43703472618 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -311,4 +311,46 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); } + /** + * @brief replacing encryption keys during password change should be allowed + * until the user logged in for the first time + */ + public function testSetPassphrase() { + + $view = new \OC\Files\View(); + + // set user password for the first time + \OCA\Encryption\Hooks::postCreateUser(array('uid' => 'newUser', 'password' => 'newUserPassword')); + + $this->assertTrue($view->file_exists('public-keys/newUser.public.key')); + $this->assertTrue($view->file_exists('newUser/files_encryption/newUser.private.key')); + + // check if we are able to decrypt the private key + $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); + $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'newUserPassword'); + $this->assertTrue(is_string($privateKey)); + + // change the password before the user logged-in for the first time, + // we can replace the encryption keys + \OCA\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged')); + + $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); + $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); + $this->assertTrue(is_string($privateKey)); + + // now create a files folder to simulate a already used account + $view->mkdir('/newUser/files'); + + // change the password after the user logged in, now the password should not change + \OCA\Encryption\Hooks::setPassphrase(array('uid' => 'newUser', 'password' => 'passwordChanged2')); + + $encryptedKey = \OCA\Encryption\Keymanager::getPrivateKey($view, 'newUser'); + $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged2'); + $this->assertFalse($privateKey); + + $privateKey = \OCA\Encryption\Crypt::decryptPrivateKey($encryptedKey, 'passwordChanged'); + $this->assertTrue(is_string($privateKey)); + + } + } diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index e8ed8950c3a..ca164784fb0 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -32,11 +32,13 @@ OCP\Util::connectHook('OC_User', 'post_login', 'OC\Files\Storage\SMB_OC', 'login OC_Mount_Config::registerBackend('\OC\Files\Storage\Local', array( 'backend' => (string)$l->t('Local'), + 'priority' => 150, 'configuration' => array( 'datadir' => (string)$l->t('Location')))); OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array( 'backend' => (string)$l->t('Amazon S3'), + 'priority' => 100, 'configuration' => array( 'key' => (string)$l->t('Key'), 'secret' => '*'.$l->t('Secret'), @@ -45,6 +47,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array( 'backend' => (string)$l->t('Amazon S3 and compliant'), + 'priority' => 100, 'configuration' => array( 'key' => (string)$l->t('Access Key'), 'secret' => '*'.$l->t('Secret Key'), @@ -58,6 +61,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\AmazonS3', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\Dropbox', array( 'backend' => 'Dropbox', + 'priority' => 100, 'configuration' => array( 'configured' => '#configured', 'app_key' => (string)$l->t('App key'), @@ -69,6 +73,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\Dropbox', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\FTP', array( 'backend' => 'FTP', + 'priority' => 100, 'configuration' => array( 'host' => (string)$l->t('Host'), 'user' => (string)$l->t('Username'), @@ -79,6 +84,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\FTP', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\Google', array( 'backend' => 'Google Drive', + 'priority' => 100, 'configuration' => array( 'configured' => '#configured', 'client_id' => (string)$l->t('Client ID'), @@ -90,6 +96,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\Google', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\Swift', array( 'backend' => (string)$l->t('OpenStack Object Storage'), + 'priority' => 100, 'configuration' => array( 'user' => (string)$l->t('Username (required)'), 'bucket' => (string)$l->t('Bucket (required)'), @@ -107,6 +114,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\Swift', array( if (!OC_Util::runningOnWindows()) { OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB', array( 'backend' => 'SMB / CIFS', + 'priority' => 100, 'configuration' => array( 'host' => (string)$l->t('Host'), 'user' => (string)$l->t('Username'), @@ -117,6 +125,7 @@ if (!OC_Util::runningOnWindows()) { OC_Mount_Config::registerBackend('\OC\Files\Storage\SMB_OC', array( 'backend' => (string)$l->t('SMB / CIFS using OC login'), + 'priority' => 90, 'configuration' => array( 'host' => (string)$l->t('Host'), 'username_as_share' => '!'.$l->t('Username as share'), @@ -127,6 +136,7 @@ if (!OC_Util::runningOnWindows()) { OC_Mount_Config::registerBackend('\OC\Files\Storage\DAV', array( 'backend' => 'WebDAV', + 'priority' => 100, 'configuration' => array( 'host' => (string)$l->t('URL'), 'user' => (string)$l->t('Username'), @@ -137,6 +147,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\DAV', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\OwnCloud', array( 'backend' => 'ownCloud', + 'priority' => 100, 'configuration' => array( 'host' => (string)$l->t('URL'), 'user' => (string)$l->t('Username'), @@ -147,6 +158,7 @@ OC_Mount_Config::registerBackend('\OC\Files\Storage\OwnCloud', array( OC_Mount_Config::registerBackend('\OC\Files\Storage\SFTP', array( 'backend' => 'SFTP', + 'priority' => 100, 'configuration' => array( 'host' => (string)$l->t('Host'), 'user' => (string)$l->t('Username'), diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 7a651239cb4..21f63bf439d 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -35,6 +35,7 @@ class OC_Mount_Config { const MOUNT_TYPE_GLOBAL = 'global'; const MOUNT_TYPE_GROUP = 'group'; const MOUNT_TYPE_USER = 'user'; + const MOUNT_TYPE_PERSONAL = 'personal'; // whether to skip backend test (for unit tests, as this static class is not mockable) public static $skipTest = false; @@ -126,6 +127,8 @@ class OC_Mount_Config { $datadir = \OC_Config::getValue("datadirectory", \OC::$SERVERROOT . "/data"); $mount_file = \OC_Config::getValue("mount_file", $datadir . "/mount.json"); + $backends = self::getBackends(); + //move config file to it's new position if (is_file(\OC::$SERVERROOT . '/config/mount.json')) { rename(\OC::$SERVERROOT . '/config/mount.json', $mount_file); @@ -133,12 +136,45 @@ class OC_Mount_Config { // Load system mount points $mountConfig = self::readData(); + + // Global mount points (is this redundant?) if (isset($mountConfig[self::MOUNT_TYPE_GLOBAL])) { foreach ($mountConfig[self::MOUNT_TYPE_GLOBAL] as $mountPoint => $options) { $options['options'] = self::decryptPasswords($options['options']); - $mountPoints[$mountPoint] = $options; + if (!isset($options['priority'])) { + $options['priority'] = $backends[$options['class']]['priority']; + } + + // Override if priority greater + if ( (!isset($mountPoints[$mountPoint])) + || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) ) { + $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; + $mountPoints[$mountPoint] = $options; + } } } + // All user mount points + if (isset($mountConfig[self::MOUNT_TYPE_USER]) && isset($mountConfig[self::MOUNT_TYPE_USER]['all'])) { + $mounts = $mountConfig[self::MOUNT_TYPE_USER]['all']; + foreach ($mounts as $mountPoint => $options) { + $mountPoint = self::setUserVars($user, $mountPoint); + foreach ($options as &$option) { + $option = self::setUserVars($user, $option); + } + $options['options'] = self::decryptPasswords($options['options']); + if (!isset($options['priority'])) { + $options['priority'] = $backends[$options['class']]['priority']; + } + + // Override if priority greater + if ( (!isset($mountPoints[$mountPoint])) + || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) ) { + $options['priority_type'] = self::MOUNT_TYPE_GLOBAL; + $mountPoints[$mountPoint] = $options; + } + } + } + // Group mount points if (isset($mountConfig[self::MOUNT_TYPE_GROUP])) { foreach ($mountConfig[self::MOUNT_TYPE_GROUP] as $group => $mounts) { if (\OC_Group::inGroup($user, $group)) { @@ -148,21 +184,42 @@ class OC_Mount_Config { $option = self::setUserVars($user, $option); } $options['options'] = self::decryptPasswords($options['options']); - $mountPoints[$mountPoint] = $options; + if (!isset($options['priority'])) { + $options['priority'] = $backends[$options['class']]['priority']; + } + + // Override if priority greater or if priority type different + if ( (!isset($mountPoints[$mountPoint])) + || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) + || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_GROUP) ) { + $options['priority_type'] = self::MOUNT_TYPE_GROUP; + $mountPoints[$mountPoint] = $options; + } } } } } + // User mount points if (isset($mountConfig[self::MOUNT_TYPE_USER])) { foreach ($mountConfig[self::MOUNT_TYPE_USER] as $mountUser => $mounts) { - if ($mountUser === 'all' or strtolower($mountUser) === strtolower($user)) { + if (strtolower($mountUser) === strtolower($user)) { foreach ($mounts as $mountPoint => $options) { $mountPoint = self::setUserVars($user, $mountPoint); foreach ($options as &$option) { $option = self::setUserVars($user, $option); } $options['options'] = self::decryptPasswords($options['options']); - $mountPoints[$mountPoint] = $options; + if (!isset($options['priority'])) { + $options['priority'] = $backends[$options['class']]['priority']; + } + + // Override if priority greater or if priority type different + if ( (!isset($mountPoints[$mountPoint])) + || ($options['priority'] >= $mountPoints[$mountPoint]['priority']) + || ($mountPoints[$mountPoint]['priority_type'] !== self::MOUNT_TYPE_USER) ) { + $options['priority_type'] = self::MOUNT_TYPE_USER; + $mountPoints[$mountPoint] = $options; + } } } } @@ -173,6 +230,9 @@ class OC_Mount_Config { if (isset($mountConfig[self::MOUNT_TYPE_USER][$user])) { foreach ($mountConfig[self::MOUNT_TYPE_USER][$user] as $mountPoint => $options) { $options['options'] = self::decryptPasswords($options['options']); + + // Always override previous config + $options['priority_type'] = self::MOUNT_TYPE_PERSONAL; $mountPoints[$mountPoint] = $options; } } @@ -244,6 +304,9 @@ class OC_Mount_Config { $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } $mount['options'] = self::decryptPasswords($mount['options']); + if (!isset($mount['priority'])) { + $mount['priority'] = $backends[$mount['class']]['priority']; + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); @@ -251,6 +314,7 @@ class OC_Mount_Config { 'class' => $mount['class'], 'mountpoint' => $mountPoint, 'backend' => $backends[$mount['class']]['backend'], + 'priority' => $mount['priority'], 'options' => $mount['options'], 'applicable' => array('groups' => array($group), 'users' => array()), 'status' => self::getBackendStatus($mount['class'], $mount['options'], false) @@ -275,12 +339,16 @@ class OC_Mount_Config { $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } $mount['options'] = self::decryptPasswords($mount['options']); + if (!isset($mount['priority'])) { + $mount['priority'] = $backends[$mount['class']]['priority']; + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); $config = array( 'class' => $mount['class'], 'mountpoint' => $mountPoint, 'backend' => $backends[$mount['class']]['backend'], + 'priority' => $mount['priority'], 'options' => $mount['options'], 'applicable' => array('groups' => array(), 'users' => array($user)), 'status' => self::getBackendStatus($mount['class'], $mount['options'], false) @@ -363,6 +431,7 @@ class OC_Mount_Config { * @param string $mountType MOUNT_TYPE_GROUP | MOUNT_TYPE_USER * @param string $applicable User or group to apply mount to * @param bool $isPersonal Personal or system mount point i.e. is this being called from the personal or admin page + * @param int|null $priority Mount point priority, null for default * @return boolean */ public static function addMountPoint($mountPoint, @@ -370,7 +439,8 @@ class OC_Mount_Config { $classOptions, $mountType, $applicable, - $isPersonal = false) { + $isPersonal = false, + $priority = null) { $backends = self::getBackends(); $mountPoint = OC\Files\Filesystem::normalizePath($mountPoint); if ($mountPoint === '' || $mountPoint === '/') { @@ -400,9 +470,24 @@ class OC_Mount_Config { 'options' => self::encryptPasswords($classOptions)) ) ); + if (! $isPersonal && !is_null($priority)) { + $mount[$applicable][$mountPoint]['priority'] = $priority; + } $mountPoints = self::readData($isPersonal ? OCP\User::getUser() : NULL); $mountPoints = self::mergeMountPoints($mountPoints, $mount, $mountType); + + // Set default priority if none set + if (!isset($mountPoints[$mountType][$applicable][$mountPoint]['priority'])) { + if (isset($backends[$class]['priority'])) { + $mountPoints[$mountType][$applicable][$mountPoint]['priority'] + = $backends[$class]['priority']; + } else { + $mountPoints[$mountType][$applicable][$mountPoint]['priority'] + = 100; + } + } + self::writeData($isPersonal ? OCP\User::getUser() : NULL, $mountPoints); return self::getBackendStatus($class, $classOptions, $isPersonal); @@ -690,8 +775,16 @@ class OC_Mount_Config { */ private static function mergeMountPoints($data, $mountPoint, $mountType) { $applicable = key($mountPoint); + $mountPath = key($mountPoint[$applicable]); if (isset($data[$mountType])) { if (isset($data[$mountType][$applicable])) { + // Merge priorities + if (isset($data[$mountType][$applicable][$mountPath]) + && isset($data[$mountType][$applicable][$mountPath]['priority']) + && !isset($mountPoint[$applicable][$mountPath]['priority'])) { + $mountPoint[$applicable][$mountPath]['priority'] + = $data[$mountType][$applicable][$mountPath]['priority']; + } $data[$mountType][$applicable] = array_merge($data[$mountType][$applicable], $mountPoint[$applicable]); } else { diff --git a/apps/files_external/tests/mountconfig.php b/apps/files_external/tests/mountconfig.php index 14fe1d90b7a..9b04e200e2b 100644 --- a/apps/files_external/tests/mountconfig.php +++ b/apps/files_external/tests/mountconfig.php @@ -41,16 +41,22 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { const TEST_USER1 = 'user1'; const TEST_USER2 = 'user2'; const TEST_GROUP1 = 'group1'; + const TEST_GROUP1B = 'group1b'; const TEST_GROUP2 = 'group2'; + const TEST_GROUP2B = 'group2b'; public function setUp() { \OC_User::createUser(self::TEST_USER1, self::TEST_USER1); \OC_User::createUser(self::TEST_USER2, self::TEST_USER2); \OC_Group::createGroup(self::TEST_GROUP1); + \OC_Group::createGroup(self::TEST_GROUP1B); \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1); + \OC_Group::addToGroup(self::TEST_USER1, self::TEST_GROUP1B); \OC_Group::createGroup(self::TEST_GROUP2); + \OC_Group::createGroup(self::TEST_GROUP2B); \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2); + \OC_Group::addToGroup(self::TEST_USER2, self::TEST_GROUP2B); \OC_User::setUserId(self::TEST_USER1); $this->userHome = \OC_User::getHome(self::TEST_USER1); @@ -81,7 +87,9 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { \OC_User::deleteUser(self::TEST_USER2); \OC_User::deleteUser(self::TEST_USER1); \OC_Group::deleteGroup(self::TEST_GROUP1); + \OC_Group::deleteGroup(self::TEST_GROUP1B); \OC_Group::deleteGroup(self::TEST_GROUP2); + \OC_Group::deleteGroup(self::TEST_GROUP2B); @unlink($this->dataDir . '/mount.json'); @@ -635,4 +643,161 @@ class Test_Mount_Config extends \PHPUnit_Framework_TestCase { $this->assertEquals('ext', $config[1]['mountpoint']); $this->assertEquals($options2, $config[1]['options']); } + + public function priorityDataProvider() { + return array( + + // test 1 - group vs group + array( + array( + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, + 'applicable' => self::TEST_GROUP1, + 'priority' => 50 + ), + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, + 'applicable' => self::TEST_GROUP1B, + 'priority' => 60 + ) + ), + 1 + ), + // test 2 - user vs personal + array( + array( + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, + 'applicable' => self::TEST_USER1, + 'priority' => 2000 + ), + array( + 'isPersonal' => true, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, + 'applicable' => self::TEST_USER1, + 'priority' => null + ) + ), + 1 + ), + // test 3 - all vs group vs user + array( + array( + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, + 'applicable' => 'all', + 'priority' => 70 + ), + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_GROUP, + 'applicable' => self::TEST_GROUP1, + 'priority' => 60 + ), + array( + 'isPersonal' => false, + 'mountType' => OC_Mount_Config::MOUNT_TYPE_USER, + 'applicable' => self::TEST_USER1, + 'priority' => 50 + ) + ), + 2 + ) + + ); + } + + /** + * Ensure priorities are being respected + * Test user is self::TEST_USER1 + * + * @dataProvider priorityDataProvider + * @param array[] $mounts array of associative array of mount parameters: + * bool $isPersonal + * string $mountType + * string $applicable + * int|null $priority null for personal + * @param int $expected index of expected visible mount + */ + public function testPriority($mounts, $expected) { + $mountConfig = array( + 'host' => 'somehost', + 'user' => 'someuser', + 'password' => 'somepassword', + 'root' => 'someroot' + ); + + // Add mount points + foreach($mounts as $i => $mount) { + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + '\OC\Files\Storage\SMB', + $mountConfig + array('id' => $i), + $mount['mountType'], + $mount['applicable'], + $mount['isPersonal'], + $mount['priority'] + ) + ); + } + + // Get mount points for user + $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); + + $this->assertEquals(1, count($mountPoints)); + $this->assertEquals($expected, $mountPoints['/'.self::TEST_USER1.'/files/ext']['options']['id']); + } + + /** + * Test for persistence of priority when changing mount options + */ + public function testPriorityPersistence() { + $class = '\OC\Files\Storage\SMB'; + $priority = 123; + $mountConfig = array( + 'host' => 'somehost', + 'user' => 'someuser', + 'password' => 'somepassword', + 'root' => 'someroot' + ); + + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + $class, + $mountConfig, + OC_Mount_Config::MOUNT_TYPE_USER, + self::TEST_USER1, + false, + $priority + ) + ); + + // Check for correct priority + $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); + $this->assertEquals($priority, + $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); + + // Simulate changed mount options (without priority set) + $this->assertTrue( + OC_Mount_Config::addMountPoint( + '/ext', + $class, + $mountConfig, + OC_Mount_Config::MOUNT_TYPE_USER, + self::TEST_USER1, + false + ) + ); + + // Check for correct priority + $mountPoints = OC_Mount_Config::getAbsoluteMountPoints(self::TEST_USER1); + $this->assertEquals($priority, + $mountPoints['/'.self::TEST_USER1.'/files/ext']['priority']); + } } diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 0ef34578117..fa43f33721c 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -9,6 +9,7 @@ OC::$CLASSPATH['OC\Files\Cache\Shared_Updater'] = 'files_sharing/lib/updater.php OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'files_sharing/lib/watcher.php'; OC::$CLASSPATH['OCA\Files\Share\Api'] = 'files_sharing/lib/api.php'; OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php'; +OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); @@ -18,3 +19,5 @@ OCP\Util::addScript('files_sharing', 'share'); \OC_Hook::connect('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook'); \OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook'); \OC_Hook::connect('OC_Appconfig', 'post_set_value', '\OCA\Files\Share\Maintainer', 'configChangeHook'); + +OC_FileProxy::register(new OCA\Files\Share\Proxy()); diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index d3d4479215e..d825ee9de15 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -18,12 +18,15 @@ OCA.Sharing.PublicApp = { _initialized: false, initialize: function($el) { + var self = this; if (this._initialized) { return; } this._initialized = true; + this.initialDir = $('#dir').val(); + // file list mode ? - if ($el.find('#filestable')) { + if ($el.find('#filestable').length) { this.fileList = new OCA.Files.FileList( $el, { @@ -55,8 +58,9 @@ OCA.Sharing.PublicApp = { var params = { x: $(document).width() * window.devicePixelRatio, a: 'true', - file: encodeURIComponent($('#dir').val() + $('#filename').val()), - t: $('#sharingToken').val() + file: encodeURIComponent(this.initialDir + $('#filename').val()), + t: $('#sharingToken').val(), + scalingup: 0 }; var img = $('<img class="publicpreview">'); @@ -112,7 +116,7 @@ OCA.Sharing.PublicApp = { data.formData = { requesttoken: $('#publicUploadRequestToken').val(), dirToken: $('#dirToken').val(), - subdir: $('input#dir').val(), + subdir: self.fileList.getCurrentDirectory(), file_directory: fileDirectory }; }); @@ -122,7 +126,7 @@ OCA.Sharing.PublicApp = { delete this.fileActions.actions.all.Share; this.fileList.setFileActions(this.fileActions); - this.fileList.changeDirectory($('#dir').val() || '/', false, true); + this.fileList.changeDirectory(this.initialDir || '/', false, true); // URL history handling this.fileList.$el.on('changeDirectory', _.bind(this._onDirectoryChanged, this)); @@ -156,16 +160,18 @@ $(document).ready(function() { var App = OCA.Sharing.PublicApp; App.initialize($('#preview')); - // HACK: for oc-dialogs previews that depends on Files: - Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { - return App.fileList.lazyLoadPreview({ - path: path, - mime: mime, - callback: ready, - width: width, - height: height, - etag: etag - }); - }; + if (window.Files) { + // HACK: for oc-dialogs previews that depends on Files: + Files.lazyLoadPreview = function(path, mime, ready, width, height, etag) { + return App.fileList.lazyLoadPreview({ + path: path, + mime: mime, + callback: ready, + width: width, + height: height, + etag: etag + }); + }; + } }); diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 973c63c5d7e..1b04097ccb1 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -15,23 +15,41 @@ $(document).ready(function() { if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined') { // TODO: make a separate class for this or a hook or jQuery event ? - var oldCreateRow = OCA.Files.FileList.prototype._createRow; - OCA.Files.FileList.prototype._createRow = function(fileData) { - var tr = oldCreateRow.apply(this, arguments); - if (fileData.shareOwner) { - tr.attr('data-share-owner', fileData.shareOwner); - } - return tr; - }; + if (OCA.Files.FileList) { + var oldCreateRow = OCA.Files.FileList.prototype._createRow; + OCA.Files.FileList.prototype._createRow = function(fileData) { + var tr = oldCreateRow.apply(this, arguments); + if (fileData.shareOwner) { + tr.attr('data-share-owner', fileData.shareOwner); + } + return tr; + }; + } $('#fileList').on('fileActionsReady',function(){ - var $fileList = $(this); - var allShared = $fileList.find('[data-share-owner] [data-Action="Share"]'); - allShared.addClass('permanent'); - allShared.find('span').text(function(){ - var $owner = $(this).closest('tr').attr('data-share-owner'); - return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner}); - }); + // if no share action exists because the admin disabled sharing for this user + // we create a share notification action to inform the user about files + // shared with him otherwise we just update the existing share action. + var allShared; + if (oc_appconfig.core.sharingDisabledForUser) { + var $fileList = $(this); + allShared = $fileList.find('[data-share-owner]'); + var shareNotification = '<a class="action action-share-notification permanent"' + + ' data-action="Share-Notification" href="#" original-title="">' + + ' <img class="svg" src="' + OC.imagePath('core', 'actions/share') + '"></img>'; + $(allShared).find('.fileactions').append(function() { + var owner = $(this).closest('tr').attr('data-share-owner'); + var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); + return shareNotification + '<span> ' + shareBy + '</span></span>'; + }); + } else { + allShared = $fileList.find('[data-share-owner] [data-Action="Share"]'); + allShared.addClass('permanent'); + allShared.find('span').text(function(){ + var $owner = $(this).closest('tr').attr('data-share-owner'); + return ' ' + t('files_sharing', 'Shared by {owner}', {owner: $owner}); + }); + } // FIXME: these calls are also working on hard-coded // list selectors... @@ -46,7 +64,7 @@ $(document).ready(function() { } }); - FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { + FileActions.register('all', 'Share', OC.PERMISSION_SHARE, OC.imagePath('core', 'actions/share'), function(filename) { var tr = FileList.findFileEl(filename); var itemType = 'file'; if ($(tr).data('type') == 'dir') { diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index 71b496ab944..49546f012a6 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -180,4 +180,26 @@ class Helper { return $relPath; } + + /** + * check if file name already exists and generate unique target + * + * @param string $path + * @param array $excludeList + * @param \OC\Files\View $view + * @return string $path + */ + public static function generateUniqueTarget($path, $excludeList, $view) { + $pathinfo = pathinfo($path); + $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; + $name = $pathinfo['filename']; + $dir = $pathinfo['dirname']; + $i = 2; + while ($view->file_exists($path) || in_array($path, $excludeList)) { + $path = \OC\Files\Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); + $i++; + } + + return $path; + } } diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index c3ad63e2fd2..f32ebabe40d 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -30,6 +30,7 @@ class Shared_Permissions extends Permissions { * @return int (-1 if file no permissions set) */ public function get($fileId, $user) { + if ($fileId == -1) { // if we ask for the mount point return -1 so that we can get the correct // permissions by the path, with the root fileId we have no idea which share is meant @@ -37,11 +38,14 @@ class Shared_Permissions extends Permissions { } $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); + + $permission = -1; + if ($source) { - return $source['permissions']; - } else { - return -1; + $permission = $this->updatePermissions($source['permissions']); } + + return $permission; } /** @@ -55,7 +59,7 @@ class Shared_Permissions extends Permissions { $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, false); if ($source) { - return $source['permissions']; + return $this->updatePermissions($source['permissions']); } else { return -1; } @@ -106,7 +110,7 @@ class Shared_Permissions extends Permissions { $result = $query->execute(array($parentId)); $filePermissions = array(); while ($row = $result->fetchRow()) { - $filePermissions[$row['fileid']] = $permissions; + $filePermissions[$row['fileid']] = $this->updatePermissions($permissions); } return $filePermissions; } diff --git a/apps/files_sharing/lib/proxy.php b/apps/files_sharing/lib/proxy.php new file mode 100644 index 00000000000..c899a4b4dd3 --- /dev/null +++ b/apps/files_sharing/lib/proxy.php @@ -0,0 +1,69 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Files\Share; + +class Proxy extends \OC_FileProxy { + + /** + * check if the deleted folder contains share mount points and move them + * up to the parent + * + * @param string $path + */ + public function preUnlink($path) { + $this->moveMountPointsUp($path); + } + + /** + * check if the deleted folder contains share mount points and move them + * up to the parent + * + * @param string $path + */ + public function preRmdir($path) { + $this->moveMountPointsUp($path); + } + + /** + * move share mount points up to the parent + * + * @param string $path + */ + private function moveMountPointsUp($path) { + $view = new \OC\Files\View('/'); + + // find share mount points within $path and move them up to the parent folder + // before we delete $path + $mountManager = \OC\Files\Filesystem::getMountManager(); + $mountedShares = $mountManager->findIn($path); + foreach ($mountedShares as $mount) { + if ($mount->getStorage() instanceof \OC\Files\Storage\Shared) { + $mountPoint = $mount->getMountPoint(); + $mountPointName = $mount->getMountPointName(); + $target = \OCA\Files_Sharing\Helper::generateUniqueTarget(dirname($path) . '/' . $mountPointName, array(), $view); + $view->rename($mountPoint, $target); + } + } + } + +} diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index af71786b104..91595461a61 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -75,16 +75,7 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $excludeList = array_merge($excludeList, $exclude); } - $pathinfo = pathinfo($target); - $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; - $name = $pathinfo['filename']; - $i = 2; - while ($view->file_exists($target) || in_array($target, $excludeList)) { - $target = '/' . $name . ' ('.$i.')' . $ext; - $i++; - } - - return $target; + return \OCA\Files_Sharing\Helper::generateUniqueTarget($target, $excludeList, $view); } public function formatItems($items, $format, $parameters = null) { diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 4b69276d05a..07a0acf00a5 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -108,6 +108,11 @@ class Shared extends \OC\Files\Storage\Common { if (pathinfo($target, PATHINFO_EXTENSION) === 'part') { $permissions |= \OCP\PERMISSION_DELETE; } + + if (\OC_Util::isSharingDisabledForUser()) { + $permissions &= ~\OCP\PERMISSION_SHARE; + } + return $permissions; } @@ -198,6 +203,9 @@ class Shared extends \OC\Files\Storage\Common { } public function isSharable($path) { + if (\OCP\Util::isSharingDisabledForUser()) { + return false; + } return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } @@ -305,27 +313,12 @@ class Shared extends \OC\Files\Storage\Common { $relTargetPath = $this->stripUserFilesPath($targetPath); - // if the user renames a mount point from a group share we need to create a new db entry - // for the unique name - if ($this->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && $this->uniqueNameSet() === false) { - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' - .' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' - .' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); - $arguments = array($this->share['item_type'], $this->share['item_source'], $this->share['item_target'], - 2, \OCP\User::getUser(), $this->share['uid_owner'], $this->share['permissions'], $this->share['stime'], $this->share['file_source'], - $relTargetPath, $this->share['token'], $this->share['id']); - - } else { - // rename mount point - $query = \OC_DB::prepare( - 'Update `*PREFIX*share` - SET `file_target` = ? - WHERE `id` = ?' - ); - $arguments = array($relTargetPath, $this->getShareId()); + if ($relTargetPath === false) { + \OCP\Util::writeLog('file sharing', 'Wrong target path given: ' . $targetPath, \OCP\Util::ERROR); + return false; } - $result = $query->execute($arguments); + $result = self::updateFileTarget($relTargetPath, $this->share); if ($result) { // update the mount manager with the new paths @@ -343,9 +336,39 @@ class Shared extends \OC\Files\Storage\Common { \OCP\Util::ERROR); } - return $result; + return (bool)$result; } + /** + * @update fileTarget in the database if the mount point changed + * @param string $newPath + * @param array $share reference to the share which should be modified + * @return type + */ + private static function updateFileTarget($newPath, &$share) { + // if the user renames a mount point from a group share we need to create a new db entry + // for the unique name + if ($share['share_type'] === \OCP\Share::SHARE_TYPE_GROUP && + (isset($share['unique_name']) && $share['unique_name'])) { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`item_type`, `item_source`, `item_target`,' + .' `share_type`, `share_with`, `uid_owner`, `permissions`, `stime`, `file_source`,' + .' `file_target`, `token`, `parent`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)'); + $arguments = array($share['item_type'], $share['item_source'], $share['item_target'], + 2, \OCP\User::getUser(), $share['uid_owner'], $share['permissions'], $share['stime'], $share['file_source'], + $newPath, $share['token'], $share['id']); + + } else { + // rename mount point + $query = \OC_DB::prepare( + 'Update `*PREFIX*share` + SET `file_target` = ? + WHERE `id` = ?' + ); + $arguments = array($newPath, $share['id']); + } + + return $query->execute($arguments); + } public function rename($path1, $path2) { @@ -471,6 +494,7 @@ class Shared extends \OC\Files\Storage\Common { || $shares ) { foreach ($shares as $share) { + self::verifyMountPoint($share); \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array( 'share' => $share, @@ -481,19 +505,39 @@ class Shared extends \OC\Files\Storage\Common { } /** - * return mount point of share, relative to data/user/files - * @return string + * check if the parent folder exists otherwise move the mount point up + * + * @param array $share reference to the share we want to check */ - public function getMountPoint() { - return $this->share['file_target']; + private static function verifyMountPoint(&$share) { + $mountPoint = basename($share['file_target']); + $parent = dirname($share['file_target']); + + while (!\OC\Files\Filesystem::is_dir($parent)) { + $parent = dirname($parent); + } + + $newMountPoint = \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint); + + if($newMountPoint !== $share['file_target']) { + $newMountPoint = \OCA\Files_Sharing\Helper::generateUniqueTarget( + $newMountPoint, + array(), + new \OC\Files\View('/' . \OCP\User::getUser() . '/files') + ); + self::updateFileTarget($newMountPoint, $share); + $share['file_target'] = $newMountPoint; + + } } /** - * get share type - * @return integer can be single user share (0) group share (1), unique group share name (2) + * return mount point of share, relative to data/user/files + * + * @return string */ - private function getShareType() { - return $this->share['share_type']; + public function getMountPoint() { + return $this->share['file_target']; } private function setMountPoint($path) { @@ -501,30 +545,17 @@ class Shared extends \OC\Files\Storage\Common { } /** - * does the group share already has a user specific unique name - * @return bool - */ - private function uniqueNameSet() { - return (isset($this->share['unique_name']) && $this->share['unique_name']); - } - - /** * the share now uses a unique name of this user + * + * @brief the share now uses a unique name of this user */ private function setUniqueName() { $this->share['unique_name'] = true; } /** - * get share ID - * @return integer unique share ID - */ - private function getShareId() { - return $this->share['id']; - } - - /** - * get the user who shared the file + * @brief get the user who shared the file + * * @return string */ public function getSharedFrom() { diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php index 21d67caad9d..5cb2b638e5a 100644 --- a/apps/files_sharing/lib/updater.php +++ b/apps/files_sharing/lib/updater.php @@ -115,11 +115,14 @@ class Shared_Updater { * @param array $params */ static public function deleteHook($params) { - self::correctFolders($params['path']); - $fileInfo = \OC\Files\Filesystem::getFileInfo($params['path']); + $path = $params['path']; + self::correctFolders($path); + + $fileInfo = \OC\Files\Filesystem::getFileInfo($path); + // mark file as deleted so that we can clean up the share table if // the file was deleted successfully - self::$toRemove[$params['path']] = $fileInfo['fileid']; + self::$toRemove[$path] = $fileInfo['fileid']; } /** diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index dc07c6fc620..6d0ed434ef2 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -171,6 +171,78 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $appConfig->setValue('core', 'shareapi_enforce_links_password', 'no'); } + /** + * @medium + */ + function testSharePermissions() { + + // sharing file to a user should work if shareapi_exclude_groups is set + // to no + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'no'); + $_POST['path'] = $this->filename; + $_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2; + $_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER; + + $result = Share\Api::createShare(array()); + + $this->assertTrue($result->succeeded()); + $data = $result->getData(); + + $share = $this->getShareFromId($data['id']); + + $items = \OCP\Share::getItemShared('file', $share['item_source']); + + $this->assertTrue(!empty($items)); + + $fileinfo = $this->view->getFileInfo($this->filename); + + $result = \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + $this->assertTrue($result); + + // exclude groups, but not the group the user belongs to. Sharing should still work + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', 'admin,group1,group2'); + + $_POST['path'] = $this->filename; + $_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2; + $_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER; + + $result = Share\Api::createShare(array()); + + $this->assertTrue($result->succeeded()); + $data = $result->getData(); + + $share = $this->getShareFromId($data['id']); + + $items = \OCP\Share::getItemShared('file', $share['item_source']); + + $this->assertTrue(!empty($items)); + + $fileinfo = $this->view->getFileInfo($this->filename); + + $result = \OCP\Share::unshare('file', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2); + + $this->assertTrue($result); + + // now we exclude the group the user belongs to ('group'), sharing should fail now + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', 'admin,group'); + + $_POST['path'] = $this->filename; + $_POST['shareWith'] = \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2; + $_POST['shareType'] = \OCP\Share::SHARE_TYPE_USER; + + $result = Share\Api::createShare(array()); + + $this->assertFalse($result->succeeded()); + + // cleanup + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups', 'no'); + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', ''); + } + /** * @medium diff --git a/apps/files_sharing/tests/base.php b/apps/files_sharing/tests/base.php index 7cd36b9d419..34ec4a36ede 100644 --- a/apps/files_sharing/tests/base.php +++ b/apps/files_sharing/tests/base.php @@ -109,6 +109,8 @@ abstract class Test_Files_Sharing_Base extends \PHPUnit_Framework_TestCase { if ($create) { \OC_User::createUser($user, $password); + \OC_Group::createGroup('group'); + \OC_Group::addToGroup($user, 'group'); } \OC_Util::tearDownFS(); diff --git a/apps/files_sharing/tests/proxy.php b/apps/files_sharing/tests/proxy.php new file mode 100644 index 00000000000..634ed86db54 --- /dev/null +++ b/apps/files_sharing/tests/proxy.php @@ -0,0 +1,98 @@ +<?php +/** + * ownCloud + * + * @author Bjoern Schiessle + * @copyright 2014 Bjoern Schiessle <schiessle@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +require_once __DIR__ . '/base.php'; + +use OCA\Files\Share; + +/** + * Class Test_Files_Sharing_Proxy + */ +class Test_Files_Sharing_Proxy extends Test_Files_Sharing_Base { + + const TEST_FOLDER_NAME = '/folder_share_api_test'; + + private static $tempStorage; + + function setUp() { + parent::setUp(); + + // load proxies + OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; + OC_FileProxy::register(new OCA\Files\Share\Proxy()); + + $this->folder = self::TEST_FOLDER_NAME; + $this->subfolder = '/subfolder_share_api_test'; + $this->subsubfolder = '/subsubfolder_share_api_test'; + + $this->filename = '/share-api-test'; + + // save file with content + $this->view->file_put_contents($this->filename, $this->data); + $this->view->mkdir($this->folder); + $this->view->mkdir($this->folder . $this->subfolder); + $this->view->mkdir($this->folder . $this->subfolder . $this->subsubfolder); + $this->view->file_put_contents($this->folder.$this->filename, $this->data); + $this->view->file_put_contents($this->folder . $this->subfolder . $this->filename, $this->data); + } + + function tearDown() { + $this->view->unlink($this->filename); + $this->view->deleteAll($this->folder); + + self::$tempStorage = null; + + parent::tearDown(); + } + + /** + * @medium + */ + function testpreUnlink() { + + $fileInfo1 = \OC\Files\Filesystem::getFileInfo($this->filename); + $fileInfo2 = \OC\Files\Filesystem::getFileInfo($this->folder); + + $result = \OCP\Share::shareItem('file', $fileInfo1->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31); + $this->assertTrue($result); + + $result = \OCP\Share::shareItem('folder', $fileInfo2->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31); + $this->assertTrue($result); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // move shared folder to 'localDir' and rename it, so that it uses the same + // name as the shared file + \OC\Files\Filesystem::mkdir('localDir'); + $result = \OC\Files\Filesystem::rename($this->folder, '/localDir/' . $this->filename); + $this->assertTrue($result); + + \OC\Files\Filesystem::unlink('localDir'); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // after we deleted 'localDir' the share should be moved up to the root and be + // renamed to "filename (2)" + $this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename)); + $this->assertTrue(\OC\Files\Filesystem::file_exists($this->filename . ' (2)' )); + } +} diff --git a/apps/files_sharing/tests/sharedstorage.php b/apps/files_sharing/tests/sharedstorage.php index 66518a2633f..258a2a9841b 100644 --- a/apps/files_sharing/tests/sharedstorage.php +++ b/apps/files_sharing/tests/sharedstorage.php @@ -49,6 +49,48 @@ class Test_Files_Sharing_Storage extends Test_Files_Sharing_Base { /** * @medium */ + function testDeleteParentOfMountPoint() { + + // share to user + $fileinfo = $this->view->getFileInfo($this->folder); + $result = \OCP\Share::shareItem('folder', $fileinfo['fileid'], \OCP\Share::SHARE_TYPE_USER, + self::TEST_FILES_SHARING_API_USER2, 31); + + $this->assertTrue($result); + + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + $user2View = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + $this->assertTrue($user2View->file_exists($this->folder)); + + // create a local folder + $result = $user2View->mkdir('localfolder'); + $this->assertTrue($result); + + // move mount point to local folder + $result = $user2View->rename($this->folder, '/localfolder/' . $this->folder); + $this->assertTrue($result); + + // mount point in the root folder should no longer exist + $this->assertFalse($user2View->is_dir($this->folder)); + + // delete the local folder + $result = $user2View->unlink('/localfolder'); + $this->assertTrue($result); + + //enforce reload of the mount points + self::loginHelper(self::TEST_FILES_SHARING_API_USER2); + + //mount point should be back at the root + $this->assertTrue($user2View->is_dir($this->folder)); + + //cleanup + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + $this->view->unlink($this->folder); + } + + /** + * @medium + */ function testRenamePartFile() { // share to user @@ -79,5 +121,9 @@ class Test_Files_Sharing_Storage extends Test_Files_Sharing_Base { self::loginHelper(self::TEST_FILES_SHARING_API_USER1); $this->assertTrue($this->view->file_exists( $this->folder. '/foo.txt')); + + //cleanup + $this->view->unlink($this->folder); } + } diff --git a/apps/files_sharing/tests/updater.php b/apps/files_sharing/tests/updater.php index 3427cfe388c..1b851cccf6c 100644 --- a/apps/files_sharing/tests/updater.php +++ b/apps/files_sharing/tests/updater.php @@ -21,47 +21,103 @@ */ require_once __DIR__ . '/../appinfo/update.php'; +require_once __DIR__ . '/base.php'; /** * Class Test_Files_Sharing_Updater */ -class Test_Files_Sharing_Updater extends \PHPUnit_Framework_TestCase { +class Test_Files_Sharing_Updater extends Test_Files_Sharing_Base { + + const TEST_FOLDER_NAME = '/folder_share_api_test'; function setUp() { - // some previous tests didn't clean up and therefore this has to be done here - // FIXME: DIRTY HACK - TODO: find tests, that don't clean up and fix it there - $this->tearDown(); + parent::setUp(); - // add items except one - because this is the test case for the broken share table - $addItems = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache` (`storage`, `path_hash`, ' . - '`parent`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`) ' . - 'VALUES (1, ?, 1, 1, 1, 1, 1, 1)'); - $items = array(1, 3); - $fileIds = array(); - foreach($items as $item) { - // the number is used as path_hash - $addItems->execute(array($item)); - $fileIds[] = \OC_DB::insertId('*PREFIX*filecache'); - } + $this->folder = self::TEST_FOLDER_NAME; - $addShares = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`file_source`, `item_type`, `uid_owner`) VALUES (?, \'file\', 1)'); - // the number is used as item_source - $addShares->execute(array($fileIds[0])); - $addShares->execute(array(200)); // id of "deleted" file - $addShares->execute(array($fileIds[1])); + $this->filename = '/share-api-test.txt'; + + // save file with content + $this->view->file_put_contents($this->filename, $this->data); + $this->view->mkdir($this->folder); + $this->view->file_put_contents($this->folder . '/' . $this->filename, $this->data); } function tearDown() { + $this->view->unlink($this->filename); + $this->view->deleteAll($this->folder); + $removeShares = \OC_DB::prepare('DELETE FROM `*PREFIX*share`'); $removeShares->execute(); $removeItems = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache`'); $removeItems->execute(); + + parent::tearDown(); + } + + /** + * test deletion of a folder which contains share mount points. Share mount + * points should move up to the parent before the folder gets deleted so + * that the mount point doesn't end up at the trash bin + */ + function testDeleteParentFolder() { + $status = \OC_App::isEnabled('files_trashbin'); + \OC_App::enable('files_trashbin'); + + \OCA\Files_Trashbin\Trashbin::registerHooks(); + OC_FileProxy::register(new OCA\Files\Share\Proxy()); + + $fileinfo = \OC\Files\Filesystem::getFileInfo($this->folder); + $this->assertTrue($fileinfo instanceof \OC\Files\FileInfo); + + \OCP\Share::shareItem('folder', $fileinfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_FILES_SHARING_API_USER2, 31); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + $view = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2 . '/files'); + + // check if user2 can see the shared folder + $this->assertTrue($view->file_exists($this->folder)); + + $view->mkdir("localFolder"); + $view->file_put_contents("localFolder/localFile.txt", "local file"); + + $view->rename($this->folder, 'localFolder/' . $this->folder); + + // share mount point should now be moved to the subfolder + $this->assertFalse($view->file_exists($this->folder)); + $this->assertTrue($view->file_exists('localFolder/' .$this->folder)); + + $view->unlink('localFolder'); + + $this->loginHelper(self::TEST_FILES_SHARING_API_USER2); + + // mount point should move up again + $this->assertTrue($view->file_exists($this->folder)); + + // trashbin should contain the local file but not the mount point + $rootView = new \OC\Files\View('/' . self::TEST_FILES_SHARING_API_USER2); + $dirContent = $rootView->getDirectoryContent('files_trashbin/files'); + $this->assertSame(1, count($dirContent)); + $firstElement = reset($dirContent); + $ext = pathinfo($firstElement['path'], PATHINFO_EXTENSION); + $this->assertTrue($rootView->file_exists('files_trashbin/files/localFolder.' . $ext . '/localFile.txt')); + $this->assertFalse($rootView->file_exists('files_trashbin/files/localFolder.' . $ext . '/' . $this->folder)); + + //cleanup + $rootView->deleteAll('files_trashin'); + + if ($status === false) { + \OC_App::disable('files_trashbin'); + } } /** * @medium */ function testRemoveBrokenShares() { + + $this->prepareFileCache(); + // check if there are just 3 shares (see setUp - precondition: empty table) $countShares = \OC_DB::prepare('SELECT COUNT(`id`) FROM `*PREFIX*share`'); $result = $countShares->execute()->fetchOne(); @@ -114,6 +170,7 @@ class Test_Files_Sharing_Updater extends \PHPUnit_Framework_TestCase { } } + // cleanup $this->cleanupSharedTable(); } @@ -123,6 +180,9 @@ class Test_Files_Sharing_Updater extends \PHPUnit_Framework_TestCase { $query->execute(); } + /** + * prepare sharing table for testRemoveSharedFolder() + */ private function prepareDB() { $this->cleanupSharedTable(); // add items except one - because this is the test case for the broken share table @@ -143,4 +203,32 @@ class Test_Files_Sharing_Updater extends \PHPUnit_Framework_TestCase { $addItems->execute($item); } } + + /** + * prepare file cache for testRemoveBrokenShares() + */ + private function prepareFileCache() { + // some previous tests didn't clean up and therefore this has to be done here + // FIXME: DIRTY HACK - TODO: find tests, that don't clean up and fix it there + $this->tearDown(); + + // add items except one - because this is the test case for the broken share table + $addItems = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache` (`storage`, `path_hash`, ' . + '`parent`, `mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`) ' . + 'VALUES (1, ?, 1, 1, 1, 1, 1, 1)'); + $items = array(1, 3); + $fileIds = array(); + foreach($items as $item) { + // the number is used as path_hash + $addItems->execute(array($item)); + $fileIds[] = \OC_DB::insertId('*PREFIX*filecache'); + } + + $addShares = \OC_DB::prepare('INSERT INTO `*PREFIX*share` (`file_source`, `item_type`, `uid_owner`) VALUES (?, \'file\', 1)'); + // the number is used as item_source + $addShares->execute(array($fileIds[0])); + $addShares->execute(array(200)); // id of "deleted" file + $addShares->execute(array($fileIds[1])); + } + } diff --git a/console.php b/console.php index dd2c1026e47..55bae7a6b38 100644 --- a/console.php +++ b/console.php @@ -8,29 +8,34 @@ use Symfony\Component\Console\Application; -require_once 'lib/base.php'; +try { + require_once 'lib/base.php'; -// Don't do anything if ownCloud has not been installed yet -if (!OC_Config::getValue('installed', false)) { - echo "Console can only be used once ownCloud has been installed" . PHP_EOL; - exit(0); -} + // Don't do anything if ownCloud has not been installed yet + if (!\OC::$server->getConfig()->getSystemValue('installed', false)) { + echo "Console can only be used once ownCloud has been installed" . PHP_EOL; + exit(0); + } -if (!OC::$CLI) { - echo "This script can be run from the command line only" . PHP_EOL; - exit(0); -} + if (!OC::$CLI) { + echo "This script can be run from the command line only" . PHP_EOL; + exit(0); + } -// load all apps to get all api routes properly setup -OC_App::loadApps(); + // load all apps to get all api routes properly setup + OC_App::loadApps(); -$defaults = new OC_Defaults; -$application = new Application($defaults->getName(), \OC_Util::getVersionString()); -require_once 'core/register_command.php'; -foreach(OC_App::getAllApps() as $app) { - $file = OC_App::getAppPath($app).'/appinfo/register_command.php'; - if(file_exists($file)) { - require $file; + $defaults = new OC_Defaults; + $application = new Application($defaults->getName(), \OC_Util::getVersionString()); + require_once 'core/register_command.php'; + foreach(OC_App::getAllApps() as $app) { + $file = OC_App::getAppPath($app).'/appinfo/register_command.php'; + if(file_exists($file)) { + require $file; + } } + $application->run(); +} catch (Exception $ex) { + echo "An unhandled exception has been thrown:" . PHP_EOL; + echo $ex; } -$application->run(); diff --git a/core/command/maintenance/repair.php b/core/command/maintenance/repair.php index c5ef0c55cc0..310c01fbe2a 100644 --- a/core/command/maintenance/repair.php +++ b/core/command/maintenance/repair.php @@ -29,7 +29,7 @@ class Repair extends Command { protected function configure() { $this ->setName('maintenance:repair') - ->setDescription('set single user mode'); + ->setDescription('repair this installation'); } protected function execute(InputInterface $input, OutputInterface $output) { diff --git a/core/js/config.php b/core/js/config.php index 33665b8401c..80b1b6d242d 100644 --- a/core/js/config.php +++ b/core/js/config.php @@ -80,6 +80,7 @@ $array = array( 'defaultExpireDate' => $defaultExpireDate, 'defaultExpireDateEnforced' => $enforceDefaultExpireDate, 'enforcePasswordForPublicLink' => \OCP\Util::isPublicLinkPasswordRequired(), + 'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(), ) ) ), diff --git a/core/js/oc-dialogs.js b/core/js/oc-dialogs.js index f6c17122d7d..54b9442af27 100644 --- a/core/js/oc-dialogs.js +++ b/core/js/oc-dialogs.js @@ -351,7 +351,7 @@ var OCdialogs = { conflict.find('.filename').text(original.name); conflict.find('.original .size').text(humanFileSize(original.size)); - conflict.find('.original .mtime').text(formatDate(original.mtime*1000)); + conflict.find('.original .mtime').text(formatDate(original.mtime)); // ie sucks if (replacement.size && replacement.lastModifiedDate) { conflict.find('.replacement .size').text(humanFileSize(replacement.size)); @@ -374,9 +374,9 @@ var OCdialogs = { //set more recent mtime bold // ie sucks - if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime*1000) { + if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() > original.mtime) { conflict.find('.replacement .mtime').css('font-weight', 'bold'); - } else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime*1000) { + } else if (replacement.lastModifiedDate && replacement.lastModifiedDate.getTime() < original.mtime) { conflict.find('.original .mtime').css('font-weight', 'bold'); } else { //TODO add to same mtime collection? diff --git a/l10n/templates/core.pot b/l10n/templates/core.pot index c6565eb862d..f1ab23fc580 100644 --- a/l10n/templates/core.pot +++ b/l10n/templates/core.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/files.pot b/l10n/templates/files.pot index da3a068b7f7..03cae133d8a 100644 --- a/l10n/templates/files.pot +++ b/l10n/templates/files.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -211,7 +211,7 @@ msgid "" "big." msgstr "" -#: js/filelist.js:602 js/filelist.js:1672 +#: js/filelist.js:602 js/filelist.js:1671 msgid "Pending" msgstr "" @@ -227,39 +227,39 @@ msgstr "" msgid "Error" msgstr "" -#: js/filelist.js:1201 +#: js/filelist.js:1213 msgid "Could not rename file" msgstr "" -#: js/filelist.js:1335 +#: js/filelist.js:1334 msgid "Error deleting file." msgstr "" -#: js/filelist.js:1438 templates/list.php:62 +#: js/filelist.js:1437 templates/list.php:62 msgid "Name" msgstr "" -#: js/filelist.js:1439 templates/list.php:75 +#: js/filelist.js:1438 templates/list.php:75 msgid "Size" msgstr "" -#: js/filelist.js:1440 templates/list.php:78 +#: js/filelist.js:1439 templates/list.php:78 msgid "Modified" msgstr "" -#: js/filelist.js:1450 js/filesummary.js:141 js/filesummary.js:168 +#: js/filelist.js:1449 js/filesummary.js:141 js/filesummary.js:168 msgid "%n folder" msgid_plural "%n folders" msgstr[0] "" msgstr[1] "" -#: js/filelist.js:1456 js/filesummary.js:142 js/filesummary.js:169 +#: js/filelist.js:1455 js/filesummary.js:142 js/filesummary.js:169 msgid "%n file" msgid_plural "%n files" msgstr[0] "" msgstr[1] "" -#: js/filelist.js:1580 js/filelist.js:1619 +#: js/filelist.js:1579 js/filelist.js:1618 msgid "Uploading %n file" msgid_plural "Uploading %n files" msgstr[0] "" diff --git a/l10n/templates/files_encryption.pot b/l10n/templates/files_encryption.pot index 051793af8e9..73f8b5c863a 100644 --- a/l10n/templates/files_encryption.pot +++ b/l10n/templates/files_encryption.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -89,7 +89,7 @@ msgid "" "the encryption app has been disabled." msgstr "" -#: hooks/hooks.php:295 +#: hooks/hooks.php:299 msgid "Following users are not set up for encryption:" msgstr "" diff --git a/l10n/templates/files_external.pot b/l10n/templates/files_external.pot index 4794ed8e20d..73e8c59268f 100644 --- a/l10n/templates/files_external.pot +++ b/l10n/templates/files_external.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/files_sharing.pot b/l10n/templates/files_sharing.pot index 8ed864c657c..8be713c64a2 100644 --- a/l10n/templates/files_sharing.pot +++ b/l10n/templates/files_sharing.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: js/share.js:33 +#: js/share.js:35 msgid "Shared by {owner}" msgstr "" diff --git a/l10n/templates/files_trashbin.pot b/l10n/templates/files_trashbin.pot index 23356738375..289e2e88369 100644 --- a/l10n/templates/files_trashbin.pot +++ b/l10n/templates/files_trashbin.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/files_versions.pot b/l10n/templates/files_versions.pot index 5ab74dc02a6..304ce12e92c 100644 --- a/l10n/templates/files_versions.pot +++ b/l10n/templates/files_versions.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/lib.pot b/l10n/templates/lib.pot index d544c5e5f73..6574e0383cf 100644 --- a/l10n/templates/lib.pot +++ b/l10n/templates/lib.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,11 +18,11 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" -#: base.php:694 +#: base.php:695 msgid "You are accessing the server from an untrusted domain." msgstr "" -#: base.php:695 +#: base.php:696 msgid "" "Please contact your administrator. If you are an administrator of this " "instance, configure the \"trusted_domain\" setting in config/config.php. An " diff --git a/l10n/templates/private.pot b/l10n/templates/private.pot index 0421990090c..f5a58d6f69f 100644 --- a/l10n/templates/private.pot +++ b/l10n/templates/private.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/settings.pot b/l10n/templates/settings.pot index f6539cae89e..f113a096426 100644 --- a/l10n/templates/settings.pot +++ b/l10n/templates/settings.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/user_ldap.pot b/l10n/templates/user_ldap.pot index 61114718e61..45238cc4881 100644 --- a/l10n/templates/user_ldap.pot +++ b/l10n/templates/user_ldap.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/l10n/templates/user_webdavauth.pot b/l10n/templates/user_webdavauth.pot index 10bd03976a2..0f65c10e0f0 100644 --- a/l10n/templates/user_webdavauth.pot +++ b/l10n/templates/user_webdavauth.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: ownCloud Core 6.0.0\n" "Report-Msgid-Bugs-To: translations@owncloud.org\n" -"POT-Creation-Date: 2014-05-20 01:54-0400\n" +"POT-Creation-Date: 2014-05-22 01:54-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" diff --git a/lib/base.php b/lib/base.php index abb76b94a5d..a022b9d005b 100644 --- a/lib/base.php +++ b/lib/base.php @@ -627,7 +627,8 @@ class OC { public static function registerLogRotate() { if (OC_Config::getValue('installed', false) && OC_Config::getValue('log_rotate_size', false) && !self::needUpgrade()) { //don't try to do this before we are properly setup - \OCP\BackgroundJob::registerJob('OC\Log\Rotate', OC_Config::getValue("datadirectory", OC::$SERVERROOT . '/data') . '/owncloud.log'); + //use custom logfile path if defined, otherwise use default of owncloud.log in data directory + \OCP\BackgroundJob::registerJob('OC\Log\Rotate', OC_Config::getValue('logfile', OC_Config::getValue("datadirectory", OC::$SERVERROOT . '/data') . '/owncloud.log')); } } diff --git a/lib/private/files/cache/permissions.php b/lib/private/files/cache/permissions.php index 2e2bdb20b78..eba18af3863 100644 --- a/lib/private/files/cache/permissions.php +++ b/lib/private/files/cache/permissions.php @@ -36,7 +36,7 @@ class Permissions { $sql = 'SELECT `permissions` FROM `*PREFIX*permissions` WHERE `user` = ? AND `fileid` = ?'; $result = \OC_DB::executeAudited($sql, array($user, $fileId)); if ($row = $result->fetchRow()) { - return $row['permissions']; + return $this->updatePermissions($row['permissions']); } else { return -1; } @@ -78,7 +78,7 @@ class Permissions { $result = \OC_DB::executeAudited($sql, $params); $filePermissions = array(); while ($row = $result->fetchRow()) { - $filePermissions[$row['fileid']] = $row['permissions']; + $filePermissions[$row['fileid']] = $this->updatePermissions($row['permissions']); } return $filePermissions; } @@ -99,7 +99,7 @@ class Permissions { $result = \OC_DB::executeAudited($sql, array($parentId, $user)); $filePermissions = array(); while ($row = $result->fetchRow()) { - $filePermissions[$row['fileid']] = $row['permissions']; + $filePermissions[$row['fileid']] = $this->updatePermissions($row['permissions']); } return $filePermissions; } @@ -140,4 +140,17 @@ class Permissions { } return $users; } + + /** + * check if admin removed the share permission for the user and update the permissions + * + * @param int $permissions + * @return int + */ + protected function updatePermissions($permissions) { + if (\OCP\Util::isSharingDisabledForUser()) { + $permissions &= ~\OCP\PERMISSION_SHARE; + } + return $permissions; + } } diff --git a/lib/private/files/mount/mount.php b/lib/private/files/mount/mount.php index 256630726d2..d4a4e186fb9 100644 --- a/lib/private/files/mount/mount.php +++ b/lib/private/files/mount/mount.php @@ -59,6 +59,8 @@ class Mount { } /** + * get complete path to the mount point, relative to data/ + * * @return string */ public function getMountPoint() { @@ -66,6 +68,15 @@ class Mount { } /** + * get name of the mount point + * + * @return string + */ + public function getMountPointName() { + return basename(rtrim($this->mountPoint, '/')); + } + + /** * @param string $mountPoint new mount point */ public function setMountPoint($mountPoint) { diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index fef33cabd87..b03ae7d0517 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -81,6 +81,10 @@ abstract class Common implements \OC\Files\Storage\Storage { } public function isSharable($path) { + if (\OC_Util::isSharingDisabledForUser()) { + return false; + } + return $this->isReadable($path); } diff --git a/lib/private/preferences.php b/lib/private/preferences.php index e6d9f28b1d6..a4bfc650d08 100644 --- a/lib/private/preferences.php +++ b/lib/private/preferences.php @@ -206,6 +206,43 @@ class Preferences { } /** + * Gets the preference for an array of users + * @param string $app + * @param string $key + * @param array $users + * @return array Mapped values: userid => value + */ + public function getValueForUsers($app, $key, $users) { + if (empty($users) || !is_array($users)) { + return array(); + } + + $chunked_users = array_chunk($users, 50, true); + $placeholders_50 = implode(',', array_fill(0, 50, '?')); + + $userValues = array(); + foreach ($chunked_users as $chunk) { + $queryParams = $chunk; + array_unshift($queryParams, $key); + array_unshift($queryParams, $app); + + $placeholders = (sizeof($chunk) == 50) ? $placeholders_50 : implode(',', array_fill(0, sizeof($chunk), '?')); + + $query = 'SELECT `userid`, `configvalue` ' + . ' FROM `*PREFIX*preferences` ' + . ' WHERE `appid` = ? AND `configkey` = ?' + . ' AND `userid` IN (' . $placeholders . ')'; + $result = $this->conn->executeQuery($query, $queryParams); + + while ($row = $result->fetch()) { + $userValues[$row['userid']] = $row['configvalue']; + } + } + + return $userValues; + } + + /** * Deletes a key * @param string $user user * @param string $app app diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 16bc492d383..46796c26370 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -485,15 +485,23 @@ class Share extends \OC\Share\Constants { $itemSourceName = $itemSource; } - // verify that the file exists before we try to share it + // check if file can be shared if ($itemType === 'file' or $itemType === 'folder') { $path = \OC\Files\Filesystem::getPath($itemSource); + // verify that the file exists before we try to share it if (!$path) { $message = 'Sharing %s failed, because the file does not exist'; $message_t = $l->t('Sharing %s failed, because the file does not exist', array($itemSourceName)); \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); throw new \Exception($message_t); } + // verify that the user has share permission + if (!\OC\Files\Filesystem::isSharable($path)) { + $message = 'You are not allowed to share %s'; + $message_t = $l->t('You are not allowed to share %s', array($itemSourceName)); + \OC_Log::write('OCP\Share', sprintf($message, $itemSourceName), \OC_Log::ERROR); + throw new \Exception($message_t); + } } //verify that we don't share a folder which already contains a share mount point diff --git a/lib/private/util.php b/lib/private/util.php index c018721afe3..23c7053002c 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -97,6 +97,29 @@ class OC_Util { } /** + * check if sharing is disabled for the current user + * + * @return boolean + */ + public static function isSharingDisabledForUser() { + if (\OC_Appconfig::getValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { + $user = \OCP\User::getUser(); + $groupsList = \OC_Appconfig::getValue('core', 'shareapi_exclude_groups_list', ''); + $excludedGroups = explode(',', $groupsList); + $usersGroups = \OC_Group::getUserGroups($user); + if (!empty($usersGroups)) { + $remainingGroups = array_diff($usersGroups, $excludedGroups); + // if the user is only in groups which are disabled for sharing then + // sharing is also disabled for the user + if (empty($remainingGroups)) { + return true; + } + } + } + return false; + } + + /** * Get the quota of a user * @param string $user * @return int Quota bytes diff --git a/lib/public/util.php b/lib/public/util.php index 3166d4040d8..d1faec3997f 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -117,6 +117,15 @@ class Util { } /** + * check if sharing is disabled for the current user + * + * @return boolean + */ + public static function isSharingDisabledForUser() { + return \OC_Util::isSharingDisabledForUser(); + } + + /** * get l10n object * @param string $application * @return \OC_L10N diff --git a/settings/admin.php b/settings/admin.php index f9406246e76..699ba97edd5 100755 --- a/settings/admin.php +++ b/settings/admin.php @@ -10,6 +10,7 @@ OC_Util::checkAdminUser(); OC_Util::addStyle( "settings", "settings" ); OC_Util::addScript( "settings", "admin" ); OC_Util::addScript( "settings", "log" ); +OC_Util::addScript( 'core', 'multiselect' ); OC_App::setActiveNavigationEntry( "admin" ); $tmpl = new OC_Template( 'settings', 'admin', 'user'); @@ -48,6 +49,23 @@ $tmpl->assign('shareAPIEnabled', OC_Appconfig::getValue('core', 'shareapi_enable $tmpl->assign('shareDefaultExpireDateSet', OC_Appconfig::getValue('core', 'shareapi_default_expire_date', 'no')); $tmpl->assign('shareExpireAfterNDays', OC_Appconfig::getValue('core', 'shareapi_expire_after_n_days', '7')); $tmpl->assign('shareEnforceExpireDate', OC_Appconfig::getValue('core', 'shareapi_enforce_expire_date', 'no')); +$excludeGroups = OC_Appconfig::getValue('core', 'shareapi_exclude_groups', 'no') === 'yes' ? true : false; +$tmpl->assign('shareExcludeGroups', $excludeGroups); +$allGroups = OC_Group::getGroups(); +$excludedGroupsList = OC_Appconfig::getValue('core', 'shareapi_exclude_groups_list', ''); +$excludedGroups = $excludedGroupsList !== '' ? explode(',', $excludedGroupsList) : array(); +$groups = array(); +foreach ($allGroups as $group) { + if (in_array($group, $excludedGroups)) { + $groups[$group] = array('gid' => $group, + 'excluded' => true); + } else { + $groups[$group] = array('gid' => $group, + 'excluded' => false); + } +} +ksort($groups); +$tmpl->assign('groups', $groups); // Check if connected using HTTPS diff --git a/settings/ajax/excludegroups.php b/settings/ajax/excludegroups.php new file mode 100644 index 00000000000..2934a448a6a --- /dev/null +++ b/settings/ajax/excludegroups.php @@ -0,0 +1,18 @@ +<?php +OC_JSON::checkSubAdminUser(); +OCP\JSON::callCheck(); + +$selectedGroups = isset($_POST["selectedGroups"]) ? json_decode($_POST["selectedGroups"]) : array(); +$changedGroup = isset($_POST["changedGroup"]) ? $_POST["changedGroup"] : ''; + +if ($changedGroup !== '') { + if(($key = array_search($changedGroup, $selectedGroups)) !== false) { + unset($selectedGroups[$key]); + } else { + $selectedGroups[] = $changedGroup; + } +} else { + \OCP\Util::writeLog('core', 'Can not update list of excluded groups from sharing, parameter missing', \OCP\Util::WARN); +} + +\OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', implode(',', $selectedGroups)); diff --git a/settings/css/settings.css b/settings/css/settings.css index 2056e567b38..d0e44e721b4 100644 --- a/settings/css/settings.css +++ b/settings/css/settings.css @@ -158,6 +158,15 @@ table.shareAPI .indent { padding-left: 2em; } vertical-align: text-bottom; } +#selectGroups select { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; + display: inline-block; + height: 36px; + padding: 7px 10px +} + span.success { background: #37ce02; border-radius: 8px; diff --git a/settings/js/admin.js b/settings/js/admin.js index cd11e68442a..249131464a8 100644 --- a/settings/js/admin.js +++ b/settings/js/admin.js @@ -1,4 +1,49 @@ +var SharingGroupList = { + applyMultipleSelect: function(element) { + var checked = []; + if ($(element).hasClass('groupsselect')) { + if (element.data('userGroups')) { + checked = element.data('userGroups'); + } + var checkHandeler = function(group) { + $.post(OC.filePath('settings', 'ajax', 'excludegroups.php'), + {changedGroup: group, selectedGroups: JSON.stringify(checked)}, + function() {}); + }; + + + var addGroup = function(select, group) { + $(this).each(function(index, element) { + if ($(element).find('option[value="' + group + '"]').length === 0 && + select.data('msid') !== $(element).data('msid')) { + $(element).append('<option value="' + escapeHTML(group) + '">' + + escapeHTML(group) + '</option>'); + } + }); + }; + + var label = null; + element.multiSelect({ + createCallback: addGroup, + createText: label, + selectedFirst: true, + checked: checked, + oncheck: checkHandeler, + onuncheck: checkHandeler, + minWidth: 100 + }); + + } + } +}; + $(document).ready(function(){ + + $('select#excludedGroups[multiple]').each(function (index, element) { + SharingGroupList.applyMultipleSelect($(element)); + }); + + $('#loglevel').change(function(){ $.post(OC.filePath('settings','ajax','setloglevel.php'), { level: $(this).val() },function(){ OC.Log.reload(); @@ -84,4 +129,8 @@ $(document).ready(function(){ OC.msg.finishedAction('#sendtestmail_msg', data); }); }); + + $('#shareapiExcludeGroups').change(function() { + $("#selectExcludedGroups").toggleClass('hidden', !this.checked); + }); }); diff --git a/settings/routes.php b/settings/routes.php index 21d406beeca..0e0f293b9be 100644 --- a/settings/routes.php +++ b/settings/routes.php @@ -84,3 +84,5 @@ $this->create('settings_admin_mail_test', '/settings/admin/mailtest') ->action('OC\Settings\Admin\Controller', 'sendTestMail'); $this->create('settings_ajax_setsecurity', '/settings/ajax/setsecurity.php') ->actionInclude('settings/ajax/setsecurity.php'); +$this->create('settings_ajax_excludegroups', '/settings/ajax/excludegroups.php') + ->actionInclude('settings/ajax/excludegroups.php'); diff --git a/settings/templates/admin.php b/settings/templates/admin.php index d1f519a072d..a4a3698d3bd 100644 --- a/settings/templates/admin.php +++ b/settings/templates/admin.php @@ -240,9 +240,6 @@ if (!$_['internetconnectionworking']) { </div> <em><?php p($l->t('Allow users to share items to the public with links')); ?></em> - - - </td> </tr> <tr> @@ -271,7 +268,24 @@ if (!$_['internetconnectionworking']) { <em><?php p($l->t('Allow users to send mail notification for shared files')); ?></em> </td> </tr> - + <tr> + <td <?php if ($_['shareAPIEnabled'] === 'no') print_unescaped('class="hidden"');?>> + <input type="checkbox" name="shareapi_exclude_groups" id="shareapiExcludeGroups" + value="1" <?php if ($_['shareExcludeGroups']) print_unescaped('checked="checked"'); ?> /> + <label for="shareapiExcludeGroups"><?php p($l->t('Exclude groups from sharing'));?></label><br/> + <div id="selectExcludedGroups" class="<?php ($_['shareExcludeGroups']) ? p('indent') : p('hidden indent'); ?>"> + <select + class="groupsselect" + id="excludedGroups" data-placeholder="groups" + title="<?php p($l->t('Groups'))?>" multiple="multiple"> + <?php foreach($_["groups"] as $group): ?> + <option value="<?php p($group['gid'])?>" <?php if($group['excluded']) { p('selected="selected"'); }?>><?php p($group['gid']);?></option> + <?php endforeach;?> + </select> + </div> + <em><?php p($l->t('These groups will still be able to receive shares, but not to initiate them.')); ?></em> + </td> + </tr> </table> </div> diff --git a/tests/lib/preferences.php b/tests/lib/preferences.php index f1f6ed08003..499be914fb7 100644 --- a/tests/lib/preferences.php +++ b/tests/lib/preferences.php @@ -184,6 +184,43 @@ class Test_Preferences_Object extends PHPUnit_Framework_TestCase { $preferences->setValue('grg', 'bar', 'foo', 'v2'); } + public function testGetUserValues() + { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*preferences` VALUES(?, ?, ?, ?)'); + $query->execute(array('SomeUser', 'testGetUserValues', 'somekey', 'somevalue')); + $query->execute(array('AnotherUser', 'testGetUserValues', 'somekey', 'someothervalue')); + $query->execute(array('AUser', 'testGetUserValues', 'somekey', 'somevalue')); + + $preferences = new OC\Preferences(\OC_DB::getConnection()); + $users = array('SomeUser', 'AnotherUser', 'NoValueSet'); + + $values = $preferences->getValueForUsers('testGetUserValues', 'somekey', $users); + $this->assertUserValues($values); + + // Add a lot of users so the array is chunked + for ($i = 1; $i <= 75; $i++) { + array_unshift($users, 'NoValueBefore#' . $i); + array_push($users, 'NoValueAfter#' . $i); + } + + $values = $preferences->getValueForUsers('testGetUserValues', 'somekey', $users); + $this->assertUserValues($values); + + // Clean DB after the test + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*preferences` WHERE `appid` = ?'); + $query->execute(array('testGetUserValues')); + } + + protected function assertUserValues($values) { + $this->assertEquals(2, sizeof($values)); + + $this->assertArrayHasKey('SomeUser', $values); + $this->assertEquals('somevalue', $values['SomeUser']); + + $this->assertArrayHasKey('AnotherUser', $values); + $this->assertEquals('someothervalue', $values['AnotherUser']); + } + public function testDeleteKey() { $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); diff --git a/tests/lib/util.php b/tests/lib/util.php index c4780cc5f48..4dc7813d918 100644 --- a/tests/lib/util.php +++ b/tests/lib/util.php @@ -235,4 +235,59 @@ class Test_Util extends PHPUnit_Framework_TestCase { array(' .', false), ); } + + /** + * @dataProvider dataProviderForTestIsSharingDisabledForUser + * @param array $groups existing groups + * @param array $membership groups the user belong to + * @param array $excludedGroups groups which should be excluded from sharing + * @param bool $expected expected result + */ + function testIsSharingDisabledForUser($groups, $membership, $excludedGroups, $expected) { + $uid = "user1"; + \OC_User::setUserId($uid); + + \OC_User::createUser($uid, "passwd"); + + foreach($groups as $group) { + \OC_Group::createGroup($group); + } + + foreach($membership as $group) { + \OC_Group::addToGroup($uid, $group); + } + + $appConfig = \OC::$server->getAppConfig(); + $appConfig->setValue('core', 'shareapi_exclude_groups_list', implode(',', $excludedGroups)); + $appConfig->setValue('core', 'shareapi_exclude_groups', 'yes'); + + $result = \OCP\Util::isSharingDisabledForUser(); + + $this->assertSame($expected, $result); + + // cleanup + \OC_User::deleteUser($uid); + \OC_User::setUserId(''); + + foreach($groups as $group) { + \OC_Group::deleteGroup($group); + } + + $appConfig->setValue('core', 'shareapi_exclude_groups_list', ''); + $appConfig->setValue('core', 'shareapi_exclude_groups', 'no'); + + } + + public function dataProviderForTestIsSharingDisabledForUser() { + return array( + // existing groups, groups the user belong to, groups excluded from sharing, expected result + array(array('g1', 'g2', 'g3'), array(), array('g1'), false), + array(array('g1', 'g2', 'g3'), array(), array(), false), + array(array('g1', 'g2', 'g3'), array('g2'), array('g1'), false), + array(array('g1', 'g2', 'g3'), array('g2'), array(), false), + array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1'), false), + array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1', 'g2'), true), + array(array('g1', 'g2', 'g3'), array('g1', 'g2'), array('g1', 'g2', 'g3'), true), + ); + } } |