diff options
Diffstat (limited to 'apps/files_sharing')
7 files changed, 388 insertions, 7 deletions
diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index f4119a55972..e98d29aeb8a 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -59,4 +59,10 @@ Turning the feature off removes shared files and folders on the server for all s <step>OCA\Files_Sharing\Migration\SetPasswordColumn</step> </post-migration> </repair-steps> + + <collaboration> + <plugins> + <plugin type="autocomplete-sort">OCA\Files_Sharing\Collaboration\ShareRecipientSorter</plugin> + </plugins> + </collaboration> </info> diff --git a/apps/files_sharing/composer/composer/autoload_classmap.php b/apps/files_sharing/composer/composer/autoload_classmap.php index 48b9651a00d..a68db3421b6 100644 --- a/apps/files_sharing/composer/composer/autoload_classmap.php +++ b/apps/files_sharing/composer/composer/autoload_classmap.php @@ -19,6 +19,7 @@ return array( 'OCA\\Files_Sharing\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', 'OCA\\Files_Sharing\\Cache' => $baseDir . '/../lib/Cache.php', '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\\ExternalSharesController' => $baseDir . '/../lib/Controller/ExternalSharesController.php', 'OCA\\Files_Sharing\\Controller\\PublicPreviewController' => $baseDir . '/../lib/Controller/PublicPreviewController.php', diff --git a/apps/files_sharing/composer/composer/autoload_static.php b/apps/files_sharing/composer/composer/autoload_static.php index b8a61eaadd1..328d6aca01d 100644 --- a/apps/files_sharing/composer/composer/autoload_static.php +++ b/apps/files_sharing/composer/composer/autoload_static.php @@ -34,6 +34,7 @@ class ComposerStaticInitFiles_Sharing 'OCA\\Files_Sharing\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', 'OCA\\Files_Sharing\\Cache' => __DIR__ . '/..' . '/../lib/Cache.php', '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\\ExternalSharesController' => __DIR__ . '/..' . '/../lib/Controller/ExternalSharesController.php', 'OCA\\Files_Sharing\\Controller\\PublicPreviewController' => __DIR__ . '/..' . '/../lib/Controller/PublicPreviewController.php', diff --git a/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php b/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php new file mode 100644 index 00000000000..db213398118 --- /dev/null +++ b/apps/files_sharing/lib/Collaboration/ShareRecipientSorter.php @@ -0,0 +1,110 @@ +<?php +/** + * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @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\Collaboration; + + +use OCP\Collaboration\AutoComplete\ISorter; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\IUserSession; +use OCP\Share\IManager; + +class ShareRecipientSorter implements ISorter { + + /** @var IManager */ + private $shareManager; + /** @var Folder */ + private $rootFolder; + /** @var IUserSession */ + private $userSession; + + public function __construct(IManager $shareManager, IRootFolder $rootFolder, IUserSession $userSession) { + $this->shareManager = $shareManager; + $this->rootFolder = $rootFolder; + $this->userSession = $userSession; + } + + public function getId() { + return 'share-recipients'; + } + + public function sort(array &$sortArray, array $context) { + // let's be tolerant. Comments uses "files" by default, other usages are often singular + if($context['itemType'] !== 'files' && $context['itemType'] !== 'file') { + return; + } + $user = $this->userSession->getUser(); + if($user === null) { + return; + } + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + /** @var Node[] $nodes */ + $nodes = $userFolder->getById((int)$context['itemId']); + if(count($nodes) === 0) { + return; + } + $al = $this->shareManager->getAccessList($nodes[0]); + + foreach ($sortArray as $type => &$byType) { + if(!isset($al[$type]) || !is_array($al[$type])) { + continue; + } + + // at least on PHP 5.6 usort turned out to be not stable. So we add + // the current index to the value and compare it on a draw + $i = 0; + $workArray = array_map(function($element) use (&$i) { + return [$i++, $element]; + }, $byType); + + usort($workArray, function ($a, $b) use ($al, $type) { + $result = $this->compare($a[1], $b[1], $al[$type]); + if($result === 0) { + $result = $a[0] - $b[0]; + } + return $result; + }); + + // and remove the index values again + $byType = array_column($workArray, 1); + } + } + + /** + * @param array $a + * @param array $b + * @param array $al + * @return int + */ + protected function compare(array $a, array $b, array $al) { + $a = $a['value']['shareWith']; + $b = $b['value']['shareWith']; + + $valueA = (int)in_array($a, $al, true); + $valueB = (int)in_array($b, $al, true); + + return $valueB - $valueA; + } +} diff --git a/apps/files_sharing/lib/Controller/ShareController.php b/apps/files_sharing/lib/Controller/ShareController.php index 700ac220804..9c3f4c6afeb 100644 --- a/apps/files_sharing/lib/Controller/ShareController.php +++ b/apps/files_sharing/lib/Controller/ShareController.php @@ -373,15 +373,20 @@ class ShareController extends Controller { $shareTmpl['previewMaxY'] = $this->config->getSystemValue('preview_max_y', 1024); $shareTmpl['disclaimer'] = $this->config->getAppValue('core', 'shareapi_public_link_disclaimertext', null); $shareTmpl['previewURL'] = $shareTmpl['downloadURL']; + $ogPreview = ''; if ($shareTmpl['previewSupported']) { $shareTmpl['previewImage'] = $this->urlGenerator->linkToRouteAbsolute( 'files_sharing.PublicPreview.getPreview', ['x' => 200, 'y' => 200, 'file' => $shareTmpl['directory_path'], 't' => $shareTmpl['dirToken']]); + $ogPreview = $shareTmpl['previewImage']; + // We just have direct previews for image files if ($share->getNode()->getMimePart() === 'image') { $shareTmpl['previewURL'] = $this->urlGenerator->linkToRouteAbsolute('files_sharing.publicpreview.directLink', ['token' => $token]); + $ogPreview = $shareTmpl['previewURL']; } } else { $shareTmpl['previewImage'] = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->imagePath('core', 'favicon-fb.png')); + $ogPreview = $shareTmpl['previewImage']; } // Load files we need @@ -411,7 +416,7 @@ class ShareController extends Controller { \OCP\Util::addHeader('meta', ['property' => "og:site_name", 'content' => $this->defaults->getName()]); \OCP\Util::addHeader('meta', ['property' => "og:url", 'content' => $shareTmpl['shareUrl']]); \OCP\Util::addHeader('meta', ['property' => "og:type", 'content' => "object"]); - \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $shareTmpl['previewImage']]); + \OCP\Util::addHeader('meta', ['property' => "og:image", 'content' => $ogPreview]); $this->eventDispatcher->dispatch('OCA\Files_Sharing::loadAdditionalScripts'); diff --git a/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php b/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php new file mode 100644 index 00000000000..8f516788761 --- /dev/null +++ b/apps/files_sharing/tests/Collaboration/ShareRecipientSorterTest.php @@ -0,0 +1,258 @@ +<?php +/** + * @copyright Copyright (c) 2017 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @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\Tests\Collaboration; + + +use OCA\Files_Sharing\Collaboration\ShareRecipientSorter; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\IUser; +use OCP\IUserSession; +use OCP\Share\IManager; +use Test\TestCase; + +class ShareRecipientSorterTest extends TestCase { + /** @var IManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $shareManager; + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + protected $rootFolder; + /** @var IUserSession|\PHPUnit_Framework_MockObject_MockObject */ + protected $userSession; + /** @var ShareRecipientSorter */ + protected $sorter; + + public function setUp() { + parent::setUp(); + + $this->shareManager = $this->createMock(IManager::class); + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->userSession = $this->createMock(IUserSession::class); + + $this->sorter = new ShareRecipientSorter($this->shareManager, $this->rootFolder, $this->userSession); + } + + /** + * @dataProvider sortDataProvider + * @param $data + */ + public function testSort($data) { + $node = $this->createMock(Node::class); + + /** @var Folder|\PHPUnit_Framework_MockObject_MockObject $folder */ + $folder = $this->createMock(Folder::class); + $this->rootFolder->expects($this->any()) + ->method('getUserFolder') + ->willReturn($folder); + + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('yvonne'); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + + if ($data['context']['itemType'] === 'files') { + $folder->expects($this->once()) + ->method('getById') + ->with($data['context']['itemId']) + ->willReturn([$node]); + + $this->shareManager->expects($this->once()) + ->method('getAccessList') + ->with($node) + ->willReturn($data['accessList']); + } else { + $folder->expects($this->never()) + ->method('getById'); + $this->shareManager->expects($this->never()) + ->method('getAccessList'); + } + + $workArray = $data['input']; + $this->sorter->sort($workArray, $data['context']); + + $this->assertEquals($data['expected'], $workArray); + } + + public function testSortNoNodes() { + /** @var Folder|\PHPUnit_Framework_MockObject_MockObject $folder */ + $folder = $this->createMock(Folder::class); + $this->rootFolder->expects($this->any()) + ->method('getUserFolder') + ->willReturn($folder); + + $folder->expects($this->once()) + ->method('getById') + ->willReturn([]); + + $user = $this->createMock(IUser::class); + $user->expects($this->any()) + ->method('getUID') + ->willReturn('yvonne'); + + $this->userSession->expects($this->once()) + ->method('getUser') + ->willReturn($user); + + $this->shareManager->expects($this->never()) + ->method('getAccessList'); + + $originalArray = [ + 'users' => [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ] + ]; + $workArray = $originalArray; + $this->sorter->sort($workArray, ['itemType' => 'files', 'itemId' => 404]); + + $this->assertEquals($originalArray, $workArray); + } + + public function sortDataProvider() { + return [[ + [ + #0 – sort properly and otherwise keep existing order + 'context' => ['itemType' => 'files', 'itemId' => 42], + 'accessList' => ['users' => ['celia', 'darius', 'faruk', 'gail'], 'bots' => ['r2-d2']], + 'input' => [ + 'users' => + [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'elena']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'c-3po']], + ['value' => ['shareWith' => 'r2-d2']], + ] + ], + 'expected' => [ + 'users' => + [ + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'elena']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'r2-d2']], + ['value' => ['shareWith' => 'c-3po']], + ] + ], + ], + [ + #1 – no recipients + 'context' => ['itemType' => 'files', 'itemId' => 42], + 'accessList' => ['users' => false], + 'input' => [ + 'users' => + [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'elena']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'c-3po']], + ['value' => ['shareWith' => 'r2-d2']], + ] + ], + 'expected' => [ + 'users' => + [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'elena']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'c-3po']], + ['value' => ['shareWith' => 'r2-d2']], + ] + ], + ], + [ + #2 – unsupported item type + 'context' => ['itemType' => 'announcements', 'itemId' => 42], + 'accessList' => null, // not needed + 'input' => [ + 'users' => + [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'elena']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'c-3po']], + ['value' => ['shareWith' => 'r2-d2']], + ] + ], + 'expected' => [ + 'users' => + [ + ['value' => ['shareWith' => 'alice']], + ['value' => ['shareWith' => 'bob']], + ['value' => ['shareWith' => 'celia']], + ['value' => ['shareWith' => 'darius']], + ['value' => ['shareWith' => 'elena']], + ['value' => ['shareWith' => 'faruk']], + ['value' => ['shareWith' => 'gail']], + ], + 'bots' => [ + ['value' => ['shareWith' => 'c-3po']], + ['value' => ['shareWith' => 'r2-d2']], + ] + ], + ], + [ + #3 – no nothing + 'context' => ['itemType' => 'files', 'itemId' => 42], + 'accessList' => [], + 'input' => [], + 'expected' => [], + ], + ]]; + } +} diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index ea2f427df75..5b0a78c9c64 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -145,7 +145,7 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.find('>span').text().trim()).toEqual('User One'); + expect($action.find('>span').text().trim()).toEqual('Shared by User One'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -166,7 +166,7 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Two'); + expect($action.text().trim()).toEqual('Shared with User One Shared with User Two'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -273,7 +273,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two'); - expect($action.find('>span').text().trim()).toEqual('Shared with Group One, Group Two, User One, User Two'); + expect($action.text().trim()).toEqual('Shared with Group One Shared with Group Two Shared with User One Shared with User Two'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); }); @@ -306,7 +306,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two'); - expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Three, User Two'); + expect($action.text().trim()).toEqual('Shared with User One Shared with User Three Shared with User Two'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); }); @@ -362,7 +362,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).toEqual('User Two'); - expect($action.find('>span').text().trim()).toEqual('User One'); + expect($action.find('>span').text().trim()).toEqual('Shared by User One'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); }); @@ -393,7 +393,7 @@ describe('OCA.Sharing.Util tests', function() { expect($tr.attr('data-share-recipients')).not.toBeDefined(); - expect($action.find('>span').text().trim()).toEqual('User One'); + expect($action.find('>span').text().trim()).toEqual('Shared by User One'); expect($action.find('.icon').hasClass('icon-shared')).toEqual(true); expect($action.find('.icon').hasClass('icon-public')).toEqual(false); }); |