add recent files to node api
This commit is contained in:
parent
d499f68fd7
commit
e321ecd592
|
@ -26,7 +26,10 @@
|
|||
|
||||
namespace OC\Files\Node;
|
||||
|
||||
use OC\DB\QueryBuilder\Literal;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\FileInfo;
|
||||
use OCP\Files\Mount\IMountPoint;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
|
||||
|
@ -358,4 +361,114 @@ class Folder extends Node implements \OCP\Files\Folder {
|
|||
$uniqueName = \OC_Helper::buildNotExistingFileNameForView($this->getPath(), $name, $this->view);
|
||||
return trim($this->getRelativePath($uniqueName), '/');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $since
|
||||
* @return \OCP\Files\Node[]
|
||||
*/
|
||||
public function getRecent($since) {
|
||||
$mimetypeLoader = \OC::$server->getMimeTypeLoader();
|
||||
$mounts = $this->root->getMountsIn($this->path);
|
||||
$mounts[] = $this->getMountPoint();
|
||||
|
||||
$mounts = array_filter($mounts, function (IMountPoint $mount) {
|
||||
return $mount->getStorage();
|
||||
});
|
||||
$storageIds = array_map(function (IMountPoint $mount) {
|
||||
return $mount->getStorage()->getCache()->getNumericStorageId();
|
||||
}, $mounts);
|
||||
/** @var IMountPoint[] $mountMap */
|
||||
$mountMap = array_combine($storageIds, $mounts);
|
||||
$folderMimetype = $mimetypeLoader->getId(FileInfo::MIMETYPE_FOLDER);
|
||||
|
||||
//todo look into options of filtering path based on storage id (only search in files/ for home storage, filter by share root for shared, etc)
|
||||
|
||||
$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
|
||||
$query = $builder
|
||||
->select('f.*')
|
||||
->from('filecache', 'f')
|
||||
->where($builder->expr()->gt('f.storage_mtime', $builder->createNamedParameter($since, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($builder->expr()->in('f.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
->andWhere($builder->expr()->orX(
|
||||
// handle non empty folders separate
|
||||
$builder->expr()->neq('f.mimetype', $builder->createNamedParameter($folderMimetype, IQueryBuilder::PARAM_INT)),
|
||||
$builder->expr()->eq('f.size', new Literal(0))
|
||||
))
|
||||
->orderBy('f.mtime', 'DESC');
|
||||
|
||||
$result = $query->execute()->fetchAll();
|
||||
|
||||
// select folders with their mtime being the mtime of the oldest file in the folder
|
||||
// this way we still show new folders but dont bumb the folder every time a file in it is changed
|
||||
$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
|
||||
$query = $builder
|
||||
->select('p.fileid', 'p.storage', 'p.mimetype', 'p.mimepart', 'p.size', 'p.path', 'p.etag', 'f1.storage_mtime', 'f1.mtime', 'p.permissions')
|
||||
->from('filecache', 'f1')
|
||||
->leftJoin('f1', 'filecache', 'f2', $builder->expr()->andX( // find the f1 with lowest mtime in the folder
|
||||
$builder->expr()->eq('f1.parent', 'f2.parent'),
|
||||
$builder->expr()->gt('f1.storage_mtime', 'f2.storage_mtime')
|
||||
))
|
||||
->innerJoin('f1', 'filecache', 'p', $builder->expr()->eq('f1.parent', 'p.fileid'))
|
||||
->where($builder->expr()->isNull('f2.fileid'))
|
||||
->andWhere($builder->expr()->gt('f1.storage_mtime', $builder->createNamedParameter($since, IQueryBuilder::PARAM_INT)))
|
||||
->andWhere($builder->expr()->in('f1.storage', $builder->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)))
|
||||
->andWhere($builder->expr()->neq('f1.size', new Literal(0)))
|
||||
->orderBy('f1.storage_mtime', 'DESC');
|
||||
|
||||
$folderResults = $query->execute()->fetchAll();
|
||||
|
||||
$found = []; // we sometimes get duplicate folders
|
||||
$folderResults = array_filter($folderResults, function ($item) use (&$found) {
|
||||
$isFound = isset($found[$item['fileid']]);
|
||||
$found[$item['fileid']] = true;
|
||||
return !$isFound;
|
||||
});
|
||||
|
||||
$result = array_merge($folderResults, $result);
|
||||
|
||||
usort($result, function ($a, $b) use ($folderMimetype) {
|
||||
$diff = $b['mtime'] - $a['mtime'];
|
||||
if ($diff === 0) {
|
||||
return $a['mimetype'] === $folderMimetype ? -1 : 1;
|
||||
} else {
|
||||
return $diff;
|
||||
}
|
||||
});
|
||||
|
||||
$files = array_filter(array_map(function (array $entry) use ($mountMap, $mimetypeLoader) {
|
||||
$mount = $mountMap[$entry['storage']];
|
||||
$entry['internalPath'] = $entry['path'];
|
||||
$entry['mimetype'] = $mimetypeLoader->getMimetypeById($entry['mimetype']);
|
||||
$entry['mimepart'] = $mimetypeLoader->getMimetypeById($entry['mimepart']);
|
||||
$path = $this->getAbsolutePath($mount, $entry['path']);
|
||||
if (is_null($path)) {
|
||||
return null;
|
||||
}
|
||||
$fileInfo = new \OC\Files\FileInfo($path, $mount->getStorage(), $entry['internalPath'], $entry, $mount);
|
||||
return $this->root->createNode($fileInfo->getPath(), $fileInfo);
|
||||
}, $result));
|
||||
|
||||
return array_values(array_filter($files, function (Node $node) {
|
||||
$relative = $this->getRelativePath($node->getPath());
|
||||
return $relative !== null && $relative !== '/';
|
||||
}));
|
||||
}
|
||||
|
||||
private function getAbsolutePath(IMountPoint $mount, $path) {
|
||||
$storage = $mount->getStorage();
|
||||
if ($storage->instanceOfStorage('\OC\Files\Storage\Wrapper\Jail')) {
|
||||
/** @var \OC\Files\Storage\Wrapper\Jail $storage */
|
||||
$jailRoot = $storage->getSourcePath('');
|
||||
$rootLength = strlen($jailRoot) + 1;
|
||||
if ($path === $jailRoot) {
|
||||
return $mount->getMountPoint();
|
||||
} else if (substr($path, 0, $rootLength) === $jailRoot . '/') {
|
||||
return $mount->getMountPoint() . substr($path, $rootLength);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return $mount->getMountPoint() . $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -471,5 +471,10 @@ class LazyRoot implements IRootFolder {
|
|||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getRecent($type) {
|
||||
return $this->__call(__FUNCTION__, func_get_args());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,6 +58,11 @@ interface FileInfo {
|
|||
*/
|
||||
const SPACE_UNLIMITED = -3;
|
||||
|
||||
/**
|
||||
* @since 9.1.0
|
||||
*/
|
||||
const MIMETYPE_FOLDER = 'httpd/unix-directory';
|
||||
|
||||
/**
|
||||
* Get the Etag of the file or folder
|
||||
*
|
||||
|
|
|
@ -175,4 +175,11 @@ interface Folder extends Node {
|
|||
* @since 8.1.0
|
||||
*/
|
||||
public function getNonExistingName($name);
|
||||
|
||||
/**
|
||||
* @param int $since
|
||||
* @return \OCP\Files\Node[]
|
||||
* @since 9.1.0
|
||||
*/
|
||||
public function getRecent($since);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ use OC\Files\Cache\Cache;
|
|||
use OC\Files\FileInfo;
|
||||
use OC\Files\Mount\MountPoint;
|
||||
use OC\Files\Node\Node;
|
||||
use OC\Files\Storage\Temporary;
|
||||
use OC\Files\Storage\Wrapper\Jail;
|
||||
use OCP\Files\NotFoundException;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OC\Files\View;
|
||||
|
@ -760,9 +762,9 @@ class FolderTest extends \Test\TestCase {
|
|||
public function uniqueNameProvider() {
|
||||
return [
|
||||
// input, existing, expected
|
||||
['foo', [] , 'foo'],
|
||||
['foo', ['foo'] , 'foo (2)'],
|
||||
['foo', ['foo', 'foo (2)'] , 'foo (3)']
|
||||
['foo', [], 'foo'],
|
||||
['foo', ['foo'], 'foo (2)'],
|
||||
['foo', ['foo', 'foo (2)'], 'foo (3)']
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -782,7 +784,7 @@ class FolderTest extends \Test\TestCase {
|
|||
->method('file_exists')
|
||||
->will($this->returnCallback(function ($path) use ($existingFiles, $folderPath) {
|
||||
foreach ($existingFiles as $existing) {
|
||||
if ($folderPath . '/' . $existing === $path){
|
||||
if ($folderPath . '/' . $existing === $path) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -792,4 +794,165 @@ class FolderTest extends \Test\TestCase {
|
|||
$node = new \OC\Files\Node\Folder($root, $view, $folderPath);
|
||||
$this->assertEquals($expected, $node->getNonExistingName($name));
|
||||
}
|
||||
|
||||
public function testRecent() {
|
||||
$manager = $this->getMock('\OC\Files\Mount\Manager');
|
||||
$folderPath = '/bar/foo';
|
||||
/**
|
||||
* @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
|
||||
*/
|
||||
$view = $this->getMock('\OC\Files\View');
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
|
||||
$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
|
||||
$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
|
||||
$baseTime = 1000;
|
||||
$storage = new Temporary();
|
||||
$mount = new MountPoint($storage, '');
|
||||
|
||||
$folderInfo->expects($this->any())
|
||||
->method('getMountPoint')
|
||||
->will($this->returnValue($mount));
|
||||
|
||||
$cache = $storage->getCache();
|
||||
|
||||
$id1 = $cache->put('bar/foo/inside.txt', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
$id2 = $cache->put('bar/foo/old.txt', [
|
||||
'storage_mtime' => $baseTime - 100,
|
||||
'mtime' => $baseTime - 100,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
$cache->put('bar/asd/outside.txt', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
$cache->put('bar/foo/toold.txt', [
|
||||
'storage_mtime' => $baseTime - 600,
|
||||
'mtime' => $baseTime - 600,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
|
||||
$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
|
||||
|
||||
|
||||
$nodes = $node->getRecent($baseTime - 500);
|
||||
$ids = array_map(function (Node $node) {
|
||||
return (int)$node->getId();
|
||||
}, $nodes);
|
||||
$this->assertEquals([$id1, $id2], $ids);
|
||||
}
|
||||
|
||||
public function testRecentFolder() {
|
||||
$manager = $this->getMock('\OC\Files\Mount\Manager');
|
||||
$folderPath = '/bar/foo';
|
||||
/**
|
||||
* @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
|
||||
*/
|
||||
$view = $this->getMock('\OC\Files\View');
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
|
||||
$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
|
||||
$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
|
||||
$baseTime = 1000;
|
||||
$storage = new Temporary();
|
||||
$mount = new MountPoint($storage, '');
|
||||
|
||||
$folderInfo->expects($this->any())
|
||||
->method('getMountPoint')
|
||||
->will($this->returnValue($mount));
|
||||
|
||||
$cache = $storage->getCache();
|
||||
|
||||
$id1 = $cache->put('bar/foo/folder', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime,
|
||||
'mimetype' => \OCP\Files\FileInfo::MIMETYPE_FOLDER,
|
||||
'size' => 3
|
||||
]);
|
||||
$id2 = $cache->put('bar/foo/folder/bar.txt', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3,
|
||||
'parent' => $id1
|
||||
]);
|
||||
$id3 = $cache->put('bar/foo/folder/asd.txt', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime - 100,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3,
|
||||
'parent' => $id1
|
||||
]);
|
||||
|
||||
$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
|
||||
|
||||
|
||||
$nodes = $node->getRecent($baseTime - 500);
|
||||
$ids = array_map(function (Node $node) {
|
||||
return (int)$node->getId();
|
||||
}, $nodes);
|
||||
$this->assertEquals([$id2, $id1, $id3], $ids);// sort folders before files with the same mtime, folders get the lowest child mtime
|
||||
}
|
||||
|
||||
public function testRecentJail() {
|
||||
$manager = $this->getMock('\OC\Files\Mount\Manager');
|
||||
$folderPath = '/bar/foo';
|
||||
/**
|
||||
* @var \OC\Files\View | \PHPUnit_Framework_MockObject_MockObject $view
|
||||
*/
|
||||
$view = $this->getMock('\OC\Files\View');
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\Node\Root $root */
|
||||
$root = $this->getMock('\OC\Files\Node\Root', array('getUser', 'getMountsIn', 'getMount'), array($manager, $view, $this->user));
|
||||
/** @var \PHPUnit_Framework_MockObject_MockObject|\OC\Files\FileInfo $folderInfo */
|
||||
$folderInfo = $this->getMockBuilder('\OC\Files\FileInfo')
|
||||
->disableOriginalConstructor()->getMock();
|
||||
|
||||
$baseTime = 1000;
|
||||
$storage = new Temporary();
|
||||
$jail = new Jail([
|
||||
'storage' => $storage,
|
||||
'root' => 'folder'
|
||||
]);
|
||||
$mount = new MountPoint($jail, '/bar/foo');
|
||||
|
||||
$folderInfo->expects($this->any())
|
||||
->method('getMountPoint')
|
||||
->will($this->returnValue($mount));
|
||||
|
||||
$cache = $storage->getCache();
|
||||
|
||||
$id1 = $cache->put('folder/inside.txt', [
|
||||
'storage_mtime' => $baseTime,
|
||||
'mtime' => $baseTime,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
$cache->put('outside.txt', [
|
||||
'storage_mtime' => $baseTime - 100,
|
||||
'mtime' => $baseTime - 100,
|
||||
'mimetype' => 'text/plain',
|
||||
'size' => 3
|
||||
]);
|
||||
|
||||
$node = new \OC\Files\Node\Folder($root, $view, $folderPath, $folderInfo);
|
||||
|
||||
$nodes = $node->getRecent($baseTime - 500);
|
||||
$ids = array_map(function (Node $node) {
|
||||
return (int)$node->getId();
|
||||
}, $nodes);
|
||||
$this->assertEquals([$id1], $ids);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue