From 18d3ec7cf8df288d6f4f92429206f461dae99d36 Mon Sep 17 00:00:00 2001 From: Elizabeth Danzberger Date: Thu, 18 Apr 2024 14:18:53 -0400 Subject: [PATCH] fix: php lint Signed-off-by: Elizabeth Danzberger --- lib/AppConfig.php | 61 +- lib/AppInfo/Application.php | 7 +- lib/Backgroundjobs/Cleanup.php | 2 +- lib/Capabilities.php | 10 - lib/Controller/AssetsController.php | 14 +- lib/Controller/DirectViewController.php | 4 +- lib/Controller/DocumentAPIController.php | 21 +- lib/Controller/DocumentController.php | 37 +- lib/Controller/DocumentTrait.php | 2 +- lib/Controller/FederationController.php | 18 +- lib/Controller/OCSController.php | 43 +- lib/Controller/SettingsController.php | 64 +- lib/Controller/TargetController.php | 9 +- lib/Controller/TemplatesController.php | 24 +- lib/Controller/WopiController.php | 699 +----------------- lib/Db/Asset.php | 10 - lib/Db/AssetMapper.php | 7 - lib/Db/Direct.php | 25 - lib/Db/DirectMapper.php | 9 - lib/Db/Wopi.php | 59 +- lib/Db/WopiMapper.php | 36 +- lib/Events/BeforeFederationRedirectEvent.php | 10 +- lib/Helper.php | 36 +- .../AddContentSecurityPolicyListener.php | 7 +- lib/Listener/AddFeaturePolicyListener.php | 6 +- lib/Listener/BeforeFetchPreviewListener.php | 8 - .../BeforeTemplateRenderedListener.php | 4 - .../FileCreatedFromTemplateListener.php | 6 - lib/Listener/LoadViewerListener.php | 6 - lib/Listener/ShareLinkListener.php | 6 - lib/Middleware/WOPIMiddleware.php | 13 +- lib/Migration/InstallDefaultFonts.php | 5 + .../Version2060Date20200302131958.php | 3 +- .../Version2060Date20200302132145.php | 3 +- .../Version30704Date20200626072306.php | 3 +- .../Version30709Date20201111104147.php | 1 - .../Version30717Date20210310164901.php | 4 +- .../Version50200Date20211220212457.php | 4 +- lib/PermissionManager.php | 24 - lib/Preview/Office.php | 6 +- .../OfficeTargetReferenceProvider.php | 22 +- lib/Service/CapabilitiesService.php | 13 +- lib/Service/ConnectivityService.php | 8 +- lib/Service/DemoService.php | 5 - lib/Service/DiscoveryService.php | 13 - lib/Service/FederationService.php | 24 +- lib/Service/FileTargetService.php | 18 +- lib/Service/FontService.php | 24 +- lib/Service/InitialStateService.php | 41 - lib/Service/RemoteService.php | 12 +- lib/Service/UserScopeService.php | 6 +- lib/Settings/Admin.php | 13 + lib/Settings/Personal.php | 16 +- lib/Settings/Section.php | 10 + lib/Template/CollaboraTemplateProvider.php | 16 +- lib/TemplateManager.php | 105 ++- lib/TokenManager.php | 42 +- lib/WOPI/Parser.php | 12 +- 58 files changed, 417 insertions(+), 1299 deletions(-) diff --git a/lib/AppConfig.php b/lib/AppConfig.php index d70e735a09..a833673b03 100644 --- a/lib/AppConfig.php +++ b/lib/AppConfig.php @@ -54,14 +54,7 @@ class AppConfig { 'watermark_linkTagsList' => 'array' ]; - public function __construct( - private IConfig $config, - private IAppManager $appManager, - private GlobalScaleConfig $globalScaleConfig, - ) { - } - - public function getAppNamespace(string $key) { + public function getAppNamespace(string $key): string { if (str_starts_with($key, 'watermark_')) { return self::WATERMARK_APP_NAMESPACE; } @@ -105,9 +98,12 @@ public function setAppValue($key, $value) { /** * Get all app settings - * @return array + * + * @return (bool|string|string[])[] + * + * @psalm-return array|string> */ - public function getAppSettings() { + public function getAppSettings(): array { $result = []; $keys = $this->config->getAppKeys(Application::APPNAME); foreach ($keys as $key) { @@ -157,7 +153,12 @@ public function getDisableCertificateValidation(): bool { return $this->config->getAppValue(Application::APPNAME, 'disable_certificate_verification', 'no') === 'yes'; } - public function getUseGroups(): ?array { + /** + * @return null|string[] + * + * @psalm-return non-empty-list|null + */ + public function getUseGroups(): array|null { $groups = $this->config->getAppValue(Application::APPNAME, 'use_groups', ''); if ($groups === '') { return null; @@ -166,7 +167,12 @@ public function getUseGroups(): ?array { return $this->splitGroups($groups); } - public function getEditGroups(): ?array { + /** + * @return null|string[] + * + * @psalm-return non-empty-list|null + */ + public function getEditGroups(): array|null { $groups = $this->config->getAppValue(Application::APPNAME, 'edit_groups', ''); if ($groups === '') { return null; @@ -175,30 +181,24 @@ public function getEditGroups(): ?array { return $this->splitGroups($groups); } - public function isReadOnlyFeatureLocked(): bool { - return $this->config->getAppValue(Application::APPNAME, self::READ_ONLY_FEATURE_LOCK, 'no') === 'yes'; - } - - private function splitGroups(string $groupString): array { - return explode('|', $groupString); - } - /** - * Allow to override values from the WOPI checkFileInfo response through app config + * @return string[] + * + * @psalm-return non-empty-list */ - public function getWopiOverride(): array { - $wopiOverride = $this->config->getAppValue(Application::APPNAME, 'wopi_override', ''); - if ($wopiOverride !== '') { - $wopiOverride = json_decode($wopiOverride, true); - return $wopiOverride ?: []; - } - return []; + private function splitGroups(string $groupString): array { + return explode('|', $groupString); } public function useSecureViewAdditionalMimes(): bool { return $this->config->getAppValue(Application::APPNAME, self::USE_SECURE_VIEW_ADDITIONAL_MIMES, 'no') === 'yes'; } + /** + * @return (false|string)[] + * + * @psalm-return array + */ public function getDomainList(): array { $urls = array_merge( [ $this->domainOnly($this->getCollaboraUrlPublic()) ], @@ -209,6 +209,11 @@ public function getDomainList(): array { return array_map(fn ($url) => idn_to_ascii($url), array_filter($urls)); } + /** + * @return string[] + * + * @psalm-return array + */ private function getFederationDomains(): array { if (!$this->appManager->isEnabledForUser('federation')) { return []; diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 59a4141b65..131536ae8b 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -70,10 +70,6 @@ class Application extends App implements IBootstrap { public const APPNAME = 'richdocuments'; - public function __construct(array $urlParams = []) { - parent::__construct(self::APPNAME, $urlParams); - } - public function register(IRegistrationContext $context): void { $context->registerTemplateProvider(CollaboraTemplateProvider::class); @@ -164,6 +160,9 @@ public function boot(IBootContext $context): void { $this->checkAndEnableCODEServer(); } + /** + * @return void + */ public function checkAndEnableCODEServer() { // Supported only on Linux OS, and x86_64 & ARM64 platforms $supportedArchs = ['x86_64', 'aarch64']; diff --git a/lib/Backgroundjobs/Cleanup.php b/lib/Backgroundjobs/Cleanup.php index 623dcf6656..e6dd20685c 100644 --- a/lib/Backgroundjobs/Cleanup.php +++ b/lib/Backgroundjobs/Cleanup.php @@ -54,7 +54,7 @@ protected function run($argument) { $this->cleanUpWopiTokens(); } - private function cleanUpWopiTokens() { + private function cleanUpWopiTokens(): void { $tokenIds = $this->wopiMapper->getExpiredTokenIds(1000); $query = $this->db->getQueryBuilder(); $query->delete('richdocuments_wopi') diff --git a/lib/Capabilities.php b/lib/Capabilities.php index a613edea41..7af18b9bcd 100644 --- a/lib/Capabilities.php +++ b/lib/Capabilities.php @@ -101,16 +101,6 @@ class Capabilities implements ICapability { private ?array $capabilities = null; - public function __construct( - private AppConfig $config, - private CapabilitiesService $capabilitiesService, - private PermissionManager $permissionManager, - private IAppManager $appManager, - private ?string $userId, - private IURLGenerator $urlGenerator - ) { - } - public function getCapabilities() { // Only expose capabilities for users with enabled office or guests (where it depends on the share owner if they have access) if (!$this->permissionManager->isEnabledForUser() && $this->userId !== null) { diff --git a/lib/Controller/AssetsController.php b/lib/Controller/AssetsController.php index 1d646772c7..9d226e06e5 100644 --- a/lib/Controller/AssetsController.php +++ b/lib/Controller/AssetsController.php @@ -63,12 +63,16 @@ public function __construct($appName, /** * @NoAdminRequired + * * @NoCSRFRequired * * @param string $path + * * @return JSONResponse + * + * @psalm-return JSONResponse<200|404, array{url?: string}, array> */ - public function create($path) { + public function create($path): JSONResponse { $userFolder = $this->rootFolder->getUserFolder($this->userId); try { @@ -88,13 +92,17 @@ public function create($path) { /** * @PublicPage + * * @NoCSRFRequired * * @param string $token - * @return Http\Response + * + * @return DataResponse|StreamResponse + * + * @psalm-return DataResponse<404, array, array>|StreamResponse<200, array> */ #[RestrictToWopiServer] - public function get($token) { + public function get($token): StreamResponse|DataResponse { try { $asset = $this->assetMapper->getAssetByToken($token); } catch (DoesNotExistException $e) { diff --git a/lib/Controller/DirectViewController.php b/lib/Controller/DirectViewController.php index f08ea2d0dc..a028d58bc4 100644 --- a/lib/Controller/DirectViewController.php +++ b/lib/Controller/DirectViewController.php @@ -191,8 +191,10 @@ public function showPublicShare(Direct $direct) { /** * @psalm-param 'Failed to open the requested file.' $message + * + * @psalm-return TemplateResponse<200, array> */ - private function renderErrorPage(string $message) { + private function renderErrorPage(string $message): TemplateResponse { $params = [ 'errors' => [['error' => $message]] ]; diff --git a/lib/Controller/DocumentAPIController.php b/lib/Controller/DocumentAPIController.php index 74f0c4446f..f875d602ee 100644 --- a/lib/Controller/DocumentAPIController.php +++ b/lib/Controller/DocumentAPIController.php @@ -46,12 +46,12 @@ use Throwable; class DocumentAPIController extends \OCP\AppFramework\OCSController { - private $rootFolder; - private $shareManager; - private $templateManager; - private $l10n; - private $logger; - private $lockManager; + private IRootFolder $rootFolder; + private IManager $shareManager; + private TemplateManager $templateManager; + private IL10N $l10n; + private LoggerInterface $logger; + private ILockManager $lockManager; private $userId; public function __construct(IRequest $request, IRootFolder $rootFolder, IManager $shareManager, TemplateManager $templateManager, IL10N $l10n, LoggerInterface $logger, ILockManager $lockManager, $userId) { @@ -73,12 +73,16 @@ public function __construct(IRequest $request, IRootFolder $rootFolder, IManager * actions in src/view/NewFileMenu.js * * @NoAdminRequired + * * @PublicPage + * + * @psalm-return JSONResponse<200|400, array{status: 'error'|'success', data?: mixed, message?: string}, array> */ public function create(string $mimeType, string $fileName, string $directoryPath = '/', ?string $shareToken = null, ?int $templateId = null): JSONResponse { try { + $share = null; if ($shareToken !== null) { - $share = $this->shareManager->getShareByToken($shareToken); + $this->shareManager->getShareByToken($shareToken); } $rootFolder = $shareToken !== null ? $share->getNode() : $this->rootFolder->getUserFolder($this->userId); @@ -156,6 +160,9 @@ public function create(string $mimeType, string $fileName, string $directoryPath ]); } + /** + * @psalm-return DataResponse<200|400|500, array, array> + */ #[Http\Attribute\NoAdminRequired] public function openLocal(int $fileId): DataResponse { try { diff --git a/lib/Controller/DocumentController.php b/lib/Controller/DocumentController.php index 387480ae94..6eb3749e61 100644 --- a/lib/Controller/DocumentController.php +++ b/lib/Controller/DocumentController.php @@ -74,7 +74,9 @@ public function __construct( * Requests is accepted only when a secret_token is provided set by admin in * settings page * - * @return array access_token, urlsrc + * @return string[] access_token, urlsrc + * + * @psalm-return array{status: 'error'|'success', message?: 'Permission denied', urlsrc?: string, token?: string} */ #[PublicPage] #[NoCSRFRequired] @@ -202,9 +204,14 @@ public function createFromTemplate(int $templateId, string $fileName, string $di return $this->documentTemplateResponse($wopi, $params); } + /** + * @return RedirectResponse|TemplateResponse + * + * @psalm-return RedirectResponse<303, array>|TemplateResponse> + */ #[PublicPage] #[NoCSRFRequired] - public function publicPage(string $shareToken, ?string $fileName = null, ?int $fileId = null): TemplateResponse|RedirectResponse { + public function publicPage(string $shareToken, ?string $fileName = null, ?int $fileId = null): TemplateResponse|RedirectResponse|RedirectResponse { try { $share = $this->shareManager->getShareByToken($shareToken); $file = $this->getFileForShare($share, $fileId, $fileName); @@ -247,7 +254,7 @@ public function remote(string $shareToken, string $remoteServer, string $remoteS // not authenticated ? if ($share->getPassword()) { if (!$this->session->exists('public_link_authenticated') - || $this->session->get('public_link_authenticated') !== (string)$share->getId() + || $this->session->get('public_link_authenticated') !== $share->getId() ) { throw new Exception('Invalid password'); } @@ -300,6 +307,9 @@ public function remote(string $shareToken, string $remoteServer, string $remoteS return new TemplateResponse('core', '403', [], 'guest'); } + /** + * @psalm-return TemplateResponse> + */ private function renderErrorPage(string $message, int $status = Http::STATUS_INTERNAL_SERVER_ERROR): TemplateResponse { $params = [ 'errors' => [['error' => $message]] @@ -309,10 +319,15 @@ private function renderErrorPage(string $message, int $status = Http::STATUS_INT return $response; } + /** + * @return RedirectResponse|TemplateResponse + * + * @psalm-return RedirectResponse<303, array>|TemplateResponse> + */ #[NoCSRFRequired] #[NoAdminRequired] #[UseSession] - public function editOnline(?string $path = null, ?string $userId = null, ?string $target = null): RedirectResponse|TemplateResponse { + public function editOnline(?string $path = null, ?string $userId = null, ?string $target = null): TemplateResponse|RedirectResponse|TemplateResponse { if ($path === null) { return $this->renderErrorPage('No path provided'); } @@ -350,10 +365,15 @@ public function editOnline(?string $path = null, ?string $userId = null, ?string return $this->renderErrorPage('File not found', Http::STATUS_NOT_FOUND); } + /** + * @return RedirectResponse|TemplateResponse + * + * @psalm-return RedirectResponse<303, array>|TemplateResponse> + */ #[NoCSRFRequired] #[NoAdminRequired] #[UseSession] - public function editOnlineTarget(int $fileId, ?string $target = null): RedirectResponse|TemplateResponse { + public function editOnlineTarget(int $fileId, ?string $target = null): TemplateResponse|RedirectResponse|TemplateResponse { if (!$this->userId) { return $this->renderErrorPage('File not found', Http::STATUS_NOT_FOUND); } @@ -375,6 +395,9 @@ public function editOnlineTarget(int $fileId, ?string $target = null): RedirectR return $this->renderErrorPage('File not found', Http::STATUS_NOT_FOUND); } + /** + * @psalm-return DataResponse<200, array, array>|DataResponse<500, 'Failed to generate token', array> + */ #[PublicPage] public function token(int $fileId, ?string $shareToken = null, ?string $path = null): DataResponse { try { @@ -395,6 +418,8 @@ public function token(int $fileId, ?string $shareToken = null, ?string $path = n /** * Since collabora does not extend the session on interaction we need to manually trigger this while editing + * + * @psalm-return DataResponse<200, array, array> */ #[NoAdminRequired] public function heartbeat(): DataResponse { @@ -431,7 +456,7 @@ private function getFileForShare(IShare $share, ?int $fileId, ?string $path = nu // not authenticated ? if ($share->getPassword()) { if (!$this->session->exists('public_link_authenticated') - || $this->session->get('public_link_authenticated') !== (string)$share->getId() + || $this->session->get('public_link_authenticated') !== $share->getId() ) { throw new NotPermittedException('Invalid password'); } diff --git a/lib/Controller/DocumentTrait.php b/lib/Controller/DocumentTrait.php index 47d938cc4e..b8c13a0037 100644 --- a/lib/Controller/DocumentTrait.php +++ b/lib/Controller/DocumentTrait.php @@ -24,7 +24,7 @@ private function documentTemplateResponse(Wopi $wopi, array $params): TemplateRe /** * Setup policy headers for the response */ - private function applyPolicies($response) { + private function applyPolicies($response): void { $collaboraHost = $this->domainOnly($this->appConfig->getCollaboraUrlPublic()); $featurePolicy = new FeaturePolicy(); diff --git a/lib/Controller/FederationController.php b/lib/Controller/FederationController.php index 809fe20028..0b7432922f 100644 --- a/lib/Controller/FederationController.php +++ b/lib/Controller/FederationController.php @@ -70,8 +70,12 @@ public function __construct( /** * @PublicPage + * * @NoCSRFRequired + * * @OCSRoute GET /api/v1/federation + * + * @psalm-return DataResponse */ public function index(): DataResponse { $response = new DataResponse([ @@ -125,16 +129,22 @@ public function remoteWopiToken($token): DataResponse { /** * @PublicPage + * * @NoCSRFRequired - * @OCSRoute POST /api/v1/federation/user * - * Return user details for a initiator user that will be used by remote instances - * to provide Collabora with the users avatar/displayname for guests on share links - * if the session was created through a direct link of a public share + * @OCSRoute POST /api/v1/federation/user + + * Return user details for a initiator user that will be used by remote instances + * to provide Collabora with the users avatar/displayname for guests on share links + * if the session was created through a direct link of a public share * * @param $token + * * @return DataResponse + * * @throws OCSNotFoundException + * + * @psalm-return DataResponse<200, array{userId: string, displayName: string, avatar: string}, array> */ public function initiatorUser($token): DataResponse { try { diff --git a/lib/Controller/OCSController.php b/lib/Controller/OCSController.php index 8a1b60ae19..cfbea3a13a 100644 --- a/lib/Controller/OCSController.php +++ b/lib/Controller/OCSController.php @@ -101,15 +101,19 @@ public function __construct(string $appName, } /** - * @NoAdminRequired - * - * Init a direct editing session + * @NoAdminRequired + + * Init a direct editing session * * @param int $fileId + * * @return DataResponse + * * @throws OCSNotFoundException|OCSBadRequestException + * + * @psalm-return DataResponse<200, array{url: string}, array> */ - public function createDirect($fileId) { + public function createDirect($fileId): DataResponse { try { $userFolder = $this->rootFolder->getUserFolder($this->userId); $nodes = $userFolder->getById($fileId); @@ -226,9 +230,14 @@ public function createPublic( /** * @PublicPage + * * @NoCSRFRequired - * @BruteForceProtection(action=richdocumentsCreatePublicFromInitiator) + * + * @BruteForceProtection (action=richdocumentsCreatePublicFromInitiator) + * * @throws OCSForbiddenException + * + * @psalm-return DataResponse<200|403|404, array{url?: string}, array> */ public function createPublicFromInitiator( string $initiatorServer, @@ -273,8 +282,12 @@ public function createPublicFromInitiator( * Generate a direct editing link for a file in a public share to open with the current user * * @NoAdminRequired - * @BruteForceProtection(action=richdocumentsCreatePublic) + * + * @BruteForceProtection (action=richdocumentsCreatePublic) + * * @PublicPage + * + * @psalm-return DataResponse<200|401|403, array, array> */ public function updateGuestName(string $access_token, string $guestName): DataResponse { try { @@ -293,13 +306,18 @@ public function updateGuestName(string $access_token, string $guestName): DataRe /** * @NoAdminRequired + * * @PublicPage * * @param string $type The template type + * * @return DataResponse + * * @throws OCSBadRequestException + * + * @psalm-return DataResponse<200, array, array> */ - public function getTemplates($type) { + public function getTemplates($type): DataResponse { if (array_key_exists($type, TemplateManager::$tplTypes)) { $templates = $this->manager->getAllFormatted($type); return new DataResponse($templates); @@ -312,8 +330,10 @@ public function getTemplates($type) { * * @param string $path Where to create the document * @param int $template The template id + * + * @psalm-return DataResponse<200, array{url: string}, array> */ - public function createFromTemplate($path, $template) { + public function createFromTemplate($path, $template): DataResponse { if ($path === null || $template === null) { throw new OCSBadRequestException('path and template must be set'); } @@ -345,7 +365,12 @@ public function createFromTemplate($path, $template) { } } - private function mb_pathinfo(string $filepath) { + /** + * @return string[] + * + * @psalm-return array{dirname?: string, basename?: string, extension?: string, filename?: string} + */ + private function mb_pathinfo(string $filepath): array { $result = []; preg_match('%^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^\.\\\\/]+?)|))[\\\\/\.]*$%im', ltrim('/' . $filepath), $matches); if ($matches[1]) { diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index 4d4dfbdc08..12d9444478 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -64,6 +64,9 @@ public function __construct($appName, parent::__construct($appName, $request); } + /** + * @psalm-return DataResponse<200|500, array{status: int|string, data: array{settings: array{wopi_url: string, public_wopi_url: string, wopi_callback_url: string, wopi_allowlist: string, disable_certificate_verification: bool, edit_groups: string, use_groups: string, doc_format: string, product_name: null|string, product_version: null|string, product_hash: null|string}, message?: string}}, array> + */ public function checkSettings(): DataResponse { try { $output = new NullOutput(); @@ -88,6 +91,9 @@ public function checkSettings(): DataResponse { ]); } + /** + * @psalm-return DataResponse<200, mixed, array>|DataResponse<404, array, array> + */ public function demoServers(): DataResponse { $demoServers = $this->demoService->fetchDemoServers(true); if (count($demoServers) > 0) { @@ -96,11 +102,19 @@ public function demoServers(): DataResponse { return new DataResponse([], Http::STATUS_NOT_FOUND); } + /** + * @psalm-return JSONResponse<200, array{wopi_url: string, public_wopi_url: string, wopi_callback_url: string, wopi_allowlist: string, disable_certificate_verification: bool, edit_groups: string, use_groups: string, doc_format: string, product_name: null|string, product_version: null|string, product_hash: null|string}, array> + */ #[NoAdminRequired] public function getSettings(): JSONResponse { return new JSONResponse($this->getSettingsData()); } + /** + * @return (bool|null|string)[] + * + * @psalm-return array{wopi_url: string, public_wopi_url: string, wopi_callback_url: string, wopi_allowlist: string, disable_certificate_verification: bool, edit_groups: string, use_groups: string, doc_format: string, product_name: null|string, product_version: null|string, product_hash: null|string} + */ private function getSettingsData(): array { return [ 'wopi_url' => $this->appConfig->getCollaboraUrlInternal(), @@ -117,6 +131,9 @@ private function getSettingsData(): array { ]; } + /** + * @psalm-return JSONResponse<200|500, array{status: 'error'|'success', data: array{message: string, settings?: array{wopi_url: string, public_wopi_url: string, wopi_callback_url: string, wopi_allowlist: string, disable_certificate_verification: bool, edit_groups: string, use_groups: string, doc_format: string, product_name: null|string, product_version: null|string, product_hash: null|string}}}, array> + */ public function setSettings( ?string $wopi_url, ?string $wopi_allowlist, @@ -185,6 +202,9 @@ public function setSettings( return new JSONResponse($response); } + /** + * @psalm-return JSONResponse<200|400, array{status: 'error'|'success', data: array{message: string}}, array> + */ public function updateWatermarkSettings($settings = []): JSONResponse { $supportedOptions = [ 'watermark_text', @@ -237,10 +257,13 @@ public function updateWatermarkSettings($settings = []): JSONResponse { * * @param $key * @param $value + * * @return JSONResponse + * + * @psalm-return JSONResponse<200, array{status: 'error'|'success', data: array{message: string}}, array> */ public function setPersonalSettings($templateFolder, - $zoteroAPIKeyInput) { + $zoteroAPIKeyInput): JSONResponse { $message = $this->l10n->t('Saved'); $status = 'success'; @@ -271,13 +294,18 @@ public function setPersonalSettings($templateFolder, /** * @NoAdminRequired + * * @PublicPage + * * @NoCSRFRequired * - * @return JSONResponse|DataResponse + * @return DataResponse|JSONResponse + * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return DataResponse<304, array, array>|JSONResponse<200, array, array> */ - public function getFontNames() { + public function getFontNames(): JSONResponse|DataResponse { $fileNames = $this->fontService->getFontFileNames(); $etag = md5(implode('/', $fileNames)); $ifNoneMatchHeader = $this->request->getHeader('If-None-Match'); @@ -291,13 +319,18 @@ public function getFontNames() { /** * @NoAdminRequired + * * @PublicPage + * * @NoCSRFRequired * - * @return JSONResponse|DataResponse + * @return DataResponse|JSONResponse + * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return DataResponse<304, array, array>|JSONResponse<200, array{kind: 'fontconfiguration', server: string, fonts: array}, array> */ - public function getJsonFontList() { + public function getJsonFontList(): JSONResponse|DataResponse|DataResponse { $files = $this->fontService->getFontFiles(); $etags = array_map( static function (ISimpleFile $f) { @@ -319,14 +352,20 @@ static function (ISimpleFile $f) { /** * @NoAdminRequired + * * @PublicPage + * * @NoCSRFRequired * * @param string $name + * * @return DataDisplayResponse|DataResponse + * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return DataDisplayResponse<200|404, array{'Content-Type'?: string, Etag?: string}>|DataResponse<304, array, array> */ - public function getFontFile(string $name) { + public function getFontFile(string $name): DataDisplayResponse|DataResponse { try { $fontFile = $this->fontService->getFontFile($name); $etag = $fontFile->getETag(); @@ -347,12 +386,18 @@ public function getFontFile(string $name) { /** * @NoAdminRequired + * * @PublicPage + * * @NoCSRFRequired * * @param string $name + * * @return DataDisplayResponse + * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return DataDisplayResponse<200|404, array{'Content-Type'?: 'image/png'}> */ public function getFontFileOverview(string $name): DataDisplayResponse { try { @@ -369,9 +414,13 @@ public function getFontFileOverview(string $name): DataDisplayResponse { /** * @param string $name + * * @return DataResponse + * * @throws NotFoundException * @throws \OCP\Files\NotPermittedException + * + * @psalm-return DataResponse<200, array, array> */ public function deleteFontFile(string $name): DataResponse { $this->fontService->deleteFontFile($name); @@ -380,6 +429,8 @@ public function deleteFontFile(string $name): DataResponse { /** * @return JSONResponse + * + * @psalm-return JSONResponse<200|400, array{error?: string, size?: float|int}, array> */ public function uploadFontFile(): JSONResponse { try { @@ -417,7 +468,6 @@ public function uploadFontFile(): JSONResponse { */ private function getUploadedFile(string $key): array { $file = $this->request->getUploadedFile($key); - $error = null; $phpFileUploadErrors = [ UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'), UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'), diff --git a/lib/Controller/TargetController.php b/lib/Controller/TargetController.php index da41896bc0..511c83db63 100644 --- a/lib/Controller/TargetController.php +++ b/lib/Controller/TargetController.php @@ -28,6 +28,8 @@ public function __construct( /** * @NoAdminRequired + * + * @psalm-return DataResponse<200, array, array>|DataResponse<404, 'File not found', array> */ public function getTargets(string $path): DataResponse { try { @@ -41,9 +43,14 @@ public function getTargets(string $path): DataResponse { /** * @NoAdminRequired + * * @NoCSRFRequired + * + * @return DataDisplayResponse|DataResponse + * + * @psalm-return DataDisplayResponse<200, array{'Content-Type': 'image/png'}>|DataResponse<404, 'File not found', array> */ - public function getPreview(string $path, string $target): Response { + public function getPreview(string $path, string $target): DataResponse|DataDisplayResponse { try { $file = $this->getFile($path); return new DataDisplayResponse( diff --git a/lib/Controller/TemplatesController.php b/lib/Controller/TemplatesController.php index 70aa4c0961..9d11b4e7dd 100644 --- a/lib/Controller/TemplatesController.php +++ b/lib/Controller/TemplatesController.php @@ -85,9 +85,9 @@ public function __construct($appName, * * @NoCSRFRequired * - * @PublicPage - * - * Get preview for a specific template + * @PublicPage + + * Get preview for a specific template * * @param int $fileId The template id * @param int $x @@ -98,14 +98,14 @@ public function __construct($appName, * * @throws NotFoundResponse * - * @psalm-return DataResponse<400|404, array, array>|DataResponse>|FileDisplayResponse> + * @psalm-return DataResponse<400|403|404, array, array>|FileDisplayResponse<200, array{'Content-Type': string}> */ public function getPreview($fileId, $x = 150, $y = 150, $a = false, $forceIcon = true, - $mode = 'fill'): DataResponse|FileDisplayResponse { + $mode = 'fill'): DataResponse|FileDisplayResponse|FileDisplayResponse { if ($fileId === '' || $x === 0 || $y === 0) { return new DataResponse([], Http::STATUS_BAD_REQUEST); } @@ -127,8 +127,10 @@ public function getPreview($fileId, * Add a global template * * @return JSONResponse + * + * @psalm-return JSONResponse<201|400, array{data: array}, array> */ - public function add() { + public function add(): JSONResponse { $files = $this->request->getUploadedFile('files'); if (!is_null($files)) { @@ -182,9 +184,12 @@ public function add() { * Delete a global template * * @param int $fileId + * * @return JSONResponse + * + * @psalm-return JSONResponse<204|404, array{data: array{message?: string, status?: 'success'}}, array> */ - public function delete($fileId) { + public function delete($fileId): JSONResponse { try { $this->manager->delete($fileId); @@ -207,7 +212,10 @@ public function delete($fileId) { * @param bool $a * @param bool $forceIcon * @param string $mode + * * @return DataResponse|FileDisplayResponse + * + * @psalm-return DataResponse<400|403|404, array, array>|FileDisplayResponse<200, array{'Content-Type': string}> */ private function fetchPreview( Node $node, @@ -215,7 +223,7 @@ private function fetchPreview( int $y, bool $a = false, bool $forceIcon = true, - string $mode = IPreview::MODE_FILL): Http\Response { + string $mode = IPreview::MODE_FILL): DataResponse|FileDisplayResponse { if (!($node instanceof Node) || (!$forceIcon && !$this->preview->isAvailable($node))) { return new DataResponse([], Http::STATUS_NOT_FOUND); } diff --git a/lib/Controller/WopiController.php b/lib/Controller/WopiController.php index d11d574fc2..3c36fab1b3 100644 --- a/lib/Controller/WopiController.php +++ b/lib/Controller/WopiController.php @@ -79,590 +79,13 @@ class WopiController extends Controller { public const WOPI_AVATAR_SIZE = 64; - public function __construct( - $appName, - IRequest $request, - private IRootFolder $rootFolder, - private IURLGenerator $urlGenerator, - private IConfig $config, - private AppConfig $appConfig, - private TokenManager $tokenManager, - private PermissionManager $permissionManager, - private IUserManager $userManager, - private WopiMapper $wopiMapper, - private LoggerInterface $logger, - private TemplateManager $templateManager, - private IShareManager $shareManager, - private UserScopeService $userScopeService, - private FederationService $federationService, - private IEncryptionManager $encryptionManager, - private IGroupManager $groupManager, - private ILockManager $lockManager, - private IEventDispatcher $eventDispatcher - ) { - parent::__construct($appName, $request); - } - - /** - * Returns general info about a file. - * - * @NoAdminRequired - * @NoCSRFRequired - * @PublicPage - * - * @param string $fileId - * @param string $access_token - * @return JSONResponse - * @throws InvalidPathException - * @throws NotFoundException - */ - public function checkFileInfo($fileId, $access_token) { - try { - [$fileId, , $version] = Helper::parseFileId($fileId); - - $wopi = $this->wopiMapper->getWopiForToken($access_token); - $file = $this->getFileForWopiToken($wopi); - if (!($file instanceof File)) { - throw new NotFoundException('No valid file found for ' . $fileId); - } - } catch (NotFoundException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (UnknownTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (ExpiredTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_UNAUTHORIZED); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - $isPublic = empty($wopi->getEditorUid()); - $guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8); - $user = $this->userManager->get($wopi->getEditorUid()); - $userDisplayName = $user !== null && !$isPublic ? $user->getDisplayName() : $wopi->getGuestDisplayname(); - $isVersion = $version !== '0'; - - // If the file is locked manually by a user we want to open it read only for all others - $canWriteThroughLock = true; - try { - $locks = $this->lockManager->getLocks($wopi->getFileid()); - $canWriteThroughLock = count($locks) > 0 && $locks[0]->getType() === ILock::TYPE_USER && $locks[0]->getOwner() !== $wopi->getEditorUid() ? false : true; - } catch (NoLockProviderException|PreConditionNotMetException) { - } - - $response = [ - 'BaseFileName' => $file->getName(), - 'Size' => $file->getSize(), - 'Version' => $version, - 'UserId' => !$isPublic ? $wopi->getEditorUid() : $guestUserId, - 'OwnerId' => $wopi->getOwnerUid(), - 'UserFriendlyName' => $userDisplayName, - 'UserExtraInfo' => [], - 'UserPrivateInfo' => [], - 'UserCanWrite' => $canWriteThroughLock && (bool)$wopi->getCanwrite(), - 'UserCanNotWriteRelative' => $isPublic || $this->encryptionManager->isEnabled() || $wopi->getHideDownload(), - 'PostMessageOrigin' => $wopi->getServerHost(), - 'LastModifiedTime' => Helper::toISO8601($file->getMTime()), - 'SupportsRename' => !$isVersion, - 'UserCanRename' => !$isPublic && !$isVersion, - 'EnableInsertRemoteImage' => !$isPublic, - 'EnableShare' => $file->isShareable() && !$isVersion && !$isPublic, - 'HideUserList' => '', - 'EnableOwnerTermination' => $wopi->getCanwrite() && !$isPublic, - 'DisablePrint' => $wopi->getHideDownload(), - 'DisableExport' => $wopi->getHideDownload(), - 'DisableCopy' => $wopi->getHideDownload(), - 'HideExportOption' => $wopi->getHideDownload(), - 'HidePrintOption' => $wopi->getHideDownload(), - 'DownloadAsPostMessage' => $wopi->getDirect(), - 'SupportsLocks' => $this->lockManager->isLockProviderAvailable(), - 'IsUserLocked' => $this->permissionManager->userIsFeatureLocked($wopi->getEditorUid()), - 'EnableRemoteLinkPicker' => (bool)$wopi->getCanwrite() && !$isPublic && !$wopi->getDirect(), - 'HasContentRange' => true, - ]; - - $enableZotero = $this->config->getAppValue(Application::APPNAME, 'zoteroEnabled', 'yes') === 'yes'; - if (!$isPublic && $enableZotero) { - $zoteroAPIKey = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'zoteroAPIKey', ''); - $response['UserPrivateInfo']['ZoteroAPIKey'] = $zoteroAPIKey; - } - if ($wopi->hasTemplateId()) { - $response['TemplateSource'] = $this->getWopiUrlForTemplate($wopi); - } - - $share = $this->getShareForWopiToken($wopi); - if ($this->permissionManager->shouldWatermark($file, $wopi->getEditorUid(), $share)) { - $email = $user !== null && !$isPublic ? $user->getEMailAddress() : ""; - $replacements = [ - 'userId' => $wopi->getEditorUid(), - 'date' => (new \DateTime())->format('Y-m-d H:i:s'), - 'themingName' => \OC::$server->getThemingDefaults()->getName(), - 'userDisplayName' => $userDisplayName, - 'email' => $email, - ]; - $watermarkTemplate = $this->appConfig->getAppValue('watermark_text'); - $response['WatermarkText'] = preg_replace_callback('/{(.+?)}/', function ($matches) use ($replacements) { - return $replacements[$matches[1]]; - }, $watermarkTemplate); - } - - $user = $this->userManager->get($wopi->getEditorUid()); - if ($user !== null) { - $response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => $wopi->getEditorUid(), 'size' => self::WOPI_AVATAR_SIZE]); - if ($this->groupManager->isAdmin($wopi->getEditorUid())) { - $response['UserExtraInfo']['is_admin'] = true; - } - } else { - $response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.GuestAvatar.getAvatar', ['guestName' => urlencode($wopi->getGuestDisplayname()), 'size' => self::WOPI_AVATAR_SIZE]); - } - - if ($isPublic) { - $response['UserExtraInfo']['is_guest'] = true; - } - - if ($wopi->isRemoteToken()) { - $response = $this->setFederationFileInfo($wopi, $response); - } - - $response = array_merge($response, $this->appConfig->getWopiOverride()); - - $this->eventDispatcher->dispatchTyped(new DocumentOpenedEvent( - $user ? $user->getUID() : null, - $file - )); - - return new JSONResponse($response); - } - - - private function setFederationFileInfo(Wopi $wopi, $response) { - $response['UserId'] = 'Guest-' . \OC::$server->getSecureRandom()->generate(8); - - if ($wopi->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_USER) { - $remoteUserId = $wopi->getGuestDisplayname(); - $cloudID = \OC::$server->getCloudIdManager()->resolveCloudId($remoteUserId); - $response['UserId'] = $cloudID->getDisplayId(); - $response['UserFriendlyName'] = $cloudID->getDisplayId(); - $response['UserExtraInfo']['avatar'] = $this->urlGenerator->linkToRouteAbsolute('core.avatar.getAvatar', ['userId' => explode('@', $remoteUserId)[0], 'size' => self::WOPI_AVATAR_SIZE]); - $cleanCloudId = str_replace(['http://', 'https://'], '', $cloudID->getId()); - $addressBookEntries = \OC::$server->getContactsManager()->search($cleanCloudId, ['CLOUD']); - foreach ($addressBookEntries as $entry) { - if (isset($entry['CLOUD'])) { - foreach ($entry['CLOUD'] as $cloudID) { - if ($cloudID === $cleanCloudId) { - $response['UserFriendlyName'] = $entry['FN']; - break; - } - } - } - } - } - - $initiator = $this->federationService->getRemoteFileDetails($wopi->getRemoteServer(), $wopi->getRemoteServerToken()); - if ($initiator === null) { - return $response; - } - - $response['UserFriendlyName'] = $this->tokenManager->prepareGuestName($initiator->getGuestDisplayname()); - if ($initiator->hasTemplateId()) { - $templateUrl = $wopi->getRemoteServer() . '/index.php/apps/richdocuments/wopi/template/' . $initiator->getTemplateId() . '?access_token=' . $initiator->getToken(); - $response['TemplateSource'] = $templateUrl; - } - if ($wopi->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_USER || ($wopi->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_GUEST && $initiator->getEditorUid())) { - $response['UserExtraInfo']['avatar'] = $wopi->getRemoteServer() . '/index.php/avatar/' . $initiator->getEditorUid() . '/' . self::WOPI_AVATAR_SIZE; - } - - return $response; - } /** - * Given an access token and a fileId, returns the contents of the file. - * Expects a valid token in access_token parameter. - * - * @PublicPage - * @NoCSRFRequired - * - * @param string $fileId - * @param string $access_token - * @return Http\Response - * @throws NotFoundException - * @throws NotPermittedException - * @throws LockedException + * @psalm-return JSONResponse, array> */ - public function getFile($fileId, - $access_token) { - [$fileId, , $version] = Helper::parseFileId($fileId); - - try { - $wopi = $this->wopiMapper->getWopiForToken($access_token); - } catch (UnknownTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (ExpiredTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_UNAUTHORIZED); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - if ((int)$fileId !== $wopi->getFileid()) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - try { - /** @var File $file */ - $file = $this->getFileForWopiToken($wopi); - \OC_User::setIncognitoMode(true); - if ($version !== '0') { - $versionManager = \OC::$server->get(IVersionManager::class); - $info = $versionManager->getVersionFile($this->userManager->get($wopi->getUserForFileAccess()), $file, $version); - if ($info->getSize() === 0) { - $response = new Http\Response(); - } else { - $response = new StreamResponse($info->fopen('rb')); - } - } else { - if ($file->getSize() === 0) { - $response = new Http\Response(); - } else { - - $filesize = $file->getSize(); - if ($this->request->getHeader('Range')) { - $partialContent = true; - preg_match('/bytes=(\d+)-(\d+)?/', $this->request->getHeader('Range'), $matches); - - $offset = intval($matches[1] ?? 0); - $length = intval($matches[2] ?? 0) - $offset + 1; - if ($length <= 0) { - $length = $filesize - $offset; - } - - $fp = $file->fopen('rb'); - $rangeStream = fopen("php://temp", "w+b"); - stream_copy_to_stream($fp, $rangeStream, $length, $offset); - fclose($fp); - - fseek($rangeStream, 0); - $response = new StreamResponse($rangeStream); - $response->addHeader('Accept-Ranges', 'bytes'); - $response->addHeader('Content-Length', $filesize); - $response->setStatus(Http::STATUS_PARTIAL_CONTENT); - $response->addHeader('Content-Range', 'bytes ' . $offset . '-' . ($offset + $length) . '/' . $filesize); - } else { - $response = new StreamResponse($file->fopen('rb')); - } - } - } - $response->addHeader('Content-Disposition', 'attachment'); - $response->addHeader('Content-Type', 'application/octet-stream'); - return $response; - } catch (\Exception $e) { - $this->logger->error('getFile failed: ' . $e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (NotFoundExceptionInterface|ContainerExceptionInterface $e) { - $this->logger->error('Version manager could not be found when trying to restore file. Versioning app disabled?: ' . $e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_BAD_REQUEST); - } - } - - /** - * Given an access token and a fileId, replaces the files with the request body. - * Expects a valid token in access_token parameter. - * - * @PublicPage - * @NoCSRFRequired - * - * @param string $fileId - * @param string $access_token - * @return JSONResponse - */ - public function putFile($fileId, - $access_token) { - [$fileId, , ] = Helper::parseFileId($fileId); - $isPutRelative = ($this->request->getHeader('X-WOPI-Override') === 'PUT_RELATIVE'); - - try { - $wopi = $this->wopiMapper->getWopiForToken($access_token); - } catch (UnknownTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (ExpiredTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_UNAUTHORIZED); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - if (!$wopi->getCanwrite()) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - if (!$this->encryptionManager->isEnabled() || $this->isMasterKeyEnabled()) { - // Set the user to register the change under his name - $this->userScopeService->setUserScope($wopi->getEditorUid()); - $this->userScopeService->setFilesystemScope($isPutRelative ? $wopi->getEditorUid() : $wopi->getUserForFileAccess()); - } else { - // Per-user encryption is enabled so that collabora isn't able to store the file by using the - // user's private key. Because of that we have to use the incognito mode for writing the file. - \OC_User::setIncognitoMode(true); - } - - try { - if ($isPutRelative) { - // the new file needs to be installed in the current user dir - $userFolder = $this->rootFolder->getUserFolder($wopi->getEditorUid()); - $file = $userFolder->getById($fileId); - if (count($file) === 0) { - return new JSONResponse([], Http::STATUS_NOT_FOUND); - } - $file = $file[0]; - $suggested = $this->request->getHeader('X-WOPI-SuggestedTarget'); - $suggested = mb_convert_encoding($suggested, 'utf-8', 'utf-7'); - - if ($suggested[0] === '.') { - $path = dirname($file->getPath()) . '/New File' . $suggested; - } elseif ($suggested[0] !== '/') { - $path = dirname($file->getPath()) . '/' . $suggested; - } else { - $path = $userFolder->getPath() . $suggested; - } - - if ($path === '') { - return new JSONResponse([ - 'status' => 'error', - 'message' => 'Cannot create the file' - ]); - } - - // create the folder first - if (!$this->rootFolder->nodeExists(dirname($path))) { - $this->rootFolder->newFolder(dirname($path)); - } - - // create a unique new file - $path = $this->rootFolder->getNonExistingName($path); - $this->rootFolder->newFile($path); - $file = $this->rootFolder->get($path); - } else { - $file = $this->getFileForWopiToken($wopi); - $wopiHeaderTime = $this->request->getHeader('X-LOOL-WOPI-Timestamp'); - - if (!empty($wopiHeaderTime) && $wopiHeaderTime !== Helper::toISO8601($file->getMTime() ?? 0)) { - $this->logger->debug('Document timestamp mismatch ! WOPI client says mtime {headerTime} but storage says {storageTime}', [ - 'headerTime' => $wopiHeaderTime, - 'storageTime' => Helper::toISO8601($file->getMTime() ?? 0) - ]); - // Tell WOPI client about this conflict. - return new JSONResponse(['LOOLStatusCode' => self::LOOL_STATUS_DOC_CHANGED], Http::STATUS_CONFLICT); - } - } - - $content = fopen('php://input', 'rb'); - - $freespace = $file->getParent()->getFreeSpace(); - $contentLength = (int)$this->request->getHeader('Content-Length'); - - try { - if ($freespace >= 0 && $contentLength > $freespace) { - throw new \Exception('Not enough storage'); - } - $this->wrappedFilesystemOperation($wopi, function () use ($file, $content) { - return $file->putContent($content); - }); - } catch (LockedException $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse(['message' => 'File locked'], Http::STATUS_INTERNAL_SERVER_ERROR); - } - - if ($isPutRelative) { - // generate a token for the new file (the user still has to be logged in) - $wopi = $this->tokenManager->generateWopiToken((string)$file->getId(), null, $wopi->getEditorUid(), $wopi->getDirect()); - return new JSONResponse(['Name' => $file->getName(), 'Url' => $this->getWopiUrlForFile($wopi, $file)], Http::STATUS_OK); - } - if ($wopi->hasTemplateId()) { - $wopi->setTemplateId(null); - $this->wopiMapper->update($wopi); - } - return new JSONResponse(['LastModifiedTime' => Helper::toISO8601($file->getMTime())]); - } catch (NotFoundException $e) { - $this->logger->warning($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_NOT_FOUND); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); - } - } - - /** - * Given an access token and a fileId, replaces the files with the request body. - * Expects a valid token in access_token parameter. - * Just actually routes to the PutFile, the implementation of PutFile - * handles both saving and saving as.* Given an access token and a fileId, replaces the files with the request body. - * - * FIXME Cleanup this code as is a lot of shared logic between putFile and putRelativeFile - * - * @PublicPage - * @NoCSRFRequired - * - * @param string $fileId - * @param string $access_token - * @return JSONResponse - * @throws DoesNotExistException - */ - public function postFile(string $fileId, string $access_token): JSONResponse { - try { - $wopiOverride = $this->request->getHeader('X-WOPI-Override'); - $wopiLock = $this->request->getHeader('X-WOPI-Lock'); - [$fileId, , ] = Helper::parseFileId($fileId); - $wopi = $this->wopiMapper->getWopiForToken($access_token); - if ((int) $fileId !== $wopi->getFileid()) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - } catch (UnknownTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (ExpiredTokenException $e) { - $this->logger->debug($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_UNAUTHORIZED); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - switch ($wopiOverride) { - case 'LOCK': - return $this->lock($wopi, $wopiLock); - case 'UNLOCK': - return $this->unlock($wopi, $wopiLock); - case 'REFRESH_LOCK': - return $this->refreshLock($wopi, $wopiLock); - case 'GET_LOCK': - return $this->getLock($wopi, $wopiLock); - case 'RENAME_FILE': - break; //FIXME: Move to function - default: - break; //FIXME: Move to function and add error for unsupported method - } - - - $isRenameFile = ($this->request->getHeader('X-WOPI-Override') === 'RENAME_FILE'); - - if (!$wopi->getCanwrite()) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - // Unless the editor is empty (public link) we modify the files as the current editor - $editor = $wopi->getEditorUid(); - if ($editor === null && !$wopi->isRemoteToken()) { - $editor = $wopi->getOwnerUid(); - } - - try { - // the new file needs to be installed in the current user dir - $userFolder = $this->rootFolder->getUserFolder($editor); - - if ($isRenameFile) { - // the new file needs to be installed in the current user dir - $file = $this->getFileForWopiToken($wopi); - - $suggested = $this->request->getHeader('X-WOPI-RequestedName'); - - $suggested = mb_convert_encoding($suggested, 'utf-8', 'utf-7') . '.' . $file->getExtension(); - - if (strpos($suggested, '.') === 0) { - $path = dirname($file->getPath()) . '/New File' . $suggested; - } elseif (strpos($suggested, '/') !== 0) { - $path = dirname($file->getPath()) . '/' . $suggested; - } else { - $path = $userFolder->getPath() . $suggested; - } - - if ($path === '') { - return new JSONResponse([ - 'status' => 'error', - 'message' => 'Cannot rename the file' - ]); - } - - // create the folder first - if (!$this->rootFolder->nodeExists(dirname($path))) { - $this->rootFolder->newFolder(dirname($path)); - } - - // create a unique new file - $path = $this->rootFolder->getNonExistingName($path); - $file = $file->move($path); - } else { - $file = $this->getFileForWopiToken($wopi); - - $suggested = $this->request->getHeader('X-WOPI-SuggestedTarget'); - $suggested = mb_convert_encoding($suggested, 'utf-8', 'utf-7'); - - if ($suggested[0] === '.') { - $path = dirname($file->getPath()) . '/New File' . $suggested; - } elseif ($suggested[0] !== '/') { - $path = dirname($file->getPath()) . '/' . $suggested; - } else { - $path = $userFolder->getPath() . $suggested; - } - - if ($path === '') { - return new JSONResponse([ - 'status' => 'error', - 'message' => 'Cannot create the file' - ]); - } - - // create the folder first - if (!$this->rootFolder->nodeExists(dirname($path))) { - $this->rootFolder->newFolder(dirname($path)); - } - - // create a unique new file - $path = $this->rootFolder->getNonExistingName($path); - $file = $this->rootFolder->newFile($path); - } - - $content = fopen('php://input', 'rb'); - // Set the user to register the change under his name - $this->userScopeService->setUserScope($wopi->getEditorUid()); - $this->userScopeService->setFilesystemScope($wopi->getEditorUid()); - - try { - $this->wrappedFilesystemOperation($wopi, function () use ($file, $content) { - return $file->putContent($content); - }); - } catch (LockedException $e) { - return new JSONResponse(['message' => 'File locked'], Http::STATUS_INTERNAL_SERVER_ERROR); - } - - // epub is exception (can be uploaded but not opened so don't try to get access token) - if ($file->getMimeType() == 'application/epub+zip') { - return new JSONResponse(['Name' => $file->getName()], Http::STATUS_OK); - } - - // generate a token for the new file (the user still has to be - // logged in) - $wopi = $this->tokenManager->generateWopiToken((string)$file->getId(), null, $wopi->getEditorUid(), $wopi->getDirect()); - - return new JSONResponse(['Name' => $file->getName(), 'Url' => $this->getWopiUrlForFile($wopi, $file)], Http::STATUS_OK); - } catch (NotFoundException $e) { - $this->logger->warning($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_NOT_FOUND); - } catch (\Exception $e) { - $this->logger->error($e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); - } - } - private function lock(Wopi $wopi, string $lock): JSONResponse { try { - $lock = $this->lockManager->lock(new LockContext( + $this->lockManager->lock(new LockContext( $this->getFileForWopiToken($wopi), ILock::TYPE_APP, Application::APPNAME @@ -682,6 +105,9 @@ private function lock(Wopi $wopi, string $lock): JSONResponse { } } + /** + * @psalm-return JSONResponse<200|400|500, array, array> + */ private function unlock(Wopi $wopi, string $lock): JSONResponse { try { $this->lockManager->unlock(new LockContext( @@ -702,57 +128,16 @@ private function unlock(Wopi $wopi, string $lock): JSONResponse { } } - private function refreshLock(Wopi $wopi, string $lock): JSONResponse { - try { - $lock = $this->lockManager->lock(new LockContext( - $this->getFileForWopiToken($wopi), - ILock::TYPE_APP, - Application::APPNAME - )); - return new JSONResponse(); - } catch (NoLockProviderException|PreConditionNotMetException $e) { - return new JSONResponse([], Http::STATUS_BAD_REQUEST); - } catch (OwnerLockedException $e) { - return new JSONResponse([], Http::STATUS_LOCKED); - } catch (\Exception $e) { - return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); - } - } - - private function getLock(Wopi $wopi, string $lock): JSONResponse { - $locks = $this->lockManager->getLocks($wopi->getFileid()); - return new JSONResponse(); - } - - /** - * @throws NotFoundException - * @throws GenericFileException - * @throws LockedException - * @throws ShareNotFound - */ - protected function wrappedFilesystemOperation(Wopi $wopi, callable $filesystemOperation): void { - $retryOperation = function () use ($filesystemOperation) { - $this->retryOperation($filesystemOperation); - }; - try { - $this->lockManager->runInScope(new LockContext( - $this->getFileForWopiToken($wopi), - ILock::TYPE_APP, - Application::APPNAME - ), $retryOperation); - } catch (NoLockProviderException $e) { - $retryOperation(); - } - } - /** * Retry operation if a LockedException occurred * Other exceptions will still be thrown + * * @param callable $operation + * * @throws LockedException * @throws GenericFileException */ - private function retryOperation(callable $operation) { + private function retryOperation(callable $operation): void { for ($i = 0; $i < 5; $i++) { try { if ($operation() !== false) { @@ -807,72 +192,4 @@ private function getFileForWopiToken(Wopi $wopi) { 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 - * - * @PublicPage - * @NoCSRFRequired - * - * @param $fileId - * @param $access_token - * @return JSONResponse|StreamResponse - */ - public function getTemplate($fileId, $access_token) { - try { - $wopi = $this->wopiMapper->getWopiForToken($access_token); - } catch (UnknownTokenException $e) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } catch (ExpiredTokenException $e) { - return new JSONResponse([], Http::STATUS_UNAUTHORIZED); - } - - if ((int)$fileId !== $wopi->getTemplateId()) { - return new JSONResponse([], Http::STATUS_FORBIDDEN); - } - - try { - $this->templateManager->setUserId($wopi->getOwnerUid()); - $file = $this->templateManager->get($wopi->getTemplateId()); - $response = new StreamResponse($file->fopen('rb')); - $response->addHeader('Content-Disposition', 'attachment'); - $response->addHeader('Content-Type', 'application/octet-stream'); - return $response; - } catch (\Exception $e) { - $this->logger->error('getTemplate failed: ' . $e->getMessage(), ['exception' => $e]); - return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); - } - } - - /** - * Check if the encryption module uses a master key. - */ - private function isMasterKeyEnabled(): bool { - try { - $util = \OC::$server->query(\OCA\Encryption\Util::class); - return $util->isMasterKeyEnabled(); - } catch (QueryException $e) { - // No encryption module enabled - return false; - } - } - - private function getWopiUrlForFile(Wopi $wopi, File $file): string { - $nextcloudUrl = $this->appConfig->getNextcloudUrl() ?: trim($this->urlGenerator->getAbsoluteURL(''), '/'); - return $nextcloudUrl . '/index.php/apps/richdocuments/wopi/files/' . $file->getId() . '_' . $this->config->getSystemValue('instanceid') . '?access_token=' . $wopi->getToken(); - } - - private function getWopiUrlForTemplate(Wopi $wopi): string { - $nextcloudUrl = $this->appConfig->getNextcloudUrl() ?: trim($this->urlGenerator->getAbsoluteURL(''), '/'); - return $nextcloudUrl . '/index.php/apps/richdocuments/wopi/template/' . $wopi->getTemplateId() . '?access_token=' . $wopi->getToken(); - } } diff --git a/lib/Db/Asset.php b/lib/Db/Asset.php index 06be2e6cae..7d7d8a9ef0 100644 --- a/lib/Db/Asset.php +++ b/lib/Db/Asset.php @@ -36,17 +36,7 @@ * @method int getTimestamp() */ class Asset extends Entity { - /** @var string */ - protected $uid; - /** @var int */ - protected $fileid; - - /** @var string */ - protected $token; - - /** @var int */ - protected $timestamp; public function __construct() { $this->addType('uid', 'string'); diff --git a/lib/Db/AssetMapper.php b/lib/Db/AssetMapper.php index bc261d2fe3..905b2be58e 100644 --- a/lib/Db/AssetMapper.php +++ b/lib/Db/AssetMapper.php @@ -37,13 +37,6 @@ class AssetMapper extends QBMapper { private ISecureRandom $random; private ITimeFactory $time; - public function __construct(IDBConnection $db, ISecureRandom $random, ITimeFactory $timeFactory) { - parent::__construct($db, 'richdocuments_assets', Asset::class); - - $this->random = $random; - $this->time = $timeFactory; - } - /** * @param null|string $uid * @param $fileid diff --git a/lib/Db/Direct.php b/lib/Db/Direct.php index 5d87090dc2..b0c2b508c1 100644 --- a/lib/Db/Direct.php +++ b/lib/Db/Direct.php @@ -40,32 +40,7 @@ * @method int getTemplateId() */ class Direct extends Entity { - /** @var string */ - protected $token; - /** @var string */ - protected $uid; - - /** @var int */ - protected $fileid; - - /** @var int */ - protected $timestamp; - - /** @var int */ - protected $templateDestination; - - /** @var int */ - protected $templateId; - - /** @var string */ - protected $share; - - /** @var string */ - protected $initiatorHost; - - /** @var string */ - protected $initiatorToken; public function __construct() { $this->addType('token', 'string'); diff --git a/lib/Db/DirectMapper.php b/lib/Db/DirectMapper.php index 575ac2dc69..d1febccafd 100644 --- a/lib/Db/DirectMapper.php +++ b/lib/Db/DirectMapper.php @@ -37,15 +37,6 @@ class DirectMapper extends QBMapper { protected ISecureRandom $random; protected ITimeFactory $timeFactory; - public function __construct(IDBConnection $db, - ISecureRandom $random, - ITimeFactory $timeFactory) { - parent::__construct($db, 'richdocuments_direct', Direct::class); - - $this->random = $random; - $this->timeFactory = $timeFactory; - } - /** * @param string|null $uid * @param int $fileid diff --git a/lib/Db/Wopi.php b/lib/Db/Wopi.php index 177f10038e..e4e4237b08 100644 --- a/lib/Db/Wopi.php +++ b/lib/Db/Wopi.php @@ -84,51 +84,12 @@ class Wopi extends Entity implements \JsonSerializable { */ public const TOKEN_TYPE_INITIATOR = 4; - /** @var string */ - protected $ownerUid; - - /** @var string */ - protected $editorUid; - - /** @var int */ - protected $fileid; - - /** @var int */ - protected $version; - - /** @var bool */ - protected $canwrite; - - /** @var string */ - protected $serverHost; - - /** @var string */ - protected $token; - - /** @var int */ - protected $expiry; - - /** @var string */ - protected $guestDisplayname; - - /** @var int */ - protected $templateDestination; - - /** @var int */ - protected $templateId; - /** @var bool */ protected $hideDownload; /** @var bool */ protected $direct; - /** @var string */ - protected $remoteServer; - - /** @var string */ - protected $remoteServerToken; - /** @var string */ protected $share; @@ -152,31 +113,19 @@ public function __construct() { $this->addType('tokenType', 'int'); } - public function hasTemplateId() { - return $this->getTemplateId() !== 0 && $this->getTemplateId() !== null; - } - - public function isGuest() { + public function isGuest(): bool { return $this->getTokenType() === Wopi::TOKEN_TYPE_GUEST || $this->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_GUEST; } - public function isRemoteToken() { - return $this->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_USER || $this->getTokenType() === Wopi::TOKEN_TYPE_REMOTE_GUEST; - } - - public function getUserForFileAccess() { + public function getUserForFileAccess(): string { if ($this->share !== null || $this->tokenType === self::TOKEN_TYPE_REMOTE_USER || $this->tokenType === self::TOKEN_TYPE_REMOTE_GUEST) { return $this->getOwnerUid(); } return $this->isGuest() ? $this->getOwnerUid() : $this->getEditorUid(); } - public function getHideDownload() { - return (bool)$this->hideDownload; - } - - public function getDirect() { - return (bool)$this->direct; + public function getHideDownload(): bool { + return $this->hideDownload; } #[\ReturnTypeWillChange] diff --git a/lib/Db/WopiMapper.php b/lib/Db/WopiMapper.php index 2f27921087..845f64d46e 100644 --- a/lib/Db/WopiMapper.php +++ b/lib/Db/WopiMapper.php @@ -46,19 +46,6 @@ class WopiMapper extends QBMapper { /** @var AppConfig */ private $appConfig; - public function __construct(IDBConnection $db, - ISecureRandom $random, - LoggerInterface $logger, - ITimeFactory $timeFactory, - AppConfig $appConfig) { - parent::__construct($db, 'richdocuments_wopi', Wopi::class); - - $this->random = $random; - $this->logger = $logger; - $this->timeFactory = $timeFactory; - $this->appConfig = $appConfig; - } - /** * @param int $fileId * @param string $owner @@ -68,9 +55,11 @@ public function __construct(IDBConnection $db, * @param string $serverHost * @param string $guestDisplayname * @param int $templateDestination + * @param false $hideDownload + * * @return Wopi */ - public function generateFileToken($fileId, $owner, $editor, $version, $updatable, $serverHost, $guestDisplayname = null, $hideDownload = false, $direct = false, $templateId = 0, $share = null) { + public function generateFileToken($fileId, $owner, $editor, $version, $updatable, $serverHost, $guestDisplayname = null, bool $hideDownload = false, bool $direct = false, int $templateId = 0, $share = null) { $token = $this->random->generate(32, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); $wopi = Wopi::fromParams([ @@ -92,13 +81,12 @@ public function generateFileToken($fileId, $owner, $editor, $version, $updatable 'tokenType' => $guestDisplayname === null ? Wopi::TOKEN_TYPE_USER : Wopi::TOKEN_TYPE_GUEST ]); - /** @var Wopi $wopi */ $wopi = $this->insert($wopi); return $wopi; } - public function generateInitiatorToken(string $uid, string $remoteServer) { + public function generateInitiatorToken(string $uid, string $remoteServer): Wopi { $token = $this->random->generate(32, ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_DIGITS); $wopi = Wopi::fromParams([ @@ -113,21 +101,6 @@ public function generateInitiatorToken(string $uid, string $remoteServer) { return $this->insert($wopi); } - /** - * - * @deprecated - * @param $token - * @return Wopi - * @throws ExpiredTokenException - * @throws UnknownTokenException - */ - public function getPathForToken( - #[\SensitiveParameter] - $token - ): Wopi { - return $this->getWopiForToken($token); - } - /** * Given a token, validates it and * constructs and validates the path. @@ -159,7 +132,6 @@ public function getWopiForToken( throw new UnknownTokenException('Could not find token.'); } - /** @var Wopi $wopi */ $wopi = Wopi::fromRow($row); if ($wopi->getExpiry() < $this->timeFactory->getTime()) { diff --git a/lib/Events/BeforeFederationRedirectEvent.php b/lib/Events/BeforeFederationRedirectEvent.php index 626fc24eb6..d13a29d227 100644 --- a/lib/Events/BeforeFederationRedirectEvent.php +++ b/lib/Events/BeforeFederationRedirectEvent.php @@ -22,23 +22,23 @@ public function __construct($node, $relativePath, $remote) { $this->remote = $remote; } - public function getRelativePath() { + public function getRelativePath(): string { return $this->relativePath; } - public function getNode() { + public function getNode(): Node { return $this->node; } - public function getRemote() { + public function getRemote(): string { return $this->remote; } - public function setRedirectUrl($redirectUrl) { + public function setRedirectUrl($redirectUrl): void { $this->redirectUrl = $redirectUrl; } - public function getRedirectUrl() { + public function getRedirectUrl(): string|null { return $this->redirectUrl; } } diff --git a/lib/Helper.php b/lib/Helper.php index baf9b5f7eb..d9fb4a4fef 100644 --- a/lib/Helper.php +++ b/lib/Helper.php @@ -16,19 +16,18 @@ use OCP\Files\Folder; class Helper { - /** @var string|null */ - private $userId; - public function __construct($userId) { - $this->userId = $userId; - } /** * @param string $fileId - * @return array + * + * @return (null|string)[] + * * @throws \Exception + * + * @psalm-return list{string, string, string, null|string} */ - public static function parseFileId(string $fileId) { + public static function parseFileId(string $fileId): array { $arr = explode('_', $fileId); $templateId = null; if (count($arr) === 1) { @@ -56,21 +55,7 @@ public static function parseFileId(string $fileId) { ]; } - /** - * WOPI helper function to convert to ISO 8601 round-trip format. - * @param integer $time Must be seconds since unix epoch - */ - public static function toISO8601($time) { - // TODO: Be more precise and don't ignore milli, micro seconds ? - $datetime = DateTime::createFromFormat('U', $time, new DateTimeZone('UTC')); - if ($datetime) { - return $datetime->format('Y-m-d\TH:i:s.u\Z'); - } - - return false; - } - - public static function getNewFileName(Folder $folder, $filename) { + public static function getNewFileName(Folder $folder, string $filename): string|null { $fileNum = 1; while ($folder->nodeExists($filename)) { @@ -80,11 +65,4 @@ public static function getNewFileName(Folder $folder, $filename) { return $filename; } - - public function getGuestNameFromCookie() { - if ($this->userId !== null || !isset($_COOKIE['guestUser']) || $_COOKIE['guestUser'] === '') { - return null; - } - return $_COOKIE['guestUser']; - } } diff --git a/lib/Listener/AddContentSecurityPolicyListener.php b/lib/Listener/AddContentSecurityPolicyListener.php index ced4bb2664..24cd976376 100644 --- a/lib/Listener/AddContentSecurityPolicyListener.php +++ b/lib/Listener/AddContentSecurityPolicyListener.php @@ -34,12 +34,7 @@ /** @template-implements IEventListener */ class AddContentSecurityPolicyListener implements IEventListener { - public function __construct( - private IRequest $request, - private AppConfig $config, - private CapabilitiesService $capabilitiesService, - ) { - } + public function handle(Event $event): void { if (!$event instanceof AddContentSecurityPolicyEvent) { diff --git a/lib/Listener/AddFeaturePolicyListener.php b/lib/Listener/AddFeaturePolicyListener.php index 4e5c702a11..5445be3527 100644 --- a/lib/Listener/AddFeaturePolicyListener.php +++ b/lib/Listener/AddFeaturePolicyListener.php @@ -33,11 +33,7 @@ /** @template-implements IEventListener */ class AddFeaturePolicyListener implements IEventListener { - public function __construct( - private IRequest $request, - private AppConfig $config, - ) { - } + public function handle(Event $event): void { if (!$event instanceof AddFeaturePolicyEvent) { diff --git a/lib/Listener/BeforeFetchPreviewListener.php b/lib/Listener/BeforeFetchPreviewListener.php index fe8510da68..50da4dac33 100644 --- a/lib/Listener/BeforeFetchPreviewListener.php +++ b/lib/Listener/BeforeFetchPreviewListener.php @@ -45,13 +45,6 @@ class BeforeFetchPreviewListener implements IEventListener { 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 BeforePreviewFetchedEvent) { return; @@ -64,7 +57,6 @@ public function handle(Event $event): void { $storage = $event->getNode()->getStorage(); if (!$shareToken && $storage->instanceOfStorage(SharedStorage::class)) { if (method_exists(IShare::class, 'getAttributes')) { - /** @var SharedStorage $storage */ $share = $storage->getShare(); } } diff --git a/lib/Listener/BeforeTemplateRenderedListener.php b/lib/Listener/BeforeTemplateRenderedListener.php index acad7dd035..b35845bcda 100644 --- a/lib/Listener/BeforeTemplateRenderedListener.php +++ b/lib/Listener/BeforeTemplateRenderedListener.php @@ -13,10 +13,6 @@ class BeforeTemplateRenderedListener implements IEventListener { private CapabilitiesService $capabilitiesService; - public function __construct(CapabilitiesService $capabilitiesService) { - $this->capabilitiesService = $capabilitiesService; - } - public function handle(Event $event): void { if (!$event instanceof BeforeTemplateRenderedEvent) { return; diff --git a/lib/Listener/FileCreatedFromTemplateListener.php b/lib/Listener/FileCreatedFromTemplateListener.php index 731a6d5da5..84bd8ce49e 100644 --- a/lib/Listener/FileCreatedFromTemplateListener.php +++ b/lib/Listener/FileCreatedFromTemplateListener.php @@ -36,12 +36,6 @@ class FileCreatedFromTemplateListener implements IEventListener { /** @var TemplateManager */ private $templateManager; - public function __construct( - TemplateManager $templateManager - ) { - $this->templateManager = $templateManager; - } - public function handle(Event $event): void { if (!($event instanceof FileCreatedFromTemplateEvent)) { return; diff --git a/lib/Listener/LoadViewerListener.php b/lib/Listener/LoadViewerListener.php index d3758eb05f..4cd800ba55 100644 --- a/lib/Listener/LoadViewerListener.php +++ b/lib/Listener/LoadViewerListener.php @@ -44,12 +44,6 @@ class LoadViewerListener implements IEventListener { private ?string $userId = null; - public function __construct(PermissionManager $permissionManager, InitialStateService $initialStateService, ?string $userId) { - $this->permissionManager = $permissionManager; - $this->initialStateService = $initialStateService; - $this->userId = $userId; - } - public function handle(Event $event): void { if (!$event instanceof LoadViewer) { return; diff --git a/lib/Listener/ShareLinkListener.php b/lib/Listener/ShareLinkListener.php index f570ce3b22..044585ac37 100644 --- a/lib/Listener/ShareLinkListener.php +++ b/lib/Listener/ShareLinkListener.php @@ -43,17 +43,11 @@ class ShareLinkListener implements \OCP\EventDispatcher\IEventListener { /** @var InitialStateService */ private $initialStateService; - public function __construct(PermissionManager $permissionManager, InitialStateService $initialStateService) { - $this->permissionManager = $permissionManager; - $this->initialStateService = $initialStateService; - } - public function handle(Event $event): void { if (!$event instanceof ShareLinkAccessedEvent) { return; } - /** @var IShare $share */ $share = $event->getShare(); $owner = $share->getShareOwner(); diff --git a/lib/Middleware/WOPIMiddleware.php b/lib/Middleware/WOPIMiddleware.php index 9c96c116ea..e728921e17 100644 --- a/lib/Middleware/WOPIMiddleware.php +++ b/lib/Middleware/WOPIMiddleware.php @@ -47,13 +47,7 @@ use Symfony\Component\HttpFoundation\IpUtils; class WOPIMiddleware extends Middleware { - public function __construct( - private IConfig $config, - private IRequest $request, - private WopiMapper $wopiMapper, - private LoggerInterface $logger - ) { - } + public function beforeController($controller, $methodName) { parent::beforeController($controller, $methodName); @@ -95,6 +89,11 @@ public function beforeController($controller, $methodName) { } } + /** + * @return JSONResponse + * + * @psalm-return JSONResponse<403|500, array{message?: 'Error'}, array> + */ public function afterException($controller, $methodName, \Exception $exception): Response { if ($exception instanceof NotPermittedException && $controller instanceof WopiController) { return new JSONResponse([], Http::STATUS_FORBIDDEN); diff --git a/lib/Migration/InstallDefaultFonts.php b/lib/Migration/InstallDefaultFonts.php index 8b58d1f791..1fca3c73e7 100644 --- a/lib/Migration/InstallDefaultFonts.php +++ b/lib/Migration/InstallDefaultFonts.php @@ -11,6 +11,11 @@ class InstallDefaultFonts implements IRepairStep { public function __construct(private IConfig $config, private FontService $fontService) { } + /** + * @return string + * + * @psalm-return 'Install default fonts' + */ public function getName(): string { return 'Install default fonts'; } diff --git a/lib/Migration/Version2060Date20200302131958.php b/lib/Migration/Version2060Date20200302131958.php index cc0a3b3c08..b01798aef4 100644 --- a/lib/Migration/Version2060Date20200302131958.php +++ b/lib/Migration/Version2060Date20200302131958.php @@ -17,7 +17,8 @@ class Version2060Date20200302131958 extends SimpleMigrationStep { * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` * @param array $options - * @return null|ISchemaWrapper + * + * @return ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version2060Date20200302132145.php b/lib/Migration/Version2060Date20200302132145.php index 53fb5875d3..ecc48a499e 100644 --- a/lib/Migration/Version2060Date20200302132145.php +++ b/lib/Migration/Version2060Date20200302132145.php @@ -14,7 +14,8 @@ class Version2060Date20200302132145 extends SimpleMigrationStep { * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` * @param array $options - * @return null|ISchemaWrapper + * + * @return ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version30704Date20200626072306.php b/lib/Migration/Version30704Date20200626072306.php index f350229198..16db0ff32b 100644 --- a/lib/Migration/Version30704Date20200626072306.php +++ b/lib/Migration/Version30704Date20200626072306.php @@ -14,7 +14,8 @@ class Version30704Date20200626072306 extends SimpleMigrationStep { * @param IOutput $output * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` * @param array $options - * @return null|ISchemaWrapper + * + * @return ISchemaWrapper */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { /** @var ISchemaWrapper $schema */ diff --git a/lib/Migration/Version30709Date20201111104147.php b/lib/Migration/Version30709Date20201111104147.php index e8a5151b2c..6daf1a1168 100644 --- a/lib/Migration/Version30709Date20201111104147.php +++ b/lib/Migration/Version30709Date20201111104147.php @@ -11,7 +11,6 @@ class Version30709Date20201111104147 extends SimpleMigrationStep { public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) { - /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); $result = $this->ensureColumnIsNullable($schema, 'richdocuments_wopi', 'version'); diff --git a/lib/Migration/Version30717Date20210310164901.php b/lib/Migration/Version30717Date20210310164901.php index 55d7526ab6..3f1e3e3013 100644 --- a/lib/Migration/Version30717Date20210310164901.php +++ b/lib/Migration/Version30717Date20210310164901.php @@ -10,8 +10,10 @@ use OCP\Migration\SimpleMigrationStep; class Version30717Date20210310164901 extends SimpleMigrationStep { + /** + * @return ISchemaWrapper + */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { - /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); $table = $schema->getTable('richdocuments_wopi'); diff --git a/lib/Migration/Version50200Date20211220212457.php b/lib/Migration/Version50200Date20211220212457.php index 5eb408c12f..958c6c6579 100644 --- a/lib/Migration/Version50200Date20211220212457.php +++ b/lib/Migration/Version50200Date20211220212457.php @@ -13,8 +13,10 @@ * Auto-generated migration step: Please modify to your needs! */ class Version50200Date20211220212457 extends SimpleMigrationStep { + /** + * @return ISchemaWrapper + */ public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { - /** @var ISchemaWrapper $schema */ $schema = $schemaClosure(); if (!$schema->hasTable('richdocuments_template')) { diff --git a/lib/PermissionManager.php b/lib/PermissionManager.php index e503dab82e..9b70903ecc 100644 --- a/lib/PermissionManager.php +++ b/lib/PermissionManager.php @@ -41,22 +41,6 @@ class PermissionManager { private IUserSession $userSession; private ISystemTagObjectMapper $systemTagObjectMapper; - public function __construct( - 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 { if ($userId === null) { // Share links set the incognito mode so in order to still get the @@ -114,14 +98,6 @@ public function userCanEdit(?string $userId = null): bool { return false; } - public function userIsFeatureLocked(?string $userId = null): bool { - 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; diff --git a/lib/Preview/Office.php b/lib/Preview/Office.php index 351e2b008d..1c4a225330 100644 --- a/lib/Preview/Office.php +++ b/lib/Preview/Office.php @@ -30,11 +30,9 @@ use Psr\Log\LoggerInterface; abstract class Office implements IProviderV2 { - private array $capabilities = []; + - public function __construct(private IClientService $clientService, private AppConfig $config, Capabilities $capabilities, private LoggerInterface $logger) { - $this->capabilitites = $capabilities->getCapabilities()['richdocuments'] ?? []; - } + public function isAvailable(\OCP\Files\FileInfo $file): bool { if (isset($this->capabilitites['collabora']['convert-to']['available'])) { diff --git a/lib/Reference/OfficeTargetReferenceProvider.php b/lib/Reference/OfficeTargetReferenceProvider.php index 6a6d932864..1cb8408161 100644 --- a/lib/Reference/OfficeTargetReferenceProvider.php +++ b/lib/Reference/OfficeTargetReferenceProvider.php @@ -17,18 +17,6 @@ class OfficeTargetReferenceProvider extends ADiscoverableReferenceProvider { - public function __construct( - private FileTargetService $fileTargetService, - private IURLGenerator $urlGenerator, - private IL10N $l10n, - private IPreview $previewManager, - private IMimeTypeDetector $mimeTypeDetector, - private IRootFolder $rootFolder, - private LoggerInterface $logger, - private ?string $userId, - ) { - } - /** * @inheritDoc */ @@ -130,6 +118,11 @@ public function getCacheKey(string $referenceId): ?string { return $this->userId ?? ''; } + /** + * @return string + * + * @psalm-return 'office-target' + */ public function getId(): string { return 'office-target'; } @@ -138,6 +131,11 @@ public function getTitle(): string { return $this->l10n->t('Link to office document section'); } + /** + * @return int + * + * @psalm-return 90 + */ public function getOrder(): int { return 90; } diff --git a/lib/Service/CapabilitiesService.php b/lib/Service/CapabilitiesService.php index e6bfb25618..d4666e8339 100644 --- a/lib/Service/CapabilitiesService.php +++ b/lib/Service/CapabilitiesService.php @@ -50,16 +50,7 @@ class CapabilitiesService { private $capabilities; - public function __construct(IConfig $config, IClientService $clientService, ICacheFactory $cacheFactory, IAppManager $appManager, IL10N $l10n, LoggerInterface $logger) { - $this->config = $config; - $this->clientService = $clientService; - $this->cache = $cacheFactory->createDistributed('richdocuments'); - $this->appManager = $appManager; - $this->l10n = $l10n; - $this->logger = $logger; - } - - public function getCapabilities() { + public function getCapabilities(): array { if (!$this->capabilities) { $this->capabilities = $this->cache->get('capabilities'); } @@ -140,7 +131,7 @@ public function resetCache(): void { $this->cache->remove('capabilities'); } - public function getCapabilitiesEndpoint(): ?string { + public function getCapabilitiesEndpoint(): string|null|null { $remoteHost = $this->config->getAppValue('richdocuments', 'wopi_url'); if ($remoteHost === '') { return null; diff --git a/lib/Service/ConnectivityService.php b/lib/Service/ConnectivityService.php index 94e3452cdf..f1f3d712a0 100644 --- a/lib/Service/ConnectivityService.php +++ b/lib/Service/ConnectivityService.php @@ -28,13 +28,7 @@ use Symfony\Component\Console\Output\OutputInterface; class ConnectivityService { - public function __construct( - private AppConfig $appConfig, - private DiscoveryService $discoveryService, - private CapabilitiesService $capabilitiesService, - private Parser $parser, - ) { - } + /** * @throws Exception diff --git a/lib/Service/DemoService.php b/lib/Service/DemoService.php index 721189b6b2..51d0f82768 100644 --- a/lib/Service/DemoService.php +++ b/lib/Service/DemoService.php @@ -36,11 +36,6 @@ class DemoService { */ private $clientService; - public function __construct(ICache $cache, IClientService $clientService) { - $this->cache = $cache; - $this->clientService = $clientService; - } - public function fetchDemoServers(bool $refresh = false) { $servers = $this->cache->get('richdocuments-demo'); if (!$refresh) { diff --git a/lib/Service/DiscoveryService.php b/lib/Service/DiscoveryService.php index c5db331d72..e5cf59eac1 100644 --- a/lib/Service/DiscoveryService.php +++ b/lib/Service/DiscoveryService.php @@ -59,18 +59,6 @@ class DiscoveryService { private ?string $discovery = null; - public function __construct( - IClientService $clientService, - ICacheFactory $cacheFactory, - IConfig $config, - LoggerInterface $logger - ) { - $this->clientService = $clientService; - $this->cache = $cacheFactory->createDistributed('richdocuments'); - $this->config = $config; - $this->logger = $logger; - } - public function get(): ?string { if ($this->discovery) { return $this->discovery; @@ -122,7 +110,6 @@ public function resetCache(): void { * @return boolean indicating if proxy.php is in initialize or false otherwise */ private function isProxyStarting(string $url): bool { - $usesProxy = false; $proxyPos = strrpos($url, 'proxy.php'); if ($proxyPos === false) { $usesProxy = false; diff --git a/lib/Service/FederationService.php b/lib/Service/FederationService.php index 0e95b5e91d..3748252b64 100644 --- a/lib/Service/FederationService.php +++ b/lib/Service/FederationService.php @@ -64,22 +64,6 @@ class FederationService { /** @var IURLGenerator */ private $urlGenerator; - public function __construct(ICacheFactory $cacheFactory, IClientService $clientService, LoggerInterface $logger, TokenManager $tokenManager, AppConfig $appConfig, IRequest $request, IURLGenerator $urlGenerator) { - $this->cache = $cacheFactory->createDistributed('richdocuments_remote/'); - $this->clientService = $clientService; - $this->logger = $logger; - $this->tokenManager = $tokenManager; - $this->appConfig = $appConfig; - $this->request = $request; - $this->urlGenerator = $urlGenerator; - try { - $this->trustedServers = \OC::$server->get(\OCA\Federation\TrustedServers::class); - } catch (NotFoundExceptionInterface $e) { - } catch (ContainerExceptionInterface $e) { - } catch (AutoloadNotAllowedException $e) { - } - } - public function getTrustedServers(): array { if (!$this->trustedServers) { return []; @@ -122,7 +106,7 @@ public function getRemoteCollaboraURL(string $remote) { return ''; } - public function isTrustedRemote(string $domainWithPort) { + public function isTrustedRemote(string $domainWithPort): bool { if (strpos($domainWithPort, 'http://') === 0 || strpos($domainWithPort, 'https://') === 0) { $port = parse_url($domainWithPort, PHP_URL_PORT); $domainWithPort = parse_url($domainWithPort, PHP_URL_HOST) . ($port ? ':' . $port : ''); @@ -205,11 +189,13 @@ public function getRemoteFileDetails(string $remote, string $remoteToken) { /** * @param File $item - * @return string|null + * + * @return null|string + * * @throws NotFoundException * @throws InvalidPathException */ - public function getRemoteRedirectURL(File $item, ?Direct $direct = null, ?IShare $share = null) { + public function getRemoteRedirectURL(File $item, ?Direct $direct = null, ?IShare $share = null): string|null|null { if (!$item->getStorage()->instanceOfStorage(SharingExternalStorage::class)) { return null; } diff --git a/lib/Service/FileTargetService.php b/lib/Service/FileTargetService.php index 789fb66c6a..96e32b27d0 100644 --- a/lib/Service/FileTargetService.php +++ b/lib/Service/FileTargetService.php @@ -11,17 +11,6 @@ class FileTargetService { - public function __construct( - private RemoteService $remoteService, - private ICacheFactory $cacheFactory, - private IRootFolder $rootFolder, - private LoggerInterface $logger, - private IL10N $l10n, - private IURLGenerator $urlGenerator, - private ?string $userId, - ) { - } - public function getFileTargets(File $file): array { $cache = $this->cacheFactory->createDistributed('richdocuments-filetarget'); $cacheKey = $file->getId() . '_' . $file->getMTime(); @@ -78,10 +67,15 @@ public function getFileTargets(File $file): array { return $categories; } - public function getTargetPreview(File $file, string $target) { + public function getTargetPreview(File $file, string $target): string|null { return $this->remoteService->fetchTargetThumbnail($file, $target); } + /** + * @return ((int|string)|mixed)[][] + * + * @psalm-return list{0?: array{id: mixed, name: array-key, preview?: string},...} + */ private function mapTargets(string $filePath, array $targets, bool $showPreview = false): array { $result = []; foreach ($targets as $name => $identifier) { diff --git a/lib/Service/FontService.php b/lib/Service/FontService.php index 3d818ba63a..5688dc2541 100644 --- a/lib/Service/FontService.php +++ b/lib/Service/FontService.php @@ -53,16 +53,6 @@ class FontService { */ private $config; - public function __construct(IAppData $appData, - ICacheFactory $cacheFactory, - IURLGenerator $url, - IConfig $config) { - $this->appData = $appData; - $this->cache = $cacheFactory->createDistributed(Application::APPNAME); - $this->url = $url; - $this->config = $config; - } - /** * @return ISimpleFolder * @throws \OCP\Files\NotPermittedException @@ -90,8 +80,11 @@ private function getFontOverviewAppDataDir(): ISimpleFolder { /** * Get the list of available font files * - * @return array + * @return ISimpleFile[] + * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return array */ public function getFontFiles(): array { $fontDir = $this->getFontAppDataDir(); @@ -125,7 +118,10 @@ static function (ISimpleFile $f) { * Get the formatted list of available fonts * * @param array $fontFiles - * @return array + * + * @return (string|string[][])[] + * + * @psalm-return array{kind: 'fontconfiguration', server: string, fonts: array} */ public function getFontList(array $fontFiles): array { $url = $this->url; @@ -151,9 +147,11 @@ static function (ISimpleFile $f) use ($url) { * @param string $fileName * @param resource $newFileResource * - * @return array + * @return (float|int)[] * * @throws \OCP\Files\NotPermittedException + * + * @psalm-return array{size: float|int} */ public function uploadFontFile(string $fileName, $newFileResource): array { $fontDir = $this->getFontAppDataDir(); diff --git a/lib/Service/InitialStateService.php b/lib/Service/InitialStateService.php index 6726a09dcf..97a577f901 100644 --- a/lib/Service/InitialStateService.php +++ b/lib/Service/InitialStateService.php @@ -36,17 +36,6 @@ class InitialStateService { private bool $hasProvidedCapabilities = false; - public function __construct( - private IInitialState $initialState, - private AppConfig $appConfig, - private CapabilitiesService $capabilitiesService, - private IURLGenerator $urlGenerator, - private Defaults $themingDefaults, - private IConfig $config, - private ?string $userId, - ) { - } - public function provideCapabilities(): void { if ($this->hasProvidedCapabilities) { return; @@ -63,36 +52,6 @@ public function provideCapabilities(): void { $this->hasProvidedCapabilities = true; } - public function provideDocument(Wopi $wopi, array $params): void { - $this->provideCapabilities(); - - $this->initialState->provideInitialState('document', $this->prepareParams($params)); - - $this->initialState->provideInitialState('wopi', $wopi); - - $this->provideOptions(); - } - - public function prepareParams(array $params): array { - $defaults = [ - 'instanceId' => $this->config->getSystemValue('instanceid'), - 'canonical_webroot' => $this->config->getAppValue(Application::APPNAME, 'canonical_webroot', ''), - 'userId' => $this->userId, - 'token' => '', - 'token_ttl' => 0, - 'directEdit' => false, - 'directGuest' => false, - 'path' => '', - 'urlsrc' => '', - 'fileId' => '', - 'title' => '', - 'permissions' => '', - 'isPublicShare' => false, - ]; - - return array_merge($defaults, $params); - } - private function provideOptions(): void { $this->initialState->provideInitialState('theme', $this->config->getAppValue(Application::APPNAME, 'theme', 'nextcloud')); $this->initialState->provideInitialState('uiDefaults', [ diff --git a/lib/Service/RemoteService.php b/lib/Service/RemoteService.php index 4385a24964..063419d6cb 100644 --- a/lib/Service/RemoteService.php +++ b/lib/Service/RemoteService.php @@ -13,13 +13,6 @@ class RemoteService { public const REMOTE_TIMEOUT_DEFAULT = 25; - public function __construct( - private AppConfig $appConfig, - private IClientService $clientService, - private LoggerInterface $logger, - ) { - } - public function fetchTargets(File $file): array { $client = $this->clientService->newClient(); try { @@ -54,6 +47,11 @@ public function fetchTargetThumbnail(File $file, string $target): ?string { return null; } + /** + * @return (((false|null|resource|string)[]|string)[]|false|int)[] + * + * @psalm-return array{timeout: 25, multipart: list{array{name: string, contents: false|resource}, array{name: 'target', contents: null|string}}, verify?: false, headers: array{'User-Agent': 'Nextcloud Server / richdocuments', Accept: 'application/json'}} + */ private function getRequestOptionsForFile(File $file, ?string $target = null): array { $useTempFile = $file->isEncrypted() || !$file->getStorage()->isLocal(); if ($useTempFile) { diff --git a/lib/Service/UserScopeService.php b/lib/Service/UserScopeService.php index ec62770199..fa6359e5d0 100644 --- a/lib/Service/UserScopeService.php +++ b/lib/Service/UserScopeService.php @@ -29,11 +29,7 @@ use OCP\IUserSession; class UserScopeService { - public function __construct( - private IUserSession $userSession, - private IUserManager $userManager - ) { - } + /** * Set a valid user in IUserSession since lots of server logic is relying on obtaining diff --git a/lib/Settings/Admin.php b/lib/Settings/Admin.php index 74e1c424ec..9e18cd892e 100644 --- a/lib/Settings/Admin.php +++ b/lib/Settings/Admin.php @@ -45,6 +45,11 @@ public function __construct( ) { } + /** + * @return TemplateResponse + * + * @psalm-return TemplateResponse<200, array> + */ public function getForm(): TemplateResponse { $this->initialStateService->provideCapabilities(); return new TemplateResponse( @@ -76,10 +81,18 @@ public function getForm(): TemplateResponse { ); } + /** + * @return string + * + * @psalm-return 'richdocuments' + */ public function getSection() { return 'richdocuments'; } + /** + * @return int + */ public function getPriority() { return 0; } diff --git a/lib/Settings/Personal.php b/lib/Settings/Personal.php index 78e71d6c4b..1245cd0225 100644 --- a/lib/Settings/Personal.php +++ b/lib/Settings/Personal.php @@ -49,7 +49,13 @@ public function __construct(IConfig $config, CapabilitiesService $capabilitiesSe $this->userId = $userId; } - /** @psalm-suppress InvalidNullableReturnType */ + /** + * @psalm-suppress InvalidNullableReturnType + * + * @return TemplateResponse|null + * + * @psalm-return TemplateResponse<200, array>|null + */ public function getForm() { if (!$this->capabilitiesService->hasTemplateSource()) { /** @psalm-suppress NullableReturnStatement */ @@ -69,6 +75,11 @@ public function getForm() { ); } + /** + * @return null|string + * + * @psalm-return 'richdocuments'|null + */ public function getSection() { if (!$this->capabilitiesService->hasTemplateSource()) { return null; @@ -77,6 +88,9 @@ public function getSection() { return 'richdocuments'; } + /** + * @return int + */ public function getPriority() { return 0; } diff --git a/lib/Settings/Section.php b/lib/Settings/Section.php index 793caf11bc..b5cf8d521f 100644 --- a/lib/Settings/Section.php +++ b/lib/Settings/Section.php @@ -42,6 +42,11 @@ public function __construct(IURLGenerator $url, CapabilitiesService $capabilitie $this->l10n = $l10n; } + /** + * @return string + * + * @psalm-return 'richdocuments' + */ public function getID() { return 'richdocuments'; } @@ -53,6 +58,11 @@ public function getName() { return $this->capabilitites->getProductName(); } + /** + * @return int + * + * @psalm-return 75 + */ public function getPriority() { return 75; } diff --git a/lib/Template/CollaboraTemplateProvider.php b/lib/Template/CollaboraTemplateProvider.php index 3c0666cc87..fd0aa93dfb 100644 --- a/lib/Template/CollaboraTemplateProvider.php +++ b/lib/Template/CollaboraTemplateProvider.php @@ -36,21 +36,11 @@ class CollaboraTemplateProvider implements ICustomTemplateProvider { /** @var TemplateManager */ - private $templateManager; + private TemplateManager $templateManager; /** @var IURLGenerator */ - private $urlGenerator; + private IURLGenerator $urlGenerator; /** @var ITemplateManager */ - private $coreTemplateManager; - - public function __construct(TemplateManager $templateManager, IURLGenerator $urlGenerator, ITemplateManager $coreTemplateManager) { - $this->templateManager = $templateManager; - $this->urlGenerator = $urlGenerator; - $this->coreTemplateManager = $coreTemplateManager; - } - - public function getTemplateType(): string { - return CollaboraTemplateProvider::class; - } + private ITemplateManager $coreTemplateManager; /** * @return Template[] diff --git a/lib/TemplateManager.php b/lib/TemplateManager.php index 091414f4f6..a94e8bcdf0 100644 --- a/lib/TemplateManager.php +++ b/lib/TemplateManager.php @@ -106,27 +106,7 @@ class TemplateManager { 'presentation' => 'pptx', ]; - public function __construct( - ?string $userId, - IConfig $config, - IAppData $appData, - IURLGenerator $urlGenerator, - IRootFolder $rootFolder, - IL10N $l, - IDBConnection $connection, - LoggerInterface $logger - ) { - $this->userId = $userId; - $this->config = $config; - $this->rootFolder = $rootFolder; - $this->urlGenerator = $urlGenerator; - $this->db = $connection; - $this->logger = $logger; - $this->appData = $appData; - $this->l = $l; - } - - private function ensureAppDataFolders() { + private function ensureAppDataFolders(): void { /* * Init the appdata folder * We need an actual folder for the fileid and previews. @@ -144,10 +124,6 @@ private function ensureAppDataFolders() { } } - public function setUserId(?string $userId): void { - $this->userId = $userId; - } - /** * Get template ISimpleFile|Node * @@ -203,7 +179,7 @@ private function filterTemplates($templates, string|null $type = null) { }); } - public function getTemplateTypeForExtension(string $extension): ?string { + public function getTemplateTypeForExtension(string $extension): string|null|null { switch ($extension) { case 'odt': case 'docx': @@ -223,8 +199,12 @@ public function getTemplateTypeForExtension(string $extension): ?string { /** * @param null|string $type + * + * @return File[] + * + * @psalm-return array */ - public function getEmpty(string|null $type = null) { + public function getEmpty(string|null $type = null): array { $folder = $this->getEmptyTemplateDir(); $templateFiles = $folder->getDirectoryListing(); @@ -248,20 +228,6 @@ public function getEmpty(string|null $type = null) { return $this->filterTemplates($templateFiles, $type); } - /** - * Remove empty_templates in appdata and recreate it from the apps templates - */ - public function updateEmptyTemplates() { - $this->ensureAppDataFolders(); - try { - $folder = $this->getEmptyTemplateDir(); - $folder->delete(); - } catch (NotFoundException $e) { - } - $this->appData->newFolder('empty_templates'); - $this->getEmpty(); - } - /** * Get all global templates * @@ -280,11 +246,13 @@ public function getSystem(string|null $type = null) { } /** - * @return array + * @return (int|mixed|string)[][] * * @param null|string $type + * + * @psalm-return array */ - public function getSystemFormatted(string|null $type = null) { + public function getSystemFormatted(string|null $type = null): array { $empty = $this->getEmpty($type); $system = $this->getSystem($type); @@ -322,9 +290,11 @@ public function getUser(string|null $type = null) { } /** - * @return array + * @return (int|mixed|string)[][] + * + * @psalm-return array */ - public function getUserFormatted(string $type) { + public function getUserFormatted(string $type): array { if ($this->userId === null) { return []; } @@ -340,8 +310,10 @@ public function getUserFormatted(string $type) { * Get all templates * * @return File[] + * + * @psalm-return list */ - public function getAll(string $type = 'document') { + public function getAll(string $type = 'document'): array { if (!array_key_exists($type, self::$tplTypes)) { return []; } @@ -359,7 +331,7 @@ public function getAll(string $type = 'document') { })); } - public function getAllFormatted(string $type) { + public function getAllFormatted(string $type): array { if (!array_key_exists($type, self::$tplTypes)) { return []; } @@ -375,9 +347,12 @@ public function getAllFormatted(string $type) { * * @param string $templateName * @param string $templateFile - * @return array + * + * @return (int|mixed|string)[] + * + * @psalm-return array{id: int, name: string, preview: string, type: mixed, delete: string, extension: string} */ - public function add($templateName, $templateFile) { + public function add($templateName, $templateFile): array { $folder = $this->getSystemTemplateDir(); try { @@ -394,10 +369,12 @@ public function add($templateName, $templateFile) { * Delete a template to the global template folder * * @param int $fileId - * @return boolean + * + * @return true + * * @throws NotFoundException */ - public function delete($fileId) { + public function delete($fileId): bool { $files = $this->getSystemTemplateDir()->getDirectoryListing(); foreach ($files as $file) { if ($file->getId() === $fileId) { @@ -412,9 +389,11 @@ public function delete($fileId) { /** * Flip $tplTypes to retrieve types by mime * - * @return array + * @return (int|string)[] + * + * @psalm-return array */ - private function flipTypes() { + private function flipTypes(): array { $result = []; foreach ($this::$tplTypes as $type => $mime) { $result = array_merge($result, array_fill_keys($mime, $type)); @@ -478,9 +457,12 @@ private function getEmptyTemplateDir() { * Format template file for json return object * * @param File $template - * @return array + * + * @return (int|string)[] + * + * @psalm-return array{id: int, name: string, preview: string, type: array-key, delete: string, extension: string} */ - public function formatNodeReturn(File $template) { + public function formatNodeReturn(File $template): array { $ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml'; $documentType = $this->flipTypes()[$template->getMimeType()]; return [ @@ -493,11 +475,10 @@ public function formatNodeReturn(File $template) { ]; } - public function isTemplate(int $fileId) { + public function isTemplate(int $fileId): bool { $empty = $this->getEmpty(); $system = $this->getSystem(); $user = $this->getUser(); - /** @var File[] $all */ $all = array_merge($empty, $system, $user); foreach ($all as $template) { @@ -509,7 +490,12 @@ public function isTemplate(int $fileId) { return false; } - public function formatEmpty(File $template) { + /** + * @return (int|string)[] + * + * @psalm-return array{id: int, name: string, type: array-key, extension: string} + */ + public function formatEmpty(File $template): array { $ooxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml'; $documentType = $this->flipTypes()[$template->getMimeType()]; return [ @@ -523,7 +509,7 @@ public function formatEmpty(File $template) { /** * @param null|string $type */ - public function isValidTemplateMime(string $mime, string|null $type = null) { + public function isValidTemplateMime(string $mime, string|null $type = null): bool { if ($type === null) { $allMimes = array_merge(self::$tplTypes['document'], self::$tplTypes['spreadsheet'], self::$tplTypes['presentation'], self::$tplTypes['drawing']); if (!in_array($mime, $allMimes)) { @@ -584,7 +570,6 @@ public function setTemplateSource(int $fileId, int $templateId): void { * @psalm-return File|array|null */ public function getTemplateSource(int $fileId): array|File|null { - $templateId = 0; try { $query = $this->db->getQueryBuilder(); $query->select('templateid') diff --git a/lib/TokenManager.php b/lib/TokenManager.php index 6a3fd37f71..21df9b2694 100644 --- a/lib/TokenManager.php +++ b/lib/TokenManager.php @@ -57,37 +57,11 @@ class TokenManager { private $wopiMapper; /** @var IL10N */ private $trans; - /** @var CapabilitiesService */ - private $capabilitiesService; - /** @var Helper */ - private $helper; + + /** @var PermissionManager */ private $permissionManager; - public function __construct( - IRootFolder $rootFolder, - IManager $shareManager, - IURLGenerator $urlGenerator, - Parser $wopiParser, - CapabilitiesService $capabilitiesService, - $UserId, - WopiMapper $wopiMapper, - IL10N $trans, - Helper $helper, - PermissionManager $permissionManager - ) { - $this->rootFolder = $rootFolder; - $this->shareManager = $shareManager; - $this->urlGenerator = $urlGenerator; - $this->wopiParser = $wopiParser; - $this->capabilitiesService = $capabilitiesService; - $this->trans = $trans; - $this->userId = $UserId; - $this->wopiMapper = $wopiMapper; - $this->helper = $helper; - $this->permissionManager = $permissionManager; - } - /** * @throws Exception */ @@ -134,7 +108,6 @@ public function generateWopiToken(string $fileId, ?string $shareToken = null, ?s if (!method_exists(IShare::class, 'getAttributes')) { break; } - /** @var SharedStorage $storage */ $share = $storage->getShare(); $attributes = $share->getAttributes(); if ($attributes !== null && $attributes->getAttribute('permissions', 'download') === false) { @@ -227,15 +200,6 @@ public function upgradeToRemoteToken(Wopi $wopi, Wopi $remoteWopi, string $share return $wopi; } - public function upgradeFromDirectInitiator(Direct $direct, Wopi $wopi) { - $wopi->setTokenType(Wopi::TOKEN_TYPE_REMOTE_GUEST); - $wopi->setEditorUid(null); - $wopi->setRemoteServer($direct->getInitiatorHost()); - $wopi->setRemoteServerToken($direct->getInitiatorToken()); - $this->wopiMapper->update($wopi); - return $wopi; - } - public function generateWopiTokenForTemplate(File $templateFile, ?string $userId, int $targetFileId, bool $direct = false): Wopi { $owneruid = $userId; $editoruid = $userId; @@ -282,7 +246,7 @@ public function extendWithInitiatorUserToken(Wopi $wopi, string $initiatorUserHo return $wopi; } - public function prepareGuestName(?string $guestName = null) { + public function prepareGuestName(?string $guestName = null): string { if (empty($guestName)) { return $this->trans->t('Anonymous guest'); } diff --git a/lib/WOPI/Parser.php b/lib/WOPI/Parser.php index 141f74364f..0dc965b762 100644 --- a/lib/WOPI/Parser.php +++ b/lib/WOPI/Parser.php @@ -26,11 +26,7 @@ use Psr\Log\LoggerInterface; class Parser { - public function __construct( - private DiscoveryService $discoveryService, - private LoggerInterface $logger - ) { - } + /** * @throws Exception @@ -42,11 +38,15 @@ public function getUrlSrcValue(string $appName): string { // https://github.com/nextcloud/richdocuments/issues/3262 $result = str_replace('\.', '.', $result); - return (string)$result; + return $result; } /** * @throws Exception + * + * @return string[] + * + * @psalm-return array{urlsrc: string, action: string} */ private function getUrlSrc(string $mimetype): array { $discovery = $this->discoveryService->get();