diff options
-rw-r--r-- | apps/federation/appinfo/app.php | 1 | ||||
-rw-r--r-- | apps/federation/appinfo/application.php | 20 | ||||
-rw-r--r-- | apps/federation/lib/dbhandler.php | 1 | ||||
-rw-r--r-- | apps/federation/lib/hooks.php | 50 | ||||
-rw-r--r-- | apps/federation/tests/lib/dbhandlertest.php | 22 | ||||
-rw-r--r-- | apps/federation/tests/lib/hookstest.php | 79 | ||||
-rw-r--r-- | apps/files_external/appinfo/routes.php | 1 | ||||
-rw-r--r-- | apps/files_external/controller/storagescontroller.php | 14 | ||||
-rw-r--r-- | apps/files_external/controller/userglobalstoragescontroller.php | 121 | ||||
-rw-r--r-- | apps/files_external/js/public_key.js | 14 | ||||
-rw-r--r-- | apps/files_external/js/settings.js | 282 | ||||
-rw-r--r-- | apps/files_external/personal.php | 26 | ||||
-rw-r--r-- | apps/files_external/settings.php | 31 | ||||
-rw-r--r-- | apps/files_external/templates/settings.php | 101 | ||||
-rw-r--r-- | apps/files_external/tests/js/settingsSpec.js | 7 | ||||
-rw-r--r-- | apps/files_sharing/js/public.js | 6 | ||||
-rw-r--r-- | apps/files_sharing/lib/external/manager.php | 2 | ||||
-rw-r--r-- | apps/user_ldap/lib/access.php | 106 | ||||
-rw-r--r-- | lib/private/share/share.php | 5 |
19 files changed, 624 insertions, 265 deletions
diff --git a/apps/federation/appinfo/app.php b/apps/federation/appinfo/app.php index 9ed00f23866..8cc77885d6f 100644 --- a/apps/federation/appinfo/app.php +++ b/apps/federation/appinfo/app.php @@ -23,3 +23,4 @@ namespace OCA\Federation\AppInfo; $app = new Application(); $app->registerSettings(); +$app->registerHooks(); diff --git a/apps/federation/appinfo/application.php b/apps/federation/appinfo/application.php index 350b140b4dd..172283536b4 100644 --- a/apps/federation/appinfo/application.php +++ b/apps/federation/appinfo/application.php @@ -22,14 +22,15 @@ namespace OCA\Federation\AppInfo; use OCA\Federation\API\OCSAuthAPI; -use OCA\Federation\Controller\AuthController; use OCA\Federation\Controller\SettingsController; use OCA\Federation\DbHandler; +use OCA\Federation\Hooks; use OCA\Federation\Middleware\AddServerMiddleware; use OCA\Federation\TrustedServers; use OCP\API; use OCP\App; use OCP\AppFramework\IAppContainer; +use OCP\Util; class Application extends \OCP\AppFramework\App { @@ -127,4 +128,21 @@ class Application extends \OCP\AppFramework\App { } + /** + * listen to federated_share_added hooks to auto-add new servers to the + * list of trusted servers. + */ + public function registerHooks() { + + $container = $this->getContainer(); + $hooksManager = new Hooks($container->query('TrustedServers')); + + Util::connectHook( + 'OCP\Share', + 'federated_share_added', + $hooksManager, + 'addServerHook' + ); + } + } diff --git a/apps/federation/lib/dbhandler.php b/apps/federation/lib/dbhandler.php index 61ba5c87cfd..7606593f780 100644 --- a/apps/federation/lib/dbhandler.php +++ b/apps/federation/lib/dbhandler.php @@ -68,6 +68,7 @@ class DbHandler { */ public function addServer($url) { $hash = $this->hash($url); + $url = rtrim($url, '/'); $query = $this->connection->getQueryBuilder(); $query->insert($this->dbTable) ->values( diff --git a/apps/federation/lib/hooks.php b/apps/federation/lib/hooks.php new file mode 100644 index 00000000000..4bf5be4e5b6 --- /dev/null +++ b/apps/federation/lib/hooks.php @@ -0,0 +1,50 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\Federation; + + + +class Hooks { + + /** @var TrustedServers */ + private $trustedServers; + + public function __construct(TrustedServers $trustedServers) { + $this->trustedServers = $trustedServers; + } + + /** + * add servers to the list of trusted servers once a federated share was established + * + * @param array $params + */ + public function addServerHook($params) { + if ( + $this->trustedServers->getAutoAddServers() === true && + $this->trustedServers->isTrustedServer($params['server']) === false + ) { + $this->trustedServers->addServer($params['server']); + } + } + +} diff --git a/apps/federation/tests/lib/dbhandlertest.php b/apps/federation/tests/lib/dbhandlertest.php index e47df092f8c..123eaaee450 100644 --- a/apps/federation/tests/lib/dbhandlertest.php +++ b/apps/federation/tests/lib/dbhandlertest.php @@ -67,17 +67,33 @@ class DbHandlerTest extends TestCase { $query->execute(); } - public function testAddServer() { - $id = $this->dbHandler->addServer('server1'); + /** + * @dataProvider dataTestAddServer + * + * @param string $url passed to the method + * @param string $expectedUrl the url we expect to be written to the db + * @param string $expectedHash the hash value we expect to be written to the db + */ + public function testAddServer($url, $expectedUrl, $expectedHash) { + $id = $this->dbHandler->addServer($url); $query = $this->connection->getQueryBuilder()->select('*')->from($this->dbTable); $result = $query->execute()->fetchAll(); $this->assertSame(1, count($result)); - $this->assertSame('server1', $result[0]['url']); + $this->assertSame($expectedUrl, $result[0]['url']); $this->assertSame($id, (int)$result[0]['id']); + $this->assertSame($expectedHash, $result[0]['url_hash']); $this->assertSame(TrustedServers::STATUS_PENDING, (int)$result[0]['status']); } + public function dataTestAddServer() { + return [ + ['http://owncloud.org', 'http://owncloud.org', md5('owncloud.org')], + ['https://owncloud.org', 'https://owncloud.org', md5('owncloud.org')], + ['http://owncloud.org/', 'http://owncloud.org', md5('owncloud.org')], + ]; + } + public function testRemove() { $id1 = $this->dbHandler->addServer('server1'); $id2 = $this->dbHandler->addServer('server2'); diff --git a/apps/federation/tests/lib/hookstest.php b/apps/federation/tests/lib/hookstest.php new file mode 100644 index 00000000000..5b19c167456 --- /dev/null +++ b/apps/federation/tests/lib/hookstest.php @@ -0,0 +1,79 @@ +<?php +/** + * @author Björn Schießle <schiessle@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + + +namespace OCA\Federation\Tests\lib; + + +use OCA\Federation\Hooks; +use OCA\Federation\TrustedServers; +use Test\TestCase; + +class HooksTest extends TestCase { + + /** @var \PHPUnit_Framework_MockObject_MockObject | TrustedServers */ + private $trustedServers; + + /** @var Hooks */ + private $hooks; + + public function setUp() { + parent::setUp(); + + $this->trustedServers = $this->getMockBuilder('OCA\Federation\TrustedServers') + ->disableOriginalConstructor()->getMock(); + + $this->hooks = new Hooks($this->trustedServers); + } + + /** + * @dataProvider dataTestAddServerHook + * + * @param bool $autoAddEnabled is auto-add enabled + * @param bool $isTrustedServer is the server already in the list of trusted servers + * @param bool $addServer should the server be added + */ + public function testAddServerHook($autoAddEnabled, $isTrustedServer, $addServer) { + $this->trustedServers->expects($this->any())->method('getAutoAddServers') + ->willReturn($autoAddEnabled); + $this->trustedServers->expects($this->any())->method('isTrustedServer') + ->with('url')->willReturn($isTrustedServer); + + if ($addServer) { + $this->trustedServers->expects($this->once())->method('addServer') + ->with('url'); + } else { + $this->trustedServers->expects($this->never())->method('addServer'); + } + + $this->hooks->addServerHook(['server' => 'url']); + + } + + public function dataTestAddServerHook() { + return [ + [true, true, false], + [false, true, false], + [true, false, true], + [false, false, false], + ]; + } +} diff --git a/apps/files_external/appinfo/routes.php b/apps/files_external/appinfo/routes.php index 39ded1dc2ec..e66c010a8cf 100644 --- a/apps/files_external/appinfo/routes.php +++ b/apps/files_external/appinfo/routes.php @@ -36,6 +36,7 @@ namespace OCA\Files_External\AppInfo; 'resources' => array( 'global_storages' => array('url' => '/globalstorages'), 'user_storages' => array('url' => '/userstorages'), + 'user_global_storages' => array('url' => '/userglobalstorages'), ), 'routes' => array( array( diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 048f3588ed7..c66bd902d8d 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -256,6 +256,20 @@ abstract class StoragesController extends Controller { } /** + * Get all storage entries + * + * @return DataResponse + */ + public function index() { + $storages = $this->service->getAllStorages(); + + return new DataResponse( + $storages, + Http::STATUS_OK + ); + } + + /** * Get an external storage entry. * * @param int $id storage id diff --git a/apps/files_external/controller/userglobalstoragescontroller.php b/apps/files_external/controller/userglobalstoragescontroller.php new file mode 100644 index 00000000000..c6f777763e8 --- /dev/null +++ b/apps/files_external/controller/userglobalstoragescontroller.php @@ -0,0 +1,121 @@ +<?php +/** + * @author Robin McCorkell <rmccorkell@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\Files_External\Controller; + +use \OCP\IRequest; +use \OCP\IL10N; +use \OCP\AppFramework\Http\DataResponse; +use \OCP\AppFramework\Controller; +use \OCP\AppFramework\Http; +use \OCA\Files_external\Service\UserGlobalStoragesService; +use \OCA\Files_external\NotFoundException; +use \OCA\Files_external\Lib\StorageConfig; +use \OCA\Files_External\Lib\Backend\Backend; + +/** + * User global storages controller + */ +class UserGlobalStoragesController extends StoragesController { + /** + * Creates a new user global storages controller. + * + * @param string $AppName application name + * @param IRequest $request request object + * @param IL10N $l10n l10n service + * @param UserGlobalStoragesService $userGlobalStoragesService storage service + */ + public function __construct( + $AppName, + IRequest $request, + IL10N $l10n, + UserGlobalStoragesService $userGlobalStoragesService + ) { + parent::__construct( + $AppName, + $request, + $l10n, + $userGlobalStoragesService + ); + } + + /** + * Get all storage entries + * + * @return DataResponse + * + * @NoAdminRequired + */ + public function index() { + $storages = $this->service->getUniqueStorages(); + + // remove configuration data, this must be kept private + foreach ($storages as $storage) { + $this->sanitizeStorage($storage); + } + + return new DataResponse( + $storages, + Http::STATUS_OK + ); + } + + /** + * Get an external storage entry. + * + * @param int $id storage id + * @return DataResponse + * + * @NoAdminRequired + */ + public function show($id) { + try { + $storage = $this->service->getStorage($id); + + $this->updateStorageStatus($storage); + } catch (NotFoundException $e) { + return new DataResponse( + [ + 'message' => (string)$this->l10n->t('Storage with id "%i" not found', array($id)) + ], + Http::STATUS_NOT_FOUND + ); + } + + $this->sanitizeStorage($storage); + + return new DataResponse( + $storage, + Http::STATUS_OK + ); + } + + /** + * Remove sensitive data from a StorageConfig before returning it to the user + * + * @param StorageConfig $storage + */ + protected function sanitizeStorage(StorageConfig $storage) { + $storage->setBackendOptions([]); + $storage->setMountOptions([]); + } + +} diff --git a/apps/files_external/js/public_key.js b/apps/files_external/js/public_key.js index a8546067452..5f9658381f0 100644 --- a/apps/files_external/js/public_key.js +++ b/apps/files_external/js/public_key.js @@ -1,10 +1,16 @@ $(document).ready(function() { - OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme) { + OCA.External.Settings.mountConfig.whenSelectAuthMechanism(function($tr, authMechanism, scheme, onCompletion) { if (scheme === 'publickey') { var config = $tr.find('.configuration'); if ($(config).find('[name="public_key_generate"]').length === 0) { setupTableRow($tr, config); + onCompletion.then(function() { + // If there's no private key, build one + if (0 === $(config).find('[data-parameter="private_key"]').val().length) { + generateKeys($tr); + } + }); } } }); @@ -22,10 +28,6 @@ $(document).ready(function() { .attr('value', t('files_external', 'Generate keys')) .attr('name', 'public_key_generate') ); - // If there's no private key, build one - if (0 === $(config).find('[data-parameter="private_key"]').val().length) { - generateKeys(tr); - } } function generateKeys(tr) { @@ -33,7 +35,7 @@ $(document).ready(function() { $.post(OC.filePath('files_external', 'ajax', 'public_key.php'), {}, function(result) { if (result && result.status === 'success') { - $(config).find('[data-parameter="public_key"]').val(result.data.public_key); + $(config).find('[data-parameter="public_key"]').val(result.data.public_key).keyup(); $(config).find('[data-parameter="private_key"]').val(result.data.private_key); OCA.External.Settings.mountConfig.saveStorageConfig(tr, function() { // Nothing to do diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index a839f396b9b..f712ecf4328 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -623,36 +623,7 @@ MountConfigListView.prototype = _.extend({ this._allBackends = this.$el.find('.selectBackend').data('configurations'); this._allAuthMechanisms = this.$el.find('#addMountPoint .authentication').data('mechanisms'); - //initialize hidden input field with list of users and groups - this.$el.find('tr:not(#addMountPoint)').each(function(i,tr) { - var $tr = $(tr); - var $applicable = $tr.find('.applicable'); - if ($applicable.length > 0) { - var groups = $applicable.data('applicable-groups'); - var groupsId = []; - $.each(groups, function () { - groupsId.push(this + '(group)'); - }); - var users = $applicable.data('applicable-users'); - if (users.indexOf('all') > -1 || users === '') { - $tr.find('.applicableUsers').val(''); - } else { - $tr.find('.applicableUsers').val(groupsId.concat(users).join(',')); - } - } - }); - - addSelect2(this.$el.find('tr:not(#addMountPoint) .applicableUsers'), this._userListLimit); - this.$el.tooltip({ - selector: '.status span', - container: 'body' - }); - this._initEvents(); - - this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { - self.recheckStorageConfig($(tr)); - }); }, /** @@ -661,7 +632,7 @@ MountConfigListView.prototype = _.extend({ */ whenSelectBackend: function(callback) { this.$el.find('tbody tr:not(#addMountPoint)').each(function(i, tr) { - var backend = $(tr).find('.backend').data('class'); + var backend = $(tr).find('.backend').data('identifier'); callback($(tr), backend); }); this.on('selectBackend', callback); @@ -725,65 +696,41 @@ MountConfigListView.prototype = _.extend({ _onSelectBackend: function(event) { var $target = $(event.target); - var $el = this.$el; var $tr = $target.closest('tr'); - $el.find('tbody').append($tr.clone()); - $el.find('tbody tr').last().find('.mountPoint input').val(''); - $tr.data('constructing', true); - var selected = $target.find('option:selected').text(); - var backend = $target.val(); - $tr.find('.backend').text(selected); - if ($tr.find('.mountPoint input').val() === '') { - $tr.find('.mountPoint input').val(this._suggestMountPoint(selected)); - } - $tr.addClass(backend); - $tr.find('.backend').data('class', backend); - var backendConfiguration = this._allBackends[backend]; - var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>'); - $.each(this._allAuthMechanisms, function(authClass, authMechanism) { - if (backendConfiguration['authSchemes'][authMechanism['scheme']]) { - selectAuthMechanism.append( - $('<option value="'+authClass+'" data-scheme="'+authMechanism['scheme']+'">'+authMechanism['name']+'</option>') - ); - } - }); - $tr.find('td.authentication').append(selectAuthMechanism); + var storageConfig = new this._storageConfigClass(); + storageConfig.mountPoint = $tr.find('.mountPoint input').val(); + storageConfig.backend = $target.val(); + $tr.find('.mountPoint input').val(''); - var $td = $tr.find('td.configuration'); - $.each(backendConfiguration['configuration'], _.partial(this.writeParameterInput, $td)); + var onCompletion = jQuery.Deferred(); + $tr = this.newStorage(storageConfig, onCompletion); + onCompletion.resolve(); - this.trigger('selectBackend', $tr, backend); - - selectAuthMechanism.trigger('change'); // generate configuration parameters for auth mechanism - - var priorityEl = $('<input type="hidden" class="priority" value="' + backendConfiguration['priority'] + '" />'); - $tr.append(priorityEl); - $td.children().not('[type=hidden]').first().focus(); - - // FIXME default backend mount options - $tr.find('input.mountOptions').val(JSON.stringify({ - 'encrypt': true, - 'previews': true, - 'filesystem_check_changes': 1 - })); - - $tr.find('td').last().attr('class', 'remove'); - $tr.find('td.mountOptionsToggle').removeClass('hidden'); - $tr.find('td').last().removeAttr('style'); - $tr.removeAttr('id'); - $target.remove(); - addSelect2($tr.find('.applicableUsers'), this._userListLimit); - - $tr.removeData('constructing'); + $tr.find('td.configuration').children().not('[type=hidden]').first().focus(); this.saveStorageConfig($tr); }, _onSelectAuthMechanism: function(event) { var $target = $(event.target); var $tr = $target.closest('tr'); - var authMechanism = $target.val(); + + var onCompletion = jQuery.Deferred(); + this.configureAuthMechanism($tr, authMechanism, onCompletion); + onCompletion.resolve(); + + this.saveStorageConfig($tr); + }, + + /** + * Configure the storage config with a new authentication mechanism + * + * @param {jQuery} $tr config row + * @param {string} authMechanism + * @param {jQuery.Deferred} onCompletion + */ + configureAuthMechanism: function($tr, authMechanism, onCompletion) { var authMechanismConfiguration = this._allAuthMechanisms[authMechanism]; var $td = $tr.find('td.configuration'); $td.find('.auth-param').remove(); @@ -793,15 +740,172 @@ MountConfigListView.prototype = _.extend({ )); this.trigger('selectAuthMechanism', - $tr, authMechanism, authMechanismConfiguration['scheme'] + $tr, authMechanism, authMechanismConfiguration['scheme'], onCompletion ); + }, + + /** + * Create a config row for a new storage + * + * @param {StorageConfig} storageConfig storage config to pull values from + * @param {jQuery.Deferred} onCompletion + * @return {jQuery} created row + */ + newStorage: function(storageConfig, onCompletion) { + var mountPoint = storageConfig.mountPoint; + var backend = this._allBackends[storageConfig.backend]; + + // FIXME: Replace with a proper Handlebar template + var $tr = this.$el.find('tr#addMountPoint'); + this.$el.find('tbody').append($tr.clone()); + + $tr.find('td').last().attr('class', 'remove'); + $tr.find('td.mountOptionsToggle').removeClass('hidden'); + $tr.find('td').last().removeAttr('style'); + $tr.removeAttr('id'); + $tr.find('select#selectBackend'); + addSelect2($tr.find('.applicableUsers'), this._userListLimit); + + if (storageConfig.id) { + $tr.data('id', storageConfig.id); + } + + $tr.find('.backend').text(backend.name); + if (mountPoint === '') { + mountPoint = this._suggestMountPoint(backend.name); + } + $tr.find('.mountPoint input').val(mountPoint); + $tr.addClass(backend.identifier); + $tr.find('.backend').data('identifier', backend.identifier); + + var selectAuthMechanism = $('<select class="selectAuthMechanism"></select>'); + $.each(this._allAuthMechanisms, function(authIdentifier, authMechanism) { + if (backend.authSchemes[authMechanism.scheme]) { + selectAuthMechanism.append( + $('<option value="'+authMechanism.identifier+'" data-scheme="'+authMechanism.scheme+'">'+authMechanism.name+'</option>') + ); + } + }); + if (storageConfig.authMechanism) { + selectAuthMechanism.val(storageConfig.authMechanism); + } else { + storageConfig.authMechanism = selectAuthMechanism.val(); + } + $tr.find('td.authentication').append(selectAuthMechanism); - if ($tr.data('constructing') !== true) { - // row is ready, trigger recheck - this.saveStorageConfig($tr); + var $td = $tr.find('td.configuration'); + $.each(backend.configuration, _.partial(this.writeParameterInput, $td)); + + this.trigger('selectBackend', $tr, backend.identifier, onCompletion); + this.configureAuthMechanism($tr, storageConfig.authMechanism, onCompletion); + + if (storageConfig.backendOptions) { + $td.children().each(function() { + var input = $(this); + var val = storageConfig.backendOptions[input.data('parameter')]; + if (val !== undefined) { + input.val(storageConfig.backendOptions[input.data('parameter')]); + highlightInput(input); + } + }); + } + + var applicable = []; + if (storageConfig.applicableUsers) { + applicable = applicable.concat(storageConfig.applicableUsers); + } + if (storageConfig.applicableGroups) { + applicable = applicable.concat( + _.map(storageConfig.applicableGroups, function(group) { + return group+'(group)'; + }) + ); } + $tr.find('.applicableUsers').val(applicable).trigger('change'); + + var priorityEl = $('<input type="hidden" class="priority" value="' + backend.priority + '" />'); + $tr.append(priorityEl); + + if (storageConfig.mountOptions) { + $tr.find('input.mountOptions').val(JSON.stringify(storageConfig.mountOptions)); + } else { + // FIXME default backend mount options + $tr.find('input.mountOptions').val(JSON.stringify({ + 'encrypt': true, + 'previews': true, + 'filesystem_check_changes': 1 + })); + } + + return $tr; }, + /** + * Load storages into config rows + */ + loadStorages: function() { + var self = this; + + if (this._isPersonal) { + // load userglobal storages + $.ajax({ + type: 'GET', + url: OC.generateUrl('apps/files_external/userglobalstorages'), + contentType: 'application/json', + success: function(result) { + var onCompletion = jQuery.Deferred(); + $.each(result, function(i, storageParams) { + storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash + var storageConfig = new self._storageConfigClass(); + _.extend(storageConfig, storageParams); + var $tr = self.newStorage(storageConfig, onCompletion); + + // userglobal storages must be at the top of the list + $tr.detach(); + self.$el.prepend($tr); + + var $authentication = $tr.find('.authentication'); + $authentication.text($authentication.find('select option:selected').text()); + + // userglobal storages do not expose configuration data + $tr.find('.configuration').text(t('files_external', 'Admin defined')); + + // disable any other inputs + $tr.find('.mountOptionsToggle, .remove').empty(); + $tr.find('input, select, button').attr('disabled', 'disabled'); + }); + onCompletion.resolve(); + } + }); + } + + var url = this._storageConfigClass.prototype._url; + + $.ajax({ + type: 'GET', + url: OC.generateUrl(url), + contentType: 'application/json', + success: function(result) { + var onCompletion = jQuery.Deferred(); + $.each(result, function(i, storageParams) { + storageParams.mountPoint = storageParams.mountPoint.substr(1); // trim leading slash + var storageConfig = new self._storageConfigClass(); + _.extend(storageConfig, storageParams); + var $tr = self.newStorage(storageConfig, onCompletion); + self.recheckStorageConfig($tr); + }); + onCompletion.resolve(); + } + }); + }, + + /** + * @param {jQuery} $td + * @param {string} parameter + * @param {string} placeholder + * @param {Array} classes + * @return {jQuery} newly created input + */ writeParameterInput: function($td, parameter, placeholder, classes) { classes = $.isArray(classes) ? classes : []; classes.push('added'); @@ -822,6 +926,7 @@ MountConfigListView.prototype = _.extend({ } highlightInput(newElement); $td.append(newElement); + return newElement; }, /** @@ -831,14 +936,14 @@ MountConfigListView.prototype = _.extend({ * @return {OCA.External.StorageConfig} storage model instance */ getStorageConfig: function($tr) { - var storageId = parseInt($tr.attr('data-id'), 10); + var storageId = $tr.data('id'); if (!storageId) { // new entry storageId = null; } var storage = new this._storageConfigClass(storageId); storage.mountPoint = $tr.find('.mountPoint input').val(); - storage.backend = $tr.find('.backend').data('class'); + storage.backend = $tr.find('.backend').data('identifier'); storage.authMechanism = $tr.find('.selectAuthMechanism').val(); var classOptions = {}; @@ -951,8 +1056,8 @@ MountConfigListView.prototype = _.extend({ if (concurrentTimer === undefined || $tr.data('save-timer') === concurrentTimer ) { - self.updateStatus($tr, result.status, result.statusMessage); - $tr.attr('data-id', result.id); + self.updateStatus($tr, result.status); + $tr.data('id', result.id); if (_.isFunction(callback)) { callback(storage); @@ -1054,12 +1159,12 @@ MountConfigListView.prototype = _.extend({ } return defaultMountPoint + append; }, - + /** * Toggles the mount options dropdown * * @param {Object} $tr configuration row - */ + */ _showMountOptionsDropdown: function($tr) { if (this._preventNextDropdown) { // prevented because the click was on the toggle @@ -1106,6 +1211,7 @@ $(document).ready(function() { var mountConfigListView = new MountConfigListView($('#externalStorage'), { encryptionEnabled: encryptionEnabled }); + mountConfigListView.loadStorages(); $('#sslCertificate').on('click', 'td.remove>img', function() { var $tr = $(this).closest('tr'); diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index df15c3bd258..05196a58905 100644 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -32,31 +32,11 @@ $appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $userStoragesService = $appContainer->query('OCA\Files_external\Service\UserStoragesService'); -OCP\Util::addScript('files_external', 'settings'); -OCP\Util::addStyle('files_external', 'settings'); - -$backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); -$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); -foreach ($backends as $backend) { - if ($backend->getCustomJs()) { - \OCP\Util::addScript('files_external', $backend->getCustomJs()); - } -} -foreach ($authMechanisms as $authMechanism) { - if ($authMechanism->getCustomJs()) { - \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); - } -} - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); -$tmpl->assign('isAdminPage', false); +$tmpl->assign('visibilityType', BackendService::VISIBILITY_PERSONAL); $tmpl->assign('storages', $userStoragesService->getStorages()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); -$tmpl->assign('backends', $backends); -$tmpl->assign('authMechanisms', $authMechanisms); +$tmpl->assign('backends', $backendService->getAvailableBackends()); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); return $tmpl->fetchPage(); diff --git a/apps/files_external/settings.php b/apps/files_external/settings.php index 03ed363bdb2..50d47d667fd 100644 --- a/apps/files_external/settings.php +++ b/apps/files_external/settings.php @@ -35,40 +35,15 @@ $appContainer = \OC_Mount_Config::$app->getContainer(); $backendService = $appContainer->query('OCA\Files_External\Service\BackendService'); $globalStoragesService = $appContainer->query('OCA\Files_external\Service\GlobalStoragesService'); -OCP\Util::addScript('files_external', 'settings'); -OCP\Util::addStyle('files_external', 'settings'); - \OC_Util::addVendorScript('select2/select2'); \OC_Util::addVendorStyle('select2/select2'); -$backends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isVisibleFor(BackendService::VISIBILITY_ADMIN); -}); -$authMechanisms = array_filter($backendService->getAuthMechanisms(), function($authMechanism) { - return $authMechanism->isVisibleFor(BackendService::VISIBILITY_ADMIN); -}); -foreach ($backends as $backend) { - if ($backend->getCustomJs()) { - \OCP\Util::addScript('files_external', $backend->getCustomJs()); - } -} -foreach ($authMechanisms as $authMechanism) { - if ($authMechanism->getCustomJs()) { - \OCP\Util::addScript('files_external', $authMechanism->getCustomJs()); - } -} - -$userBackends = array_filter($backendService->getAvailableBackends(), function($backend) { - return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL); -}); - $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('encryptionEnabled', \OC::$server->getEncryptionManager()->isEnabled()); -$tmpl->assign('isAdminPage', true); +$tmpl->assign('visibilityType', BackendService::VISIBILITY_ADMIN); $tmpl->assign('storages', $globalStoragesService->getStorages()); -$tmpl->assign('backends', $backends); -$tmpl->assign('authMechanisms', $authMechanisms); -$tmpl->assign('userBackends', $userBackends); +$tmpl->assign('backends', $backendService->getAvailableBackends()); +$tmpl->assign('authMechanisms', $backendService->getAuthMechanisms()); $tmpl->assign('dependencies', OC_Mount_Config::dependencyMessage($backendService->getBackends())); $tmpl->assign('allowUserMounting', $backendService->isUserMountingAllowed()); return $tmpl->fetchPage(); diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index cebf6cc4de0..f7caf3d2caa 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -3,6 +3,21 @@ use \OCA\Files_External\Lib\DefinitionParameter; use \OCA\Files_External\Service\BackendService; + script('files_external', 'settings'); + style('files_external', 'settings'); + + // load custom JS + foreach ($_['backends'] as $backend) { + if ($backend->getCustomJs()) { + script('files_external', $backend->getCustomJs()); + } + } + foreach ($_['authMechanisms'] as $authMechanism) { + if ($authMechanism->getCustomJs()) { + script('files_external', $authMechanism->getCustomJs()); + } + } + function writeParameterInput($parameter, $options, $classes = []) { $value = ''; if (isset($options[$parameter->getName()])) { @@ -56,7 +71,7 @@ <form id="files_external" class="section" data-encryption-enabled="<?php echo $_['encryptionEnabled']?'true': 'false'; ?>"> <h2><?php p($l->t('External Storage')); ?></h2> <?php if (isset($_['dependencies']) and ($_['dependencies']<>'')) print_unescaped(''.$_['dependencies'].''); ?> - <table id="externalStorage" class="grid" data-admin='<?php print_unescaped(json_encode($_['isAdminPage'])); ?>'> + <table id="externalStorage" class="grid" data-admin='<?php print_unescaped(json_encode($_['visibilityType'] === BackendService::VISIBILITY_ADMIN)); ?>'> <thead> <tr> <th></th> @@ -64,79 +79,12 @@ <th><?php p($l->t('External storage')); ?></th> <th><?php p($l->t('Authentication')); ?></th> <th><?php p($l->t('Configuration')); ?></th> - <?php if ($_['isAdminPage']) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN) print_unescaped('<th>'.$l->t('Available for').'</th>'); ?> <th> </th> <th> </th> </tr> </thead> <tbody> - <?php foreach ($_['storages'] as $storage): ?> - <tr class="<?php p($storage->getBackend()->getIdentifier()); ?>" data-id="<?php p($storage->getId()); ?>"> - <td class="status"> - <span></span> - </td> - <td class="mountPoint"><input type="text" name="mountPoint" - value="<?php p(ltrim($storage->getMountPoint(), '/')); ?>" - data-mountpoint="<?php p(ltrim($storage->getMountPoint(), '/')); ?>" - placeholder="<?php p($l->t('Folder name')); ?>" /> - </td> - <td class="backend" data-class="<?php p($storage->getBackend()->getIdentifier()); ?>"><?php p($storage->getBackend()->getText()); ?> - </td> - <td class="authentication"> - <select class="selectAuthMechanism"> - <?php - $authSchemes = $storage->getBackend()->getAuthSchemes(); - $authMechanisms = array_filter($_['authMechanisms'], function($mech) use ($authSchemes) { - return isset($authSchemes[$mech->getScheme()]); - }); - ?> - <?php foreach ($authMechanisms as $mech): ?> - <option value="<?php p($mech->getIdentifier()); ?>" data-scheme="<?php p($mech->getScheme());?>" - <?php if ($mech->getIdentifier() === $storage->getAuthMechanism()->getIdentifier()): ?>selected<?php endif; ?> - ><?php p($mech->getText()); ?></option> - <?php endforeach; ?> - </select> - </td> - <td class="configuration"> - <?php - $options = $storage->getBackendOptions(); - foreach ($storage->getBackend()->getParameters() as $parameter) { - writeParameterInput($parameter, $options); - } - foreach ($storage->getAuthMechanism()->getParameters() as $parameter) { - writeParameterInput($parameter, $options, ['auth-param']); - } - ?> - </td> - <?php if ($_['isAdminPage']): ?> - <td class="applicable" - align="right" - data-applicable-groups='<?php print_unescaped(json_encode($storage->getApplicableGroups())); ?>' - data-applicable-users='<?php print_unescaped(json_encode($storage->getApplicableUsers())); ?>'> - <input type="hidden" class="applicableUsers" style="width:20em;" value=""/> - </td> - <?php endif; ?> - <td class="mountOptionsToggle"> - <img - class="svg action" - title="<?php p($l->t('Advanced settings')); ?>" - alt="<?php p($l->t('Advanced settings')); ?>" - src="<?php print_unescaped(image_path('core', 'actions/settings.svg')); ?>" - /> - <input type="hidden" class="mountOptions" value="<?php p(json_encode($storage->getMountOptions())); ?>" /> - <?php if ($_['isAdminPage']): ?> - <input type="hidden" class="priority" value="<?php p($storage->getPriority()); ?>" /> - <?php endif; ?> - </td> - <td class="remove"> - <img alt="<?php p($l->t('Delete')); ?>" - title="<?php p($l->t('Delete')); ?>" - class="svg action" - src="<?php print_unescaped(image_path('core', 'actions/delete.svg')); ?>" - /> - </td> - </tr> - <?php endforeach; ?> <tr id="addMountPoint"> <td class="status"> <span></span> @@ -151,7 +99,9 @@ <?php p($l->t('Add storage')); ?> </option> <?php - $sortedBackends = $_['backends']; + $sortedBackends = array_filter($_['backends'], function($backend) use ($_) { + return $backend->isVisibleFor($_['visibilityType']); + }); uasort($sortedBackends, function($a, $b) { return strcasecmp($a->getText(), $b->getText()); }); @@ -164,7 +114,7 @@ </td> <td class="authentication" data-mechanisms='<?php p(json_encode($_['authMechanisms'])); ?>'></td> <td class="configuration"></td> - <?php if ($_['isAdminPage']): ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?> <td class="applicable" align="right"> <input type="hidden" class="applicableUsers" style="width:20em;" value="" /> </td> @@ -189,7 +139,7 @@ </table> <br /> - <?php if ($_['isAdminPage']): ?> + <?php if ($_['visibilityType'] === BackendService::VISIBILITY_ADMIN): ?> <br /> <input type="checkbox" name="allowUserMounting" id="allowUserMounting" class="checkbox" value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> /> @@ -197,7 +147,12 @@ <p id="userMountingBackends"<?php if ($_['allowUserMounting'] != 'yes'): ?> class="hidden"<?php endif; ?>> <?php p($l->t('Allow users to mount the following external storage')); ?><br /> - <?php $i = 0; foreach ($_['userBackends'] as $backend): ?> + <?php + $userBackends = array_filter($_['backends'], function($backend) { + return $backend->isAllowedVisibleFor(BackendService::VISIBILITY_PERSONAL); + }); + ?> + <?php $i = 0; foreach ($userBackends as $backend): ?> <?php if ($deprecateTo = $backend->getDeprecateTo()): ?> <input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" /> <?php else: ?> diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 67a81277124..3d0168898ca 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -54,7 +54,8 @@ describe('OCA.External.Settings tests', function() { // within the DOM by the server template $('#externalStorage .selectBackend:first').data('configurations', { '\\OC\\TestBackend': { - 'backend': 'Test Backend Name', + 'identifier': '\\OC\\TestBackend', + 'name': 'Test Backend', 'configuration': { 'field1': 'Display Name 1', 'field2': '&Display Name 2' @@ -65,7 +66,8 @@ describe('OCA.External.Settings tests', function() { 'priority': 11 }, '\\OC\\AnotherTestBackend': { - 'backend': 'Another Test Backend Name', + 'identifier': '\\OC\\AnotherTestBackend', + 'name': 'Another Test Backend', 'configuration': { 'field1': 'Display Name 1', 'field2': '&Display Name 2' @@ -80,6 +82,7 @@ describe('OCA.External.Settings tests', function() { $('#externalStorage #addMountPoint .authentication:first').data('mechanisms', { 'mechanism1': { + 'identifier': 'mechanism1', 'name': 'Mechanism 1', 'configuration': { }, diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 82691129926..9b2e2c00f60 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -286,8 +286,12 @@ OCA.Sharing.PublicApp = { _saveToOwnCloud: function (remote, token, owner, name, isProtected) { var location = window.location.protocol + '//' + window.location.host + OC.webroot; + + if(remote.substr(-1) !== '/') { + remote += '/' + }; - var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location) // our location is the remote for the other server + var url = remote + 'index.php/apps/files#' + 'remote=' + encodeURIComponent(location) // our location is the remote for the other server + "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) + "&name=" + encodeURIComponent(name) + "&protected=" + isProtected; diff --git a/apps/files_sharing/lib/external/manager.php b/apps/files_sharing/lib/external/manager.php index 93e2cdb540b..020f55c5157 100644 --- a/apps/files_sharing/lib/external/manager.php +++ b/apps/files_sharing/lib/external/manager.php @@ -192,6 +192,8 @@ class Manager { $acceptShare->execute(array(1, $mountPoint, $hash, $id, $this->uid)); $this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept'); + \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $share['remote']]); + //FIXME $this->scrapNotification($share['remote_id']); return true; } diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index 7be91186c16..42e57e8296e 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -864,14 +864,13 @@ class Access extends LDAPUtility implements user\IUserTools { * @param bool $pagedSearchOK whether a paged search has been executed * @param bool $skipHandling required for paged search when cookies to * prior results need to be gained - * @return array|false array with the search result as first value and pagedSearchOK as - * second | false if not successful + * @return bool cookie validity, true if we have more pages, false otherwise. */ private function processPagedSearchStatus($sr, $filter, $base, $iFoundItems, $limit, $offset, $pagedSearchOK, $skipHandling) { + $cookie = null; if($pagedSearchOK) { $cr = $this->connection->getConnectionResource(); foreach($sr as $key => $res) { - $cookie = null; if($this->ldap->controlPagedResultResponse($cr, $res, $cookie)) { $this->setPagedResultCookie($base[$key], $filter, $limit, $offset, $cookie); } @@ -892,6 +891,12 @@ class Access extends LDAPUtility implements user\IUserTools { \OCP\Util::writeLog('user_ldap', 'Paged search was not available', \OCP\Util::INFO); } } + /* ++ Fixing RHDS searches with pages with zero results ++ + * Return cookie status. If we don't have more pages, with RHDS + * cookie is null, with openldap cookie is an empty string and + * to 386ds '0' is a valid cookie. Even if $iFoundItems == 0 + */ + return !empty($cookie) || $cookie === '0'; } /** @@ -920,7 +925,6 @@ class Access extends LDAPUtility implements user\IUserTools { $this->connection->getConnectionResource(); do { - $continue = false; $search = $this->executeSearch($filter, $base, $attr, $limitPerPage, $offset); if($search === false) { @@ -928,12 +932,20 @@ class Access extends LDAPUtility implements user\IUserTools { } list($sr, $pagedSearchOK) = $search; - $count = $this->countEntriesInSearchResults($sr, $limitPerPage, $continue); + /* ++ Fixing RHDS searches with pages with zero results ++ + * countEntriesInSearchResults() method signature changed + * by removing $limit and &$hasHitLimit parameters + */ + $count = $this->countEntriesInSearchResults($sr); $counter += $count; - $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage, + $hasMorePages = $this->processPagedSearchStatus($sr, $filter, $base, $count, $limitPerPage, $offset, $pagedSearchOK, $skipHandling); $offset += $limitPerPage; + /* ++ Fixing RHDS searches with pages with zero results ++ + * Continue now depends on $hasMorePages value + */ + $continue = $pagedSearchOK && $hasMorePages; } while($continue && (is_null($limit) || $limit <= 0 || $limit > $counter)); return $counter; @@ -941,20 +953,15 @@ class Access extends LDAPUtility implements user\IUserTools { /** * @param array $searchResults - * @param int $limit - * @param bool $hasHitLimit * @return int */ - private function countEntriesInSearchResults($searchResults, $limit, &$hasHitLimit) { + private function countEntriesInSearchResults($searchResults) { $cr = $this->connection->getConnectionResource(); $counter = 0; foreach($searchResults as $res) { $count = intval($this->ldap->countEntries($cr, $res)); $counter += $count; - if($count > 0 && $count === $limit) { - $hasHitLimit = true; - } } return $counter; @@ -975,38 +982,53 @@ class Access extends LDAPUtility implements user\IUserTools { //otherwise search will fail $limit = null; } - $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); - if($search === false) { - return array(); - } - list($sr, $pagedSearchOK) = $search; - $cr = $this->connection->getConnectionResource(); - if($skipHandling) { - //i.e. result do not need to be fetched, we just need the cookie - //thus pass 1 or any other value as $iFoundItems because it is not - //used - $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit, - $offset, $pagedSearchOK, - $skipHandling); - return array(); - } + /* ++ Fixing RHDS searches with pages with zero results ++ + * As we can have pages with zero results and/or pages with less + * than $limit results but with a still valid server 'cookie', + * loops through until we get $continue equals true and + * $findings['count'] < $limit + */ + $findings = array(); + $savedoffset = $offset; + do { + $continue = false; + $search = $this->executeSearch($filter, $base, $attr, $limit, $offset); + if($search === false) { + return array(); + } + list($sr, $pagedSearchOK) = $search; + $cr = $this->connection->getConnectionResource(); - // Do the server-side sorting - foreach(array_reverse($attr) as $sortAttr){ - foreach($sr as $searchResource) { - $this->ldap->sort($cr, $searchResource, $sortAttr); + if($skipHandling) { + //i.e. result do not need to be fetched, we just need the cookie + //thus pass 1 or any other value as $iFoundItems because it is not + //used + $this->processPagedSearchStatus($sr, $filter, $base, 1, $limit, + $offset, $pagedSearchOK, + $skipHandling); + return array(); } - } - $findings = array(); - foreach($sr as $res) { - $findings = array_merge($findings, $this->ldap->getEntries($cr , $res )); - } + // Do the server-side sorting + foreach(array_reverse($attr) as $sortAttr){ + foreach($sr as $searchResource) { + $this->ldap->sort($cr, $searchResource, $sortAttr); + } + } + + + foreach($sr as $res) { + $findings = array_merge($findings, $this->ldap->getEntries($cr , $res )); + } - $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'], - $limit, $offset, $pagedSearchOK, + $continue = $this->processPagedSearchStatus($sr, $filter, $base, $findings['count'], + $limit, $offset, $pagedSearchOK, $skipHandling); + $offset += $limit; + } while ($continue && $pagedSearchOK && $findings['count'] < $limit); + // reseting offset + $offset = $savedoffset; // if we're here, probably no connection resource is returned. // to make ownCloud behave nicely, we simply give back an empty array. @@ -1594,7 +1616,13 @@ class Access extends LDAPUtility implements user\IUserTools { } } - } else if($this->connection->hasPagedResultSupport && $limit === 0) { + /* ++ Fixing RHDS searches with pages with zero results ++ + * We coudn't get paged searches working with our RHDS for login ($limit = 0), + * due to pages with zero results. + * So we added "&& !empty($this->lastCookie)" to this test to ignore pagination + * if we don't have a previous paged search. + */ + } else if($this->connection->hasPagedResultSupport && $limit === 0 && !empty($this->lastCookie)) { // a search without limit was requested. However, if we do use // Paged Search once, we always must do it. This requires us to // initialize it with the configured page size. diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 6aac0d6264d..1308c99f804 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -2566,7 +2566,10 @@ class Share extends Constants { $result = self::tryHttpPost($url, $fields); $status = json_decode($result['result'], true); - return ($result['success'] && $status['ocs']['meta']['statuscode'] === 100); + if ($result['success'] && $status['ocs']['meta']['statuscode'] === 100) { + \OC_Hook::emit('OCP\Share', 'federated_share_added', ['server' => $remote]); + return true; + } } |