diff --git a/.gitignore b/.gitignore index f25fb52ce2..964701eea6 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ !/apps/files !/apps/federation !/apps/federatedfilesharing +!/apps/sharebymail !/apps/encryption !/apps/files_external !/apps/files_sharing diff --git a/apps/files/lib/Controller/ApiController.php b/apps/files/lib/Controller/ApiController.php index d6f88581b9..138b68601c 100644 --- a/apps/files/lib/Controller/ApiController.php +++ b/apps/files/lib/Controller/ApiController.php @@ -200,7 +200,8 @@ class ApiController extends Controller { \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_EMAIL ]; foreach ($requestedShareTypes as $requestedShareType) { // one of each type is enough to find out about the types diff --git a/apps/files_sharing/lib/Controller/ShareAPIController.php b/apps/files_sharing/lib/Controller/ShareAPIController.php index ad9ac6c085..4f80b8fc96 100644 --- a/apps/files_sharing/lib/Controller/ShareAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareAPIController.php @@ -36,7 +36,6 @@ use OCP\IL10N; use OCP\IUserManager; use OCP\IRequest; use OCP\IURLGenerator; -use OCP\IUser; use OCP\Files\IRootFolder; use OCP\Lock\LockedException; use OCP\Share\IManager; @@ -186,7 +185,11 @@ class ShareAPIController extends OCSController { } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_REMOTE) { $result['share_with'] = $share->getSharedWith(); - $result['share_with_displayname'] = $share->getSharedWith(); + $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'CLOUD'); + $result['token'] = $share->getToken(); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_EMAIL) { + $result['share_with'] = $share->getSharedWith(); + $result['share_with_displayname'] = $this->getDisplayNameFromAddressBook($share->getSharedWith(), 'EMAIL'); $result['token'] = $share->getToken(); } @@ -195,6 +198,28 @@ class ShareAPIController extends OCSController { return $result; } + /** + * Check if one of the users address books knows the exact property, if + * yes we return the full name. + * + * @param string $query + * @param string $property + * @return string + */ + private function getDisplayNameFromAddressBook($query, $property) { + // FIXME: If we inject the contacts manager it gets initialized bofore any address books are registered + $result = \OC::$server->getContactsManager()->search($query, [$property]); + foreach ($result as $r) { + foreach($r[$property] as $value) { + if ($value === $query) { + return $r['FN']; + } + } + } + + return $query; + } + /** * Get a specific share by id * @@ -400,6 +425,17 @@ class ShareAPIController extends OCSController { $share->setSharedWith($shareWith); $share->setPermissions($permissions); + } else if ($shareType === \OCP\Share::SHARE_TYPE_EMAIL) { + if ($share->getNodeType() === 'file') { + $share->setPermissions(\OCP\Constants::PERMISSION_READ); + } else { + $share->setPermissions( + \OCP\Constants::PERMISSION_READ | + \OCP\Constants::PERMISSION_CREATE | + \OCP\Constants::PERMISSION_UPDATE | + \OCP\Constants::PERMISSION_DELETE); + } + $share->setSharedWith($shareWith); } else { throw new OCSBadRequestException($this->l->t('Unknown share type')); } @@ -466,6 +502,9 @@ class ShareAPIController extends OCSController { $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, false, -1, 0)); $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, false, -1, 0)); $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $node, false, -1, 0)); + if($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { + $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $node, false, -1, 0)); + } if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { $shares = array_merge($shares, $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $node, false, -1, 0)); } @@ -541,7 +580,12 @@ class ShareAPIController extends OCSController { $userShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $path, $reshares, -1, 0); $groupShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $path, $reshares, -1, 0); $linkShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_LINK, $path, $reshares, -1, 0); - $shares = array_merge($userShares, $groupShares, $linkShares); + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { + $mailShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_EMAIL, $path, $reshares, -1, 0); + } else { + $mailShares = []; + } + $shares = array_merge($userShares, $groupShares, $linkShares, $mailShares); if ($this->shareManager->outgoingServer2ServerSharesAllowed()) { $federatedShares = $this->shareManager->getSharesBy($this->currentUser, \OCP\Share::SHARE_TYPE_REMOTE, $path, $reshares, -1, 0); @@ -774,14 +818,25 @@ class ShareAPIController extends OCSController { // First check if it is an internal share. try { $share = $this->shareManager->getShareById('ocinternal:' . $id); + return $share; } catch (ShareNotFound $e) { - if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { - throw new ShareNotFound(); - } - - $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); + // Do nothing, just try the other share type } + try { + if ($this->shareManager->shareProviderExists(\OCP\Share::SHARE_TYPE_EMAIL)) { + $share = $this->shareManager->getShareById('ocMailShare:' . $id); + return $share; + } + } catch (ShareNotFound $e) { + // Do nothing, just try the other share type + } + + if (!$this->shareManager->outgoingServer2ServerSharesAllowed()) { + throw new ShareNotFound(); + } + $share = $this->shareManager->getShareById('ocFederatedSharing:' . $id); + return $share; } diff --git a/apps/files_sharing/lib/Controller/ShareesAPIController.php b/apps/files_sharing/lib/Controller/ShareesAPIController.php index 99c6b55240..5e01c9bfb0 100644 --- a/apps/files_sharing/lib/Controller/ShareesAPIController.php +++ b/apps/files_sharing/lib/Controller/ShareesAPIController.php @@ -273,15 +273,15 @@ class ShareesAPIController extends OCSController { /** * @param string $search - * @return array possible sharees + * @return array */ protected function getRemote($search) { - $this->result['remotes'] = []; + $result = ['results' => [], 'exact' => []]; // Search in contacts //@todo Pagination missing $addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']); - $foundRemoteById = false; + $result['exactIdMatch'] = false; foreach ($addressBookContacts as $contact) { if (isset($contact['isLocalSystemBook'])) { continue; @@ -295,10 +295,10 @@ class ShareesAPIController extends OCSController { list(, $serverUrl) = $this->splitUserRemote($cloudId); if (strtolower($contact['FN']) === strtolower($search) || strtolower($cloudId) === strtolower($search)) { if (strtolower($cloudId) === strtolower($search)) { - $foundRemoteById = true; + $result['exactIdMatch'] = true; } - $this->result['exact']['remotes'][] = [ - 'label' => $contact['FN'], + $result['exact'][] = [ + 'label' => $contact['FN'] . " ($cloudId)", 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => $cloudId, @@ -306,8 +306,8 @@ class ShareesAPIController extends OCSController { ], ]; } else { - $this->result['remotes'][] = [ - 'label' => $contact['FN'], + $result['results'][] = [ + 'label' => $contact['FN'] . " ($cloudId)", 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => $cloudId, @@ -320,11 +320,11 @@ class ShareesAPIController extends OCSController { } if (!$this->shareeEnumeration) { - $this->result['remotes'] = []; + $result['results'] = []; } - if (!$foundRemoteById && substr_count($search, '@') >= 1 && $this->offset === 0) { - $this->result['exact']['remotes'][] = [ + if (!$result['exactIdMatch'] && substr_count($search, '@') >= 1 && $this->offset === 0) { + $result['exact'][] = [ 'label' => $search, 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, @@ -334,6 +334,8 @@ class ShareesAPIController extends OCSController { } $this->reachedEndFor[] = 'remotes'; + + return $result; } /** @@ -404,68 +406,6 @@ class ShareesAPIController extends OCSController { return $remote; } - /** - * @param string $search - */ - protected function getEmails($search) { - $this->result['emails'] = []; - $this->result['exact']['emails'] = []; - - $foundEmail = false; - - // Search in contacts - //@todo Pagination missing - $addressBookContacts = $this->contactsManager->search($search, ['FN', 'EMAIL']); - foreach ($addressBookContacts as $contact) { - if (!isset($contact['EMAIL'])) { - continue; - } - - $emails = $contact['EMAIL']; - if (!is_array($emails)) { - $emails = [$emails]; - } - - foreach ($emails as $email) { - if (strtolower($search) === strtolower($contact['FN']) || - strtolower($search) === strtolower($email) - ) { - if (strtolower($search) === strtolower($email)) { - $foundEmail = true; - } - - $this->result['exact']['emails'][] = [ - 'label' => $contact['FN'], - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $email, - ], - ]; - } else if ($this->shareeEnumeration) { - $this->result['emails'][] = [ - 'label' => $contact['FN'], - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $email, - ], - ]; - } - } - } - - if (!$foundEmail && substr_count($search, '@') >= 1 && $this->offset === 0) { - $this->result['exact']['emails'][] = [ - 'label' => $search, - 'value' => [ - 'shareType' => Share::SHARE_TYPE_EMAIL, - 'shareWith' => $search, - ], - ]; - } - - $this->reachedEndFor[] = 'emails'; - } - /** * @NoAdminRequired * @@ -487,17 +427,16 @@ class ShareesAPIController extends OCSController { $shareTypes = [ Share::SHARE_TYPE_USER, + Share::SHARE_TYPE_REMOTE, + Share::SHARE_TYPE_EMAIL ]; if ($this->shareManager->allowGroupSharing()) { $shareTypes[] = Share::SHARE_TYPE_GROUP; } - $shareTypes[] = Share::SHARE_TYPE_EMAIL; - $shareTypes[] = Share::SHARE_TYPE_REMOTE; - - if (is_array($shareType)) { - $shareTypes = array_intersect($shareTypes, $shareType); + if (isset($_GET['shareType']) && is_array($_GET['shareType'])) { + $shareTypes = array_intersect($shareTypes, $_GET['shareType']); sort($shareTypes); } else if (is_numeric($shareType)) { $shareTypes = array_intersect($shareTypes, [(int) $shareType]); @@ -509,6 +448,11 @@ class ShareesAPIController extends OCSController { $shareTypes = array_diff($shareTypes, [Share::SHARE_TYPE_REMOTE]); } + if (!$this->shareManager->shareProviderExists(Share::SHARE_TYPE_EMAIL)) { + // Remove mail shares from type array, because the share provider is not loaded + $shareTypes = array_diff($shareTypes, [Share::SHARE_TYPE_EMAIL]); + } + $this->shareWithGroupOnly = $this->config->getAppValue('core', 'shareapi_only_share_with_group_members', 'no') === 'yes'; $this->shareeEnumeration = $this->config->getAppValue('core', 'shareapi_allow_share_dialog_user_enumeration', 'yes') === 'yes'; $this->limit = (int) $perPage; @@ -560,13 +504,30 @@ class ShareesAPIController extends OCSController { } // Get remote + $remoteResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; if (in_array(Share::SHARE_TYPE_REMOTE, $shareTypes)) { - $this->getRemote($search); + $remoteResults = $this->getRemote($search); } - // Get email + $mailResults = ['results' => [], 'exact' => [], 'exactIdMatch' => false]; if (in_array(Share::SHARE_TYPE_EMAIL, $shareTypes)) { - $this->getEmails($search); + $mailResults = $this->getEmail($search); + } + + // if we have a exact match, either for the federated cloud id or for the + // email address we only return the exact match. It is highly unlikely + // that the exact same email address and federated cloud id exists + if ($mailResults['exactIdMatch'] && !$remoteResults['exactIdMatch']) { + $this->result['emails'] = $mailResults['results']; + $this->result['exact']['emails'] = $mailResults['exact']; + } else if (!$mailResults['exactIdMatch'] && $remoteResults['exactIdMatch']) { + $this->result['remotes'] = $remoteResults['results']; + $this->result['exact']['remotes'] = $remoteResults['exact']; + } else { + $this->result['remotes'] = $remoteResults['results']; + $this->result['exact']['remotes'] = $remoteResults['exact']; + $this->result['emails'] = $mailResults['results']; + $this->result['exact']['emails'] = $mailResults['exact']; } $response = new Http\DataResponse($this->result); @@ -583,6 +544,70 @@ class ShareesAPIController extends OCSController { return $response; } + /** + * @param string $search + * @return array + */ + protected function getEmail($search) { + $result = ['results' => [], 'exact' => []]; + + // Search in contacts + //@todo Pagination missing + $addressBookContacts = $this->contactsManager->search($search, ['EMAIL', 'FN']); + $result['exactIdMatch'] = false; + foreach ($addressBookContacts as $contact) { + if (isset($contact['isLocalSystemBook'])) { + continue; + } + if (isset($contact['EMAIL'])) { + $emailAddresses = $contact['EMAIL']; + if (!is_array($emailAddresses)) { + $emailAddresses = [$emailAddresses]; + } + foreach ($emailAddresses as $emailAddress) { + if (strtolower($contact['FN']) === strtolower($search) || strtolower($emailAddress) === strtolower($search)) { + if (strtolower($emailAddress) === strtolower($search)) { + $result['exactIdMatch'] = true; + } + $result['exact'][] = [ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $emailAddress, + ], + ]; + } else { + $result['results'][] = [ + 'label' => $contact['FN'] . " ($emailAddress)", + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $emailAddress, + ], + ]; + } + } + } + } + + if (!$this->shareeEnumeration) { + $result['results'] = []; + } + + if (!$result['exactIdMatch'] && filter_var($search, FILTER_VALIDATE_EMAIL)) { + $result['exact'][] = [ + 'label' => $search, + 'value' => [ + 'shareType' => Share::SHARE_TYPE_EMAIL, + 'shareWith' => $search, + ], + ]; + } + + $this->reachedEndFor[] = 'emails'; + + return $result; + } + /** * Generates a bunch of pagination links for the current page * diff --git a/apps/files_sharing/tests/ApiTest.php b/apps/files_sharing/tests/ApiTest.php index 6ab0cb4e1a..540905a7dc 100644 --- a/apps/files_sharing/tests/ApiTest.php +++ b/apps/files_sharing/tests/ApiTest.php @@ -234,6 +234,7 @@ class ApiTest extends TestCase { function testEnfoceLinkPassword() { + $password = md5(time()); $appConfig = \OC::$server->getAppConfig(); $appConfig->setValue('core', 'shareapi_enforce_links_password', 'yes'); @@ -257,14 +258,14 @@ class ApiTest extends TestCase { // share with password should succeed $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1); - $result = $ocs->createShare($this->folder, \OCP\Constants::PERMISSION_ALL, \OCP\Share::SHARE_TYPE_LINK, null, 'false', 'bar'); + $result = $ocs->createShare($this->folder, \OCP\Constants::PERMISSION_ALL, \OCP\Share::SHARE_TYPE_LINK, null, 'false', $password); $ocs->cleanup(); $data = $result->getData(); // setting new password should succeed $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1); - $ocs->updateShare($data['id'], null, 'bar'); + $ocs->updateShare($data['id'], null, $password); $ocs->cleanup(); // removing password should fail @@ -887,6 +888,9 @@ class ApiTest extends TestCase { * @depends testCreateShareLink */ function testUpdateShare() { + + $password = md5(time()); + $node1 = $this->userFolder->get($this->filename); $share1 = $this->shareManager->newShare(); $share1->setNode($node1) @@ -915,7 +919,7 @@ class ApiTest extends TestCase { $this->assertNull($share2->getPassword()); $ocs = $this->createOCS(self::TEST_FILES_SHARING_API_USER1); - $ocs->updateShare($share2->getId(), null, 'foo'); + $ocs->updateShare($share2->getId(), null, $password); $ocs->cleanup(); $share2 = $this->shareManager->getShareById('ocinternal:' . $share2->getId()); diff --git a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php index 6ad0576b6f..890fdb6eda 100644 --- a/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareAPIControllerTest.php @@ -23,8 +23,10 @@ */ namespace OCA\Files_Sharing\Tests\Controller; +use OC\ContactsManager; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\Contacts; use OCP\Files\Folder; use OCP\IL10N; use OCA\Files_Sharing\Controller\ShareAPIController; diff --git a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php index 6ee1ff596e..e8ee55d184 100644 --- a/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php +++ b/apps/files_sharing/tests/Controller/ShareesAPIControllerTest.php @@ -771,28 +771,44 @@ class ShareesAPIControllerTest extends TestCase { $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); } + /** + * @dataProvider dataGetRemote + * + * @param string $searchTerm + * @param array $contacts + * @param bool $shareeEnumeration + * @param array $expected + * @param bool $reachedEnd + */ + public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) { + $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); + $this->contactsManager->expects($this->any()) + ->method('search') + ->with($searchTerm, ['CLOUD', 'FN']) + ->willReturn($contacts); + + $result = $this->invokePrivate($this->sharees, 'getRemote', [$searchTerm]); + + $this->assertEquals($expected, $result); + $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); + } + public function dataGetRemote() { return [ - ['test', [], true, [], [], true], - ['test', [], false, [], [], true], + ['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], + ['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], [ 'test@remote', [], true, - [ - ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], - ], - [], + ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], true, ], [ 'test@remote', [], false, - [ - ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], - ], - [], + ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], true, ], [ @@ -814,10 +830,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], true, - [], - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']], - ], + ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [], 'exactIdMatch' => false], true, ], [ @@ -839,8 +852,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], false, - [], - [], + ['results' => [], 'exact' => [], 'exactIdMatch' => false], true, ], [ @@ -862,12 +874,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], true, - [ - ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], - ], - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']], - ], + ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], true, ], [ @@ -889,10 +896,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], false, - [ - ['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']], - ], - [], + ['results' => [], 'exact' => [['label' => 'test@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'test@remote']]], 'exactIdMatch' => false], true, ], [ @@ -914,10 +918,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], true, - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']], - ], - [], + ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], true, ], [ @@ -939,10 +940,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], false, - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']], - ], - [], + ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'username@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], true, ], // contact with space @@ -965,10 +963,7 @@ class ShareesAPIControllerTest extends TestCase { ], ], false, - [ - ['label' => 'User Name @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']], - ], - [], + ['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user name@localhost', 'server' => 'localhost']]], 'exactIdMatch' => true], true, ], // remote with space, no contact @@ -991,62 +986,57 @@ class ShareesAPIControllerTest extends TestCase { ], ], false, - [ - ['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']], - ], - [], + ['results' => [], 'exact' => [['label' => 'user space@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'user space@remote']]], 'exactIdMatch' => false], true, ], ]; } /** - * @dataProvider dataGetRemote + * @dataProvider dataGetEmail * * @param string $searchTerm * @param array $contacts * @param bool $shareeEnumeration - * @param array $exactExpected * @param array $expected * @param bool $reachedEnd */ - public function testGetRemote($searchTerm, $contacts, $shareeEnumeration, $exactExpected, $expected, $reachedEnd) { + public function testGetEmail($searchTerm, $contacts, $shareeEnumeration, $expected, $reachedEnd) { $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); $this->contactsManager->expects($this->any()) ->method('search') - ->with($searchTerm, ['CLOUD', 'FN']) + ->with($searchTerm, ['EMAIL', 'FN']) ->willReturn($contacts); - $this->invokePrivate($this->sharees, 'getRemote', [$searchTerm]); - $result = $this->invokePrivate($this->sharees, 'result'); + $result = $this->invokePrivate($this->sharees, 'getEmail', [$searchTerm]); - $this->assertEquals($exactExpected, $result['exact']['remotes']); - $this->assertEquals($expected, $result['remotes']); + $this->assertEquals($expected, $result); $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); } - public function dataGetEmails() { + public function dataGetEmail() { return [ - ['test', [], true, [], [], true], - ['test', [], false, [], [], true], + ['test', [], true, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], + ['test', [], false, ['results' => [], 'exact' => [], 'exactIdMatch' => false], true], [ 'test@remote.com', [], true, - [ - ['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']], - ], + ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], + true, + ], + [ // no valid email address + 'test@remote', [], true, + ['results' => [], 'exact' => [], 'exactIdMatch' => false], + true, ], [ 'test@remote.com', [], false, - [ - ['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']], - ], - [], + ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], true, ], [ @@ -1063,15 +1053,12 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], true, - [], - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']], - ], + ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [], 'exactIdMatch' => false], true, ], [ @@ -1088,13 +1075,12 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], false, - [], - [], + ['results' => [], 'exact' => [], 'exactIdMatch' => false], true, ], [ @@ -1111,17 +1097,12 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], true, - [ - ['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']], - ], - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']], - ], + ['results' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], true, ], [ @@ -1138,19 +1119,16 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], false, - [ - ['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']], - ], - [], + ['results' => [], 'exact' => [['label' => 'test@remote.com', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'test@remote.com']]], 'exactIdMatch' => false], true, ], [ - 'username@localhost.com', + 'username@localhost', [ [ 'FN' => 'User3 @ Localhost', @@ -1163,19 +1141,16 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], true, - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']], - ], - [], + ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true], true, ], [ - 'username@localhost.com', + 'username@localhost', [ [ 'FN' => 'User3 @ Localhost', @@ -1188,20 +1163,40 @@ class ShareesAPIControllerTest extends TestCase { [ 'FN' => 'User @ Localhost', 'EMAIL' => [ - 'username@localhost.com', + 'username@localhost', ], ], ], false, - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']], - ], - [], + ['results' => [], 'exact' => [['label' => 'User @ Localhost (username@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost']]], 'exactIdMatch' => true], true, ], - // Test single email + // contact with space [ - 'username@localhost.com', + 'user name@localhost', + [ + [ + 'FN' => 'User3 @ Localhost', + ], + [ + 'FN' => 'User2 @ Localhost', + 'EMAIL' => [ + ], + ], + [ + 'FN' => 'User Name @ Localhost', + 'EMAIL' => [ + 'user name@localhost', + ], + ], + ], + false, + ['results' => [], 'exact' => [['label' => 'User Name @ Localhost (user name@localhost)', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'user name@localhost']]], 'exactIdMatch' => true], + true, + ], + // remote with space, no contact + [ + 'user space@remote.com', [ [ 'FN' => 'User3 @ Localhost', @@ -1213,137 +1208,106 @@ class ShareesAPIControllerTest extends TestCase { ], [ 'FN' => 'User @ Localhost', - 'EMAIL' => 'username@localhost.com', + 'EMAIL' => [ + 'username@localhost', + ], ], ], false, - [ - ['label' => 'User @ Localhost', 'value' => ['shareType' => Share::SHARE_TYPE_EMAIL, 'shareWith' => 'username@localhost.com']], - ], - [], + ['results' => [], 'exact' => [], 'exactIdMatch' => false], true, ], ]; } - /** - * @dataProvider dataGetEmails - * - * @param string $searchTerm - * @param array $contacts - * @param bool $shareeEnumeration - * @param array $exactExpected - * @param array $expected - * @param bool $reachedEnd - */ - public function testGetEmails($searchTerm, $contacts, $shareeEnumeration, $exactExpected, $expected, $reachedEnd) { - $this->invokePrivate($this->sharees, 'shareeEnumeration', [$shareeEnumeration]); - $this->contactsManager->expects($this->any()) - ->method('search') - ->with($searchTerm, ['FN', 'EMAIL']) - ->willReturn($contacts); - - $this->invokePrivate($this->sharees, 'getEmails', [$searchTerm]); - $result = $this->invokePrivate($this->sharees, 'result'); - - $this->assertEquals($exactExpected, $result['exact']['emails']); - $this->assertEquals($expected, $result['emails']); - $this->assertCount((int) $reachedEnd, $this->invokePrivate($this->sharees, 'reachedEndFor')); - } - public function dataSearch() { - $allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_EMAIL, Share::SHARE_TYPE_REMOTE]; + $allTypes = [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE, Share::SHARE_TYPE_EMAIL]; return [ - [[], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + [[], '', 'yes', true, true, $allTypes, false, true, true], // Test itemType [[ 'search' => '', - ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'search' => 'foobar', - ], '', 'yes', true, 'foobar', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'search' => 0, - ], '', 'yes', true, '0', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], // Test itemType [[ 'itemType' => '', - ], '', 'yes', true, '', '', $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'itemType' => 'folder', - ], '', 'yes', true, '', 'folder', $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'itemType' => 0, - ], '', 'yes', true, '', '0', $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], // Test shareType [[ - ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'shareType' => 0, - ], '', 'yes', true, '', null, [0], 1, 200, false, true, true], + ], '', 'yes', true, false, [0], false, true, true], [[ 'shareType' => '0', - ], '', 'yes', true, '', null, [0], 1, 200, false, true, true], + ], '', 'yes', true, false, [0], false, true, true], [[ 'shareType' => 1, - ], '', 'yes', true, '', null, [1], 1, 200, false, true, true], + ], '', 'yes', true, false, [1], false, true, true], [[ 'shareType' => 12, - ], '', 'yes', true, '', null, [], 1, 200, false, true, true], + ], '', 'yes', true, false, [], false, true, true], [[ 'shareType' => 'foobar', - ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'shareType' => [0, 1, 2], - ], '', 'yes', true, '', null, [0, 1], 1, 200, false, true, true], + ], '', 'yes', false, false, [0, 1], false, true, true], [[ 'shareType' => [0, 1], - ], '', 'yes', true, '', null, [0, 1], 1, 200, false, true, true], + ], '', 'yes', false, false, [0, 1], false, true, true], [[ 'shareType' => $allTypes, - ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'shareType' => $allTypes, - ], '', 'yes', false, '', null, [0, 1, 4], 1, 200, false, true, true], + ], '', 'yes', false, false, [0, 1], false, true, true], [[ 'shareType' => $allTypes, - ], '', 'yes', true, '', null, [0, 4, 6], 1, 200, false, true, false], + ], '', 'yes', true, false, [0, 6], false, true, false], [[ 'shareType' => $allTypes, - ], '', 'yes', false, '', null, [0, 4], 1, 200, false, true, false], + ], '', 'yes', false, true, [0, 4], false, true, false], // Test pagination [[ 'page' => 1, - ], '', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'page' => 10, - ], '', 'yes', true, '', null, $allTypes, 10, 200, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], // Test perPage [[ 'perPage' => 1, - ], '', 'yes', true, '', null, $allTypes, 1, 1, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], [[ 'perPage' => 10, - ], '', 'yes', true, '', null, $allTypes, 1, 10, false, true, true], + ], '', 'yes', true, true, $allTypes, false, true, true], // Test $shareWithGroupOnly setting - [[], 'no', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], - [[], 'yes', 'yes', true, '', null, $allTypes, 1, 200, true, true, true], + [[], 'no', 'yes', true, true, $allTypes, false, true, true], + [[], 'yes', 'yes', true, true, $allTypes, true, true, true], // Test $shareeEnumeration setting - [[], 'no', 'yes', true, '', null, $allTypes, 1, 200, false, true, true], - [[], 'no', 'no', true, '', null, $allTypes, 1, 200, false, false, true], - - // Test keep case for search - [[ - 'search' => 'foo@example.com/ownCloud', - ], '', 'yes', true, 'foo@example.com/ownCloud', null, $allTypes, 1, 200, false, true, true], + [[], 'no', 'yes', true, true, $allTypes, false, true, true], + [[], 'no', 'no', true, true, $allTypes, false, false, true], ]; } @@ -1354,16 +1318,12 @@ class ShareesAPIControllerTest extends TestCase { * @param string $apiSetting * @param string $enumSetting * @param bool $remoteSharingEnabled - * @param string $search - * @param string $itemType * @param array $shareTypes - * @param int $page - * @param int $perPage * @param bool $shareWithGroupOnly * @param bool $shareeEnumeration * @param bool $allowGroupSharing */ - public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $search, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly, $shareeEnumeration, $allowGroupSharing) { + public function testSearch($getData, $apiSetting, $enumSetting, $remoteSharingEnabled, $emailSharingEnabled, $shareTypes, $shareWithGroupOnly, $shareeEnumeration, $allowGroupSharing) { $search = isset($getData['search']) ? $getData['search'] : ''; $itemType = isset($getData['itemType']) ? $getData['itemType'] : null; $page = isset($getData['page']) ? $getData['page'] : 1; @@ -1399,11 +1359,10 @@ class ShareesAPIControllerTest extends TestCase { $this->getMockBuilder('OCP\ILogger')->disableOriginalConstructor()->getMock(), $this->shareManager ]) - ->setMethods(array('searchSharees', 'isRemoteSharingAllowed')) + ->setMethods(array('searchSharees', 'isRemoteSharingAllowed', 'shareProviderExists')) ->getMock(); $sharees->expects($this->once()) ->method('searchSharees') - ->with($search, $itemType, $shareTypes, $page, $perPage) ->willReturnCallback(function ($isearch, $iitemType, $ishareTypes, $ipage, $iperPage) use ($search, $itemType, $shareTypes, $page, $perPage) { @@ -1411,7 +1370,10 @@ class ShareesAPIControllerTest extends TestCase { // We are doing strict comparisons here, so we can differ 0/'' and null on shareType/itemType $this->assertSame($search, $isearch); $this->assertSame($itemType, $iitemType); - $this->assertSame($shareTypes, $ishareTypes); + $this->assertSame(count($shareTypes), count($ishareTypes)); + foreach($shareTypes as $expected) { + $this->assertTrue(in_array($expected, $ishareTypes)); + } $this->assertSame($page, $ipage); $this->assertSame($perPage, $iperPage); return new Http\DataResponse(); @@ -1421,6 +1383,11 @@ class ShareesAPIControllerTest extends TestCase { ->with($itemType) ->willReturn($remoteSharingEnabled); + $this->shareManager->expects($this->any()) + ->method('shareProviderExists') + ->with(\OCP\Share::SHARE_TYPE_EMAIL) + ->willReturn($emailSharingEnabled); + $this->assertInstanceOf(Http\DataResponse::class, $sharees->search($search, $itemType, $page, $perPage, $shareType)); $this->assertSame($shareWithGroupOnly, $this->invokePrivate($sharees, 'shareWithGroupOnly')); @@ -1519,7 +1486,7 @@ class ShareesAPIControllerTest extends TestCase { public function dataSearchSharees() { return [ - ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], [], + ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], [ 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []], 'users' => [], @@ -1527,7 +1494,7 @@ class ShareesAPIControllerTest extends TestCase { 'remotes' => [], 'emails' => [], ], false], - ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], [], + ['test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_GROUP, Share::SHARE_TYPE_REMOTE], 1, 2, false, [], [], ['results' => [], 'exact' => [], 'exactIdMatch' => false], [ 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []], 'users' => [], @@ -1541,7 +1508,7 @@ class ShareesAPIControllerTest extends TestCase { ], [ ['label' => 'testgroup1', 'value' => ['shareType' => Share::SHARE_TYPE_GROUP, 'shareWith' => 'testgroup1']], ], [ - ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], + 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false, ], [ 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []], @@ -1562,7 +1529,7 @@ class ShareesAPIControllerTest extends TestCase { 'test', 'folder', [Share::SHARE_TYPE_USER, Share::SHARE_TYPE_REMOTE], 1, 2, false, [ ['label' => 'test One', 'value' => ['shareType' => Share::SHARE_TYPE_USER, 'shareWith' => 'test1']], ], null, [ - ['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']], + 'results' => [['label' => 'testz@remote', 'value' => ['shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => 'testz@remote']]], 'exact' => [], 'exactIdMatch' => false ], [ 'exact' => ['users' => [], 'groups' => [], 'remotes' => [], 'emails' => []], @@ -1660,14 +1627,11 @@ class ShareesAPIControllerTest extends TestCase { $result['groups'] = $mockedGroupsResult; $this->invokePrivate($sharees, 'result', [$result]); }); + $sharees->expects(($mockedRemotesResult === null) ? $this->never() : $this->once()) ->method('getRemote') ->with($searchTerm) - ->willReturnCallback(function() use ($sharees, $mockedRemotesResult) { - $result = $this->invokePrivate($sharees, 'result'); - $result['remotes'] = $mockedRemotesResult; - $this->invokePrivate($sharees, 'result', [$result]); - }); + ->willReturn($mockedRemotesResult); $ocs = $this->invokePrivate($sharees, 'searchSharees', [$searchTerm, $itemType, $shareTypes, $page, $perPage, $shareWithGroupOnly]); $this->assertInstanceOf('\OCP\AppFramework\Http\DataResponse', $ocs); diff --git a/apps/sharebymail/appinfo/app.php b/apps/sharebymail/appinfo/app.php new file mode 100644 index 0000000000..5ef7b6f18c --- /dev/null +++ b/apps/sharebymail/appinfo/app.php @@ -0,0 +1,24 @@ + + * + * @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 . + * + */ + +$settings = new \OCA\ShareByMail\Settings(); + +\OCP\Util::connectHook('\OCP\Config', 'js', $settings, 'announceShareProvider'); diff --git a/apps/sharebymail/appinfo/info.xml b/apps/sharebymail/appinfo/info.xml new file mode 100644 index 0000000000..f1771fc955 --- /dev/null +++ b/apps/sharebymail/appinfo/info.xml @@ -0,0 +1,15 @@ + + + sharebymail + Share by mail + Share provider which allows you to share files by mail + AGPL + Bjoern Schiessle + 1.0.0 + ShareByMail + other + + + + + diff --git a/apps/sharebymail/lib/Settings.php b/apps/sharebymail/lib/Settings.php new file mode 100644 index 0000000000..4ab1622425 --- /dev/null +++ b/apps/sharebymail/lib/Settings.php @@ -0,0 +1,38 @@ + + * + * @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 . + * + */ + + +namespace OCA\ShareByMail; + + +class Settings { + + /** + * announce that the share-by-mail share provider is enabled + * + * @param array $settings + */ + public function announceShareProvider(array $settings) { + $array = json_decode($settings['array']['oc_appconfig'], true); + $array['shareByMailEnabled'] = true; + $settings['array']['oc_appconfig'] = json_encode($array); + } +} diff --git a/apps/sharebymail/lib/ShareByMailProvider.php b/apps/sharebymail/lib/ShareByMailProvider.php new file mode 100644 index 0000000000..a332bd13d8 --- /dev/null +++ b/apps/sharebymail/lib/ShareByMailProvider.php @@ -0,0 +1,709 @@ + + * + * @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 . + * + */ + +namespace OCA\ShareByMail; + +use OC\HintException; +use OC\Share20\Exception\InvalidShare; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\ILogger; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\Mail\IMailer; +use OCP\Security\ISecureRandom; +use OC\Share20\Share; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IShare; +use OCP\Share\IShareProvider; + +/** + * Class ShareByMail + * + * @package OCA\ShareByMail + */ +class ShareByMailProvider implements IShareProvider { + + /** @var IDBConnection */ + private $dbConnection; + + /** @var ILogger */ + private $logger; + + /** @var ISecureRandom */ + private $secureRandom; + + /** @var IUserManager */ + private $userManager; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var IL10N */ + private $l; + + /** @var IMailer */ + private $mailer; + + /** @var IURLGenerator */ + private $urlGenerator; + + /** + * Return the identifier of this provider. + * + * @return string Containing only [a-zA-Z0-9] + */ + public function identifier() { + return 'ocShareByMail'; + } + + /** + * DefaultShareProvider constructor. + * + * @param IDBConnection $connection + * @param ISecureRandom $secureRandom + * @param IUserManager $userManager + * @param IRootFolder $rootFolder + * @param IL10N $l + * @param ILogger $logger + * @param IMailer $mailer + * @param IURLGenerator $urlGenerator + */ + public function __construct( + IDBConnection $connection, + ISecureRandom $secureRandom, + IUserManager $userManager, + IRootFolder $rootFolder, + IL10N $l, + ILogger $logger, + IMailer $mailer, + IURLGenerator $urlGenerator + ) { + $this->dbConnection = $connection; + $this->secureRandom = $secureRandom; + $this->userManager = $userManager; + $this->rootFolder = $rootFolder; + $this->l = $l; + $this->logger = $logger; + $this->mailer = $mailer; + $this->urlGenerator = $urlGenerator; + } + + /** + * Share a path + * + * @param IShare $share + * @return IShare The share object + * @throws ShareNotFound + * @throws \Exception + */ + public function create(IShare $share) { + + $shareWith = $share->getSharedWith(); + /* + * Check if file is not already shared with the remote user + */ + $alreadyShared = $this->getSharedWith($shareWith, \OCP\Share::SHARE_TYPE_EMAIL, $share->getNode(), 1, 0); + if (!empty($alreadyShared)) { + $message = 'Sharing %s failed, this item is already shared with %s'; + $message_t = $this->l->t('Sharing %s failed, this item is already shared with %s', array($share->getNode()->getName(), $shareWith)); + $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'Federated File Sharing']); + throw new \Exception($message_t); + } + + $shareId = $this->createMailShare($share); + + $data = $this->getRawShare($shareId); + return $this->createShareObject($data); + + } + + /** + * @param IShare $share + * @return int + * @throws \Exception + */ + protected function createMailShare(IShare $share) { + $share->setToken($this->generateToken()); + $shareId = $this->addShareToDB( + $share->getNodeId(), + $share->getNodeType(), + $share->getSharedWith(), + $share->getSharedBy(), + $share->getShareOwner(), + $share->getPermissions(), + $share->getToken() + ); + + try { + $link = $this->urlGenerator->linkToRouteAbsolute('files_sharing.sharecontroller.showShare', + ['token' => $share->getToken()]); + $this->sendMailNotification($share->getNode()->getName(), + $link, + $share->getShareOwner(), + $share->getSharedBy(), $share->getSharedWith()); + } catch (HintException $hintException) { + $this->logger->error('Failed to send share by mail: ' . $hintException->getMessage()); + $this->removeShareFromTable($shareId); + throw $hintException; + } catch (\Exception $e) { + $this->logger->error('Failed to send share by mail: ' . $e->getMessage()); + $this->removeShareFromTable($shareId); + throw new HintException('Failed to send share by mail', + $this->l->t('Failed to send share by E-mail')); + } + + return $shareId; + + } + + protected function sendMailNotification($filename, $link, $owner, $initiator, $shareWith) { + if ($owner === $initiator) { + $subject = (string)$this->l->t('%s shared »%s« with you', array($owner, $filename)); + } else { + $subject = (string)$this->l->t('%s shared »%s« with you on behalf of %s', array($owner, $filename, $initiator)); + } + + $message = $this->mailer->createMessage(); + $htmlBody = $this->createMailBody('mail', $filename, $link, $owner, $initiator); + $textBody = $this->createMailBody('altmail', $filename, $link, $owner, $initiator); + $message->setTo([$shareWith]); + $message->setSubject($subject); + $message->setBody($textBody, 'text/plain'); + $message->setHtmlBody($htmlBody); + $this->mailer->send($message); + + } + + /** + * create mail body + * + * @param $filename + * @param $link + * @param $owner + * @param $initiator + * @return string plain text mail + * @throws HintException + */ + protected function createMailBody($template, $filename, $link, $owner, $initiator) { + + $mailBodyTemplate = new \OC_Template('sharebymail', $template, ''); + $mailBodyTemplate->assign ('filename', $filename); + $mailBodyTemplate->assign ('link', $link); + $mailBodyTemplate->assign ('owner', $owner); + $mailBodyTemplate->assign ('initiator', $initiator); + $mailBodyTemplate->assign ('onBehalfOf', $initiator !== $owner); + $mailBody = $mailBodyTemplate->fetchPage(); + + if (is_string($mailBody)) { + return $mailBody; + } + + throw new HintException('Failed to create the E-mail', + $this->l->t('Failed to create the E-mail')); + } + + /** + * generate share token + * + * @return string + */ + protected function generateToken() { + $token = $this->secureRandom->generate( + 15, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); + return $token; + } + + /** + * Get all children of this share + * + * @param IShare $parent + * @return IShare[] + */ + public function getChildren(IShare $parent) { + $children = []; + + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('parent', $qb->createNamedParameter($parent->getId()))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) + ->orderBy('id'); + + $cursor = $qb->execute(); + while($data = $cursor->fetch()) { + $children[] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + return $children; + } + + /** + * add share to the database and return the ID + * + * @param int $itemSource + * @param string $itemType + * @param string $shareWith + * @param string $sharedBy + * @param string $uidOwner + * @param int $permissions + * @param string $token + * @return int + */ + protected function addShareToDB($itemSource, $itemType, $shareWith, $sharedBy, $uidOwner, $permissions, $token) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->insert('share') + ->setValue('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) + ->setValue('item_type', $qb->createNamedParameter($itemType)) + ->setValue('item_source', $qb->createNamedParameter($itemSource)) + ->setValue('file_source', $qb->createNamedParameter($itemSource)) + ->setValue('share_with', $qb->createNamedParameter($shareWith)) + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) + ->setValue('permissions', $qb->createNamedParameter($permissions)) + ->setValue('token', $qb->createNamedParameter($token)) + ->setValue('stime', $qb->createNamedParameter(time())); + + /* + * Added to fix https://github.com/owncloud/core/issues/22215 + * Can be removed once we get rid of ajax/share.php + */ + $qb->setValue('file_target', $qb->createNamedParameter('')); + + $qb->execute(); + $id = $qb->getLastInsertId(); + + return (int)$id; + } + + /** + * Update a share + * + * @param IShare $share + * @return IShare The share object + */ + public function update(IShare $share) { + /* + * We allow updating the permissions of mail shares + */ + $qb = $this->dbConnection->getQueryBuilder(); + $qb->update('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))) + ->set('permissions', $qb->createNamedParameter($share->getPermissions())) + ->set('uid_owner', $qb->createNamedParameter($share->getShareOwner())) + ->set('uid_initiator', $qb->createNamedParameter($share->getSharedBy())) + ->execute(); + + return $share; + } + + /** + * @inheritdoc + */ + public function move(IShare $share, $recipient) { + /** + * nothing to do here, mail shares are only outgoing shares + */ + return $share; + } + + /** + * Delete a share (owner unShares the file) + * + * @param IShare $share + */ + public function delete(IShare $share) { + $this->removeShareFromTable($share->getId()); + } + + /** + * @inheritdoc + */ + public function deleteFromSelf(IShare $share, $recipient) { + // nothing to do here, mail shares are only outgoing shares + return; + } + + /** + * @inheritdoc + */ + public function getSharesBy($userId, $shareType, $node, $reshares, $limit, $offset) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + //Special case for old shares created via the web UI + $or1 = $qb->expr()->andX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->isNull('uid_initiator') + ); + + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)), + $or1 + ) + ); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + + $qb->setFirstResult($offset); + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getShareById($id, $recipientId = null) { + $qb = $this->dbConnection->getQueryBuilder(); + + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound(); + } + + try { + $share = $this->createShareObject($data); + } catch (InvalidShare $e) { + throw new ShareNotFound(); + } + + return $share; + } + + /** + * Get shares for a given path + * + * @param \OCP\Files\Node $path + * @return IShare[] + */ + public function getSharesByPath(Node $path) { + $qb = $this->dbConnection->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId()))) + ->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) + ->execute(); + + $shares = []; + while($data = $cursor->fetch()) { + $shares[] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + return $shares; + } + + /** + * @inheritdoc + */ + public function getSharedWith($userId, $shareType, $node, $limit, $offset) { + /** @var IShare[] $shares */ + $shares = []; + + //Get shares directly with this user + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share'); + + // Order by id + $qb->orderBy('id'); + + // Set limit and offset + if ($limit !== -1) { + $qb->setMaxResults($limit); + } + $qb->setFirstResult($offset); + + $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))); + $qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($userId))); + + // Filter by node if provided + if ($node !== null) { + $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId()))); + } + + $cursor = $qb->execute(); + + while($data = $cursor->fetch()) { + $shares[] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + + return $shares; + } + + /** + * Get a share by token + * + * @param string $token + * @return IShare + * @throws ShareNotFound + */ + public function getShareByToken($token) { + $qb = $this->dbConnection->getQueryBuilder(); + + $cursor = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) + ->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token))) + ->execute(); + + $data = $cursor->fetch(); + + if ($data === false) { + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); + } + + try { + $share = $this->createShareObject($data); + } catch (InvalidShare $e) { + throw new ShareNotFound('Share not found', $this->l->t('Could not find share')); + } + + return $share; + } + + /** + * remove share from table + * + * @param string $shareId + */ + protected function removeShareFromTable($shareId) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($shareId))); + $qb->execute(); + } + + /** + * Create a share object from an database row + * + * @param array $data + * @return IShare + * @throws InvalidShare + * @throws ShareNotFound + */ + protected function createShareObject($data) { + + $share = new Share($this->rootFolder, $this->userManager); + $share->setId((int)$data['id']) + ->setShareType((int)$data['share_type']) + ->setPermissions((int)$data['permissions']) + ->setTarget($data['file_target']) + ->setMailSend((bool)$data['mail_send']) + ->setToken($data['token']); + + $shareTime = new \DateTime(); + $shareTime->setTimestamp((int)$data['stime']); + $share->setShareTime($shareTime); + $share->setSharedWith($data['share_with']); + + if ($data['uid_initiator'] !== null) { + $share->setShareOwner($data['uid_owner']); + $share->setSharedBy($data['uid_initiator']); + } else { + //OLD SHARE + $share->setSharedBy($data['uid_owner']); + $path = $this->getNode($share->getSharedBy(), (int)$data['file_source']); + + $owner = $path->getOwner(); + $share->setShareOwner($owner->getUID()); + } + + $share->setNodeId((int)$data['file_source']); + $share->setNodeType($data['item_type']); + + $share->setProviderId($this->identifier()); + + return $share; + } + + /** + * Get the node with file $id for $user + * + * @param string $userId + * @param int $id + * @return \OCP\Files\File|\OCP\Files\Folder + * @throws InvalidShare + */ + private function getNode($userId, $id) { + try { + $userFolder = $this->rootFolder->getUserFolder($userId); + } catch (NotFoundException $e) { + throw new InvalidShare(); + } + + $nodes = $userFolder->getById($id); + + if (empty($nodes)) { + throw new InvalidShare(); + } + + return $nodes[0]; + } + + /** + * A user is deleted from the system + * So clean up the relevant shares. + * + * @param string $uid + * @param int $shareType + */ + public function userDeleted($uid, $shareType) { + $qb = $this->dbConnection->getQueryBuilder(); + + $qb->delete('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL))) + ->andWhere($qb->expr()->eq('uid_owner', $qb->createNamedParameter($uid))) + ->execute(); + } + + /** + * This provider does not support group shares + * + * @param string $gid + */ + public function groupDeleted($gid) { + return; + } + + /** + * This provider does not support group shares + * + * @param string $uid + * @param string $gid + */ + public function userDeletedFromGroup($uid, $gid) { + return; + } + + /** + * get database row of a give share + * + * @param $id + * @return array + * @throws ShareNotFound + */ + protected function getRawShare($id) { + + // Now fetch the inserted share and create a complete share object + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new ShareNotFound; + } + + return $data; + } + + public function getSharesInFolder($userId, Folder $node, $reshares) { + $qb = $this->dbConnection->getQueryBuilder(); + $qb->select('*') + ->from('share', 's') + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->andWhere( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_EMAIL)) + ); + + /** + * Reshares for this user are shares where they are the owner. + */ + if ($reshares === false) { + $qb->andWhere($qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId))); + } else { + $qb->andWhere( + $qb->expr()->orX( + $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId)), + $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)) + ) + ); + } + + $qb->innerJoin('s', 'filecache' ,'f', 's.file_source = f.fileid'); + $qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId()))); + + $qb->orderBy('id'); + + $cursor = $qb->execute(); + $shares = []; + while ($data = $cursor->fetch()) { + $shares[$data['fileid']][] = $this->createShareObject($data); + } + $cursor->closeCursor(); + + return $shares; + } + +} diff --git a/apps/sharebymail/templates/altmail.php b/apps/sharebymail/templates/altmail.php new file mode 100644 index 0000000000..7b9de6295f --- /dev/null +++ b/apps/sharebymail/templates/altmail.php @@ -0,0 +1,36 @@ + + * + * @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 . + * + */ + +/** @var OC_Theme $theme */ +/** @var array $_ */ +if ($_['onBehalfOf']) { + print_unescaped($l->t("Hey there,\n\n%s shared »%s« with you on behalf of %s.\n\n%s\n\n", [$_['owner'], $_['filename'], $_['initiator'], $_['link']])); +} else { + print_unescaped($l->t("Hey there,\n\n%s shared »%s« with you.\n\n%s\n\n", [$_['owner'], $_['filename'], $_['link']])); +} +// TRANSLATORS term at the end of a mail +p($l->t("Cheers!")); +print_unescaped("\n"); +?> + +-- +getName() . ' - ' . $theme->getSlogan()); ?> +getBaseUrl()); diff --git a/apps/sharebymail/templates/mail.php b/apps/sharebymail/templates/mail.php new file mode 100644 index 0000000000..48f7dcb063 --- /dev/null +++ b/apps/sharebymail/templates/mail.php @@ -0,0 +1,64 @@ + + * + * @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 . + * + */ + +/** @var OC_Theme $theme */ +/** @var array $_ */ +?> + + + +
+ + + + + + + + + + + + + + + + + + +
  + <?php p($theme->getName()); ?> +
 
  + t('Hey there,

%s shared %s with you on behalf of %s.

', [$_['owner'], $_['link'], $_['filename'], $_['initiator']])); + } else { + print_unescaped($l->t('Hey there,

%s shared %s with you.

', [$_['owner'], $_['link'], $_['filename']])); + } + // TRANSLATORS term at the end of a mail + p($l->t('Cheers!')); + ?> +
 
 --
+ getName()); ?> - + getSlogan()); ?> +
getBaseUrl());?> +
 
