From 13897b2941a61068b2754864e16456fb4804a205 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 23 Nov 2022 12:29:32 +0100 Subject: [PATCH] Implement direct editing API Signed-off-by: Raul --- lib/AppInfo/Application.php | 3 + lib/Db/Wopi.php | 1 + lib/DirectEditing/DirectEditor.php | 198 ++++++++++++++++++ lib/DirectEditing/GraphicsCreator.php | 54 +++++ lib/DirectEditing/PresentationCreator.php | 58 +++++ lib/DirectEditing/SpreadsheetCreator.php | 58 +++++ lib/DirectEditing/TextCreator.php | 59 ++++++ .../RegisterDirectEditorEventListener.php | 48 +++++ 8 files changed, 479 insertions(+) create mode 100644 lib/DirectEditing/DirectEditor.php create mode 100644 lib/DirectEditing/GraphicsCreator.php create mode 100644 lib/DirectEditing/PresentationCreator.php create mode 100644 lib/DirectEditing/SpreadsheetCreator.php create mode 100644 lib/DirectEditing/TextCreator.php create mode 100644 lib/Listener/RegisterDirectEditorEventListener.php diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 0b0bf37e14..976a392572 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -31,6 +31,7 @@ use OCA\Richdocuments\Listener\CSPListener; use OCA\Richdocuments\Listener\LoadViewerListener; use OCA\Richdocuments\Listener\ShareLinkListener; +use OCA\Richdocuments\Listener\RegisterDirectEditorEventListener; use OCA\Richdocuments\Middleware\WOPIMiddleware; use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener; use OCA\Richdocuments\PermissionManager; @@ -47,6 +48,7 @@ use OCP\AppFramework\Bootstrap\IBootContext; use OCP\AppFramework\Bootstrap\IBootstrap; use OCP\AppFramework\Bootstrap\IRegistrationContext; +use OCP\DirectEditing\RegisterDirectEditorEvent; use OCP\Files\Template\FileCreatedFromTemplateEvent; use OCP\Files\Template\ITemplateManager; use OCP\Files\Template\TemplateFileCreator; @@ -73,6 +75,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(LoadViewer::class, LoadViewerListener::class); $context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkListener::class); $context->registerEventListener(BeforePreviewFetchedEvent::class, BeforeFetchPreviewListener::class); + $context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorEventListener::class); } public function boot(IBootContext $context): void { diff --git a/lib/Db/Wopi.php b/lib/Db/Wopi.php index c90f199436..2fb1514d95 100644 --- a/lib/Db/Wopi.php +++ b/lib/Db/Wopi.php @@ -186,6 +186,7 @@ public function getDirect() { return (bool)$this->direct; } + #[\ReturnTypeWillChange] public function jsonSerialize() { $properties = get_object_vars($this); $reflection = new \ReflectionClass($this); diff --git a/lib/DirectEditing/DirectEditor.php b/lib/DirectEditing/DirectEditor.php new file mode 100644 index 0000000000..2d8b4d8ac6 --- /dev/null +++ b/lib/DirectEditing/DirectEditor.php @@ -0,0 +1,198 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Richdocuments\DirectEditing; + +use OCA\Richdocuments\AppConfig; +use OCA\Richdocuments\AppInfo\Application; +use OCA\Richdocuments\Capabilities; +use OCA\Richdocuments\Controller\DocumentTrait; +use OCA\Richdocuments\Service\InitialStateService; +use OCA\Richdocuments\TokenManager; +use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\DirectEditing\IEditor; +use OCP\DirectEditing\IToken; +use OCP\Files\InvalidPathException; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\IConfig; +use OCP\IInitialStateService; +use OCP\IL10N; +use Psr\Log\LoggerInterface; + +class DirectEditor implements IEditor { + use DocumentTrait; + + /** @var IL10N */ + private $l10n; + + /** @var IInitialStateService */ + private $initialStateService; + + /** @var string[] */ + private $mimetypes; + + /** @var TokenManager */ + private $tokenManager; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var IConfig */ + private $config; + + /** @var AppConfig */ + private $appConfig; + + /** @var LoggerInterface */ + private $logger; + + + public function __construct( + IL10N $l10n, + InitialStateService $initialStateService, + Capabilities $capabilities, + TokenManager $tokenManager, + IConfig $config, + AppConfig $appConfig, + IRootFolder $rootFolder, + LoggerInterface $logger + ) { + $this->l10n = $l10n; + $this->initialStateService = $initialStateService; + $this->mimetypes = $capabilities->getCapabilities()[Application::APPNAME]['mimetypes']; + $this->tokenManager = $tokenManager; + $this->config = $config; + $this->appConfig = $appConfig; + $this->rootFolder = $rootFolder; + $this->logger = $logger; + } + + /** + * Return a unique identifier for the editor + * + * e.g. richdocuments + * + * @return string + */ + public function getId(): string { + return Application::APPNAME; + } + + /** + * Return a readable name for the editor + * + * e.g. Collabora Online + * + * @return string + */ + public function getName(): string { + return $this->l10n->t('Nextcloud Office'); + } + + /** + * A list of mimetypes that should open the editor by default + * + * @return array + */ + public function getMimetypes(): array { + return $this->mimetypes; + } + + /** + * A list of mimetypes that can be opened in the editor optionally + * + * @return array + */ + public function getMimetypesOptional(): array { + return []; + } + + /** + * Return a list of file creation options to be presented to the user + * + * @return array of ACreateFromTemplate|ACreateEmpty + */ + public function getCreators(): array { + return [ + new GraphicsCreator($this->l10n), + new PresentationCreator($this->l10n, $this->config), + new SpreadsheetCreator($this->l10n, $this->config), + new TextCreator($this->l10n, $this->config), + ]; + } + + /** + * Return if the view is able to securely view a file without downloading it to the browser + * + * @return bool + */ + public function isSecure(): bool { + return true; + } + + /** + * Return a template response for displaying the editor + * + * open can only be called once when the client requests the editor with a one-time-use token + * For handling editing and later requests, editors need to impelement their own token handling and take care of invalidation + * + * This behavior is similar to the current direct editing implementation in collabora where we generate a one-time token and switch over to the regular wopi token for the actual editing/saving process + * + * @param IToken $token + * @return Response + */ + public function open(IToken $token): Response { + $token->useTokenScope(); + try { + $folder = $this->rootFolder->getUserFolder($token->getUser()); + $item = $token->getFile(); + + [$urlSrc, $token, $wopi] = $this->tokenManager->getToken($item->getId(), null, $token->getUser(), true); + + $params = [ + 'permissions' => $item->getPermissions(), + 'title' => $item->getName(), + 'fileId' => $wopi->getFileid() . '_' . $this->config->getSystemValue('instanceid'), + 'token' => $wopi->getToken(), + 'token_ttl' => $wopi->getExpiry(), + 'urlsrc' => $urlSrc, + 'path' => $folder->getRelativePath($item->getPath()), + 'instanceId' => $this->config->getSystemValue('instanceid'), + 'canonical_webroot' => $this->appConfig->getAppValue('canonical_webroot'), + 'direct' => true, + ]; + + $this->initialStateService->provideDocument($wopi); + $response = new TemplateResponse('richdocuments', 'documents', $params, 'base'); + $this->applyPolicies($response); + return $response; + } catch (InvalidPathException|NotFoundException|NotPermittedException $e) { + $this->logger->error($e->getMessage()); + } + return new NotFoundResponse(); + } +} diff --git a/lib/DirectEditing/GraphicsCreator.php b/lib/DirectEditing/GraphicsCreator.php new file mode 100644 index 0000000000..cdc18af27e --- /dev/null +++ b/lib/DirectEditing/GraphicsCreator.php @@ -0,0 +1,54 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Richdocuments\DirectEditing; + +use OCP\DirectEditing\ACreateEmpty; +use OCP\IL10N; + +class GraphicsCreator extends ACreateEmpty { + /** + * @var IL10N + */ + private $l10n; + + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + public function getId(): string { + return 'richdocuments_graphics'; + } + + public function getName(): string { + return $this->l10n->t('diagram'); + } + + public function getExtension(): string { + return 'odg'; + } + + public function getMimetype(): string { + return 'application/vnd.oasis.opendocument.graphics'; + } +} diff --git a/lib/DirectEditing/PresentationCreator.php b/lib/DirectEditing/PresentationCreator.php new file mode 100644 index 0000000000..352683aec1 --- /dev/null +++ b/lib/DirectEditing/PresentationCreator.php @@ -0,0 +1,58 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Richdocuments\DirectEditing; + +use OCA\Richdocuments\AppInfo\Application; +use OCP\DirectEditing\ACreateEmpty; +use OCP\IConfig; +use OCP\IL10N; + +class PresentationCreator extends ACreateEmpty { + /** @var IL10N */ + private $l10n; + /** @var IConfig */ + private $config; + + public function __construct(IL10N $l10n, IConfig $config) { + $this->l10n = $l10n; + $this->config = $config; + } + + public function getId(): string { + return 'richdocuments_presentation'; + } + + public function getName(): string { + return $this->l10n->t('presentation'); + } + + public function getExtension(): string { + $useOoxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml'; + return $useOoxml ? 'pptx' : 'odp'; + } + + public function getMimetype(): string { + return 'application/vnd.oasis.opendocument.presentation'; + } +} diff --git a/lib/DirectEditing/SpreadsheetCreator.php b/lib/DirectEditing/SpreadsheetCreator.php new file mode 100644 index 0000000000..516acef932 --- /dev/null +++ b/lib/DirectEditing/SpreadsheetCreator.php @@ -0,0 +1,58 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Richdocuments\DirectEditing; + +use OCA\Richdocuments\AppInfo\Application; +use OCP\DirectEditing\ACreateEmpty; +use OCP\IConfig; +use OCP\IL10N; + +class SpreadsheetCreator extends ACreateEmpty { + /** @var IL10N */ + private $l10n; + /** @var IConfig */ + private $config; + + public function __construct(IL10N $l10n, IConfig $config) { + $this->l10n = $l10n; + $this->config = $config; + } + + public function getId(): string { + return 'richdocuments_spreadsheet'; + } + + public function getName(): string { + return $this->l10n->t('spreadsheet'); + } + + public function getExtension(): string { + $useOoxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml'; + return $useOoxml ? 'xlsx' : 'ods'; + } + + public function getMimetype(): string { + return 'application/vnd.oasis.opendocument.spreadsheet'; + } +} diff --git a/lib/DirectEditing/TextCreator.php b/lib/DirectEditing/TextCreator.php new file mode 100644 index 0000000000..eb788e3c98 --- /dev/null +++ b/lib/DirectEditing/TextCreator.php @@ -0,0 +1,59 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + + +namespace OCA\Richdocuments\DirectEditing; + +use OCA\Richdocuments\AppInfo\Application; +use OCP\DirectEditing\ACreateEmpty; +use OCP\IConfig; +use OCP\IL10N; + +class TextCreator extends ACreateEmpty { + /** @var IL10N */ + private $l10n; + /** @var IConfig */ + private $config; + + public function __construct(IL10N $l10n, IConfig $config) { + $this->l10n = $l10n; + $this->config = $config; + } + + public function getId(): string { + return 'richdocuments_text'; + } + + public function getName(): string { + return $this->l10n->t('document'); + } + + public function getExtension(): string { + $useOoxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml'; + return $useOoxml ? 'docx' : 'odt'; + } + + public function getMimetype(): string { + return 'application/vnd.oasis.opendocument.text'; + } +} diff --git a/lib/Listener/RegisterDirectEditorEventListener.php b/lib/Listener/RegisterDirectEditorEventListener.php new file mode 100644 index 0000000000..8690bfe8b0 --- /dev/null +++ b/lib/Listener/RegisterDirectEditorEventListener.php @@ -0,0 +1,48 @@ + + * + * @author Raul Ferreira Fuentes + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Richdocuments\Listener; + +use OCA\Richdocuments\DirectEditing\DirectEditor; +use OCP\DirectEditing\RegisterDirectEditorEvent; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; + +/** @implements IEventListener */ +class RegisterDirectEditorEventListener implements IEventListener { + /** @var DirectEditor */ + protected $editor; + + public function __construct(DirectEditor $editor) { + $this->editor = $editor; + } + + public function handle(Event $event): void { + if (!$event instanceof RegisterDirectEditorEvent) { + return; + } + $event->register($this->editor); + } +}