diff options
author | Morris Jobke <hey@morrisjobke.de> | 2018-07-05 13:31:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-07-05 13:31:58 +0200 |
commit | cbfcfb236f3e8ace6c64ab5a654b9a331a3ce1c0 (patch) | |
tree | b86e43db95907d58f56fdb885316a01fdbfa7bac | |
parent | 86d9528bc93402a18a3202bb3ff17c812b94402e (diff) | |
parent | f39dfc7ab8ced56de4935a56d87334b211df9b5e (diff) | |
download | nextcloud-server-cbfcfb236f3e8ace6c64ab5a654b9a331a3ce1c0.tar.gz nextcloud-server-cbfcfb236f3e8ace6c64ab5a654b9a331a3ce1c0.zip |
Merge pull request #9909 from nextcloud/feature/2192/allow_group_share_undeletion
Add API to undelete delete group shares
-rw-r--r-- | apps/federatedfilesharing/lib/FederatedShareProvider.php | 5 | ||||
-rw-r--r-- | apps/files/css/files.scss | 3 | ||||
-rw-r--r-- | apps/files/img/unshare.svg | 1 | ||||
-rw-r--r-- | apps/files/tests/Controller/ViewControllerTest.php | 15 | ||||
-rw-r--r-- | apps/files_sharing/appinfo/app.php | 14 | ||||
-rw-r--r-- | apps/files_sharing/appinfo/routes.php | 13 | ||||
-rw-r--r-- | apps/files_sharing/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | apps/files_sharing/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | apps/files_sharing/js/app.js | 59 | ||||
-rw-r--r-- | apps/files_sharing/js/share.js | 16 | ||||
-rw-r--r-- | apps/files_sharing/js/sharedfilelist.js | 52 | ||||
-rw-r--r-- | apps/files_sharing/lib/Controller/DeletedShareAPIController.php | 168 | ||||
-rw-r--r-- | apps/files_sharing/lib/Controller/ShareAPIController.php | 1 | ||||
-rw-r--r-- | apps/sharebymail/lib/ShareByMailProvider.php | 5 | ||||
-rw-r--r-- | lib/private/Share20/DefaultShareProvider.php | 37 | ||||
-rw-r--r-- | lib/private/Share20/Manager.php | 27 | ||||
-rw-r--r-- | lib/public/Share/IManager.php | 29 | ||||
-rw-r--r-- | lib/public/Share/IShareProvider.php | 14 |
18 files changed, 443 insertions, 18 deletions
diff --git a/apps/federatedfilesharing/lib/FederatedShareProvider.php b/apps/federatedfilesharing/lib/FederatedShareProvider.php index 5a12f8e9be3..f81b826f12c 100644 --- a/apps/federatedfilesharing/lib/FederatedShareProvider.php +++ b/apps/federatedfilesharing/lib/FederatedShareProvider.php @@ -38,6 +38,7 @@ use OCP\IConfig; use OCP\IL10N; use OCP\ILogger; use OCP\IUserManager; +use OCP\Share\Exceptions\GenericShareException; use OCP\Share\IShare; use OCP\Share\IShareProvider; use OC\Share20\Exception\InvalidShare; @@ -585,6 +586,10 @@ class FederatedShareProvider implements IShareProvider { // TODO move this code over to this app } + public function restore(IShare $share, string $recipient): IShare { + throw new GenericShareException('not implemented'); + } + public function getSharesInFolder($userId, Folder $node, $reshares) { $qb = $this->dbConnection->getQueryBuilder(); diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index 01703df5bf1..24ecbf399e3 100644 --- a/apps/files/css/files.scss +++ b/apps/files/css/files.scss @@ -105,6 +105,9 @@ .nav-icon-trashbin { background-image: url('../img/delete.svg?v=1'); } +.nav-icon-deletedshares { + background-image: url('../img/unshare.svg?v=1'); +} #app-navigation .nav-files a.nav-icon-files { width: auto; diff --git a/apps/files/img/unshare.svg b/apps/files/img/unshare.svg new file mode 100644 index 00000000000..0c22ca64057 --- /dev/null +++ b/apps/files/img/unshare.svg @@ -0,0 +1 @@ +<svg width="16" height="16" version="1.1" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="m12.5 1a2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 0.003906 0.12891l-4.9023 2.4512a2.5 2.5 0 0 0-1.6016-0.58008 2.5 2.5 0 0 0-2.5 2.5 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 0.30469-0.021484l3.4395-1.7246-1.25-0.625a2.5 2.5 0 0 0 0.0058594-0.12891 2.5 2.5 0 0 0-0.0039062-0.12891l4.9023-2.4512a2.5 2.5 0 0 0 1.6016 0.58008 2.5 2.5 0 0 0 0.26562-0.013672l1.5625-0.7832a2.5 2.5 0 0 0 0.67188-1.7031 2.5 2.5 0 0 0-2.5-2.5zm0.25391 9.0156-3.7246 1.8672 0.97656 0.48828a2.5 2.5 0 0 0-0.005859 0.12891 2.5 2.5 0 0 0 2.5 2.5 2.5 2.5 0 0 0 2.5-2.5 2.5 2.5 0 0 0-2.2461-2.4844z"/><rect transform="rotate(-26.63)" x="-1.0586" y="11.891" width="11.687" height="2.0029" ry="0" style="paint-order:normal"/></svg> diff --git a/apps/files/tests/Controller/ViewControllerTest.php b/apps/files/tests/Controller/ViewControllerTest.php index a739e26bd90..eae627fd6a4 100644 --- a/apps/files/tests/Controller/ViewControllerTest.php +++ b/apps/files/tests/Controller/ViewControllerTest.php @@ -207,6 +207,17 @@ class ViewControllerTest extends TestCase { 'classes' => '', ], [ + 'id' => 'deletedshares', + 'appname' => 'files_sharing', + 'script' => 'list.php', + 'order' => 18, + 'name' => \OC::$server->getL10N('files_sharing')->t('Deleted shares'), + 'active' => false, + 'icon' => '', + 'type' => 'link', + 'classes' => '', + ], + [ 'id' => 'systemtagsfilter', 'appname' => 'systemtags', 'script' => 'list.php', @@ -270,6 +281,10 @@ class ViewControllerTest extends TestCase { 'content' => null, ], [ + 'id' => 'deletedshares', + 'content' => null, + ], + [ 'id' => 'systemtagsfilter', 'content' => null, ], diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index f5aa1fc09a1..4f5cf09befb 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -48,6 +48,9 @@ $eventDispatcher->addListener( ); $config = \OC::$server->getConfig(); +$shareManager = \OC::$server->getShareManager(); +$userSession = \OC::$server->getUserSession(); + if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') { \OCA\Files\App::getNavigationManager()->add(function () { $l = \OC::$server->getL10N('files_sharing'); @@ -59,6 +62,17 @@ if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') { 'name' => $l->t('Shared with you'), ]; }); + + \OCA\Files\App::getNavigationManager()->add(function () { + $l = \OC::$server->getL10N('files_sharing'); + return [ + 'id' => 'deletedshares', + 'appname' => 'files_sharing', + 'script' => 'list.php', + 'order' => 18, + 'name' => $l->t('Deleted shares'), + ]; + }); if (\OCP\Util::isSharingDisabledForUser() === false) { \OCA\Files\App::getNavigationManager()->add(function () { diff --git a/apps/files_sharing/appinfo/routes.php b/apps/files_sharing/appinfo/routes.php index 8e5110c6a16..eea6715e57d 100644 --- a/apps/files_sharing/appinfo/routes.php +++ b/apps/files_sharing/appinfo/routes.php @@ -74,6 +74,19 @@ return [ 'verb' => 'DELETE', ], /* + * Deleted Shares + */ + [ + 'name' => 'DeletedShareAPI#index', + 'url' => '/api/v1/deletedshares', + 'verb' => 'GET', + ], + [ + 'name' => 'DeletedShareAPI#undelete', + 'url' => '/api/v1/deletedshares/{id}', + 'verb' => 'POST', + ], + /* * OCS Sharee API */ [ diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php index e5a86bbd095..cb64d44a54d 100644 --- a/apps/files_sharing/composer/composer/autoload_classmap.php +++ b/apps/files_sharing/composer/composer/autoload_classmap.php @@ -22,6 +22,7 @@ return array( 'OCA\\Files_Sharing\\Capabilities' => $baseDir . '/../lib/Capabilities.php', 'OCA\\Files_Sharing\\Collaboration\\ShareRecipientSorter' => $baseDir . '/../lib/Collaboration/ShareRecipientSorter.php', 'OCA\\Files_Sharing\\Command\\CleanupRemoteStorages' => $baseDir . '/../lib/Command/CleanupRemoteStorages.php', + 'OCA\\Files_Sharing\\Controller\\DeletedShareAPIController' => $baseDir . '/../lib/Controller/DeletedShareAPIController.php', 'OCA\\Files_Sharing\\Controller\\ExternalSharesController' => $baseDir . '/../lib/Controller/ExternalSharesController.php', 'OCA\\Files_Sharing\\Controller\\PublicPreviewController' => $baseDir . '/../lib/Controller/PublicPreviewController.php', 'OCA\\Files_Sharing\\Controller\\RemoteController' => $baseDir . '/../lib/Controller/RemoteController.php', diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php index bf41ef45ea1..dcfd5c4fe4f 100644 --- a/apps/files_sharing/composer/composer/autoload_static.php +++ b/apps/files_sharing/composer/composer/autoload_static.php @@ -37,6 +37,7 @@ class ComposerStaticInitFiles_Sharing 'OCA\\Files_Sharing\\Capabilities' => __DIR__ . '/..' . '/../lib/Capabilities.php', 'OCA\\Files_Sharing\\Collaboration\\ShareRecipientSorter' => __DIR__ . '/..' . '/../lib/Collaboration/ShareRecipientSorter.php', 'OCA\\Files_Sharing\\Command\\CleanupRemoteStorages' => __DIR__ . '/..' . '/../lib/Command/CleanupRemoteStorages.php', + 'OCA\\Files_Sharing\\Controller\\DeletedShareAPIController' => __DIR__ . '/..' . '/../lib/Controller/DeletedShareAPIController.php', 'OCA\\Files_Sharing\\Controller\\ExternalSharesController' => __DIR__ . '/..' . '/../lib/Controller/ExternalSharesController.php', 'OCA\\Files_Sharing\\Controller\\PublicPreviewController' => __DIR__ . '/..' . '/../lib/Controller/PublicPreviewController.php', 'OCA\\Files_Sharing\\Controller\\RemoteController' => __DIR__ . '/..' . '/../lib/Controller/RemoteController.php', diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index e6c9159eda4..f63410bc9bf 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -92,6 +92,30 @@ OCA.Sharing.App = { return this._linkFileList; }, + initSharingDeleted: function($el) { + if (this._deletedFileList) { + return this._deletedFileList; + } + this._deletedFileList = new OCA.Sharing.FileList( + $el, + { + id: 'shares.deleted', + scrollContainer: $('#app-content'), + showDeleted: true, + sharedWithUser: true, + fileActions: this._restoreShareAction(), + config: OCA.Files.App.getFilesConfig() + } + ); + + this._extendFileList(this._deletedFileList); + this._deletedFileList.appName = t('files_sharing', 'Deleted shares'); + this._deletedFileList.$el.find('#emptycontent').html('<div class="icon-share"></div>' + + '<h2>' + t('files_sharing', 'No deleted shares') + '</h2>' + + '<p>' + t('files_sharing', 'Shares you deleted will show up here') + '</p>'); + return this._deletedFileList; + }, + removeSharingIn: function() { if (this._inFileList) { this._inFileList.$fileList.empty(); @@ -110,6 +134,12 @@ OCA.Sharing.App = { } }, + removeSharingDeleted: function() { + if (this._deletedFileList) { + this._deletedFileList.$fileList.empty(); + } + }, + /** * Destroy the app */ @@ -151,6 +181,29 @@ OCA.Sharing.App = { return fileActions; }, + _restoreShareAction: function() { + var fileActions = new OCA.Files.FileActions(); + fileActions.registerAction({ + name: 'Restore', + displayName: '', + altText: t('files_sharing', 'Restore share'), + mime: 'all', + permissions: OC.PERMISSION_ALL, + iconClass: 'icon-history', + type: OCA.Files.FileActions.TYPE_INLINE, + actionHandler: function(fileName, context) { + var shareId = context.$file.data('shareId'); + $.post(OC.linkToOCS('apps/files_sharing/api/v1/deletedshares', 2) + shareId) + .success(function(result) { + context.fileList.remove(context.fileInfoModel.attributes.name); + }).fail(function() { + OC.Notification.showTemporary(t('files_sharing', 'Something happened. Unable to restore the share.')); + }); + } + }); + return fileActions; + }, + _onActionsUpdated: function(ev) { _.each([this._inFileList, this._outFileList, this._linkFileList], function(list) { if (!list) { @@ -193,4 +246,10 @@ $(document).ready(function() { $('#app-content-sharinglinks').on('hide', function() { OCA.Sharing.App.removeSharingLinks(); }); + $('#app-content-deletedshares').on('show', function(e) { + OCA.Sharing.App.initSharingDeleted($(e.target)); + }); + $('#app-content-deletedshares').on('hide', function() { + OCA.Sharing.App.removeSharingDeleted(); + }); }); diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 22513e1b470..a925920f3bc 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -42,6 +42,12 @@ var fileActions = fileList.fileActions; var oldCreateRow = fileList._createRow; fileList._createRow = function(fileData) { + + if (fileData.permissions === 0) { + // no permission, disabling sidebar + delete fileActions.actions.all.Details; + } + var tr = oldCreateRow.apply(this, arguments); var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData); tr.attr('data-share-permissions', sharePermissions); @@ -158,11 +164,15 @@ permissions: OC.PERMISSION_ALL, iconClass: 'icon-shared', type: OCA.Files.FileActions.TYPE_INLINE, - actionHandler: function(fileName) { - fileList.showDetailsView(fileName, 'shareTabView'); + actionHandler: function(fileName, context) { + // do not open sidebar if permission is set and equal to 0 + var permissions = parseInt(context.$file.data('share-permissions'), 10); + if (isNaN(permissions) || permissions > 0) { + fileList.showDetailsView(fileName, 'shareTabView'); + } }, render: function(actionSpec, isDefault, context) { - var permissions = parseInt(context.$file.attr('data-permissions'), 10); + var permissions = parseInt(context.$file.data('permissions'), 10); // if no share permissions but share owner exists, still show the link if ((permissions & OC.PERMISSION_SHARE) !== 0 || context.$file.attr('data-share-owner')) { return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context); diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index ad818d91413..973d2120b16 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -37,6 +37,7 @@ */ _sharedWithUser: false, _linksOnly: false, + _showDeleted: false, _clientSideSort: true, _allowSelection: false, @@ -56,6 +57,9 @@ if (options && options.linksOnly) { this._linksOnly = true; } + if (options && options.showDeleted) { + this._showDeleted = true; + } }, _renderRow: function() { @@ -78,7 +82,11 @@ var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE; $tr.attr('data-permissions', permission); } - + if (this._showDeleted) { + var permission = fileData.permissions; + $tr.attr('data-share-permissions', permission); + } + // add row with expiration date for link only shares - influenced by _createRow of filelist if (this._linksOnly) { var expirationTimestamp = 0; @@ -183,20 +191,36 @@ // there is only root this._setCurrentDir('/', false); + + if (this._showDeleted) { + var shares = $.ajax({ + url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'deletedshares', + /* jshint camelcase: false */ + data: { + format: 'json', + include_tags: true + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true'); + }, + }); + } else { + var shares = $.ajax({ + url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', + /* jshint camelcase: false */ + data: { + format: 'json', + shared_with_me: !!this._sharedWithUser, + include_tags: true + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true'); + }, + }); + } var promises = []; - var shares = $.ajax({ - url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', - /* jshint camelcase: false */ - data: { - format: 'json', - shared_with_me: !!this._sharedWithUser, - include_tags: true - }, - type: 'GET', - beforeSend: function(xhr) { - xhr.setRequestHeader('OCS-APIREQUEST', 'true'); - }, - }); promises.push(shares); if (!!this._sharedWithUser) { diff --git a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php new file mode 100644 index 00000000000..d95b434e48f --- /dev/null +++ b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php @@ -0,0 +1,168 @@ +<?php +declare(strict_types=1); +/** + * @Copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * @Copyright 2018, John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> + * + * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Files_Sharing\Controller; + +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSException; +use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\AppFramework\OCSController; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\IGroupManager; +use OCP\IRequest; +use OCP\IUserManager; +use OCP\Share\Exceptions\GenericShareException; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IManager as ShareManager; +use OCP\Share\IShare; + +class DeletedShareAPIController extends OCSController { + + /** @var ShareManager */ + private $shareManager; + + /** @var string */ + private $userId; + + /** @var IUserManager */ + private $userManager; + + /** @var IGroupManager */ + private $groupManager; + + /** @var IRootFolder */ + private $rootFolder; + + public function __construct(string $appName, + IRequest $request, + ShareManager $shareManager, + string $UserId, + IUserManager $userManager, + IGroupManager $groupManager, + IRootFolder $rootFolder) { + parent::__construct($appName, $request); + + $this->shareManager = $shareManager; + $this->userId = $UserId; + $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->rootFolder = $rootFolder; + } + + private function formatShare(IShare $share): array { + + $result = [ + 'id' => $share->getFullId(), + 'share_type' => $share->getShareType(), + 'uid_owner' => $share->getSharedBy(), + 'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(), + 'permissions' => 0, + 'stime' => $share->getShareTime()->getTimestamp(), + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => $share->getShareOwner(), + 'displayname_file_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(), + 'path' => $share->getTarget(), + ]; + $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() === '') { + throw new NotFoundException(); + } + } else { + $node = $nodes[0]; + } + + $result['path'] = $userFolder->getRelativePath($node->getPath()); + if ($node instanceOf \OCP\Files\Folder) { + $result['item_type'] = 'folder'; + } else { + $result['item_type'] = 'file'; + } + $result['mimetype'] = $node->getMimetype(); + $result['storage_id'] = $node->getStorage()->getId(); + $result['storage'] = $node->getStorage()->getCache()->getNumericStorageId(); + $result['item_source'] = $node->getId(); + $result['file_source'] = $node->getId(); + $result['file_parent'] = $node->getParent()->getId(); + $result['file_target'] = $share->getTarget(); + + $expiration = $share->getExpirationDate(); + if ($expiration !== null) { + $result['expiration'] = $expiration->format('Y-m-d 00:00:00'); + } + + $group = $this->groupManager->get($share->getSharedWith()); + $result['share_with'] = $share->getSharedWith(); + $result['share_with_displayname'] = $group !== null ? $group->getDisplayName() : $share->getSharedWith(); + + return $result; + + } + + /** + * @NoAdminRequired + */ + public function index(): DataResponse { + $shares = $this->shareManager->getDeletedSharedWith($this->userId, \OCP\Share::SHARE_TYPE_GROUP, null, -1, 0); + + $shares = array_map(function (IShare $share) { + return $this->formatShare($share); + }, $shares); + + return new DataResponse($shares); + } + + /** + * @NoAdminRequired + * + * @throws OCSException + */ + public function undelete(string $id): DataResponse { + try { + $share = $this->shareManager->getShareById($id, $this->userId); + } catch (ShareNotFound $e) { + throw new OCSNotFoundException('Share not found'); + } + + if ($share->getPermissions() !== 0) { + throw new OCSNotFoundException('No deleted share found'); + } + + try { + $this->shareManager->restoreShare($share, $this->userId); + } catch (GenericShareException $e) { + throw new OCSException('Something went wrong'); + } + + return new DataResponse([]); + } +} diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 35fe3ac81ab..67ff9eae6d3 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -151,7 +151,6 @@ class ShareAPIController extends OCSController { $node = $recipientNode; } else { $nodes = $userFolder->getById($share->getNodeId()); - if (empty($nodes)) { // fallback to guessing the path $node = $userFolder->get($share->getTarget()); diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php index 61b479d3e9b..1a1855b9c44 100644 --- a/apps/sharebymail/lib/ShareByMailProvider.php +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -42,6 +42,7 @@ use OCP\Mail\IMailer; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; use OC\Share20\Share; +use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareProvider; @@ -692,6 +693,10 @@ class ShareByMailProvider implements IShareProvider { // nothing to do here, mail shares are only outgoing shares } + public function restore(IShare $share, string $recipient): IShare { + throw new GenericShareException('not implemented'); + } + /** * @inheritdoc */ diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index 3c56b24707c..5e52156d1d0 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -31,6 +31,7 @@ namespace OC\Share20; use OC\Files\Cache\Cache; use OCP\Files\Folder; +use OCP\Share\IShare; use OCP\Share\IShareProvider; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; @@ -412,6 +413,41 @@ class DefaultShareProvider implements IShareProvider { /** * @inheritdoc + * + * For now this only works for group shares + * If this gets implemented for normal shares we have to extend it + */ + public function restore(IShare $share, string $recipient): IShare { + $qb = $this->dbConn->getQueryBuilder(); + $qb->select('permissions') + ->from('share') + ->where( + $qb->expr()->eq('id', $qb->createNamedParameter($share->getId())) + ); + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + $originalPermission = $data['permissions']; + + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('permissions', $qb->createNamedParameter($originalPermission)) + ->where( + $qb->expr()->eq('parent', $qb->createNamedParameter($share->getParent())) + )->andWhere( + $qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)) + )->andWhere( + $qb->expr()->eq('share_with', $qb->createNamedParameter($recipient)) + ); + + $qb->execute(); + + return $this->getShareById($share->getId(), $recipient); + } + + /** + * @inheritdoc */ public function move(\OCP\Share\IShare $share, $recipient) { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { @@ -922,6 +958,7 @@ class DefaultShareProvider implements IShareProvider { while($data = $stmt->fetch()) { $shareMap[$data['parent']]->setPermissions((int)$data['permissions']); $shareMap[$data['parent']]->setTarget($data['file_target']); + $shareMap[$data['parent']]->setParent($data['parent']); } $stmt->closeCursor(); diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index cddd8c8d92b..5116351a6bc 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -61,6 +61,7 @@ use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; use OCP\Share\IProviderFactory; +use OCP\Share\IShare; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; use OCP\Share\IShareProvider; @@ -978,6 +979,13 @@ class Manager implements IManager { $this->eventDispatcher->dispatch('OCP\Share::postUnshareFromSelf', $event); } + public function restoreShare(IShare $share, string $recipientId): IShare { + list($providerId, ) = $this->splitFullId($share->getFullId()); + $provider = $this->factory->getProvider($providerId); + + return $provider->restore($share, $recipientId); + } + /** * @inheritdoc */ @@ -1124,6 +1132,25 @@ class Manager implements IManager { /** * @inheritdoc */ + public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0) { + $shares = $this->getSharedWith($userId, $shareType, $node, $limit, $offset); + + // Only get deleted shares + $shares = array_filter($shares, function(IShare $share) { + return $share->getPermissions() === 0; + }); + + // Only get shares where the owner still exists + $shares = array_filter($shares, function (IShare $share) { + return $this->userManager->userExists($share->getShareOwner()); + }); + + return $shares; + } + + /** + * @inheritdoc + */ public function getShareById($id, $recipient = null) { if ($id === null) { throw new ShareNotFound(); diff --git a/lib/public/Share/IManager.php b/lib/public/Share/IManager.php index 493db5e5149..d4fc3e14749 100644 --- a/lib/public/Share/IManager.php +++ b/lib/public/Share/IManager.php @@ -29,6 +29,7 @@ namespace OCP\Share; use OCP\Files\Folder; use OCP\Files\Node; +use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; /** @@ -84,6 +85,20 @@ interface IManager { public function deleteFromSelf(IShare $share, $recipientId); /** + * Restore the share when it has been deleted + * Certain share types can be restored when they have been deleted + * but the provider should properly handle this\ + * + * @param IShare $share The share to restore + * @param string $recipientId The user to restore the share for + * @return IShare The restored share object + * @throws GenericShareException In case restoring the share failed + * + * @since 14.0.0 + */ + public function restoreShare(IShare $share, string $recipientId): IShare; + + /** * Move the share as a recipient of the share. * This is updating the share target. So where the recipient has the share mounted. * @@ -135,6 +150,20 @@ interface IManager { public function getSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0); /** + * Get deleted shares shared with $user. + * Filter by $node if provided + * + * @param string $userId + * @param int $shareType + * @param Node|null $node + * @param int $limit The maximum number of shares returned, -1 for all + * @param int $offset + * @return IShare[] + * @since 14.0.0 + */ + public function getDeletedSharedWith($userId, $shareType, $node = null, $limit = 50, $offset = 0); + + /** * Retrieve a share by the share id. * If the recipient is set make sure to retrieve the file for that user. * This makes sure that if a user has moved/deleted a group share this diff --git a/lib/public/Share/IShareProvider.php b/lib/public/Share/IShareProvider.php index 4a1ac9b8b8d..6731bf8882b 100644 --- a/lib/public/Share/IShareProvider.php +++ b/lib/public/Share/IShareProvider.php @@ -25,6 +25,8 @@ namespace OCP\Share; use OCP\Files\Folder; +use OCP\Share\Exceptions\GenericShareException; +use OCP\Share\Exceptions\ShareNotFound; use OCP\Files\Node; /** @@ -81,6 +83,18 @@ interface IShareProvider { public function deleteFromSelf(\OCP\Share\IShare $share, $recipient); /** + * Restore a share for a given recipient. The implementation could be provider independant. + * + * @param IShare $share + * @param string $recipient + * @return IShare The restored share object + * + * @since 14.0.0 + * @throws GenericShareException In case the share could not be restored + */ + public function restore(IShare $share, string $recipient): IShare; + + /** * Move a share as a recipient. * This is updating the share target. Thus the mount point of the recipient. * This may require special handling. If a user moves a group share |