diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index 40248360c29ca840895993136bdb7ee9c8f5dbac..bedca77a6f7f92bc7d29ff78fd3b122fda6cf8e8 100644 --- a/apps/files/css/files.scss +++ b/apps/files/css/files.scss @@ -129,6 +129,7 @@ } .nav-icon-sharingin, .nav-icon-sharingout, +.nav-icon-pendingshares, .nav-icon-shareoverview { @include icon-color('share', 'files', $color-black); } diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index fd6ec5aaa3c1db3af4dd8ff00f918067b8c75976..f07af2a2fe5e4bcda92a272f8019e6dbad9187f8 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -288,6 +288,13 @@ class ViewControllerTest extends TestCase { 'order' => 19, 'name' => \OC::$server->getL10N('files_sharing')->t('Deleted shares'), ], + [ + 'id' => 'pendingshares', + 'appname' => 'files_sharing', + 'script' => 'list.php', + 'order' => 19, + 'name' => \OC::$server->getL10N('files_sharing')->t('Pending shares'), + ], ], 'active' => false, 'icon' => '', @@ -348,6 +355,10 @@ class ViewControllerTest extends TestCase { 'id' => 'deletedshares', 'content' => null, ], + 'pendingshares' => [ + 'id' => 'pendingshares', + 'content' => null + ], 'shareoverview' => [ 'id' => 'shareoverview', 'content' => null, diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index ad3d11fc3a165e4f11ad0cbbcb03b2daad5bec87..1346e0c68991cc48030f1aafac9e168583ffd2c6 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -75,6 +75,11 @@ return [ 'url' => '/api/v1/shares', 'verb' => 'POST', ], + [ + 'name' => 'ShareAPI#pendingShares', + 'url' => '/api/v1/shares/pending', + 'verb' => 'GET', + ], [ 'name' => 'ShareAPI#getShare', 'url' => '/api/v1/shares/{id}', diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 6cf7a805ada0137f2dd8adc8825dcf22d8ff74e4..1a686ee228d60dca9a6ed8a64642d577ce8b15e4 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -22,6 +22,7 @@ OCA.Sharing.App = { _inFileList: null, _outFileList: null, _overviewFileList: null, + _pendingFileList: null, initSharingIn: function($el) { if (this._inFileList) { @@ -129,6 +130,33 @@ OCA.Sharing.App = { return this._deletedFileList }, + initSharingPening: function($el) { + if (this._pendingFileList) { + return this._pendingFileList + } + this._pendingFileList = new OCA.Sharing.FileList( + $el, + { + id: 'shares.pending', + showPending: true, + sharedWithUser: true, + fileActions: this._acceptShareAction(), + config: OCA.Files.App.getFilesConfig(), + // The file list is created when a "show" event is handled, so + // it should be marked as "shown" like it would have been done + // if handling the event with the file list already created. + shown: true, + } + ) + + this._extendFileList(this._pendingFileList) + this._pendingFileList.appName = t('files_sharing', 'Pending shares') + this._pendingFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + + '<h2>' + t('files_sharing', 'No pending shares') + '</h2>' + + '<p>' + t('files_sharing', 'Shares you have received but not confirmed will show up here') + '</p>') + return this._pendingFileList + }, + initShareingOverview: function($el) { if (this._overviewFileList) { return this._overviewFileList @@ -178,6 +206,12 @@ OCA.Sharing.App = { } }, + removeSharingPending: function() { + if (this._pendingFileList) { + this._pendingFileList.$fileList.empty() + } + }, + removeSharingOverview: function() { if (this._overviewFileList) { this._overviewFileList.$fileList.empty() @@ -249,6 +283,47 @@ OCA.Sharing.App = { return fileActions }, + _acceptShareAction: function() { + const fileActions = new OCA.Files.FileActions() + fileActions.registerAction({ + name: 'Accept share', + displayName: t('files_sharing', 'Accept share'), + mime: 'all', + permissions: OC.PERMISSION_ALL, + iconClass: 'icon-checkmark', + type: OCA.Files.FileActions.TYPE_INLINE, + actionHandler: function(fileName, context) { + const shareId = context.$file.data('shareId') + $.post(OC.linkToOCS('apps/files_sharing/api/v1/shares/pending', 2) + shareId) + .success(function(result) { + context.fileList.remove(context.fileInfoModel.attributes.name) + }).fail(function() { + OC.Notification.showTemporary(t('files_sharing', 'Something happened. Unable to accept the share.')) + }) + }, + }) + fileActions.registerAction({ + name: 'Reject share', + displayName: t('files_sharing', 'Reject share'), + mime: 'all', + permissions: OC.PERMISSION_ALL, + iconClass: 'icon-close', + type: OCA.Files.FileActions.TYPE_INLINE, + actionHandler: function(fileName, context) { + const shareId = context.$file.data('shareId') + $.ajax({ + url: OC.linkToOCS('apps/files_sharing/api/v1/shares', 2) + shareId, + type: 'DELETE', + }).success(function(result) { + context.fileList.remove(context.fileInfoModel.attributes.name) + }).fail(function() { + OC.Notification.showTemporary(t('files_sharing', 'Something happened. Unable to reject the share.')) + }) + }, + }) + return fileActions + }, + _onActionsUpdated: function(ev) { _.each([this._inFileList, this._outFileList, this._linkFileList], function(list) { if (!list) { @@ -297,6 +372,12 @@ $(document).ready(function() { $('#app-content-deletedshares').on('hide', function() { OCA.Sharing.App.removeSharingDeleted() }) + $('#app-content-pendingshares').on('show', function(e) { + OCA.Sharing.App.initSharingPening($(e.target)) + }) + $('#app-content-pendingshares').on('hide', function() { + OCA.Sharing.App.removeSharingPending() + }) $('#app-content-shareoverview').on('show', function(e) { OCA.Sharing.App.initShareingOverview($(e.target)) }) diff --git a/apps/files_sharing/js/dist/files_sharing.js b/apps/files_sharing/js/dist/files_sharing.js index e89a4075e70d9640847db1b8a46e71ded47199e4..ee608f58f6c8139bd8b19030585da22709209185 100644 Binary files a/apps/files_sharing/js/dist/files_sharing.js and b/apps/files_sharing/js/dist/files_sharing.js differ diff --git a/apps/files_sharing/js/dist/files_sharing.js.map b/apps/files_sharing/js/dist/files_sharing.js.map index 208ceb155b43f2e2f8ace596d642549230536cfd..447bb7f9afe975d695dfa5095c5f2cd0e1af5dfe 100644 Binary files a/apps/files_sharing/js/dist/files_sharing.js.map and b/apps/files_sharing/js/dist/files_sharing.js.map differ diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 5b198300589301205d9b23a7da4f32693e6d0173..f40a1f1a853f50a91c892cfc73c7890272108c50 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -39,6 +39,7 @@ _sharedWithUser: false, _linksOnly: false, _showDeleted: false, + _showPending: false, _clientSideSort: true, _allowSelection: false, _isOverview: false, @@ -62,6 +63,9 @@ if (options && options.showDeleted) { this._showDeleted = true } + if (options && options.showPending) { + this._showPending = true + } if (options && options.isOverview) { this._isOverview = true } @@ -87,7 +91,7 @@ var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE $tr.attr('data-permissions', permission) } - if (this._showDeleted) { + if (this._showDeleted || this._showPending) { var permission = fileData.permissions $tr.attr('data-share-permissions', permission) } @@ -196,6 +200,18 @@ } } + var pendingShares = { + url: OC.linkToOCS('apps/files_sharing/api/v1/shares', 2) + 'pending', + /* jshint camelcase: false */ + data: { + format: 'json' + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true') + } + } + var shares = { url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', /* jshint camelcase: false */ @@ -227,6 +243,8 @@ // and make sure we have 2 promises if (this._showDeleted) { promises.push($.ajax(deletedShares)) + } else if (this._showPending) { + promises.push($.ajax(pendingShares)) } else { promises.push($.ajax(shares)) diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index 48802617b4f35ab3fd0dbc6c5ba756eb93ca851f..5a18cce8e4974a98bfdb5701e91df2aa80bcbd68 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -278,6 +278,15 @@ class Application extends App { 'name' => $l->t('Deleted shares'), ]); + array_push($sharingSublistArray, [ + 'id' => 'pendingshares', + 'appname' => 'files_sharing', + 'script' => 'list.php', + 'order' => 19, + 'name' => $l->t('Pending shares'), + ]); + + // show_Quick_Access stored as string \OCA\Files\App::getNavigationManager()->add([ 'id' => 'shareoverview', diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 906eb82221bf40f5c42b61c9a18e68ff86f6f98d..b71d3dac3a76f61855a8f97dc7f74e81e2587d7c 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -1079,6 +1079,56 @@ class ShareAPIController extends OCSController { return new DataResponse($this->formatShare($share)); } + /** + * @NoAdminRequired + */ + public function pendingShares(): DataResponse { + $pendingShares = []; + + $shareTypes = [ + IShare::TYPE_USER, + IShare::TYPE_GROUP + ]; + + foreach ($shareTypes as $shareType) { + $shares = $this->shareManager->getSharedWith($this->currentUser, $shareType, null, -1, 0); + + foreach ($shares as $share) { + if ($share->getStatus() === IShare::STATUS_PENDING || $share->getStatus() === IShare::STATUS_REJECTED) { + $pendingShares[] = $share; + } + } + } + + $result = array_filter(array_map(function (IShare $share) { + $userFolder = $this->rootFolder->getUserFolder($share->getSharedBy()); + $nodes = $userFolder->getById($share->getNodeId()); + if (empty($nodes)) { + // fallback to guessing the path + $node = $userFolder->get($share->getTarget()); + if ($node === null || $share->getTarget() === '') { + return null; + } + } else { + $node = $nodes[0]; + } + + try { + $formattedShare = $this->formatShare($share, $node); + $formattedShare['status'] = $share->getStatus(); + $formattedShare['path'] = $share->getNode()->getName(); + $formattedShare['permissions'] = 0; + return $formattedShare; + } catch (NotFoundException $e) { + return null; + } + }, $pendingShares), function ($entry) { + return $entry !== null; + }); + + return new DataResponse($result); + } + /** * @NoAdminRequired *