Merge pull request #58207 from nextcloud/share-verify-mount-event-improvements
Some checks are pending
CodeQL Advanced / Analyze (actions) (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Integration sqlite / changes (push) Waiting to run
Integration sqlite / integration-sqlite (master, 8.4, main, --tags ~@large files_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, capabilities_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, collaboration_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, comments_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, dav_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, federation_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, file_conversions) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, files_reminders) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, filesdrop_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, ldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, openldap_numerical_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, remoteapi_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, routing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, setup_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharees_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, sharing_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, theming_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite (master, 8.4, main, videoverification_features) (push) Blocked by required conditions
Integration sqlite / integration-sqlite-summary (push) Blocked by required conditions
Psalm static code analysis / static-code-analysis (push) Waiting to run
Psalm static code analysis / static-code-analysis-security (push) Waiting to run
Psalm static code analysis / static-code-analysis-ocp (push) Waiting to run
Psalm static code analysis / static-code-analysis-ncu (push) Waiting to run
Psalm static code analysis / static-code-analysis-strict (push) Waiting to run

feat: improve VerifyMountPointEvent event
This commit is contained in:
Robin Appelman 2026-02-16 20:49:39 +01:00 committed by GitHub
commit 3ec7bd2ee1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 104 additions and 19 deletions

View file

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

View file

@ -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,40 @@ 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);
}
}

View file

@ -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;
}
/**
@ -45,12 +40,15 @@ class VerifyMountPointEvent extends Event {
/**
* @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 @@ class VerifyMountPointEvent extends Event {
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;
}
}