From e7bfb8e0e3f99c1e1e9b4e7ebd4fab6505c8eea2 Mon Sep 17 00:00:00 2001 From: Rashesh Padia Date: Tue, 25 Feb 2025 16:25:34 +0530 Subject: [PATCH] feat: wip: send templates with SharedSetting --- appinfo/routes.php | 10 +++++ lib/Controller/SettingsController.php | 37 +++++++++++++++- lib/Controller/WopiController.php | 14 +++--- lib/Service/SettingsService.php | 63 +++++++++++++++++++++++---- 4 files changed, 107 insertions(+), 17 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index ba06093291..8414e9ed92 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -46,6 +46,16 @@ 'name' => '.+', ], ], + [ + 'name' => 'settings#getTemplateFile', + 'url' => 'settings/{identifier}/{category}/{name}', + 'verb' => 'GET', + 'requirements' => [ + 'identifier' => '[a-zA-Z0-9_\-]+', + 'category' => '[a-zA-Z0-9_\-]+', + 'name' => '.+', + ], + ], ['name' => 'settings#generateIframeToken', 'url' => 'settings/generateToken/{type}', 'verb' => 'GET'], // Direct Editing: Webview diff --git a/lib/Controller/SettingsController.php b/lib/Controller/SettingsController.php index bd3a22346d..11a46dd08e 100644 --- a/lib/Controller/SettingsController.php +++ b/lib/Controller/SettingsController.php @@ -15,6 +15,7 @@ use OCA\Richdocuments\Service\FontService; use OCA\Richdocuments\Service\SettingsService; use OCA\Richdocuments\UploadException; +use OCP\AppFramework\Http\DataDownloadResponse; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http; @@ -22,6 +23,7 @@ use OCP\AppFramework\Http\DataDisplayResponse; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; +use OCP\Files\File; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; @@ -33,6 +35,7 @@ use OCP\Util; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Output\NullOutput; +use OCA\Richdocuments\TemplateManager; class SettingsController extends Controller { // TODO adapt overview generation if we add more font mimetypes @@ -61,6 +64,7 @@ public function __construct( private IURLGenerator $urlGenerator, private WopiMapper $wopiMapper, private ?string $userId, + private TemplateManager $templateManager, ) { parent::__construct($appName, $request); } @@ -504,7 +508,38 @@ public function getSettingsFile(string $type, string $token, string $category, s return new DataDisplayResponse('Something went wrong', 500); } } - + + /** + * @param string $identifier + * @param string $category + * @param string $name + * + * @return DataDisplayResponse + * + * @NoAdminRequired + * @PublicPage + * @NoCSRFRequired + **/ + public function getTemplateFile(string $identifier, string $category, string $name): DataDisplayResponse { + try { + $file = $this->templateManager->get((int)$identifier); + } catch (NotFoundException) { + return new DataDisplayResponse('', Http::STATUS_NOT_FOUND); + } + + if ($file instanceof File || $file instanceof ISimpleFile) { + $response = new DataDisplayResponse( + $file->getContent(), + Http::STATUS_OK, + [ + 'Content-Type' => $file->getMimeType() ?: 'application/octet-stream' + ] + ); + return $response; + } + return new DataDisplayResponse('', Http::STATUS_NOT_FOUND); + } + /** * @param string $key diff --git a/lib/Controller/WopiController.php b/lib/Controller/WopiController.php index 4399b221e5..d8195e9fa6 100644 --- a/lib/Controller/WopiController.php +++ b/lib/Controller/WopiController.php @@ -405,7 +405,7 @@ public function getSettings(string $type, string $access_token): JSONResponse { if (empty($type)) { return new JSONResponse(['error' => 'Invalid type parameter'], Http::STATUS_BAD_REQUEST); } - + try { $wopi = $this->wopiMapper->getWopiForToken($access_token); if ($wopi->getTokenType() !== Wopi::TOKEN_TYPE_SETTING_AUTH) { @@ -415,7 +415,7 @@ public function getSettings(string $type, string $access_token): JSONResponse { $isPublic = empty($wopi->getEditorUid()); $guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8); $userId = !$isPublic ? $wopi->getEditorUid() : $guestUserId; - + $userConfig = $this->settingsService->generateSettingsConfig($type, $userId); return new JSONResponse($userConfig, Http::STATUS_OK); } catch (UnknownTokenException|ExpiredTokenException $e) { @@ -426,7 +426,7 @@ public function getSettings(string $type, string $access_token): JSONResponse { return new JSONResponse(['error' => 'Internal Server Error'], Http::STATUS_INTERNAL_SERVER_ERROR); } } - + #[NoAdminRequired] #[NoCSRFRequired] #[PublicPage] @@ -444,11 +444,11 @@ public function uploadSettingsFile(string $fileId, string $access_token): JSONRe $fileContent = stream_get_contents($content); fclose($content); - + // Use the fileId as a file path URL (e.g., "/settings/systemconfig/wordbook/en_US%20(1).dic") $settingsUrl = new SettingsUrl($fileId); $result = $this->settingsService->uploadFile($settingsUrl, $fileContent, $userId); - + return new JSONResponse([ 'status' => 'success', 'filename' => $settingsUrl->getFileName(), @@ -501,7 +501,7 @@ public function deleteSettingsFile(string $fileId, string $access_token): JSONRe } } - + /** * Given an access token and a fileId, replaces the files with the request body. * Expects a valid token in access_token parameter. @@ -984,7 +984,7 @@ 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(); } - + private function generateSettingToken(string $userId): string { return $this->settingsService->generateIframeToken('user', $userId)['token']; } diff --git a/lib/Service/SettingsService.php b/lib/Service/SettingsService.php index 0c944d7e31..393e76f21d 100644 --- a/lib/Service/SettingsService.php +++ b/lib/Service/SettingsService.php @@ -10,6 +10,7 @@ use OCA\Richdocuments\AppInfo\Application; use OCA\Richdocuments\Db\WopiMapper; +use OCA\Richdocuments\TemplateManager; use OCA\Richdocuments\WOPI\SettingsUrl; use OCP\Files\Folder; use OCP\Files\IAppData; @@ -44,6 +45,7 @@ public function __construct( private WopiMapper $wopiMapper, private IGroupManager $groupManager, private IRootFolder $rootFolder, + private TemplateManager $templateManager, ) { // Create a distributed cache for caching file lists $this->cache = $cacheFactory->createDistributed(Application::APPNAME); @@ -141,12 +143,12 @@ public function generateIframeToken(string $type, string $userId): array { if ($type === 'admin' && !$this->groupManager->isAdmin($userId)) { throw new NotPermittedException('Permission denied'); } - + $serverHost = $this->urlGenerator->getAbsoluteURL('/'); $version = $this->capabilitiesService->getProductVersion(); - + $wopi = $this->wopiMapper->generateUserSettingsToken(-1, $userId, $version, $serverHost); - + return [ 'token' => $wopi->getToken(), 'token_ttl' => $wopi->getExpiry(), @@ -164,7 +166,48 @@ public function generateIframeToken(string $type, string $userId): array { public function getFolderEtag($type) : string { return $this->getTypeFolder($type)->getEtag(); } - + + public const SUPPORTED_PRESENTATION_MIMES = [ + 'application/vnd.oasis.opendocument.presentation', + 'application/vnd.oasis.opendocument.presentation-template', + ]; + + /** + * + * get presentation templates + * @return array + */ + public function getPresentationTemplates(string $type): array { + $templates = array_filter( + $this->templateManager->getSystem('presentation'), + function ($template) { + return in_array( + $template->getMimeType(), + self::SUPPORTED_PRESENTATION_MIMES, + true + ); + } + ); + + $result = []; + + foreach ($templates as $template) { + $uri = $this->urlGenerator->linkToRouteAbsolute( + 'richdocuments.settings.getTemplateFile', + [ + 'identifier' => $template->getId(), + 'category' => 'template', + 'name' => $template->getName(), + ] + ); + $result[] = [ + 'uri' => $uri, + 'stamp' => $template->getEtag(), + ]; + } + return $result; + } + /** * generate setting config * @@ -189,6 +232,8 @@ public function generateSettingsConfig(string $type, string $userId): array { $config[$category] = $files; } + $config['template'] = self::getPresentationTemplates($type); + return $config; } @@ -269,14 +314,14 @@ private function refreshFolderEtag($type) { * @return string */ private function generateFileUri(string $type, string $category, string $fileName, string $userId): string { - + // Passing userId is dangerous so we have to trim from url... if (strpos($type, '/') !== false) { $type = explode('/', $type)[0]; } $token = $this->generateIframeToken($type, $userId); - + return $this->urlGenerator->linkToRouteAbsolute( 'richdocuments.settings.getSettingsFile', [ @@ -302,13 +347,13 @@ public function getSettingsFile(string $type, string $category, string $name): I } catch (NotFoundException $e) { throw new NotFoundException("Type folder '{$type}' not found."); } - + try { $categoryFolder = $baseFolder->getFolder($category); } catch (NotFoundException $e) { throw new NotFoundException("Category folder '{$category}' not found in type '{$type}'."); } - + try { return $categoryFolder->getFile($name); } catch (NotFoundException $e) { @@ -337,7 +382,7 @@ public function deleteSettingsFile(string $type, string $category, string $name, throw new NotFoundException("User folder '{$userId}' not found."); } } - + try { $categoryFolder = $baseFolder->getFolder($category); } catch (NotFoundException $e) {