mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
Merge pull request #38307 from nextcloud/backport/37961/stable26
[stable26] SystemTags endpoint to return tags used by a user with meta data
This commit is contained in:
commit
4bd2f33cfe
12 changed files with 333 additions and 54 deletions
|
|
@ -304,6 +304,7 @@ return array(
|
|||
'OCA\\DAV\\SystemTag\\SystemTagNode' => $baseDir . '/../lib/SystemTag/SystemTagNode.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagPlugin' => $baseDir . '/../lib/SystemTag/SystemTagPlugin.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsByIdCollection' => $baseDir . '/../lib/SystemTag/SystemTagsByIdCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsInUseCollection' => $baseDir . '/../lib/SystemTag/SystemTagsInUseCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsObjectMappingCollection' => $baseDir . '/../lib/SystemTag/SystemTagsObjectMappingCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsObjectTypeCollection' => $baseDir . '/../lib/SystemTag/SystemTagsObjectTypeCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsRelationsCollection' => $baseDir . '/../lib/SystemTag/SystemTagsRelationsCollection.php',
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ class ComposerStaticInitDAV
|
|||
'OCA\\DAV\\SystemTag\\SystemTagNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagNode.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagPlugin' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagPlugin.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsByIdCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsByIdCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsInUseCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsInUseCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsObjectMappingCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsObjectMappingCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsObjectTypeCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsObjectTypeCollection.php',
|
||||
'OCA\\DAV\\SystemTag\\SystemTagsRelationsCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsRelationsCollection.php',
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ use OCP\Accounts\IAccountManager;
|
|||
use OCP\App\IAppManager;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\EventDispatcher\IEventDispatcher;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\IConfig;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Sabre\DAV\SimpleCollection;
|
||||
|
|
@ -65,6 +66,7 @@ class RootCollection extends SimpleCollection {
|
|||
$dispatcher = \OC::$server->get(IEventDispatcher::class);
|
||||
$config = \OC::$server->get(IConfig::class);
|
||||
$proxyMapper = \OC::$server->query(ProxyMapper::class);
|
||||
$rootFolder = \OCP\Server::get(IRootFolder::class);
|
||||
|
||||
$userPrincipalBackend = new Principal(
|
||||
$userManager,
|
||||
|
|
@ -131,6 +133,7 @@ class RootCollection extends SimpleCollection {
|
|||
$groupManager,
|
||||
\OC::$server->getEventDispatcher()
|
||||
);
|
||||
$systemTagInUseCollection = \OCP\Server::get(SystemTag\SystemTagsInUseCollection::class);
|
||||
$commentsCollection = new Comments\RootCollection(
|
||||
\OC::$server->getCommentsManager(),
|
||||
$userManager,
|
||||
|
|
@ -179,6 +182,7 @@ class RootCollection extends SimpleCollection {
|
|||
$systemAddressBookRoot]),
|
||||
$systemTagCollection,
|
||||
$systemTagRelationsCollection,
|
||||
$systemTagInUseCollection,
|
||||
$commentsCollection,
|
||||
$uploadCollection,
|
||||
$avatarCollection,
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ class SystemTagNode implements \Sabre\DAV\INode {
|
|||
*/
|
||||
protected $isAdmin;
|
||||
|
||||
protected int $numberOfFiles = -1;
|
||||
protected int $referenceFileId = -1;
|
||||
|
||||
/**
|
||||
* Sets up the node, expects a full path name
|
||||
*
|
||||
|
|
@ -172,4 +175,20 @@ class SystemTagNode implements \Sabre\DAV\INode {
|
|||
throw new NotFound('Tag with id ' . $this->tag->getId() . ' not found', 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
public function getNumberOfFiles(): int {
|
||||
return $this->numberOfFiles;
|
||||
}
|
||||
|
||||
public function setNumberOfFiles(int $numberOfFiles): void {
|
||||
$this->numberOfFiles = $numberOfFiles;
|
||||
}
|
||||
|
||||
public function getReferenceFileId(): int {
|
||||
return $this->referenceFileId;
|
||||
}
|
||||
|
||||
public function setReferenceFileId(int $referenceFileId): void {
|
||||
$this->referenceFileId = $referenceFileId;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
public const USERASSIGNABLE_PROPERTYNAME = '{http://owncloud.org/ns}user-assignable';
|
||||
public const GROUPS_PROPERTYNAME = '{http://owncloud.org/ns}groups';
|
||||
public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign';
|
||||
public const NUM_FILES_PROPERTYNAME = '{http://nextcloud.org/ns}files-assigned';
|
||||
public const FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid';
|
||||
|
||||
/**
|
||||
* @var \Sabre\DAV\Server $server
|
||||
|
|
@ -224,6 +226,11 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
return;
|
||||
}
|
||||
|
||||
// child nodes from systemtags-assigned should point to normal tag endpoint
|
||||
if (preg_match('/^systemtags-assigned\/[0-9]+/', $propFind->getPath())) {
|
||||
$propFind->setPath(str_replace('systemtags-assigned/', 'systemtags/', $propFind->getPath()));
|
||||
}
|
||||
|
||||
$propFind->handle(self::ID_PROPERTYNAME, function () use ($node) {
|
||||
return $node->getSystemTag()->getId();
|
||||
});
|
||||
|
|
@ -258,6 +265,16 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
}
|
||||
return implode('|', $groups);
|
||||
});
|
||||
|
||||
if ($node instanceof SystemTagNode) {
|
||||
$propFind->handle(self::NUM_FILES_PROPERTYNAME, function () use ($node): int {
|
||||
return $node->getNumberOfFiles();
|
||||
});
|
||||
|
||||
$propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node): int {
|
||||
return $node->getReferenceFileId();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -279,6 +296,8 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
self::USERVISIBLE_PROPERTYNAME,
|
||||
self::USERASSIGNABLE_PROPERTYNAME,
|
||||
self::GROUPS_PROPERTYNAME,
|
||||
self::NUM_FILES_PROPERTYNAME,
|
||||
self::FILEID_PROPERTYNAME,
|
||||
], function ($props) use ($node) {
|
||||
$tag = $node->getSystemTag();
|
||||
$name = $tag->getName();
|
||||
|
|
@ -315,6 +334,11 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
|
|||
$this->tagManager->setTagGroups($tag, $groupIds);
|
||||
}
|
||||
|
||||
if (isset($props[self::NUM_FILES_PROPERTYNAME]) || isset($props[self::FILEID_PROPERTYNAME])) {
|
||||
// read-only properties
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
if ($updateTag) {
|
||||
$node->update($name, $userVisible, $userAssignable);
|
||||
}
|
||||
|
|
|
|||
108
apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
Normal file
108
apps/dav/lib/SystemTag/SystemTagsInUseCollection.php
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCA\DAV\SystemTag;
|
||||
|
||||
use OC\SystemTag\SystemTag;
|
||||
use OC\SystemTag\SystemTagsInFilesDetector;
|
||||
use OC\User\NoUserException;
|
||||
use OCP\Files\IRootFolder;
|
||||
use OCP\Files\NotPermittedException;
|
||||
use OCP\IUserSession;
|
||||
use OCP\SystemTag\ISystemTagManager;
|
||||
use Sabre\DAV\Exception\Forbidden;
|
||||
use Sabre\DAV\Exception\NotFound;
|
||||
use Sabre\DAV\SimpleCollection;
|
||||
|
||||
class SystemTagsInUseCollection extends SimpleCollection {
|
||||
protected IUserSession $userSession;
|
||||
protected IRootFolder $rootFolder;
|
||||
protected string $mediaType;
|
||||
protected ISystemTagManager $systemTagManager;
|
||||
protected SystemTagsInFilesDetector $systemTagsInFilesDetector;
|
||||
|
||||
/** @noinspection PhpMissingParentConstructorInspection */
|
||||
public function __construct(
|
||||
IUserSession $userSession,
|
||||
IRootFolder $rootFolder,
|
||||
ISystemTagManager $systemTagManager,
|
||||
SystemTagsInFilesDetector $systemTagsInFilesDetector,
|
||||
string $mediaType = ''
|
||||
) {
|
||||
$this->userSession = $userSession;
|
||||
$this->rootFolder = $rootFolder;
|
||||
$this->systemTagManager = $systemTagManager;
|
||||
$this->mediaType = $mediaType;
|
||||
$this->systemTagsInFilesDetector = $systemTagsInFilesDetector;
|
||||
$this->name = 'systemtags-assigned';
|
||||
if ($this->mediaType != '') {
|
||||
$this->name .= '/' . $this->mediaType;
|
||||
}
|
||||
}
|
||||
|
||||
public function setName($name): void {
|
||||
throw new Forbidden('Permission denied to rename this collection');
|
||||
}
|
||||
|
||||
public function getChild($name): self {
|
||||
if ($this->mediaType !== '') {
|
||||
throw new NotFound('Invalid media type');
|
||||
}
|
||||
return new self($this->userSession, $this->rootFolder, $this->systemTagManager, $this->systemTagsInFilesDetector, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SystemTagNode[]
|
||||
* @throws NotPermittedException
|
||||
* @throws Forbidden
|
||||
*/
|
||||
public function getChildren(): array {
|
||||
$user = $this->userSession->getUser();
|
||||
$userFolder = null;
|
||||
try {
|
||||
if ($user) {
|
||||
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
|
||||
}
|
||||
} catch (NoUserException) {
|
||||
// will throw a Sabre exception in the next step.
|
||||
}
|
||||
if ($user === null || $userFolder === null) {
|
||||
throw new Forbidden('Permission denied to read this collection');
|
||||
}
|
||||
|
||||
$result = $this->systemTagsInFilesDetector->detectAssignedSystemTagsIn($userFolder, $this->mediaType);
|
||||
$children = [];
|
||||
foreach ($result as $tagData) {
|
||||
$tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable']);
|
||||
// read only, so we can submit the isAdmin parameter as false generally
|
||||
$node = new SystemTagNode($tag, $user, false, $this->systemTagManager);
|
||||
$node->setNumberOfFiles($tagData['number_files']);
|
||||
$node->setReferenceFileId($tagData['ref_file_id']);
|
||||
$children[] = $node;
|
||||
}
|
||||
return $children;
|
||||
}
|
||||
}
|
||||
|
|
@ -1594,6 +1594,7 @@ return array(
|
|||
'OC\\SystemTag\\SystemTag' => $baseDir . '/lib/private/SystemTag/SystemTag.php',
|
||||
'OC\\SystemTag\\SystemTagManager' => $baseDir . '/lib/private/SystemTag/SystemTagManager.php',
|
||||
'OC\\SystemTag\\SystemTagObjectMapper' => $baseDir . '/lib/private/SystemTag/SystemTagObjectMapper.php',
|
||||
'OC\\SystemTag\\SystemTagsInFilesDetector' => $baseDir . '/lib/private/SystemTag/SystemTagsInFilesDetector.php',
|
||||
'OC\\TagManager' => $baseDir . '/lib/private/TagManager.php',
|
||||
'OC\\Tagging\\Tag' => $baseDir . '/lib/private/Tagging/Tag.php',
|
||||
'OC\\Tagging\\TagMapper' => $baseDir . '/lib/private/Tagging/TagMapper.php',
|
||||
|
|
|
|||
|
|
@ -1627,6 +1627,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\SystemTag\\SystemTag' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTag.php',
|
||||
'OC\\SystemTag\\SystemTagManager' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagManager.php',
|
||||
'OC\\SystemTag\\SystemTagObjectMapper' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagObjectMapper.php',
|
||||
'OC\\SystemTag\\SystemTagsInFilesDetector' => __DIR__ . '/../../..' . '/lib/private/SystemTag/SystemTagsInFilesDetector.php',
|
||||
'OC\\TagManager' => __DIR__ . '/../../..' . '/lib/private/TagManager.php',
|
||||
'OC\\Tagging\\Tag' => __DIR__ . '/../../..' . '/lib/private/Tagging/Tag.php',
|
||||
'OC\\Tagging\\TagMapper' => __DIR__ . '/../../..' . '/lib/private/Tagging/TagMapper.php',
|
||||
|
|
|
|||
|
|
@ -41,8 +41,27 @@ class CacheQueryBuilder extends QueryBuilder {
|
|||
parent::__construct($connection, $systemConfig, $logger);
|
||||
}
|
||||
|
||||
public function selectTagUsage(): self {
|
||||
$this
|
||||
->select('systemtag.name', 'systemtag.id', 'systemtag.visibility', 'systemtag.editable')
|
||||
->selectAlias($this->createFunction('COUNT(filecache.fileid)'), 'number_files')
|
||||
->selectAlias($this->createFunction('MAX(filecache.fileid)'), 'ref_file_id')
|
||||
->from('filecache', 'filecache')
|
||||
->leftJoin('filecache', 'systemtag_object_mapping', 'systemtagmap', $this->expr()->andX(
|
||||
$this->expr()->eq('filecache.fileid', $this->expr()->castColumn('systemtagmap.objectid', IQueryBuilder::PARAM_INT)),
|
||||
$this->expr()->eq('systemtagmap.objecttype', $this->createNamedParameter('files'))
|
||||
))
|
||||
->leftJoin('systemtagmap', 'systemtag', 'systemtag', $this->expr()->andX(
|
||||
$this->expr()->eq('systemtag.id', 'systemtagmap.systemtagid'),
|
||||
$this->expr()->eq('systemtag.visibility', $this->createNamedParameter(true))
|
||||
))
|
||||
->groupBy('systemtag.name', 'systemtag.id', 'systemtag.visibility', 'systemtag.editable');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function selectFileCache(string $alias = null, bool $joinExtendedCache = true) {
|
||||
$name = $alias ? $alias : 'filecache';
|
||||
$name = $alias ?: 'filecache';
|
||||
$this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", "$name.name", 'mimetype', 'mimepart', 'size', 'mtime',
|
||||
'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'unencrypted_size')
|
||||
->from('filecache', $name);
|
||||
|
|
|
|||
|
|
@ -25,13 +25,17 @@
|
|||
*/
|
||||
namespace OC\Files\Cache;
|
||||
|
||||
use OC\Files\Cache\Wrapper\CacheJail;
|
||||
use OC\Files\Node\Root;
|
||||
use OC\Files\Search\QueryOptimizer\QueryOptimizer;
|
||||
use OC\Files\Search\SearchBinaryOperator;
|
||||
use OC\SystemConfig;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\Files\Cache\ICache;
|
||||
use OCP\Files\Cache\ICacheEntry;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\IMimeTypeLoader;
|
||||
use OCP\Files\Mount\IMountPoint;
|
||||
use OCP\Files\Search\ISearchBinaryOperator;
|
||||
use OCP\Files\Search\ISearchQuery;
|
||||
use OCP\IDBConnection;
|
||||
|
|
@ -74,6 +78,45 @@ class QuerySearchHelper {
|
|||
);
|
||||
}
|
||||
|
||||
protected function applySearchConstraints(CacheQueryBuilder $query, ISearchQuery $searchQuery, array $caches): void {
|
||||
$storageFilters = array_values(array_map(function (ICache $cache) {
|
||||
return $cache->getQueryFilterForStorage();
|
||||
}, $caches));
|
||||
$storageFilter = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $storageFilters);
|
||||
$filter = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$searchQuery->getSearchOperation(), $storageFilter]);
|
||||
$this->queryOptimizer->processOperator($filter);
|
||||
|
||||
$searchExpr = $this->searchBuilder->searchOperatorToDBExpr($query, $filter);
|
||||
if ($searchExpr) {
|
||||
$query->andWhere($searchExpr);
|
||||
}
|
||||
|
||||
$this->searchBuilder->addSearchOrdersToQuery($query, $searchQuery->getOrder());
|
||||
|
||||
if ($searchQuery->getLimit()) {
|
||||
$query->setMaxResults($searchQuery->getLimit());
|
||||
}
|
||||
if ($searchQuery->getOffset()) {
|
||||
$query->setFirstResult($searchQuery->getOffset());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array<array-key, array{id: int, name: string, visibility: int, editable: int, ref_file_id: int, number_files: int}>
|
||||
*/
|
||||
public function findUsedTagsInCaches(ISearchQuery $searchQuery, array $caches): array {
|
||||
$query = $this->getQueryBuilder();
|
||||
$query->selectTagUsage();
|
||||
|
||||
$this->applySearchConstraints($query, $searchQuery, $caches);
|
||||
|
||||
$result = $query->execute();
|
||||
$tags = $result->fetchAll();
|
||||
$result->closeCursor();
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a file system search in multiple caches
|
||||
*
|
||||
|
|
@ -127,26 +170,7 @@ class QuerySearchHelper {
|
|||
));
|
||||
}
|
||||
|
||||
$storageFilters = array_values(array_map(function (ICache $cache) {
|
||||
return $cache->getQueryFilterForStorage();
|
||||
}, $caches));
|
||||
$storageFilter = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_OR, $storageFilters);
|
||||
$filter = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$searchQuery->getSearchOperation(), $storageFilter]);
|
||||
$this->queryOptimizer->processOperator($filter);
|
||||
|
||||
$searchExpr = $this->searchBuilder->searchOperatorToDBExpr($builder, $filter);
|
||||
if ($searchExpr) {
|
||||
$query->andWhere($searchExpr);
|
||||
}
|
||||
|
||||
$this->searchBuilder->addSearchOrdersToQuery($query, $searchQuery->getOrder());
|
||||
|
||||
if ($searchQuery->getLimit()) {
|
||||
$query->setMaxResults($searchQuery->getLimit());
|
||||
}
|
||||
if ($searchQuery->getOffset()) {
|
||||
$query->setFirstResult($searchQuery->getOffset());
|
||||
}
|
||||
$this->applySearchConstraints($query, $searchQuery, $caches);
|
||||
|
||||
$result = $query->execute();
|
||||
$files = $result->fetchAll();
|
||||
|
|
@ -158,7 +182,7 @@ class QuerySearchHelper {
|
|||
$result->closeCursor();
|
||||
|
||||
// loop through all caches for each result to see if the result matches that storage
|
||||
// results are grouped by the same array keys as the caches argument to allow the caller to distringuish the source of the results
|
||||
// results are grouped by the same array keys as the caches argument to allow the caller to distinguish the source of the results
|
||||
$results = array_fill_keys(array_keys($caches), []);
|
||||
foreach ($rawEntries as $rawEntry) {
|
||||
foreach ($caches as $cacheKey => $cache) {
|
||||
|
|
@ -170,4 +194,37 @@ class QuerySearchHelper {
|
|||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{array<string, ICache>, array<string, IMountPoint>}
|
||||
*/
|
||||
public function getCachesAndMountPointsForSearch(Root $root, string $path, bool $limitToHome = false): array {
|
||||
$rootLength = strlen($path);
|
||||
$mount = $root->getMount($path);
|
||||
$storage = $mount->getStorage();
|
||||
$internalPath = $mount->getInternalPath($path);
|
||||
|
||||
if ($internalPath !== '') {
|
||||
// a temporary CacheJail is used to handle filtering down the results to within this folder
|
||||
$caches = ['' => new CacheJail($storage->getCache(''), $internalPath)];
|
||||
} else {
|
||||
$caches = ['' => $storage->getCache('')];
|
||||
}
|
||||
$mountByMountPoint = ['' => $mount];
|
||||
|
||||
if (!$limitToHome) {
|
||||
/** @var IMountPoint[] $mounts */
|
||||
$mounts = $root->getMountsIn($path);
|
||||
foreach ($mounts as $mount) {
|
||||
$storage = $mount->getStorage();
|
||||
if ($storage) {
|
||||
$relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
|
||||
$caches[$relativeMountPoint] = $storage->getCache('');
|
||||
$mountByMountPoint[$relativeMountPoint] = $mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [$caches, $mountByMountPoint];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ namespace OC\Files\Node;
|
|||
|
||||
use OC\Files\Cache\QuerySearchHelper;
|
||||
use OC\Files\Search\SearchBinaryOperator;
|
||||
use OC\Files\Cache\Wrapper\CacheJail;
|
||||
use OC\Files\Search\SearchComparison;
|
||||
use OC\Files\Search\SearchOrder;
|
||||
use OC\Files\Search\SearchQuery;
|
||||
|
|
@ -203,7 +202,7 @@ class Folder extends Node implements \OCP\Files\Folder {
|
|||
throw new NotPermittedException('No create permission for path');
|
||||
}
|
||||
|
||||
private function queryFromOperator(ISearchOperator $operator, string $uid = null): ISearchQuery {
|
||||
private function queryFromOperator(ISearchOperator $operator, string $uid = null, int $limit = 0, int $offset = 0): ISearchQuery {
|
||||
if ($uid === null) {
|
||||
$user = null;
|
||||
} else {
|
||||
|
|
@ -211,7 +210,7 @@ class Folder extends Node implements \OCP\Files\Folder {
|
|||
$userManager = \OC::$server->query(IUserManager::class);
|
||||
$user = $userManager->get($uid);
|
||||
}
|
||||
return new SearchQuery($operator, 0, 0, [], $user);
|
||||
return new SearchQuery($operator, $limit, $offset, [], $user);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -233,40 +232,13 @@ class Folder extends Node implements \OCP\Files\Folder {
|
|||
throw new \InvalidArgumentException('searching by owner is only allows on the users home folder');
|
||||
}
|
||||
|
||||
$rootLength = strlen($this->path);
|
||||
$mount = $this->root->getMount($this->path);
|
||||
$storage = $mount->getStorage();
|
||||
$internalPath = $mount->getInternalPath($this->path);
|
||||
|
||||
// collect all caches for this folder, indexed by their mountpoint relative to this folder
|
||||
// and save the mount which is needed later to construct the FileInfo objects
|
||||
|
||||
if ($internalPath !== '') {
|
||||
// a temporary CacheJail is used to handle filtering down the results to within this folder
|
||||
$caches = ['' => new CacheJail($storage->getCache(''), $internalPath)];
|
||||
} else {
|
||||
$caches = ['' => $storage->getCache('')];
|
||||
}
|
||||
$mountByMountPoint = ['' => $mount];
|
||||
|
||||
if (!$limitToHome) {
|
||||
$mounts = $this->root->getMountsIn($this->path);
|
||||
foreach ($mounts as $mount) {
|
||||
$storage = $mount->getStorage();
|
||||
if ($storage) {
|
||||
$relativeMountPoint = ltrim(substr($mount->getMountPoint(), $rootLength), '/');
|
||||
$caches[$relativeMountPoint] = $storage->getCache('');
|
||||
$mountByMountPoint[$relativeMountPoint] = $mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var QuerySearchHelper $searchHelper */
|
||||
$searchHelper = \OC::$server->get(QuerySearchHelper::class);
|
||||
[$caches, $mountByMountPoint] = $searchHelper->getCachesAndMountPointsForSearch($this->root, $this->path, $limitToHome);
|
||||
$resultsPerCache = $searchHelper->searchInCaches($query, $caches);
|
||||
|
||||
// loop through all results per-cache, constructing the FileInfo object from the CacheEntry and merge them all
|
||||
$files = array_merge(...array_map(function (array $results, $relativeMountPoint) use ($mountByMountPoint) {
|
||||
$files = array_merge(...array_map(function (array $results, string $relativeMountPoint) use ($mountByMountPoint) {
|
||||
$mount = $mountByMountPoint[$relativeMountPoint];
|
||||
return array_map(function (ICacheEntry $result) use ($relativeMountPoint, $mount) {
|
||||
return $this->cacheEntryToFileInfo($mount, $relativeMountPoint, $result);
|
||||
|
|
|
|||
72
lib/private/SystemTag/SystemTagsInFilesDetector.php
Normal file
72
lib/private/SystemTag/SystemTagsInFilesDetector.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2023 Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\SystemTag;
|
||||
|
||||
use OC\Files\Cache\QuerySearchHelper;
|
||||
use OC\Files\Node\Root;
|
||||
use OC\Files\Search\SearchBinaryOperator;
|
||||
use OC\Files\Search\SearchComparison;
|
||||
use OC\Files\Search\SearchQuery;
|
||||
use OCP\Files\Folder;
|
||||
use OCP\Files\Search\ISearchBinaryOperator;
|
||||
use OCP\Files\Search\ISearchComparison;
|
||||
|
||||
class SystemTagsInFilesDetector {
|
||||
public function __construct(protected QuerySearchHelper $searchHelper) {
|
||||
}
|
||||
|
||||
public function detectAssignedSystemTagsIn(
|
||||
Folder $folder,
|
||||
string $filteredMediaType = '',
|
||||
int $limit = 0,
|
||||
int $offset = 0
|
||||
): array {
|
||||
$operator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'systemtag', '%');
|
||||
// Currently query has to have exactly one search condition. If no media type is provided,
|
||||
// we fall back to the presence of a system tag.
|
||||
if ($filteredMediaType !== '') {
|
||||
$mimeOperator = new SearchComparison(ISearchComparison::COMPARE_LIKE, 'mimetype', $filteredMediaType . '/%');
|
||||
$operator = new SearchBinaryOperator(ISearchBinaryOperator::OPERATOR_AND, [$operator, $mimeOperator]);
|
||||
}
|
||||
|
||||
$query = new SearchQuery($operator, $limit, $offset, []);
|
||||
[$caches, ] = $this->searchHelper->getCachesAndMountPointsForSearch(
|
||||
$this->getRootFolder($folder),
|
||||
$folder->getPath(),
|
||||
);
|
||||
return $this->searchHelper->findUsedTagsInCaches($query, $caches);
|
||||
}
|
||||
|
||||
protected function getRootFolder(?Folder $folder): Root {
|
||||
if ($folder instanceof Root) {
|
||||
return $folder;
|
||||
} elseif ($folder === null) {
|
||||
throw new \LogicException('Could not climb up to root folder');
|
||||
}
|
||||
return $this->getRootFolder($folder->getParent());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue