From b6230eea7f67850fe974bbe9e82b63d8a30cbfe1 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 | 57 ++++++++++++++++++- .../Share/Events/VerifyMountPointEvent.php | 49 +++++++++++----- 3 files changed, 101 insertions(+), 19 deletions(-) diff --git a/apps/files_sharing/lib/ShareTargetValidator.php b/apps/files_sharing/lib/ShareTargetValidator.php index 7534cabc33a..0b359465ba9 100644 --- a/apps/files_sharing/lib/ShareTargetValidator.php +++ b/apps/files_sharing/lib/ShareTargetValidator.php @@ -61,7 +61,7 @@ class ShareTargetValidator { $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 @@ class ShareTargetValidator { $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 18fcd35ed28..ee968c02e04 100644 --- a/apps/files_sharing/tests/ShareTargetValidatorTest.php +++ b/apps/files_sharing/tests/ShareTargetValidatorTest.php @@ -8,16 +8,25 @@ declare(strict_types=1); 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 @@ class ShareTargetValidatorTest extends TestCase { $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,38 @@ class ShareTargetValidatorTest extends TestCase { $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 2eb392773e4..e94fbb4ef7a 100644 --- a/lib/public/Share/Events/VerifyMountPointEvent.php +++ b/lib/public/Share/Events/VerifyMountPointEvent.php @@ -10,30 +10,25 @@ namespace OCP\Share\Events; 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; } /** @@ -51,6 +46,8 @@ class VerifyMountPointEvent extends Event { } /** + * 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 +60,30 @@ class VerifyMountPointEvent extends Event { public function setParent(string $parent): void { $this->parent = $parent; } + + /** + * @since 33.0.3 + */ + 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 33.0.3 + */ + public function createParent(): bool { + return $this->createParent; + } + + /** + * @since 33.0.3 + */ + public function getUser(): IUser { + return $this->user; + } }