aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/css/files.css4
-rw-r--r--apps/files/js/fileactions.js2
-rw-r--r--apps/files/js/filelist.js43
-rw-r--r--apps/files/tests/js/filelistSpec.js65
-rw-r--r--apps/files_encryption/hooks/hooks.php10
-rw-r--r--apps/files_encryption/tests/hooks.php42
-rw-r--r--apps/files_external/appinfo/app.php12
-rwxr-xr-xapps/files_external/lib/config.php103
-rw-r--r--apps/files_external/tests/mountconfig.php165
-rw-r--r--apps/files_sharing/appinfo/app.php3
-rw-r--r--apps/files_sharing/js/public.js38
-rw-r--r--apps/files_sharing/js/share.js50
-rw-r--r--apps/files_sharing/lib/helper.php22
-rw-r--r--apps/files_sharing/lib/permissions.php14
-rw-r--r--apps/files_sharing/lib/proxy.php69
-rw-r--r--apps/files_sharing/lib/share/file.php11
-rw-r--r--apps/files_sharing/lib/sharedstorage.php121
-rw-r--r--apps/files_sharing/lib/updater.php9
-rw-r--r--apps/files_sharing/tests/api.php72
-rw-r--r--apps/files_sharing/tests/base.php2
-rw-r--r--apps/files_sharing/tests/proxy.php98
-rw-r--r--apps/files_sharing/tests/sharedstorage.php46
-rw-r--r--apps/files_sharing/tests/updater.php128
-rw-r--r--console.php45
-rw-r--r--core/command/maintenance/repair.php2
-rw-r--r--core/js/config.php1
-rw-r--r--core/js/oc-dialogs.js6
-rw-r--r--l10n/templates/core.pot2
-rw-r--r--l10n/templates/files.pot20
-rw-r--r--l10n/templates/files_encryption.pot4
-rw-r--r--l10n/templates/files_external.pot2
-rw-r--r--l10n/templates/files_sharing.pot4
-rw-r--r--l10n/templates/files_trashbin.pot2
-rw-r--r--l10n/templates/files_versions.pot2
-rw-r--r--l10n/templates/lib.pot6
-rw-r--r--l10n/templates/private.pot2
-rw-r--r--l10n/templates/settings.pot2
-rw-r--r--l10n/templates/user_ldap.pot2
-rw-r--r--l10n/templates/user_webdavauth.pot2
-rw-r--r--lib/base.php3
-rw-r--r--lib/private/files/cache/permissions.php19
-rw-r--r--lib/private/files/mount/mount.php11
-rw-r--r--lib/private/files/storage/common.php4
-rw-r--r--lib/private/preferences.php37
-rw-r--r--lib/private/share/share.php10
-rwxr-xr-xlib/private/util.php23
-rw-r--r--lib/public/util.php9
-rwxr-xr-xsettings/admin.php18
-rw-r--r--settings/ajax/excludegroups.php18
-rw-r--r--settings/css/settings.css9
-rw-r--r--settings/js/admin.js49
-rw-r--r--settings/routes.php2
-rw-r--r--settings/templates/admin.php22
-rw-r--r--tests/lib/preferences.php37
-rw-r--r--tests/lib/util.php55
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),
+ );
+ }
}