diff options
author | Julius Härtl <jus@bitgrid.net> | 2022-10-26 22:51:26 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-26 22:51:26 +0300 |
commit | d03fa731c96c87708838c6b7318bc0ff560d3bba (patch) | |
tree | bd87038a8d554b5594c1fd0f5bf41f87358b5ad8 | |
parent | 45fcd99ae300ca198cad79cbac7fd3355cfb2802 (diff) | |
parent | fc9cbbd21d7d8680aa4830fb02c5529ceb8095f2 (diff) |
Merge pull request #2579 from nextcloud/bugfix/watermark-preview
-rw-r--r-- | css/admin.scss | 2 | ||||
-rw-r--r-- | css/templatePicker.scss | 2 | ||||
-rw-r--r-- | lib/AppInfo/Application.php | 3 | ||||
-rw-r--r-- | lib/Controller/SettingsController.php | 1 | ||||
-rw-r--r-- | lib/Controller/WopiController.php | 70 | ||||
-rw-r--r-- | lib/Listener/BeforeFetchPreviewListener.php | 82 | ||||
-rw-r--r-- | lib/PermissionManager.php | 98 | ||||
-rw-r--r-- | src/components/AdminSettings.vue | 30 | ||||
-rw-r--r-- | tests/lib/PermissionManagerTest.php | 19 | ||||
-rw-r--r-- | tests/psalm-baseline.xml | 27 | ||||
-rw-r--r-- | tests/stub.phpstub | 30 |
11 files changed, 259 insertions, 105 deletions
diff --git a/css/admin.scss b/css/admin.scss index a28b833a..d95e55e1 100644 --- a/css/admin.scss +++ b/css/admin.scss @@ -1,4 +1,4 @@ -@use "sass:math"; +@use 'sass:math'; .rd-settings-documentation { max-width: 50em; diff --git a/css/templatePicker.scss b/css/templatePicker.scss index ee071863..b432d9a8 100644 --- a/css/templatePicker.scss +++ b/css/templatePicker.scss @@ -20,7 +20,7 @@ * */ - @use "sass:math"; +@use 'sass:math'; #template-picker { .template-container:not(.hidden) { diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 19fd8e64..c61b92ad 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -27,6 +27,7 @@ namespace OCA\Richdocuments\AppInfo; use OCA\Files_Sharing\Event\ShareLinkAccessedEvent; use OCA\Richdocuments\AppConfig; use OCA\Richdocuments\Capabilities; +use OCA\Richdocuments\Listener\BeforeFetchPreviewListener; use OCA\Richdocuments\Listener\CSPListener; use OCA\Richdocuments\Listener\LoadViewerListener; use OCA\Richdocuments\Listener\ShareLinkListener; @@ -52,6 +53,7 @@ use OCP\Files\Template\TemplateFileCreator; use OCP\IConfig; use OCP\IL10N; use OCP\IPreview; +use OCP\Preview\BeforeFetchPreviewEvent; use OCP\Security\CSP\AddContentSecurityPolicyEvent; class Application extends App implements IBootstrap { @@ -70,6 +72,7 @@ class Application extends App implements IBootstrap { $context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class); $context->registerEventListener(LoadViewer::class, LoadViewerListener::class); $context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkListener::class); + $context->registerEventListener(BeforeFetchPreviewEvent::class, BeforeFetchPreviewListener::class); } public function boot(IBootContext $context): void { diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 51210044..f9d99a73 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -237,6 +237,7 @@ class SettingsController extends Controller { 'watermark_enabled', 'watermark_shareAll', 'watermark_shareRead', + 'watermark_shareDisabledDownload', 'watermark_linkSecure', 'watermark_linkRead', 'watermark_linkAll', diff --git a/lib/Controller/WopiController.php b/lib/Controller/WopiController.php index 478a784c..62bb5e02 100644 --- a/lib/Controller/WopiController.php +++ b/lib/Controller/WopiController.php @@ -67,6 +67,7 @@ use OCP\Lock\LockedException; use OCP\PreConditionNotMetException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager as IShareManager; +use OCP\Share\IShare; use Psr\Container\ContainerExceptionInterface; use Psr\Container\NotFoundExceptionInterface; @@ -234,7 +235,8 @@ class WopiController extends Controller { $response['TemplateSaveAs'] = $file->getName(); } - if ($this->shouldWatermark($isPublic, $wopi->getEditorUid(), $fileId, $wopi)) { + $share = $this->getShareForWopiToken($wopi); + if ($this->permissionManager->shouldWatermark($file, $wopi->getEditorUid(), $share)) { $email = $user !== null && !$isPublic ? $user->getEMailAddress() : ""; $replacements = [ 'userId' => $wopi->getEditorUid(), @@ -318,62 +320,6 @@ class WopiController extends Controller { return $response; } - private function shouldWatermark($isPublic, $userId, $fileId, Wopi $wopi) { - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_enabled', 'no') === 'no') { - return false; - } - - if ($isPublic) { - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkAll', 'no') === 'yes') { - return true; - } - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkRead', 'no') === 'yes' && !$wopi->getCanwrite()) { - return true; - } - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkSecure', 'no') === 'yes' && $wopi->getHideDownload()) { - return true; - } - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkTags', 'no') === 'yes') { - $tags = $this->appConfig->getAppValueArray('watermark_linkTagsList'); - $fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId]; - foreach ($fileTags as $tagId) { - if (in_array($tagId, $tags, true)) { - return true; - } - } - } - } else { - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareAll', 'no') === 'yes') { - $files = $this->rootFolder->getUserFolder($userId)->getById($fileId); - if (count($files) !== 0 && $files[0]->getOwner()->getUID() !== $userId) { - return true; - } - } - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareRead', 'no') === 'yes' && !$wopi->getCanwrite()) { - return true; - } - } - if ($userId !== null && $this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allGroups', 'no') === 'yes') { - $groups = $this->appConfig->getAppValueArray('watermark_allGroupsList'); - foreach ($groups as $group) { - if (\OC::$server->getGroupManager()->isInGroup($userId, $group)) { - return true; - } - } - } - if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allTags', 'no') === 'yes') { - $tags = $this->appConfig->getAppValueArray('watermark_allTagsList'); - $fileTags = \OC::$server->getSystemTagObjectMapper()->getTagIdsForObjects([$fileId], 'files')[$fileId]; - foreach ($fileTags as $tagId) { - if (in_array($tagId, $tags, true)) { - return true; - } - } - } - - return false; - } - /** * Given an access token and a fileId, returns the contents of the file. * Expects a valid token in access_token parameter. @@ -842,6 +788,7 @@ class WopiController extends Controller { * @throws ShareNotFound */ private function getFileForWopiToken(Wopi $wopi) { + $this->userScopeService->setUserScope($wopi->getEditorUid()); if (!empty($wopi->getShare())) { $share = $this->shareManager->getShareByToken($wopi->getShare()); $node = $share->getNode(); @@ -875,6 +822,15 @@ class WopiController extends Controller { return array_shift($files); } + private function getShareForWopiToken(Wopi $wopi): ?IShare { + try { + return $wopi->getShare() ? $this->shareManager->getShareByToken($wopi->getShare()) : null; + } catch (ShareNotFound $e) { + } + + return null; + } + /** * Endpoint to return the template file that is requested by collabora to create a new document * diff --git a/lib/Listener/BeforeFetchPreviewListener.php b/lib/Listener/BeforeFetchPreviewListener.php new file mode 100644 index 00000000..20163f15 --- /dev/null +++ b/lib/Listener/BeforeFetchPreviewListener.php @@ -0,0 +1,82 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + + +namespace OCA\Richdocuments\Listener; + +use OCA\Files_Sharing\SharedStorage; +use OCA\Richdocuments\PermissionManager; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\NotFoundException; +use OCP\IRequest; +use OCP\IUserSession; +use OCP\Preview\BeforeFetchPreviewEvent; +use OCP\Share\Exceptions\ShareNotFound; +use OCP\Share\IManager; +use OCP\Share\IShare; + +class BeforeFetchPreviewListener implements IEventListener { + private PermissionManager $permissionManager; + private IUserSession $userSession; + private IRequest $request; + private IManager $shareManager; + + public function __construct(PermissionManager $permissionManager, IUserSession $userSession, IRequest $request, IManager $shareManager) { + $this->permissionManager = $permissionManager; + $this->userSession = $userSession; + $this->request = $request; + $this->shareManager = $shareManager; + } + + public function handle(Event $event): void { + if (!$event instanceof BeforeFetchPreviewEvent) { + return; + } + $shareToken = $this->request->getParam('token'); + + $share = null; + + // Get share for internal shares + $storage = $event->getNode()->getStorage(); + if (!$shareToken && $storage->instanceOfStorage(SharedStorage::class)) { + if (method_exists(IShare::class, 'getAttributes')) { + /** @var SharedStorage $storage */ + $share = $storage->getShare(); + } + } + + // Get different share for public previews as the share from the node is only set for mounted shares + try { + $share = $shareToken ? $this->shareManager->getShareByToken($shareToken) : $share; + } catch (ShareNotFound $e) { + } + + $userId = $this->userSession->getUser() ? $this->userSession->getUser()->getUID() : null; + if ($this->permissionManager->shouldWatermark($event->getNode(), $userId, $share)) { + throw new NotFoundException(); + } + } +} diff --git a/lib/PermissionManager.php b/lib/PermissionManager.php index a509dc81..752a3618 100644 --- a/lib/PermissionManager.php +++ b/lib/PermissionManager.php @@ -23,26 +23,38 @@ declare(strict_types=1); namespace OCA\Richdocuments; +use OCP\Constants; +use OCP\Files\Node; +use OCP\IConfig; use OCP\IGroupManager; use OCP\IUserManager; use OCP\IUserSession; +use OCP\Share\IAttributes; +use OCP\Share\IShare; +use OCP\SystemTag\ISystemTagObjectMapper; class PermissionManager { - private AppConfig $config; + private AppConfig $appConfig; + private IConfig $config; private IGroupManager $groupManager; private IUserManager $userManager; private IUserSession $userSession; + private ISystemTagObjectMapper $systemTagObjectMapper; public function __construct( - AppConfig $config, - IGroupManager $groupManager, - IUserManager $userManager, - IUserSession $userSession + AppConfig $appConfig, + IConfig $config, + IGroupManager $groupManager, + IUserManager $userManager, + IUserSession $userSession, + ISystemTagObjectMapper $systemTagObjectMapper ) { + $this->appConfig = $appConfig; $this->config = $config; $this->groupManager = $groupManager; $this->userManager = $userManager; $this->userSession = $userSession; + $this->systemTagObjectMapper = $systemTagObjectMapper; } private function userMatchesGroupList(?string $userId = null, ?array $groupList = []): bool { @@ -75,7 +87,7 @@ class PermissionManager { } public function isEnabledForUser(string $userId = null): bool { - if ($this->userMatchesGroupList($userId, $this->config->getUseGroups())) { + if ($this->userMatchesGroupList($userId, $this->appConfig->getUseGroups())) { return true; } @@ -83,7 +95,7 @@ class PermissionManager { } public function userCanEdit(string $userId = null): bool { - if ($this->userMatchesGroupList($userId, $this->config->getEditGroups())) { + if ($this->userMatchesGroupList($userId, $this->appConfig->getEditGroups())) { return true; } @@ -91,10 +103,80 @@ class PermissionManager { } public function userIsFeatureLocked(string $userId = null): bool { - if ($this->config->isReadOnlyFeatureLocked() && !$this->userCanEdit($userId)) { + if ($this->appConfig->isReadOnlyFeatureLocked() && !$this->userCanEdit($userId)) { return true; } return false; } + + public function shouldWatermark(Node $node, ?string $userId = null, ?IShare $share = null): bool { + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_enabled', 'no') === 'no') { + return false; + } + + $fileId = $node->getId(); + + $isUpdatable = $node->isUpdateable() && (!$share || $share->getPermissions() & Constants::PERMISSION_UPDATE); + + $hasShareAttributes = $share && method_exists($share, 'getAttributes') && $share->getAttributes() instanceof IAttributes; + $isDisabledDownload = $hasShareAttributes && $share->getAttributes()->getAttribute('permissions', 'download') === false; + $isHideDownload = $share && $share->getHideDownload(); + $isSecureView = $isDisabledDownload || $isHideDownload; + + if ($share && $share->getShareType() === IShare::TYPE_LINK) { + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkAll', 'no') === 'yes') { + return true; + } + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkRead', 'no') === 'yes' && !$isUpdatable) { + return true; + } + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkSecure', 'no') === 'yes' && $isSecureView) { + return true; + } + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_linkTags', 'no') === 'yes') { + $tags = $this->appConfig->getAppValueArray('watermark_linkTagsList'); + $fileTags = $this->systemTagObjectMapper->getTagIdsForObjects([$fileId], 'files')[$fileId]; + foreach ($fileTags as $tagId) { + if (in_array($tagId, $tags, true)) { + return true; + } + } + } + } + + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareAll', 'no') === 'yes') { + if ($node->getOwner()->getUID() !== $userId) { + return true; + } + } + + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareRead', 'no') === 'yes' && !$isUpdatable) { + return true; + } + + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_shareDisabledDownload', 'no') === 'yes' && $isDisabledDownload) { + return true; + } + + if ($userId !== null && $this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allGroups', 'no') === 'yes') { + $groups = $this->appConfig->getAppValueArray('watermark_allGroupsList'); + foreach ($groups as $group) { + if ($this->groupManager->isInGroup($userId, $group)) { + return true; + } + } + } + if ($this->config->getAppValue(AppConfig::WATERMARK_APP_NAMESPACE, 'watermark_allTags', 'no') === 'yes') { + $tags = $this->appConfig->getAppValueArray('watermark_allTagsList'); + $fileTags = $this->systemTagObjectMapper->getTagIdsForObjects([$fileId], 'files')[$fileId]; + foreach ($fileTags as $tagId) { + if (in_array($tagId, $tags, true)) { + return true; + } + } + } + + return false; + } } diff --git a/src/components/AdminSettings.vue b/src/components/AdminSettings.vue index 58bb8742..d7ecb3c4 100644 --- a/src/components/AdminSettings.vue +++ b/src/components/AdminSettings.vue @@ -298,6 +298,12 @@ <div v-if="isSetup" id="secure-view-settings" class="section"> <h2>{{ t('richdocuments', 'Secure view settings') }}</h2> <p>{{ t('richdocuments', 'Secure view enables you to secure documents by embedding a watermark') }}</p> + <ul> + <li>{{ t('richdocuments', 'The settings only apply to compatible office files that are opened in Nextcloud Office') }}</li> + <li>{{ t('richdocuments', 'The following options within Nextcloud Office will be disabled: Copy, Download, Export, Print') }}</li> + <li>{{ t('richdocuments', 'Files may still be downloadable through Nextcloud unless restricted otherwise through sharing or access control settings') }}</li> + <li>{{ t('richdocuments', 'Previews will be blocked for watermarked files to not leak the first page of documents') }}</li> + </ul> <SettingsCheckbox v-model="settings.watermark.enabled" :label="t('richdocuments', 'Enable watermarking')" hint="" @@ -335,6 +341,12 @@ hint="" :disabled="updating" @input="update" /> + <SettingsCheckbox v-if="!settings.watermark.shareAll" + v-model="settings.watermark.shareDisabledDownload" + :label="t('richdocuments', 'Show watermark for shares without download permission')" + hint="" + :disabled="updating" + @input="update" /> <h3>Link shares</h3> <SettingsCheckbox v-model="settings.watermark.linkAll" @@ -772,18 +784,18 @@ export default { margin-left: 25px; &:not(.multiselect) { margin-top: 10px; - font-style: italic; } - ul { - margin-bottom: 15px; - } + } - li { - list-style: disc; - padding: 3px; - margin-left: 20px; - } + ul { + margin-bottom: 15px; + } + + li { + list-style: disc; + padding: 3px; + margin-left: 20px; } .modal__content { diff --git a/tests/lib/PermissionManagerTest.php b/tests/lib/PermissionManagerTest.php index a48d7478..de830b93 100644 --- a/tests/lib/PermissionManagerTest.php +++ b/tests/lib/PermissionManagerTest.php @@ -28,10 +28,13 @@ use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; use OCP\IUserSession; +use OCP\SystemTag\ISystemTagObjectMapper; use PHPUnit\Framework\MockObject\MockObject; use Test\TestCase; class PermissionManagerTest extends TestCase { + /** @var AppConfig|MockObject */ + private $appConfig; /** @var IConfig|MockObject */ private $config; /** @var IGroupManager|MockObject */ @@ -45,15 +48,17 @@ class PermissionManagerTest extends TestCase { public function setUp(): void { parent::setUp(); - $this->config = $this->createMock(AppConfig::class); + $this->appConfig = $this->createMock(AppConfig::class); + $this->config = $this->createMock(IConfig::class); $this->groupManager = $this->createMock(IGroupManager::class); $this->userManager = $this->createMock(IUserManager::class); $this->userSession = $this->createMock(IUserSession::class); - $this->permissionManager = new PermissionManager($this->config, $this->groupManager, $this->userManager, $this->userSession); + $this->systemTagMapper = $this->createMock(ISystemTagObjectMapper::class); + $this->permissionManager = new PermissionManager($this->appConfig, $this->config, $this->groupManager, $this->userManager, $this->userSession, $this->systemTagMapper); } public function testIsEnabledForUserEnabledNoRestrictions(): void { - $this->config + $this->appConfig ->expects($this->once()) ->method('getUseGroups') ->willReturn(null); @@ -77,7 +82,7 @@ class PermissionManagerTest extends TestCase { /** @dataProvider dataGroupMatchGroups */ public function testEditGroups($editGroups, $userGroups, $result): void { $userMock = $this->createMock(IUser::class); - $this->config->expects($this->any()) + $this->appConfig->expects($this->any()) ->method('getEditGroups') ->willReturn($editGroups); $this->userManager->expects($this->any()) @@ -93,7 +98,7 @@ class PermissionManagerTest extends TestCase { /** @dataProvider dataGroupMatchGroups */ public function testUseGroups($editGroups, $userGroups, $result): void { $userMock = $this->createMock(IUser::class); - $this->config->expects($this->any()) + $this->appConfig->expects($this->any()) ->method('getUseGroups') ->willReturn($editGroups); $this->userManager->expects($this->any()) @@ -109,10 +114,10 @@ class PermissionManagerTest extends TestCase { /** @dataProvider dataGroupMatchGroups */ public function testFeatureLock($editGroups, $userGroups, $result): void { $userMock = $this->createMock(IUser::class); - $this->config->expects($this->any()) + $this->appConfig->expects($this->any()) ->method('getEditGroups') ->willReturn($editGroups); - $this->config->expects($this->any()) + $this->appConfig->expects($this->any()) ->method('isReadOnlyFeatureLocked') ->willReturn(true); $this->userManager->expects($this->any()) diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index 87c7e3dd..bf7f35c2 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -22,6 +22,9 @@ <code>OpenDocument</code> <code>Pdf</code> </MissingDependency> + <UndefinedClass occurrences="1"> + <code>BeforeFetchPreviewEvent</code> + </UndefinedClass> </file> <file src="lib/Command/ActivateConfig.php"> <RedundantCondition occurrences="1"> @@ -76,24 +79,16 @@ <code>$item->getId()</code> <code>$node->getId()</code> </InvalidScalarArgument> - <MissingDependency occurrences="7"> - <code>$this->rootFolder</code> + <MissingDependency occurrences="5"> <code>$this->rootFolder</code> <code>$this->rootFolder</code> <code>$this->rootFolder</code> - <code>BeforeFederationRedirectEvent</code> <code>IRootFolder</code> <code>IRootFolder</code> </MissingDependency> <RedundantCondition occurrences="1"> <code>$app !== ''</code> </RedundantCondition> - <UndefinedClass occurrences="1"> - <code>\OCA\Files_Sharing\External\Storage</code> - </UndefinedClass> - <UndefinedInterfaceMethod occurrences="1"> - <code>getRemote</code> - </UndefinedInterfaceMethod> </file> <file src="lib/Controller/OCSController.php"> <MissingDependency occurrences="4"> @@ -129,9 +124,7 @@ </UndefinedDocblockClass> </file> <file src="lib/Controller/WopiController.php"> - <MissingDependency occurrences="13"> - <code>$this->rootFolder</code> - <code>$this->rootFolder</code> + <MissingDependency occurrences="11"> <code>$this->rootFolder</code> <code>$this->rootFolder</code> <code>$this->rootFolder</code> @@ -159,6 +152,16 @@ <code>$time</code> </InvalidScalarArgument> </file> + <file src="lib/Listener/BeforeFetchPreviewListener.php"> + <UndefinedClass occurrences="1"> + <code>BeforeFetchPreviewEvent</code> + </UndefinedClass> + </file> + <file src="lib/PermissionManager.php"> + <RedundantCondition occurrences="1"> + <code>$share && method_exists($share, 'getAttributes')</code> + </RedundantCondition> + </file> <file src="lib/Preview/MSExcel.php"> <MissingDependency occurrences="1"> <code>Office</code> diff --git a/tests/stub.phpstub b/tests/stub.phpstub index 8b53b34d..e58a0f07 100644 --- a/tests/stub.phpstub +++ b/tests/stub.phpstub @@ -3,31 +3,40 @@ declare(strict_types=1); namespace OCA\Federation { - class TrustedServers { - public function getServers() {} - public function isTrustedServer($domainWithPort) {} - } + class TrustedServers { + public function getServers() { + } + public function isTrustedServer($domainWithPort) { + } + } } namespace OCA\Viewer\Event { - class LoadViewer extends \OCP\EventDispatcher\Event {} + class LoadViewer extends \OCP\EventDispatcher\Event { + } } namespace Doctrine\DBAL\Platforms { - class SqlitePlatform {} + class SqlitePlatform { + } } namespace OCA\Files_Sharing { + use OCP\Files\Storage\IStorage; use \OCP\Share\IShare; - class SharedStorage { - public function getShare(): IShare {} + + abstract class SharedStorage implements IStorage { + public function getShare(): IShare { + } } } namespace OCA\Files_Sharing\Event { use \OCP\Share\IShare; + class ShareLinkAccessedEvent extends \OCP\EventDispatcher\Event { - public function __construct(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = '') {} + public function __construct(IShare $share, string $step = '', int $errorCode = 200, string $errorMessage = '') { + } public function getShare(): IShare { } @@ -44,5 +53,6 @@ namespace OCA\Files_Sharing\Event { } class OC_Helper { - public static function getFileTemplateManager() {} + public static function getFileTemplateManager() { + } } |