diff --git a/apps/files_sharing/lib/ShareTargetValidator.php b/apps/files_sharing/lib/ShareTargetValidator.php index 3dbd3877819c4..2b1ca7aca9333 100644 --- a/apps/files_sharing/lib/ShareTargetValidator.php +++ b/apps/files_sharing/lib/ShareTargetValidator.php @@ -60,7 +60,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(); @@ -78,9 +78,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 new file mode 100644 index 0000000000000..07ff9e69e5af1 --- /dev/null +++ b/apps/files_sharing/tests/ShareTargetValidatorTest.php @@ -0,0 +1,101 @@ + + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +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\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; + protected string $folder2; + + protected function setUp(): void { + parent::setUp(); + + $this->folder = '/folder_share_storage_test'; + $this->folder2 = '/folder_share_storage_test2'; + + $this->filename = '/share-api-storage.txt'; + + + $this->view->mkdir($this->folder); + $this->view->mkdir($this->folder2); + + // save file with content + $this->view->file_put_contents($this->filename, 'root file'); + $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->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); + } + + /** + * 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..e94fbb4ef7ad9 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; } /** @@ -51,6 +46,8 @@ public function getView(): 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 +60,30 @@ public function getParent(): string { 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; + } }