Skip to content

Commit 614981a

Browse files
committed
feat(directediting): Allow opening by file id
Signed-off-by: Julius Härtl <jus@bitgrid.net>
1 parent 6bb0985 commit 614981a

4 files changed

Lines changed: 106 additions & 9 deletions

File tree

apps/files/lib/Controller/DirectEditingController.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
use OCP\IURLGenerator;
3636

3737
class DirectEditingController extends OCSController {
38-
3938
/** @var IEventDispatcher */
4039
private $eventDispatcher;
4140

@@ -94,14 +93,14 @@ public function create(string $path, string $editorId, string $creatorId, string
9493
/**
9594
* @NoAdminRequired
9695
*/
97-
public function open(string $path, string $editorId = null): DataResponse {
96+
public function open(string $path, string $editorId = null, ?int $fileId = null): DataResponse {
9897
if (!$this->directEditingManager->isEnabled()) {
9998
return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR);
10099
}
101100
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager));
102101

103102
try {
104-
$token = $this->directEditingManager->open($path, $editorId);
103+
$token = $this->directEditingManager->open($path, $editorId, $fileId);
105104
return new DataResponse([
106105
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token])
107106
]);

apps/files/lib/DirectEditingCapabilities.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
declare(strict_types=1);
34
/**
45
* @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net>
@@ -29,7 +30,6 @@
2930
use OCP\IURLGenerator;
3031

3132
class DirectEditingCapabilities implements ICapability, IInitialStateExcludedCapability {
32-
3333
protected DirectEditingService $directEditingService;
3434
protected IURLGenerator $urlGenerator;
3535

@@ -43,7 +43,8 @@ public function getCapabilities() {
4343
'files' => [
4444
'directEditing' => [
4545
'url' => $this->urlGenerator->linkToOCSRouteAbsolute('files.DirectEditing.info'),
46-
'etag' => $this->directEditingService->getDirectEditingETag()
46+
'etag' => $this->directEditingService->getDirectEditingETag(),
47+
'supportsFileId' => true,
4748
]
4849
],
4950
];

lib/private/DirectEditing/Manager.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,11 @@
2626
namespace OC\DirectEditing;
2727

2828
use Doctrine\DBAL\FetchMode;
29-
use OC\Files\Node\Folder;
29+
use \OCP\Files\Folder;
3030
use OCP\AppFramework\Http\NotFoundResponse;
3131
use OCP\AppFramework\Http\Response;
3232
use OCP\AppFramework\Http\TemplateResponse;
33+
use OCP\Constants;
3334
use OCP\DB\QueryBuilder\IQueryBuilder;
3435
use OCP\DirectEditing\ACreateFromTemplate;
3536
use OCP\DirectEditing\IEditor;
@@ -152,9 +153,25 @@ public function create(string $path, string $editorId, string $creatorId, $templ
152153
throw new \RuntimeException('No creator found');
153154
}
154155

155-
public function open(string $filePath, string $editorId = null): string {
156-
/** @var File $file */
157-
$file = $this->rootFolder->getUserFolder($this->userId)->get($filePath);
156+
public function open(string $filePath, string $editorId = null, ?int $fileId = null): string {
157+
$userFolder = $this->rootFolder->getUserFolder($this->userId);
158+
$file = $userFolder->get($filePath);
159+
if ($fileId !== null && $file instanceof Folder) {
160+
$files = $file->getById($fileId);
161+
162+
// Workaround to always open files with edit permissions if multiple occurences of
163+
// the same file id are in the user home, ideally we should also track the path of the file when opening
164+
usort($files, function (Node $a, Node $b) {
165+
return ($b->getPermissions() & Constants::PERMISSION_UPDATE) <=> ($a->getPermissions() & Constants::PERMISSION_UPDATE);
166+
});
167+
$file = array_shift($files);
168+
}
169+
170+
if (!$file instanceof File) {
171+
throw new NotFoundException();
172+
}
173+
174+
$filePath = $userFolder->getRelativePath($file->getPath());
158175

159176
if ($editorId === null) {
160177
$editorId = $this->findEditorForFile($file);

tests/lib/DirectEditing/ManagerTest.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,86 @@ public function testCreateTokenAccess() {
211211
$this->assertInstanceOf(NotFoundResponse::class, $secondResult);
212212
}
213213

214+
public function testOpenByPath() {
215+
$expectedToken = 'TOKEN' . time();
216+
$file = $this->createMock(File::class);
217+
$file->expects($this->any())
218+
->method('getId')
219+
->willReturn(123);
220+
$file->expects($this->any())
221+
->method('getPath')
222+
->willReturn('/admin/files/File.txt');
223+
$this->random->expects($this->once())
224+
->method('generate')
225+
->willReturn($expectedToken);
226+
$folder = $this->createMock(Folder::class);
227+
$this->userFolder
228+
->method('nodeExists')
229+
->withConsecutive(['/File.txt'], ['/'])
230+
->willReturnOnConsecutiveCalls(false, true);
231+
$this->userFolder
232+
->method('get')
233+
->with('/File.txt')
234+
->willReturn($file);
235+
$this->userFolder
236+
->method('getRelativePath')
237+
->willReturn('/File.txt');
238+
$this->manager->open('/File.txt', 'testeditor');
239+
$firstResult = $this->manager->edit($expectedToken);
240+
$secondResult = $this->manager->edit($expectedToken);
241+
$this->assertInstanceOf(DataResponse::class, $firstResult);
242+
$this->assertInstanceOf(NotFoundResponse::class, $secondResult);
243+
}
244+
245+
public function testOpenById() {
246+
$expectedToken = 'TOKEN' . time();
247+
$fileRead = $this->createMock(File::class);
248+
$fileRead->method('getPermissions')
249+
->willReturn(1);
250+
$fileRead->expects($this->any())
251+
->method('getId')
252+
->willReturn(123);
253+
$fileRead->expects($this->any())
254+
->method('getPath')
255+
->willReturn('/admin/files/shared_file.txt');
256+
$file = $this->createMock(File::class);
257+
$file->method('getPermissions')
258+
->willReturn(1);
259+
$file->expects($this->any())
260+
->method('getId')
261+
->willReturn(123);
262+
$file->expects($this->any())
263+
->method('getPath')
264+
->willReturn('/admin/files/File.txt');
265+
$this->random->expects($this->once())
266+
->method('generate')
267+
->willReturn($expectedToken);
268+
$folder = $this->createMock(Folder::class);
269+
$folder->expects($this->any())
270+
->method('getById')
271+
->willReturn([
272+
$fileRead,
273+
$file
274+
]);
275+
$this->userFolder
276+
->method('nodeExists')
277+
->withConsecutive(['/File.txt'], ['/'])
278+
->willReturnOnConsecutiveCalls(false, true);
279+
$this->userFolder
280+
->method('get')
281+
->with('/')
282+
->willReturn($folder);
283+
$this->userFolder
284+
->method('getRelativePath')
285+
->willReturn('/File.txt');
286+
287+
$this->manager->open('/', 'testeditor', 123);
288+
$firstResult = $this->manager->edit($expectedToken);
289+
$secondResult = $this->manager->edit($expectedToken);
290+
$this->assertInstanceOf(DataResponse::class, $firstResult);
291+
$this->assertInstanceOf(NotFoundResponse::class, $secondResult);
292+
}
293+
214294
public function testCreateFileAlreadyExists() {
215295
$this->expectException(\RuntimeException::class);
216296
$this->userFolder

0 commit comments

Comments
 (0)