diff options
author | Julius Härtl <jus@bitgrid.net> | 2021-12-29 15:25:47 +0300 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2022-01-14 18:07:37 +0300 |
commit | c3237172e0004a1ac51a522fd54de9ba6cec2cf5 (patch) | |
tree | 053deace604b14398d811347033b4c2635e9af37 | |
parent | d0b6ef883ecb3bbfc7278aa9faad8a20d11f110f (diff) |
Fix public link file creation
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r-- | appinfo/routes.php | 3 | ||||
-rw-r--r-- | lib/Controller/DocumentAPIController.php | 139 | ||||
-rw-r--r-- | lib/Controller/DocumentController.php | 121 | ||||
-rw-r--r-- | lib/TemplateManager.php | 18 | ||||
-rw-r--r-- | src/services/api.js | 38 | ||||
-rw-r--r-- | src/view/NewFileMenu.js | 33 | ||||
-rw-r--r-- | tests/psalm-baseline.xml | 14 |
7 files changed, 230 insertions, 136 deletions
diff --git a/appinfo/routes.php b/appinfo/routes.php index 877616dd..57e4edc8 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -34,7 +34,6 @@ return [ ['name' => 'document#createFromTemplate', 'url' => 'indexTemplate', 'verb' => 'GET'], ['name' => 'document#publicPage', 'url' => '/public', 'verb' => 'GET'], - ['name' => 'document#create', 'url' => 'ajax/documents/create', 'verb' => 'POST'], // external api access ['name' => 'document#extAppGetData', 'url' => '/ajax/extapp/data/{fileId}', 'verb' => 'POST'], @@ -67,6 +66,8 @@ return [ ['name' => 'templates#delete', 'url' => '/template/{fileId}', 'verb' => 'DELETE'], ], 'ocs' => [ + ['name' => 'documentAPI#create', 'url' => '/api/v1/file', 'verb' => 'POST'], + ['name' => 'OCS#createDirect', 'url' => '/api/v1/document', 'verb' => 'POST'], ['name' => 'OCS#createPublic', 'url' => '/api/v1/share', 'verb' => 'POST'], ['name' => 'OCS#createPublicFromInitiator', 'url' => '/api/v1/direct/share/initiator', 'verb' => 'POST'], diff --git a/lib/Controller/DocumentAPIController.php b/lib/Controller/DocumentAPIController.php new file mode 100644 index 00000000..86cb2081 --- /dev/null +++ b/lib/Controller/DocumentAPIController.php @@ -0,0 +1,139 @@ +<?php +/* + * @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +declare(strict_types=1); + +namespace OCA\Richdocuments\Controller; + +use Exception; +use OCA\Richdocuments\AppInfo\Application; +use OCA\Richdocuments\Helper; +use OCA\Richdocuments\TemplateManager; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\JSONResponse; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\IL10N; +use OCP\IRequest; +use OCP\Share\IManager; +use Psr\Log\LoggerInterface; +use Throwable; + +class DocumentAPIController extends \OCP\AppFramework\OCSController { + + private $rootFolder; + private $shareManager; + private $templateManager; + private $l10n; + private $logger; + private $userId; + + public function __construct(IRequest $request, IRootFolder $rootFolder, IManager $shareManager, TemplateManager $templateManager, IL10N $l10n, LoggerInterface $logger, $userId) { + parent::__construct(Application::APPNAME, $request); + $this->rootFolder = $rootFolder; + $this->shareManager = $shareManager; + $this->templateManager = $templateManager; + $this->l10n = $l10n; + $this->logger = $logger; + $this->userId = $userId; + } + + /** + * @NoAdminRequired + * @PublicPage + */ + public function create(string $mimeType, string $fileName, string $directoryPath = '/', string $shareToken = null): JSONResponse { + try { + if ($shareToken !== null) { + $share = $this->shareManager->getShareByToken($shareToken); + } + + $rootFolder = $shareToken !== null ? $share->getNode() : $this->rootFolder->getUserFolder($this->userId); + $folder = $rootFolder->get($directoryPath); + + if (!($folder instanceof Folder)) { + throw new Exception('Node is not a folder'); + } + } catch (Throwable $e) { + $this->logger->error('Failed to create document', ['exception' => $e]); + return new JSONResponse([ + 'status' => 'error', + 'message' => $this->l10n->t('Cannot create document') + ], Http::STATUS_BAD_REQUEST); + } + + $basename = $this->l10n->t('New Document.odt'); + switch ($mimeType) { + case 'application/vnd.oasis.opendocument.spreadsheet': + $basename = $this->l10n->t('New Spreadsheet.ods'); + break; + case 'application/vnd.oasis.opendocument.presentation': + $basename = $this->l10n->t('New Presentation.odp'); + break; + case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': + $basename = $this->l10n->t('New Document.docx'); + break; + case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': + $basename = $this->l10n->t('New Spreadsheet.xlsx'); + break; + case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': + $basename = $this->l10n->t('New Presentation.pptx'); + break; + default: + break; + } + + if (!$fileName) { + $fileName = Helper::getNewFileName($folder, $basename); + } + + if ($folder->nodeExists($fileName)) { + return new JSONResponse([ + 'status' => 'error', + 'message' => $this->l10n->t('File already exists') + ], Http::STATUS_BAD_REQUEST); + } + + try { + $file = $folder->newFile($fileName); + $templateType = $this->templateManager->getTemplateTypeForExtension(pathinfo($fileName, PATHINFO_EXTENSION)); + $empty = $this->templateManager->getEmpty($templateType); + $templateFile = array_shift($empty); + $file->putContent($this->templateManager->getEmptyFileContent($file->getExtension())); + if ($this->templateManager->isSupportedTemplateSource($templateFile->getExtension())) { + // Only use TemplateSource if supported filetype + $this->templateManager->setTemplateSource($file->getId(), $templateFile->getId()); + } + } catch (Exception $e) { + $this->logger->error('Failed to create file from template', ['exception' => $e]); + return new JSONResponse([ + 'status' => 'error', + 'message' => $this->l10n->t('Not allowed to create document') + ], Http::STATUS_BAD_REQUEST); + } + return new JSONResponse([ + 'status' => 'success', + 'data' => \OCA\Files\Helper::formatFileInfo($file->getFileInfo()) + ]); + } +} diff --git a/lib/Controller/DocumentController.php b/lib/Controller/DocumentController.php index 8e1932a2..23e1bde5 100644 --- a/lib/Controller/DocumentController.php +++ b/lib/Controller/DocumentController.php @@ -11,19 +11,16 @@ namespace OCA\Richdocuments\Controller; -use OCA\Richdocuments\AppInfo\Application; use OCA\Richdocuments\Events\BeforeFederationRedirectEvent; use OCA\Richdocuments\Service\FederationService; use OCA\Richdocuments\Service\InitialStateService; +use OCA\Richdocuments\TemplateManager; use OCA\Richdocuments\TokenManager; use \OCP\AppFramework\Controller; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; use OCP\Constants; use OCP\Files\File; use OCP\Files\Folder; -use OCP\Files\GenericFileException; use OCP\Files\IRootFolder; use OCP\Files\Node; use OCP\Files\NotFoundException; @@ -36,18 +33,14 @@ use \OCP\AppFramework\Http\ContentSecurityPolicy; use \OCP\AppFramework\Http\FeaturePolicy; use \OCP\AppFramework\Http\TemplateResponse; use \OCA\Richdocuments\AppConfig; -use \OCA\Richdocuments\Helper; use OCP\ISession; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; -use OC\Files\Type\TemplateManager; class DocumentController extends Controller { /** @var string */ private $uid; - /** @var IL10N */ - private $l10n; /** @var IConfig */ private $config; /** @var AppConfig */ @@ -62,36 +55,30 @@ class DocumentController extends Controller { private $session; /** @var IRootFolder */ private $rootFolder; - /** @var \OCA\Richdocuments\TemplateManager */ + /** @var TemplateManager */ private $templateManager; /** @var FederationService */ private $federationService; /** @var InitialStateService */ private $initialState; - const ODT_TEMPLATE_PATH = '/emptyTemplates/odttemplate.odt'; - - public function __construct( $appName, IRequest $request, IConfig $config, AppConfig $appConfig, - IL10N $l10n, IManager $shareManager, TokenManager $tokenManager, IRootFolder $rootFolder, ISession $session, $UserId, ILogger $logger, - \OCA\Richdocuments\TemplateManager $templateManager, + TemplateManager $templateManager, FederationService $federationService, - Helper $helper, InitialStateService $initialState ) { parent::__construct($appName, $request); $this->uid = $UserId; - $this->l10n = $l10n; $this->config = $config; $this->appConfig = $appConfig; $this->shareManager = $shareManager; @@ -501,108 +488,6 @@ class DocumentController extends Controller { return new TemplateResponse('core', '403', [], 'guest'); } - /** - * @NoAdminRequired - * - * @param string $mimetype - * @param string $filename - * @param string $dir - * @return JSONResponse - * @throws NotPermittedException - * @throws GenericFileException - */ - public function create($mimetype, - $filename, - $dir = '/'){ - - $root = $this->rootFolder->getUserFolder($this->uid); - try { - /** @var Folder $folder */ - $folder = $root->get($dir); - } catch (NotFoundException $e) { - return new JSONResponse([ - 'status' => 'error', - 'message' => $this->l10n->t('Cannot create document') - ], Http::STATUS_BAD_REQUEST); - } - - if (!($folder instanceof Folder)) { - return new JSONResponse([ - 'status' => 'error', - 'message' => $this->l10n->t('Cannot create document') - ], Http::STATUS_BAD_REQUEST); - } - - $basename = $this->l10n->t('New Document.odt'); - switch ($mimetype) { - case 'application/vnd.oasis.opendocument.spreadsheet': - $basename = $this->l10n->t('New Spreadsheet.ods'); - break; - case 'application/vnd.oasis.opendocument.presentation': - $basename = $this->l10n->t('New Presentation.odp'); - break; - case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': - $basename = $this->l10n->t('New Document.docx'); - break; - case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': - $basename = $this->l10n->t('New Spreadsheet.xlsx'); - break; - case 'application/vnd.openxmlformats-officedocument.presentationml.presentation': - $basename = $this->l10n->t('New Presentation.pptx'); - break; - default: - // to be safe - $mimetype = 'application/vnd.oasis.opendocument.text'; - break; - } - - if (!$filename){ - $filename = Helper::getNewFileName($folder, $basename); - } - - if ($folder->nodeExists($filename)) { - return new JSONResponse([ - 'status' => 'error', - 'message' => $this->l10n->t('Document already exists') - ], Http::STATUS_BAD_REQUEST); - } - - try { - $file = $folder->newFile($filename); - } catch (NotPermittedException $e) { - return new JSONResponse([ - 'status' => 'error', - 'message' => $this->l10n->t('Not allowed to create document') - ], Http::STATUS_BAD_REQUEST); - } - - $content = ''; - if (class_exists(TemplateManager::class)){ - $manager = \OC_Helper::getFileTemplateManager(); - $content = $manager->getTemplate($mimetype); - } - - if (!$content){ - // FIXME: see if this is used, - $content = file_get_contents(dirname(dirname(__DIR__)) . self::ODT_TEMPLATE_PATH); - } - - if ($content) { - $file->putContent($content); - - return new JSONResponse([ - 'status' => 'success', - 'data' => \OCA\Files\Helper::formatFileInfo($file->getFileInfo()) - ]); - } - - - return new JSONResponse([ - 'status' => 'error', - 'message' => $this->l10n->t('Cannot create document') - ]); - } - private function renderErrorPage($message) { $params = [ 'errors' => [['error' => $message]] diff --git a/lib/TemplateManager.php b/lib/TemplateManager.php index 1f4d1499..803f8be9 100644 --- a/lib/TemplateManager.php +++ b/lib/TemplateManager.php @@ -197,6 +197,24 @@ class TemplateManager { }); } + public function getTemplateTypeForExtension(string $extension): ?string { + switch ($extension) { + case 'odt': + case 'docx': + return 'document'; + case 'ods': + case 'xlsx': + return 'spreadsheet'; + case 'odp': + case 'pptx': + return 'presentation'; + case 'odg': + return 'drawing'; + } + + return null; + } + public function getEmpty($type = null) { $folder = $this->getEmptyTemplateDir(); diff --git a/src/services/api.js b/src/services/api.js new file mode 100644 index 00000000..cd5d98ad --- /dev/null +++ b/src/services/api.js @@ -0,0 +1,38 @@ +/* + * @copyright Copyright (c) 2021 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +import axios from '@nextcloud/axios' +import { generateOcsUrl } from '@nextcloud/router' + +export const createEmptyFile = async(mimeType, fileName) => { + const shareToken = document.getElementById('sharingToken')?.value + const directoryPath = document.getElementById('dir')?.value + + const response = await axios.post(generateOcsUrl('apps/richdocuments/api/v1', 2) + 'file', { + mimeType, + fileName, + directoryPath, + shareToken, + }) + + return response.data +} diff --git a/src/view/NewFileMenu.js b/src/view/NewFileMenu.js index d713864a..5402d1ff 100644 --- a/src/view/NewFileMenu.js +++ b/src/view/NewFileMenu.js @@ -21,6 +21,9 @@ */ import Types from '../helpers/types' +import { createEmptyFile } from '../services/api' + +const isPublic = window.document.getElementById('isPublic')?.value === '1' /** @type OC.Plugin */ const NewFileMenu = { @@ -37,7 +40,7 @@ const NewFileMenu = { iconClass: 'icon-filetype-document', fileType: 'x-office-document', actionHandler(filename) { - if (OC.getCapabilities().richdocuments.templates) { + if (!isPublic && OC.getCapabilities().richdocuments.templates) { self._openTemplatePicker('document', document.mime, filename) } else { self._createDocument(document.mime, filename) @@ -52,7 +55,7 @@ const NewFileMenu = { iconClass: 'icon-filetype-spreadsheet', fileType: 'x-office-spreadsheet', actionHandler(filename) { - if (OC.getCapabilities().richdocuments.templates) { + if (!isPublic && OC.getCapabilities().richdocuments.templates) { self._openTemplatePicker('spreadsheet', spreadsheet.mime, filename) } else { self._createDocument(spreadsheet.mime, filename) @@ -67,7 +70,7 @@ const NewFileMenu = { iconClass: 'icon-filetype-presentation', fileType: 'x-office-presentation', actionHandler(filename) { - if (OC.getCapabilities().richdocuments.templates) { + if (!isPublic && OC.getCapabilities().richdocuments.templates) { self._openTemplatePicker('presentation', presentation.mime, filename) } else { self._createDocument(presentation.mime, filename) @@ -80,17 +83,21 @@ const NewFileMenu = { OCA.Files.Files.isFileNameValid(filename) filename = FileList.getUniqueName(filename) - $.post( - OC.generateUrl('apps/richdocuments/ajax/documents/create'), - { mimetype, filename, dir: document.getElementById('dir').value }, - function(response) { - if (response && response.status === 'success') { - FileList.add(response.data, { animate: true, scrollTo: true }) - } else { - OC.dialogs.alert(response.data.message, t('core', 'Could not create file')) - } + createEmptyFile(mimetype, filename).then((response) => { + if (response && response.status === 'success') { + FileList.add(response.data, { animate: true, scrollTo: true }) + const fileModel = FileList.getModelForFile(filename) + const fileAction = OCA.Files.fileActions.getDefaultFileAction(fileModel.get('mimetype'), 'file', OC.PERMISSION_ALL) + fileAction.action(filename, { + $file: null, + dir: FileList.getCurrentDirectory(), + FileList, + fileActions: FileList.fileActions, + }) + } else { + OC.dialogs.alert(response.data.message, t('core', 'Could not create file')) } - ) + }) }, _createDocumentFromTemplate(templateId, mimetype, filename) { diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml index d705ac7a..9e44e9d3 100644 --- a/tests/psalm-baseline.xml +++ b/tests/psalm-baseline.xml @@ -59,6 +59,14 @@ <code>IRootFolder</code> </MissingDependency> </file> + <file src="lib/Controller/DocumentAPIController.php"> + <MissingDependency occurrences="1"> + <code>IRootFolder</code> + </MissingDependency> + <UndefinedClass occurrences="1"> + <code>\OCA\Files\Helper</code> + </UndefinedClass> + </file> <file src="lib/Controller/DocumentController.php"> <InvalidScalarArgument occurrences="5"> <code>$fileId</code> @@ -67,8 +75,7 @@ <code>$item->getId()</code> <code>$node->getId()</code> </InvalidScalarArgument> - <MissingDependency occurrences="8"> - <code>$this->rootFolder</code> + <MissingDependency occurrences="7"> <code>$this->rootFolder</code> <code>$this->rootFolder</code> <code>$this->rootFolder</code> @@ -80,9 +87,8 @@ <RedundantCondition occurrences="1"> <code>$app !== ''</code> </RedundantCondition> - <UndefinedClass occurrences="2"> + <UndefinedClass occurrences="1"> <code>\OCA\Files_Sharing\External\Storage</code> - <code>\OC_Helper</code> </UndefinedClass> <UndefinedInterfaceMethod occurrences="1"> <code>getRemote</code> |