feat(directediting): Allow opening by file idtags/v26.0.0beta5
@@ -35,7 +35,6 @@ use OCP\IRequest; | |||
use OCP\IURLGenerator; | |||
class DirectEditingController extends OCSController { | |||
/** @var IEventDispatcher */ | |||
private $eventDispatcher; | |||
@@ -94,14 +93,14 @@ class DirectEditingController extends OCSController { | |||
/** | |||
* @NoAdminRequired | |||
*/ | |||
public function open(string $path, string $editorId = null): DataResponse { | |||
public function open(string $path, string $editorId = null, ?int $fileId = null): DataResponse { | |||
if (!$this->directEditingManager->isEnabled()) { | |||
return new DataResponse(['message' => 'Direct editing is not enabled'], Http::STATUS_INTERNAL_SERVER_ERROR); | |||
} | |||
$this->eventDispatcher->dispatchTyped(new RegisterDirectEditorEvent($this->directEditingManager)); | |||
try { | |||
$token = $this->directEditingManager->open($path, $editorId); | |||
$token = $this->directEditingManager->open($path, $editorId, $fileId); | |||
return new DataResponse([ | |||
'url' => $this->urlGenerator->linkToRouteAbsolute('files.DirectEditingView.edit', ['token' => $token]) | |||
]); |
@@ -1,4 +1,5 @@ | |||
<?php | |||
declare(strict_types=1); | |||
/** | |||
* @copyright Copyright (c) 2022 Julius Härtl <jus@bitgrid.net> | |||
@@ -29,7 +30,6 @@ use OCP\Capabilities\IInitialStateExcludedCapability; | |||
use OCP\IURLGenerator; | |||
class DirectEditingCapabilities implements ICapability, IInitialStateExcludedCapability { | |||
protected DirectEditingService $directEditingService; | |||
protected IURLGenerator $urlGenerator; | |||
@@ -43,7 +43,8 @@ class DirectEditingCapabilities implements ICapability, IInitialStateExcludedCap | |||
'files' => [ | |||
'directEditing' => [ | |||
'url' => $this->urlGenerator->linkToOCSRouteAbsolute('files.DirectEditing.info'), | |||
'etag' => $this->directEditingService->getDirectEditingETag() | |||
'etag' => $this->directEditingService->getDirectEditingETag(), | |||
'supportsFileId' => true, | |||
] | |||
], | |||
]; |
@@ -26,10 +26,11 @@ | |||
namespace OC\DirectEditing; | |||
use Doctrine\DBAL\FetchMode; | |||
use OC\Files\Node\Folder; | |||
use \OCP\Files\Folder; | |||
use OCP\AppFramework\Http\NotFoundResponse; | |||
use OCP\AppFramework\Http\Response; | |||
use OCP\AppFramework\Http\TemplateResponse; | |||
use OCP\Constants; | |||
use OCP\DB\QueryBuilder\IQueryBuilder; | |||
use OCP\DirectEditing\ACreateFromTemplate; | |||
use OCP\DirectEditing\IEditor; | |||
@@ -152,9 +153,25 @@ class Manager implements IManager { | |||
throw new \RuntimeException('No creator found'); | |||
} | |||
public function open(string $filePath, string $editorId = null): string { | |||
/** @var File $file */ | |||
$file = $this->rootFolder->getUserFolder($this->userId)->get($filePath); | |||
public function open(string $filePath, string $editorId = null, ?int $fileId = null): string { | |||
$userFolder = $this->rootFolder->getUserFolder($this->userId); | |||
$file = $userFolder->get($filePath); | |||
if ($fileId !== null && $file instanceof Folder) { | |||
$files = $file->getById($fileId); | |||
// Workaround to always open files with edit permissions if multiple occurences of | |||
// the same file id are in the user home, ideally we should also track the path of the file when opening | |||
usort($files, function (Node $a, Node $b) { | |||
return ($b->getPermissions() & Constants::PERMISSION_UPDATE) <=> ($a->getPermissions() & Constants::PERMISSION_UPDATE); | |||
}); | |||
$file = array_shift($files); | |||
} | |||
if (!$file instanceof File) { | |||
throw new NotFoundException(); | |||
} | |||
$filePath = $userFolder->getRelativePath($file->getPath()); | |||
if ($editorId === null) { | |||
$editorId = $this->findEditorForFile($file); |
@@ -211,6 +211,86 @@ class ManagerTest extends TestCase { | |||
$this->assertInstanceOf(NotFoundResponse::class, $secondResult); | |||
} | |||
public function testOpenByPath() { | |||
$expectedToken = 'TOKEN' . time(); | |||
$file = $this->createMock(File::class); | |||
$file->expects($this->any()) | |||
->method('getId') | |||
->willReturn(123); | |||
$file->expects($this->any()) | |||
->method('getPath') | |||
->willReturn('/admin/files/File.txt'); | |||
$this->random->expects($this->once()) | |||
->method('generate') | |||
->willReturn($expectedToken); | |||
$folder = $this->createMock(Folder::class); | |||
$this->userFolder | |||
->method('nodeExists') | |||
->withConsecutive(['/File.txt'], ['/']) | |||
->willReturnOnConsecutiveCalls(false, true); | |||
$this->userFolder | |||
->method('get') | |||
->with('/File.txt') | |||
->willReturn($file); | |||
$this->userFolder | |||
->method('getRelativePath') | |||
->willReturn('/File.txt'); | |||
$this->manager->open('/File.txt', 'testeditor'); | |||
$firstResult = $this->manager->edit($expectedToken); | |||
$secondResult = $this->manager->edit($expectedToken); | |||
$this->assertInstanceOf(DataResponse::class, $firstResult); | |||
$this->assertInstanceOf(NotFoundResponse::class, $secondResult); | |||
} | |||
public function testOpenById() { | |||
$expectedToken = 'TOKEN' . time(); | |||
$fileRead = $this->createMock(File::class); | |||
$fileRead->method('getPermissions') | |||
->willReturn(1); | |||
$fileRead->expects($this->any()) | |||
->method('getId') | |||
->willReturn(123); | |||
$fileRead->expects($this->any()) | |||
->method('getPath') | |||
->willReturn('/admin/files/shared_file.txt'); | |||
$file = $this->createMock(File::class); | |||
$file->method('getPermissions') | |||
->willReturn(1); | |||
$file->expects($this->any()) | |||
->method('getId') | |||
->willReturn(123); | |||
$file->expects($this->any()) | |||
->method('getPath') | |||
->willReturn('/admin/files/File.txt'); | |||
$this->random->expects($this->once()) | |||
->method('generate') | |||
->willReturn($expectedToken); | |||
$folder = $this->createMock(Folder::class); | |||
$folder->expects($this->any()) | |||
->method('getById') | |||
->willReturn([ | |||
$fileRead, | |||
$file | |||
]); | |||
$this->userFolder | |||
->method('nodeExists') | |||
->withConsecutive(['/File.txt'], ['/']) | |||
->willReturnOnConsecutiveCalls(false, true); | |||
$this->userFolder | |||
->method('get') | |||
->with('/') | |||
->willReturn($folder); | |||
$this->userFolder | |||
->method('getRelativePath') | |||
->willReturn('/File.txt'); | |||
$this->manager->open('/', 'testeditor', 123); | |||
$firstResult = $this->manager->edit($expectedToken); | |||
$secondResult = $this->manager->edit($expectedToken); | |||
$this->assertInstanceOf(DataResponse::class, $firstResult); | |||
$this->assertInstanceOf(NotFoundResponse::class, $secondResult); | |||
} | |||
public function testCreateFileAlreadyExists() { | |||
$this->expectException(\RuntimeException::class); | |||
$this->userFolder |