diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index 01703df5bf..24ecbf399e 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 0000000000..0c22ca6405 --- /dev/null +++ b/apps/files/img/unshare.svg @@ -0,0 +1 @@ + diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index f5aa1fc09a..40a103edb9 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,19 @@ if ($config->getAppValue('core', 'shareapi_enabled', 'yes') === 'yes') { 'name' => $l->t('Shared with you'), ]; }); + $deletedShares = $shareManager->getDeletedSharedWith($userSession->getUser()->getUID(), \OCP\Share::SHARE_TYPE_GROUP, null, -1, 0); + if (count($deletedShares) > 0) { + \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/js/app.js b/apps/files_sharing/js/app.js index e6c9159eda..01857e4f26 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('
' + + '

' + t('files_sharing', 'No deleted shares') + '

' + + '

' + t('files_sharing', 'Shares you deleted will show up here') + '

'); + 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/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index ad818d9141..aaa04ca12f 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,7 @@ var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE; $tr.attr('data-permissions', permission); } - + // add row with expiration date for link only shares - influenced by _createRow of filelist if (this._linksOnly) { var expirationTimestamp = 0; @@ -183,20 +187,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 index 2e4f4d52d7..bd00d1a261 100644 --- a/apps/files_sharing/lib/Controller/DeletedShareAPIController.php +++ b/apps/files_sharing/lib/Controller/DeletedShareAPIController.php @@ -30,6 +30,8 @@ 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\IGroupManager; use OCP\IRequest; use OCP\IUserManager; use OCP\Share\Exceptions\GenericShareException; @@ -48,25 +50,81 @@ class DeletedShareAPIController extends OCSController { /** @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) { + 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 { - return [ + + $result = [ 'id' => $share->getFullId(), - 'uid_owner' => $share->getShareOwner(), - 'displayname_owner' => $this->userManager->get($share->getShareOwner())->getDisplayName(), + 'share_type' => $share->getShareType(), + 'uid_owner' => $share->getSharedBy(), + 'displayname_owner' => $this->userManager->get($share->getSharedBy())->getDisplayName(), + 'permissions' => $share->getPermissions(), + '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; + } /** diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index 35fe3ac81a..67ff9eae6d 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());