From eeda34b28ab2fd9777165c14071b0cd4d73be8b5 Mon Sep 17 00:00:00 2001 From: Raul Date: Wed, 24 Aug 2022 10:02:45 +0200 Subject: [PATCH] Implement direct editing API Signed-off-by: Raul --- lib/AppInfo/Application.php | 3 + lib/DirectEditing/DirectEditor.php | 149 ++++++++++++++++++ lib/DirectEditing/GraphicsCreator.php | 55 +++++++ lib/DirectEditing/PresentationCreator.php | 55 +++++++ lib/DirectEditing/SpreadsheetCreator.php | 55 +++++++ lib/DirectEditing/TextCreator.php | 56 +++++++ .../RegisterDirectEditorEventListener.php | 48 ++++++ 7 files changed, 421 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 8263e3a1ba..f5b642d4ed 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -30,6 +30,7 @@ use OCA\Richdocuments\Listener\CSPListener; use OCA\Richdocuments\Listener\LoadViewerListener; use OCA\Richdocuments\Listener\ShareLinkListener; +use OCA\Richdocuments\Listeners\RegisterDirectEditorEventListener; use OCA\Richdocuments\Middleware\WOPIMiddleware; use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener; use OCA\Richdocuments\Preview\MSExcel; @@ -45,6 +46,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; @@ -69,6 +71,7 @@ public function register(IRegistrationContext $context): void { $context->registerEventListener(AddContentSecurityPolicyEvent::class, CSPListener::class); $context->registerEventListener(LoadViewer::class, LoadViewerListener::class); $context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkListener::class); + $context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorEventListener::class); } public function boot(IBootContext $context): void { diff --git a/lib/DirectEditing/DirectEditor.php b/lib/DirectEditing/DirectEditor.php new file mode 100644 index 0000000000..0839158083 --- /dev/null +++ b/lib/DirectEditing/DirectEditor.php @@ -0,0 +1,149 @@ + + * + * @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 OCA\Richdocuments\Capabilities; +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\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\IInitialStateService; +use OCP\IL10N; + +class DirectEditor implements IEditor { + + /** @var IL10N */ + private $l10n; + + /** @var IInitialStateService */ + private $initialStateService; + + /** @var string[] */ + private $mimetypes; + + + public function __construct(IL10N $l10n, IInitialStateService $initialStateService, Capabilities $capabilities) { + $this->l10n = $l10n; + $this->initialStateService = $initialStateService; + $this->mimetypes = $capabilities->getCapabilities()[Application::APPNAME]['mimetypes']; + } + + /** + * 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), + new SpreadsheetCreator($this->l10n), + new TextCreator($this->l10n), + ]; + } + + /** + * 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 { + $session = $this->apiService->create($token->getFile()->getId()); + $this->initialStateService->provideInitialState('text', 'file', [ + 'fileId' => $token->getFile()->getId(), + 'mimetype' => $token->getFile()->getMimeType(), + 'content' => $token->getFile()->getContent(), + 'session' => \json_encode($session->getData()) + ]); + $this->initialStateService->provideInitialState('text', 'directEditingToken', $token->getToken()); + return new TemplateResponse(Application::APPNAME, 'main', [], 'base'); + } catch (InvalidPathException $e) { + } catch (NotFoundException $e) { + } catch (NotPermittedException $e) { + } + return new NotFoundResponse(); + } +} diff --git a/lib/DirectEditing/GraphicsCreator.php b/lib/DirectEditing/GraphicsCreator.php new file mode 100644 index 0000000000..479126faf3 --- /dev/null +++ b/lib/DirectEditing/GraphicsCreator.php @@ -0,0 +1,55 @@ + + * + * @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..cd11887562 --- /dev/null +++ b/lib/DirectEditing/PresentationCreator.php @@ -0,0 +1,55 @@ + + * + * @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 PresentationCreator extends ACreateEmpty { + + /** + * @var IL10N + */ + private $l10n; + + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + public function getId(): string { + return 'richdocuments_presentation'; + } + + public function getName(): string { + return $this->l10n->t('presentation'); + } + + public function getExtension(): string { + return '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..8684cd50a8 --- /dev/null +++ b/lib/DirectEditing/SpreadsheetCreator.php @@ -0,0 +1,55 @@ + + * + * @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 SpreadsheetCreator extends ACreateEmpty { + + /** + * @var IL10N + */ + private $l10n; + + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + public function getId(): string { + return 'richdocuments_spreadsheet'; + } + + public function getName(): string { + return $this->l10n->t('spreadsheet'); + } + + public function getExtension(): string { + return '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..e569af494a --- /dev/null +++ b/lib/DirectEditing/TextCreator.php @@ -0,0 +1,56 @@ + + * + * @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 TextCreator extends ACreateEmpty { + + /** + * @var IL10N + */ + private $l10n; + + public function __construct(IL10N $l10n) { + $this->l10n = $l10n; + } + + public function getId(): string { + return 'richdocuments_text'; + } + + public function getName(): string { + return $this->l10n->t('document'); + } + + public function getExtension(): string { + return '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..fa73fe1ec4 --- /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\Listeners; + +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); + } +}