diff --git a/apps/dav/lib/Connector/Sabre/SharesPlugin.php b/apps/dav/lib/Connector/Sabre/SharesPlugin.php index afafa4dcc14a..b6d9e83d3250 100644 --- a/apps/dav/lib/Connector/Sabre/SharesPlugin.php +++ b/apps/dav/lib/Connector/Sabre/SharesPlugin.php @@ -133,7 +133,9 @@ private function getSharesForNodeIds($nodeIDs) { \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, - \OCP\Share::SHARE_TYPE_REMOTE + \OCP\Share::SHARE_TYPE_REMOTE, + \OCP\Share::SHARE_TYPE_REMOTE_GROUP + ]; // Query DB for share types for specified node IDs diff --git a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php index 9fefcf047ba3..25f631de9f6f 100644 --- a/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/SharesPluginTest.php @@ -91,7 +91,8 @@ public function testGetProperties($shareTypes) { \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, - \OCP\Share::SHARE_TYPE_REMOTE + \OCP\Share::SHARE_TYPE_REMOTE, + \OCP\Share::SHARE_TYPE_REMOTE_GROUP ]; $this->shareManager->expects($this->any()) @@ -172,7 +173,8 @@ public function testPreloadThenGetProperties($shareTypes) { \OCP\Share::SHARE_TYPE_USER, \OCP\Share::SHARE_TYPE_GROUP, \OCP\Share::SHARE_TYPE_LINK, - \OCP\Share::SHARE_TYPE_REMOTE + \OCP\Share::SHARE_TYPE_REMOTE, + \OCP\Share::SHARE_TYPE_REMOTE_GROUP ]; $this->shareManager->expects($this->any()) diff --git a/apps/federatedfilesharing/lib/AppInfo/Application.php b/apps/federatedfilesharing/lib/AppInfo/Application.php index ccbee8ba8214..180c4bc64aa4 100644 --- a/apps/federatedfilesharing/lib/AppInfo/Application.php +++ b/apps/federatedfilesharing/lib/AppInfo/Application.php @@ -140,7 +140,8 @@ function ($c) use ($server) { $server->getUserManager(), $c->query('AddressHandler'), $c->query('FederatedShareManager'), - $server->getLogger() + $server->getLogger(), + $server->getConfig() ); } ); diff --git a/apps/federatedfilesharing/lib/Controller/OcmController.php b/apps/federatedfilesharing/lib/Controller/OcmController.php index d739d268e633..74e1d8559840 100644 --- a/apps/federatedfilesharing/lib/Controller/OcmController.php +++ b/apps/federatedfilesharing/lib/Controller/OcmController.php @@ -32,10 +32,12 @@ use OCA\FederatedFileSharing\Ocm\Exception\OcmException; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; +use OCP\IConfig; use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; use OCP\IUserManager; +use OCP\Share\Exceptions\ShareNotFound; /** * Class OcmController @@ -75,6 +77,9 @@ class OcmController extends Controller { */ protected $logger; + /** @var IConfig */ + private $config; + /** * OcmController constructor. * @@ -86,6 +91,7 @@ class OcmController extends Controller { * @param AddressHandler $addressHandler * @param FedShareManager $fedShareManager * @param ILogger $logger + * @param IConfig $config */ public function __construct( $appName, @@ -95,7 +101,8 @@ public function __construct( IUserManager $userManager, AddressHandler $addressHandler, FedShareManager $fedShareManager, - ILogger $logger + ILogger $logger, + IConfig $config ) { parent::__construct($appName, $request); @@ -105,6 +112,7 @@ public function __construct( $this->addressHandler = $addressHandler; $this->fedShareManager = $fedShareManager; $this->logger = $logger; + $this->config = $config; } /** @@ -177,6 +185,24 @@ public function createShare( $resourceType, $protocol ) { + // Allow other apps to overwrite the behaviour of this endpoint + $controllerClass = $this->config->getSystemValue('sharing.ocmController'); + if (($controllerClass !== '') && ($controllerClass !== null)) { + $controller = \OC::$server->query($controllerClass); + return $controller->createShare( + $shareWith, + $name, + $description, + $providerId, + $owner, + $ownerDisplayName, + $sender, + $senderDisplayName, + $shareType, + $resourceType, + $protocol + ); + } try { $this->ocmMiddleware->assertIncomingSharingEnabled(); $this->ocmMiddleware->assertNotNull( @@ -284,6 +310,17 @@ public function processNotification( $providerId, $notification ) { + // Allow other apps to overwrite the behaviour of this endpoint + $controllerClass = $this->config->getSystemValue('sharing.ocmController'); + if (($controllerClass !== '') && ($controllerClass !== null)) { + $controller = \OC::$server->query($controllerClass); + return $controller->processNotification( + $notificationType, + $resourceType, + $providerId, + $notification + ); + } try { if (!\is_array($notification)) { throw new BadRequestException( diff --git a/apps/federatedfilesharing/tests/Controller/OcmControllerTest.php b/apps/federatedfilesharing/tests/Controller/OcmControllerTest.php index 2a953a2ddf1b..6532d544a783 100644 --- a/apps/federatedfilesharing/tests/Controller/OcmControllerTest.php +++ b/apps/federatedfilesharing/tests/Controller/OcmControllerTest.php @@ -33,6 +33,7 @@ use OCA\FederatedFileSharing\Tests\TestCase; use OCP\App\IAppManager; use OCP\AppFramework\Http; +use OCP\IConfig; use OCP\ILogger; use OCP\IRequest; use OCP\IURLGenerator; @@ -86,6 +87,11 @@ class OcmControllerTest extends TestCase { */ private $logger; + /** + * @var IConfig | \PHPUnit\Framework\MockObject\MockObject + */ + private $config; + /** * @var OcmController */ @@ -107,6 +113,7 @@ protected function setUp(): void { $this->addressHandler = $this->createMock(AddressHandler::class); $this->fedShareManager = $this->createMock(FedShareManager::class); $this->logger = $this->createMock(ILogger::class); + $this->config = $this->createMock(IConfig::class); $this->ocmController = new OcmController( 'federatedfilesharing', @@ -116,7 +123,8 @@ protected function setUp(): void { $this->userManager, $this->addressHandler, $this->fedShareManager, - $this->logger + $this->logger, + $this->config ); } diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 2c98da47d548..61a8da9e851b 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -209,18 +209,19 @@ OCA.Sharing.App = { fileList.fileSummary.$el.find('.filesize').remove(); }, - _setShareState: function(fileId, state, isRemote) { + _setShareState: function(fileId, state, isRemote, shareType) { var method = 'POST'; if (state === OC.Share.STATE_REJECTED) { method = 'DELETE'; } - var endPoint = isRemote === true ? 'remote_shares/pending/' : 'shares/pending/'; var xhr = $.ajax({ url: OC.linkToOCS('apps/files_sharing/api/v1') + endPoint + encodeURIComponent(fileId) + '?format=json', contentType: 'application/json', dataType: 'json', type: method, + // be aware that `shareType` should not be an empty string + data: JSON.stringify((shareType !== undefined ? { shareType: shareType } : {})), }); xhr.fail(function(response) { var message = ''; @@ -236,6 +237,7 @@ OCA.Sharing.App = { _shareStateActionHandler: function(context, newState) { var targetFileData = context.fileList.elementToFile(context.$file); var isRemote = targetFileData.shareLocationType === 'remote'; + const shareType = targetFileData.shareType; function responseCallback(response, status) { if (status === 'success') { var meta = response.ocs.meta; @@ -252,7 +254,7 @@ OCA.Sharing.App = { } context.fileList.showFileBusyState(context.$file, true); - this._setShareState(context.fileInfoModel.get('shares')[0].id, newState, isRemote) + this._setShareState(context.fileInfoModel.get('shares')[0].id, newState, isRemote, shareType) .then(responseCallback); }, diff --git a/apps/files_sharing/js/external.js b/apps/files_sharing/js/external.js index e9eb3ac5d9ca..41afb6d45b7e 100644 --- a/apps/files_sharing/js/external.js +++ b/apps/files_sharing/js/external.js @@ -142,16 +142,20 @@ shares[index], false, function(result, share) { + let shareType = "user"; + if (share.hasOwnProperty('share_type') ){ + shareType = share.share_type; + } if (result) { // Accept - $.post(OC.generateUrl('/apps/files_sharing/api/externalShares'), {id: share.id}) + $.post(OC.generateUrl('/apps/files_sharing/api/externalShares'), {id: share.id, share_type: shareType }) .then(function() { fileList.reload(); }); } else { // Delete $.ajax({ - url: OC.generateUrl('/apps/files_sharing/api/externalShares/'+share.id), + url: OC.generateUrl('/apps/files_sharing/api/externalShares/'+share.id+"?share_type="+shareType), type: 'DELETE' }); } diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 93789ef86462..004f014cd574 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -151,6 +151,8 @@ hasShares = true; } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { hasShares = true; + } else if (shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) { + hasShares = true; } }); OCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink); diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 7a8230d77680..e9b55312cbac 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -88,6 +88,7 @@ $dateColumn.before($tr.children('td:first')); $tr.find('td.filename input:checkbox').remove(); $tr.attr('data-share-location-type', fileData.shareLocationType); + $tr.attr('data-share-type', fileData.shareType); $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); // add row with expiration date for link only shares - influenced by _createRow of filelist if (this._linksOnly) { @@ -309,7 +310,8 @@ } fileInfo.shareState = parseInt($el.attr('data-share-state'), 10); - fileInfo.shareLocationType = $el.attr('data-share-location-type'); + fileInfo.shareLocationType = $el.attr('data-share-location-type'); + fileInfo.shareType = $el.attr('data-share-type') || ""; return fileInfo; }, @@ -355,7 +357,8 @@ path: OC.dirname(share.mountpoint), permissions: share.permissions, tags: share.tags || [], - shareLocationType: 'remote' + shareLocationType: 'remote', + shareType: share.share_type || '' }; file.shares = [{ diff --git a/apps/files_sharing/lib/AppInfo/Application.php b/apps/files_sharing/lib/AppInfo/Application.php index 47637b98750c..11cd5437d8b4 100644 --- a/apps/files_sharing/lib/AppInfo/Application.php +++ b/apps/files_sharing/lib/AppInfo/Application.php @@ -81,7 +81,8 @@ public function __construct(array $urlParams = []) { $c->query('Request'), $c->query('ExternalManager'), $c->query('HttpClientService'), - $server->getEventDispatcher() + $server->getEventDispatcher(), + $server->getConfig() ); }); @@ -119,6 +120,8 @@ public function __construct(array $urlParams = []) { $server->getEventDispatcher(), $uid ), + $server->getConfig(), + $server->getLogger(), $uid ); }); diff --git a/apps/files_sharing/lib/Controller/RemoteOcsController.php b/apps/files_sharing/lib/Controller/RemoteOcsController.php index 97c437921c74..8d82b28cf736 100644 --- a/apps/files_sharing/lib/Controller/RemoteOcsController.php +++ b/apps/files_sharing/lib/Controller/RemoteOcsController.php @@ -29,6 +29,8 @@ use OCP\Share; use OCP\Files\StorageNotAvailableException; use OCP\Files\StorageInvalidException; +use OCP\IConfig; +use OCP\ILogger; class RemoteOcsController extends OCSController { /** @var IRequest */ @@ -40,23 +42,39 @@ class RemoteOcsController extends OCSController { /** @var string */ protected $uid; + /** + * @var IConfig + */ + protected $config; + + /** + * @var ILogger + */ + protected $logger; + /** * RemoteOcsController constructor. * * @param string $appName * @param IRequest $request * @param Manager $externalManager + * @param IConfig config + * @param ILogger $loggar * @param string $uid */ public function __construct( $appName, IRequest $request, Manager $externalManager, + IConfig $config, + ILogger $logger, $uid ) { parent::__construct($appName, $request); $this->request = $request; $this->externalManager = $externalManager; + $this->config = $config; + $this->logger = $logger; $this->uid = $uid; } @@ -126,6 +144,8 @@ public function declineShare($id) { */ public function getShares($includingPending = false) { $shares = []; + $groupExternalManager = null; + foreach ($this->externalManager->getAcceptedShares() as $shareInfo) { try { $shares[] = $this->extendShareInfo($shareInfo); @@ -136,6 +156,22 @@ public function getShares($includingPending = false) { } } + // Allow the Federated Groups app to overwrite the behaviour of this endpoint + $managerClass = $this->config->getSystemValue('sharing.groupExternalManager'); + if (!empty($managerClass)) { + $groupExternalManager = \OC::$server->query($managerClass); + + foreach ($groupExternalManager->getAcceptedShares() as $shareInfo) { + try { + $shares[] = $this->extendShareInfo($shareInfo); + } catch (StorageNotAvailableException $e) { + $this->logger->logException($e, ['app' => 'files_sharing']); + } catch (StorageInvalidException $e) { + $this->logger->logException($e, ['app' => 'files_sharing']); + } + } + } + if ($includingPending === true) { /** * pending shares have mountpoint looking like @@ -152,7 +188,10 @@ function ($share) { $share['mountpoint'] = \rtrim($share['mountpoint'], '}'); return $share; }, - $this->externalManager->getOpenShares() + \array_merge( + $this->externalManager->getOpenShares(), + $groupExternalManager === null ? [] : $groupExternalManager->getOpenShares() + ) ); $shares = \array_merge($shares, $openShares); } diff --git a/apps/files_sharing/lib/Controller/Share20OcsController.php b/apps/files_sharing/lib/Controller/Share20OcsController.php index ab9eb943f0b1..0a2d706cb78b 100644 --- a/apps/files_sharing/lib/Controller/Share20OcsController.php +++ b/apps/files_sharing/lib/Controller/Share20OcsController.php @@ -23,6 +23,7 @@ use Exception; use OC\Files\Filesystem; +use OC\Share20\Exception\ProviderException; use OCA\Files_Sharing\SharingAllowlist; use OCP\Constants; use OC\OCS\Result; @@ -267,7 +268,7 @@ protected function formatShare(IShare $share, $received = false) { if ($share->getToken() !== null) { $result['url'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', ['token' => $share->getToken()]); } - } elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE) { + } elseif ($share->getShareType() === Share::SHARE_TYPE_REMOTE || $share->getShareType() === Share::SHARE_TYPE_REMOTE_GROUP) { $result['share_with'] = $share->getSharedWith(); $result['share_with_displayname'] = $share->getSharedWith(); $result['token'] = $share->getToken(); @@ -553,7 +554,7 @@ public function createShare() { if ($password !== '') { $share->setPassword($password); } - } elseif ($shareType === Share::SHARE_TYPE_REMOTE) { + } elseif ($shareType === Share::SHARE_TYPE_REMOTE || $shareType === Share::SHARE_TYPE_REMOTE_GROUP) { if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { $share->getNode()->unlock(ILockingProvider::LOCK_SHARED); return new Result(null, 403, $this->l->t('Sharing %s failed because the back end does not allow shares from type %s', [$path->getPath(), $shareType])); @@ -702,6 +703,7 @@ public function getShares() { return new Result(); } + $supportedShareTypes = $this->getSupportedShareTypes(); $sharedWithMe = $this->request->getParam('shared_with_me', null); $reshares = $this->request->getParam('reshares', null); $subfiles = $this->request->getParam('subfiles'); @@ -710,26 +712,16 @@ public function getShares() { $includeTags = $this->request->getParam('include_tags', false); $shareTypes = $this->request->getParam('share_types', ''); if ($shareTypes === '') { - $shareTypes = [ - Share::SHARE_TYPE_USER, - Share::SHARE_TYPE_GROUP, - Share::SHARE_TYPE_LINK, - Share::SHARE_TYPE_REMOTE, - ]; + $shareTypes = $supportedShareTypes; } else { $shareTypes = \explode(',', $shareTypes); } - $requestedShareTypes = [ - Share::SHARE_TYPE_USER => false, - Share::SHARE_TYPE_GROUP => false, - Share::SHARE_TYPE_LINK => false, - Share::SHARE_TYPE_REMOTE => false, - ]; - + $requestedShareTypes = array_fill_keys($supportedShareTypes, false); + if ($this->shareManager->outgoingServer2ServerSharesAllowed() === false) { // if outgoing remote shares aren't allowed, the remote share type can't be chosen - unset($requestedShareTypes[Share::SHARE_TYPE_REMOTE]); + unset($requestedShareTypes[Share::SHARE_TYPE_REMOTE], $requestedShareTypes[Share::SHARE_TYPE_REMOTE_GROUP]); } foreach ($shareTypes as $shareType) { if (isset($requestedShareTypes[$shareType])) { @@ -1211,16 +1203,27 @@ private function parseDate($expireDate) { */ private function getShareById($id, $recipient = null) { $share = null; - + $providerIds = \array_keys($this->shareManager->getProvidersCapabilities()); + // First check if it is an internal share. - try { - $share = $this->shareManager->getShareById('ocinternal:'.$id, $recipient); - } catch (ShareNotFound $e) { - if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { - throw new ShareNotFound(); + foreach ($providerIds as $providerId) { + try { + $share = $this->shareManager->getShareById($providerId .":". $id, $recipient); + return $share; + } catch (ShareNotFound $e) { + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + throw new ShareNotFound(); + } + + continue; + } catch (ProviderException $e) { + // We should iterate all provider to find proper provider for given share + continue; } + } - $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id, $recipient); + if ($share === null) { + throw new ShareNotFound(); } return $share; @@ -1290,4 +1293,23 @@ private function getPermissionsFromRequest() { return $permission; } + + /** + * @return mixed + */ + private function getSupportedShareTypes() { + $providersCapabilities = $this->shareManager->getProvidersCapabilities(); + + $shareTypes = []; + + foreach ($providersCapabilities as $capabilities) { + foreach ($capabilities as $key => $value) { + $shareTypes[] = $key; + } + } + $shareTypes = \array_unique($shareTypes); + $shareTypes = array_keys(array_intersect(Share::CONVERT_SHARE_TYPE_TO_STRING, $shareTypes)); + + return $shareTypes; + } } diff --git a/apps/files_sharing/lib/Controllers/ExternalSharesController.php b/apps/files_sharing/lib/Controllers/ExternalSharesController.php index 62e0cf2794db..7cc05cca6921 100644 --- a/apps/files_sharing/lib/Controllers/ExternalSharesController.php +++ b/apps/files_sharing/lib/Controllers/ExternalSharesController.php @@ -29,6 +29,7 @@ use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\Http\Client\IClientService; +use OCP\IConfig; use OCP\IRequest; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -41,6 +42,8 @@ class ExternalSharesController extends Controller { /** @var \OCA\Files_Sharing\External\Manager */ private $externalManager; + /** @var \OCA\Files_Sharing\External\Manager */ + private $groupExternalManager = null; /** @var IClientService */ private $clientService; /** @@ -48,6 +51,10 @@ class ExternalSharesController extends Controller { */ private $dispatcher; + /** @var IConfig $config */ + private $config; + + public const group_share_type = "group"; /** * ExternalSharesController constructor. * @@ -56,18 +63,21 @@ class ExternalSharesController extends Controller { * @param \OCA\Files_Sharing\External\Manager $externalManager * @param IClientService $clientService * @param EventDispatcherInterface $eventDispatcher + * @param IConfig $config */ public function __construct( $appName, IRequest $request, \OCA\Files_Sharing\External\Manager $externalManager, IClientService $clientService, - EventDispatcherInterface $eventDispatcher + EventDispatcherInterface $eventDispatcher, + IConfig $config ) { parent::__construct($appName, $request); $this->externalManager = $externalManager; $this->clientService = $clientService; $this->dispatcher = $eventDispatcher; + $this->config = $config; } /** @@ -77,7 +87,13 @@ public function __construct( * @return JSONResponse */ public function index() { - return new JSONResponse($this->externalManager->getOpenShares()); + $federatedGroupResult = []; + $groupExternalManager = $this->initGroupManager(); + if ($groupExternalManager !== null) { + $federatedGroupResult = $groupExternalManager->getOpenShares(); + } + $result = array_merge($federatedGroupResult, $this->externalManager->getOpenShares()); + return new JSONResponse($result); } /** @@ -85,13 +101,16 @@ public function index() { * @NoOutgoingFederatedSharingRequired * * @param int $id + * @param string $share_type * @return JSONResponse */ - public function create($id) { - $shareInfo = $this->externalManager->getShare($id); + public function create($id, $share_type) { + $manager = $this->getManagerForShareType($share_type); + $shareInfo = $manager->getShare($id); + if ($shareInfo !== false) { - $mountPoint = $this->externalManager->getShareRecipientMountPoint($shareInfo); - $fileId = $this->externalManager->getShareFileId($shareInfo, $mountPoint); + $mountPoint = $manager->getShareRecipientMountPoint($shareInfo); + $fileId = $manager->getShareFileId($shareInfo, $mountPoint); $event = new GenericEvent( null, @@ -106,7 +125,7 @@ public function create($id) { ] ); $this->dispatcher->dispatch($event, 'remoteshare.accepted'); - $this->externalManager->acceptShare($id); + $manager->acceptShare($id); } return new JSONResponse(); } @@ -118,8 +137,9 @@ public function create($id) { * @param integer $id * @return JSONResponse */ - public function destroy($id) { - $shareInfo = $this->externalManager->getShare($id); + public function destroy($id, $share_type) { + $manager = $this->getManagerForShareType($share_type); + $shareInfo = $manager->getShare($id); if ($shareInfo !== false) { $event = new GenericEvent( null, @@ -131,11 +151,30 @@ public function destroy($id) { ] ); $this->dispatcher->dispatch($event, 'remoteshare.declined'); - $this->externalManager->declineShare($id); + $manager->declineShare($id); } return new JSONResponse(); } + private function initGroupManager() { + // Allow other apps to add an external manager for user-to-group shares + $managerClass = $this->config->getSystemValue('sharing.groupExternalManager'); + if ($managerClass !== '') { + return \OC::$server->query($managerClass); + } + return null; + } + + private function getManagerForShareType($share_type) { + $groupExternalManager = $this->initGroupManager(); + if ($share_type === self::group_share_type && $groupExternalManager !== null) { + $manager = $groupExternalManager; + } else { + $manager = $this->externalManager; + } + return $manager; + } + /** * Test whether the specified remote is accessible * diff --git a/apps/files_sharing/tests/Controller/RemoteOcsControllerTest.php b/apps/files_sharing/tests/Controller/RemoteOcsControllerTest.php index 514149929d03..7fa328b898d3 100644 --- a/apps/files_sharing/tests/Controller/RemoteOcsControllerTest.php +++ b/apps/files_sharing/tests/Controller/RemoteOcsControllerTest.php @@ -24,6 +24,8 @@ use OC\Files\FileInfo; use OCA\Files_Sharing\Controller\RemoteOcsController; use OCA\Files_Sharing\External\Manager; +use OCP\IConfig; +use OCP\ILogger; use OCP\IRequest; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; @@ -38,16 +40,27 @@ class RemoteOcsControllerTest extends TestCase { /** @var Manager */ protected $externalManager; + /** @var IConfig */ + protected $config; + + /** @var ILogger */ + protected $logger; + /** @var RemoteOcsController | MockObject */ protected $controller; protected function setUp(): void { $this->request = $this->createMock(IRequest::class); $this->externalManager = $this->createMock(Manager::class); + $this->config = $this->createMock(IConfig::class); + $this->logger = $this->createMock(ILogger::class); + $this->controller = new RemoteOcsController( $this->appName, $this->request, $this->externalManager, + $this->config, + $this->logger, 'user' ); } @@ -170,6 +183,8 @@ public function testGetShare($getShareResult, $expectedStatusCode) { $this->appName, $this->request, $this->externalManager, + $this->config, + $this->logger, 'user' ]) ->setMethods(['getFileInfo']) diff --git a/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php b/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php index 0598929f22b8..babfb5c4e06b 100644 --- a/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php +++ b/apps/files_sharing/tests/Controller/Share20OcsControllerTest.php @@ -121,6 +121,20 @@ protected function setUp(): void { ->expects($this->any()) ->method('newShare') ->willReturn($this->newShare()); + $this->shareManager + ->method('getProvidersCapabilities') + ->willReturn([ + "ocinternal" => + [ + "user" => ["shareExpiration"], + "group" => ["shareExpiration"], + "link" => ["shareExpiration", "passwordProtected"] + ], + "ocFederatedSharing"=> + [ + "remote" => ["shareExpiration"] + ], + ]); $this->groupManager = $this->createMock(IGroupManager::class); $this->userManager = $this->createMock(IUserManager::class); $this->request = $this->createMock(IRequest::class); diff --git a/apps/files_sharing/tests/Controllers/ExternalShareControllerTest.php b/apps/files_sharing/tests/Controllers/ExternalShareControllerTest.php index 2e745d5675cf..0d9ee014986c 100644 --- a/apps/files_sharing/tests/Controllers/ExternalShareControllerTest.php +++ b/apps/files_sharing/tests/Controllers/ExternalShareControllerTest.php @@ -65,7 +65,8 @@ public function getExternalShareController() { $this->request, $this->externalManager, $this->clientService, - \OC::$server->getEventDispatcher() + \OC::$server->getEventDispatcher(), + \OC::$server->getConfig() ); } @@ -107,7 +108,7 @@ public function testCreate() { $called[] = 'remoteshare.accepted'; \array_push($called, $event); }); - $this->assertEquals(new JSONResponse(), $this->getExternalShareController()->create($shareId)); + $this->assertEquals(new JSONResponse(), $this->getExternalShareController()->create($shareId, "remote")); $this->assertSame('remoteshare.accepted', $called[0]); $this->assertInstanceOf(GenericEvent::class, $called[1]); @@ -137,7 +138,7 @@ public function testDestroy() { \array_push($called, $event); }); - $this->assertEquals(new JSONResponse(), $this->getExternalShareController()->destroy($shareId)); + $this->assertEquals(new JSONResponse(), $this->getExternalShareController()->destroy($shareId, "user")); $this->assertSame('remoteshare.declined', $called[0]); $this->assertInstanceOf(GenericEvent::class, $called[1]); diff --git a/apps/files_sharing/tests/js/externalSpec.js b/apps/files_sharing/tests/js/externalSpec.js index 21f88b3c333a..4d0ea3d7fed9 100644 --- a/apps/files_sharing/tests/js/externalSpec.js +++ b/apps/files_sharing/tests/js/externalSpec.js @@ -174,7 +174,7 @@ describe('OCA.Sharing external tests', function() { var request = fakeServer.requests[1]; var query = OC.parseQueryString(request.requestBody); expect(request.method).toEqual('POST'); - expect(query).toEqual({id: '123'}); + expect(query).toEqual({id: '123', share_type: 'user'}); expect(request.url).toEqual( OC.webroot + '/index.php/apps/files_sharing/api/externalShares' ); @@ -200,7 +200,7 @@ describe('OCA.Sharing external tests', function() { var request = fakeServer.requests[1]; expect(request.method).toEqual('DELETE'); expect(request.url).toEqual( - OC.webroot + '/index.php/apps/files_sharing/api/externalShares/123' + OC.webroot + '/index.php/apps/files_sharing/api/externalShares/123?share_type=user' ); expect(plugin.filesApp.fileList.reload.notCalled).toEqual(true); diff --git a/changelog/unreleased/40577 b/changelog/unreleased/40577 new file mode 100644 index 000000000000..02ebc8dcb44e --- /dev/null +++ b/changelog/unreleased/40577 @@ -0,0 +1,10 @@ +Enhancement: Add support for OCM via ScienceMesh + + +We've added an if-statement in the files_sharing ShareesController +code that searches for remote sharees. When the 'sciencemesh' app +is installed, use it instead of the federatedfilesharing app to +find sharee matches for OCM sharing. + +https://github.com/owncloud/core/issues/40577 +https://github.com/pondersource/oc-sciencemesh/pull/39 \ No newline at end of file diff --git a/core/css/icons.css b/core/css/icons.css index 40c81111fb1a..6f2ff89a9351 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -203,6 +203,10 @@ img.icon-loading-small-dark, object.icon-loading-small-dark, video.icon-loading- background-image: url('../img/actions/history.svg'); } +.icon-group { + background-image: url('../img/actions/group.svg'); +} + .icon-info { background-image: url('../img/actions/info.svg'); } diff --git a/core/css/share.css b/core/css/share.css index a6f7bd67d7cc..22ea16d4c6be 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -53,9 +53,16 @@ .share-autocomplete-item { display: flex; + align-items: center; max-width: 220px; } +.share-autocomplete-item .icon { + margin-left: auto; + margin-right: 5px; + background-size: 24px 24px; +} + @media (min-width: 1152px) { .share-autocomplete-item { max-width: 20vw; diff --git a/core/img/actions/group.svg b/core/img/actions/group.svg new file mode 100644 index 000000000000..9b37641c5d13 --- /dev/null +++ b/core/img/actions/group.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/core/js/share.js b/core/js/share.js index 28de617b055d..c4d0e2a5222b 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -9,6 +9,7 @@ OC.Share = _.extend(OC.Share || {}, { SHARE_TYPE_LINK:3, SHARE_TYPE_GUEST:4, SHARE_TYPE_REMOTE:6, + SHARE_TYPE_REMOTE_GROUP:7, STATE_ACCEPTED: 0, STATE_PENDING: 1, diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 856814912dae..6b6a131e3c65 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -62,6 +62,9 @@ '{{/if}}' + '
{{typeInfo}}' + '' + + '{{#if showIcon}}' + + '' + + '{{/if}}' + '' + '' + ''; @@ -353,8 +356,9 @@ }, autocompleteRenderItem: function(ul, item) { - var text = item.label; + let showIcon = false; + let iconClass = "" var typeInfo = t('core', 'User'); if (item.batch) { @@ -363,6 +367,11 @@ if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) { typeInfo = t('core', 'Group'); } + if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE_GROUP) { + typeInfo = t('core', 'Federated Group'); + showIcon = true + iconClass = "icon-contacts-dark" + } if (item.value.shareType === OC.Share.SHARE_TYPE_GUEST) { typeInfo = t('core', 'Guest'); } @@ -375,7 +384,7 @@ typeInfo = t('core', 'Federated'); } } - if(item.value.userType === OC.User.USER_TYPE_GUEST){ + if (item.value.userType === OC.User.USER_TYPE_GUEST) { typeInfo = t('core', 'Guest'); } @@ -383,12 +392,14 @@ var $el = $(template({ showAvatar: this.configModel.areAvatarsEnabled(), displayName: text, + showIcon, + iconClass, typeInfo: typeInfo, additionalInfo: item.value.shareWithAdditionalInfo, shareTypeClass: (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) ? 'group' : 'user' })); - if(this.configModel.areAvatarsEnabled()) { + if (this.configModel.areAvatarsEnabled()) { var $avatar = $el.find('.avatardiv'); if (item.value.shareType === OC.Share.SHARE_TYPE_USER) { $avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label); diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index 8583b6da6b33..908e27395382 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -31,6 +31,7 @@ class Constants { public const SHARE_TYPE_GUEST = 4; public const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it public const SHARE_TYPE_REMOTE = 6; + public const SHARE_TYPE_REMOTE_GROUP = 7; public const CONVERT_SHARE_TYPE_TO_STRING = [ self::SHARE_TYPE_USER => 'user', @@ -39,6 +40,7 @@ class Constants { self::SHARE_TYPE_GUEST => 'guest', self::SHARE_TYPE_CONTACT => 'contact', self::SHARE_TYPE_REMOTE => 'remote', + self::SHARE_TYPE_REMOTE_GROUP => 'remote_group', ]; /** diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index bac7968de6ec..4a69bd7f4c84 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -52,6 +52,7 @@ use OCP\Share\IAttributes; use OCP\Share\IManager; use OCP\Share\IProviderFactory; +use OC\Share20\Exception\ProviderException; use OCP\Share\IShare; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\GenericEvent; @@ -266,7 +267,7 @@ protected function generalChecks(\OCP\Share\IShare $share) { if ($share->getSharedWith() !== null) { throw new \InvalidArgumentException('SharedWith should be empty'); } - } elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { + } elseif ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE || $share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE_GROUP) { if ($share->getSharedWith() === null) { throw new \InvalidArgumentException('SharedWith should not be empty'); } @@ -1529,10 +1530,32 @@ public function getShareByToken($token) { // If it is not a link share try to fetch a federated share by token if ($share === null) { - $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE); - $share = $provider->getShareByToken($token); + try { + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE); + $share = $provider->getShareByToken($token); + } catch(ShareNotFound $ex) { + $this->logger->error( + "shared file not found by token: $token for federated user share, try to check federated group share.", + ['app' => __CLASS__] + ); + try { + $provider = $this->factory->getProviderForType(\OCP\Share::SHARE_TYPE_REMOTE_GROUP); + if ($provider !== null) { + $share = $provider->getShareByToken($token); + } + } catch (ShareNotFound $ex) { + $this->logger->error( + "shared file not found by token: $token for federated group share", + ['app' => __CLASS__] + ); + } catch (ProviderException $ex) { + $this->logger->logException( + $ex, + ['app' => __CLASS__] + ); + } + } } - if (self::shareHasExpired($share)) { $this->activityManager->setAgentAuthor(IEvent::AUTOMATION_AUTHOR); try { diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index 243a702cc564..ae9478065bf6 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -130,7 +130,8 @@ public function getProviderForType($shareType) { $shareType === \OCP\Share::SHARE_TYPE_GROUP || $shareType === \OCP\Share::SHARE_TYPE_LINK) { $provider = $this->defaultShareProvider(); - } elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE) { + } elseif ($shareType === \OCP\Share::SHARE_TYPE_REMOTE || + $shareType === \OCP\Share::SHARE_TYPE_REMOTE_GROUP) { $provider = $this->federatedShareProvider(); }