+
diff --git a/apps/sharebymail/tests/SettingsTest.php b/apps/sharebymail/tests/SettingsTest.php new file mode 100644 index 0000000000..f415421b0c --- /dev/null +++ b/apps/sharebymail/tests/SettingsTest.php @@ -0,0 +1,64 @@ + + * + * @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 . + * + */ + + +namespace OCA\ShareByMail\Tests; + + +use OCA\ShareByMail\Settings; +use Test\TestCase; + +class SettingsTest extends TestCase { + + /** @var Settings */ + private $instance; + + public function setUp() { + parent::setUp(); + + $this->instance = new Settings(); + } + + public function testAnnounceShareProvider() { + $before = [ + 'oc_appconfig' => + json_encode([ + 'key1' => 'value1', + 'key2' => 'value2' + ]), + 'oc_foo' => 'oc_bar' + ]; + + $after = [ + 'oc_appconfig' => + json_encode([ + 'key1' => 'value1', + 'key2' => 'value2', + 'shareByMailEnabled' => true + ]), + 'oc_foo' => 'oc_bar' + ]; + + $this->instance->announceShareProvider(['array' => &$before]); + $this->assertSame($after, $before); + } + +} diff --git a/apps/sharebymail/tests/ShareByMailProviderTest.php b/apps/sharebymail/tests/ShareByMailProviderTest.php new file mode 100644 index 0000000000..eedce286a0 --- /dev/null +++ b/apps/sharebymail/tests/ShareByMailProviderTest.php @@ -0,0 +1,655 @@ + + * + * @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 . + * + */ + + +namespace OCA\ShareByMail\Tests; + + +use OC\HintException; +use OCA\ShareByMail\ShareByMailProvider; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IL10N; +use OCP\ILogger; +use OCP\IURLGenerator; +use OCP\IUserManager; +use OCP\Mail\IMailer; +use OCP\Security\ISecureRandom; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IManager; +use OCP\Share\IShare; +use Test\TestCase; + +/** + * Class ShareByMailProviderTest + * + * @package OCA\ShareByMail\Tests + * @group DB + */ +class ShareByMailProviderTest extends TestCase { + + /** @var IDBConnection */ + private $connection; + + /** @var IManager */ + private $shareManager; + + /** @var IL10N | \PHPUnit_Framework_MockObject_MockObject */ + private $l; + + /** @var ILogger | \PHPUnit_Framework_MockObject_MockObject */ + private $logger; + + /** @var IRootFolder | \PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var IUserManager | \PHPUnit_Framework_MockObject_MockObject */ + private $userManager; + + /** @var ISecureRandom | \PHPUnit_Framework_MockObject_MockObject */ + private $secureRandom; + + /** @var IMailer | \PHPUnit_Framework_MockObject_MockObject */ + private $mailer; + + /** @var IURLGenerator | \PHPUnit_Framework_MockObject_MockObject */ + private $urlGenerator; + + /** @var IShare | \PHPUnit_Framework_MockObject_MockObject */ + private $share; + + public function setUp() { + parent::setUp(); + + $this->shareManager = \OC::$server->getShareManager(); + $this->connection = \OC::$server->getDatabaseConnection(); + + $this->l = $this->getMockBuilder('OCP\IL10N')->getMock(); + $this->l->method('t') + ->will($this->returnCallback(function($text, $parameters = []) { + return vsprintf($text, $parameters); + })); + $this->logger = $this->getMockBuilder('OCP\ILogger')->getMock(); + $this->rootFolder = $this->getMockBuilder('OCP\Files\IRootFolder')->getMock(); + $this->userManager = $this->getMockBuilder('OCP\IUserManager')->getMock(); + $this->secureRandom = $this->getMockBuilder('\OCP\Security\ISecureRandom')->getMock(); + $this->mailer = $this->getMockBuilder('\OCP\Mail\IMailer')->getMock(); + $this->urlGenerator = $this->getMockBuilder('\OCP\IUrlGenerator')->getMock(); + $this->share = $this->getMockBuilder('\OCP\Share\IShare')->getMock(); + + $this->userManager->expects($this->any())->method('userExists')->willReturn(true); + } + + /** + * get instance of Mocked ShareByMailProvider + * + * @param array $mockedMethods internal methods which should be mocked + * @return \PHPUnit_Framework_MockObject_MockObject | ShareByMailProvider + */ + private function getInstance(array $mockedMethods = []) { + + $instance = $this->getMockBuilder('OCA\ShareByMail\ShareByMailProvider') + ->setConstructorArgs( + [ + $this->connection, + $this->secureRandom, + $this->userManager, + $this->rootFolder, + $this->l, + $this->logger, + $this->mailer, + $this->urlGenerator + ] + ); + + if (!empty($mockedMethods)) { + $instance->setMethods($mockedMethods); + return $instance->getMock(); + } + + return new ShareByMailProvider( + $this->connection, + $this->secureRandom, + $this->userManager, + $this->rootFolder, + $this->l, + $this->logger, + $this->mailer, + $this->urlGenerator + ); + + } + + public function tearDown() { + $this->connection->getQueryBuilder()->delete('share')->execute(); + + return parent::tearDown(); + } + + public function testCreate() { + $share = $this->getMockBuilder('\OCP\Share\IShare')->getMock(); + $share->expects($this->once())->method('getSharedWith')->willReturn('user1'); + + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']); + + $instance->expects($this->once())->method('getSharedWith')->willReturn([]); + $instance->expects($this->once())->method('createMailShare')->with($share)->willReturn(42); + $instance->expects($this->once())->method('getRawShare')->with(42)->willReturn('rawShare'); + $instance->expects($this->once())->method('createShareObject')->with('rawShare')->willReturn('shareObject'); + + $this->assertSame('shareObject', + $instance->create($share) + ); + } + + /** + * @expectedException \Exception + */ + public function testCreateFailed() { + $this->share->expects($this->once())->method('getSharedWith')->willReturn('user1'); + $node = $this->getMockBuilder('OCP\Files\Node')->getMock(); + $node->expects($this->any())->method('getName')->willReturn('fileName'); + $this->share->expects($this->any())->method('getNode')->willReturn($node); + + $instance = $this->getInstance(['getSharedWith', 'createMailShare', 'getRawShare', 'createShareObject']); + + $instance->expects($this->once())->method('getSharedWith')->willReturn(['found']); + $instance->expects($this->never())->method('createMailShare'); + $instance->expects($this->never())->method('getRawShare'); + $instance->expects($this->never())->method('createShareObject'); + + $this->assertSame('shareObject', + $instance->create($this->share) + ); + } + + public function testCreateMailShare() { + $this->share->expects($this->any())->method('getToken')->willReturn('token'); + $this->share->expects($this->once())->method('setToken')->with('token'); + $node = $this->getMockBuilder('OCP\Files\Node')->getMock(); + $node->expects($this->any())->method('getName')->willReturn('fileName'); + $this->share->expects($this->any())->method('getNode')->willReturn($node); + + $instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']); + + $instance->expects($this->once())->method('generateToken')->willReturn('token'); + $instance->expects($this->once())->method('addShareToDB')->willReturn(42); + $instance->expects($this->once())->method('sendMailNotification'); + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']); + $instance->expects($this->once())->method('sendMailNotification'); + + $this->assertSame(42, + $this->invokePrivate($instance, 'createMailShare', [$this->share]) + ); + + } + + /** + * @expectedException \OC\HintException + */ + public function testCreateMailShareFailed() { + $this->share->expects($this->any())->method('getToken')->willReturn('token'); + $this->share->expects($this->once())->method('setToken')->with('token'); + $node = $this->getMockBuilder('OCP\Files\Node')->getMock(); + $node->expects($this->any())->method('getName')->willReturn('fileName'); + $this->share->expects($this->any())->method('getNode')->willReturn($node); + + $instance = $this->getInstance(['generateToken', 'addShareToDB', 'sendMailNotification']); + + $instance->expects($this->once())->method('generateToken')->willReturn('token'); + $instance->expects($this->once())->method('addShareToDB')->willReturn(42); + $instance->expects($this->once())->method('sendMailNotification'); + $this->urlGenerator->expects($this->once())->method('linkToRouteAbsolute') + ->with('files_sharing.sharecontroller.showShare', ['token' => 'token']); + $instance->expects($this->once())->method('sendMailNotification') + ->willReturnCallback( + function() { + throw new \Exception('should be converted to a hint exception'); + } + ); + + $this->assertSame(42, + $this->invokePrivate($instance, 'createMailShare', [$this->share]) + ); + + } + + public function testGenerateToken() { + $instance = $this->getInstance(); + + $this->secureRandom->expects($this->once())->method('generate')->willReturn('token'); + + $this->assertSame('token', + $this->invokePrivate($instance, 'generateToken') + ); + } + + public function testAddShareToDB() { + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + + $instance = $this->getInstance(); + $id = $this->invokePrivate( + $instance, + 'addShareToDB', + [ + $itemSource, + $itemType, + $shareWith, + $sharedBy, + $uidOwner, + $permissions, + $token + ] + ); + + $qb = $this->connection->getQueryBuilder(); + $result = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->execute()->fetchAll(); + + $this->assertSame(1, count($result)); + + $this->assertSame($itemSource, (int)$result[0]['item_source']); + $this->assertSame($itemType, $result[0]['item_type']); + $this->assertSame($shareWith, $result[0]['share_with']); + $this->assertSame($sharedBy, $result[0]['uid_initiator']); + $this->assertSame($uidOwner, $result[0]['uid_owner']); + $this->assertSame($permissions, (int)$result[0]['permissions']); + $this->assertSame($token, $result[0]['token']); + + } + + public function testUpdate() { + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + + $instance = $this->getInstance(); + + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $this->share->expects($this->once())->method('getPermissions')->willReturn($permissions + 1); + $this->share->expects($this->once())->method('getShareOwner')->willReturn($uidOwner); + $this->share->expects($this->once())->method('getSharedBy')->willReturn($sharedBy); + $this->share->expects($this->once())->method('getId')->willReturn($id); + + $this->assertSame($this->share, + $instance->update($this->share) + ); + + $qb = $this->connection->getQueryBuilder(); + $result = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->execute()->fetchAll(); + + $this->assertSame(1, count($result)); + + $this->assertSame($itemSource, (int)$result[0]['item_source']); + $this->assertSame($itemType, $result[0]['item_type']); + $this->assertSame($shareWith, $result[0]['share_with']); + $this->assertSame($sharedBy, $result[0]['uid_initiator']); + $this->assertSame($uidOwner, $result[0]['uid_owner']); + $this->assertSame($permissions + 1, (int)$result[0]['permissions']); + $this->assertSame($token, $result[0]['token']); + } + + public function testDelete() { + $instance = $this->getInstance(['removeShareFromTable']); + $this->share->expects($this->once())->method('getId')->willReturn(42); + $instance->expects($this->once())->method('removeShareFromTable')->with(42); + $instance->delete($this->share); + } + + public function testGetShareById() { + $instance = $this->getInstance(['createShareObject']); + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $this->createDummyShare($itemType, $itemSource, $shareWith, "user1wrong", "user2wrong", $permissions, $token); + $id2 = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $instance->expects($this->once())->method('createShareObject') + ->willReturnCallback( + function ($data) use ($uidOwner, $sharedBy, $id2) { + $this->assertSame($uidOwner, $data['uid_owner']); + $this->assertSame($sharedBy, $data['uid_initiator']); + $this->assertSame($id2, (int)$data['id']); + return $this->share; + } + ); + + $result = $instance->getShareById($id2); + + $this->assertInstanceOf('OCP\Share\IShare', $result); + } + + /** + * @expectedException \OCP\Share\Exceptions\ShareNotFound + */ + public function testGetShareByIdFailed() { + $instance = $this->getInstance(['createShareObject']); + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $instance->getShareById($id+1); + } + + public function testGetShareByPath() { + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $node = $this->getMockBuilder('OCP\Files\Node')->getMock(); + $node->expects($this->once())->method('getId')->willReturn($itemSource); + + + $instance = $this->getInstance(['createShareObject']); + + $this->createDummyShare($itemType, 111, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $instance->expects($this->once())->method('createShareObject') + ->willReturnCallback( + function ($data) use ($uidOwner, $sharedBy, $id) { + $this->assertSame($uidOwner, $data['uid_owner']); + $this->assertSame($sharedBy, $data['uid_initiator']); + $this->assertSame($id, (int)$data['id']); + return $this->share; + } + ); + + $result = $instance->getSharesByPath($node); + + $this->assertTrue(is_array($result)); + $this->assertSame(1, count($result)); + $this->assertInstanceOf('OCP\Share\IShare', $result[0]); + } + + public function testGetShareByToken() { + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $instance = $this->getInstance(['createShareObject']); + + $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, \OCP\Share::SHARE_TYPE_LINK); + + $this->assertTrue($idMail !== $idPublic); + + $instance->expects($this->once())->method('createShareObject') + ->willReturnCallback( + function ($data) use ($idMail) { + $this->assertSame($idMail, (int)$data['id']); + return $this->share; + } + ); + + $this->assertInstanceOf('OCP\Share\IShare', + $instance->getShareByToken('token') + ); + } + + /** + * @expectedException \OCP\Share\Exceptions\ShareNotFound + */ + public function testGetShareByTokenFailed() { + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $instance = $this->getInstance(['createShareObject']); + + $idMail = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + $idPublic = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, "token2", \OCP\Share::SHARE_TYPE_LINK); + + $this->assertTrue($idMail !== $idPublic); + + $this->assertInstanceOf('OCP\Share\IShare', + $instance->getShareByToken('token2') + ); + } + + public function testRemoveShareFromTable() { + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $instance = $this->getInstance(); + + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from('share') + ->where($query->expr()->eq('id', $query->createNamedParameter($id))); + $before = $query->execute()->fetchAll(); + + $this->assertTrue(is_array($before)); + $this->assertSame(1, count($before)); + + $this->invokePrivate($instance, 'removeShareFromTable', [$id]); + + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from('share') + ->where($query->expr()->eq('id', $query->createNamedParameter($id))); + $after = $query->execute()->fetchAll(); + + $this->assertTrue(is_array($after)); + $this->assertEmpty($after); + } + + public function testUserDeleted() { + + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, 'user2Wrong', $permissions, $token); + + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from('share'); + $before = $query->execute()->fetchAll(); + + $this->assertTrue(is_array($before)); + $this->assertSame(2, count($before)); + + + $instance = $this->getInstance(); + + $instance->userDeleted($uidOwner, \OCP\Share::SHARE_TYPE_EMAIL); + + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from('share'); + $after = $query->execute()->fetchAll(); + + $this->assertTrue(is_array($after)); + $this->assertSame(1, count($after)); + $this->assertSame($id, (int)$after[0]['id']); + + } + + public function testGetRawShare() { + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $instance = $this->getInstance(); + + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $result = $this->invokePrivate($instance, 'getRawShare', [$id]); + + $this->assertTrue(is_array($result)); + $this->assertSame($itemSource, (int)$result['item_source']); + $this->assertSame($itemType, $result['item_type']); + $this->assertSame($shareWith, $result['share_with']); + $this->assertSame($sharedBy, $result['uid_initiator']); + $this->assertSame($uidOwner, $result['uid_owner']); + $this->assertSame($permissions, (int)$result['permissions']); + $this->assertSame($token, $result['token']); + } + + /** + * @expectedException \OCP\Share\Exceptions\ShareNotFound + */ + public function testGetRawShareFailed() { + $itemSource = 11; + $itemType = 'file'; + $shareWith = 'user@server.com'; + $sharedBy = 'user1'; + $uidOwner = 'user2'; + $permissions = 1; + $token = 'token'; + + $instance = $this->getInstance(); + + $id = $this->createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token); + + $this->invokePrivate($instance, 'getRawShare', [$id+1]); + } + + private function createDummyShare($itemType, $itemSource, $shareWith, $sharedBy, $uidOwner, $permissions, $token, $shareType = \OCP\Share::SHARE_TYPE_EMAIL) { + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->setValue('share_type', $qb->createNamedParameter($shareType)) + ->setValue('item_type', $qb->createNamedParameter($itemType)) + ->setValue('item_source', $qb->createNamedParameter($itemSource)) + ->setValue('file_source', $qb->createNamedParameter($itemSource)) + ->setValue('share_with', $qb->createNamedParameter($shareWith)) + ->setValue('uid_owner', $qb->createNamedParameter($uidOwner)) + ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy)) + ->setValue('permissions', $qb->createNamedParameter($permissions)) + ->setValue('token', $qb->createNamedParameter($token)) + ->setValue('stime', $qb->createNamedParameter(time())); + + /* + * Added to fix https://github.com/owncloud/core/issues/22215 + * Can be removed once we get rid of ajax/share.php + */ + $qb->setValue('file_target', $qb->createNamedParameter('')); + + $qb->execute(); + $id = $qb->getLastInsertId(); + + return (int)$id; + } + + public function testGetSharesInFolder() { + $userManager = \OC::$server->getUserManager(); + $rootFolder = \OC::$server->getRootFolder(); + + $provider = $this->getInstance(['sendMailNotification']); + + $u1 = $userManager->createUser('testFed', md5(time())); + $u2 = $userManager->createUser('testFed2', md5(time())); + + $folder1 = $rootFolder->getUserFolder($u1->getUID())->newFolder('foo'); + $file1 = $folder1->newFile('bar1'); + $file2 = $folder1->newFile('bar2'); + + $share1 = $this->shareManager->newShare(); + $share1->setSharedWith('user@server.com') + ->setSharedBy($u1->getUID()) + ->setShareOwner($u1->getUID()) + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file1); + $provider->create($share1); + + $share2 = $this->shareManager->newShare(); + $share2->setSharedWith('user@server.com') + ->setSharedBy($u2->getUID()) + ->setShareOwner($u1->getUID()) + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file2); + $provider->create($share2); + + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, false); + $this->assertCount(1, $result); + $this->assertCount(1, $result[$file1->getId()]); + + $result = $provider->getSharesInFolder($u1->getUID(), $folder1, true); + $this->assertCount(2, $result); + $this->assertCount(1, $result[$file1->getId()]); + $this->assertCount(1, $result[$file2->getId()]); + + $u1->delete(); + $u2->delete(); + } + +} diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature index 98bf321dc6..9103a71ab9 100644 --- a/build/integration/features/provisioning-v1.feature +++ b/build/integration/features/provisioning-v1.feature @@ -255,7 +255,7 @@ Feature: provisioning Scenario: Delete a user Given As an "admin" And user "brand-new-user" exists - When sending "DELETE" to "/cloud/users/brand-new-user" + When sending "DELETE" to "/cloud/users/brand-new-user" Then the OCS status code should be "100" And the HTTP status code should be "200" And user "brand-new-user" does not exist @@ -291,6 +291,7 @@ Feature: provisioning | files_trashbin | | files_versions | | provisioning_api | + | sharebymail | | systemtags | | theming | | twofactor_backupcodes | diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index b04c2acae1..24922d6263 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -25,6 +25,7 @@ isDefaultExpireDateEnforced: oc_appconfig.core.defaultExpireDateEnforced === true, isDefaultExpireDateEnabled: oc_appconfig.core.defaultExpireDateEnabled === true, isRemoteShareAllowed: oc_appconfig.core.remoteShareAllowed, + isMailShareAllowed: oc_appconfig.shareByMailEnabled !== undefined, defaultExpireDate: oc_appconfig.core.defaultExpireDate, isResharingAllowed: oc_appconfig.core.resharingAllowed, allowGroupSharing: oc_appconfig.core.allowGroupSharing diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 66ed85eda3..775eaa554b 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -24,25 +24,28 @@ '{{#if avatarEnabled}}' + '
' + '{{/if}}' + - '{{shareWithDisplayName}}' + + '{{shareWithDisplayName}}' + '' + '{{#if editPermissionPossible}}' + + '{{#unless isFileSharedByMail}}' + '' + '' + '' + '' + + '{{/unless}}' + '{{/if}}' + + '{{#unless isMailShare}}' + '' + '' + - '' + + '{{/unless}}' + + '{{unshareLabel}}' + + '' + '' + '{{/each}}' + '{{#each linkReshares}}' + @@ -141,6 +146,7 @@ getShareeObject: function(shareIndex) { var shareWith = this.model.getShareWith(shareIndex); var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex); + var shareWithTitle = ''; var shareType = this.model.getShareType(shareIndex); var hasPermissionOverride = {}; @@ -148,6 +154,16 @@ shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')'; } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')'; + } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { + shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'email') + ')'; + } + + if (shareType === OC.Share.SHARE_TYPE_GROUP) { + shareWithTitle = shareWith + " (" + t('core', 'group') + ')'; + } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) { + shareWithTitle = shareWith + " (" + t('core', 'remote') + ')'; + } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) { + shareWithTitle = shareWith + " (" + t('core', 'email') + ')'; } return _.extend(hasPermissionOverride, { @@ -160,10 +176,13 @@ wasMailSent: this.model.notificationMailWasSent(shareIndex), shareWith: shareWith, shareWithDisplayName: shareWithDisplayName, + shareWithTitle: shareWithTitle, shareType: shareType, shareId: this.model.get('shares')[shareIndex].id, modSeed: shareType !== OC.Share.SHARE_TYPE_USER, - isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE + isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE, + isMailShare: shareType === OC.Share.SHARE_TYPE_EMAIL, + isFileSharedByMail: shareType === OC.Share.SHARE_TYPE_EMAIL && !this.model.isFolder() }); }, diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 7efb361f51..3b2a7480c4 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -154,10 +154,16 @@ var users = result.ocs.data.exact.users.concat(result.ocs.data.users); var groups = result.ocs.data.exact.groups.concat(result.ocs.data.groups); var remotes = result.ocs.data.exact.remotes.concat(result.ocs.data.remotes); + if (typeof(result.ocs.data.emails) !== 'undefined') { + var emails = result.ocs.data.exact.emails.concat(result.ocs.data.emails); + } else { + var emails = []; + } var usersLength; var groupsLength; var remotesLength; + var emailsLength; var i, j; @@ -212,10 +218,18 @@ break; } } + } else if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) { + emailsLength = emails.length; + for (j = 0; j < emailsLength; j++) { + if (emails[j].value.shareWith === share.share_with) { + emails.splice(j, 1); + break; + } + } } } - var suggestions = users.concat(groups).concat(remotes); + var suggestions = users.concat(groups).concat(remotes).concat(emails); if (suggestions.length > 0) { $('.shareWithField').removeClass('error') @@ -258,16 +272,13 @@ sharee: text }); } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) { - if (item.value.server) { - text = t('core', '{sharee} (at {server})', { - sharee: text, - server: item.value.server - }); - } else { text = t('core', '{sharee} (remote)', { sharee: text }); - } + } else if (item.value.shareType === OC.Share.SHARE_TYPE_EMAIL) { + text = t('core', '{sharee} (email)', { + sharee: text + }); } var insert = $("