Prechádzať zdrojové kódy

feat(directediting): Allow opening by file id

Signed-off-by: Julius Härtl <jus@bitgrid.net>
Julius Härtl 1 rok pred
rodič
commit
614981ae9a

+ 2 - 3
apps/files/lib/Controller/DirectEditingController.php

@@ -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])
 			]);

+ 3 - 2
apps/files/lib/DirectEditingCapabilities.php

@@ -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,
 				]
 			],
 		];

+ 21 - 4
lib/private/DirectEditing/Manager.php

@@ -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);

+ 80 - 0
tests/lib/DirectEditing/ManagerTest.php

@@ -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