Skip to content

Commit e355ea1

Browse files
committed
Implement direct editing API
Signed-off-by: Raul <r.ferreira.fuentes@gmail.com>
1 parent 2aeb616 commit e355ea1

File tree

8 files changed

+483
-0
lines changed

8 files changed

+483
-0
lines changed

lib/AppInfo/Application.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
use OCA\Richdocuments\Listener\CSPListener;
3232
use OCA\Richdocuments\Listener\LoadViewerListener;
3333
use OCA\Richdocuments\Listener\ShareLinkListener;
34+
use OCA\Richdocuments\Listener\RegisterDirectEditorEventListener;
3435
use OCA\Richdocuments\Middleware\WOPIMiddleware;
3536
use OCA\Richdocuments\Listener\FileCreatedFromTemplateListener;
3637
use OCA\Richdocuments\PermissionManager;
@@ -47,6 +48,7 @@
4748
use OCP\AppFramework\Bootstrap\IBootContext;
4849
use OCP\AppFramework\Bootstrap\IBootstrap;
4950
use OCP\AppFramework\Bootstrap\IRegistrationContext;
51+
use OCP\DirectEditing\RegisterDirectEditorEvent;
5052
use OCP\Files\Template\FileCreatedFromTemplateEvent;
5153
use OCP\Files\Template\ITemplateManager;
5254
use OCP\Files\Template\TemplateFileCreator;
@@ -73,6 +75,7 @@ public function register(IRegistrationContext $context): void {
7375
$context->registerEventListener(LoadViewer::class, LoadViewerListener::class);
7476
$context->registerEventListener(ShareLinkAccessedEvent::class, ShareLinkListener::class);
7577
$context->registerEventListener(BeforePreviewFetchedEvent::class, BeforeFetchPreviewListener::class);
78+
$context->registerEventListener(RegisterDirectEditorEvent::class, RegisterDirectEditorEventListener::class);
7679
}
7780

