summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.htaccess7
-rw-r--r--apps/files_external/controller/storagescontroller.php3
-rw-r--r--apps/files_external/js/statusmanager.js263
-rw-r--r--apps/files_external/lib/auth/password/globalauth.php5
-rw-r--r--apps/files_external/lib/config/configadapter.php10
-rw-r--r--apps/files_external/lib/failedcache.php3
-rw-r--r--apps/files_external/lib/insufficientdataformeaningfulanswerexception.php11
-rw-r--r--apps/files_external/lib/smb.php13
-rw-r--r--apps/files_external/lib/storageconfig.php2
-rw-r--r--build/integration/features/sharing-v1.feature25
-rw-r--r--console.php2
-rw-r--r--lib/private/console/application.php21
-rw-r--r--lib/private/db/mdb2schemamanager.php3
-rw-r--r--lib/private/diagnostics/querylogger.php2
-rw-r--r--lib/private/files.php10
-rw-r--r--lib/private/files/node/root.php2
-rw-r--r--lib/private/files/storage/common.php2
-rw-r--r--lib/private/files/stream/dir.php1
-rw-r--r--lib/private/files/type/detection.php4
-rw-r--r--lib/private/share/hooks.php2
-rw-r--r--lib/private/share20/manager.php14
-rw-r--r--lib/private/share20/share.php2
-rw-r--r--lib/public/console/consoleevent.php69
-rw-r--r--settings/js/users/users.js32
-rw-r--r--tests/lib/share20/managertest.php44
25 files changed, 411 insertions, 141 deletions
diff --git a/.htaccess b/.htaccess
index 4a4adce144c..725efa0971a 100644
--- a/.htaccess
+++ b/.htaccess
@@ -1,9 +1,12 @@
<IfModule mod_headers.c>
- <IfModule mod_fcgid.c>
- <IfModule mod_setenvif.c>
+ <IfModule mod_setenvif.c>
+ <IfModule mod_fcgid.c>
SetEnvIfNoCase ^Authorization$ "(.+)" XAUTHORIZATION=$1
RequestHeader set XAuthorization %{XAUTHORIZATION}e env=XAUTHORIZATION
</IfModule>
+ <IfModule mod_proxy_fcgi.c>
+ SetEnvIfNoCase Authorization "(.+)" HTTP_AUTHORIZATION=$1
+ </IfModule>
</IfModule>
<IfModule mod_env.c>
diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php
index db1cdeb23b9..65ceba21454 100644
--- a/apps/files_external/controller/storagescontroller.php
+++ b/apps/files_external/controller/storagescontroller.php
@@ -255,8 +255,9 @@ abstract class StoragesController extends Controller {
)
);
} catch (InsufficientDataForMeaningfulAnswerException $e) {
+ $status = $e->getCode() ? $e->getCode() : StorageNotAvailableException::STATUS_INDETERMINATE;
$storage->setStatus(
- StorageNotAvailableException::STATUS_INDETERMINATE,
+ $status,
$this->l10n->t('Insufficient data: %s', [$e->getMessage()])
);
} catch (StorageNotAvailableException $e) {
diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js
index 27635d2f1df..33d2ea104be 100644
--- a/apps/files_external/js/statusmanager.js
+++ b/apps/files_external/js/statusmanager.js
@@ -22,15 +22,15 @@ if (!OCA.External.StatusManager) {
OCA.External.StatusManager = {
- mountStatus : null,
- mountPointList : null,
+ mountStatus: null,
+ mountPointList: null,
/**
* Function
* @param {callback} afterCallback
*/
- getMountStatus : function(afterCallback) {
+ getMountStatus: function (afterCallback) {
var self = this;
if (typeof afterCallback !== 'function' || self.isGetMountStatusRunning) {
return;
@@ -46,9 +46,9 @@ OCA.External.StatusManager = {
* @param {string} mount_point
*/
- getMountPointListElement : function(mount_point) {
+ getMountPointListElement: function (mount_point) {
var element;
- $.each(this.mountPointList, function(key, value){
+ $.each(this.mountPointList, function (key, value) {
if (value.mount_point === mount_point) {
element = value;
return false;
@@ -63,7 +63,7 @@ OCA.External.StatusManager = {
* @param {string} mount_point
*/
- getMountStatusForMount : function(mountData, afterCallback) {
+ getMountStatusForMount: function (mountData, afterCallback) {
var self = this;
if (typeof afterCallback !== 'function' || self.isGetMountStatusRunning) {
return $.Deferred().resolve();
@@ -72,41 +72,46 @@ OCA.External.StatusManager = {
var defObj;
if (self.mountStatus[mountData.mount_point]) {
defObj = $.Deferred();
- afterCallback(mountData, self.mountStatus[mountData.mount_point]);
+ afterCallback(mountData, self.mountStatus[mountData.mount_point]);
defObj.resolve(); // not really useful, but it'll keep the same behaviour
} else {
defObj = $.ajax({
- type : 'GET',
+ type: 'GET',
url: OC.webroot + '/index.php/apps/files_external/' + ((mountData.type === 'personal') ? 'userstorages' : 'userglobalstorages') + '/' + mountData.id,
- success : function(response) {
+ success: function (response) {
if (response && response.status === 0) {
self.mountStatus[mountData.mount_point] = response;
} else {
- if (response && response.statusMessage) {
- // failure response with error message
- self.mountStatus[mountData.mount_point] = { type: mountData.type,
- status: 1,
- error: response.statusMessage};
- } else {
- self.mountStatus[mountData.mount_point] = { type: mountData.type,
- status: 1,
- error: t('files_external', 'Empty response from the server')};
- }
+ var statusCode = response.status ? response.status : 1;
+ var statusMessage = response.statusMessage ? response.statusMessage : t('files_external', 'Empty response from the server')
+ // failure response with error message
+ self.mountStatus[mountData.mount_point] = {
+ type: mountData.type,
+ status: statusCode,
+ id: mountData.id,
+ error: statusMessage,
+ userProvided: response.userProvided
+ };
}
afterCallback(mountData, self.mountStatus[mountData.mount_point]);
},
- error : function(jqxhr, state, error) {
+ error: function (jqxhr, state, error) {
var message;
- if(mountData.location === 3){
+ if (mountData.location === 3) {
// In this case the error is because mount point use Login credentials and don't exist in the session
message = t('files_external', 'Couldn\'t access. Please logout and login to activate this mount point');
} else {
- message = t('files_external', 'Couldn\'t get the information from the ownCloud server: {code} {type}', {code: jqxhr.status, type: error});
+ message = t('files_external', 'Couldn\'t get the information from the ownCloud server: {code} {type}', {
+ code: jqxhr.status,
+ type: error
+ });
}
- self.mountStatus[mountData.mount_point] = { type: mountData.type,
- status: 1,
- location: mountData.location,
- error: message};
+ self.mountStatus[mountData.mount_point] = {
+ type: mountData.type,
+ status: 1,
+ location: mountData.location,
+ error: message
+ };
afterCallback(mountData, self.mountStatus[mountData.mount_point]);
}
});
@@ -119,7 +124,7 @@ OCA.External.StatusManager = {
* @param {function} afterCallback function to be executed
*/
- getMountPointList : function(afterCallback) {
+ getMountPointList: function (afterCallback) {
var self = this;
if (typeof afterCallback !== 'function' || self.isGetMountPointListRunning) {
return;
@@ -130,11 +135,11 @@ OCA.External.StatusManager = {
} else {
self.isGetMountPointListRunning = true;
$.ajax({
- type : 'GET',
- url : OC.linkToOCS('apps/files_external/api/v1') + 'mounts?format=json',
- success : function(response) {
+ type: 'GET',
+ url: OC.linkToOCS('apps/files_external/api/v1') + 'mounts?format=json',
+ success: function (response) {
self.mountPointList = [];
- _.each(response.ocs.data, function(mount){
+ _.each(response.ocs.data, function (mount) {
var element = {};
element.mount_point = mount.name;
element.type = mount.scope;
@@ -147,11 +152,11 @@ OCA.External.StatusManager = {
});
afterCallback(self.mountPointList);
},
- error : function(jqxhr, state, error) {
+ error: function (jqxhr, state, error) {
self.mountPointList = [];
- OC.Notification.showTemporary(t('files_external', 'Couldn\'t get the list of external mount points: {type}', {type : error}));
+ OC.Notification.showTemporary(t('files_external', 'Couldn\'t get the list of external mount points: {type}', {type: error}));
},
- complete : function() {
+ complete: function () {
self.isGetMountPointListRunning = false;
}
});
@@ -163,21 +168,25 @@ OCA.External.StatusManager = {
* @param {string} name MountPoint Name
*/
- manageMountPointError : function(name) {
- var self = this;
- this.getMountStatus($.proxy(function(allMountStatus) {
- if (typeof allMountStatus[name] !== 'undefined' || allMountStatus[name].status === 1) {
+ manageMountPointError: function (name) {
+ this.getMountStatus($.proxy(function (allMountStatus) {
+ if (allMountStatus.hasOwnProperty(name) && allMountStatus[name].status > 0 && allMountStatus[name].status < 7) {
var mountData = allMountStatus[name];
if (mountData.type === "system") {
- OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in admin settings page?', t('files_external', 'External mount error'), function(e){
- if(e === true) {
- window.location.href = OC.generateUrl('/settings/admin#files_external');
- }
- });
+ if (mountData.userProvided) {
+ // personal mount whit credentials problems
+ this.showCredentialsDialog(name, mountData);
+ } else {
+ OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in admin settings page?', t('files_external', 'External mount error'), function (e) {
+ if (e === true) {
+ OC.redirect(OC.generateUrl('/settings/admin#files_external'));
+ }
+ });
+ }
} else {
- OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in personal settings page?', t('files_external', 'External mount error'), function(e){
- if(e === true) {
- window.location.href = OC.generateUrl('/settings/personal#external-storage');
+ OC.dialogs.confirm(t('files_external', 'There was an error with message: ') + mountData.error + '. Do you want to review mount point config in personal settings page?', t('files_external', 'External mount error'), function (e) {
+ if (e === true) {
+ OC.redirect(OC.generateUrl('/settings/personal#' + t('files_external', 'external-storage')));
}
});
}
@@ -191,13 +200,13 @@ OCA.External.StatusManager = {
* @param {object} mountStatus
*/
- processMountStatusIndividual : function(mountData, mountStatus) {
+ processMountStatusIndividual: function (mountData, mountStatus) {
var mountPoint = mountData.mount_point;
- if (mountStatus.status === 1) {
+ if (mountStatus.status > 0) {
var trElement = FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(mountPoint));
- route = OCA.External.StatusManager.Utils.getIconRoute(trElement) + '-error';
+ var route = OCA.External.StatusManager.Utils.getIconRoute(trElement) + '-error';
if (OCA.External.StatusManager.Utils.isCorrectViewAndRootFolder()) {
OCA.External.StatusManager.Utils.showIconError(mountPoint, $.proxy(OCA.External.StatusManager.manageMountPointError, OCA.External.StatusManager), route);
@@ -218,9 +227,9 @@ OCA.External.StatusManager = {
* @param {object} mountStatus
*/
- processMountList : function(mountList) {
+ processMountList: function (mountList) {
var elementList = null;
- $.each(mountList, function(name, value){
+ $.each(mountList, function (name, value) {
var trElement = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(value.mount_point));
trElement.attr('data-external-backend', value.backend);
if (elementList) {
@@ -247,9 +256,9 @@ OCA.External.StatusManager = {
* Function to process the whole mount point list in relation with their status (Async queue)
*/
- launchFullConnectivityCheckOneByOne : function() {
+ launchFullConnectivityCheckOneByOne: function () {
var self = this;
- this.getMountPointList(function(list){
+ this.getMountPointList(function (list) {
// check if we have a list first
if (list === undefined && !self.emptyWarningShown) {
self.emptyWarningShown = true;
@@ -264,17 +273,19 @@ OCA.External.StatusManager = {
}
var ajaxQueue = [];
- $.each(list, function(key, value){
- var queueElement = {funcName: $.proxy(self.getMountStatusForMount, self),
- funcArgs: [value,
- $.proxy(self.processMountStatusIndividual, self)]};
+ $.each(list, function (key, value) {
+ var queueElement = {
+ funcName: $.proxy(self.getMountStatusForMount, self),
+ funcArgs: [value,
+ $.proxy(self.processMountStatusIndividual, self)]
+ };
ajaxQueue.push(queueElement);
});
- var rolQueue = new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4, function(){
+ var rolQueue = new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4, function () {
if (!self.notificationHasShown) {
var showNotification = false;
- $.each(self.mountStatus, function(key, value){
+ $.each(self.mountStatus, function (key, value) {
if (value.status === 1) {
self.notificationHasShown = true;
showNotification = true;
@@ -297,20 +308,22 @@ OCA.External.StatusManager = {
* @param {boolean} recheck delete cached info and force api call to check mount point status
*/
- launchPartialConnectivityCheck : function(mountListData, recheck) {
+ launchPartialConnectivityCheck: function (mountListData, recheck) {
if (mountListData.length === 0) {
return;
}
var self = this;
var ajaxQueue = [];
- $.each(mountListData, function(key, value){
+ $.each(mountListData, function (key, value) {
if (recheck && value.mount_point in self.mountStatus) {
delete self.mountStatus[value.mount_point];
}
- var queueElement = {funcName: $.proxy(self.getMountStatusForMount, self),
- funcArgs: [value,
- $.proxy(self.processMountStatusIndividual, self)]};
+ var queueElement = {
+ funcName: $.proxy(self.getMountStatusForMount, self),
+ funcArgs: [value,
+ $.proxy(self.processMountStatusIndividual, self)]
+ };
ajaxQueue.push(queueElement);
});
new OCA.External.StatusManager.RollingQueue(ajaxQueue, 4).runQueue();
@@ -323,21 +336,19 @@ OCA.External.StatusManager = {
* @param {boolean} recheck delete cached info and force api call to check mount point status
*/
- recheckConnectivityForMount : function(mountListNames, recheck) {
+ recheckConnectivityForMount: function (mountListNames, recheck) {
if (mountListNames.length === 0) {
return;
}
var self = this;
var mountListData = [];
- var recheckPersonalGlobal = false;
- var recheckAdminGlobal = false;
if (!self.mountStatus) {
self.mountStatus = {};
}
- $.each(mountListNames, function(key, value){
+ $.each(mountListNames, function (key, value) {
var mountData = self.getMountPointListElement(value);
if (mountData) {
mountListData.push(mountData);
@@ -346,7 +357,7 @@ OCA.External.StatusManager = {
// for all mounts in the list, delete the cached status values
if (recheck) {
- $.each(mountListData, function(key, value){
+ $.each(mountListData, function (key, value) {
if (value.mount_point in self.mountStatus) {
delete self.mountStatus[value.mount_point];
}
@@ -355,12 +366,96 @@ OCA.External.StatusManager = {
self.processMountList(mountListData);
self.launchPartialConnectivityCheck(mountListData, recheck);
+ },
+
+ credentialsDialogTemplate:
+ '<div id="files_external_div_form"><div>' +
+ '<div>{{credentials_text}}</div>' +
+ '<form>' +
+ '<input type="text" name="username" placeholder="{{placeholder_username}}"/>' +
+ '<input type="password" name="password" placeholder="{{placeholder_password}}"/>' +
+ '</form>' +
+ '</div></div>',
+
+ /**
+ * Function to display custom dialog to enter credentials
+ * @param mountPoint
+ * @param mountData
+ */
+ showCredentialsDialog: function (mountPoint, mountData) {
+ var template = Handlebars.compile(OCA.External.StatusManager.credentialsDialogTemplate);
+ var dialog = $(template({
+ credentials_text: t('files_external', 'Please enter the credentials for the {mount} mount', {
+ 'mount': mountPoint
+ }),
+ placeholder_username: t('files_external', 'Username'),
+ placeholder_password: t('files_external', 'Password')
+ }));
+
+ $('body').append(dialog);
+
+ var apply = function () {
+ var username = dialog.find('[name=username]').val();
+ var password = dialog.find('[name=password]').val();
+ var endpoint = OC.generateUrl('apps/files_external/userglobalstorages/{id}', {
+ id: mountData.id
+ });
+ $('.oc-dialog-close').hide();
+ $.ajax({
+ type: 'PUT',
+ url: endpoint,
+ data: {
+ backendOptions: {
+ user: username,
+ password: password
+ }
+ },
+ success: function (data) {
+ OC.Notification.showTemporary(t('files_external', 'Credentials saved'));
+ dialog.ocdialog('close');
+ /* Trigger status check again */
+ OCA.External.StatusManager.recheckConnectivityForMount([OC.basename(data.mountPoint)], true);
+ },
+ error: function () {
+ $('.oc-dialog-close').show();
+ OC.Notification.showTemporary(t('files_external', 'Credentials saving failed'));
+ }
+ });
+ return false;
+ };
+
+ var ocdialogParams = {
+ modal: true,
+ title: t('files_external', 'Credentials required'),
+ buttons: [{
+ text: t('files_external', 'Save'),
+ click: apply,
+ closeOnEscape: true
+ }],
+ closeOnExcape: true
+ };
+
+ dialog.ocdialog(ocdialogParams)
+ .bind('ocdialogclose', function () {
+ dialog.ocdialog('destroy').remove();
+ });
+
+ dialog.find('form').on('submit', apply);
+ dialog.find('form input:first').focus();
+ dialog.find('form input').keyup(function (e) {
+ if ((e.which && e.which === 13) || (e.keyCode && e.keyCode === 13)) {
+ $(e.target).closest('form').submit();
+ return false;
+ } else {
+ return true;
+ }
+ });
}
};
OCA.External.StatusManager.Utils = {
- showIconError: function(folder, clickAction, errorImageUrl) {
+ showIconError: function (folder, clickAction, errorImageUrl) {
var imageUrl = "url(" + errorImageUrl + ")";
var trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder));
this.changeFolderIcon(folder, imageUrl);
@@ -371,14 +466,14 @@ OCA.External.StatusManager.Utils = {
/**
* @param folder string with the folder or jQuery element pointing to the tr element
*/
- storeDefaultFolderIconAndBgcolor: function(folder) {
+ storeDefaultFolderIconAndBgcolor: function (folder) {
var trFolder;
if (folder instanceof $) {
trFolder = folder;
} else {
trFolder = $('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]'); //FileList.findFileEl(OCA.External.StatusManager.Utils.jqSelEscape(folder)); //$('#fileList tr[data-file=\"' + OCA.External.StatusManager.Utils.jqSelEscape(folder) + '\"]');
}
- trFolder.each(function(){
+ trFolder.each(function () {
var thisElement = $(this);
if (thisElement.data('oldbgcolor') === undefined) {
thisElement.data('oldbgcolor', thisElement.css('background-color'));
@@ -386,7 +481,7 @@ OCA.External.StatusManager.Utils = {
});
var icon = trFolder.find('td:first-child div.thumbnail');
- icon.each(function(){
+ icon.each(function () {
var thisElement = $(this);
if (thisElement.data('oldImage') === undefined) {
thisElement.data('oldImage', thisElement.css('background-image'));
@@ -397,7 +492,7 @@ OCA.External.StatusManager.Utils = {
/**
* @param folder string with the folder or jQuery element pointing to the tr element
*/
- restoreFolder: function(folder) {
+ restoreFolder: function (folder) {
var trFolder;
if (folder instanceof $) {
trFolder = folder;
@@ -407,7 +502,7 @@ OCA.External.StatusManager.Utils = {
}
trFolder.removeClass('externalErroredRow').removeClass('externalDisabledRow');
tdChilds = trFolder.find("td:first-child div.thumbnail");
- tdChilds.each(function(){
+ tdChilds.each(function () {
var thisElement = $(this);
thisElement.css('background-image', thisElement.data('oldImage'));
});
@@ -417,12 +512,12 @@ OCA.External.StatusManager.Utils = {
* @param folder string with the folder or jQuery element pointing to the first td element
* of the tr matching the folder name
*/
- changeFolderIcon: function(filename) {
+ changeFolderIcon: function (filename) {
var file;
var route;
if (filename instanceof $) {
//trElementList
- $.each(filename, function(index){
+ $.each(filename, function (index) {
route = OCA.External.StatusManager.Utils.getIconRoute($(this));
$(this).attr("data-icon", route);
$(this).find('td:first-child div.thumbnail').css('background-image', "url(" + route + ")").css('display', 'none').css('display', 'inline');
@@ -440,7 +535,7 @@ OCA.External.StatusManager.Utils = {
* @param backend string with the name of the external storage backend
* of the tr matching the folder name
*/
- getIconRoute: function(tr) {
+ getIconRoute: function (tr) {
var icon = OC.imagePath('core', 'filetypes/folder-external');
var backend = null;
@@ -460,7 +555,7 @@ OCA.External.StatusManager.Utils = {
return icon;
},
- toggleLink: function(filename, active, action) {
+ toggleLink: function (filename, active, action) {
var link;
if (filename instanceof $) {
link = filename;
@@ -473,7 +568,7 @@ OCA.External.StatusManager.Utils = {
} else {
link.find('.fileactions, .nametext .action').remove(); // from files/js/fileactions (display)
link.off('click.connectivity');
- link.on('click.connectivity', function(e){
+ link.on('click.connectivity', function (e) {
if (action && $.isFunction(action)) {
action(filename);
}
@@ -483,7 +578,7 @@ OCA.External.StatusManager.Utils = {
}
},
- isCorrectViewAndRootFolder: function() {
+ isCorrectViewAndRootFolder: function () {
// correct views = files & extstoragemounts
if (OCA.Files.App.getActiveView() === 'files' || OCA.Files.App.getActiveView() === 'extstoragemounts') {
return OCA.Files.App.getCurrentAppContainer().find('#dir').val() === '/';
@@ -492,15 +587,15 @@ OCA.External.StatusManager.Utils = {
},
/* escape a selector expression for jQuery */
- jqSelEscape: function(expression) {
- if(expression){
+ jqSelEscape: function (expression) {
+ if (expression) {
return expression.replace(/[!"#$%&'()*+,.\/:;<=>?@\[\\\]^`{|}~]/g, '\\$&');
}
return null;
},
/* Copied from http://stackoverflow.com/questions/2631001/javascript-test-for-existence-of-nested-object-key */
- checkNested: function(cobj /*, level1, level2, ... levelN*/) {
+ checkNested: function (cobj /*, level1, level2, ... levelN*/) {
var args = Array.prototype.slice.call(arguments),
obj = args.shift();
diff --git a/apps/files_external/lib/auth/password/globalauth.php b/apps/files_external/lib/auth/password/globalauth.php
index dcfea65b555..b1e52fb53ab 100644
--- a/apps/files_external/lib/auth/password/globalauth.php
+++ b/apps/files_external/lib/auth/password/globalauth.php
@@ -55,7 +55,10 @@ class GlobalAuth extends AuthMechanism {
public function getAuth($uid) {
$auth = $this->credentialsManager->retrieve($uid, self::CREDENTIALS_IDENTIFIER);
if (!is_array($auth)) {
- return [];
+ return [
+ 'user' => '',
+ 'password' => ''
+ ];
} else {
return $auth;
}
diff --git a/apps/files_external/lib/config/configadapter.php b/apps/files_external/lib/config/configadapter.php
index 2bf39bcaa4f..51c2debd726 100644
--- a/apps/files_external/lib/config/configadapter.php
+++ b/apps/files_external/lib/config/configadapter.php
@@ -130,6 +130,16 @@ class ConfigAdapter implements IMountProvider {
$impl = new FailedStorage(['exception' => $e]);
}
+ try {
+ $availability = $impl->getAvailability();
+ if (!$availability['available']) {
+ $impl = new FailedStorage(['exception' => null]);
+ }
+ } catch (\Exception $e) {
+ // propagate exception into filesystem
+ $impl = new FailedStorage(['exception' => $e]);
+ }
+
$mount = new MountPoint(
$impl,
'/' . $user->getUID() . '/files' . $storage->getMountPoint(),
diff --git a/apps/files_external/lib/failedcache.php b/apps/files_external/lib/failedcache.php
index f9866f43058..0f59495e595 100644
--- a/apps/files_external/lib/failedcache.php
+++ b/apps/files_external/lib/failedcache.php
@@ -22,6 +22,7 @@
namespace OCA\Files_External\Lib;
use OC\Files\Cache\CacheEntry;
+use OCP\Constants;
use OCP\Files\Cache\ICache;
/**
@@ -40,7 +41,7 @@ class FailedCache implements ICache {
'size' => 0,
'mimetype' => 'httpd/unix-directory',
'mimepart' => 'httpd',
- 'permissions' => 0,
+ 'permissions' => Constants::PERMISSION_READ,
'mtime' => time()
]);
} else {
diff --git a/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php
index 871301b9b51..22d83ef56f4 100644
--- a/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php
+++ b/apps/files_external/lib/insufficientdataformeaningfulanswerexception.php
@@ -27,4 +27,15 @@ use \OCP\Files\StorageNotAvailableException;
* Authentication mechanism or backend has insufficient data
*/
class InsufficientDataForMeaningfulAnswerException extends StorageNotAvailableException {
+ /**
+ * StorageNotAvailableException constructor.
+ *
+ * @param string $message
+ * @param int $code
+ * @param \Exception $previous
+ * @since 6.0.0
+ */
+ public function __construct($message = '', $code = self::STATUS_INDETERMINATE, \Exception $previous = null) {
+ parent::__construct($message, $code, $previous);
+ }
}
diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php
index 9da21dc88e6..50bd56f28ad 100644
--- a/apps/files_external/lib/smb.php
+++ b/apps/files_external/lib/smb.php
@@ -314,4 +314,17 @@ class SMB extends Common {
|| Server::NativeAvailable()
) ? true : ['smbclient'];
}
+
+ /**
+ * Test a storage for availability
+ *
+ * @return bool
+ */
+ public function test() {
+ try {
+ return parent::test();
+ } catch (Exception $e) {
+ return false;
+ }
+ }
}
diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php
index 7f716893842..6f44b25a2e6 100644
--- a/apps/files_external/lib/storageconfig.php
+++ b/apps/files_external/lib/storageconfig.php
@@ -24,6 +24,7 @@
namespace OCA\Files_external\Lib;
+use OCA\Files_External\Lib\Auth\IUserProvided;
use \OCA\Files_External\Lib\Backend\Backend;
use \OCA\Files_External\Lib\Auth\AuthMechanism;
@@ -406,6 +407,7 @@ class StorageConfig implements \JsonSerializable {
if (!is_null($this->statusMessage)) {
$result['statusMessage'] = $this->statusMessage;
}
+ $result['userProvided'] = $this->authMechanism instanceof IUserProvided;
$result['type'] = ($this->getType() === self::MOUNT_TYPE_PERSONAl) ? 'personal': 'system';
return $result;
}
diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature
index bdc1a4224d8..1a1a5c1981a 100644
--- a/build/integration/features/sharing-v1.feature
+++ b/build/integration/features/sharing-v1.feature
@@ -480,13 +480,18 @@ Feature: sharing
Then the OCS status code should be "100"
And the HTTP status code should be "200"
-
-
-
-
-
-
-
-
-
-
+ Scenario: Keep usergroup shares (#22143)
+ Given As an "admin"
+ And user "user0" exists
+ And user "user1" exists
+ And user "user2" exists
+ And group "group" exists
+ And user "user1" belongs to group "group"
+ And user "user2" belongs to group "group"
+ And user "user0" created a folder "/TMP"
+ And file "TMP" of user "user0" is shared with group "group"
+ And user "user1" created a folder "/myFOLDER"
+ And User "user1" moves file "/TMP" to "/myFOLDER/myTMP"
+ And user "user2" does not exist
+ And user "user1" should see following elements
+ | /myFOLDER/myTMP/ |
diff --git a/console.php b/console.php
index 2073654fa8d..d08d400c051 100644
--- a/console.php
+++ b/console.php
@@ -80,7 +80,7 @@ try {
echo "The process control (PCNTL) extensions are required in case you want to interrupt long running commands - see http://php.net/manual/en/book.pcntl.php" . PHP_EOL;
}
- $application = new Application(\OC::$server->getConfig());
+ $application = new Application(\OC::$server->getConfig(), \OC::$server->getEventDispatcher(), \OC::$server->getRequest());
$application->loadCommands(new ConsoleOutput());
$application->run();
} catch (Exception $ex) {
diff --git a/lib/private/console/application.php b/lib/private/console/application.php
index c7d9c24d7cb..10ff69b1c80 100644
--- a/lib/private/console/application.php
+++ b/lib/private/console/application.php
@@ -25,25 +25,34 @@ namespace OC\Console;
use OC_App;
use OC_Defaults;
+use OCP\Console\ConsoleEvent;
use OCP\IConfig;
+use OCP\IRequest;
use Symfony\Component\Console\Application as SymfonyApplication;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class Application {
- /**
- * @var IConfig
- */
+ /** @var IConfig */
private $config;
+ /** @var EventDispatcherInterface */
+ private $dispatcher;
+ /** @var IRequest */
+ private $request;
/**
* @param IConfig $config
+ * @param EventDispatcherInterface $dispatcher
+ * @param IRequest $request
*/
- public function __construct(IConfig $config) {
+ public function __construct(IConfig $config, EventDispatcherInterface $dispatcher, IRequest $request) {
$defaults = new OC_Defaults;
$this->config = $config;
$this->application = new SymfonyApplication($defaults->getName(), \OC_Util::getVersionString());
+ $this->dispatcher = $dispatcher;
+ $this->request = $request;
}
/**
@@ -107,6 +116,10 @@ class Application {
* @throws \Exception
*/
public function run(InputInterface $input = null, OutputInterface $output = null) {
+ $this->dispatcher->dispatch(ConsoleEvent::EVENT_RUN, new ConsoleEvent(
+ ConsoleEvent::EVENT_RUN,
+ $this->request->server['argv']
+ ));
return $this->application->run($input, $output);
}
}
diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php
index 495ccb902d6..bcabb6fe57a 100644
--- a/lib/private/db/mdb2schemamanager.php
+++ b/lib/private/db/mdb2schemamanager.php
@@ -49,7 +49,6 @@ class MDB2SchemaManager {
/**
* saves database scheme to xml file
* @param string $file name of file
- * @param int|string $mode
* @return bool
*
* TODO: write more documentation
@@ -123,7 +122,7 @@ class MDB2SchemaManager {
/**
* update the database scheme
* @param string $file file to read structure from
- * @return string|boolean
+ * @return boolean
*/
public function simulateUpdateDbFromStructure($file) {
$toSchema = $this->readSchemaFromFile($file);
diff --git a/lib/private/diagnostics/querylogger.php b/lib/private/diagnostics/querylogger.php
index 794e7a5e263..66a65b71d04 100644
--- a/lib/private/diagnostics/querylogger.php
+++ b/lib/private/diagnostics/querylogger.php
@@ -53,7 +53,7 @@ class QueryLogger implements IQueryLogger {
}
/**
- * @return \OCP\Diagnostics\IQuery[]
+ * @return Query[]
*/
public function getQueries() {
return $this->queries;
diff --git a/lib/private/files.php b/lib/private/files.php
index 7b451ac19be..a18bcc76519 100644
--- a/lib/private/files.php
+++ b/lib/private/files.php
@@ -160,6 +160,8 @@ class OC_Files {
/**
* @param View $view
* @param string $name
+ * @param string $dir
+ * @param boolean $onlyHeader
*/
private static function getSingleFile($view, $dir, $name, $onlyHeader) {
$filename = $dir . '/' . $name;
@@ -185,7 +187,7 @@ class OC_Files {
/**
* @param View $view
- * @param $dir
+ * @param string $dir
* @param string[]|string $files
*/
public static function lockFiles($view, $dir, $files) {
@@ -290,11 +292,11 @@ class OC_Files {
}
/**
- * @param $dir
+ * @param string $dir
* @param $files
- * @param $getType
+ * @param integer $getType
* @param View $view
- * @param $filename
+ * @param string $filename
*/
private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) {
if ($getType === self::FILE) {
diff --git a/lib/private/files/node/root.php b/lib/private/files/node/root.php
index 35163be0a0d..40ed531d5df 100644
--- a/lib/private/files/node/root.php
+++ b/lib/private/files/node/root.php
@@ -169,7 +169,7 @@ class Root extends Folder implements IRootFolder {
* @param string $path
* @throws \OCP\Files\NotFoundException
* @throws \OCP\Files\NotPermittedException
- * @return \OCP\Files\Node
+ * @return string
*/
public function get($path) {
$path = $this->normalizePath($path);
diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php
index 95bb3f74ba7..7d8d9ebd25e 100644
--- a/lib/private/files/storage/common.php
+++ b/lib/private/files/storage/common.php
@@ -396,7 +396,7 @@ abstract class Common implements Storage, ILockingStorage {
* get the ETag for a file or folder
*
* @param string $path
- * @return string|false
+ * @return string
*/
public function getETag($path) {
return uniqid();
diff --git a/lib/private/files/stream/dir.php b/lib/private/files/stream/dir.php
index fabadb0d596..7489ee683a2 100644
--- a/lib/private/files/stream/dir.php
+++ b/lib/private/files/stream/dir.php
@@ -58,6 +58,7 @@ class Dir {
/**
* @param string $path
+ * @param string[] $content
*/
public static function register($path, $content) {
self::$dirs[$path] = $content;
diff --git a/lib/private/files/type/detection.php b/lib/private/files/type/detection.php
index 9cc2e97c3cc..f106a98064f 100644
--- a/lib/private/files/type/detection.php
+++ b/lib/private/files/type/detection.php
@@ -121,7 +121,7 @@ class Detection implements IMimeTypeDetector {
}
/**
- * @return array
+ * @return string[]
*/
public function getAllAliases() {
$this->loadAliases();
@@ -264,7 +264,7 @@ class Detection implements IMimeTypeDetector {
/**
* Get path to the icon of a file type
- * @param string $mimeType the MIME type
+ * @param string $mimetype the MIME type
* @return string the url
*/
public function mimeTypeIcon($mimetype) {
diff --git a/lib/private/share/hooks.php b/lib/private/share/hooks.php
index 1fa233916d1..c939164e39e 100644
--- a/lib/private/share/hooks.php
+++ b/lib/private/share/hooks.php
@@ -38,7 +38,7 @@ class Hooks extends \OC\Share\Constants {
public static function post_deleteUser($arguments) {
// Delete any items shared with the deleted user
$query = \OC_DB::prepare('DELETE FROM `*PREFIX*share`'
- .' WHERE `share_with` = ? AND `share_type` = ? OR `share_type` = ?');
+ .' WHERE `share_with` = ? AND (`share_type` = ? OR `share_type` = ?)');
$query->execute(array($arguments['uid'], self::SHARE_TYPE_USER, self::$shareTypeGroupUserUnique));
// Delete any items the deleted user shared
$query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*share` WHERE `uid_owner` = ?');
diff --git a/lib/private/share20/manager.php b/lib/private/share20/manager.php
index 6ea638b84e6..d65fb927f9b 100644
--- a/lib/private/share20/manager.php
+++ b/lib/private/share20/manager.php
@@ -77,6 +77,7 @@ class Manager implements IManager {
* @param IL10N $l
* @param IProviderFactory $factory
* @param IUserManager $userManager
+ * @param IRootFolder $rootFolder
*/
public function __construct(
ILogger $logger,
@@ -613,6 +614,19 @@ class Manager implements IManager {
]);
}
+ if ($share->getPermissions() !== $originalShare->getPermissions()) {
+ $userFolder = $this->rootFolder->getUserFolder($share->getShareOwner());
+ \OC_Hook::emit('OCP\Share', 'post_update_permissions', array(
+ 'itemType' => $share->getNode() instanceof \OCP\Files\File ? 'file' : 'folder',
+ 'itemSource' => $share->getNode()->getId(),
+ 'shareType' => $share->getShareType(),
+ 'shareWith' => $share->getSharedWith(),
+ 'uidOwner' => $share->getSharedBy(),
+ 'permissions' => $share->getPermissions(),
+ 'path' => $userFolder->getRelativePath($share->getNode()->getPath()),
+ ));
+ }
+
return $share;
}
diff --git a/lib/private/share20/share.php b/lib/private/share20/share.php
index 448b05d20a3..cd30f24c42e 100644
--- a/lib/private/share20/share.php
+++ b/lib/private/share20/share.php
@@ -105,6 +105,8 @@ class Share implements \OCP\Share\IShare {
* @inheritdoc
*/
public function setNode(Node $node) {
+ $this->fileId = null;
+ $this->nodeType = null;
$this->node = $node;
return $this;
}
diff --git a/lib/public/console/consoleevent.php b/lib/public/console/consoleevent.php
new file mode 100644
index 00000000000..b3f1229f0e8
--- /dev/null
+++ b/lib/public/console/consoleevent.php
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCP\Console;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Class ConsoleEvent
+ *
+ * @package OCP\Console
+ * @since 9.0.0
+ */
+class ConsoleEvent extends Event {
+
+ const EVENT_RUN = 'OC\Console\Application::run';
+
+ /** @var string */
+ protected $event;
+
+ /** @var string[] */
+ protected $arguments;
+
+ /**
+ * DispatcherEvent constructor.
+ *
+ * @param string $event
+ * @param string[] $arguments
+ * @since 9.0.0
+ */
+ public function __construct($event, array $arguments) {
+ $this->event = $event;
+ $this->arguments = $arguments;
+ }
+
+ /**
+ * @return string
+ * @since 9.0.0
+ */
+ public function getEvent() {
+ return $this->event;
+ }
+
+ /**
+ * @return string[]
+ * @since 9.0.0
+ */
+ public function getArguments() {
+ return $this->arguments;
+ }
+}
diff --git a/settings/js/users/users.js b/settings/js/users/users.js
index 151ab6cdecc..306e3952e53 100644
--- a/settings/js/users/users.js
+++ b/settings/js/users/users.js
@@ -732,24 +732,20 @@ $(document).ready(function () {
.focus()
.keypress(function (event) {
if (event.keyCode === 13) {
- if ($(this).val().length > 0) {
- $tr.data('mailAddress', $input.val());
- $input.blur();
- $.ajax({
- type: 'PUT',
- url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: uid}),
- data: {
- mailAddress: $(this).val()
- }
- }).fail(function (result) {
- OC.Notification.show(result.responseJSON.data.message);
- // reset the values
- $tr.data('mailAddress', mailAddress);
- $tr.children('.mailAddress').children('span').text(mailAddress);
- });
- } else {
- $input.blur();
- }
+ $tr.data('mailAddress', $input.val());
+ $input.blur();
+ $.ajax({
+ type: 'PUT',
+ url: OC.generateUrl('/settings/users/{id}/mailAddress', {id: uid}),
+ data: {
+ mailAddress: $(this).val()
+ }
+ }).fail(function (result) {
+ OC.Notification.show(result.responseJSON.data.message);
+ // reset the values
+ $tr.data('mailAddress', mailAddress);
+ $tr.children('.mailAddress').children('span').text(mailAddress);
+ });
}
})
.blur(function () {
diff --git a/tests/lib/share20/managertest.php b/tests/lib/share20/managertest.php
index 270f5da33cd..131bc7fbfd2 100644
--- a/tests/lib/share20/managertest.php
+++ b/tests/lib/share20/managertest.php
@@ -1870,19 +1870,24 @@ class ManagerTest extends \Test\TestCase {
$originalShare = $this->manager->newShare();
$originalShare->setShareType(\OCP\Share::SHARE_TYPE_USER)
- ->setSharedWith('origUser');
+ ->setSharedWith('origUser')
+ ->setPermissions(1);
+
+ $node = $this->getMock('\OCP\Files\File');
+ $node->method('getId')->willReturn(100);
+ $node->method('getPath')->willReturn('/newUser/files/myPath');
$manager->expects($this->once())->method('canShare')->willReturn(true);
$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $node = $this->getMock('\OCP\Files\File');
-
$share = $this->manager->newShare();
$share->setProviderId('foo')
->setId('42')
->setShareType(\OCP\Share::SHARE_TYPE_USER)
->setSharedWith('origUser')
->setShareOwner('newUser')
+ ->setSharedBy('sharer')
+ ->setPermissions(31)
->setNode($node);
$this->defaultProvider->expects($this->once())
@@ -1894,6 +1899,20 @@ class ManagerTest extends \Test\TestCase {
\OCP\Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListner, 'post');
$hookListner->expects($this->never())->method('post');
+ $this->rootFolder->method('getUserFolder')->with('newUser')->will($this->returnSelf());
+ $this->rootFolder->method('getRelativePath')->with('/newUser/files/myPath')->willReturn('/myPath');
+
+ $hookListner2 = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock();
+ \OCP\Util::connectHook('OCP\Share', 'post_update_permissions', $hookListner2, 'post');
+ $hookListner2->expects($this->once())->method('post')->with([
+ 'itemType' => 'file',
+ 'itemSource' => 100,
+ 'shareType' => \OCP\Share::SHARE_TYPE_USER,
+ 'shareWith' => 'origUser',
+ 'uidOwner' => 'sharer',
+ 'permissions' => 31,
+ 'path' => '/myPath',
+ ]);
$manager->updateShare($share);
}
@@ -1911,7 +1930,8 @@ class ManagerTest extends \Test\TestCase {
$originalShare = $this->manager->newShare();
$originalShare->setShareType(\OCP\Share::SHARE_TYPE_GROUP)
- ->setSharedWith('origUser');
+ ->setSharedWith('origUser')
+ ->setPermissions(31);
$manager->expects($this->once())->method('canShare')->willReturn(true);
$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
@@ -1924,7 +1944,8 @@ class ManagerTest extends \Test\TestCase {
->setShareType(\OCP\Share::SHARE_TYPE_GROUP)
->setSharedWith('origUser')
->setShareOwner('owner')
- ->setNode($node);
+ ->setNode($node)
+ ->setPermissions(31);
$this->defaultProvider->expects($this->once())
->method('update')
@@ -1935,6 +1956,9 @@ class ManagerTest extends \Test\TestCase {
\OCP\Util::connectHook('OCP\Share', 'post_set_expiration_date', $hookListner, 'post');
$hookListner->expects($this->never())->method('post');
+ $hookListner2 = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock();
+ \OCP\Util::connectHook('OCP\Share', 'post_update_permissions', $hookListner2, 'post');
+ $hookListner2->expects($this->never())->method('post');
$manager->updateShare($share);
}
@@ -1953,7 +1977,8 @@ class ManagerTest extends \Test\TestCase {
->getMock();
$originalShare = $this->manager->newShare();
- $originalShare->setShareType(\OCP\Share::SHARE_TYPE_LINK);
+ $originalShare->setShareType(\OCP\Share::SHARE_TYPE_LINK)
+ ->setPermissions(15);
$tomorrow = new \DateTime();
$tomorrow->setTime(0,0,0);
@@ -1970,7 +1995,8 @@ class ManagerTest extends \Test\TestCase {
->setShareOwner('owner')
->setPassword('password')
->setExpirationDate($tomorrow)
- ->setNode($file);
+ ->setNode($file)
+ ->setPermissions(15);
$manager->expects($this->once())->method('canShare')->willReturn(true);
$manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
@@ -1990,6 +2016,10 @@ class ManagerTest extends \Test\TestCase {
'uidOwner' => 'owner',
]);
+ $hookListner2 = $this->getMockBuilder('Dummy')->setMethods(['post'])->getMock();
+ \OCP\Util::connectHook('OCP\Share', 'post_update_permissions', $hookListner2, 'post');
+ $hookListner2->expects($this->never())->method('post');
+
$manager->updateShare($share);
}