aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/composer/composer/autoload_classmap.php3
-rw-r--r--lib/composer/composer/autoload_static.php3
-rw-r--r--lib/private/Files/Cache/CacheQueryBuilder.php21
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php99
-rw-r--r--lib/private/Files/Cache/Scanner.php17
-rw-r--r--lib/private/Files/Node/Folder.php36
-rw-r--r--lib/private/Files/ObjectStore/NoopScanner.php81
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreScanner.php98
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php4
-rw-r--r--lib/private/Mail/Message.php16
-rw-r--r--lib/private/SystemTag/SystemTagsInFilesDetector.php72
-rw-r--r--lib/public/Mail/IMessage.php12
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