7881
public function boot(IBootContext $context): void {

lib/Db/Wopi.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ public function getDirect() {
186186
return (bool)$this->direct;
187187
}
188188

189+
#[\ReturnTypeWillChange]
189190
public function jsonSerialize() {
190191
$properties = get_object_vars($this);
191192
$reflection = new \ReflectionClass($this);

lib/DirectEditing/DirectEditor.php

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
4+
*
5+
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OCA\Richdocuments\DirectEditing;
25+
26+
use OCA\Richdocuments\AppConfig;
27+
use OCA\Richdocuments\AppInfo\Application;
28+
use OCA\Richdocuments\Capabilities;
29+
use OCA\Richdocuments\Controller\DocumentTrait;
30+
use OCA\Richdocuments\Service\InitialStateService;
31+
use OCA\Richdocuments\TokenManager;
32+
use OCP\AppFramework\Http\NotFoundResponse;
33+
use OCP\AppFramework\Http\Response;
34+
use OCP\AppFramework\Http\TemplateResponse;
35+
use OCP\DirectEditing\IEditor;
36+
use OCP\DirectEditing\IToken;
37+
use OCP\Files\InvalidPathException;
38+
use OCP\Files\IRootFolder;
39+
use OCP\Files\NotFoundException;
40+
use OCP\Files\NotPermittedException;
41+
use OCP\IConfig;
42+
use OCP\IInitialStateService;
43+
use OCP\IL10N;
44+
use Psr\Log\LoggerInterface;
45+
46+
class DirectEditor implements IEditor {
47+
use DocumentTrait;
48+
49+
/** @var IL10N */
50+
private $l10n;
51+
52+
/** @var IInitialStateService */
53+
private $initialStateService;
54+
55+
/** @var string[] */
56+
private $mimetypes;
57+
58+
/** @var TokenManager */
59+
private $tokenManager;
60+
61+
/** @var IRootFolder */
62+
private $rootFolder;
63+
64+
/** @var IConfig */
65+
private $config;
66+
67+
/** @var AppConfig */
68+
private $appConfig;
69+
70+
/** @var LoggerInterface */
71+
private $logger;
72+
73+
74+
public function __construct(
75+
IL10N $l10n,
76+
InitialStateService $initialStateService,
77+
Capabilities $capabilities,
78+
TokenManager $tokenManager,
79+
IConfig $config,
80+
AppConfig $appConfig,
81+
IRootFolder $rootFolder,
82+
LoggerInterface $logger
83+
) {
84+
$this->l10n = $l10n;
85+
$this->initialStateService = $initialStateService;
86+
$this->mimetypes = $capabilities->getCapabilities()[Application::APPNAME]['mimetypes'];
87+
$this->tokenManager = $tokenManager;
88+
$this->config = $config;
89+
$this->appConfig = $appConfig;
90+
$this->rootFolder = $rootFolder;
91+
$this->logger = $logger;
92+
}
93+
94+
/**
95+
* Return a unique identifier for the editor
96+
*
97+
* e.g. richdocuments
98+
*
99+
* @return string
100+
*/
101+
public function getId(): string {
102+
return Application::APPNAME;
103+
}
104+
105+
/**
106+
* Return a readable name for the editor
107+
*
108+
* e.g. Collabora Online
109+
*
110+
* @return string
111+
*/
112+
public function getName(): string {
113+
return $this->l10n->t('Nextcloud Office');
114+
}
115+
116+
/**
117+
* A list of mimetypes that should open the editor by default
118+
*
119+
* @return array
120+
*/
121+
public function getMimetypes(): array {
122+
return $this->mimetypes;
123+
}
124+
125+
/**
126+
* A list of mimetypes that can be opened in the editor optionally
127+
*
128+
* @return array
129+
*/
130+
public function getMimetypesOptional(): array {
131+
return [];
132+
}
133+
134+
/**
135+
* Return a list of file creation options to be presented to the user
136+
*
137+
* @return array of ACreateFromTemplate|ACreateEmpty
138+
*/
139+
public function getCreators(): array {
140+
return [
141+
new GraphicsCreator($this->l10n),
142+
new PresentationCreator($this->l10n),
143+
new SpreadsheetCreator($this->l10n),
144+
new TextCreator($this->l10n),
145+
];
146+
}
147+
148+
/**
149+
* Return if the view is able to securely view a file without downloading it to the browser
150+
*
151+
* @return bool
152+
*/
153+
public function isSecure(): bool {
154+
return true;
155+
}
156+
157+
/**
158+
* Return a template response for displaying the editor
159+
*
160+
* open can only be called once when the client requests the editor with a one-time-use token
161+
* For handling editing and later requests, editors need to impelement their own token handling and take care of invalidation
162+
*
163+
* 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
164+
*
165+
* @param IToken $token
166+
* @return Response
167+
*/
168+
public function open(IToken $token): Response {
169+
$token->useTokenScope();
170+
try {
171+
$folder = $this->rootFolder->getUserFolder($token->getUser());
172+
$item = $token->getFile();
173+
174+
[$urlSrc, $token, $wopi] = $this->tokenManager->getToken($item->getId(), null, $token->getUser(), true);
175+
176+
$params = [
177+
'permissions' => $item->getPermissions(),
178+
'title' => $item->getName(),
179+
'fileId' => $wopi->getFileid() . '_' . $this->config->getSystemValue('instanceid'),
180+
'token' => $wopi->getToken(),
181+
'token_ttl' => $wopi->getExpiry(),
182+
'urlsrc' => $urlSrc,
183+
'path' => $folder->getRelativePath($item->getPath()),
184+
'instanceId' => $this->config->getSystemValue('instanceid'),
185+
'canonical_webroot' => $this->appConfig->getAppValue('canonical_webroot'),
186+
'direct' => true,
187+
];
188+
189+
$this->initialStateService->provideDocument($wopi);
190+
$response = new TemplateResponse('richdocuments', 'documents', $params, 'base');
191+
$this->applyPolicies($response);
192+
return $response;
193+
} catch (InvalidPathException|NotFoundException|NotPermittedException $e) {
194+
$this->logger->error($e->getMessage());
195+
}
196+
return new NotFoundResponse();
197+
}
198+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
4+
*
5+
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OCA\Richdocuments\DirectEditing;
25+
26+
use OCP\DirectEditing\ACreateEmpty;
27+
use OCP\IL10N;
28+
29+
class GraphicsCreator extends ACreateEmpty {
30+
31+
/**
32+
* @var IL10N
33+
*/
34+
private $l10n;
35+
36+
public function __construct(IL10N $l10n) {
37+
$this->l10n = $l10n;
38+
}
39+
40+
public function getId(): string {
41+
return 'richdocuments_graphics';
42+
}
43+
44+
public function getName(): string {
45+
return $this->l10n->t('diagram');
46+
}
47+
48+
public function getExtension(): string {
49+
return 'odg';
50+
}
51+
52+
public function getMimetype(): string {
53+
return 'application/vnd.oasis.opendocument.graphics';
54+
}
55+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
4+
*
5+
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
6+
*
7+
* @license GNU AGPL version 3 or any later version
8+
*
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as
11+
* published by the Free Software Foundation, either version 3 of the
12+
* License, or (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
21+
*
22+
*/
23+
24+
namespace OCA\Richdocuments\DirectEditing;
25+
26+
use OCA\Richdocuments\AppInfo\Application;
27+
use OCP\DirectEditing\ACreateEmpty;
28+
use OCP\IConfig;
29+
use OCP\IL10N;
30+
31+
class PresentationCreator extends ACreateEmpty {
32+
33+
/** @var IL10N */
34+
private $l10n;
35+
/** @var IConfig */
36+
private $config;
37+
38+
public function __construct(IL10N $l10n, IConfig $config) {
39+
$this->l10n = $l10n;
40+
$this->config = $config;
41+
}
42+
43+
public function getId(): string {
44+
return 'richdocuments_presentation';
45+
}
46+
47+
public function getName(): string {
48+
return $this->l10n->t('presentation');
49+
}
50+
51+
public function getExtension(): string {
52+
$useOoxml = $this->config->getAppValue(Application::APPNAME, 'doc_format', '') === 'ooxml';
53+
return $useOoxml ? 'pptx' : 'odp';
54+
}
55+
56+
public function getMimetype(): string {
57+
return 'application/vnd.oasis.opendocument.presentation';
58+
}
59+
}

0 commit comments

Comments
 (0)