diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 3 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 3 | ||||
-rw-r--r-- | lib/private/Files/Cache/CacheQueryBuilder.php | 21 | ||||
-rw-r--r-- | lib/private/Files/Cache/QuerySearchHelper.php | 99 | ||||
-rw-r--r-- | lib/private/Files/Cache/Scanner.php | 17 | ||||
-rw-r--r-- | lib/private/Files/Node/Folder.php | 36 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/NoopScanner.php | 81 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/ObjectStoreScanner.php | 98 | ||||
-rw-r--r-- | lib/private/Files/ObjectStore/ObjectStoreStorage.php | 4 | ||||
-rw-r--r-- | lib/private/Mail/Message.php | 16 | ||||
-rw-r--r-- | lib/private/SystemTag/SystemTagsInFilesDetector.php | 72 | ||||
-rw-r--r-- | lib/public/Mail/IMessage.php | 12 |
12 files changed, 316 insertions, 146 deletions
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index ec11cda456d..f35b9c94fbc 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1258,7 +1258,7 @@ return array( 'OC\\Files\\ObjectStore\\Azure' => $baseDir . '/lib/private/Files/ObjectStore/Azure.php', 'OC\\Files\\ObjectStore\\HomeObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Mapper' => $baseDir . '/lib/private/Files/ObjectStore/Mapper.php', - 'OC\\Files\\ObjectStore\\NoopScanner' => $baseDir . '/lib/private/Files/ObjectStore/NoopScanner.php', + 'OC\\Files\\ObjectStore\\ObjectStoreScanner' => $baseDir . '/lib/private/Files/ObjectStore/ObjectStoreScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => $baseDir . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\S3' => $baseDir . '/lib/private/Files/ObjectStore/S3.php', 'OC\\Files\\ObjectStore\\S3ConnectionTrait' => $baseDir . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php', @@ -1620,6 +1620,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', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 308290b95bc..8de7ef99d02 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1291,7 +1291,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Files\\ObjectStore\\Azure' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Azure.php', 'OC\\Files\\ObjectStore\\HomeObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\Mapper' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/Mapper.php', - 'OC\\Files\\ObjectStore\\NoopScanner' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/NoopScanner.php', + 'OC\\Files\\ObjectStore\\ObjectStoreScanner' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/ObjectStoreScanner.php', 'OC\\Files\\ObjectStore\\ObjectStoreStorage' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/ObjectStoreStorage.php', 'OC\\Files\\ObjectStore\\S3' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3.php', 'OC\\Files\\ObjectStore\\S3ConnectionTrait' => __DIR__ . '/../../..' . '/lib/private/Files/ObjectStore/S3ConnectionTrait.php', @@ -1653,6 +1653,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', diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php index 496a8361d77..34d2177b84e 100644 --- a/lib/private/Files/Cache/CacheQueryBuilder.php +++ b/lib/private/Files/Cache/CacheQueryBuilder.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); diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index eba2aac927b..30b3c7225ac 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -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]; + } } diff --git a/lib/private/Files/Cache/Scanner.php b/lib/private/Files/Cache/Scanner.php index 6969828782a..e3a08264716 100644 --- a/lib/private/Files/Cache/Scanner.php +++ b/lib/private/Files/Cache/Scanner.php @@ -41,8 +41,8 @@ use OCP\Files\Cache\IScanner; use OCP\Files\ForbiddenException; use OCP\Files\NotFoundException; use OCP\Files\Storage\IReliableEtagStorage; +use OCP\IDBConnection; use OCP\Lock\ILockingProvider; -use OC\Files\Storage\Wrapper\Encoding; use OC\Files\Storage\Wrapper\Jail; use OC\Hooks\BasicEmitter; use Psr\Log\LoggerInterface; @@ -89,12 +89,15 @@ class Scanner extends BasicEmitter implements IScanner { */ protected $lockingProvider; + protected IDBConnection $connection; + public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); $this->cacheActive = !\OC::$server->getConfig()->getSystemValueBool('filesystem_cache_readonly', false); $this->lockingProvider = \OC::$server->getLockingProvider(); + $this->connection = \OC::$server->get(IDBConnection::class); } /** @@ -430,7 +433,7 @@ class Scanner extends BasicEmitter implements IScanner { } if ($this->useTransactions) { - \OC::$server->getDatabaseConnection()->beginTransaction(); + $this->connection->beginTransaction(); } $exceptionOccurred = false; @@ -473,8 +476,8 @@ class Scanner extends BasicEmitter implements IScanner { // process is running in parallel // log and ignore if ($this->useTransactions) { - \OC::$server->getDatabaseConnection()->rollback(); - \OC::$server->getDatabaseConnection()->beginTransaction(); + $this->connection->rollback(); + $this->connection->beginTransaction(); } \OC::$server->get(LoggerInterface::class)->debug('Exception while scanning file "' . $child . '"', [ 'app' => 'core', @@ -483,7 +486,7 @@ class Scanner extends BasicEmitter implements IScanner { $exceptionOccurred = true; } catch (\OCP\Lock\LockedException $e) { if ($this->useTransactions) { - \OC::$server->getDatabaseConnection()->rollback(); + $this->connection->rollback(); } throw $e; } @@ -494,7 +497,7 @@ class Scanner extends BasicEmitter implements IScanner { $this->removeFromCache($child); } if ($this->useTransactions) { - \OC::$server->getDatabaseConnection()->commit(); + $this->connection->commit(); } if ($exceptionOccurred) { // It might happen that the parallel scan process has already @@ -558,7 +561,7 @@ class Scanner extends BasicEmitter implements IScanner { } } - private function runBackgroundScanJob(callable $callback, $path) { + protected function runBackgroundScanJob(callable $callback, $path) { try { $callback(); \OC_Hook::emit('Scanner', 'correctFolderSize', ['path' => $path]); diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 2c376fe5885..1d6d88bafe6 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -33,7 +33,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; @@ -204,7 +203,7 @@ class Folder extends Node implements \OCP\Files\Folder { throw new NotPermittedException('No create permission for path "' . $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 { @@ -212,7 +211,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); } /** @@ -234,40 +233,13 @@ class Folder extends Node implements \OCP\Files\Folder { throw new \InvalidArgumentException('searching by owner is only allowed in 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); diff --git a/lib/private/Files/ObjectStore/NoopScanner.php b/lib/private/Files/ObjectStore/NoopScanner.php deleted file mode 100644 index bdfc93758d4..00000000000 --- a/lib/private/Files/ObjectStore/NoopScanner.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * 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, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -namespace OC\Files\ObjectStore; - -use OC\Files\Cache\Scanner; -use OC\Files\Storage\Storage; - -class NoopScanner extends Scanner { - public function __construct(Storage $storage) { - // we don't need the storage, so do nothing here - } - - /** - * scan a single file and store it in the cache - * - * @param string $file - * @param int $reuseExisting - * @param int $parentId - * @param array|null $cacheData existing data in the cache for the file to be scanned - * @return array an array of metadata of the scanned file - */ - public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) { - return []; - } - - /** - * scan a folder and all it's children - * - * @param string $path - * @param bool $recursive - * @param int $reuse - * @return array with the meta data of the scanned file or folder - */ - public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { - return []; - } - - /** - * scan all the files and folders in a folder - * - * @param string $path - * @param bool $recursive - * @param int $reuse - * @param array $folderData existing cache data for the folder to be scanned - * @return int the size of the scanned folder or -1 if the size is unknown at this stage - */ - protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true, array $data = []) { - return 0; - } - - /** - * walk over any folders that are not fully scanned yet and scan them - */ - public function backgroundScan() { - //noop - } -} diff --git a/lib/private/Files/ObjectStore/ObjectStoreScanner.php b/lib/private/Files/ObjectStore/ObjectStoreScanner.php new file mode 100644 index 00000000000..e589ca51aae --- /dev/null +++ b/lib/private/Files/ObjectStore/ObjectStoreScanner.php @@ -0,0 +1,98 @@ +<?php +/** + * @copyright Copyright (c) 2016, ownCloud, Inc. + * + * @author Christoph Wurst <christoph@winzerhof-wurst.at> + * @author Joas Schilling <coding@schilljs.com> + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <robin@icewind.nl> + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * 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, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ +namespace OC\Files\ObjectStore; + +use OC\Files\Cache\Scanner; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Files\FileInfo; + +class ObjectStoreScanner extends Scanner { + public function scanFile($file, $reuseExisting = 0, $parentId = -1, $cacheData = null, $lock = true, $data = null) { + return []; + } + + public function scan($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $lock = true) { + return []; + } + + protected function scanChildren($path, $recursive = self::SCAN_RECURSIVE, $reuse = -1, $folderId = null, $lock = true, array $data = []) { + return 0; + } + + public function backgroundScan() { + $lastPath = null; + // find any path marked as unscanned and run the scanner until no more paths are unscanned (or we get stuck) + // we sort by path DESC to ensure that contents of a folder are handled before the parent folder + while (($path = $this->getIncomplete()) !== false && $path !== $lastPath) { + $this->runBackgroundScanJob(function () use ($path) { + $item = $this->cache->get($path); + if ($item && $item->getMimeType() !== FileInfo::MIMETYPE_FOLDER) { + $fh = $this->storage->fopen($path, 'r'); + if ($fh) { + $stat = fstat($fh); + if ($stat['size']) { + $this->cache->update($item->getId(), ['size' => $stat['size']]); + } + } + } + }, $path); + // FIXME: this won't proceed with the next item, needs revamping of getIncomplete() + // to make this possible + $lastPath = $path; + } + } + + /** + * Unlike the default Cache::getIncomplete this one sorts by path. + * + * This is needed since self::backgroundScan doesn't fix child entries when running on a parent folder. + * By sorting by path we ensure that we encounter the child entries first. + * + * @return false|string + * @throws \OCP\DB\Exception + */ + private function getIncomplete() { + $query = $this->connection->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->where($query->expr()->eq('storage', $query->createNamedParameter($this->cache->getNumericStorageId(), IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))) + ->orderBy('path', 'DESC') + ->setMaxResults(1); + + $result = $query->executeQuery(); + $path = $result->fetchOne(); + $result->closeCursor(); + + if ($path === false) { + return false; + } + + // Make sure Oracle does not continue with null for empty strings + return (string)$path; + } +} diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index 921c50fd958..ea60de137d2 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -163,14 +163,14 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil * * @param string $path * @param \OC\Files\Storage\Storage (optional) the storage to pass to the scanner - * @return \OC\Files\ObjectStore\NoopScanner + * @return \OC\Files\ObjectStore\ObjectStoreScanner */ public function getScanner($path = '', $storage = null) { if (!$storage) { $storage = $this; } if (!isset($this->scanner)) { - $this->scanner = new NoopScanner($storage); + $this->scanner = new ObjectStoreScanner($storage); } return $this->scanner; } diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php index 01beefcc6d6..35d6f96d33a 100644 --- a/lib/private/Mail/Message.php +++ b/lib/private/Mail/Message.php @@ -76,6 +76,22 @@ class Message implements IMessage { } /** + * Can be used to "attach content inline" as message parts with specific MIME type and encoding. + * {@inheritDoc} + * @since 26.0.0 + */ + public function attachInline(string $body, string $name, string $contentType = null): IMessage { + # To be sure this works with iCalendar messages, we encode with 8bit instead of + # quoted-printable encoding. We save the current encoder, replace the current + # encoder with an 8bit encoder and after we've finished, we reset the encoder + # to the previous one. Originally intended to be added after the message body, + # as it is curently unknown if all mail clients handle this properly if added + # before. + $this->symfonyEmail->embed($body, $name, $contentType); + return $this; + } + + /** * Converts the [['displayName' => 'email'], ['displayName2' => 'email2']] arrays to valid Adresses * * @param array $addresses Array of mail addresses diff --git a/lib/private/SystemTag/SystemTagsInFilesDetector.php b/lib/private/SystemTag/SystemTagsInFilesDetector.php new file mode 100644 index 00000000000..c9f26c58c02 --- /dev/null +++ b/lib/private/SystemTag/SystemTagsInFilesDetector.php @@ -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()); + } +} diff --git a/lib/public/Mail/IMessage.php b/lib/public/Mail/IMessage.php index 35a67bc4bb8..b3cc86839f1 100644 --- a/lib/public/Mail/IMessage.php +++ b/lib/public/Mail/IMessage.php @@ -40,6 +40,18 @@ interface IMessage { public function attach(IAttachment $attachment): IMessage; /** + * Can be used to "attach content inline" as message parts with specific MIME type and encoding. + * + * @param string $body body of the MIME part + * @param string $name the file name + * @param string|null $contentType MIME Content-Type (e.g. text/plain or text/calendar) + * + * @return IMessage + * @since 26.0.0 + */ + public function attachInline(string $body, string $name, string $contentType = null): IMessage; + + /** * Set the from address of this message. * * If no "From" address is used \OC\Mail\Mailer will use mail_from_address and mail_domain from config.php |