From a3cc9754f84f3bf24d9db5deba77cec7aba0b1b3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 9 Feb 2026 18:33:22 +0100 Subject: [PATCH] feat: improve VerifyMountPointEvent event Signed-off-by: Robin Appelman --- .../lib/ShareTargetValidator.php | 14 +++-- .../tests/ShareTargetValidatorTest.php | 59 ++++++++++++++++++- .../Share/Events/VerifyMountPointEvent.php | 50 ++++++++++++---- 3 files changed, 104 insertions(+), 19 deletions(-) diff --git a/apps/files_sharing/lib/ShareTargetValidator.php b/apps/files_sharing/lib/ShareTargetValidator.php index 554f76c0dca41..e1c8c0345cdd5 100644 --- a/apps/files_sharing/lib/ShareTargetValidator.php +++ b/apps/files_sharing/lib/ShareTargetValidator.php @@ -61,7 +61,7 @@ public function verifyMountPoint( $parent = dirname($share->getTarget()); $recipientView = $this->getViewForUser($user); - $event = new VerifyMountPointEvent($share, $recipientView, $parent); + $event = new VerifyMountPointEvent($share, $recipientView, $parent, $user); $this->eventDispatcher->dispatchTyped($event); $parent = $event->getParent(); @@ -79,9 +79,15 @@ public function verifyMountPoint( $this->folderExistsCache->set($parent, $parentExists); } if (!$parentExists) { - $parent = Helper::getShareFolder($recipientView, $user->getUID()); - /** @psalm-suppress InternalMethod */ - $absoluteParent = $recipientView->getAbsolutePath($parent); + if ($event->createParent()) { + $internalPath = $parentMount->getInternalPath($absoluteParent); + $parentMount->getStorage()->mkdir($internalPath); + $parentMount->getStorage()->getUpdater()->update($internalPath); + } else { + $parent = Helper::getShareFolder($recipientView, $user->getUID()); + /** @psalm-suppress InternalMethod */ + $absoluteParent = $recipientView->getAbsolutePath($parent); + } } $newAbsoluteMountPoint = $this->generateUniqueTarget( diff --git a/apps/files_sharing/tests/ShareTargetValidatorTest.php b/apps/files_sharing/tests/ShareTargetValidatorTest.php index 18fcd35ed285c..7589c32285de9 100644 --- a/apps/files_sharing/tests/ShareTargetValidatorTest.php +++ b/apps/files_sharing/tests/ShareTargetValidatorTest.php @@ -8,16 +8,25 @@ namespace OCA\Files_Sharing\Tests; +use OC\EventDispatcher\EventDispatcher; +use OC\Files\SetupManager; use OCA\Files_Sharing\ShareTargetValidator; use OCP\Constants; +use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Config\ICachedMountInfo; -use OCP\Files\Folder; +use OCP\Files\Mount\IMountManager; use OCP\IUser; use OCP\Server; +use OCP\Share\Events\VerifyMountPointEvent; +use OCP\Share\IManager; use OCP\Share\IShare; +use Psr\Container\ContainerInterface; +use Psr\Log\LoggerInterface; +use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyEventDispatcher; #[\PHPUnit\Framework\Attributes\Group('DB')] class ShareTargetValidatorTest extends TestCase { + private IEventDispatcher $eventDispatcher; private ShareTargetValidator $targetValidator; private IUser $user2; @@ -40,7 +49,17 @@ protected function setUp(): void { $this->view->file_put_contents($this->folder . $this->filename, 'file in subfolder'); $this->view->file_put_contents($this->folder2 . $this->filename, 'file in subfolder2'); - $this->targetValidator = Server::get(ShareTargetValidator::class); + $this->eventDispatcher = new EventDispatcher( + new SymfonyEventDispatcher(), + Server::get(ContainerInterface::class), + $this->createMock(LoggerInterface::class), + ); + $this->targetValidator = new ShareTargetValidator( + Server::get(IManager::class), + $this->eventDispatcher, + Server::get(SetupManager::class), + Server::get(IMountManager::class), + ); $this->user2 = $this->createMock(IUser::class); $this->user2->method('getUID') ->willReturn(self::TEST_FILES_SHARING_API_USER2); @@ -138,4 +157,40 @@ public function testShareMountOverShare(): void { $this->shareManager->deleteShare($share2); $this->view->unlink($this->folder); } + + + /** + * test if the parent folder is created if asked for + */ + public function testShareMountCreateParentFolder(): void { + // share to user + $share = $this->share( + IShare::TYPE_USER, + $this->folder, + self::TEST_FILES_SHARING_API_USER1, + self::TEST_FILES_SHARING_API_USER2, + Constants::PERMISSION_ALL); + $this->shareManager->acceptShare($share, self::TEST_FILES_SHARING_API_USER2); + + $share->setTarget('/foo/bar' . $this->folder); + $this->shareManager->moveShare($share, self::TEST_FILES_SHARING_API_USER2); + + $share = $this->shareManager->getShareById($share->getFullId()); + $this->assertSame('/foo/bar' . $this->folder, $share->getTarget()); + + $this->eventDispatcher->addListener(VerifyMountPointEvent::class, function (VerifyMountPointEvent $event) { + $event->setCreateParent(true); + }); + $this->targetValidator->verifyMountPoint($this->user2, $share, [], [$share]); + + $share = $this->shareManager->getShareById($share->getFullId()); + $this->assertSame('/foo/bar' . $this->folder, $share->getTarget()); + $userFolder = $this->rootFolder->getUserFolder(self::TEST_FILES_SHARING_API_USER2); + $this->assertTrue($userFolder->nodeExists('/foo/bar')); + + //cleanup + self::loginHelper(self::TEST_FILES_SHARING_API_USER1); + $this->shareManager->deleteShare($share); + $this->view->unlink($this->folder); + } } diff --git a/lib/public/Share/Events/VerifyMountPointEvent.php b/lib/public/Share/Events/VerifyMountPointEvent.php index 2eb392773e454..03c9ab1f6c9d2 100644 --- a/lib/public/Share/Events/VerifyMountPointEvent.php +++ b/lib/public/Share/Events/VerifyMountPointEvent.php @@ -10,30 +10,25 @@ use OC\Files\View; use OCP\EventDispatcher\Event; +use OCP\IUser; use OCP\Share\IShare; /** * @since 19.0.0 */ class VerifyMountPointEvent extends Event { - /** @var IShare */ - private $share; - /** @var View */ - private $view; - /** @var string */ - private $parent; + private bool $createParent = false; /** * @since 19.0.0 */ - public function __construct(IShare $share, - View $view, - string $parent) { + public function __construct( + private readonly IShare $share, + private readonly View $view, + private string $parent, + private readonly IUser $user, + ) { parent::__construct(); - - $this->share = $share; - $this->view = $view; - $this->parent = $parent; } /** @@ -45,12 +40,15 @@ public function getShare(): IShare { /** * @since 19.0.0 + * @depecated 34.0.0 Get the user folder for `$this->getUser()` instead */ public function getView(): View { return $this->view; } /** + * The parent folder where the share is placed, as relative path to the users home directory. + * * @since 19.0.0 */ public function getParent(): string { @@ -63,4 +61,30 @@ public function getParent(): string { public function setParent(string $parent): void { $this->parent = $parent; } + + /** + * @since 34.0.0 + */ + public function setCreateParent(bool $create): void { + $this->createParent = $create; + } + + /** + * Whether the parent folder should be created if missing. + * + * If set for `false` (the default), and the parent folder doesn't exist already, + * the share will be moved to the default share folder instead. + * + * @since 34.0.0 + */ + public function createParent(): bool { + return $this->createParent; + } + + /** + * @since 34.0.0 + */ + public function getUser(): IUser { + return $this->user; + } }