Compare commits

...

7 Commits

Author SHA1 Message Date
Carl Schwan
56c440937a refactor: Rebase master on top of node changes
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:57:00 +01:00
Carl Schwan
ec40a61e34 fix: Throw exception when trying to access invalid Node
Instead of just ignoring it and possibly creating subtle bugs.

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:57:00 +01:00
Carl Schwan
ccc6077159 refactor(node): Be more exact about the types returned
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:56:59 +01:00
Carl Schwan
6d61b9f05a refactor(files): Return ?int in FileInfo::getId and Node::getId
Instead of -1

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:48:35 +01:00
Carl Schwan
00e214080f fix(FileInfo): Ensure getChecksum doesn't return null
Same for getETag

Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:47:28 +01:00
Carl Schwan
5b67d03825 fix(filesystem): Fix type hinting in Filesystem
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:47:28 +01:00
Carl Schwan
29bdc676f9 refactor: Add type-hinting to Node-API
Signed-off-by: Carl Schwan <carl.schwan@nextcloud.com>
2026-01-12 10:47:26 +01:00
72 changed files with 1284 additions and 1794 deletions

View File

@@ -53,10 +53,6 @@ class CommentsSearchProvider implements IProvider {
public function search(IUser $user, ISearchQuery $query): SearchResult {
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
if ($userFolder === null) {
return SearchResult::complete($this->l10n->t('Comments'), []);
}
$result = [];
$numComments = 50;
$offset = 0;

View File

@@ -7,7 +7,9 @@
*/
namespace OCA\DAV\Connector\Sabre;
use OC\Files\Mount\DummyMountPoint;
use OC\Files\Mount\MoveableMount;
use OC\Files\Storage\FailedStorage;
use OC\Files\Utils\PathHelper;
use OC\Files\View;
use OCA\DAV\AppInfo\Application;
@@ -113,9 +115,9 @@ class Directory extends Node implements
$info = $this->fileView->getFileInfo($this->path . '/' . $name);
if (!$info) {
// use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
$info = new \OC\Files\FileInfo($path, null, null, [
$info = new \OC\Files\FileInfo($path, new FailedStorage(['exception' => new \LogicException('Dummy storage') ]), '', [
'type' => FileInfo::TYPE_FILE
], null);
], new DummyMountPoint());
}
$node = new File($this->fileView, $info);

View File

@@ -55,12 +55,6 @@ class File extends Node implements IFile {
/**
* Sets up the node, expects a full path name
*
* @param View $view
* @param FileInfo $info
* @param ?\OCP\Share\IManager $shareManager
* @param ?IRequest $request
* @param ?IL10N $l10n
*/
public function __construct(View $view, FileInfo $info, ?IManager $shareManager = null, ?IRequest $request = null, ?IL10N $l10n = null) {
parent::__construct($view, $info, $shareManager);
@@ -369,7 +363,7 @@ class File extends Node implements IFile {
if ($checksumHeader) {
$checksum = trim($checksumHeader);
$this->setChecksum($checksum);
} elseif ($this->getChecksum() !== null && $this->getChecksum() !== '') {
} elseif ($this->getChecksum() !== '') {
$this->setChecksum('');
}
} catch (StorageNotAvailableException $e) {
@@ -549,9 +543,6 @@ class File extends Node implements IFile {
}
$node = $this->getNode();
$storage = $node->getStorage();
if (!$storage) {
return false;
}
if (!($node->getPermissions() & Constants::PERMISSION_READ)) {
return false;
@@ -612,10 +603,8 @@ class File extends Node implements IFile {
/**
* Get the checksum for this file
*
* @return string|null
*/
public function getChecksum() {
public function getChecksum(): string {
return $this->info->getChecksum();
}

View File

@@ -278,7 +278,7 @@ class FilesPlugin extends ServerPlugin {
if ($node instanceof File) {
//Add OC-Checksum header
$checksum = $node->getChecksum();
if ($checksum !== null && $checksum !== '') {
if ($checksum !== '') {
$response->addHeader('OC-Checksum', $checksum);
}
}
@@ -312,7 +312,7 @@ class FilesPlugin extends ServerPlugin {
});
$propFind->handle(self::INTERNAL_FILEID_PROPERTYNAME, function () use ($node) {
return $node->getInternalFileId();
return (string)$node->getInternalFileId();
});
$propFind->handle(self::PERMISSIONS_PROPERTYNAME, function () use ($node) {
@@ -500,7 +500,7 @@ class FilesPlugin extends ServerPlugin {
$propFind->handle(self::CHECKSUMS_PROPERTYNAME, function () use ($node) {
$checksum = $node->getChecksum();
if ($checksum === null || $checksum === '') {
if ($checksum === '') {
return null;
}

View File

@@ -27,18 +27,8 @@ use OCP\Share\IManager;
abstract class Node implements \Sabre\DAV\INode {
/**
* The path to the current node
*
* @var string
*/
protected $path;
protected FileInfo $info;
/**
* @var IManager
*/
protected $shareManager;
protected string $path = '';
protected \OCP\Files\Node $node;
/**
@@ -46,14 +36,11 @@ abstract class Node implements \Sabre\DAV\INode {
*/
public function __construct(
protected View $fileView,
FileInfo $info,
?IManager $shareManager = null,
protected FileInfo $info,
protected ?IManager $shareManager = null,
) {
$this->path = $this->fileView->getRelativePath($info->getPath());
$this->info = $info;
if ($shareManager) {
$this->shareManager = $shareManager;
} else {
if (!$this->shareManager) {
$this->shareManager = Server::get(\OCP\Share\IManager::class);
}
if ($info instanceof Folder || $info instanceof File) {
@@ -141,7 +128,7 @@ abstract class Node implements \Sabre\DAV\INode {
public function getLastModified() {
$timestamp = $this->info->getMtime();
if (!empty($timestamp)) {
return (int)$timestamp;
return $timestamp;
}
return $timestamp;
}
@@ -207,7 +194,7 @@ abstract class Node implements \Sabre\DAV\INode {
* @return int
*/
public function getId() {
return $this->info->getId();
return $this->info->getId() ?? -1;
}
/**
@@ -221,11 +208,8 @@ abstract class Node implements \Sabre\DAV\INode {
return null;
}
/**
* @return integer
*/
public function getInternalFileId() {
return $this->info->getId();
public function getInternalFileId(): int {
return $this->info->getId() ?? -1;
}
public function getInternalPath(): string {

View File

@@ -70,6 +70,8 @@ class DirectoryTest extends \Test\TestCase {
parent::setUp();
$this->view = $this->createMock(View::class);
$this->view->method('getAbsolutePath')->willReturnArgument(0);
$this->info = $this->createMock(FileInfo::class);
$this->storage = $this->createMock(IStorage::class);
$this->info->method('getStorage')
@@ -107,7 +109,7 @@ class DirectoryTest extends \Test\TestCase {
->willReturn(true);
$this->view->expects($this->never())
->method('rmdir');
$dir = $this->getDir();
$dir = $this->getDir('/');
$dir->delete();
}
@@ -231,6 +233,8 @@ class DirectoryTest extends \Test\TestCase {
->method('isReadable')
->willReturn(false);
$this->view->method('getRelativePath')->willReturnArgument(0);
$dir = new Directory($this->view, $info);
$dir->getChildren();
}
@@ -243,6 +247,8 @@ class DirectoryTest extends \Test\TestCase {
->method('isReadable')
->willReturn(false);
$this->view->method('getRelativePath')->willReturnArgument(0);
$dir = new Directory($this->view, $this->info);
$dir->getChild('test');
}
@@ -255,6 +261,8 @@ class DirectoryTest extends \Test\TestCase {
->method('getFileInfo')
->willThrowException(new StorageNotAvailableException());
$this->view->method('getRelativePath')->willReturnArgument(0);
$dir = new Directory($this->view, $this->info);
$dir->getChild('.');
}
@@ -269,6 +277,8 @@ class DirectoryTest extends \Test\TestCase {
$this->view->expects($this->never())
->method('getFileInfo');
$this->view->method('getRelativePath')->willReturnArgument(0);
$dir = new Directory($this->view, $this->info);
$dir->getChild('.');
}
@@ -562,12 +572,12 @@ class DirectoryTest extends \Test\TestCase {
private function moveTest(string $source, string $destination, array $updatables, array $deletables): void {
$view = new TestViewDirectory($updatables, $deletables);
$sourceInfo = new FileInfo($source, null, null, [
$sourceInfo = new FileInfo($source, $this->createMock(IStorage::class), '', [
'type' => FileInfo::TYPE_FOLDER,
], null);
$targetInfo = new FileInfo(dirname($destination), null, null, [
], $this->createMock(IMountPoint::class));
$targetInfo = new FileInfo(dirname($destination), $this->createMock(IStorage::class), '', [
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$sourceNode = new Directory($view, $sourceInfo);
$targetNode = $this->getMockBuilder(Directory::class)
@@ -592,8 +602,8 @@ class DirectoryTest extends \Test\TestCase {
$view = new TestViewDirectory($updatables, $deletables);
$sourceInfo = new FileInfo($source, null, null, ['type' => FileInfo::TYPE_FOLDER], null);
$targetInfo = new FileInfo(dirname($destination), null, null, ['type' => FileInfo::TYPE_FOLDER], null);
$sourceInfo = new FileInfo($source, $this->createMock(IStorage::class), '', ['type' => FileInfo::TYPE_FOLDER], $this->createMock(IMountPoint::class));
$targetInfo = new FileInfo(dirname($destination), $this->createMock(IStorage::class), '', ['type' => FileInfo::TYPE_FOLDER], $this->createMock(IMountPoint::class));
$sourceNode = new Directory($view, $sourceInfo);
$targetNode = $this->getMockBuilder(Directory::class)

View File

@@ -26,6 +26,7 @@ use OCP\Files\ForbiddenException;
use OCP\Files\InvalidContentException;
use OCP\Files\InvalidPathException;
use OCP\Files\LockNotAcquiredException;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotPermittedException;
use OCP\Files\Storage\IStorage;
use OCP\Files\StorageNotAvailableException;
@@ -184,10 +185,10 @@ class FileTest extends TestCase {
->method('getRelativePath')
->willReturnArgument(0);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -227,12 +228,12 @@ class FileTest extends TestCase {
$info = new \OC\Files\FileInfo(
$viewRoot . '/' . ltrim($path, '/'),
$this->getMockStorage(),
null,
'',
[
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
],
null
$this->createMock(IMountPoint::class),
);
/** @var File&MockObject $file */
@@ -495,10 +496,10 @@ class FileTest extends TestCase {
'method' => 'PUT',
], $this->requestId, $this->config, null);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info, null, $request);
@@ -529,10 +530,10 @@ class FileTest extends TestCase {
// simulate situation where the target file is locked
$view->lockFile('/test.txt', ILockingProvider::LOCK_EXCLUSIVE);
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/' . $this->user . '/files/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -567,10 +568,10 @@ class FileTest extends TestCase {
->method('getRelativePath')
->willReturnArgument(0);
$info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo("/i\nvalid", $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
// action
@@ -608,10 +609,10 @@ class FileTest extends TestCase {
->method('getRelativePath')
->willReturnArgument(0);
$info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/valid', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
$file->setName("/i\nvalid");
@@ -642,10 +643,10 @@ class FileTest extends TestCase {
'method' => 'PUT',
], $this->requestId, $this->config, null);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info, null, $request);
@@ -674,14 +675,17 @@ class FileTest extends TestCase {
$view = $this->getMockBuilder(View::class)
->getMock();
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
$view->expects($this->once())
->method('unlink')
->willReturn(true);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -698,10 +702,13 @@ class FileTest extends TestCase {
$view = $this->getMockBuilder(View::class)
->getMock();
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => 0,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -718,15 +725,18 @@ class FileTest extends TestCase {
$view = $this->getMockBuilder(View::class)
->getMock();
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
// but fails
$view->expects($this->once())
->method('unlink')
->willReturn(false);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -743,15 +753,18 @@ class FileTest extends TestCase {
$view = $this->getMockBuilder(View::class)
->getMock();
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
// but fails
$view->expects($this->once())
->method('unlink')
->willThrowException(new ForbiddenException('', true));
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -785,12 +798,12 @@ class FileTest extends TestCase {
$info = new \OC\Files\FileInfo(
'/' . $this->user . '/files/' . $path,
$this->getMockStorage(),
null,
'',
[
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
],
null
$this->createMock(IMountPoint::class)
);
$file = new File($view, $info);
@@ -921,10 +934,10 @@ class FileTest extends TestCase {
->method('fopen')
->willReturn(false);
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FILE,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -943,10 +956,10 @@ class FileTest extends TestCase {
->method('fopen')
->willThrowException(new ForbiddenException('', true));
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FILE,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -964,10 +977,10 @@ class FileTest extends TestCase {
$view->expects($this->never())
->method('fopen');
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), null, [
$info = new \OC\Files\FileInfo('/test.txt', $this->getMockStorage(), '', [
'permissions' => Constants::PERMISSION_CREATE, // no read perm
'type' => FileInfo::TYPE_FOLDER,
], null);
], $this->createMock(IMountPoint::class));
$file = new File($view, $info);
@@ -1012,12 +1025,12 @@ class FileTest extends TestCase {
$info = new \OC\Files\FileInfo(
'/' . $this->user . '/files/' . $path,
$this->getMockStorage(),
null,
'',
[
'permissions' => Constants::PERMISSION_ALL,
'type' => FileInfo::TYPE_FOLDER,
],
null
$this->createMock(IMountPoint::class)
);
$file = new File($view, $info);

View File

@@ -96,7 +96,7 @@ class FilesPluginTest extends TestCase {
->willReturn('00000123instanceid');
$node->expects($this->any())
->method('getInternalFileId')
->willReturn('123');
->willReturn(123);
$node->expects($this->any())
->method('getEtag')
->willReturn('"abc"');

View File

@@ -57,6 +57,8 @@ class FilesReportPluginTest extends \Test\TestCase {
$this->tree = $this->createMock(Tree::class);
$this->view = $this->createMock(View::class);
$this->view->method('getAbsolutePath')->willReturnArgument(0);
$this->view->method('getRelativePath')->willReturnArgument(0);
$this->server = $this->getMockBuilder(Server::class)
->setConstructorArgs([$this->tree])
@@ -315,14 +317,14 @@ class FilesReportPluginTest extends \Test\TestCase {
$node1->expects($this->once())
->method('getInternalFileId')
->willReturn('111');
->willReturn(111);
$node1->expects($this->any())
->method('getPath')
->willReturn('/node1');
$node1->method('getFileInfo')->willReturn($fileInfo);
$node2->expects($this->once())
->method('getInternalFileId')
->willReturn('222');
->willReturn(222);
$node2->expects($this->once())
->method('getSize')
->willReturn(1024);

View File

@@ -55,7 +55,7 @@ class NodeTest extends \Test\TestCase {
public function testDavPermissions(int $permissions, string $type, bool $shared, int $shareRootPermissions, bool $mounted, string $internalPath, string $expected): void {
$info = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->onlyMethods(['getPermissions', 'isShared', 'isMounted', 'getType', 'getInternalPath', 'getStorage', 'getMountPoint'])
->onlyMethods(['getPermissions', 'isShared', 'isMounted', 'getType', 'getPath', 'getInternalPath', 'getStorage', 'getMountPoint'])
->getMock();
$info->method('getPermissions')
->willReturn($permissions);
@@ -65,6 +65,8 @@ class NodeTest extends \Test\TestCase {
->willReturn($mounted);
$info->method('getType')
->willReturn($type);
$info->method('getPath')
->willReturn('');
$info->method('getInternalPath')
->willReturn($internalPath);
$info->method('getMountPoint')
@@ -94,8 +96,10 @@ class NodeTest extends \Test\TestCase {
$info->method('getStorage')
->willReturn($storage);
$view = $this->createMock(View::class);
$view->method('getRelativePath')->willReturnArgument(0);
$view->method('getAbsolutePath')->willReturnArgument(0);
$node = new File($view, $info);
$node = new File($view, $info);
$this->assertEquals($expected, $node->getDavPermissions());
}
@@ -160,15 +164,18 @@ class NodeTest extends \Test\TestCase {
$info = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->onlyMethods(['getStorage', 'getType', 'getMountPoint', 'getPermissions'])
->onlyMethods(['getStorage', 'getType', 'getPath', 'getMountPoint', 'getPermissions'])
->getMock();
$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn($type);
$info->method('getPath')->willReturn('');
$info->method('getMountPoint')->willReturn($mountpoint);
$info->method('getPermissions')->willReturn($permissions);
$view = $this->createMock(View::class);
$view->method('getRelativePath')->willReturnArgument(0);
$view->method('getAbsolutePath')->willReturnArgument(0);
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -196,14 +203,17 @@ class NodeTest extends \Test\TestCase {
/** @var Folder&MockObject $info */
$info = $this->getMockBuilder(Folder::class)
->disableOriginalConstructor()
->onlyMethods(['getStorage', 'getType'])
->onlyMethods(['getStorage', 'getType', 'getPath'])
->getMock();
$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);
$info->method('getPath')->willReturn('');
/** @var View&MockObject $view */
$view = $this->createMock(View::class);
$view->method('getRelativePath')->willReturnArgument(0);
$view->method('getAbsolutePath')->willReturnArgument(0);
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -217,14 +227,17 @@ class NodeTest extends \Test\TestCase {
/** @var Folder&MockObject */
$info = $this->getMockBuilder(Folder::class)
->disableOriginalConstructor()
->onlyMethods(['getStorage', 'getType'])
->onlyMethods(['getStorage', 'getType', 'getPath'])
->getMock();
$info->method('getStorage')->willReturn($storage);
$info->method('getType')->willReturn(FileInfo::TYPE_FOLDER);
$info->method('getPath')->willReturn('');
/** @var View&MockObject */
$view = $this->createMock(View::class);
$view->method('getRelativePath')->willReturnArgument(0);
$view->method('getAbsolutePath')->willReturnArgument(0);
$node = new File($view, $info);
$this->invokePrivate($node, 'shareManager', [$shareManager]);
@@ -243,6 +256,9 @@ class NodeTest extends \Test\TestCase {
$view = $this->getMockBuilder(View::class)
->disableOriginalConstructor()
->getMock();
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
$info = $this->getMockBuilder(FileInfo::class)
->disableOriginalConstructor()
->getMock();
@@ -263,6 +279,8 @@ class NodeTest extends \Test\TestCase {
$this->expectException(\InvalidArgumentException::class);
$view = $this->createMock(View::class);
$view->method('getRelativePath')->willReturnArgument(0);
$view->method('getAbsolutePath')->willReturnArgument(0);
$info = $this->createMock(FileInfo::class);
$node = new File($view, $info);

View File

@@ -19,6 +19,7 @@ use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
use OCA\DAV\Connector\Sabre\File;
use OCA\DAV\Connector\Sabre\ObjectTree;
use OCP\Files\Mount\IMountManager;
use PHPUnit\Framework\MockObject\MockObject;
/**
* Class ObjectTreeTest
@@ -41,7 +42,7 @@ class ObjectTreeTest extends \Test\TestCase {
#[\PHPUnit\Framework\Attributes\DataProvider('copyDataProvider')]
public function testCopy(string $sourcePath, string $targetPath, string $targetParent): void {
$view = $this->createMock(View::class);
$view = $this->createView();
$view->expects($this->once())
->method('verifyPath')
->with($targetParent);
@@ -85,7 +86,7 @@ class ObjectTreeTest extends \Test\TestCase {
public function testCopyFailNotCreatable($sourcePath, $targetPath, $targetParent): void {
$this->expectException(\Sabre\DAV\Exception\Forbidden::class);
$view = $this->createMock(View::class);
$view = $this->createView();
$view->expects($this->never())
->method('verifyPath');
$view->expects($this->once())
@@ -129,7 +130,7 @@ class ObjectTreeTest extends \Test\TestCase {
): void {
$rootNode = $this->createMock(Directory::class);
$mountManager = $this->createMock(Manager::class);
$view = $this->createMock(View::class);
$view = $this->createView();
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('getType')
->willReturn($type);
@@ -240,4 +241,11 @@ class ObjectTreeTest extends \Test\TestCase {
$this->assertInstanceOf('\Sabre\DAV\INode', $tree->getNodeForPath($path));
}
private function createView(): View&MockObject {
$view = $this->createMock(View::class);
$view->method('getAbsolutePath')->willReturnArgument(0);
$view->method('getRelativePath')->willReturnArgument(0);
return $view;
}
}

View File

@@ -118,7 +118,7 @@ abstract class RequestTestCase extends TestCase {
$sapi = new Sapi($request);
$server->sapi = $sapi;
$server->httpRequest = $request;
$server->exec();
$server->start();
return $sapi->getResponse();
}

View File

@@ -97,6 +97,7 @@ class ViewOnlyPluginTest extends TestCase {
#[\PHPUnit\Framework\Attributes\DataProvider('providesDataForCanGet')]
public function testCanGet(bool $isVersion, ?bool $attrEnabled, bool $expectCanDownloadFile, bool $allowViewWithoutDownload): void {
$nodeInfo = $this->createMock(File::class);
$nodeInfo->method('getId')->willReturn(42);
if ($isVersion) {
$davPath = 'versions/alice/versions/117/123456';
$version = $this->createMock(IVersion::class);

View File

@@ -313,6 +313,9 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
try {
$fileId = $share->getNode()->getId();
if ($fileId == null) {
throw new \LogicException('Invalid node for share');
}
[$file, $link] = $this->getFile($user, $fileId);
} catch (\Exception) {
throw new ShareNotFound();
@@ -388,6 +391,9 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
try {
$fileId = $share->getNode()->getId();
if ($fileId == null) {
throw new \LogicException('Invalid node for share');
}
[$file, $link] = $this->getFile($user, $fileId);
} catch (\Exception) {
throw new ShareNotFound();

View File

@@ -201,9 +201,7 @@ class ApiController extends Controller {
];
$shareTypes = [];
$nodeIds = array_map(function (Node $node) {
return $node->getId();
}, $nodes);
$nodeIds = array_filter(array_map(fn (Node $node): ?int => $node->getId(), $nodes));
foreach ($requestedShareTypes as $shareType) {
$nodesLeft = array_combine($nodeIds, array_fill(0, count($nodeIds), true));

View File

@@ -99,7 +99,7 @@ class ConversionApiController extends OCSController {
}
$file = $userFolder->get($convertedFileRelativePath);
$fileId = $file->getId();
$fileId = $file->getId() ?? -1;
return new DataResponse([
'path' => $convertedFileRelativePath,

View File

@@ -134,14 +134,18 @@ class SyncLivePhotosListener implements IEventListener {
return;
}
$this->pendingRenames[] = $sourceFile->getId();
$sourceFileId = $sourceFile->getId();
if ($sourceFileId === null) {
throw new \LogicException('Invalid source file given with a null id');
}
$this->pendingRenames[] = $sourceFileId;
try {
$peerFile->move($targetParent->getPath() . '/' . $peerTargetName);
} catch (\Throwable $ex) {
throw new AbortedEventException($ex->getMessage());
}
$this->pendingRenames = array_diff($this->pendingRenames, [$sourceFile->getId()]);
$this->pendingRenames = array_diff($this->pendingRenames, [$sourceFileId]);
}
@@ -163,15 +167,26 @@ class SyncLivePhotosListener implements IEventListener {
$targetPeerFile = $peerFile->copy($targetParent->getPath() . '/' . $peerTargetName);
}
$targetFileId = $targetFile->getId();
if ($targetFileId === null) {
throw new \LogicException('Invalid target file given with a null id');
}
$targetPeerFileId = $targetPeerFile->getId();
if ($targetPeerFileId === null) {
throw new \LogicException('Invalid target peer file given with a null id');
}
/** @var FilesMetadata $targetMetadata */
$targetMetadata = $this->filesMetadataManager->getMetadata($targetFile->getId(), true);
$targetMetadata = $this->filesMetadataManager->getMetadata($targetFileId, true);
$targetMetadata->setStorageId($targetFile->getStorage()->getCache()->getNumericStorageId());
$targetMetadata->setString('files-live-photo', (string)$targetPeerFile->getId());
$targetMetadata->setString('files-live-photo', (string)$targetPeerFileId);
$this->filesMetadataManager->saveMetadata($targetMetadata);
/** @var FilesMetadata $peerMetadata */
$peerMetadata = $this->filesMetadataManager->getMetadata($targetPeerFile->getId(), true);
$peerMetadata = $this->filesMetadataManager->getMetadata($targetPeerFileId, true);
$peerMetadata->setStorageId($targetPeerFile->getStorage()->getCache()->getNumericStorageId());
$peerMetadata->setString('files-live-photo', (string)$targetFile->getId());
$peerMetadata->setString('files-live-photo', (string)$targetFileId);
$this->filesMetadataManager->saveMetadata($peerMetadata);
}
@@ -185,14 +200,22 @@ class SyncLivePhotosListener implements IEventListener {
private function handleDeletion(BeforeNodeDeletedEvent $event, Node $peerFile): void {
$deletedFile = $event->getNode();
if ($deletedFile->getMimetype() === 'video/quicktime') {
if (isset($this->pendingDeletion[$peerFile->getId()])) {
unset($this->pendingDeletion[$peerFile->getId()]);
$peerFileId = $peerFile->getId();
if ($peerFileId === null) {
throw new \LogicException('Invalid peer file given with a null id');
}
if (isset($this->pendingDeletion[$peerFileId])) {
unset($this->pendingDeletion[$peerFileId]);
return;
} else {
throw new AbortedEventException('Cannot delete the video part of a live photo');
}
} else {
$this->pendingDeletion[$deletedFile->getId()] = true;
$deletedFileId = $peerFile->getId();
if ($deletedFileId === null) {
throw new \LogicException('Invalid deleted file given with a null id');
}
$this->pendingDeletion[$deletedFileId] = true;
try {
$peerFile->delete();
} catch (\Throwable $ex) {
@@ -243,7 +266,7 @@ class SyncLivePhotosListener implements IEventListener {
$this->pendingCopies[] = $peerFileId;
if ($event instanceof BeforeNodeCopiedEvent) {
$this->runMoveOrCopyChecks($sourceNode, $targetNode, $peerFile);
} elseif ($event instanceof NodeCopiedEvent) {
} elseif ($event instanceof NodeCopiedEvent && $peerFile instanceof File) {
$this->handleCopy($sourceNode, $targetNode, $peerFile);
}
$this->pendingCopies = array_diff($this->pendingCopies, [$peerFileId]);

View File

@@ -45,6 +45,9 @@ class TagService {
}
$fileId = $this->homeFolder->get($path)->getId();
if ($fileId === null) {
return [];
}
$currentTags = $this->tagger->getTagsForObjects([$fileId]);

View File

@@ -43,6 +43,7 @@ class ConversionApiControllerTest extends TestCase {
$this->request = $this->createMock(IRequest::class);
$this->fileConversionManager = $this->createMock(IConversionManager::class);
$this->file = $this->createMock(File::class);
$this->file->method('isReadable')->willReturn(true);
$this->l10n = $this->createMock(IL10N::class);
$this->user = 'userid';

View File

@@ -9,12 +9,14 @@ declare(strict_types=1);
use OC\Files\FileInfo;
use OCA\Files\Helper;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
class HelperTest extends \Test\TestCase {
private static function makeFileInfo($name, $size, $mtime, $isDir = false): FileInfo {
private function makeFileInfo($name, $size, $mtime, $isDir = false): FileInfo {
return new FileInfo(
'/' . $name,
null,
$this->createMock(IStorage::class),
'/',
[
'name' => $name,
@@ -23,21 +25,21 @@ class HelperTest extends \Test\TestCase {
'type' => $isDir ? 'dir' : 'file',
'mimetype' => $isDir ? 'httpd/unix-directory' : 'application/octet-stream'
],
null
$this->createMock(IMountPoint::class),
);
}
/**
* Returns a file list for testing
*/
private static function getTestFileList(): array {
private function getTestFileList(): array {
return [
self::makeFileInfo('a.txt', 4, 2.3 * pow(10, 9)),
self::makeFileInfo('q.txt', 5, 150),
self::makeFileInfo('subdir2', 87, 128, true),
self::makeFileInfo('b.txt', 2.2 * pow(10, 9), 800),
self::makeFileInfo('o.txt', 12, 100),
self::makeFileInfo('subdir', 88, 125, true),
$this->makeFileInfo('a.txt', 4, 2.3 * pow(10, 9)),
$this->makeFileInfo('q.txt', 5, 150),
$this->makeFileInfo('subdir2', 87, 128, true),
$this->makeFileInfo('b.txt', 2.2 * pow(10, 9), 800),
$this->makeFileInfo('o.txt', 12, 100),
$this->makeFileInfo('subdir', 88, 125, true),
];
}
@@ -81,7 +83,7 @@ class HelperTest extends \Test\TestCase {
if (($sort === 'mtime') && (PHP_INT_SIZE < 8)) {
$this->markTestSkipped('Skip mtime sorting on 32bit');
}
$files = self::getTestFileList();
$files = $this->getTestFileList();
$files = Helper::sortFiles($files, $sort, $sortDescending);
$fileNames = [];
foreach ($files as $fileInfo) {

View File

@@ -90,7 +90,7 @@ class ListShares extends Base {
if (is_numeric($file)) {
return (int)$file;
}
return $this->getFile($file)->getId();
return $this->getFile($file)->getId() ?? -1;
}
private function getFile(string $file): Node {

View File

@@ -73,7 +73,7 @@ class DeletedShareAPIController extends OCSController {
if (!$node) {
// fallback to guessing the path
$node = $userFolder->get($share->getTarget());
if ($node === null || $share->getTarget() === '') {
if ($share->getTarget() === '') {
throw new NotFoundException();
}
}

View File

@@ -157,7 +157,7 @@ class ShareAPIController extends OCSController {
if (!$node) {
// fallback to guessing the path
$node = $userFolder->get($share->getTarget());
if ($node === null || $share->getTarget() === '') {
if ($share->getTarget() === '') {
throw new NotFoundException();
}
}
@@ -200,9 +200,9 @@ class ShareAPIController extends OCSController {
$result['has_preview'] = $this->previewManager->isAvailable($node);
$result['storage_id'] = $node->getStorage()->getId();
$result['storage'] = $node->getStorage()->getCache()->getNumericStorageId();
$result['item_source'] = $node->getId();
$result['file_source'] = $node->getId();
$result['file_parent'] = $node->getParent()->getId();
$result['item_source'] = $node->getId() ?? -1;
$result['file_source'] = $node->getId() ?? -1;
$result['file_parent'] = $node->getParent()->getId() ?? -1;
$result['file_target'] = $share->getTarget();
$result['item_size'] = $node->getSize();
$result['item_mtime'] = $node->getMTime();
@@ -1440,7 +1440,7 @@ class ShareAPIController extends OCSController {
if (!$node) {
// fallback to guessing the path
$node = $userFolder->get($share->getTarget());
if ($node === null || $share->getTarget() === '') {
if ($share->getTarget() === '') {
return null;
}
}

View File

@@ -138,8 +138,8 @@ class ShareInfoController extends ApiController {
private function format(Node $node, int $permissionMask): array {
$entry = [];
$entry['id'] = $node->getId();
$entry['parentId'] = $node->getParent()->getId();
$entry['id'] = $node->getId() ?? -1;
$entry['parentId'] = $node->getParent()->getId() ?? -1;
$entry['mtime'] = $node->getMTime();
$entry['name'] = $node->getName();

View File

@@ -522,7 +522,7 @@ class ShareAPIControllerTest extends TestCase {
public function testDeleteShareOwnerless(): void {
$ocs = $this->mockFormatShare();
$mount = $this->createMock(IShareOwnerlessMount::class);
$mount = $this->createMockForIntersectionOfInterfaces([IShareOwnerlessMount::class, IMountPoint::class]);
$file = $this->createMock(File::class);
$file
@@ -609,7 +609,7 @@ class ShareAPIControllerTest extends TestCase {
?string $password = null,
string $label = '',
?IShareAttributes $attributes = null,
): MockObject {
): IShare&MockObject {
$share = $this->createMock(IShare::class);
$share->method('getId')->willReturn($id);
$share->method('getShareType')->willReturn($shareType);
@@ -855,6 +855,7 @@ class ShareAPIControllerTest extends TestCase {
$mountPoint->method('getMountType')->willReturn('');
$nodeParams = $shareParams[5];
/** @var \OCP\Files\Node&MockObject $node */
$node = $this->createMock($nodeParams['class']);
$node->method('getId')->willReturn($nodeParams['id']);
$node->method('getPath')->willReturn($nodeParams['path']);
@@ -863,7 +864,7 @@ class ShareAPIControllerTest extends TestCase {
$node->method('getSize')->willReturn(123465);
$node->method('getMTime')->willReturn(1234567890);
$node->method('getMimeType')->willReturn($nodeParams['mimeType']);
$node->method('getMountPoint')->willReturn($mountPoint);
$node->method('getInternalPath')->willReturn(ltrim($nodeParams['path'], '/'));
$shareParams[5] = $node;
@@ -4011,7 +4012,7 @@ class ShareAPIControllerTest extends TestCase {
public function testUpdateShareOwnerless(): void {
$ocs = $this->mockFormatShare();
$mount = $this->createMock(IShareOwnerlessMount::class);
$mount = $this->createMockForIntersectionOfInterfaces([IShareOwnerlessMount::class, IMountPoint::class]);
$file = $this->createMock(File::class);
$file
@@ -4942,6 +4943,7 @@ class ShareAPIControllerTest extends TestCase {
$expects['attributes'] = \json_encode($shareParams['attributes']);
}
if (isset($shareParams['node'])) {
/** @var MockObject&\OCP\Files\Node $node */
$node = $this->createMock($shareParams['node']['class']);
$node->method('getMimeType')->willReturn($shareParams['node']['mimeType']);
@@ -4951,6 +4953,7 @@ class ShareAPIControllerTest extends TestCase {
$node->method('getMountPoint')->willReturn($mountPoint);
$node->method('getPath')->willReturn($shareParams['node']['path']);
$node->method('getInternalPath')->willReturn(ltrim($shareParams['node']['path'], '/'));
$node->method('getId')->willReturn($shareParams['node']['id']);
$parent = $this->createMock(Folder::class);
@@ -5169,6 +5172,7 @@ class ShareAPIControllerTest extends TestCase {
$file->method('getSize')->willReturn(123456);
$file->method('getMTime')->willReturn(1234567890);
$file->method('getInternalPath')->willReturn(ltrim('file', '/'));
$mountPoint = $this->createMock(IMountPoint::class);
$mountPoint->method('getMountType')->willReturn('');

View File

@@ -72,8 +72,13 @@ class SyncLivePhotosListener implements IEventListener {
$sourceFile = $event->getSource();
if ($sourceFile->getMimetype() === 'video/quicktime') {
if (isset($this->pendingRestores[$peerFile->getId()])) {
unset($this->pendingRestores[$peerFile->getId()]);
$peerFileId = $peerFile->getId();
if ($peerFileId === null) {
throw new \LogicException('Invalid peer file given with a null id');
}
if (isset($this->pendingRestores[$peerFileId])) {
unset($this->pendingRestores[$peerFileId]);
return;
} else {
$event->abortOperation(new NotPermittedException('Cannot restore the video part of a live photo'));
@@ -97,7 +102,12 @@ class SyncLivePhotosListener implements IEventListener {
$event->abortOperation(new NotFoundException("Couldn't find peer file in trashbin"));
}
$this->pendingRestores[$sourceFile->getId()] = true;
$sourceFileId = $sourceFile->getId();
if ($sourceFileId === null) {
throw new \LogicException('Invalid source file given with a null id');
}
$this->pendingRestores[$sourceFileId] = true;
try {
$this->trashManager->restoreItem($trashItem);
} catch (\Throwable $ex) {

View File

@@ -31,7 +31,7 @@ abstract class AbstractTrash implements ITrash {
}
public function getFileId(): int {
return $this->data->getId();
return $this->data->getId() ?? -1;
}
public function getFileInfo(): FileInfo {

View File

@@ -7,7 +7,10 @@
namespace OCA\Files_Trashbin\Trash;
use OCP\Files\FileInfo;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use Override;
class TrashItem implements ITrashItem {
@@ -46,126 +49,150 @@ class TrashItem implements ITrashItem {
return $this->user;
}
public function getEtag() {
return $this->fileInfo->getEtag();
}
public function getSize($includeMounts = true) {
return $this->fileInfo->getSize($includeMounts);
}
public function getMtime() {
return $this->fileInfo->getMtime();
}
public function getName() {
return $this->fileInfo->getName();
}
public function getInternalPath() {
return $this->fileInfo->getInternalPath();
}
public function getPath() {
return $this->fileInfo->getPath();
}
public function getMimetype(): string {
return $this->fileInfo->getMimetype();
}
public function getMimePart() {
return $this->fileInfo->getMimePart();
}
public function getStorage() {
return $this->fileInfo->getStorage();
}
public function getId() {
return $this->fileInfo->getId();
}
public function isEncrypted() {
return $this->fileInfo->isEncrypted();
}
public function getPermissions() {
return $this->fileInfo->getPermissions();
}
public function getType() {
return $this->fileInfo->getType();
}
public function isReadable() {
return $this->fileInfo->isReadable();
}
public function isUpdateable() {
return $this->fileInfo->isUpdateable();
}
public function isCreatable() {
return $this->fileInfo->isCreatable();
}
public function isDeletable() {
return $this->fileInfo->isDeletable();
}
public function isShareable() {
return $this->fileInfo->isShareable();
}
public function isShared() {
return $this->fileInfo->isShared();
}
public function isMounted() {
return $this->fileInfo->isMounted();
}
public function getMountPoint() {
return $this->fileInfo->getMountPoint();
}
public function getOwner() {
return $this->fileInfo->getOwner();
}
public function getChecksum() {
return $this->fileInfo->getChecksum();
}
public function getExtension(): string {
return $this->fileInfo->getExtension();
}
public function getTitle(): string {
return $this->getOriginalLocation();
}
public function getCreationTime(): int {
return $this->fileInfo->getCreationTime();
}
public function getUploadTime(): int {
return $this->fileInfo->getUploadTime();
}
public function getParentId(): int {
return $this->fileInfo->getParentId();
}
public function getDeletedBy(): ?IUser {
return $this->deletedBy;
}
/**
* @inheritDoc
* @return array<string, int|string|bool|float|string[]|int[]>
*/
#[Override]
public function getEtag(): string {
return $this->fileInfo->getEtag();
}
#[Override]
public function getSize(bool $includeMounts = true): int|float {
return $this->fileInfo->getSize($includeMounts);
}
#[Override]
public function getMtime(): int {
return $this->fileInfo->getMtime();
}
#[Override]
public function getName(): string {
return $this->fileInfo->getName();
}
#[Override]
public function getInternalPath(): string {
return $this->fileInfo->getInternalPath();
}
#[Override]
public function getPath(): string {
return $this->fileInfo->getPath();
}
#[Override]
public function getMimetype(): string {
return $this->fileInfo->getMimetype();
}
#[Override]
public function getMimePart(): string {
return $this->fileInfo->getMimePart();
}
#[Override]
public function getStorage(): IStorage {
return $this->fileInfo->getStorage();
}
#[Override]
public function getId(): ?int {
return $this->fileInfo->getId();
}
#[Override]
public function isEncrypted(): bool {
return $this->fileInfo->isEncrypted();
}
#[Override]
public function getPermissions(): int {
return $this->fileInfo->getPermissions();
}
#[Override]
public function getType(): string {
return $this->fileInfo->getType();
}
#[Override]
public function isReadable(): bool {
return $this->fileInfo->isReadable();
}
#[Override]
public function isUpdateable(): bool {
return $this->fileInfo->isUpdateable();
}
#[Override]
public function isCreatable(): bool {
return $this->fileInfo->isCreatable();
}
#[Override]
public function isDeletable(): bool {
return $this->fileInfo->isDeletable();
}
public function isShareable(): bool {
return $this->fileInfo->isShareable();
}
#[Override]
public function isShared(): bool {
return $this->fileInfo->isShared();
}
#[Override]
public function isMounted(): bool {
return $this->fileInfo->isMounted();
}
#[Override]
public function getMountPoint(): IMountPoint {
return $this->fileInfo->getMountPoint();
}
#[Override]
public function getOwner(): ?IUser {
return $this->fileInfo->getOwner();
}
#[Override]
public function getChecksum(): string {
return $this->fileInfo->getChecksum();
}
#[Override]
public function getExtension(): string {
return $this->fileInfo->getExtension();
}
#[Override]
public function getTitle(): string {
return $this->getOriginalLocation();
}
#[Override]
public function getCreationTime(): int {
return $this->fileInfo->getCreationTime();
}
#[Override]
public function getUploadTime(): int {
return $this->fileInfo->getUploadTime();
}
#[Override]
public function getParentId(): int {
return $this->fileInfo->getParentId();
}
#[Override]
public function getMetadata(): array {
return $this->fileInfo->getMetadata();
}

View File

@@ -8,7 +8,6 @@
namespace OCA\Files_Versions\Listener;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OC\DB\Exceptions\DbalException;
use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount;
use OC\Files\Node\NonExistingFile;
@@ -122,7 +121,12 @@ class FileEventsListener implements IEventListener {
return;
}
$this->nodesTouched[$node->getId()] = $node;
$nodeId = $node->getId();
if ($nodeId === null) {
return;
}
$this->nodesTouched[$nodeId] = $node;
}
public function touch_hook(Node $node): void {
@@ -142,13 +146,17 @@ class FileEventsListener implements IEventListener {
return;
}
$previousNode = $this->nodesTouched[$node->getId()] ?? null;
$nodeId = $node->getId();
if ($nodeId === null) {
throw new \LogicException('Invalid node given');
}
$previousNode = $this->nodesTouched[$nodeId] ?? null;
if ($previousNode === null) {
return;
}
unset($this->nodesTouched[$node->getId()]);
unset($this->nodesTouched[$nodeId]);
try {
if ($node instanceof File && $this->versionManager instanceof INeedSyncVersionBackend) {
@@ -157,15 +165,15 @@ class FileEventsListener implements IEventListener {
// We update the timestamp of the version entity associated with the previousNode.
$this->versionManager->updateVersionEntity($node, $revision, ['timestamp' => $node->getMTime()]);
}
} catch (DbalException $ex) {
} catch (Exception $ex) {
// Ignore UniqueConstraintViolationException, as we are probably in the middle of a rollback
// Where the previous node would temporary have the mtime of the old version, so the rollback touches it to fix it.
if (!($ex->getPrevious() instanceof UniqueConstraintViolationException)) {
// Where the previous node would temporarily have the mtime of the old version, so the rollback touches it to fix it.
if ($ex->getReason() !== Exception::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
throw $ex;
}
} catch (DoesNotExistException $ex) {
// Ignore DoesNotExistException, as we are probably in the middle of a rollback
// Where the previous node would temporary have a wrong mtime, so the rollback touches it to fix it.
// Where the previous node would temporarily have a wrong mtime, so the rollback touches it to fix it.
}
}
@@ -208,8 +216,13 @@ class FileEventsListener implements IEventListener {
$path = $this->getPathForNode($node);
$result = Storage::store($path);
$nodeId = $node->getId();
if ($nodeId === null) {
throw new \LogicException('Invalid node given');
}
// Store the result of the version creation so it can be used in post_write_hook.
$this->writeHookInfo[$node->getId()] = [
$this->writeHookInfo[$nodeId] = [
'previousNode' => $node,
'versionCreated' => $result !== false
];
@@ -235,7 +248,12 @@ class FileEventsListener implements IEventListener {
return;
}
$writeHookInfo = $this->writeHookInfo[$node->getId()] ?? null;
$nodeId = $node->getId();
if ($nodeId === null) {
throw new \LogicException('Invalid node given');
}
$writeHookInfo = $this->writeHookInfo[$nodeId] ?? null;
if ($writeHookInfo === null) {
return;

View File

@@ -885,16 +885,16 @@ class Storage {
if ($quota >= 0) {
if ($softQuota) {
$root = Server::get(IRootFolder::class);
$userFolder = $root->getUserFolder($uid);
if (is_null($userFolder)) {
$availableSpace = 0;
} else {
try {
$userFolder = $root->getUserFolder($uid);
$free = $quota - $userFolder->getSize(false); // remaining free space for user
if ($free > 0) {
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - $versionsSize; // how much space can be used for versions
} else {
$availableSpace = $free - $versionsSize;
}
} catch (NoUserException $e) {
$availableSpace = 0;
}
} else {
$availableSpace = $quota;

View File

@@ -136,7 +136,7 @@ class Listener {
foreach ($mounts as $mount) {
$owner = $mount->getUser()->getUID();
$ownerFolder = $this->rootFolder->getUserFolder($owner);
$nodes = $ownerFolder->getById($event->getObjectId());
$nodes = $ownerFolder->getById((int)$event->getObjectId());
if (!empty($nodes)) {
/** @var Node $node */
$node = array_shift($nodes);

View File

@@ -136,9 +136,9 @@ class File implements IEntity, IDisplayText, IUrl, IIcon, IContextPortation {
if (!$this->event instanceof MapperEvent || $this->event->getObjectType() !== 'files') {
throw new NotFoundException();
}
$nodes = $this->root->getById((int)$this->event->getObjectId());
if (is_array($nodes) && isset($nodes[0])) {
$this->node = $nodes[0];
$node = $this->root->getFirstNodeById((int)$this->event->getObjectId());
if ($node !== null) {
$this->node = $node;
return $this->node;
}
break;

View File

@@ -566,11 +566,6 @@
<MoreSpecificReturnType>
<code><![CDATA[Folder]]></code>
</MoreSpecificReturnType>
<NullArgument>
<code><![CDATA[null]]></code>
<code><![CDATA[null]]></code>
<code><![CDATA[null]]></code>
</NullArgument>
<ParamNameMismatch>
<code><![CDATA[$fullSourcePath]]></code>
</ParamNameMismatch>
@@ -641,14 +636,6 @@
<code><![CDATA[unlockFile]]></code>
<code><![CDATA[verifyPath]]></code>
</InternalMethod>
<InvalidNullableReturnType>
<code><![CDATA[int]]></code>
<code><![CDATA[integer]]></code>
</InvalidNullableReturnType>
<NullableReturnStatement>
<code><![CDATA[$this->info->getId()]]></code>
<code><![CDATA[$this->info->getId()]]></code>
</NullableReturnStatement>
</file>
<file src="apps/dav/lib/Connector/Sabre/ObjectTree.php">
<InternalMethod>
@@ -1821,14 +1808,6 @@
<code><![CDATA[new View('/' . $user . '/files_trashbin/files')]]></code>
</InternalMethod>
</file>
<file src="apps/files_trashbin/lib/Sabre/AbstractTrash.php">
<InvalidNullableReturnType>
<code><![CDATA[int]]></code>
</InvalidNullableReturnType>
<NullableReturnStatement>
<code><![CDATA[$this->data->getId()]]></code>
</NullableReturnStatement>
</file>
<file src="apps/files_trashbin/lib/Sabre/AbstractTrashFolder.php">
<InvalidReturnStatement>
<code><![CDATA[$entry]]></code>
@@ -3724,44 +3703,11 @@
<code><![CDATA[array{int, string, int}]]></code>
</MoreSpecificReturnType>
</file>
<file src="lib/private/Files/Filesystem.php">
<LessSpecificReturnStatement>
<code><![CDATA[$mount->getStorage()]]></code>
<code><![CDATA[self::getMountManager()->findByNumericId($id)]]></code>
<code><![CDATA[self::getMountManager()->findByStorageId($id)]]></code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code><![CDATA[Mount\MountPoint[]]]></code>
<code><![CDATA[Mount\MountPoint[]]]></code>
<code><![CDATA[\OC\Files\Storage\Storage|null]]></code>
</MoreSpecificReturnType>
</file>
<file src="lib/private/Files/Mount/MountPoint.php">
<UndefinedInterfaceMethod>
<code><![CDATA[wrap]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="lib/private/Files/Node/File.php">
<InvalidReturnStatement>
<code><![CDATA[$this->view->hash($type, $this->path, $raw)]]></code>
</InvalidReturnStatement>
<InvalidReturnType>
<code><![CDATA[string]]></code>
</InvalidReturnType>
</file>
<file src="lib/private/Files/Node/Folder.php">
<LessSpecificReturnStatement>
<code><![CDATA[array_map(function (FileInfo $file) {
return $this->createNode($file->getPath(), $file);
}, $files)]]></code>
</LessSpecificReturnStatement>
<MoreSpecificImplementedParamType>
<code><![CDATA[$node]]></code>
</MoreSpecificImplementedParamType>
<MoreSpecificReturnType>
<code><![CDATA[\OC\Files\Node\Node[]]]></code>
</MoreSpecificReturnType>
</file>
<file src="lib/private/Files/Node/HookConnector.php">
<UndefinedInterfaceMethod>
<code><![CDATA[emit]]></code>
@@ -3779,64 +3725,6 @@
<code><![CDATA[emit]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="lib/private/Files/Node/LazyFolder.php">
<InvalidReturnStatement>
<code><![CDATA[$this->__call(__FUNCTION__, func_get_args())]]></code>
</InvalidReturnStatement>
</file>
<file src="lib/private/Files/Node/LazyUserFolder.php">
<LessSpecificReturnStatement>
<code><![CDATA[$node]]></code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code><![CDATA[Folder]]></code>
</MoreSpecificReturnType>
</file>
<file src="lib/private/Files/Node/Node.php">
<InvalidNullableReturnType>
<code><![CDATA[FileInfo]]></code>
</InvalidNullableReturnType>
<InvalidReturnType>
<code><![CDATA[getChecksum]]></code>
</InvalidReturnType>
<LessSpecificReturnStatement>
<code><![CDATA[$this->parent]]></code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code><![CDATA[INode|IRootFolder]]></code>
</MoreSpecificReturnType>
<NullableReturnStatement>
<code><![CDATA[$this->fileInfo]]></code>
</NullableReturnStatement>
<ParamNameMismatch>
<code><![CDATA[$type]]></code>
</ParamNameMismatch>
<UndefinedInterfaceMethod>
<code><![CDATA[$this->fileInfo]]></code>
<code><![CDATA[$this->fileInfo]]></code>
</UndefinedInterfaceMethod>
</file>
<file src="lib/private/Files/Node/Root.php">
<LessSpecificReturnStatement>
<code><![CDATA[$folders]]></code>
<code><![CDATA[$this->mountManager->findByNumericId($numericId)]]></code>
<code><![CDATA[$this->mountManager->findByStorageId($storageId)]]></code>
<code><![CDATA[$this->mountManager->findIn($mountPoint)]]></code>
<code><![CDATA[$this->user]]></code>
</LessSpecificReturnStatement>
<MoreSpecificReturnType>
<code><![CDATA[MountPoint[]]]></code>
<code><![CDATA[\OC\Files\Mount\MountPoint[]]]></code>
<code><![CDATA[\OC\Files\Mount\MountPoint[]]]></code>
<code><![CDATA[\OC\User\User]]></code>
</MoreSpecificReturnType>
<NullableReturnStatement>
<code><![CDATA[$this->user]]></code>
</NullableReturnStatement>
<UndefinedMethod>
<code><![CDATA[remove]]></code>
</UndefinedMethod>
</file>
<file src="lib/private/Files/ObjectStore/S3ConnectionTrait.php">
<InternalClass>
<code><![CDATA[ClientResolver::_default_signature_provider()]]></code>

View File

@@ -1737,6 +1737,7 @@ return array(
'OC\\Files\\Filesystem' => $baseDir . '/lib/private/Files/Filesystem.php',
'OC\\Files\\Lock\\LockManager' => $baseDir . '/lib/private/Files/Lock/LockManager.php',
'OC\\Files\\Mount\\CacheMountProvider' => $baseDir . '/lib/private/Files/Mount/CacheMountProvider.php',
'OC\\Files\\Mount\\DummyMountPoint' => $baseDir . '/lib/private/Files/Mount/DummyMountPoint.php',
'OC\\Files\\Mount\\HomeMountPoint' => $baseDir . '/lib/private/Files/Mount/HomeMountPoint.php',
'OC\\Files\\Mount\\LocalHomeMountProvider' => $baseDir . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
'OC\\Files\\Mount\\Manager' => $baseDir . '/lib/private/Files/Mount/Manager.php',

View File

@@ -1778,6 +1778,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Files\\Filesystem' => __DIR__ . '/../../..' . '/lib/private/Files/Filesystem.php',
'OC\\Files\\Lock\\LockManager' => __DIR__ . '/../../..' . '/lib/private/Files/Lock/LockManager.php',
'OC\\Files\\Mount\\CacheMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/CacheMountProvider.php',
'OC\\Files\\Mount\\DummyMountPoint' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/DummyMountPoint.php',
'OC\\Files\\Mount\\HomeMountPoint' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/HomeMountPoint.php',
'OC\\Files\\Mount\\LocalHomeMountProvider' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/LocalHomeMountProvider.php',
'OC\\Files\\Mount\\Manager' => __DIR__ . '/../../..' . '/lib/private/Files/Mount/Manager.php',

View File

@@ -149,6 +149,6 @@ class AppData implements IAppData {
}
public function getId(): int {
return $this->getAppDataFolder()->getId();
return $this->getAppDataFolder()->getId() ?? -1;
}
}

View File

@@ -12,61 +12,29 @@ use OCA\Files_Sharing\External\Mount;
use OCA\Files_Sharing\ISharedMountPoint;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use Override;
/**
* @template-implements \ArrayAccess<string,mixed>
*/
class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
private array|ICacheEntry $data;
/**
* @var string
*/
private $path;
/**
* @var \OC\Files\Storage\Storage $storage
*/
private $storage;
/**
* @var string
*/
private $internalPath;
/**
* @var \OCP\Files\Mount\IMountPoint
*/
private $mount;
private string $path;
private IStorage $storage;
private string $internalPath;
private IMountPoint $mount;
private ?IUser $owner;
/**
* @var string[]
*/
/** @var string[] */
private array $childEtags = [];
/**
* @var IMountPoint[]
*/
/** @var IMountPoint[] */
private array $subMounts = [];
private bool $subMountsUsed = false;
/**
* The size of the file/folder without any sub mount
*/
/** The size of the file/folder without any sub mount */
private int|float $rawSize = 0;
/**
* @param string|boolean $path
* @param Storage\Storage $storage
* @param string $internalPath
* @param array|ICacheEntry $data
* @param IMountPoint $mount
* @param ?IUser $owner
*/
public function __construct($path, $storage, $internalPath, $data, $mount, $owner = null) {
public function __construct(string|bool $path, IStorage $storage, string $internalPath, array|ICacheEntry $data, IMountPoint $mount, ?IUser $owner = null) {
$this->path = $path;
$this->storage = $storage;
$this->internalPath = $internalPath;
@@ -106,30 +74,23 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
};
}
/**
* @return string
*/
public function getPath() {
#[Override]
public function getPath(): string {
return $this->path;
}
public function getStorage() {
#[Override]
public function getStorage(): IStorage {
return $this->storage;
}
/**
* @return string
*/
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
return $this->internalPath;
}
/**
* Get FileInfo ID or null in case of part file
*
* @return int|null
*/
public function getId() {
#[Override]
public function getId(): ?int {
return isset($this->data['fileid']) ? (int)$this->data['fileid'] : null;
}
@@ -137,40 +98,31 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
return $this->data['mimetype'] ?? 'application/octet-stream';
}
/**
* @return string
*/
public function getMimePart() {
#[Override]
public function getMimePart(): string {
return $this->data['mimepart'];
}
/**
* @return string
*/
public function getName() {
#[Override]
public function getName(): string {
return empty($this->data['name'])
? basename($this->getPath())
: $this->data['name'];
}
/**
* @return string
*/
public function getEtag() {
#[Override]
public function getEtag(): string {
$this->updateEntryFromSubMounts();
if (count($this->childEtags) > 0) {
$combinedEtag = $this->data['etag'] . '::' . implode('::', $this->childEtags);
return md5($combinedEtag);
} else {
return $this->data['etag'];
return $this->data['etag'] ?? '';
}
}
/**
* @param bool $includeMounts
* @return int|float
*/
public function getSize($includeMounts = true) {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
if ($includeMounts) {
$this->updateEntryFromSubMounts();
@@ -184,18 +136,14 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
}
}
/**
* @return int
*/
public function getMTime() {
#[Override]
public function getMTime(): int {
$this->updateEntryFromSubMounts();
return (int)$this->data['mtime'];
}
/**
* @return bool
*/
public function isEncrypted() {
#[Override]
public function isEncrypted(): bool {
return $this->data['encrypted'] ?? false;
}
@@ -206,108 +154,81 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
return isset($this->data['encryptedVersion']) ? (int)$this->data['encryptedVersion'] : 1;
}
/**
* @return int
*/
public function getPermissions() {
return (int)$this->data['permissions'];
#[Override]
public function getPermissions(): int {
/** @var \OCP\Constants::PERMISSION_* $permission */
$permission = (int)$this->data['permissions'];
return $permission;
}
/**
* @return string \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER
*/
public function getType() {
#[Override]
public function getType(): string {
if (!isset($this->data['type'])) {
$this->data['type'] = ($this->getMimetype() === self::MIMETYPE_FOLDER) ? self::TYPE_FOLDER : self::TYPE_FILE;
}
return $this->data['type'];
}
public function getData() {
public function getData(): array|ICacheEntry {
return $this->data;
}
/**
* @param int $permissions
* @return bool
*/
protected function checkPermissions($permissions) {
protected function checkPermissions(int $permissions): bool {
return ($this->getPermissions() & $permissions) === $permissions;
}
/**
* @return bool
*/
public function isReadable() {
#[Override]
public function isReadable(): bool {
return $this->checkPermissions(\OCP\Constants::PERMISSION_READ);
}
/**
* @return bool
*/
public function isUpdateable() {
#[Override]
public function isUpdateable(): bool {
return $this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE);
}
/**
* Check whether new files or folders can be created inside this folder
*
* @return bool
*/
public function isCreatable() {
#[Override]
public function isCreatable(): bool {
return $this->checkPermissions(\OCP\Constants::PERMISSION_CREATE);
}
/**
* @return bool
*/
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
return $this->checkPermissions(\OCP\Constants::PERMISSION_DELETE);
}
/**
* @return bool
*/
public function isShareable() {
#[Override]
public function isShareable(): bool {
return $this->checkPermissions(\OCP\Constants::PERMISSION_SHARE);
}
/**
* Check if a file or folder is shared
*
* @return bool
*/
public function isShared() {
#[Override]
public function isShared(): bool {
return $this->mount instanceof ISharedMountPoint;
}
public function isMounted() {
#[Override]
public function isMounted(): bool {
$isHome = $this->mount instanceof HomeMountPoint;
return !$isHome && !$this->isShared();
}
/**
* Get the mountpoint the file belongs to
*
* @return \OCP\Files\Mount\IMountPoint
*/
public function getMountPoint() {
#[Override]
public function getMountPoint(): IMountPoint {
return $this->mount;
}
/**
* Get the owner of the file
*
* @return ?IUser
* Get the owner of the file.
*/
public function getOwner() {
public function getOwner(): ?IUser {
return $this->owner;
}
/**
* @param IMountPoint[] $mounts
*/
public function setSubMounts(array $mounts) {
public function setSubMounts(array $mounts): void {
$this->subMounts = $mounts;
}
@@ -321,21 +242,23 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
if ($subStorage) {
$subCache = $subStorage->getCache('');
$rootEntry = $subCache->get('');
$this->addSubEntry($rootEntry, $mount->getMountPoint());
if (!empty($rootEntry)) {
$this->addSubEntry($rootEntry, $mount->getMountPoint());
}
}
}
}
/**
* Add a cache entry which is the child of this folder
* Add a cache entry which is the child of this folder.
*
* Sets the size, etag and size to for cross-storage childs
* Sets the size, etag and size to for cross-storage children.
*
* @param array|ICacheEntry $data cache entry for the child
* @param string $entryPath full path of the child entry
*/
public function addSubEntry($data, $entryPath) {
if (!$data) {
public function addSubEntry(array|ICacheEntry $data, string $entryPath): void {
if (empty($data)) {
return;
}
$hasUnencryptedSize = isset($data['unencrypted_size']) && $data['unencrypted_size'] > 0;
@@ -360,33 +283,32 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess {
}
}
/**
* @inheritdoc
*/
public function getChecksum() {
return $this->data['checksum'];
#[Override]
public function getChecksum(): string {
return $this->data['checksum'] ?? '';
}
#[Override]
public function getExtension(): string {
return pathinfo($this->getName(), PATHINFO_EXTENSION);
}
#[Override]
public function getCreationTime(): int {
return (int)$this->data['creation_time'];
}
#[Override]
public function getUploadTime(): int {
return (int)$this->data['upload_time'];
}
#[Override]
public function getParentId(): int {
return $this->data['parent'] ?? -1;
}
/**
* @inheritDoc
* @return array<string, int|string|bool|float|string[]|int[]>
*/
#[Override]
public function getMetadata(): array {
return $this->data['metadata'] ?? [];
}

View File

@@ -246,7 +246,7 @@ class Filesystem {
* get the storage mounted at $mountPoint
*
* @param string $mountPoint
* @return \OC\Files\Storage\Storage|null
* @return \OCP\Files\Storage\IStorage|null
*/
public static function getStorage($mountPoint) {
$mount = self::getMountManager()->find($mountPoint);
@@ -255,7 +255,7 @@ class Filesystem {
/**
* @param string $id
* @return Mount\MountPoint[]
* @return \OCP\Files\Mount\IMountPoint[]
*/
public static function getMountByStorageId($id) {
return self::getMountManager()->findByStorageId($id);
@@ -263,7 +263,7 @@ class Filesystem {
/**
* @param int $id
* @return Mount\MountPoint[]
* @return \OCP\Files\Mount\IMountPoint[]
*/
public static function getMountByNumericId($id) {
return self::getMountManager()->findByNumericId($id);

View File

@@ -0,0 +1,70 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH
* SPDX-FileContributor: Carl Schwan
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Files\Mount;
use OC\Files\Storage\FailedStorage;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
final class DummyMountPoint implements IMountPoint {
public function getMountPoint(): string {
return '';
}
public function setMountPoint($mountPoint): void {
}
public function getStorage(): IStorage {
return new FailedStorage(['exception' => new \LogicException('Dummy storage') ]);
}
public function getStorageId(): string {
return '';
}
public function getNumericStorageId(): int {
return -1;
}
public function getInternalPath($path): string {
return $path;
}
public function wrapStorage($wrapper): void {
}
public function getOption($name, $default): mixed {
if ($name === 'previews') {
return false;
}
return $default;
}
public function getOptions(): array {
return ['previews' => false];
}
public function getStorageRootId(): int {
return -1;
}
public function getMountId(): ?int {
return null;
}
public function getMountType(): string {
return 'dummy';
}
public function getMountProvider(): string {
return '';
}
}

View File

@@ -8,27 +8,18 @@
namespace OC\Files\Node;
use OCP\Files\GenericFileException;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Lock\LockedException;
use Override;
class File extends Node implements \OCP\Files\File {
/**
* Creates a Folder that represents a non-existing path
*
* @param string $path path
* @return NonExistingFile non-existing node
*/
protected function createNonExistingNode($path) {
#[Override]
protected function createNonExistingNode(string $path): NonExistingFile {
return new NonExistingFile($this->root, $this->view, $path);
}
/**
* @return string
* @throws NotPermittedException
* @throws GenericFileException
* @throws LockedException
*/
public function getContent() {
#[Override]
public function getContent(): string {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) {
$content = $this->view->file_get_contents($this->path);
if ($content === false) {
@@ -40,13 +31,8 @@ class File extends Node implements \OCP\Files\File {
}
}
/**
* @param string|resource $data
* @throws NotPermittedException
* @throws GenericFileException
* @throws LockedException
*/
public function putContent($data) {
#[Override]
public function putContent($data): void {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
$this->sendHooks(['preWrite']);
if ($this->view->file_put_contents($this->path, $data) === false) {
@@ -59,13 +45,8 @@ class File extends Node implements \OCP\Files\File {
}
}
/**
* @param string $mode
* @return resource|false
* @throws NotPermittedException
* @throws LockedException
*/
public function fopen($mode) {
#[Override]
public function fopen(string $mode) {
$preHooks = [];
$postHooks = [];
$requiredPermissions = \OCP\Constants::PERMISSION_READ;
@@ -100,12 +81,8 @@ class File extends Node implements \OCP\Files\File {
}
}
/**
* @throws NotPermittedException
* @throws \OCP\Files\InvalidPathException
* @throws \OCP\Files\NotFoundException
*/
public function delete() {
#[Override]
public function delete(): void {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
$this->sendHooks(['preDelete']);
$fileInfo = $this->getFileInfo();
@@ -118,22 +95,16 @@ class File extends Node implements \OCP\Files\File {
}
}
/**
* @param string $type
* @param bool $raw
* @return string
*/
public function hash($type, $raw = false) {
return $this->view->hash($type, $this->path, $raw);
}
/**
* @inheritdoc
*/
public function getChecksum() {
return $this->getFileInfo()->getChecksum();
#[Override]
public function hash(string $type, bool $raw = false): string {
$hash = $this->view->hash($type, $this->path, $raw);
if ($hash === false) {
throw new NotFoundException('Unable to compute hash of non-exiting file');
}
return $hash;
}
#[Override]
public function getExtension(): string {
return $this->getFileInfo()->getExtension();
}

View File

@@ -14,6 +14,7 @@ use OC\Files\Search\SearchOrder;
use OC\Files\Search\SearchQuery;
use OC\Files\Utils\PathHelper;
use OC\User\LazyUser;
use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\FileInfo;
use OCP\Files\Folder as IFolder;
@@ -26,31 +27,23 @@ use OCP\Files\Search\ISearchComparison;
use OCP\Files\Search\ISearchOperator;
use OCP\Files\Search\ISearchOrder;
use OCP\Files\Search\ISearchQuery;
use OCP\IConfig;
use OCP\IUserManager;
use OCP\Server;
use Override;
class Folder extends Node implements IFolder {
private ?IUserManager $userManager = null;
private bool $wasDeleted = false;
/**
* Creates a Folder that represents a non-existing path
*
* @param string $path path
* @return NonExistingFolder non-existing node
*/
protected function createNonExistingNode($path) {
#[Override]
protected function createNonExistingNode(string $path): NonExistingFolder {
return new NonExistingFolder($this->root, $this->view, $path);
}
/**
* @param string $path path relative to the folder
* @return string
* @throws \OCP\Files\NotPermittedException
*/
public function getFullPath($path) {
#[Override]
public function getFullPath(string $path): string {
$path = $this->normalizePath($path);
if (!$this->isValidPath($path)) {
throw new NotPermittedException('Invalid path "' . $path . '"');
@@ -58,31 +51,18 @@ class Folder extends Node implements IFolder {
return $this->path . $path;
}
/**
* @param string $path
* @return string|null
*/
public function getRelativePath($path) {
#[Override]
public function getRelativePath(string $path): ?string {
return PathHelper::getRelativePath($this->getPath(), $path);
}
/**
* check if a node is a (grand-)child of the folder
*
* @param \OC\Files\Node\Node $node
* @return bool
*/
public function isSubNode($node) {
#[Override]
public function isSubNode(INode $node): bool {
return str_starts_with($node->getPath(), $this->path . '/');
}
/**
* get the content of this directory
*
* @return Node[]
* @throws \OCP\Files\NotFoundException
*/
public function getDirectoryListing() {
#[Override]
public function getDirectoryListing(): array {
$folderContent = $this->view->getDirectoryContent($this->path, '', $this->getFileInfo(false));
return array_map(function (FileInfo $info) {
@@ -108,11 +88,11 @@ class Folder extends Node implements IFolder {
}
}
public function get($path) {
public function get(string $path): INode {
return $this->root->get($this->getFullPath($path));
}
public function nodeExists($path) {
public function nodeExists(string $path): bool {
try {
$this->get($path);
return true;
@@ -121,13 +101,9 @@ class Folder extends Node implements IFolder {
}
}
/**
* @param string $path
* @return \OC\Files\Node\Folder
* @throws \OCP\Files\NotPermittedException
*/
public function newFolder($path) {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
#[Override]
public function newFolder(string $path): self {
if ($this->checkPermissions(Constants::PERMISSION_CREATE)) {
$fullPath = $this->getFullPath($path);
$nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath);
$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
@@ -156,18 +132,13 @@ class Folder extends Node implements IFolder {
}
}
/**
* @param string $path
* @param string | resource | null $content
* @return \OC\Files\Node\File
* @throws \OCP\Files\NotPermittedException
*/
public function newFile($path, $content = null) {
#[Override]
public function newFile(string $path, $content = null): File {
if ($path === '') {
throw new NotPermittedException('Could not create as provided path is empty');
}
$this->recreateIfNeeded();
if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) {
if ($this->checkPermissions(Constants::PERMISSION_CREATE)) {
$fullPath = $this->getFullPath($path);
$nonExisting = new NonExistingFile($this->root, $this->view, $fullPath);
$this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]);
@@ -190,20 +161,16 @@ class Folder extends Node implements IFolder {
if ($uid === null) {
$user = null;
} else {
/** @var IUserManager $userManager */
$userManager = \OCP\Server::get(IUserManager::class);
$user = $userManager->get($uid);
if ($this->userManager === null) {
$this->userManager = Server::get(IUserManager::class);
}
$user = $this->userManager->get($uid);
}
return new SearchQuery($operator, $limit, $offset, [], $user);
}
/**
* search for files with the name matching $query
*
* @param string|ISearchQuery $query
* @return \OC\Files\Node\Node[]
*/
public function search($query) {
#[Override]
public function search(string|ISearchQuery $query): array {
if (is_string($query)) {
$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'name', '%' . $query . '%'));
}
@@ -217,7 +184,7 @@ class Folder extends Node implements IFolder {
}
/** @var QuerySearchHelper $searchHelper */
$searchHelper = \OC::$server->get(QuerySearchHelper::class);
$searchHelper = Server::get(QuerySearchHelper::class);
[$caches, $mountByMountPoint] = $searchHelper->getCachesAndMountPointsForSearch($this->root, $this->path, $limitToHome);
$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
@@ -264,7 +231,7 @@ class Folder extends Node implements IFolder {
if ($ownerId !== false) {
// Cache the user manager (for performance)
if ($this->userManager === null) {
$this->userManager = \OCP\Server::get(IUserManager::class);
$this->userManager = Server::get(IUserManager::class);
}
$owner = new LazyUser($ownerId, $this->userManager);
}
@@ -279,13 +246,8 @@ class Folder extends Node implements IFolder {
);
}
/**
* search for files by mimetype
*
* @param string $mimetype
* @return Node[]
*/
public function searchByMime($mimetype) {
#[Override]
public function searchByMime(string $mimetype): array {
if (!str_contains($mimetype, '/')) {
$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $mimetype . '/%'));
} else {
@@ -294,37 +256,30 @@ class Folder extends Node implements IFolder {
return $this->search($query);
}
/**
* search for files by tag
*
* @param string|int $tag name or tag id
* @param string $userId owner of the tags
* @return Node[]
*/
public function searchByTag($tag, $userId) {
#[Override]
public function searchByTag(string|int $tag, string $userId): array {
$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'tagname', $tag), $userId);
return $this->search($query);
}
#[Override]
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array {
$query = $this->queryFromOperator(new SearchComparison(ISearchComparison::COMPARE_EQUAL, 'systemtag', $tagName), $userId, $limit, $offset);
return $this->search($query);
}
/**
* @param int $id
* @return \OCP\Files\Node[]
*/
public function getById($id) {
return $this->root->getByIdInPath((int)$id, $this->getPath());
#[Override]
public function getById(int $id): array {
return $this->root->getByIdInPath($id, $this->getPath());
}
public function getFirstNodeById(int $id): ?\OCP\Files\Node {
#[Override]
public function getFirstNodeById(int $id): ?INode {
return $this->root->getFirstNodeByIdInPath($id, $this->getPath());
}
public function getAppDataDirectoryName(): string {
$instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid');
$instanceId = Server::get(IConfig::class)->getSystemValueString('instanceid');
return 'appdata_' . $instanceId;
}
@@ -336,9 +291,6 @@ class Folder extends Node implements IFolder {
* So in that case we directly check the mount of the root if it contains
* the id. If it does we check if the path is inside the path we are working
* in.
*
* @param int $id
* @return array
*/
protected function getByIdInRootMount(int $id): array {
if (!method_exists($this->root, 'createNode')) {
@@ -371,12 +323,14 @@ class Folder extends Node implements IFolder {
))];
}
public function getFreeSpace() {
#[Override]
public function getFreeSpace(): float|int|false {
return $this->view->free_space($this->path);
}
public function delete() {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) {
#[Override]
public function delete(): void {
if ($this->checkPermissions(Constants::PERMISSION_DELETE)) {
$this->sendHooks(['preDelete']);
$fileInfo = $this->getFileInfo();
$this->view->rmdir($this->path);
@@ -388,27 +342,21 @@ class Folder extends Node implements IFolder {
}
}
/**
* Add a suffix to the name in case the file exists
*
* @param string $filename
* @return string
* @throws NotPermittedException
*/
public function getNonExistingName($filename) {
#[Override]
public function getNonExistingName(string $name): string {
$path = $this->getPath();
if ($path === '/') {
$path = '';
}
if ($pos = strrpos($filename, '.')) {
$name = substr($filename, 0, $pos);
$ext = substr($filename, $pos);
if ($pos = strrpos($name, '.')) {
$name = substr($name, 0, $pos);
$ext = substr($name, $pos);
} else {
$name = $filename;
$name = $name;
$ext = '';
}
$newpath = $path . '/' . $filename;
$newpath = $path . '/' . $name;
if ($this->view->file_exists($newpath)) {
if (preg_match_all('/\((\d+)\)/', $name, $matches, PREG_OFFSET_CAPTURE)) {
/** @var array<int<0, max>, array> $matches */
@@ -437,12 +385,8 @@ class Folder extends Node implements IFolder {
return trim($this->getRelativePath($newpath), '/');
}
/**
* @param int $limit
* @param int $offset
* @return INode[]
*/
public function getRecent($limit, $offset = 0) {
#[Override]
public function getRecent(int $limit, int $offset = 0): array {
$filterOutNonEmptyFolder = new SearchBinaryOperator(
// filter out non empty folders
ISearchBinaryOperator::OPERATOR_OR,
@@ -505,7 +449,8 @@ class Folder extends Node implements IFolder {
return $this->search($query);
}
public function verifyPath($fileName, $readonly = false): void {
#[Override]
public function verifyPath(string $fileName, bool $readonly = false): void {
$this->view->verifyPath(
$this->getPath(),
$fileName,

View File

@@ -14,6 +14,9 @@ use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotPermittedException;
use OCP\Files\Search\ISearchQuery;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use Override;
/**
@@ -32,7 +35,6 @@ class LazyFolder implements Folder {
protected array $data;
/**
* @param IRootFolder $rootFolder
* @param \Closure(): Folder $folderClosure
* @param array $data
*/
@@ -65,77 +67,8 @@ class LazyFolder implements Folder {
return call_user_func_array([$this->getRealFolder(), $method], $args);
}
/**
* @inheritDoc
*/
public function getUser() {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function listen($scope, $method, callable $callback) {
$this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function removeListener($scope = null, $method = null, ?callable $callback = null) {
$this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function emit($scope, $method, $arguments = []) {
$this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function mount($storage, $mountPoint, $arguments = []) {
$this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMount(string $mountPoint): IMountPoint {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @return IMountPoint[]
*/
public function getMountsIn(string $mountPoint): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMountByStorageId($storageId) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMountByNumericStorageId($numericId) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function unMount($mount) {
$this->__call(__FUNCTION__, func_get_args());
}
public function get($path) {
#[Override]
public function get(string $path): \OCP\Files\Node {
return $this->getRootFolder()->get($this->getFullPath($path));
}
@@ -144,166 +77,128 @@ class LazyFolder implements Folder {
return $this->getRootFolder()->getOrCreateFolder($this->getFullPath($path), $maxRetries);
}
/**
* @inheritDoc
*/
public function rename($targetPath) {
#[Override]
public function move(string $targetPath): \OCP\Files\Node {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function delete() {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function copy($targetPath) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function touch($mtime = null) {
#[Override]
public function delete(): void {
$this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getStorage() {
#[Override]
public function copy(string $targetPath): \OCP\Files\Node {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getPath() {
#[Override]
public function touch(?int $mtime = null): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getStorage(): IStorage {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getPath(): string {
if (isset($this->data['path'])) {
return $this->data['path'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getId() {
#[Override]
public function getId(): int {
if (isset($this->data['fileid'])) {
return $this->data['fileid'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function stat() {
#[Override]
public function stat(): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMTime() {
#[Override]
public function getMTime(): int {
if (isset($this->data['mtime'])) {
return $this->data['mtime'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getSize($includeMounts = true): int|float {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
if (isset($this->data['size'])) {
return $this->data['size'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getEtag() {
#[Override]
public function getEtag(): string {
if (isset($this->data['etag'])) {
return $this->data['etag'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getPermissions() {
#[Override]
public function getPermissions(): int {
if (isset($this->data['permissions'])) {
return $this->data['permissions'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isReadable() {
#[Override]
public function isReadable(): bool {
if (isset($this->data['permissions'])) {
return ($this->data['permissions'] & Constants::PERMISSION_READ) == Constants::PERMISSION_READ;
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isUpdateable() {
#[Override]
public function isUpdateable(): bool {
if (isset($this->data['permissions'])) {
return ($this->data['permissions'] & Constants::PERMISSION_UPDATE) == Constants::PERMISSION_UPDATE;
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
if (isset($this->data['permissions'])) {
return ($this->data['permissions'] & Constants::PERMISSION_DELETE) == Constants::PERMISSION_DELETE;
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isShareable() {
#[Override]
public function isShareable(): bool {
if (isset($this->data['permissions'])) {
return ($this->data['permissions'] & Constants::PERMISSION_SHARE) == Constants::PERMISSION_SHARE;
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getParent() {
#[Override]
public function getParent(): IRootFolder|\OCP\Files\Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getName() {
#[Override]
public function getName(): string {
if (isset($this->data['path'])) {
return basename($this->data['path']);
}
@@ -313,13 +208,7 @@ class LazyFolder implements Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getUserFolder($userId) {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getMimetype(): string {
if (isset($this->data['mimetype'])) {
return $this->data['mimetype'];
@@ -327,10 +216,8 @@ class LazyFolder implements Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMimePart() {
#[Override]
public function getMimePart(): string {
if (isset($this->data['mimetype'])) {
[$part,] = explode('/', $this->data['mimetype']);
return $part;
@@ -338,66 +225,51 @@ class LazyFolder implements Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isEncrypted() {
#[Override]
public function isEncrypted(): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getType() {
#[Override]
public function getType(): string {
if (isset($this->data['type'])) {
return $this->data['type'];
}
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isShared() {
#[Override]
public function isShared(): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isMounted() {
#[Override]
public function isMounted(): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getMountPoint() {
#[Override]
public function getMountPoint(): IMountPoint {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getOwner() {
#[Override]
public function getOwner(): ?IUser {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getChecksum() {
#[Override]
public function getChecksum(): string {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getExtension(): string {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getFullPath($path) {
#[Override]
public function getFullPath(string $path): string {
if (isset($this->data['path'])) {
$path = PathHelper::normalizePath($path);
if (!Filesystem::isValidPath($path)) {
@@ -408,148 +280,112 @@ class LazyFolder implements Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isSubNode($node) {
#[Override]
public function isSubNode(\OCP\Files\Node $node): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getDirectoryListing() {
#[Override]
public function getDirectoryListing(): array {
return $this->__call(__FUNCTION__, func_get_args());
}
public function nodeExists($path) {
#[Override]
public function nodeExists(string $path): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function newFolder($path) {
#[Override]
public function newFolder(string $path): \OCP\Files\Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function newFile($path, $content = null) {
#[Override]
public function newFile(string $path, $content = null): \OCP\Files\File {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function search($query) {
#[Override]
public function search(string|ISearchQuery $query): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function searchByMime($mimetype) {
#[Override]
public function searchByMime(string $mimetype): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function searchByTag($tag, $userId) {
#[Override]
public function searchByTag(int|string $tag, string $userId): array {
return $this->__call(__FUNCTION__, func_get_args());
}
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0) {
#[Override]
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getById($id) {
return $this->getRootFolder()->getByIdInPath((int)$id, $this->getPath());
#[Override]
public function getById(int $id): array {
return $this->getRootFolder()->getByIdInPath($id, $this->getPath());
}
#[Override]
public function getFirstNodeById(int $id): ?\OCP\Files\Node {
return $this->getRootFolder()->getFirstNodeByIdInPath($id, $this->getPath());
}
/**
* @inheritDoc
*/
public function getFreeSpace() {
#[Override]
public function getFreeSpace(): int|float|false {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function isCreatable() {
#[Override]
public function isCreatable(): bool {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getNonExistingName($filename) {
#[Override]
public function getNonExistingName(string $name): string {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function move($targetPath) {
#[Override]
public function lock(int $type): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function changeLock(int $targetType): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function unlock(int $type): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getRecent(int $limit, int $offset = 0): array {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function lock($type) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function changeLock($targetType) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function unlock($type) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
public function getRecent($limit, $offset = 0) {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
#[Override]
public function getCreationTime(): int {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
*/
#[Override]
public function getUploadTime(): int {
return $this->__call(__FUNCTION__, func_get_args());
}
public function getRelativePath($path) {
#[Override]
public function getRelativePath(string $path): ?string {
return PathHelper::getRelativePath($this->getPath(), $path);
}
#[Override]
public function getParentId(): int {
if (isset($this->data['parent'])) {
return $this->data['parent'];
@@ -557,15 +393,13 @@ class LazyFolder implements Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
/**
* @inheritDoc
* @return array<string, int|string|bool|float|string[]|int[]>
*/
#[Override]
public function getMetadata(): array {
return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args());
}
public function verifyPath($fileName, $readonly = false): void {
#[Override]
public function verifyPath(string $fileName, $readonly = false): void {
$this->__call(__FUNCTION__, func_get_args());
}
}

View File

@@ -12,6 +12,7 @@ use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Node;
use OCP\Files\Node as INode;
use Override;
/**
* Class LazyRoot
@@ -34,23 +35,48 @@ class LazyRoot extends LazyFolder implements IRootFolder {
return $folder;
}
public function getUserFolder($userId) {
#[Override]
public function listen($scope, $method, callable $callback): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function removeListener($scope = null, $method = null, ?callable $callback = null): void {
$this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getUserFolder(string $userId): \OCP\Files\Folder {
return $this->__call(__FUNCTION__, func_get_args());
}
public function getByIdInPath(int $id, string $path) {
#[Override]
public function getByIdInPath(int $id, string $path): array {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getFirstNodeByIdInPath(int $id, string $path): ?Node {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
return $this->getRootFolder()->getNodeFromCacheEntryAndMount($cacheEntry, $mountPoint);
}
#[Override]
public function getAppDataDirectoryName(): string {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getMountsIn(string $mountPoint): array {
return $this->__call(__FUNCTION__, func_get_args());
}
#[Override]
public function getMount(string $mountPoint): IMountPoint {
return $this->__call(__FUNCTION__, func_get_args());
}
}

View File

@@ -14,6 +14,7 @@ use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountManager;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
use OCP\IUser;
use Psr\Log\LoggerInterface;
@@ -53,6 +54,7 @@ class LazyUserFolder extends LazyFolder {
]);
throw $e;
}
/** @var Folder $node */
return $node;
} catch (NotFoundException $e) {
if (!$this->getRootFolder()->nodeExists('/' . $user->getUID())) {
@@ -65,7 +67,7 @@ class LazyUserFolder extends LazyFolder {
);
}
public function getMountPoint() {
public function getMountPoint(): IMountPoint {
if ($this->folder !== null) {
return $this->folder->getMountPoint();
}

View File

@@ -10,75 +10,56 @@ namespace OC\Files\Node;
use OC\Files\Filesystem;
use OC\Files\Mount\MoveableMount;
use OC\Files\Utils\PathHelper;
use OC\Files\View;
use OCP\EventDispatcher\GenericEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\FileInfo;
use OCP\Files\InvalidPathException;
use OCP\Files\IRootFolder;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Node as INode;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Lock\LockedException;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
use OCP\PreConditionNotMetException;
use OCP\Server;
use Override;
// FIXME: this class really should be abstract (+1)
class Node implements INode {
abstract class Node implements INode {
/**
* @var \OC\Files\View $view
* @param string $path Absolute path to the node (e.g. /admin/files/folder/file)
* @throws PreConditionNotMetException
*/
protected $view;
protected IRootFolder $root;
/**
* @var string $path Absolute path to the node (e.g. /admin/files/folder/file)
*/
protected $path;
protected ?FileInfo $fileInfo;
protected ?INode $parent;
private bool $infoHasSubMountsIncluded;
/**
* @param \OC\Files\View $view
* @param \OCP\Files\IRootFolder $root
* @param string $path
* @param FileInfo $fileInfo
*/
public function __construct(IRootFolder $root, $view, $path, $fileInfo = null, ?INode $parent = null, bool $infoHasSubMountsIncluded = true) {
public function __construct(
protected IRootFolder $root,
protected View $view,
protected string $path,
protected ?FileInfo $fileInfo = null,
protected ?\OCP\Files\Folder $parent = null,
protected bool $infoHasSubMountsIncluded = true,
) {
if (Filesystem::normalizePath($view->getRoot()) !== '/') {
throw new PreConditionNotMetException('The view passed to the node should not have any fake root set');
}
$this->view = $view;
$this->root = $root;
$this->path = $path;
$this->fileInfo = $fileInfo;
$this->parent = $parent;
$this->infoHasSubMountsIncluded = $infoHasSubMountsIncluded;
}
/**
* Creates a Node of the same type that represents a non-existing path
* Creates a Node of the same type that represents a non-existing path.
*
* @param string $path path
* @return Node non-existing node
* @throws \Exception
*/
protected function createNonExistingNode($path) {
throw new \Exception('Must be implemented by subclasses');
}
abstract protected function createNonExistingNode(string $path): INode;
/**
* Returns the matching file info
* Returns the matching file info.
*
* @return FileInfo
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getFileInfo(bool $includeMountPoint = true) {
if (!$this->fileInfo) {
public function getFileInfo(bool $includeMountPoint = true): FileInfo {
$fileInfo = $this->fileInfo;
if (!$fileInfo) {
if (!Filesystem::isValidPath($this->path)) {
throw new InvalidPathException();
}
@@ -95,16 +76,15 @@ class Node implements INode {
}
$this->infoHasSubMountsIncluded = true;
}
return $this->fileInfo;
return $fileInfo;
}
/**
* @param string[] $hooks
*/
protected function sendHooks($hooks, ?array $args = null) {
protected function sendHooks(array $hooks, ?array $args = null): void {
$args = !empty($args) ? $args : [$this];
/** @var IEventDispatcher $dispatcher */
$dispatcher = \OC::$server->get(IEventDispatcher::class);
$dispatcher = Server::get(IEventDispatcher::class);
foreach ($hooks as $hook) {
if (method_exists($this->root, 'emit')) {
$this->root->emit('\OC\Files', $hook, $args);
@@ -121,30 +101,24 @@ class Node implements INode {
}
/**
* @param int $permissions
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
protected function checkPermissions($permissions) {
protected function checkPermissions(int $permissions): bool {
return ($this->getPermissions() & $permissions) === $permissions;
}
public function delete() {
#[Override]
public function delete(): void {
}
/**
* @param int $mtime
* @throws InvalidPathException
* @throws NotFoundException
* @throws NotPermittedException
*/
public function touch($mtime = null) {
#[Override]
public function touch(?int $mtime = null): void {
if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) {
$this->sendHooks(['preTouch']);
$this->view->touch($this->path, $mtime);
$this->sendHooks(['postTouch']);
if ($this->fileInfo) {
if ($this->fileInfo instanceof \OC\Files\FileInfo) {
if (is_null($mtime)) {
$mtime = time();
}
@@ -155,7 +129,8 @@ class Node implements INode {
}
}
public function getStorage() {
#[Override]
public function getStorage(): IStorage {
$storage = $this->getMountPoint()->getStorage();
if (!$storage) {
throw new \Exception('No storage for node');
@@ -163,119 +138,77 @@ class Node implements INode {
return $storage;
}
/**
* @return string
*/
public function getPath() {
#[Override]
public function getPath(): string {
return $this->path;
}
/**
* @return string
*/
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
return $this->getFileInfo(false)->getInternalPath();
}
/**
* @return int
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getId() {
return $this->getFileInfo(false)->getId() ?? -1;
#[Override]
public function getId(): ?int {
return $this->getFileInfo(false)->getId();
}
/**
* @return array
*/
public function stat() {
#[Override]
public function stat(): array|false {
return $this->view->stat($this->path);
}
/**
* @return int
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getMTime() {
#[Override]
public function getMTime(): int {
return $this->getFileInfo()->getMTime();
}
/**
* @param bool $includeMounts
* @return int|float
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getSize($includeMounts = true): int|float {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
return $this->getFileInfo()->getSize($includeMounts);
}
/**
* @return string
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getEtag() {
#[Override]
public function getEtag(): string {
return $this->getFileInfo()->getEtag();
}
/**
* @return int
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getPermissions() {
#[Override]
public function getPermissions(): int {
return $this->getFileInfo(false)->getPermissions();
}
/**
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
public function isReadable() {
#[Override]
public function isReadable(): bool {
return $this->getFileInfo(false)->isReadable();
}
/**
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
public function isUpdateable() {
#[Override]
public function isUpdateable(): bool {
return $this->getFileInfo(false)->isUpdateable();
}
/**
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
return $this->getFileInfo(false)->isDeletable();
}
/**
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
public function isShareable() {
#[Override]
public function isShareable(): bool {
return $this->getFileInfo(false)->isShareable();
}
/**
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
*/
public function isCreatable() {
#[Override]
public function isCreatable(): bool {
return $this->getFileInfo(false)->isCreatable();
}
public function getParent(): INode|IRootFolder {
#[Override]
public function getParent(): \OCP\Files\Folder {
if ($this->parent === null) {
$newPath = dirname($this->path);
if ($newPath === '' || $newPath === '.' || $newPath === '/') {
@@ -286,8 +219,9 @@ class Node implements INode {
try {
$fileInfo = $this->getFileInfo();
} catch (NotFoundException) {
$this->parent = $this->root->get($newPath);
/** @var \OCP\Files\Folder $this->parent */
/** @var \OCP\Files\Folder $parent */
$parent = $this->root->get($newPath);
$this->parent = $parent;
return $this->parent;
}
@@ -306,102 +240,84 @@ class Node implements INode {
return $this->parent;
}
/**
* @return string
*/
public function getName() {
#[Override]
public function getName(): string {
return basename($this->path);
}
/**
* @param string $path
* @return string
*/
protected function normalizePath($path) {
protected function normalizePath(string $path): string {
return PathHelper::normalizePath($path);
}
/**
* check if the requested path is valid
*
* @param string $path
* @return bool
* Check if the requested path is valid.
*/
public function isValidPath($path) {
public function isValidPath(string $path): bool {
return Filesystem::isValidPath($path);
}
public function isMounted() {
#[Override]
public function isMounted(): bool {
return $this->getFileInfo(false)->isMounted();
}
public function isShared() {
#[Override]
public function isShared(): bool {
return $this->getFileInfo(false)->isShared();
}
#[Override]
public function getMimeType(): string {
return $this->getFileInfo(false)->getMimetype();
}
public function getMimePart() {
#[Override]
public function getMimePart(): string {
return $this->getFileInfo(false)->getMimePart();
}
public function getType() {
#[Override]
public function getType(): string {
return $this->getFileInfo(false)->getType();
}
public function isEncrypted() {
#[Override]
public function isEncrypted(): bool {
return $this->getFileInfo(false)->isEncrypted();
}
public function getMountPoint() {
#[Override]
public function getMountPoint(): IMountPoint {
return $this->getFileInfo(false)->getMountPoint();
}
public function getOwner() {
#[Override]
public function getOwner(): ?IUser {
return $this->getFileInfo(false)->getOwner();
}
public function getChecksum() {
}
#[Override]
public function getExtension(): string {
return $this->getFileInfo(false)->getExtension();
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @throws LockedException
*/
public function lock($type) {
#[Override]
public function lock(int $type): void {
$this->view->lockFile($this->path, $type);
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @throws LockedException
*/
public function changeLock($type) {
$this->view->changeLock($this->path, $type);
#[Override]
public function changeLock(int $targetType): void {
$this->view->changeLock($this->path, $targetType);
}
/**
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @throws LockedException
*/
public function unlock($type) {
#[Override]
public function unlock(int $type): void {
$this->view->unlockFile($this->path, $type);
}
/**
* @param string $targetPath
* @return INode
* @throws InvalidPathException
* @throws NotFoundException
* @throws NotPermittedException if copy not allowed or failed
*/
public function copy($targetPath) {
#[Override]
public function copy(string $targetPath): INode {
$targetPath = $this->normalizePath($targetPath);
$parent = $this->root->get(dirname($targetPath));
if ($parent instanceof Folder && $this->isValidPath($targetPath) && $parent->isCreatable()) {
@@ -420,16 +336,10 @@ class Node implements INode {
}
}
/**
* @param string $targetPath
* @return INode
* @throws InvalidPathException
* @throws NotFoundException
* @throws NotPermittedException if move not allowed or failed
* @throws LockedException
*/
public function move($targetPath) {
#[Override]
public function move(string $targetPath): INode {
$targetPath = $this->normalizePath($targetPath);
$parent = $this->root->get(dirname($targetPath));
if (
($parent instanceof Folder)
@@ -450,12 +360,10 @@ class Node implements INode {
}
$mountPoint = $this->getMountPoint();
if ($mountPoint) {
// update the cached fileinfo with the new (internal) path
/** @var \OC\Files\FileInfo $oldFileInfo */
$oldFileInfo = $this->getFileInfo();
$this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner());
}
// update the cached fileinfo with the new (internal) path
/** @var \OC\Files\FileInfo $oldFileInfo */
$oldFileInfo = $this->getFileInfo();
$this->fileInfo = new \OC\Files\FileInfo($targetPath, $oldFileInfo->getStorage(), $mountPoint->getInternalPath($targetPath), $oldFileInfo->getData(), $mountPoint, $oldFileInfo->getOwner());
$targetNode = $this->root->get($targetPath);
$this->sendHooks(['postRename'], [$this, $targetNode]);
@@ -467,23 +375,28 @@ class Node implements INode {
}
}
#[Override]
public function getCreationTime(): int {
return $this->getFileInfo()->getCreationTime();
}
#[Override]
public function getUploadTime(): int {
return $this->getFileInfo()->getUploadTime();
}
#[Override]
public function getParentId(): int {
return $this->fileInfo->getParentId();
}
/**
* @inheritDoc
* @return array<string, int|string|bool|float|string[]|int[]>
*/
#[Override]
public function getMetadata(): array {
return $this->fileInfo->getMetadata();
}
#[Override]
public function getChecksum(): string {
return $this->getFileInfo()->getChecksum();
}
}

View File

@@ -8,29 +8,31 @@
namespace OC\Files\Node;
use OCP\Files\NotFoundException;
use Override;
class NonExistingFile extends File {
/**
* @param string $newPath
* @throws \OCP\Files\NotFoundException
*/
public function rename($newPath) {
#[Override]
public function move(string $targetPath): \OCP\Files\Node {
throw new NotFoundException();
}
public function delete() {
#[Override]
public function delete(): void {
throw new NotFoundException();
}
public function copy($targetPath) {
#[Override]
public function copy(string $targetPath): \OCP\Files\Node {
throw new NotFoundException();
}
public function touch($mtime = null) {
#[Override]
public function touch(?int $mtime = null): void {
throw new NotFoundException();
}
public function getId() {
#[Override]
public function getId(): ?int {
if ($this->fileInfo) {
return parent::getId();
} else {
@@ -38,7 +40,8 @@ class NonExistingFile extends File {
}
}
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
if ($this->fileInfo) {
return parent::getInternalPath();
} else {
@@ -46,11 +49,13 @@ class NonExistingFile extends File {
}
}
public function stat() {
#[Override]
public function stat(): array {
throw new NotFoundException();
}
public function getMTime() {
#[Override]
public function getMTime(): int {
if ($this->fileInfo) {
return parent::getMTime();
} else {
@@ -58,7 +63,8 @@ class NonExistingFile extends File {
}
}
public function getSize($includeMounts = true): int|float {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
if ($this->fileInfo) {
return parent::getSize($includeMounts);
} else {
@@ -66,7 +72,8 @@ class NonExistingFile extends File {
}
}
public function getEtag() {
#[Override]
public function getEtag(): string {
if ($this->fileInfo) {
return parent::getEtag();
} else {
@@ -74,7 +81,8 @@ class NonExistingFile extends File {
}
}
public function getPermissions() {
#[Override]
public function getPermissions(): int {
if ($this->fileInfo) {
return parent::getPermissions();
} else {
@@ -82,7 +90,8 @@ class NonExistingFile extends File {
}
}
public function isReadable() {
#[Override]
public function isReadable(): bool {
if ($this->fileInfo) {
return parent::isReadable();
} else {
@@ -90,7 +99,8 @@ class NonExistingFile extends File {
}
}
public function isUpdateable() {
#[Override]
public function isUpdateable(): bool {
if ($this->fileInfo) {
return parent::isUpdateable();
} else {
@@ -98,7 +108,8 @@ class NonExistingFile extends File {
}
}
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
if ($this->fileInfo) {
return parent::isDeletable();
} else {
@@ -106,7 +117,8 @@ class NonExistingFile extends File {
}
}
public function isShareable() {
#[Override]
public function isShareable(): bool {
if ($this->fileInfo) {
return parent::isShareable();
} else {
@@ -114,14 +126,17 @@ class NonExistingFile extends File {
}
}
public function getContent() {
#[Override]
public function getContent(): string {
throw new NotFoundException();
}
public function putContent($data) {
#[Override]
public function putContent($data): void {
throw new NotFoundException();
}
#[Override]
public function getMimeType(): string {
if ($this->fileInfo) {
return parent::getMimeType();
@@ -130,6 +145,7 @@ class NonExistingFile extends File {
}
}
#[Override]
public function fopen($mode) {
throw new NotFoundException();
}

View File

@@ -8,29 +8,32 @@
namespace OC\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Files\Search\ISearchQuery;
use Override;
class NonExistingFolder extends Folder {
/**
* @param string $newPath
* @throws \OCP\Files\NotFoundException
*/
public function rename($newPath) {
#[Override]
public function move(string $targetPath): \OCP\Files\Node {
throw new NotFoundException();
}
public function delete() {
#[Override]
public function delete(): void {
throw new NotFoundException();
}
public function copy($targetPath) {
#[Override]
public function copy(string $targetPath): \OCP\Files\Node {
throw new NotFoundException();
}
public function touch($mtime = null) {
#[Override]
public function touch(?int $mtime = null): void {
throw new NotFoundException();
}
public function getId() {
#[Override]
public function getId(): ?int {
if ($this->fileInfo) {
return parent::getId();
} else {
@@ -38,7 +41,8 @@ class NonExistingFolder extends Folder {
}
}
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
if ($this->fileInfo) {
return parent::getInternalPath();
} else {
@@ -46,11 +50,13 @@ class NonExistingFolder extends Folder {
}
}
public function stat() {
#[Override]
public function stat(): array|false {
throw new NotFoundException();
}
public function getMTime() {
#[Override]
public function getMTime(): int {
if ($this->fileInfo) {
return parent::getMTime();
} else {
@@ -58,7 +64,8 @@ class NonExistingFolder extends Folder {
}
}
public function getSize($includeMounts = true): int|float {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
if ($this->fileInfo) {
return parent::getSize($includeMounts);
} else {
@@ -66,7 +73,8 @@ class NonExistingFolder extends Folder {
}
}
public function getEtag() {
#[Override]
public function getEtag(): string {
if ($this->fileInfo) {
return parent::getEtag();
} else {
@@ -74,7 +82,8 @@ class NonExistingFolder extends Folder {
}
}
public function getPermissions() {
#[Override]
public function getPermissions(): int {
if ($this->fileInfo) {
return parent::getPermissions();
} else {
@@ -82,7 +91,8 @@ class NonExistingFolder extends Folder {
}
}
public function isReadable() {
#[Override]
public function isReadable(): bool {
if ($this->fileInfo) {
return parent::isReadable();
} else {
@@ -90,7 +100,7 @@ class NonExistingFolder extends Folder {
}
}
public function isUpdateable() {
public function isUpdateable(): bool {
if ($this->fileInfo) {
return parent::isUpdateable();
} else {
@@ -98,7 +108,8 @@ class NonExistingFolder extends Folder {
}
}
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
if ($this->fileInfo) {
return parent::isDeletable();
} else {
@@ -106,7 +117,8 @@ class NonExistingFolder extends Folder {
}
}
public function isShareable() {
#[Override]
public function isShareable(): bool {
if ($this->fileInfo) {
return parent::isShareable();
} else {
@@ -114,55 +126,68 @@ class NonExistingFolder extends Folder {
}
}
public function get($path) {
#[Override]
public function get(string $path): \OCP\Files\Node {
throw new NotFoundException();
}
public function getDirectoryListing() {
#[Override]
public function getDirectoryListing(): array {
throw new NotFoundException();
}
public function nodeExists($path) {
#[Override]
public function nodeExists(string $path): bool {
return false;
}
public function newFolder($path) {
#[Override]
public function newFolder(string $path): Folder {
throw new NotFoundException();
}
public function newFile($path, $content = null) {
#[Override]
public function newFile(string $path, $content = null): File {
throw new NotFoundException();
}
public function search($query) {
#[Override]
public function search(string|ISearchQuery $query): array {
throw new NotFoundException();
}
public function searchByMime($mimetype) {
#[Override]
public function searchByMime(string $mimetype): array {
throw new NotFoundException();
}
public function searchByTag($tag, $userId) {
#[Override]
public function searchByTag(int|string $tag, string $userId): array {
throw new NotFoundException();
}
#[Override]
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array {
throw new NotFoundException();
}
public function getById($id) {
#[Override]
public function getById(int $id): array {
throw new NotFoundException();
}
#[Override]
public function getFirstNodeById(int $id): ?\OCP\Files\Node {
throw new NotFoundException();
}
public function getFreeSpace() {
#[Override]
public function getFreeSpace(): float|int|false {
throw new NotFoundException();
}
public function isCreatable() {
#[Override]
public function isCreatable(): bool {
if ($this->fileInfo) {
return parent::isCreatable();
} else {

View File

@@ -26,12 +26,14 @@ use OCP\Files\Mount\IMountPoint;
use OCP\Files\Node as INode;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\Storage\IStorage;
use OCP\IAppConfig;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Server;
use Override;
use Psr\Log\LoggerInterface;
/**
@@ -81,87 +83,42 @@ class Root extends Folder implements IRootFolder {
}
/**
* Get the user for which the filesystem is setup
*
* @return \OC\User\User
* @internal Only used in unit tests
*/
public function getUser() {
public function getUser(): ?IUser {
return $this->user;
}
/**
* @param string $scope
* @param string $method
* @param callable $callback
*/
#[Override]
public function listen($scope, $method, callable $callback) {
$this->emitter->listen($scope, $method, $callback);
}
/**
* @param string $scope optional
* @param string $method optional
* @param callable $callback optional
*/
#[Override]
public function removeListener($scope = null, $method = null, ?callable $callback = null) {
$this->emitter->removeListener($scope, $method, $callback);
}
/**
* @param string $scope
* @param string $method
* @param Node[] $arguments
*/
public function emit($scope, $method, $arguments = []) {
public function emit(string $scope, string $method, array $arguments = []) {
$this->emitter->emit($scope, $method, $arguments);
}
/**
* @param \OC\Files\Storage\Storage $storage
* @param string $mountPoint
* @param array $arguments
*/
public function mount($storage, $mountPoint, $arguments = []) {
public function mount(IStorage $storage, string $mountPoint, array $arguments = []) {
$mount = new MountPoint($storage, $mountPoint, $arguments);
$this->mountManager->addMount($mount);
}
#[Override]
public function getMount(string $mountPoint): IMountPoint {
return $this->mountManager->find($mountPoint);
}
/**
* @param string $mountPoint
* @return \OC\Files\Mount\MountPoint[]
*/
#[Override]
public function getMountsIn(string $mountPoint): array {
return $this->mountManager->findIn($mountPoint);
}
/**
* @param string $storageId
* @return \OC\Files\Mount\MountPoint[]
*/
public function getMountByStorageId($storageId) {
return $this->mountManager->findByStorageId($storageId);
}
/**
* @param int $numericId
* @return MountPoint[]
*/
public function getMountByNumericStorageId($numericId) {
return $this->mountManager->findByNumericId($numericId);
}
/**
* @param \OC\Files\Mount\MountPoint $mount
*/
public function unMount($mount) {
$this->mountManager->remove($mount);
}
public function get($path) {
public function get(string $path): \OCP\Files\Node {
$path = $this->normalizePath($path);
if ($this->isValidPath($path)) {
$fullPath = $this->getFullPath($path);
@@ -176,154 +133,105 @@ class Root extends Folder implements IRootFolder {
}
}
//most operations can't be done on the root
// most operations can't be done on the root
/**
* @param string $targetPath
* @return Node
* @throws \OCP\Files\NotPermittedException
*/
public function rename($targetPath) {
#[Override]
public function move(string $targetPath): \OCP\Files\Node {
throw new NotPermittedException();
}
public function delete() {
#[Override]
public function delete(): void {
throw new NotPermittedException();
}
/**
* @param string $targetPath
* @return Node
* @throws \OCP\Files\NotPermittedException
*/
public function copy($targetPath) {
#[Override]
public function copy(string $targetPath): \OCP\Files\Node {
throw new NotPermittedException();
}
/**
* @param int $mtime
* @throws \OCP\Files\NotPermittedException
*/
public function touch($mtime = null) {
#[Override]
public function touch(?int $mtime = null): void {
throw new NotPermittedException();
}
/**
* @return \OC\Files\Storage\Storage
* @throws \OCP\Files\NotFoundException
*/
public function getStorage() {
#[Override]
public function getStorage(): IStorage {
throw new NotFoundException();
}
/**
* @return string
*/
public function getPath() {
#[Override]
public function getPath(): string {
return '/';
}
/**
* @return string
*/
public function getInternalPath() {
#[Override]
public function getInternalPath(): string {
return '';
}
/**
* @return int
*/
public function getId() {
#[Override]
public function getId(): ?int {
return 0;
}
/**
* @return array
*/
public function stat() {
#[Override]
public function stat(): array {
return [];
}
/**
* @return int
*/
public function getMTime() {
#[Override]
public function getMTime(): int {
return 0;
}
/**
* @param bool $includeMounts
* @return int|float
*/
public function getSize($includeMounts = true): int|float {
#[Override]
public function getSize(bool $includeMounts = true): int|float {
return 0;
}
/**
* @return string
*/
public function getEtag() {
#[Override]
public function getEtag(): string {
return '';
}
/**
* @return int
*/
public function getPermissions() {
#[Override]
public function getPermissions(): int {
return \OCP\Constants::PERMISSION_CREATE;
}
/**
* @return bool
*/
public function isReadable() {
#[Override]
public function isReadable(): bool {
return false;
}
/**
* @return bool
*/
public function isUpdateable() {
#[Override]
public function isUpdateable(): bool {
return false;
}
/**
* @return bool
*/
public function isDeletable() {
#[Override]
public function isDeletable(): bool {
return false;
}
/**
* @return bool
*/
public function isShareable() {
#[Override]
public function isShareable(): bool {
return false;
}
/**
* @throws \OCP\Files\NotFoundException
*/
public function getParent(): INode|IRootFolder {
#[Override]
public function getParent(): \OCP\Files\Folder {
throw new NotFoundException();
}
/**
* @return string
*/
public function getName() {
#[Override]
public function getName(): string {
return '';
}
/**
* Returns a view to user's files folder
*
* @param string $userId user ID
* @return \OCP\Files\Folder
* @throws NoUserException
* @throws NotPermittedException
*/
public function getUserFolder($userId) {
#[Override]
public function getUserFolder(string $userId): \OCP\Files\Folder {
$userObject = $this->userManager->get($userId);
if (is_null($userObject)) {
@@ -366,10 +274,11 @@ class Root extends Folder implements IRootFolder {
return $this->userFolderCache->get($userId);
}
public function getUserMountCache() {
public function getUserMountCache(): IUserMountCache {
return $this->userMountCache;
}
#[Override]
public function getFirstNodeByIdInPath(int $id, string $path): ?INode {
// scope the cache by user, so we don't return nodes for different users
if ($this->user) {
@@ -380,7 +289,7 @@ class Root extends Folder implements IRootFolder {
$node = $this->get($cachedPath);
// by validating that the cached path still has the requested fileid we can work around the need to invalidate the cached path
// if the cached path is invalid or a different file now we fall back to the uncached logic
if ($node && $node->getId() === $id) {
if ($node->getId() === $id) {
return $node;
}
} catch (NotFoundException|NotPermittedException) {
@@ -399,10 +308,7 @@ class Root extends Folder implements IRootFolder {
return $node;
}
/**
* @param int $id
* @return Node[]
*/
#[Override]
public function getByIdInPath(int $id, string $path): array {
$mountCache = $this->getUserMountCache();
if ($path !== '' && strpos($path, '/', 1) > 0) {
@@ -497,6 +403,7 @@ class Root extends Folder implements IRootFolder {
return $folders;
}
#[Override]
public function getNodeFromCacheEntryAndMount(ICacheEntry $cacheEntry, IMountPoint $mountPoint): INode {
$path = $cacheEntry->getPath();
$fullPath = $mountPoint->getMountPoint() . $path;

View File

@@ -14,6 +14,7 @@ use OCP\Files\Folder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use Override;
class NewSimpleFile implements ISimpleFile {
private Folder $parentFolder;
@@ -68,21 +69,10 @@ class NewSimpleFile implements ISimpleFile {
}
}
/**
* Get the content
*
* @throws NotFoundException
* @throws NotPermittedException
*/
#[Override]
public function getContent(): string {
if ($this->file) {
$result = $this->file->getContent();
if ($result === false) {
$this->checkFile();
}
return $result;
return $this->file->getContent();
} else {
return '';
}

View File

@@ -12,6 +12,7 @@ use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\Lock\LockedException;
use Override;
class SimpleFile implements ISimpleFile {
private File $file;
@@ -48,22 +49,9 @@ class SimpleFile implements ISimpleFile {
return $this->file->getMTime();
}
/**
* Get the content
*
* @throws GenericFileException
* @throws LockedException
* @throws NotFoundException
* @throws NotPermittedException
*/
#[Override]
public function getContent(): string {
$result = $this->file->getContent();
if ($result === false) {
$this->checkFile();
}
return $result;
return $this->file->getContent();
}
/**
@@ -158,6 +146,6 @@ class SimpleFile implements ISimpleFile {
}
public function getId(): int {
return $this->file->getId();
return $this->file->getId() ?? -1;
}
}

View File

@@ -29,10 +29,6 @@ class PathHelper {
}
}
/**
* @param string $path
* @return string
*/
public static function normalizePath(string $path): string {
if ($path === '' || $path === '/') {
return '/';

View File

@@ -338,7 +338,7 @@ class View {
* @param string $path
* @return mixed
*/
public function stat($path) {
public function stat($path): array|false {
return $this->basicOperation('stat', $path);
}
@@ -1119,12 +1119,7 @@ class View {
return $this->basicOperation('getMimeType', $path);
}
/**
* @param string $type
* @param string $path
* @param bool $raw
*/
public function hash($type, $path, $raw = false): string|bool {
public function hash(string $type, string $path, bool $raw = false): string|false {
$postFix = (substr($path, -1) === '/') ? '/' : '';
$absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path));
if (Filesystem::isValidPath($path)) {
@@ -1149,11 +1144,9 @@ class View {
}
/**
* @param string $path
* @return mixed
* @throws InvalidPathException
*/
public function free_space($path = '/') {
public function free_space(string $path = '/'): float|int|false {
$this->assertPathLength($path);
$result = $this->basicOperation('free_space', $path);
if ($result === null) {

View File

@@ -97,7 +97,8 @@ class Generator {
*/
public function generatePreviews(File $file, array $specifications, ?string $mimeType = null, bool $cacheResult = true): ISimpleFile {
//Make sure that we can read the file
if (!$file->isReadable()) {
$id = $file->getId();
if ($id === null || !$file->isReadable()) {
$this->logger->warning('Cannot read file: {path}, skipping preview generation.', ['path' => $file->getPath()]);
throw new NotFoundException('Cannot read file');
}
@@ -106,7 +107,7 @@ class Generator {
$mimeType = $file->getMimeType();
}
[$file->getId() => $previews] = $this->previewMapper->getAvailablePreviews([$file->getId()]);
[$id => $previews] = $this->previewMapper->getAvailablePreviews([$id]);
$previewVersion = null;
if ($file instanceof IVersionedPreviewFile) {

View File

@@ -216,7 +216,7 @@ class PreviewManager implements IPreview {
}
$mount = $file->getMountPoint();
if ($mount && !$mount->getOption('previews', true)) {
if (!$mount->getOption('previews', true)) {
return false;
}

View File

@@ -653,9 +653,7 @@ class DefaultShareProvider implements
$childMountNodes = array_filter($node->getDirectoryListing(), function (Node $node): bool {
return $node->getInternalPath() === '';
});
$childMountRootIds = array_map(function (Node $node): int {
return $node->getId();
}, $childMountNodes);
$childMountRootIds = array_map(fn (Node $node): int => $node->getId() ?? -1, $childMountNodes);
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
$qb->andWhere(

View File

@@ -123,7 +123,7 @@ class SpeechToTextManager implements ISpeechToTextManager {
if (isset($this->taskProcessingManager->getAvailableTaskTypes()['core:audio2text'])) {
$taskProcessingTask = new Task(
AudioToText::ID,
['input' => $file->getId()],
['input' => $file->getId() ?? -1],
$appId,
$userId,
'from-SpeechToTextManager||' . $file->getId() . '||' . ($userId ?? '') . '||' . $appId,

View File

@@ -1,12 +1,12 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal Nextcloud classes
namespace OCP\Files;
@@ -14,22 +14,21 @@ use OCP\AppFramework\Attribute\Consumable;
use OCP\Lock\LockedException;
/**
* Interface File
* Represents a file, which is a leaf node in a hierarchical structure.
*
* @since 6.0.0
*/
#[Consumable(since: '6.0.0')]
interface File extends Node {
/**
* Get the content of the file as string
* Get the content of the file as string.
*
* @return string
* @throws NotPermittedException
* @throws GenericFileException
* @throws LockedException
* @since 6.0.0
*/
public function getContent();
public function getContent(): string;
/**
* Write to the file from string data
@@ -40,51 +39,39 @@ interface File extends Node {
* @throws LockedException
* @since 6.0.0
*/
public function putContent($data);
/**
* Get the mimetype of the file
*
* @since 6.0.0
*/
public function getMimeType(): string;
public function putContent($data): void;
/**
* Open the file as stream, resulting resource can be operated as stream like the result from php's own fopen
*
* @param string $mode
* @return resource|false
* @throws NotPermittedException
* @throws LockedException
* @since 6.0.0
*/
public function fopen($mode);
public function fopen(string $mode);
/**
* Compute the hash of the file
* Compute the hash of the file.
*
* Type of hash is set with $type and can be anything supported by php's hash_file
*
* @param string $type
* @param bool $raw
* @return string
* @since 6.0.0
*/
public function hash($type, $raw = false);
public function hash(string $type, bool $raw = false): string;
/**
* Get the stored checksum for this file
* Get the stored checksum for this file,
*
* @return string
* @since 9.0.0
* @throws InvalidPathException
* @throws NotFoundException
*/
public function getChecksum();
public function getChecksum(): string;
/**
* Get the extension of this file
* Get the extension of this file.
*
* @return string
* @since 15.0.0
*/
public function getExtension(): string;

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -8,7 +10,10 @@
namespace OCP\Files;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Constants;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\IUser;
/**
* Interface FileInfo
@@ -54,53 +59,55 @@ interface FileInfo {
public const BLACKLIST_FILES_REGEX = '\.(part|filepart)$';
/**
* Get the Etag of the file or folder
* Get the Etag of the file or folder.
*
* @return string
* The Etag is a string id used to detect changes to a file or folder,
* every time the file or folder is changed the Etag will change too.
*
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0
*/
public function getEtag();
public function getEtag(): string;
/**
* Get the size in bytes for the file or folder
* Get the size of the file or folder in bytes.
*
* @param bool $includeMounts whether or not to include the size of any sub mounts, since 16.0.0
* @return int|float
* @since 7.0.0
*/
public function getSize($includeMounts = true);
public function getSize(bool $includeMounts = true): int|float;
/**
* Get the last modified date as timestamp for the file or folder
* Get the modified date of the file or folder as unix timestamp.
*
* @throws InvalidPathException
* @throws NotFoundException
*
* @return int
* @since 7.0.0
*/
public function getMtime();
public function getMtime(): int;
/**
* Get the name of the file or folder
* Get the name of the file or folder.
*
* @return string
* @since 7.0.0
*/
public function getName();
public function getName(): string;
/**
* Get the path relative to the storage
* Get the path of the file or folder relative to the mountpoint of its storage.
*
* @return string
* @since 7.0.0
*/
public function getInternalPath();
public function getInternalPath(): string;
/**
* Get the absolute path
* Get the full path of the file or folder.
*
* @return string
* @since 7.0.0
*/
public function getPath();
public function getPath(): string;
/**
* Get the full mimetype of the file or folder i.e. 'image/png'
@@ -112,141 +119,147 @@ interface FileInfo {
/**
* Get the first part of the mimetype of the file or folder i.e. 'image'
*
* @return string
* @since 7.0.0
*/
public function getMimePart();
public function getMimePart(): string;
/**
* Get the storage the file or folder is storage on
* Get the storage the file or folder is storage on.
*
* @return IStorage
* @throws NotFoundException
* @since 7.0.0
*/
public function getStorage();
public function getStorage(): IStorage;
/**
* Get the file id of the file or folder
* Get the internal file id for the file or folder.
*
* @return int|null
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0
*/
public function getId();
public function getId(): ?int;
/**
* Check whether the node is encrypted.
*
* If it is a file, then it is server side encrypted.
* If it is a folder, then it is end-to-end encrypted.
*
* @return bool
* @since 7.0.0
*/
public function isEncrypted();
public function isEncrypted(): bool;
/**
* Get the permissions of the file or folder as bitmasked combination of the following constants
* \OCP\Constants::PERMISSION_CREATE
* \OCP\Constants::PERMISSION_READ
* \OCP\Constants::PERMISSION_UPDATE
* \OCP\Constants::PERMISSION_DELETE
* \OCP\Constants::PERMISSION_SHARE
* \OCP\Constants::PERMISSION_ALL
* Get the permissions of the file or folder as bit-masked combination of the
* following constants.
*
* @return int
* Constants::PERMISSION_CREATE
* Constants::PERMISSION_READ
* Constants::PERMISSION_UPDATE
* Constants::PERMISSION_DELETE
* Constants::PERMISSION_SHARE
* Constants::PERMISSION_ALL
*
* @return int-mask-of<Constants::PERMISSION_*>
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0 - namespace of constants has changed in 8.0.0
*/
public function getPermissions();
public function getPermissions(): int;
/**
* Check whether this is a file or a folder
*
* @return string \OCP\Files\FileInfo::TYPE_FILE|\OCP\Files\FileInfo::TYPE_FOLDER
* @return FileInfo::TYPE_FILE|FileInfo::TYPE_FOLDER
* @since 7.0.0
*/
public function getType();
public function getType(): string;
/**
* Check if the file or folder is readable
* Check if the file or folder is readable.
*
* @return bool
* @throws NotFoundException
* @throws InvalidPathException
* @since 7.0.0
*/
public function isReadable();
public function isReadable(): bool;
/**
* Check if a file is writable
* Check if the file or folder is writable.
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0
*/
public function isUpdateable();
public function isUpdateable(): bool;
/**
* Check whether new files or folders can be created inside this folder
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 8.0.0
*/
public function isCreatable();
public function isCreatable(): bool;
/**
* Check if a file or folder can be deleted
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0
*/
public function isDeletable();
public function isDeletable(): bool;
/**
* Check if a file or folder can be shared
* Check if the file or folder is shareable.
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 7.0.0
*/
public function isShareable();
public function isShareable(): bool;
/**
* Check if a file or folder is shared
* Check if a file or folder is shared.
*
* @return bool
* @since 7.0.0
*/
public function isShared();
public function isShared(): bool;
/**
* Check if a file or folder is mounted
*
* @return bool
* @since 7.0.0
*/
public function isMounted();
public function isMounted(): bool;
/**
* Get the mountpoint the file belongs to
* Get the mountpoint the file belongs to.
*
* @return \OCP\Files\Mount\IMountPoint
* @since 8.0.0
*/
public function getMountPoint();
public function getMountPoint(): IMountPoint;
/**
* Get the owner of the file
* Get the owner of the file.
*
* @return ?\OCP\IUser
* @since 9.0.0
*/
public function getOwner();
public function getOwner(): ?IUser;
/**
* Get the stored checksum(s) for this file
* Get the stored checksum(s) for this file.
*
* Checksums are stored in the format TYPE:CHECKSUM, here may be multiple checksums separated by a single space
* e.g. MD5:d3b07384d113edec49eaa6238ad5ff00 SHA1:f1d2d2f924e986ac86fdf7b36c94bcdf32beec15
*
* @return string
* @note This will return an empty string if no checksum is currently stored.
*
* @since 9.0.0
*/
public function getChecksum();
public function getChecksum(): string;
/**
* Get the extension of the file
@@ -275,7 +288,7 @@ interface FileInfo {
* If the upload time is not known, 0 will be returned
*
* Upload time will be set automatically by the server for files uploaded over DAV
* files created by Nextcloud apps generally do not have an the upload time set
* files created by Nextcloud apps generally do not have the upload time set.
*
* @return int
* @since 18.0.0
@@ -283,16 +296,15 @@ interface FileInfo {
public function getUploadTime(): int;
/**
* Get the fileid or the parent folder
* or -1 if this item has no parent folder (because it is the root)
* Get the fileId or the parent folder or -1 if this item has no parent folder
* (because it is the root).
*
* @return int
* @since 28.0.0
*/
public function getParentId(): int;
/**
* Get the metadata, if available
* Get the metadata, if available.
*
* @return array<string, int|string|bool|float|string[]|int[]>
* @since 28.0.0

View File

@@ -10,60 +10,63 @@
namespace OCP\Files;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Files\Search\ISearchQuery;
/**
* Folder interface.
*
* Represents a container node that can hold files, subfolders,
* or other nodes in a hierarchical structure.
*
* @since 6.0.0
*/
#[Consumable(since: '6.0.0')]
interface Folder extends Node {
/**
* Get the full path of an item in the folder within owncloud's filesystem
*
* @param string $path relative path of an item in the folder
* @return string
* @throws \OCP\Files\NotPermittedException
* @throws NotPermittedException
* @since 6.0.0
*/
public function getFullPath($path);
public function getFullPath(string $path): string;
/**
* Get the path of an item in the folder relative to the folder
*
* @param string $path absolute path of an item in the folder
* @throws \OCP\Files\NotFoundException
* @return string|null
* @throws NotFoundException
* @since 6.0.0
*/
public function getRelativePath($path);
public function getRelativePath(string $path): ?string;
/**
* check if a node is a (grand-)child of the folder
* Check if a node is a (grand-)child of the folder.
*
* @param \OCP\Files\Node $node
* @return bool
* @since 6.0.0
*/
public function isSubNode($node);
public function isSubNode(Node $node): bool;
/**
* get the content of this directory
* Get the content of this directory.
*
* @throws \OCP\Files\NotFoundException
* @return \OCP\Files\Node[]
* @return Node[]
* @throws NotFoundException
* @since 6.0.0
*/
public function getDirectoryListing();
public function getDirectoryListing(): array;
/**
* Get the node at $path
* Get the node at $path.
*
* @param string $path relative path of the file or folder
* @return \OCP\Files\Node
* @throws \OCP\Files\NotFoundException
* @throws \OCP\Files\NotPermittedException
* @throws NotFoundException
* @throws NotPermittedException
* @since 6.0.0
*/
public function get($path);
public function get(string $path): Node;
/**
* Get or create new folder if the folder does not already exist.
@@ -78,87 +81,81 @@ interface Folder extends Node {
* Check if a file or folder exists in the folder
*
* @param string $path relative path of the file or folder
* @return bool
* @since 6.0.0
*/
public function nodeExists($path);
public function nodeExists(string $path): bool;
/**
* Create a new folder
*
* @param string $path relative path of the new folder
* @return \OCP\Files\Folder
* @throws \OCP\Files\NotPermittedException
* @throws NotPermittedException
* @since 6.0.0
*/
public function newFolder($path);
public function newFolder(string $path): Folder;
/**
* Create a new file
*
* @param string $path relative path of the new file
* @param string|resource|null $content content for the new file, since 19.0.0
* @return \OCP\Files\File
* @throws \OCP\Files\NotPermittedException
* @throws NotPermittedException
* @since 6.0.0
*/
public function newFile($path, $content = null);
public function newFile(string $path, $content = null): File;
/**
* search for files with the name matching $query
* Search for files with the name matching $query.
*
* @param string|ISearchQuery $query
* @return \OCP\Files\Node[]
* @return Node[]
* @since 6.0.0
*/
public function search($query);
public function search(string|ISearchQuery $query): array;
/**
* search for files by mimetype
* $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image)
* Search for files by mimetype.
*
* @param string $mimetype
* @return \OCP\Files\Node[]
* @param string $mimetype can either be a full mimetype (image/png) or a wildcard mimetype (image)
* @return Node[]
* @since 6.0.0
*/
public function searchByMime($mimetype);
public function searchByMime(string $mimetype): array;
/**
* search for files by tag
* Search for files by tag.
*
* @param string|int $tag tag name or tag id
* @param string $userId owner of the tags
* @return \OCP\Files\Node[]
* @return Node[]
* @since 8.0.0
*/
public function searchByTag($tag, $userId);
public function searchByTag(string|int $tag, string $userId): array;
/**
* search for files by system tag
* Search for files by system tag.
*
* @param string|int $tag tag name
* @param string $tag tag name
* @param string $userId user id to ensure access on returned nodes
* @return \OCP\Files\Node[]
* @return Node[]
* @since 28.0.0
*/
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0);
public function searchBySystemTag(string $tagName, string $userId, int $limit = 0, int $offset = 0): array;
/**
* get a file or folder inside the folder by its internal id
* Get a file or folder inside the folder by its internal id.
*
* This method could return multiple entries. For example once the file/folder
* is shared or mounted (files_external) to the user multiple times.
*
* Note that the different entries can have different permissions.
*
* @param int $id
* @return \OCP\Files\Node[]
* @return Node[]
* @since 6.0.0
*/
public function getById($id);
public function getById(int $id): array;
/**
* get a file or folder inside the folder by its internal id
* Get a file or folder inside the folder by its internal id.
*
* Unlike getById, this method only returns a single node even if the user has
* access to the file with the requested id multiple times.
@@ -169,54 +166,47 @@ interface Folder extends Node {
* Apps that require accurate information about the users access to the file should use getById
* instead of pick the correct node out of the result.
*
* @param int $id
* @return Node|null
* @since 29.0.0
*/
public function getFirstNodeById(int $id): ?Node;
/**
* Get the amount of free space inside the folder
* Get the amount of free space inside the folder.
*
* @return int
* @since 6.0.0
*/
public function getFreeSpace();
public function getFreeSpace(): int|float|false;
/**
* Check if new files or folders can be created within the folder
* Check if new files or folders can be created within the folder.
*
* @return bool
* @since 6.0.0
*/
public function isCreatable();
public function isCreatable(): bool;
/**
* Add a suffix to the name in case the file exists
* Add a suffix to the name in case the file exists.
*
* @param string $filename
* @return string
* @throws NotPermittedException
* @since 8.1.0
*/
public function getNonExistingName($filename);
public function getNonExistingName(string $name): string;
/**
* @param int $limit
* @param int $offset
* @return \OCP\Files\Node[]
* Get recent files and folders.
*
* @return Node[]
* @since 9.1.0
*/
public function getRecent($limit, $offset = 0);
public function getRecent(int $limit, int $offset = 0): array;
/**
* Verify if the given path is valid and allowed from this folder.
* Verify if the given fileName is valid and allowed from this folder.
*
* @param string $path the path from this folder
* @param string $fileName
* @param bool $readonly Check only if the path is allowed for read-only access
* @throws InvalidPathException
* @since 32.0.0
*/
public function verifyPath($fileName, $readonly = false): void;
public function verifyPath(string $fileName, bool $readonly = false): void;
}

View File

@@ -1,5 +1,7 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -16,23 +18,36 @@ use OCP\Files\Node as INode;
/**
* Interface IRootFolder
*
* Hooks available in scope \OC\Files. These are deprecated.
* - preWrite(\OCP\Files\Node $node)
* - postWrite(\OCP\Files\Node $node)
* - preCreate(\OCP\Files\Node $node)
* - postCreate(\OCP\Files\Node $node)
* - preDelete(\OCP\Files\Node $node)
* - postDelete(\OCP\Files\Node $node)
* - preTouch(\OC\FilesP\Node $node, int $mtime)
* - postTouch(\OCP\Files\Node $node)
* - preCopy(\OCP\Files\Node $source, \OCP\Files\Node $target)
* - postCopy(\OCP\Files\Node $source, \OCP\Files\Node $target)
* - preRename(\OCP\Files\Node $source, \OCP\Files\Node $target)
* - postRename(\OCP\Files\Node $source, \OCP\Files\Node $target)
*
* @since 8.0.0
*/
interface IRootFolder extends Folder, Emitter {
/**
* Returns a view to user's files folder
* Returns a view to user's files folder.
*
* @param string $userId user ID
* @return Folder
* @throws NoUserException
* @throws NotPermittedException
*
* @since 8.2.0
*/
public function getUserFolder($userId);
public function getUserFolder(string $userId): Folder;
/**
* Get a file or folder by fileid, inside a parent path
* Get a file or folder by fileId, inside a parent path.
*
* @param int $id
* @param string $path
@@ -40,10 +55,10 @@ interface IRootFolder extends Folder, Emitter {
*
* @since 24.0.0
*/
public function getByIdInPath(int $id, string $path);
public function getByIdInPath(int $id, string $path): array;
/**
* get a file or folder inside the folder by its internal id
* Get a file or folder inside the folder by its internal id.
*
* Unlike getByIdInPath, this method only returns a single node even if the user has
* access to the file with the requested id multiple times.
@@ -54,8 +69,6 @@ interface IRootFolder extends Folder, Emitter {
* Apps that require accurate information about the users access to the file should use getByIdInPath
* instead of pick the correct node out of the result.
*
* @param int $id
* @return Node|null
* @since 29.0.0
*/
public function getFirstNodeByIdInPath(int $id, string $path): ?Node;
@@ -68,7 +81,7 @@ interface IRootFolder extends Folder, Emitter {
public function getMountsIn(string $mountPoint): array;
/**
* Create a `Node` for a file or folder from the cache entry and mountpoint
* Create a `Node` for a file or folder from the cache entry and mountpoint.
*
* @param ICacheEntry $cacheEntry
* @param IMountPoint $mountPoint

View File

@@ -1,56 +1,58 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal Nextcloud classes
namespace OCP\Files;
use OCP\Files\Storage\IStorage;
use OCP\AppFramework\Attribute\Consumable;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
/**
* Interface Node
*
* Represents a generic node in a hierarchical structure. This can be either
* a \OCP\Files\Folder or \OCP\Files\File.
*
* @since 6.0.0 - extends FileInfo was added in 8.0.0
*/
#[Consumable(since: '6.0.0')]
interface Node extends FileInfo {
/**
* Move the file or folder to a new location
* Move the file or folder to a new location.
*
* @param string $targetPath the absolute target path
* @return Node
* @throws NotFoundException
* @throws NotPermittedException if move not allowed or failed
* @throws LockedException
* @throws InvalidPathException
* @since 6.0.0
*/
public function move($targetPath);
public function move(string $targetPath): Node;
/**
* Delete the file or folder
*
* @return void
* @throws NotPermittedException
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function delete();
public function delete(): void;
/**
* Copy the file or folder to a new location
*
* @param string $targetPath the absolute target path
* @return Node
* @since 6.0.0
*/
public function copy($targetPath);
public function copy(string $targetPath): Node;
/**
* Change the modified date of the file or folder
@@ -63,158 +65,25 @@ interface Node extends FileInfo {
* @return void
* @since 6.0.0
*/
public function touch($mtime = null);
public function touch(?int $mtime = null): void;
/**
* Get the storage backend the file or folder is stored on
* Get metadata of the file or folder.
*
* @return IStorage
* @throws NotFoundException
* @since 6.0.0
*/
public function getStorage();
/**
* Get the full path of the file or folder
*
* @return string
* @since 6.0.0
*/
public function getPath();
/**
* Get the path of the file or folder relative to the mountpoint of it's storage
*
* @return string
* @since 6.0.0
*/
public function getInternalPath();
/**
* Get the internal file id for the file or folder
*
* @return int
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function getId();
/**
* Get metadata of the file or folder
* The returned array contains the following values:
* - mtime
* - size
*
* @return array
* @since 6.0.0
*/
public function stat();
public function stat(): array|false;
/**
* Get the modified date of the file or folder as unix timestamp
* Get the parent folder of the file or folder.
*
* @return int
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function getMTime();
/**
* Get the size of the file or folder in bytes
*
* @param bool $includeMounts
* @return int|float
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function getSize($includeMounts = true);
/**
* Get the Etag of the file or folder
* The Etag is an string id used to detect changes to a file or folder,
* every time the file or folder is changed the Etag will change to
*
* @return string
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function getEtag();
/**
* Get the permissions of the file or folder as a combination of one or more of the following constants:
* - \OCP\Constants::PERMISSION_READ
* - \OCP\Constants::PERMISSION_UPDATE
* - \OCP\Constants::PERMISSION_CREATE
* - \OCP\Constants::PERMISSION_DELETE
* - \OCP\Constants::PERMISSION_SHARE
*
* @return int
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0 - namespace of constants has changed in 8.0.0
*/
public function getPermissions();
/**
* Check if the file or folder is readable
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function isReadable();
/**
* Check if the file or folder is writable
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function isUpdateable();
/**
* Check if the file or folder is deletable
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function isDeletable();
/**
* Check if the file or folder is shareable
*
* @return bool
* @throws InvalidPathException
* @throws NotFoundException
* @since 6.0.0
*/
public function isShareable();
/**
* Get the parent folder of the file or folder
*
* @return Folder
* @since 6.0.0
*/
public function getParent();
/**
* Get the filename of the file or folder
*
* @return string
* @since 6.0.0
*/
public function getName();
public function getParent(): Folder;
/**
* Acquire a lock on this file or folder.
@@ -233,11 +102,11 @@ interface Node extends FileInfo {
* Note that in most cases you won't need to manually manage the locks for any files you're working with,
* any filesystem operation will automatically acquire the relevant locks for that operation.
*
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_SHARED|ILockingProvider::LOCK_EXCLUSIVE $type
* @throws LockedException
* @since 9.1.0
*/
public function lock($type);
public function lock(int $type): void;
/**
* Check the type of an existing lock.
@@ -248,11 +117,11 @@ interface Node extends FileInfo {
* A locked exception will be thrown when these preconditions are not met.
* Note that this is also the case if no existing lock exists for the file.
*
* @param int $targetType \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_SHARED|ILockingProvider::LOCK_EXCLUSIVE $targetType
* @throws LockedException
* @since 9.1.0
*/
public function changeLock($targetType);
public function changeLock(int $targetType): void;
/**
* Release an existing lock.
@@ -261,9 +130,9 @@ interface Node extends FileInfo {
*
* Note that this method will not give any sort of error when trying to free a lock that doesn't exist.
*
* @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE
* @param ILockingProvider::LOCK_SHARED|ILockingProvider::LOCK_EXCLUSIVE $type
* @throws LockedException
* @since 9.1.0
*/
public function unlock($type);
public function unlock(int $type): void;
}

View File

@@ -90,7 +90,7 @@ final class Template implements \JsonSerializable {
'templateId' => $this->templateId,
'basename' => $this->file->getName(),
'etag' => $this->file->getEtag(),
'fileid' => $this->file->getId(),
'fileid' => $this->file->getId() ?? -1,
'filename' => $this->templateId,
'lastmod' => $this->file->getMTime(),
'mime' => $this->file->getMimetype(),

View File

@@ -257,10 +257,10 @@ interface IManager {
* @param bool $currentAccess Should the user have currently access to the file
* @return ($currentAccess is true
* ? array{
* users?: array<string, array{node_id: int, node_path: string}>,
* remote?: array<string, array{node_id: int, node_path: string}>,
* users?: array<string, array{node_id: ?int, node_path: string}>,
* remote?: array<string, array{node_id: ?int, node_path: string}>,
* public?: bool,
* mail?: array<string, array{node_id: int, node_path: string}>
* mail?: array<string, array{node_id: ?int, node_path: string}>
* }
* : array{users?: list<string>, remote?: bool, public?: bool, mail?: list<string>})
* @since 12.0.0

View File

@@ -15,6 +15,7 @@ use OC\Encryption\Exceptions\DecryptionFailedException;
use OC\Encryption\Manager;
use OC\Files\FileInfo;
use OC\Files\View;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorage;
use OCP\IUserManager;
use OCP\UserInterface;
@@ -250,15 +251,15 @@ class DecryptAllTest extends TestCase {
[
'/user1/files', '', null,
[
new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], null),
new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], null),
new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], null),
new FileInfo('path', $storage, 'intPath', ['name' => 'foo', 'type' => 'dir'], $this->createMock(IMountPoint::class)),
new FileInfo('path', $storage, 'intPath', ['name' => 'bar', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class)),
new FileInfo('path', $sharedStorage, 'intPath', ['name' => 'shared', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class)),
],
],
[
'/user1/files/foo', '', null,
[
new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], null)
new FileInfo('path', $storage, 'intPath', ['name' => 'subfile', 'type' => 'file', 'encrypted' => true], $this->createMock(IMountPoint::class))
],
],
]);

View File

@@ -15,6 +15,7 @@ use OC\Files\View;
use OCP\Constants;
use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
use OCP\Files\NotPermittedException;
use OCP\Files\Storage\IStorage;
use PHPUnit\Framework\MockObject\MockObject;

View File

@@ -85,8 +85,8 @@ class FolderTest extends NodeTestCase {
->method('getDirectoryContent')
->with('/bar/foo')
->willReturn([
new FileInfo('/bar/foo/asd', null, 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], null),
new FileInfo('/bar/foo/qwerty', null, 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], null),
new FileInfo('/bar/foo/asd', $this->createMock(IStorage::class), 'foo/asd', ['fileid' => 2, 'path' => '/bar/foo/asd', 'name' => 'asd', 'size' => 100, 'mtime' => 50, 'mimetype' => 'text/plain'], $this->createMock(IMountPoint::class)),
new FileInfo('/bar/foo/qwerty', $this->createMock(IStorage::class), 'foo/qwerty', ['fileid' => 3, 'path' => '/bar/foo/qwerty', 'name' => 'qwerty', 'size' => 200, 'mtime' => 55, 'mimetype' => 'httpd/unix-directory'], $this->createMock(IMountPoint::class)),
]);
$this->view->method('getFileInfo')
->willReturn($this->createMock(FileInfo::class));
@@ -489,12 +489,12 @@ class FolderTest extends NodeTestCase {
public function testIsSubNode(): void {
$rootFolderMock = $this->createMock(IRootFolder::class);
$file = new Node($rootFolderMock, $this->view, '/foo/bar');
$file = new File($rootFolderMock, $this->view, '/foo/bar');
$folder = new Folder($rootFolderMock, $this->view, '/foo');
$this->assertTrue($folder->isSubNode($file));
$this->assertFalse($folder->isSubNode($folder));
$file = new Node($rootFolderMock, $this->view, '/foobar');
$file = new File($rootFolderMock, $this->view, '/foobar');
$this->assertFalse($folder->isSubNode($file));
}

View File

@@ -119,10 +119,12 @@ abstract class NodeTestCase extends \Test\TestCase {
return $storage;
}
protected function getFileInfo($data, $internalPath = '', ?IStorage $storage = null) {
protected function getFileInfo(array $data, string $internalPath = '', ?IStorage $storage = null): FileInfo {
$mount = $this->createMock(IMountPoint::class);
$mount->method('getStorage')
->willReturn($storage);
$mount->method('getInternalPath')
->willReturnArgument(0);
return new FileInfo('', $this->getMockStorage(), $internalPath, $data, $mount);
}

View File

@@ -19,8 +19,10 @@ use OC\User\NoUserException;
use OCP\Cache\CappedMemoryCache;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Mount\IMountPoint;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\Files\Storage\IStorage;
use OCP\IAppConfig;
use OCP\ICacheFactory;
use OCP\IUser;
@@ -75,8 +77,8 @@ class RootTest extends \Test\TestCase {
return $view;
}
protected function getFileInfo($data) {
return new FileInfo('', null, '', $data, null);
protected function getFileInfo($data): FileInfo {
return new FileInfo('', $this->createMock(IStorage::class), '', $data, $this->createMock(IMountPoint::class));
}
public function testGet(): void {

View File

@@ -91,7 +91,7 @@ class SimpleFileTest extends \Test\TestCase {
public function testGetContentInvalidAppData(): void {
$this->file->method('getContent')
->willReturn(false);
->willThrowException($this->createMock(NotFoundException::class));
$this->file->method('stat')->willReturn(false);
$parent = $this->createMock(Folder::class);

View File

@@ -905,7 +905,7 @@ class ViewTest extends \Test\TestCase {
$info = $view->getFileInfo('test.part');
$this->assertInstanceOf('\OCP\Files\FileInfo', $info);
$this->assertNull($info->getId());
$this->assertEquals(null, $info->getId());
$this->assertEquals(6, $sizeWritten);
$this->assertEquals(6, $info->getSize());
$this->assertEquals('foobar', $view->file_get_contents('test.part'));

View File

@@ -779,7 +779,7 @@ class ManagerTest extends \Test\TestCase {
}
public function createShare($id, $type, $node, $sharedWith, $sharedBy, $shareOwner,
$permissions, $expireDate = null, $password = null, $attributes = null) {
$permissions, $expireDate = null, $password = null, $attributes = null): IShare&MockObject {
$share = $this->createMock(IShare::class);
$share->method('getShareType')->willReturn($type);
@@ -807,6 +807,7 @@ class ManagerTest extends \Test\TestCase {
File::class,
[
'getId' => 108,
'getPath' => 'path',
],
'default',
];
@@ -815,6 +816,7 @@ class ManagerTest extends \Test\TestCase {
Node::class,
[
'getId' => 108,
'getPath' => 'path',
],
'default',
];
@@ -854,6 +856,7 @@ class ManagerTest extends \Test\TestCase {
'getPath' => 'path',
'getName' => 'name',
'getOwner' => $user0,
'getInternalPath' => 'not-null',
],
'default',
];
@@ -871,6 +874,7 @@ class ManagerTest extends \Test\TestCase {
'getPath' => 'path',
'getName' => 'name',
'getOwner' => $user0,
'getInternalPath' => 'not-null',
],
'default',
];
@@ -908,6 +912,8 @@ class ManagerTest extends \Test\TestCase {
'isShareable' => true,
'getPermissions' => Constants::PERMISSION_ALL,
'getId' => 42,
'getPath' => 'path',
'getInternalPath' => 'not-null',
],
'none',
];
@@ -923,6 +929,8 @@ class ManagerTest extends \Test\TestCase {
'getPermissions' => Constants::PERMISSION_ALL,
'getId' => 187,
'getOwner' => $user0,
'getPath' => 'path',
'getInternalPath' => 'not-null',
],
'default',
];
@@ -939,6 +947,8 @@ class ManagerTest extends \Test\TestCase {
'getPermissions' => Constants::PERMISSION_ALL,
'getId' => 108,
'getOwner' => $user0,
'getPath' => 'path',
'getInternalPath' => 'not-null',
],
'default',
];
@@ -963,13 +973,15 @@ class ManagerTest extends \Test\TestCase {
'getPermissions' => Constants::PERMISSION_READ ^ Constants::PERMISSION_UPDATE,
'getId' => 108,
'getOwner' => $user0,
'getPath' => 'path',
'getInternalPath' => 'not-null',
],
'remote',
];
$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 1, null, null], null, false];
$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 3, null, null], null, false];
$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of ', true];
$data[] = [[null, IShare::TYPE_REMOTE, $remoteFile, $user2, $user0, $user0, 31, null, null], 'Cannot increase permissions of path', true];
return $data;
}
@@ -983,7 +995,7 @@ class ManagerTest extends \Test\TestCase {
$return->method('getUID')
->willReturn($uid);
} elseif ($methodName === 'getMountPoint') {
$return = $this->createMock($return);
$return = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, $return]);
}
$mock->method($methodName)->willReturn($return);
}
@@ -2945,7 +2957,7 @@ class ManagerTest extends \Test\TestCase {
}
public function testGetSharesByOwnerless(): void {
$mount = $this->createMock(IShareOwnerlessMount::class);
$mount = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, IShareOwnerlessMount::class]);
$node = $this->createMock(Folder::class);
$node
@@ -4668,7 +4680,7 @@ class ManagerTest extends \Test\TestCase {
$share1 = $this->createMock(IShare::class);
$share2 = $this->createMock(IShare::class);
$mount = $this->createMock(IShareOwnerlessMount::class);
$mount = $this->createMockForIntersectionOfInterfaces([IMountPoint::class, IShareOwnerlessMount::class]);
$folder = $this->createMock(Folder::class);
$folder

View File

@@ -8,6 +8,7 @@
namespace Test\Share20;
use OC\Share20\ShareHelper;
use OCP\Files\Folder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\Share\IManager;
@@ -109,8 +110,7 @@ class ShareHelperTest extends TestCase {
public function testGetPathsForUsers(array $users, array $nodes, array $expected): void {
$lastNode = null;
foreach ($nodes as $nodeId => $nodeName) {
/** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
$node = $this->createMock(Node::class);
$node = $this->createMock(Folder::class);
$node->expects($this->any())
->method('getId')
->willReturn($nodeId);
@@ -166,8 +166,7 @@ class ShareHelperTest extends TestCase {
public function testGetPathsForRemotes(array $remotes, array $nodes, array $expected): void {
$lastNode = null;
foreach ($nodes as $nodeId => $nodePath) {
/** @var Node|\PHPUnit\Framework\MockObject\MockObject $node */
$node = $this->createMock(Node::class);
$node = $this->createMock(Folder::class);
$node->expects($this->any())
->method('getId')
->willReturn($nodeId);