diff options
author | Roeland Jago Douma <rullzer@users.noreply.github.com> | 2019-11-28 08:49:57 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-11-28 08:49:57 +0100 |
commit | 4173d9d74993bfae8d9c7dce5b983cad1b133751 (patch) | |
tree | bc234c62e53fe95d7ae4ec0d90507526f1a7f2a8 /lib | |
parent | 62dc32019146bdc186e110bde23dd210633acbe7 (diff) | |
parent | bde624b07423de4a6b9e3aaae6371cd4f886c2de (diff) | |
download | nextcloud-server-4173d9d74993bfae8d9c7dce5b983cad1b133751.tar.gz nextcloud-server-4173d9d74993bfae8d9c7dce5b983cad1b133751.zip |
Merge pull request #17625 from nextcloud/enh/noid/direct-editing
Direct editing API to allow file editing using a one-time token
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 10 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 10 | ||||
-rw-r--r-- | lib/private/DirectEditing/Manager.php | 236 | ||||
-rw-r--r-- | lib/private/DirectEditing/Token.php | 76 | ||||
-rw-r--r-- | lib/private/Server.php | 2 | ||||
-rw-r--r-- | lib/public/DirectEditing/ACreateEmpty.php | 79 | ||||
-rw-r--r-- | lib/public/DirectEditing/ACreateFromTemplate.php | 39 | ||||
-rw-r--r-- | lib/public/DirectEditing/ATemplate.php | 71 | ||||
-rw-r--r-- | lib/public/DirectEditing/IEditor.php | 99 | ||||
-rw-r--r-- | lib/public/DirectEditing/IManager.php | 88 | ||||
-rw-r--r-- | lib/public/DirectEditing/IToken.php | 77 | ||||
-rw-r--r-- | lib/public/DirectEditing/RegisterDirectEditorEvent.php | 57 |
12 files changed, 844 insertions, 0 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 3d73c7b690d..900a03a0f35 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -170,6 +170,13 @@ return array( 'OCP\\Diagnostics\\IEventLogger' => $baseDir . '/lib/public/Diagnostics/IEventLogger.php', 'OCP\\Diagnostics\\IQuery' => $baseDir . '/lib/public/Diagnostics/IQuery.php', 'OCP\\Diagnostics\\IQueryLogger' => $baseDir . '/lib/public/Diagnostics/IQueryLogger.php', + 'OCP\\DirectEditing\\ACreateEmpty' => $baseDir . '/lib/public/DirectEditing/ACreateEmpty.php', + 'OCP\\DirectEditing\\ACreateFromTemplate' => $baseDir . '/lib/public/DirectEditing/ACreateFromTemplate.php', + 'OCP\\DirectEditing\\ATemplate' => $baseDir . '/lib/public/DirectEditing/ATemplate.php', + 'OCP\\DirectEditing\\IEditor' => $baseDir . '/lib/public/DirectEditing/IEditor.php', + 'OCP\\DirectEditing\\IManager' => $baseDir . '/lib/public/DirectEditing/IManager.php', + 'OCP\\DirectEditing\\IToken' => $baseDir . '/lib/public/DirectEditing/IToken.php', + 'OCP\\DirectEditing\\RegisterDirectEditorEvent' => $baseDir . '/lib/public/DirectEditing/RegisterDirectEditorEvent.php', 'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => $baseDir . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php', 'OCP\\Encryption\\IEncryptionModule' => $baseDir . '/lib/public/Encryption/IEncryptionModule.php', 'OCP\\Encryption\\IFile' => $baseDir . '/lib/public/Encryption/IFile.php', @@ -789,6 +796,7 @@ return array( 'OC\\Core\\Migrations\\Version16000Date20190428150708' => $baseDir . '/core/Migrations/Version16000Date20190428150708.php', 'OC\\Core\\Migrations\\Version17000Date20190514105811' => $baseDir . '/core/Migrations/Version17000Date20190514105811.php', 'OC\\Core\\Migrations\\Version18000Date20190920085628' => $baseDir . '/core/Migrations/Version18000Date20190920085628.php', + 'OC\\Core\\Migrations\\Version18000Date20191014105105' => $baseDir . '/core/Migrations/Version18000Date20191014105105.php', 'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => $baseDir . '/core/Notification/RemoveLinkSharesNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', @@ -840,6 +848,8 @@ return array( 'OC\\Diagnostics\\EventLogger' => $baseDir . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => $baseDir . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => $baseDir . '/lib/private/Diagnostics/QueryLogger.php', + 'OC\\DirectEditing\\Manager' => $baseDir . '/lib/private/DirectEditing/Manager.php', + 'OC\\DirectEditing\\Token' => $baseDir . '/lib/private/DirectEditing/Token.php', 'OC\\Encryption\\DecryptAll' => $baseDir . '/lib/private/Encryption/DecryptAll.php', 'OC\\Encryption\\EncryptionWrapper' => $baseDir . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => $baseDir . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index e00acfbfdb5..84532fabf5f 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -199,6 +199,13 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Diagnostics\\IEventLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IEventLogger.php', 'OCP\\Diagnostics\\IQuery' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQuery.php', 'OCP\\Diagnostics\\IQueryLogger' => __DIR__ . '/../../..' . '/lib/public/Diagnostics/IQueryLogger.php', + 'OCP\\DirectEditing\\ACreateEmpty' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ACreateEmpty.php', + 'OCP\\DirectEditing\\ACreateFromTemplate' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ACreateFromTemplate.php', + 'OCP\\DirectEditing\\ATemplate' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/ATemplate.php', + 'OCP\\DirectEditing\\IEditor' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IEditor.php', + 'OCP\\DirectEditing\\IManager' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IManager.php', + 'OCP\\DirectEditing\\IToken' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/IToken.php', + 'OCP\\DirectEditing\\RegisterDirectEditorEvent' => __DIR__ . '/../../..' . '/lib/public/DirectEditing/RegisterDirectEditorEvent.php', 'OCP\\Encryption\\Exceptions\\GenericEncryptionException' => __DIR__ . '/../../..' . '/lib/public/Encryption/Exceptions/GenericEncryptionException.php', 'OCP\\Encryption\\IEncryptionModule' => __DIR__ . '/../../..' . '/lib/public/Encryption/IEncryptionModule.php', 'OCP\\Encryption\\IFile' => __DIR__ . '/../../..' . '/lib/public/Encryption/IFile.php', @@ -818,6 +825,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Core\\Migrations\\Version16000Date20190428150708' => __DIR__ . '/../../..' . '/core/Migrations/Version16000Date20190428150708.php', 'OC\\Core\\Migrations\\Version17000Date20190514105811' => __DIR__ . '/../../..' . '/core/Migrations/Version17000Date20190514105811.php', 'OC\\Core\\Migrations\\Version18000Date20190920085628' => __DIR__ . '/../../..' . '/core/Migrations/Version18000Date20190920085628.php', + 'OC\\Core\\Migrations\\Version18000Date20191014105105' => __DIR__ . '/../../..' . '/core/Migrations/Version18000Date20191014105105.php', 'OC\\Core\\Notification\\RemoveLinkSharesNotifier' => __DIR__ . '/../../..' . '/core/Notification/RemoveLinkSharesNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', @@ -869,6 +877,8 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Diagnostics\\EventLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/EventLogger.php', 'OC\\Diagnostics\\Query' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/Query.php', 'OC\\Diagnostics\\QueryLogger' => __DIR__ . '/../../..' . '/lib/private/Diagnostics/QueryLogger.php', + 'OC\\DirectEditing\\Manager' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Manager.php', + 'OC\\DirectEditing\\Token' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Token.php', 'OC\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/lib/private/Encryption/DecryptAll.php', 'OC\\Encryption\\EncryptionWrapper' => __DIR__ . '/../../..' . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php new file mode 100644 index 00000000000..26adad9e572 --- /dev/null +++ b/lib/private/DirectEditing/Manager.php @@ -0,0 +1,236 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OC\DirectEditing; + +use Doctrine\DBAL\FetchMode; +use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DirectEditing\ACreateFromTemplate; +use OCP\DirectEditing\IEditor; +use \OCP\DirectEditing\IManager; +use OCP\DirectEditing\IToken; +use OCP\DirectEditing\RegisterDirectEditorEvent; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\IDBConnection; +use OCP\IUserSession; +use OCP\Security\ISecureRandom; +use OCP\Share\IShare; + +class Manager implements IManager { + + private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ; + + public const TABLE_TOKENS = 'direct_edit'; + + /** @var IEditor[] */ + private $editors = []; + + /** @var IDBConnection */ + private $connection; + /** + * @var ISecureRandom + */ + private $random; + private $userId; + private $rootFolder; + + public function __construct( + ISecureRandom $random, + IDBConnection $connection, + IUserSession $userSession, + IRootFolder $rootFolder + ) { + $this->random = $random; + $this->connection = $connection; + $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null; + $this->rootFolder = $rootFolder; + } + + public function registerDirectEditor(IEditor $directEditor): void { + $this->editors[$directEditor->getId()] = $directEditor; + } + + public function getEditors(): array { + return $this->editors; + } + + public function getTemplates(string $editor, string $type): array { + if (!array_key_exists($editor, $this->editors)) { + throw new \RuntimeException('No matching editor found'); + } + $templates = []; + foreach ($this->editors[$editor]->getCreators() as $creator) { + if ($creator instanceof ACreateFromTemplate && $creator->getId() === $type) { + $templates = $creator->getTemplates(); + } + } + $return = []; + $return['templates'] = $templates; + return $return; + } + + public function create(string $path, string $editorId, string $creatorId, $templateId = null): string { + $userFolder = $this->rootFolder->getUserFolder($this->userId); + $file = $userFolder->newFile($path); + $editor = $this->getEditor($editorId); + $creators = $editor->getCreators(); + foreach ($creators as $creator) { + if ($creator->getId() === $creatorId) { + $creator->create($file, $creatorId, $templateId); + return $this->createToken($editorId, $file); + } + } + throw new \RuntimeException('No creator found'); + } + + public function open(int $fileId, string $editorId = null): string { + $file = $this->rootFolder->getUserFolder($this->userId)->getById($fileId); + if (count($file) === 0 || !($file[0] instanceof File) || $file === null) { + throw new NotFoundException(); + } + /** @var File $file */ + $file = $file[0]; + + if ($editorId === null) { + $editorId = $this->findEditorForFile($file); + } + + return $this->createToken($editorId, $file); + } + + private function findEditorForFile(File $file) { + foreach ($this->editors as $editor) { + if (in_array($file->getMimeType(), $editor->getMimetypes())) { + return $editor->getId(); + } + } + throw new \RuntimeException('No default editor found for files mimetype'); + } + + public function edit(string $token): Response { + try { + /** @var IEditor $editor */ + $tokenObject = $this->getToken($token); + if ($tokenObject->hasBeenAccessed()) { + throw new \RuntimeException('Token has already been used and can only be used for followup requests'); + } + $editor = $this->getEditor($tokenObject->getEditor()); + $this->accessToken($token); + + } catch (\Throwable $throwable) { + $this->invalidateToken($token); + return new NotFoundResponse(); + } + return $editor->open($tokenObject); + } + + public function editSecure(File $file, string $editorId): TemplateResponse { + // TODO: Implementation in follow up + } + + private function getEditor($editorId): IEditor { + if (!array_key_exists($editorId, $this->editors)) { + throw new \RuntimeException('No editor found'); + } + return $this->editors[$editorId]; + } + + public function getToken(string $token): IToken { + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from(self::TABLE_TOKENS) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) { + return new Token($this, $tokenRow); + } + throw new \RuntimeException('Failed to validate the token'); + } + + public function cleanup(): int { + $query = $this->connection->getQueryBuilder(); + $query->delete(self::TABLE_TOKENS) + ->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME))); + return $query->execute(); + } + + public function refreshToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->update(self::TABLE_TOKENS) + ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + + public function invalidateToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->delete(self::TABLE_TOKENS) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + public function accessToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->update(self::TABLE_TOKENS) + ->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)) + ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + public function invokeTokenScope($userId): void { + \OC_User::setIncognitoMode(true); + \OC_User::setUserId($userId); + } + + public function createToken($editorId, File $file, IShare $share = null): string { + $token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE); + $query = $this->connection->getQueryBuilder(); + $query->insert(self::TABLE_TOKENS) + ->values([ + 'token' => $query->createNamedParameter($token), + 'editor_id' => $query->createNamedParameter($editorId), + 'file_id' => $query->createNamedParameter($file->getId()), + 'user_id' => $query->createNamedParameter($this->userId), + 'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null), + 'timestamp' => $query->createNamedParameter(time()) + ]); + $query->execute(); + return $token; + } + + public function getFileForToken($userId, $fileId) { + $userFolder = $this->rootFolder->getUserFolder($userId); + return $userFolder->getById($fileId)[0]; + } + +} diff --git a/lib/private/DirectEditing/Token.php b/lib/private/DirectEditing/Token.php new file mode 100644 index 00000000000..946699900b7 --- /dev/null +++ b/lib/private/DirectEditing/Token.php @@ -0,0 +1,76 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OC\DirectEditing; + + +use OCP\DirectEditing\IToken; +use OCP\Files\File; + +class Token implements IToken { + + /** @var Manager */ + private $manager; + private $data; + + public function __construct(Manager $manager, $data) { + $this->manager = $manager; + $this->data = $data; + } + + public function extend(): void { + $this->manager->refreshToken($this->data['token']); + } + + public function invalidate(): void { + $this->manager->invalidateToken($this->data['token']); + } + + public function getFile(): File { + if ($this->data['share_id'] !== null) { + return $this->manager->getShareForToken($this->data['share_id']); + } + return $this->manager->getFileForToken($this->data['user_id'], $this->data['file_id']); + } + + public function getToken(): string { + return $this->data['token']; + } + + public function useTokenScope(): void { + $this->manager->invokeTokenScope($this->data['user_id']); + } + + public function hasBeenAccessed(): bool { + return $this->data['accessed'] === '1'; + } + + public function getEditor(): string { + return $this->data['editor_id']; + } + + public function getUser(): string { + return $this->data['user_id']; + } + +} diff --git a/lib/private/Server.php b/lib/private/Server.php index b4af17ba288..9fb197fcb18 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -199,6 +199,8 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class); $this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class); + $this->registerAlias(\OCP\DirectEditing\IManager::class, \OC\DirectEditing\Manager::class); + $this->registerAlias(IActionFactory::class, ActionFactory::class); diff --git a/lib/public/DirectEditing/ACreateEmpty.php b/lib/public/DirectEditing/ACreateEmpty.php new file mode 100644 index 00000000000..ab7f7fd3ae8 --- /dev/null +++ b/lib/public/DirectEditing/ACreateEmpty.php @@ -0,0 +1,79 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + + +use OCP\Files\File; + +/** + * @since 18.0.0 + */ +abstract class ACreateEmpty { + + /** + * Unique id for the creator to filter templates + * + * e.g. document/spreadsheet/presentation + * + * @since 18.0.0 + * @return string + */ + abstract public function getId(): string; + + /** + * Descriptive name for the create action + * + * e.g Create a new document + * + * @since 18.0.0 + * @return string + */ + abstract public function getName(): string; + + /** + * Default file extension for the new file + * + * @since 18.0.0 + * @return string + */ + abstract public function getExtension(): string; + + /** + * Mimetype of the resulting created file + * + * @since 18.0.0 + * @return string + */ + abstract public function getMimetype(): string; + + /** + * Add content when creating empty files + * + * @since 18.0.0 + * @param File $file + */ + public function create(File $file, string $creatorId = null, string $templateId = null): void { + + } +} diff --git a/lib/public/DirectEditing/ACreateFromTemplate.php b/lib/public/DirectEditing/ACreateFromTemplate.php new file mode 100644 index 00000000000..89420a63743 --- /dev/null +++ b/lib/public/DirectEditing/ACreateFromTemplate.php @@ -0,0 +1,39 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + +/** + * @since 18.0.0 + */ +abstract class ACreateFromTemplate extends ACreateEmpty { + + /** + * List of available templates for the create from template action + * + * @since 18.0.0 + * @return ATemplate[] + */ + abstract public function getTemplates(): array; + +} diff --git a/lib/public/DirectEditing/ATemplate.php b/lib/public/DirectEditing/ATemplate.php new file mode 100644 index 00000000000..734317eebef --- /dev/null +++ b/lib/public/DirectEditing/ATemplate.php @@ -0,0 +1,71 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + +use JsonSerializable; + +/** + * Class ATemplate + * + * @package OCP\DirectEditing + * @since 18.0.0 + */ +abstract class ATemplate implements JsonSerializable { + + /** + * Return a unique id so the app can identify the template + * + * @since 18.0.0 + * @return string + */ + abstract public function getId(): string; + + /** + * Return a title that is displayed to the user + * + * @since 18.0.0 + * @return string + */ + abstract public function getTitle(): string; + + /** + * Return a link to the template preview image + * + * @since 18.0.0 + * @return string + */ + abstract public function getPreview(): string; + + /** + * @since 18.0.0 + * @return array|mixed + */ + public function jsonSerialize() { + return [ + 'id' => $this->getId(), + 'title' => $this->getTitle(), + 'preview' => $this->getPreview(), + ]; + } +} diff --git a/lib/public/DirectEditing/IEditor.php b/lib/public/DirectEditing/IEditor.php new file mode 100644 index 00000000000..a2bc0d74255 --- /dev/null +++ b/lib/public/DirectEditing/IEditor.php @@ -0,0 +1,99 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + + +use OCP\AppFramework\Http\Response; + +/** + * @since 18.0.0 + */ +interface IEditor { + + /** + * Return a unique identifier for the editor + * + * e.g. richdocuments + * + * @since 18.0.0 + * @return string + */ + public function getId(): string; + + /** + * Return a readable name for the editor + * + * e.g. Collabora Online + * + * @since 18.0.0 + * @return string + */ + public function getName(): string; + + /** + * A list of mimetypes that should open the editor by default + * + * @since 18.0.0 + * @return string[] + */ + public function getMimetypes(): array; + + /** + * A list of mimetypes that can be opened in the editor optionally + * + * @since 18.0.0 + * @return string[] + */ + public function getMimetypesOptional(): array; + + /** + * Return a list of file creation options to be presented to the user + * + * @since 18.0.0 + * @return ACreateFromTemplate[]|ACreateEmpty[] + */ + public function getCreators(): array; + + /** + * Return if the view is able to securely view a file without downloading it to the browser + * + * @since 18.0.0 + * @return bool + */ + public function isSecure(): bool; + + /** + * 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 + * + * @since 18.0.0 + * @return Response + */ + public function open(IToken $token): Response; +} diff --git a/lib/public/DirectEditing/IManager.php b/lib/public/DirectEditing/IManager.php new file mode 100644 index 00000000000..07b9c5a1e4e --- /dev/null +++ b/lib/public/DirectEditing/IManager.php @@ -0,0 +1,88 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + +use OCP\AppFramework\Http\Response; +use OCP\Files\NotPermittedException; +use RuntimeException; + +/** + * Interface IManager + * + * @package OCP\DirectEditing + * @since 18.0.0 + */ +interface IManager { + + /** + * Register a new editor + * + * @since 18.0.0 + * @param IEditor $directEditor + */ + public function registerDirectEditor(IEditor $directEditor): void; + + /** + * Open the editing page for a provided token + * + * @since 18.0.0 + * @param string $token + * @return Response + */ + public function edit(string $token): Response; + + /** + * Create a new token based on the file path and editor details + * + * @since 18.0.0 + * @param string $path + * @param string $editorId + * @param string $creatorId + * @param null $templateId + * @return string + * @throws NotPermittedException + * @throws RuntimeException + */ + public function create(string $path, string $editorId, string $creatorId, $templateId = null): string; + + /** + * Get the token details for a given token + * + * @since 18.0.0 + * @param string $token + * @return IToken + */ + public function getToken(string $token): IToken; + + /** + * Cleanup expired tokens + * + * @since 18.0.0 + * @return int number of deleted tokens + */ + public function cleanup(): int; + +} + diff --git a/lib/public/DirectEditing/IToken.php b/lib/public/DirectEditing/IToken.php new file mode 100644 index 00000000000..a730493d76e --- /dev/null +++ b/lib/public/DirectEditing/IToken.php @@ -0,0 +1,77 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + + +use OCP\Files\File; + +/** + * @since 18.0.0 + */ +interface IToken { + + /** + * Extend the token validity time + * + * @since 18.0.0 + */ + public function extend(): void; + + /** + * Invalidate the token + * + * @since 18.0.0 + */ + public function invalidate(): void; + + /** + * Check if the token has already been used + * + * @since 18.0.0 + * @return bool + */ + public function hasBeenAccessed(): bool; + + /** + * Change to the user scope of the token + * + * @since 18.0.0 + */ + public function useTokenScope(): void; + + /** + * Get the file that is related to the token + * + * @since 18.0.0 + * @return File + */ + public function getFile(): File; + + /** + * @since 18.0.0 + * @return string + */ + public function getEditor(): string; + +} diff --git a/lib/public/DirectEditing/RegisterDirectEditorEvent.php b/lib/public/DirectEditing/RegisterDirectEditorEvent.php new file mode 100644 index 00000000000..801e9f8fb1b --- /dev/null +++ b/lib/public/DirectEditing/RegisterDirectEditorEvent.php @@ -0,0 +1,57 @@ +<?php +/** + * @copyright Copyright (c) 2019 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/>. + * + */ + +namespace OCP\DirectEditing; + +use OCP\EventDispatcher\Event; + +/** + * @since 18.0.0 + */ +class RegisterDirectEditorEvent extends Event { + + /** + * @var IManager + */ + private $manager; + + /** + * RegisterDirectEditorEvent constructor. + * + * @param IManager $manager + * @since 18.0.0 + */ + public function __construct(IManager $manager) { + parent::__construct(); + $this->manager = $manager; + } + + /** + * @since 18.0.0 + * @param IEditor $editor + */ + public function register(IEditor $editor): void { + $this->manager->registerDirectEditor($editor); + } + +} |