diff options
Diffstat (limited to 'apps')
21 files changed, 539 insertions, 202 deletions
diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js index 9451e828f91..eae18c1d485 100644 --- a/apps/comments/js/commentstabview.js +++ b/apps/comments/js/commentstabview.js @@ -32,7 +32,7 @@ '{{/if}}' + ' </div>' + ' <form class="newCommentForm">' + - ' <input type="text" class="message" placeholder="{{newMessagePlaceholder}}" value="{{{message}}}" />' + + ' <input type="text" class="message" placeholder="{{newMessagePlaceholder}}" value="{{message}}" />' + ' <input class="submit icon-confirm" type="submit" value="" />' + '{{#if isEditMode}}' + ' <input class="cancel pull-right" type="button" value="{{cancelText}}" />' + diff --git a/apps/dav/lib/Connector/Sabre/FilesPlugin.php b/apps/dav/lib/Connector/Sabre/FilesPlugin.php index c472f75b6bf..dd5f958ed4c 100644 --- a/apps/dav/lib/Connector/Sabre/FilesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/FilesPlugin.php @@ -324,18 +324,13 @@ class FilesPlugin extends ServerPlugin { return $displayName; }); - $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { - if ($node->getPath() === '/') { - return $this->config->getSystemValue('data-fingerprint', ''); - } - }); - $propFind->handle(self::HAS_PREVIEW_PROPERTYNAME, function () use ($node) { return json_encode($this->previewManager->isAvailable($node->getFileInfo())); }); } - if ($node instanceof \OCA\DAV\Files\FilesHome) { + if ($node instanceof \OCA\DAV\Connector\Sabre\Node + || $node instanceof \OCA\DAV\Files\FilesHome) { $propFind->handle(self::DATA_FINGERPRINT_PROPERTYNAME, function() use ($node) { return $this->config->getSystemValue('data-fingerprint', ''); }); diff --git a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php index 6630c027541..e2d63868af0 100644 --- a/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/FilesPluginTest.php @@ -213,7 +213,8 @@ class FilesPluginTest extends TestCase { $this->assertEquals('http://example.com/', $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); $this->assertEquals('foo', $propFind->get(self::OWNER_ID_PROPERTYNAME)); $this->assertEquals('M. Foo', $propFind->get(self::OWNER_DISPLAY_NAME_PROPERTYNAME)); - $this->assertEquals([self::SIZE_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties()); + $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals([self::SIZE_PROPERTYNAME], $propFind->get404Properties()); } public function testGetPropertiesForFileHome() { @@ -357,7 +358,8 @@ class FilesPluginTest extends TestCase { $this->assertEquals(1025, $propFind->get(self::SIZE_PROPERTYNAME)); $this->assertEquals('DWCKMSR', $propFind->get(self::PERMISSIONS_PROPERTYNAME)); $this->assertEquals(null, $propFind->get(self::DOWNLOADURL_PROPERTYNAME)); - $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME, self::DATA_FINGERPRINT_PROPERTYNAME], $propFind->get404Properties()); + $this->assertEquals('my_fingerprint', $propFind->get(self::DATA_FINGERPRINT_PROPERTYNAME)); + $this->assertEquals([self::DOWNLOADURL_PROPERTYNAME], $propFind->get404Properties()); } public function testGetPropertiesForRootDirectory() { diff --git a/apps/federatedfilesharing/appinfo/routes.php b/apps/federatedfilesharing/appinfo/routes.php index a4f56e372c9..9caaa939348 100644 --- a/apps/federatedfilesharing/appinfo/routes.php +++ b/apps/federatedfilesharing/appinfo/routes.php @@ -26,5 +26,14 @@ return [ 'routes' => [ ['name' => 'MountPublicLink#createFederatedShare', 'url' => '/createFederatedShare', 'verb' => 'POST'], ['name' => 'MountPublicLink#askForFederatedShare', 'url' => '/askForFederatedShare', 'verb' => 'POST'], - ] + ], + 'ocs' => [ + ['root' => '/cloud', 'name' => 'RequestHandler#createShare', 'url' => '/shares', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#reShare', 'url' => '/shares/{id}/reshare', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#updatePermissions', 'url' => '/shares/{id}/permissions', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#acceptShare', 'url' => '/shares/{id}/accept', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#declineShare', 'url' => '/shares/{id}/decline', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#unshare', 'url' => '/shares/{id}/unshare', 'verb' => 'POST'], + ['root' => '/cloud', 'name' => 'RequestHandler#revoke', 'url' => '/shares/{id}/revoke', 'verb' => 'POST'], + ], ]; diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index b767a322505..b470bb3e584 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -24,7 +24,12 @@ namespace OCA\FederatedFileSharing\AppInfo; +use OC\AppFramework\Utility\SimpleContainer; +use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\Controller\RequestHandlerController; use OCA\FederatedFileSharing\FederatedShareProvider; +use OCA\FederatedFileSharing\Notifications; +use OCA\FederatedFileSharing\RequestHandler; use OCP\AppFramework\App; class Application extends App { @@ -34,6 +39,35 @@ class Application extends App { public function __construct() { parent::__construct('federatedfilesharing'); + + $container = $this->getContainer(); + $server = $container->getServer(); + + $container->registerService('RequestHandlerController', function(SimpleContainer $c) use ($server) { + $addressHandler = new AddressHandler( + $server->getURLGenerator(), + $server->getL10N('federatedfilesharing') + ); + $notification = new Notifications( + $addressHandler, + $server->getHTTPClientService(), + new \OCA\FederatedFileSharing\DiscoveryManager( + $server->getMemCacheFactory(), + $server->getHTTPClientService() + ), + \OC::$server->getJobList() + ); + return new RequestHandlerController( + $c->query('AppName'), + $server->getRequest(), + $this->getFederatedShareProvider(), + $server->getDatabaseConnection(), + $server->getShareManager(), + $notification, + $addressHandler, + $server->getUserManager() + ); + }); } /** diff --git a/apps/federatedfilesharing/lib/RequestHandler.php b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php index f531c7bcb4a..9a41962ee3a 100644 --- a/apps/federatedfilesharing/lib/RequestHandler.php +++ b/apps/federatedfilesharing/lib/Controller/RequestHandlerController.php @@ -24,25 +24,28 @@ * */ -namespace OCA\FederatedFileSharing; +namespace OCA\FederatedFileSharing\Controller; +use OCA\FederatedFileSharing\DiscoveryManager; use OCA\Files_Sharing\Activity; +use OCA\FederatedFileSharing\AddressHandler; +use OCA\FederatedFileSharing\FederatedShareProvider; +use OCA\FederatedFileSharing\Notifications; use OCP\AppFramework\Http; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSException; +use OCP\AppFramework\OCS\OCSForbiddenException; +use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\AppFramework\OCSController; use OCP\Constants; use OCP\Files\NotFoundException; use OCP\IDBConnection; use OCP\IRequest; use OCP\IUserManager; use OCP\Share; +use OCP\Share\IShare; -/** - * Class RequestHandler - * - * handles OCS Request to the federated share API - * - * @package OCA\FederatedFileSharing\API - */ -class RequestHandler { +class RequestHandlerController extends OCSController { /** @var FederatedShareProvider */ private $federatedShareProvider; @@ -53,9 +56,6 @@ class RequestHandler { /** @var Share\IManager */ private $shareManager; - /** @var IRequest */ - private $request; - /** @var Notifications */ private $notifications; @@ -71,41 +71,47 @@ class RequestHandler { /** * Server2Server constructor. * + * @param string $appName + * @param IRequest $request * @param FederatedShareProvider $federatedShareProvider * @param IDBConnection $connection * @param Share\IManager $shareManager - * @param IRequest $request * @param Notifications $notifications * @param AddressHandler $addressHandler * @param IUserManager $userManager */ - public function __construct(FederatedShareProvider $federatedShareProvider, + public function __construct($appName, + IRequest $request, + FederatedShareProvider $federatedShareProvider, IDBConnection $connection, Share\IManager $shareManager, - IRequest $request, Notifications $notifications, AddressHandler $addressHandler, IUserManager $userManager ) { + parent::__construct($appName, $request); + $this->federatedShareProvider = $federatedShareProvider; $this->connection = $connection; $this->shareManager = $shareManager; - $this->request = $request; $this->notifications = $notifications; $this->addressHandler = $addressHandler; $this->userManager = $userManager; } /** + * @NoCSRFRequired + * @PublicPage + * * create a new share * - * @param array $params - * @return \OC_OCS_Result + * @return Http\DataResponse + * @throws OCSException */ - public function createShare($params) { + public function createShare() { if (!$this->isS2SEnabled(true)) { - return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing'); + throw new OCSException('Server does not support federated cloud sharing', 503); } $remote = isset($_POST['remote']) ? $_POST['remote'] : null; @@ -121,7 +127,7 @@ class RequestHandler { if ($remote && $token && $name && $owner && $remoteId && $shareWith) { if(!\OCP\Util::isValidFileName($name)) { - return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.'); + throw new OCSException('The mountpoint name contains invalid characters.', 400); } // FIXME this should be a method in the user management instead @@ -134,7 +140,7 @@ class RequestHandler { \OCP\Util::writeLog('files_sharing', 'shareWith after, ' . $shareWith, \OCP\Util::DEBUG); if (!\OCP\User::userExists($shareWith)) { - return new \OC_OCS_Result(null, 400, 'User does not exists'); + throw new OCSException('User does not exists', 400); } \OC_Util::setupFS($shareWith); @@ -192,25 +198,30 @@ class RequestHandler { $notificationManager->notify($notification); - return new \OC_OCS_Result(); + return new Http\DataResponse(); } catch (\Exception $e) { \OCP\Util::writeLog('files_sharing', 'server can not add remote share, ' . $e->getMessage(), \OCP\Util::ERROR); - return new \OC_OCS_Result(null, 500, 'internal server error, was not able to add share from ' . $remote); + throw new OCSException('internal server error, was not able to add share from ' . $remote, 500); } } - return new \OC_OCS_Result(null, 400, 'server can not add remote share, missing parameter'); + throw new OCSException('server can not add remote share, missing parameter', 400); } /** + * @NoCSRFRequired + * @PublicPage + * * create re-share on behalf of another user * - * @param $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSBadRequestException + * @throws OCSForbiddenException + * @throws OCSNotFoundException */ - public function reShare($params) { + public function reShare($id) { - $id = isset($params['id']) ? (int)$params['id'] : null; $token = $this->request->getParam('token', null); $shareWith = $this->request->getParam('shareWith', null); $permission = (int)$this->request->getParam('permission', null); @@ -222,13 +233,13 @@ class RequestHandler { $permission === null || $remoteId === null ) { - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + throw new OCSBadRequestException(); } try { $share = $this->federatedShareProvider->getShareById($id); } catch (Share\Exceptions\ShareNotFound $e) { - return new \OC_OCS_Result(null, Http::STATUS_NOT_FOUND); + throw new OCSNotFoundException(); } // don't allow to share a file back to the owner @@ -236,7 +247,7 @@ class RequestHandler { $owner = $share->getShareOwner(); $currentServer = $this->addressHandler->generateRemoteURL(); if ($this->addressHandler->compareAddresses($user, $remote,$owner , $currentServer)) { - return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN); + throw new OCSForbiddenException(); } if ($this->verifyShare($share, $token)) { @@ -250,37 +261,42 @@ class RequestHandler { try { $result = $this->federatedShareProvider->create($share); $this->federatedShareProvider->storeRemoteId((int)$result->getId(), $remoteId); - return new \OC_OCS_Result(['token' => $result->getToken(), 'remoteId' => $result->getId()]); + return new Http\DataResponse([ + 'token' => $result->getToken(), + 'remoteId' => $result->getId() + ]); } catch (\Exception $e) { - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + throw new OCSBadRequestException(); } } else { - return new \OC_OCS_Result(null, Http::STATUS_FORBIDDEN); + throw new OCSForbiddenException(); } } - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); - + throw new OCSBadRequestException(); } /** + * @NoCSRFRequired + * @PublicPage + * * accept server-to-server share * - * @param array $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSException */ - public function acceptShare($params) { + public function acceptShare($id) { if (!$this->isS2SEnabled()) { - return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing'); + throw new OCSException('Server does not support federated cloud sharing', 503); } - $id = $params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; try { $share = $this->federatedShareProvider->getShareById($id); } catch (Share\Exceptions\ShareNotFound $e) { - return new \OC_OCS_Result(); + return new Http\DataResponse(); } if ($this->verifyShare($share, $token)) { @@ -292,7 +308,7 @@ class RequestHandler { } } - return new \OC_OCS_Result(); + return new Http\DataResponse(); } protected function executeAcceptShare(Share\IShare $share) { @@ -309,24 +325,27 @@ class RequestHandler { } /** + * @NoCSRFRequired + * @PublicPage + * * decline server-to-server share * - * @param array $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSException */ - public function declineShare($params) { + public function declineShare($id) { if (!$this->isS2SEnabled()) { - return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing'); + throw new OCSException('Server does not support federated cloud sharing', 503); } - $id = (int)$params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; try { $share = $this->federatedShareProvider->getShareById($id); } catch (Share\Exceptions\ShareNotFound $e) { - return new \OC_OCS_Result(); + return new Http\DataResponse(); } if($this->verifyShare($share, $token)) { @@ -338,7 +357,7 @@ class RequestHandler { $this->executeDeclineShare($share); } - return new \OC_OCS_Result(); + return new Http\DataResponse(); } /** @@ -376,18 +395,21 @@ class RequestHandler { } /** + * @NoCSRFRequired + * @PublicPage + * * remove server-to-server share if it was unshared by the owner * - * @param array $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSException */ - public function unshare($params) { + public function unshare($id) { if (!$this->isS2SEnabled()) { - return new \OC_OCS_Result(null, 503, 'Server does not support federated cloud sharing'); + throw new OCSException('Server does not support federated cloud sharing', 503); } - $id = $params['id']; $token = isset($_POST['token']) ? $_POST['token'] : null; $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ? AND `share_token` = ?'); @@ -423,7 +445,7 @@ class RequestHandler { '', '', $user, Activity::TYPE_REMOTE_SHARE, Activity::PRIORITY_MEDIUM); } - return new \OC_OCS_Result(); + return new Http\DataResponse(); } private function cleanupRemote($remote) { @@ -434,24 +456,26 @@ class RequestHandler { /** + * @NoCSRFRequired + * @PublicPage + * * federated share was revoked, either by the owner or the re-sharer * - * @param $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSBadRequestException */ - public function revoke($params) { - $id = (int)$params['id']; + public function revoke($id) { $token = $this->request->getParam('token'); $share = $this->federatedShareProvider->getShareById($id); if ($this->verifyShare($share, $token)) { $this->federatedShareProvider->removeShareFromTable($share); - return new \OC_OCS_Result(); + return new Http\DataResponse(); } - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); - + throw new OCSBadRequestException(); } /** @@ -537,20 +561,23 @@ class RequestHandler { } /** + * @NoCSRFRequired + * @PublicPage + * * update share information to keep federated re-shares in sync * - * @param array $params - * @return \OC_OCS_Result + * @param int $id + * @return Http\DataResponse + * @throws OCSBadRequestException */ - public function updatePermissions($params) { - $id = (int)$params['id']; + public function updatePermissions($id) { $token = $this->request->getParam('token', null); $permissions = $this->request->getParam('permissions', null); try { $share = $this->federatedShareProvider->getShareById($id); } catch (Share\Exceptions\ShareNotFound $e) { - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + throw new OCSBadRequestException(); } $validPermission = ctype_digit($permissions); @@ -558,10 +585,10 @@ class RequestHandler { if ($validPermission && $validToken) { $this->updatePermissionsInDatabase($share, (int)$permissions); } else { - return new \OC_OCS_Result(null, Http::STATUS_BAD_REQUEST); + throw new OCSBadRequestException(); } - return new \OC_OCS_Result(); + return new Http\DataResponse(); } /** diff --git a/apps/federatedfilesharing/tests/RequestHandlerTest.php b/apps/federatedfilesharing/tests/Controller/RequestHandlerControllerTest.php index 8f9f1384184..8e1000fb500 100644 --- a/apps/federatedfilesharing/tests/RequestHandlerTest.php +++ b/apps/federatedfilesharing/tests/Controller/RequestHandlerControllerTest.php @@ -30,7 +30,7 @@ namespace OCA\FederatedFileSharing\Tests; use OC\Files\Filesystem; use OCA\FederatedFileSharing\DiscoveryManager; use OCA\FederatedFileSharing\FederatedShareProvider; -use OCA\FederatedFileSharing\RequestHandler; +use OCA\FederatedFileSharing\Controller\RequestHandlerController; use OCP\IUserManager; use OCP\Share\IShare; @@ -40,7 +40,7 @@ use OCP\Share\IShare; * @package OCA\FederatedFileSharing\Tests * @group DB */ -class RequestHandlerTest extends TestCase { +class RequestHandlerControllerTest extends TestCase { const TEST_FOLDER_NAME = '/folder_share_api_test'; @@ -50,23 +50,23 @@ class RequestHandlerTest extends TestCase { private $connection; /** - * @var RequestHandler + * @var RequestHandlerController */ private $s2s; - /** @var \OCA\FederatedFileSharing\FederatedShareProvider | PHPUnit_Framework_MockObject_MockObject */ + /** @var \OCA\FederatedFileSharing\FederatedShareProvider|\PHPUnit_Framework_MockObject_MockObject */ private $federatedShareProvider; - /** @var \OCA\FederatedFileSharing\Notifications | PHPUnit_Framework_MockObject_MockObject */ + /** @var \OCA\FederatedFileSharing\Notifications|\PHPUnit_Framework_MockObject_MockObject */ private $notifications; - /** @var \OCA\FederatedFileSharing\AddressHandler | PHPUnit_Framework_MockObject_MockObject */ + /** @var \OCA\FederatedFileSharing\AddressHandler|\PHPUnit_Framework_MockObject_MockObject */ private $addressHandler; - /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + /** @var IUserManager|\PHPUnit_Framework_MockObject_MockObject */ private $userManager; - /** @var IShare | \PHPUnit_Framework_MockObject_MockObject */ + /** @var IShare|\PHPUnit_Framework_MockObject_MockObject */ private $share; protected function setUp() { @@ -77,12 +77,12 @@ class RequestHandlerTest extends TestCase { $config = $this->getMockBuilder('\OCP\IConfig') ->disableOriginalConstructor()->getMock(); - $clientService = $this->getMock('\OCP\Http\Client\IClientService'); + $clientService = $this->getMockBuilder('\OCP\Http\Client\IClientService')->getMock(); $httpHelperMock = $this->getMockBuilder('\OC\HTTPHelper') ->setConstructorArgs([$config, $clientService]) ->getMock(); $httpHelperMock->expects($this->any())->method('post')->with($this->anything())->will($this->returnValue(true)); - $this->share = $this->getMock('\OCP\Share\IShare'); + $this->share = $this->getMockBuilder('\OCP\Share\IShare')->getMock(); $this->federatedShareProvider = $this->getMockBuilder('OCA\FederatedFileSharing\FederatedShareProvider') ->disableOriginalConstructor()->getMock(); $this->federatedShareProvider->expects($this->any()) @@ -96,15 +96,16 @@ class RequestHandlerTest extends TestCase { ->disableOriginalConstructor()->getMock(); $this->addressHandler = $this->getMockBuilder('OCA\FederatedFileSharing\AddressHandler') ->disableOriginalConstructor()->getMock(); - $this->userManager = $this->getMock('OCP\IUserManager'); + $this->userManager = $this->getMockBuilder('OCP\IUserManager')->getMock(); $this->registerHttpHelper($httpHelperMock); - $this->s2s = new RequestHandler( + $this->s2s = new RequestHandlerController( + 'federatedfilesharing', + \OC::$server->getRequest(), $this->federatedShareProvider, \OC::$server->getDatabaseConnection(), \OC::$server->getShareManager(), - \OC::$server->getRequest(), $this->notifications, $this->addressHandler, $this->userManager @@ -127,7 +128,7 @@ class RequestHandlerTest extends TestCase { /** * Register an http helper mock for testing purposes. - * @param $httpHelper http helper mock + * @param \OC\HTTPHelper $httpHelper helper mock */ private function registerHttpHelper($httpHelper) { $this->oldHttpHelper = \OC::$server->query('HTTPHelper'); @@ -158,9 +159,7 @@ class RequestHandlerTest extends TestCase { $_POST['shareWith'] = self::TEST_FILES_SHARING_API_USER2; $_POST['remoteId'] = 1; - $result = $this->s2s->createShare(null); - - $this->assertTrue($result->succeeded()); + $this->s2s->createShare(null); $query = \OCP\DB::prepare('SELECT * FROM `*PREFIX*share_external` WHERE `remote_id` = ?'); $result = $query->execute(array('1')); @@ -178,13 +177,14 @@ class RequestHandlerTest extends TestCase { function testDeclineShare() { - $this->s2s = $this->getMockBuilder('\OCA\FederatedFileSharing\RequestHandler') + $this->s2s = $this->getMockBuilder('\OCA\FederatedFileSharing\Controller\RequestHandlerController') ->setConstructorArgs( [ + 'federatedfilessharing', + \OC::$server->getRequest(), $this->federatedShareProvider, \OC::$server->getDatabaseConnection(), \OC::$server->getShareManager(), - \OC::$server->getRequest(), $this->notifications, $this->addressHandler, $this->userManager @@ -197,7 +197,7 @@ class RequestHandlerTest extends TestCase { $_POST['token'] = 'token'; - $this->s2s->declineShare(array('id' => 42)); + $this->s2s->declineShare(42); } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index f191ade240b..ca41012764a 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -199,6 +199,7 @@ * @param options.folderDropOptions folder drop options, disabled by default * @param options.scrollTo name of file to scroll to after the first load * @param {OC.Files.Client} [options.filesClient] files API client + * @param {OC.Backbone.Model} [options.filesConfig] files app configuration * @private */ initialize: function($el, options) { @@ -212,6 +213,10 @@ this._filesConfig = options.config; } else if (!_.isUndefined(OCA.Files) && !_.isUndefined(OCA.Files.App)) { this._filesConfig = OCA.Files.App.getFilesConfig(); + } else { + this._filesConfig = new OC.Backbone.Model({ + 'showhidden': false + }); } if (options.dragOptions) { @@ -239,6 +244,7 @@ this._filesConfig.on('change:showhidden', function() { var showHidden = this.get('showhidden'); self.$el.toggleClass('hide-hidden-files', !showHidden); + self.updateSelectionSummary(); if (!showHidden) { // hiding files could make the page too small, need to try rendering next page @@ -264,7 +270,7 @@ this.files = []; this._selectedFiles = {}; - this._selectionSummary = new OCA.Files.FileSummary(); + this._selectionSummary = new OCA.Files.FileSummary(undefined, {config: this._filesConfig}); // dummy root dir info this.dirInfo = new OC.Files.FileInfo({}); @@ -2304,7 +2310,7 @@ var $tr = $('<tr class="summary"></tr>'); this.$el.find('tfoot').append($tr); - return new OCA.Files.FileSummary($tr); + return new OCA.Files.FileSummary($tr, {config: this._filesConfig}); }, updateEmptyContent: function() { var permissions = this.getDirectoryPermissions(); @@ -2451,6 +2457,7 @@ var summary = this._selectionSummary.summary; var selection; + var showHidden = !!this._filesConfig.get('showhidden'); if (summary.totalFiles === 0 && summary.totalDirs === 0) { this.$el.find('#headerName a.name>span:first').text(t('files','Name')); this.$el.find('#headerSize a>span:first').text(t('files','Size')); @@ -2477,6 +2484,11 @@ selection = fileInfo; } + if (!showHidden && summary.totalHidden > 0) { + var hiddenInfo = n('files', 'including %n hidden', 'including %n hidden', summary.totalHidden); + selection += ' (' + hiddenInfo + ')'; + } + this.$el.find('#headerName a.name>span:first').text(selection); this.$el.find('#modified a>span:first').text(''); this.$el.find('table').addClass('multiselect'); diff --git a/apps/files/js/filesummary.js b/apps/files/js/filesummary.js index a4cefe692a8..519718cfc82 100644 --- a/apps/files/js/filesummary.js +++ b/apps/files/js/filesummary.js @@ -20,6 +20,15 @@ */ (function() { + var INFO_TEMPLATE = + '<span class="info">' + + '<span class="dirinfo"></span>' + + '<span class="connector"> and </span>' + + '<span class="fileinfo"></span>' + + '<span class="hiddeninfo"></span>' + + '<span class="filter"></span>' + + '</span>'; + /** * The FileSummary class encapsulates the file summary values and * the logic to render it in the given container @@ -28,26 +37,51 @@ * @memberof OCA.Files * * @param $tr table row element + * @param {OC.Backbone.Model} [options.filesConfig] files app configuration */ - var FileSummary = function($tr) { + var FileSummary = function($tr, options) { + options = options || {}; + var self = this; this.$el = $tr; + var filesConfig = options.config; + if (filesConfig) { + this._showHidden = !!filesConfig.get('showhidden'); + filesConfig.on('change:showhidden', function() { + self._showHidden = !!this.get('showhidden'); + self.update(); + }); + } this.clear(); this.render(); }; FileSummary.prototype = { + _showHidden: null, + summary: { totalFiles: 0, totalDirs: 0, + totalHidden: 0, totalSize: 0, filter:'', sumIsPending:false }, /** + * Returns whether the given file info must be hidden + * + * @param {OC.Files.FileInfo} fileInfo file info + * + * @return {boolean} true if the file is a hidden file, false otherwise + */ + _isHiddenFile: function(file) { + return file.name && file.name.charAt(0) === '.'; + }, + + /** * Adds file - * @param file file to add - * @param update whether to update the display + * @param {OC.Files.FileInfo} file file to add + * @param {boolean} update whether to update the display */ add: function(file, update) { if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) { @@ -59,6 +93,10 @@ else { this.summary.totalFiles++; } + if (this._isHiddenFile(file)) { + this.summary.totalHidden++; + } + var size = parseInt(file.size, 10) || 0; if (size >=0) { this.summary.totalSize += size; @@ -71,8 +109,8 @@ }, /** * Removes file - * @param file file to remove - * @param update whether to update the display + * @param {OC.Files.FileInfo} file file to remove + * @param {boolean} update whether to update the display */ remove: function(file, update) { if (file.name && file.name.toLowerCase().indexOf(this.summary.filter) === -1) { @@ -84,6 +122,9 @@ else { this.summary.totalFiles--; } + if (this._isHiddenFile(file)) { + this.summary.totalHidden--; + } var size = parseInt(file.size, 10) || 0; if (size >=0) { this.summary.totalSize -= size; @@ -111,6 +152,7 @@ var summary = { totalDirs: 0, totalFiles: 0, + totalHidden: 0, totalSize: 0, filter: this.summary.filter, sumIsPending: false @@ -127,6 +169,9 @@ else { summary.totalFiles++; } + if (this._isHiddenFile(file)) { + summary.totalHidden++; + } var size = parseInt(file.size, 10) || 0; if (size >=0) { summary.totalSize += size; @@ -154,6 +199,13 @@ this.update(); }, + _infoTemplate: function(data) { + if (!this._infoTemplateCompiled) { + this._infoTemplateCompiled = Handlebars.compile(INFO_TEMPLATE); + } + return this._infoTemplateCompiled(data); + }, + /** * Renders the file summary element */ @@ -171,10 +223,12 @@ var $fileInfo = this.$el.find('.fileinfo'); var $connector = this.$el.find('.connector'); var $filterInfo = this.$el.find('.filter'); + var $hiddenInfo = this.$el.find('.hiddeninfo'); // Substitute old content with new translations $dirInfo.html(n('files', '%n folder', '%n folders', this.summary.totalDirs)); $fileInfo.html(n('files', '%n file', '%n files', this.summary.totalFiles)); + $hiddenInfo.html(' (' + n('files', 'including %n hidden', 'including %n hidden', this.summary.totalHidden) + ')'); var fileSize = this.summary.sumIsPending ? t('files', 'Pending') : OC.Util.humanFileSize(this.summary.totalSize); this.$el.find('.filesize').html(fileSize); @@ -194,6 +248,7 @@ if (this.summary.totalDirs > 0 && this.summary.totalFiles > 0) { $connector.removeClass('hidden'); } + $hiddenInfo.toggleClass('hidden', this.summary.totalHidden === 0 || this._showHidden) if (this.summary.filter === '') { $filterInfo.html(''); $filterInfo.addClass('hidden'); @@ -206,19 +261,7 @@ if (!this.$el) { return; } - // TODO: ideally this should be separate to a template or something var summary = this.summary; - var directoryInfo = n('files', '%n folder', '%n folders', summary.totalDirs); - var fileInfo = n('files', '%n file', '%n files', summary.totalFiles); - var filterInfo = ''; - if (this.summary.filter !== '') { - filterInfo = ' ' + n('files', 'matches \'{filter}\'', 'match \'{filter}\'', summary.totalFiles + summary.totalDirs, {filter: summary.filter}); - } - - var infoVars = { - dirs: '<span class="dirinfo">'+directoryInfo+'</span><span class="connector">', - files: '</span><span class="fileinfo">'+fileInfo+'</span>' - }; // don't show the filesize column, if filesize is NaN (e.g. in trashbin) var fileSize = ''; @@ -227,15 +270,14 @@ fileSize = '<td class="filesize">' + fileSize + '</td>'; } - var info = t('files', '{dirs} and {files}', infoVars, null, {'escape': false}); - - var $summary = $('<td><span class="info">'+info+'<span class="filter">'+filterInfo+'</span></span></td>'+fileSize+'<td class="date"></td>'); - - if (!this.summary.totalFiles && !this.summary.totalDirs) { - this.$el.addClass('hidden'); - } - + var $summary = $( + '<td>' + this._infoTemplate() + '</td>' + + fileSize + + '<td class="date"></td>' + ); + this.$el.addClass('hidden'); this.$el.append($summary); + this.update(); } }; OCA.Files.FileSummary = FileSummary; diff --git a/apps/files/lib/Command/Scan.php b/apps/files/lib/Command/Scan.php index 25933ae25aa..0234fb435a7 100644 --- a/apps/files/lib/Command/Scan.php +++ b/apps/files/lib/Command/Scan.php @@ -28,9 +28,11 @@ namespace OCA\Files\Command; +use Doctrine\DBAL\Connection; use OC\Core\Command\Base; use OC\ForbiddenException; use OCP\Files\StorageNotAvailableException; +use OCP\IDBConnection; use OCP\IUserManager; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -106,7 +108,8 @@ class Scan extends Base { } protected function scanFiles($user, $path, $verbose, OutputInterface $output, $backgroundScan = false) { - $scanner = new \OC\Files\Utils\Scanner($user, \OC::$server->getDatabaseConnection(), \OC::$server->getLogger()); + $connection = $this->reconnectToDatabase($output); + $scanner = new \OC\Files\Utils\Scanner($user, $connection, \OC::$server->getLogger()); # check on each file/folder if there was a user interrupt (ctrl-c) and throw an exception # printout and count if ($verbose) { @@ -318,4 +321,26 @@ class Scan extends Base { return date('H:i:s', $secs); } + /** + * @return \OCP\IDBConnection + */ + protected function reconnectToDatabase(OutputInterface $output) { + /** @var Connection | IDBConnection $connection*/ + $connection = \OC::$server->getDatabaseConnection(); + try { + $connection->close(); + } catch (\Exception $ex) { + $output->writeln("<info>Error while disconnecting from database: {$ex->getMessage()}</info>"); + } + while (!$connection->isConnected()) { + try { + $connection->connect(); + } catch (\Exception $ex) { + $output->writeln("<info>Error while re-connecting to database: {$ex->getMessage()}</info>"); + sleep(60); + } + } + return $connection; + } + } diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index cf9f43f2d59..0a4812f3a81 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -385,8 +385,9 @@ describe('OCA.Files.FileList tests', function() { $summary = $('#filestable .summary'); expect($summary.hasClass('hidden')).toEqual(false); // yes, ugly... - expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.connector').hasClass('hidden')).toEqual(true); expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false); expect($summary.find('.filesize').text()).toEqual('12 B'); expect($('#filestable thead th').hasClass('hidden')).toEqual(false); @@ -456,7 +457,8 @@ describe('OCA.Files.FileList tests', function() { $summary = $('#filestable .summary'); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual('1 folder and 2 files'); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('2 files'); expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false); expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false); expect($summary.find('.filesize').text()).toEqual('69 KB'); @@ -511,7 +513,8 @@ describe('OCA.Files.FileList tests', function() { $summary = $('#filestable .summary'); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual('1 folder and 1 file'); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(false); expect($summary.find('.fileinfo').hasClass('hidden')).toEqual(false); expect($summary.find('.filesize').text()).toEqual('57 KB'); @@ -677,12 +680,14 @@ describe('OCA.Files.FileList tests', function() { deferredRename.resolve(201); - expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('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'); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('3 files'); }); it('Shows busy state while rename in progress', function() { var $tr; @@ -856,11 +861,14 @@ describe('OCA.Files.FileList tests', function() { }); var $tr = fileList.add(fileData); - expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); var model = fileList.getModelForFile('test file'); model.set({size: '100'}); - expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); + + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); }); }) describe('List rendering', function() { @@ -877,7 +885,8 @@ describe('OCA.Files.FileList tests', function() { fileList.setFiles(testFiles); $summary = $('#filestable .summary'); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual('1 folder and 3 files'); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('3 files'); expect($summary.find('.filesize').text()).toEqual('69 KB'); }); it('shows headers, summary and hide empty content message after setting files', function(){ @@ -962,10 +971,12 @@ describe('OCA.Files.FileList tests', function() { fileList.setFiles([testFiles[0]]); $summary = $('#filestable .summary'); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); fileList.remove('unexist.txt'); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual('0 folders and 1 file'); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); }); }); describe('Filtered list rendering', function() { @@ -987,14 +998,18 @@ describe('OCA.Files.FileList tests', function() { expect($('#fileList tr:not(.hidden)').length).toEqual(3); expect(fileList.files.length).toEqual(4); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'"); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('2 files'); + expect($summary.find('.filter').text()).toEqual(" match 'e'"); expect($nofilterresults.hasClass('hidden')).toEqual(true); fileList.setFilter('ee'); expect($('#fileList tr:not(.hidden)').length).toEqual(1); expect(fileList.files.length).toEqual(4); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'"); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); + expect($summary.find('.filter').text()).toEqual(" matches 'ee'"); expect($nofilterresults.hasClass('hidden')).toEqual(true); fileList.setFilter('eee'); @@ -1007,21 +1022,26 @@ describe('OCA.Files.FileList tests', function() { expect($('#fileList tr:not(.hidden)').length).toEqual(1); expect(fileList.files.length).toEqual(4); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches 'ee'"); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); + expect($summary.find('.filter').text()).toEqual(" matches 'ee'"); expect($nofilterresults.hasClass('hidden')).toEqual(true); fileList.setFilter('e'); expect($('#fileList tr:not(.hidden)').length).toEqual(3); expect(fileList.files.length).toEqual(4); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("1 folder and 2 files match 'e'"); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('2 files'); + expect($summary.find('.filter').text()).toEqual(" match 'e'"); expect($nofilterresults.hasClass('hidden')).toEqual(true); fileList.setFilter(''); expect($('#fileList tr:not(.hidden)').length).toEqual(4); expect(fileList.files.length).toEqual(4); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("1 folder and 3 files"); + expect($summary.find('.dirinfo').text()).toEqual('1 folder'); + expect($summary.find('.fileinfo').text()).toEqual('3 files'); expect($nofilterresults.hasClass('hidden')).toEqual(true); }); it('filters the list of non-rendered rows using filter()', function() { @@ -1032,7 +1052,9 @@ describe('OCA.Files.FileList tests', function() { fileList.setFilter('63'); expect($('#fileList tr:not(.hidden)').length).toEqual(1); expect($summary.hasClass('hidden')).toEqual(false); - expect($summary.find('.info').text()).toEqual("0 folders and 1 file matches '63'"); + expect($summary.find('.dirinfo').hasClass('hidden')).toEqual(true); + expect($summary.find('.fileinfo').text()).toEqual('1 file'); + expect($summary.find('.filter').text()).toEqual(" matches '63'"); expect($nofilterresults.hasClass('hidden')).toEqual(true); }); it('hides the emptyfiles notice when using filter()', function() { @@ -1654,6 +1676,18 @@ describe('OCA.Files.FileList tests', function() { $('#fileList tr td.filename input:checkbox').click(); expect($('.select-all').prop('checked')).toEqual(false); }); + it('Selecting all files also selects hidden files when invisible', function() { + filesConfig.set('showhidden', false); + var $tr = fileList.add(new FileInfo({ + name: '.hidden', + type: 'dir', + mimetype: 'httpd/unix-directory', + size: 150 + })); + $('.select-all').click(); + expect($tr.find('td.filename input:checkbox').prop('checked')).toEqual(true); + expect(_.pluck(fileList.getSelectedFiles(), 'name')).toContain('.hidden'); + }); it('Clicking "select all" will select/deselect all files', function() { fileList.setFiles(generateFiles(0, 41)); $('.select-all').click(); @@ -1731,6 +1765,44 @@ describe('OCA.Files.FileList tests', function() { fileList.findFileEl('One.txt').find('input:checkbox').click().click(); expect($summary.text()).toEqual('Name'); }); + it('Displays the number of hidden files in selection summary if hidden files are invisible', function() { + filesConfig.set('showhidden', false); + var $tr = fileList.add(new FileInfo({ + name: '.hidden', + type: 'dir', + mimetype: 'httpd/unix-directory', + size: 150 + })); + $('.select-all').click(); + var $summary = $('#headerName a.name>span:first'); + expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)'); + }); + it('Does not displays the number of hidden files in selection summary if hidden files are visible', function() { + filesConfig.set('showhidden', true); + var $tr = fileList.add(new FileInfo({ + name: '.hidden', + type: 'dir', + mimetype: 'httpd/unix-directory', + size: 150 + })); + $('.select-all').click(); + var $summary = $('#headerName a.name>span:first'); + expect($summary.text()).toEqual('2 folders and 3 files'); + }); + it('Toggling hidden file visibility updates selection summary', function() { + filesConfig.set('showhidden', false); + var $tr = fileList.add(new FileInfo({ + name: '.hidden', + type: 'dir', + mimetype: 'httpd/unix-directory', + size: 150 + })); + $('.select-all').click(); + var $summary = $('#headerName a.name>span:first'); + expect($summary.text()).toEqual('2 folders and 3 files (including 1 hidden)'); + filesConfig.set('showhidden', true); + expect($summary.text()).toEqual('2 folders and 3 files'); + }); it('Select/deselect files shows/hides file actions', function() { var $actions = $('#headerName .selectedActions'); var $checkbox = fileList.findFileEl('One.txt').find('input:checkbox'); diff --git a/apps/files/tests/js/filesummarySpec.js b/apps/files/tests/js/filesummarySpec.js index ec94c28acb6..e3f24d9ad43 100644 --- a/apps/files/tests/js/filesummarySpec.js +++ b/apps/files/tests/js/filesummarySpec.js @@ -39,7 +39,8 @@ describe('OCA.Files.FileSummary tests', function() { totalSize: 256000 }); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('5 folders and 2 files'); + expect($container.find('.dirinfo').text()).toEqual('5 folders'); + expect($container.find('.fileinfo').text()).toEqual('2 files'); expect($container.find('.filesize').text()).toEqual('250 KB'); }); it('hides summary when no files or folders', function() { @@ -62,7 +63,8 @@ describe('OCA.Files.FileSummary tests', function() { s.add({type: 'dir', size: 100}); s.update(); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('6 folders and 3 files'); + expect($container.find('.dirinfo').text()).toEqual('6 folders'); + expect($container.find('.fileinfo').text()).toEqual('3 files'); expect($container.find('.filesize').text()).toEqual('500 KB'); expect(s.summary.totalDirs).toEqual(6); expect(s.summary.totalFiles).toEqual(3); @@ -79,7 +81,8 @@ describe('OCA.Files.FileSummary tests', function() { s.remove({type: 'dir', size: 100}); s.update(); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('4 folders and 1 file'); + expect($container.find('.dirinfo').text()).toEqual('4 folders'); + expect($container.find('.fileinfo').text()).toEqual('1 file'); expect($container.find('.filesize').text()).toEqual('125 KB'); expect(s.summary.totalDirs).toEqual(4); expect(s.summary.totalFiles).toEqual(1); @@ -95,7 +98,9 @@ describe('OCA.Files.FileSummary tests', function() { filter: 'foo' }); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('5 folders and 2 files match \'foo\''); + expect($container.find('.dirinfo').text()).toEqual('5 folders'); + expect($container.find('.fileinfo').text()).toEqual('2 files'); + expect($container.find('.filter').text()).toEqual(' match \'foo\''); expect($container.find('.filesize').text()).toEqual('250 KB'); }); it('hides filtered summary when no files or folders', function() { @@ -122,7 +127,9 @@ describe('OCA.Files.FileSummary tests', function() { s.add({name: 'foo', type: 'dir', size: 102}); s.update(); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('6 folders and 3 files match \'foo\''); + expect($container.find('.dirinfo').text()).toEqual('6 folders'); + expect($container.find('.fileinfo').text()).toEqual('3 files'); + expect($container.find('.filter').text()).toEqual(' match \'foo\''); expect($container.find('.filesize').text()).toEqual('500 KB'); expect(s.summary.totalDirs).toEqual(6); expect(s.summary.totalFiles).toEqual(3); @@ -142,7 +149,9 @@ describe('OCA.Files.FileSummary tests', function() { s.remove({name: 'foo', type: 'dir', size: 98}); s.update(); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('4 folders and 1 file match \'foo\''); + expect($container.find('.dirinfo').text()).toEqual('4 folders'); + expect($container.find('.fileinfo').text()).toEqual('1 file'); + expect($container.find('.filter').text()).toEqual(' match \'foo\''); expect($container.find('.filesize').text()).toEqual('125 KB'); expect(s.summary.totalDirs).toEqual(4); expect(s.summary.totalFiles).toEqual(1); @@ -158,7 +167,8 @@ describe('OCA.Files.FileSummary tests', function() { s.add({type: 'dir', size: -1}); s.update(); expect($container.hasClass('hidden')).toEqual(false); - expect($container.find('.info').text()).toEqual('1 folder and 0 files'); + expect($container.find('.dirinfo').text()).toEqual('1 folder'); + expect($container.find('.fileinfo').hasClass('hidden')).toEqual(true); expect($container.find('.filesize').text()).toEqual('Pending'); expect(s.summary.totalDirs).toEqual(1); expect(s.summary.totalFiles).toEqual(0); @@ -175,10 +185,56 @@ describe('OCA.Files.FileSummary tests', function() { s.remove({type: 'dir', size: -1}); s.update(); expect($container.hasClass('hidden')).toEqual(true); - expect($container.find('.info').text()).toEqual('0 folders and 0 files'); - expect($container.find('.filesize').text()).toEqual('0 B'); expect(s.summary.totalDirs).toEqual(0); expect(s.summary.totalFiles).toEqual(0); expect(s.summary.totalSize).toEqual(0); }); + describe('hidden files', function() { + var config; + var summary; + + beforeEach(function() { + config = new OC.Backbone.Model(); + summary = new FileSummary($container, { + config: config + }); + }); + + it('renders hidden count section when hidden files are hidden', function() { + config.set('showhidden', false); + summary.add({name: 'abc', type: 'file', size: 256000}); + summary.add({name: 'def', type: 'dir', size: 100}); + summary.add({name: '.hidden', type: 'dir', size: 512000}); + summary.update(); + expect($container.hasClass('hidden')).toEqual(false); + expect($container.find('.dirinfo').text()).toEqual('2 folders'); + expect($container.find('.fileinfo').text()).toEqual('1 file'); + expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(false); + expect($container.find('.hiddeninfo').text()).toEqual(' (including 1 hidden)'); + expect($container.find('.filesize').text()).toEqual('750 KB'); + }); + it('does not render hidden count section when hidden files exist but are visible', function() { + config.set('showhidden', true); + summary.add({name: 'abc', type: 'file', size: 256000}); + summary.add({name: 'def', type: 'dir', size: 100}); + summary.add({name: '.hidden', type: 'dir', size: 512000}); + summary.update(); + expect($container.hasClass('hidden')).toEqual(false); + expect($container.find('.dirinfo').text()).toEqual('2 folders'); + expect($container.find('.fileinfo').text()).toEqual('1 file'); + expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true); + expect($container.find('.filesize').text()).toEqual('750 KB'); + }); + it('does not render hidden count section when no hidden files exist', function() { + config.set('showhidden', false); + summary.add({name: 'abc', type: 'file', size: 256000}); + summary.add({name: 'def', type: 'dir', size: 100}); + summary.update(); + expect($container.hasClass('hidden')).toEqual(false); + expect($container.find('.dirinfo').text()).toEqual('1 folder'); + expect($container.find('.fileinfo').text()).toEqual('1 file'); + expect($container.find('.hiddeninfo').hasClass('hidden')).toEqual(true); + expect($container.find('.filesize').text()).toEqual('250 KB'); + }); + }); }); diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 3ceca430424..e1875fe2394 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -106,6 +106,16 @@ class Shared extends \OC\Files\Storage\Wrapper\Jail implements ISharedStorage { } /** + * @inheritdoc + */ + public function instanceOfStorage($class) { + if (in_array($class, ['\OC\Files\Storage\Home', '\OC\Files\ObjectStore\HomeObjectStoreStorage'])) { + return false; + } + return parent::instanceOfStorage($class); + } + + /** * @return string */ public function getShareId() { diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php index fbb4c904773..b4e3a95710f 100644 --- a/apps/theming/lib/Controller/ThemingController.php +++ b/apps/theming/lib/Controller/ThemingController.php @@ -304,6 +304,13 @@ class ThemingController extends Controller { $responseCss = ''; $color = $this->config->getAppValue($this->appName, 'color'); $elementColor = $this->util->elementColor($color); + + if($this->util->invertTextColor($color)) { + $textColor = '#000000'; + } else { + $textColor = '#ffffff'; + } + if($color !== '') { $responseCss .= sprintf( '#body-user #header,#body-settings #header,#body-public #header,#body-login,.searchbox input[type="search"]:focus,.searchbox input[type="search"]:active,.searchbox input[type="search"]:valid {background-color: %s}' . "\n", @@ -321,19 +328,26 @@ class ThemingController extends Controller { 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($elementColor).'\');' . "}\n"; $responseCss .= '.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary,' . - '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active,' . - '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . - '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . - '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active {' . 'border: 1px solid '.$elementColor.';'. 'background-color: '.$elementColor.';'. - 'opacity: 0.8' . + 'opacity: 0.8;' . + 'color: ' . $textColor . ';'. "}\n" . '.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover,' . '.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus {' . 'border: 1px solid '.$elementColor.';'. 'background-color: '.$elementColor.';'. 'opacity: 1.0;' . + 'color: ' . $textColor . ';'. + "}\n" . + '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . + '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . + '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. + 'opacity: 0.4;' . + 'color: '.$textColor.';'. "}\n"; $responseCss .= '.ui-widget-header { border: 1px solid ' . $color . '; background: '. $color . '; color: #ffffff;' . "}\n"; $responseCss .= '.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {' . diff --git a/apps/theming/lib/Settings/Admin.php b/apps/theming/lib/Settings/Admin.php index afd74ced217..22ab5650e5b 100644 --- a/apps/theming/lib/Settings/Admin.php +++ b/apps/theming/lib/Settings/Admin.php @@ -61,7 +61,7 @@ class Admin implements ISettings { $theme = $this->config->getSystemValue('theme', ''); if ($theme !== '') { $themable = false; - $errorMessage = $this->l->t('You already use a custom theme'); + $errorMessage = $this->l->t('You are already using a custom theme'); } $parameters = [ diff --git a/apps/theming/tests/Controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php index d053d8c1a1c..193e0bdcb4b 100644 --- a/apps/theming/tests/Controller/ThemingControllerTest.php +++ b/apps/theming/tests/Controller/ThemingControllerTest.php @@ -437,19 +437,26 @@ class ThemingControllerTest extends TestCase { 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($color).'\');' . "}\n"; $expectedData .= '.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary,' . - '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active,' . - '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . - '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . - '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . - 'border: 1px solid '.$color .';'. + '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active {' . + 'border: 1px solid '.$color.';'. 'background-color: '.$color.';'. - 'opacity: 0.8' . + 'opacity: 0.8;' . + 'color: #ffffff;'. "}\n" . '.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover,' . '.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus {' . 'border: 1px solid '.$color.';'. 'background-color: '.$color.';'. 'opacity: 1.0;' . + 'color: #ffffff;'. + "}\n" . + '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . + '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . + '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + 'border: 1px solid '.$color.';'. + 'background-color: '.$color.';'. + 'opacity: 0.4;' . + 'color: #ffffff;'. "}\n"; $expectedData .= '.ui-widget-header { border: 1px solid ' . $color . '; background: '. $color . '; color: #ffffff;' . "}\n"; $expectedData .= '.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {' . @@ -520,19 +527,26 @@ class ThemingControllerTest extends TestCase { 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton('#555555').'\');' . "}\n"; $expectedData .= '.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary,' . - '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active,' . - '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . - '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . - '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . - 'border: 1px solid #555555;'. - 'background-color: #555555;'. - 'opacity: 0.8' . + '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active {' . + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. + 'opacity: 0.8;' . + 'color: #000000;'. "}\n" . '.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover,' . '.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus {' . - 'border: 1px solid #555555;'. - 'background-color: #555555;'. + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. 'opacity: 1.0;' . + 'color: #000000;'. + "}\n" . + '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . + '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . + '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. + 'opacity: 0.4;' . + 'color: #000000;'. "}\n"; $expectedData .= '.ui-widget-header { border: 1px solid ' . $color . '; background: '. $color . '; color: #ffffff;' . "}\n"; $expectedData .= '.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {' . @@ -689,19 +703,26 @@ class ThemingControllerTest extends TestCase { 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton($color).'\');' . "}\n"; $expectedData .= '.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary,' . - '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active,' . - '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . - '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . - '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . - 'border: 1px solid '.$color .';'. + '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active {' . + 'border: 1px solid '.$color.';'. 'background-color: '.$color.';'. - 'opacity: 0.8' . + 'opacity: 0.8;' . + 'color: #ffffff;'. "}\n" . '.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover,' . '.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus {' . 'border: 1px solid '.$color.';'. 'background-color: '.$color.';'. 'opacity: 1.0;' . + 'color: #ffffff;'. + "}\n" . + '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . + '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . + '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + 'border: 1px solid '.$color.';'. + 'background-color: '.$color.';'. + 'opacity: 0.4;' . + 'color: #ffffff;'. "}\n"; $expectedData .= '.ui-widget-header { border: 1px solid ' . $color . '; background: '. $color . '; color: #ffffff;' . "}\n"; $expectedData .= '.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {' . @@ -789,19 +810,26 @@ class ThemingControllerTest extends TestCase { 'background-image: url(\'data:image/svg+xml;base64,'.$this->util->generateRadioButton('#555555').'\');' . "}\n"; $expectedData .= '.primary, input[type="submit"].primary, input[type="button"].primary, button.primary, .button.primary,' . - '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active,' . - '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . - '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . - '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . - 'border: 1px solid #555555;'. - 'background-color: #555555;'. - 'opacity: 0.8' . + '.primary:active, input[type="submit"].primary:active, input[type="button"].primary:active, button.primary:active, .button.primary:active {' . + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. + 'opacity: 0.8;' . + 'color: #000000;'. "}\n" . '.primary:hover, input[type="submit"].primary:hover, input[type="button"].primary:hover, button.primary:hover, .button.primary:hover,' . '.primary:focus, input[type="submit"].primary:focus, input[type="button"].primary:focus, button.primary:focus, .button.primary:focus {' . - 'border: 1px solid #555555;'. - 'background-color: #555555;'. + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. 'opacity: 1.0;' . + 'color: #000000;'. + "}\n" . + '.primary:disabled, input[type="submit"].primary:disabled, input[type="button"].primary:disabled, button.primary:disabled, .button.primary:disabled,' . + '.primary:disabled:hover, input[type="submit"].primary:disabled:hover, input[type="button"].primary:disabled:hover, button.primary:disabled:hover, .button.primary:disabled:hover,' . + '.primary:disabled:focus, input[type="submit"].primary:disabled:focus, input[type="button"].primary:disabled:focus, button.primary:disabled:focus, .button.primary:disabled:focus {' . + 'border: 1px solid '.$elementColor.';'. + 'background-color: '.$elementColor.';'. + 'opacity: 0.4;' . + 'color: #000000;'. "}\n"; $expectedData .= '.ui-widget-header { border: 1px solid ' . $color . '; background: '. $color . '; color: #ffffff;' . "}\n"; $expectedData .= '.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active {' . diff --git a/apps/theming/tests/Settings/AdminTest.php b/apps/theming/tests/Settings/AdminTest.php index 73339cf86b7..d4f5490d352 100644 --- a/apps/theming/tests/Settings/AdminTest.php +++ b/apps/theming/tests/Settings/AdminTest.php @@ -112,8 +112,8 @@ class AdminTest extends TestCase { $this->l10n ->expects($this->once()) ->method('t') - ->with('You already use a custom theme') - ->willReturn('You already use a custom theme'); + ->with('You are already using a custom theme') + ->willReturn('You are already using a custom theme'); $this->themingDefaults ->expects($this->once()) ->method('getEntity') @@ -137,7 +137,7 @@ class AdminTest extends TestCase { ->willReturn('/my/route'); $params = [ 'themable' => false, - 'errorMessage' => 'You already use a custom theme', + 'errorMessage' => 'You are already using a custom theme', 'name' => 'MyEntity', 'url' => 'https://example.com', 'slogan' => 'MySlogan', diff --git a/apps/user_ldap/ajax/getConfiguration.php b/apps/user_ldap/ajax/getConfiguration.php index 0537349bbeb..34ac97f29e7 100644 --- a/apps/user_ldap/ajax/getConfiguration.php +++ b/apps/user_ldap/ajax/getConfiguration.php @@ -32,4 +32,9 @@ OCP\JSON::callCheck(); $prefix = (string)$_POST['ldap_serverconfig_chooser']; $ldapWrapper = new OCA\User_LDAP\LDAP(); $connection = new \OCA\User_LDAP\Connection($ldapWrapper, $prefix); -OCP\JSON::success(array('configuration' => $connection->getConfiguration())); +$configuration = $connection->getConfiguration(); +if (isset($configuration['ldap_agent_password']) && $configuration['ldap_agent_password'] !== '') { + // hide password + $configuration['ldap_agent_password'] = '**PASSWORD SET**'; +} +OCP\JSON::success(array('configuration' => $configuration)); diff --git a/apps/user_ldap/ajax/testConfiguration.php b/apps/user_ldap/ajax/testConfiguration.php index f73e11d3e21..0416e49767b 100644 --- a/apps/user_ldap/ajax/testConfiguration.php +++ b/apps/user_ldap/ajax/testConfiguration.php @@ -33,12 +33,19 @@ OCP\JSON::callCheck(); $l = \OC::$server->getL10N('user_ldap'); $ldapWrapper = new OCA\User_LDAP\LDAP(); -$connection = new \OCA\User_LDAP\Connection($ldapWrapper, '', null); -//needs to be true, otherwise it will also fail with an irritating message -$_POST['ldap_configuration_active'] = 1; +$connection = new \OCA\User_LDAP\Connection($ldapWrapper, $_POST['ldap_serverconfig_chooser']); + try { - if ($connection->setConfiguration($_POST)) { + $configurationOk = true; + $conf = $connection->getConfiguration(); + if ($conf['ldap_configuration_active'] === '0') { + //needs to be true, otherwise it will also fail with an irritating message + $conf['ldap_configuration_active'] = '1'; + $configurationOk = $connection->setConfiguration($conf); + } + if ($configurationOk) { + //Configuration is okay /* * Clossing the session since it won't be used from this point on. There might be a potential * race condition if a second request is made: either this request or the other might not @@ -46,7 +53,6 @@ try { * problem with that other than the extra connection. */ \OC::$server->getSession()->close(); - //Configuration is okay if ($connection->bind()) { /* * This shiny if block is an ugly hack to find out whether anonymous diff --git a/apps/user_ldap/js/wizard/configModel.js b/apps/user_ldap/js/wizard/configModel.js index c3f1e85b592..3732409c3e1 100644 --- a/apps/user_ldap/js/wizard/configModel.js +++ b/apps/user_ldap/js/wizard/configModel.js @@ -318,7 +318,7 @@ OCA = OCA || {}; */ requestConfigurationTest: function() { var url = OC.generateUrl('apps/user_ldap/ajax/testConfiguration.php'); - var params = OC.buildQueryString(this.configuration); + var params = OC.buildQueryString({ldap_serverconfig_chooser: this.configID}); var model = this; $.post(url, params, function(result) { model._processTestResult(model, result) }); //TODO: make sure only one test is running at a time diff --git a/apps/user_ldap/js/wizard/view.js b/apps/user_ldap/js/wizard/view.js index 9c7e2132dc6..1a89eba3515 100644 --- a/apps/user_ldap/js/wizard/view.js +++ b/apps/user_ldap/js/wizard/view.js @@ -271,7 +271,7 @@ OCA = OCA || {}; * requests a configuration test */ onTestButtonClick: function() { - this.configModel.requestWizard('ldap_action_test_connection', this.configModel.configuration); + this.configModel.requestWizard('ldap_action_test_connection', {ldap_serverconfig_chooser: this.configModel.configID}); }, /** |