diff options
Diffstat (limited to 'lib/private/SystemTag/SystemTagObjectMapper.php')
-rw-r--r-- | lib/private/SystemTag/SystemTagObjectMapper.php | 209 |
1 files changed, 152 insertions, 57 deletions
diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php index 5a09a1754f2..1fa5975dafb 100644 --- a/lib/private/SystemTag/SystemTagObjectMapper.php +++ b/lib/private/SystemTag/SystemTagObjectMapper.php @@ -3,63 +3,30 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @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/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ namespace OC\SystemTag; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IDBConnection; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\MapperEvent; use OCP\SystemTag\TagNotFoundException; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; class SystemTagObjectMapper implements ISystemTagObjectMapper { public const RELATION_TABLE = 'systemtag_object_mapping'; - /** @var ISystemTagManager */ - protected $tagManager; - - /** @var IDBConnection */ - protected $connection; - - /** @var EventDispatcherInterface */ - protected $dispatcher; - - /** - * Constructor. - * - * @param IDBConnection $connection database connection - * @param ISystemTagManager $tagManager system tag manager - * @param EventDispatcherInterface $dispatcher - */ - public function __construct(IDBConnection $connection, ISystemTagManager $tagManager, EventDispatcherInterface $dispatcher) { - $this->connection = $connection; - $this->tagManager = $tagManager; - $this->dispatcher = $dispatcher; + public function __construct( + protected IDBConnection $connection, + protected ISystemTagManager $tagManager, + protected IEventDispatcher $dispatcher, + ) { } /** @@ -77,24 +44,25 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ->from(self::RELATION_TABLE) ->where($query->expr()->in('objectid', $query->createParameter('objectids'))) ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) - ->setParameter('objectids', $objIds, IQueryBuilder::PARAM_STR_ARRAY) ->setParameter('objecttype', $objectType) ->addOrderBy('objectid', 'ASC') ->addOrderBy('systemtagid', 'ASC'); - + $chunks = array_chunk($objIds, 900, false); $mapping = []; foreach ($objIds as $objId) { $mapping[$objId] = []; } + foreach ($chunks as $chunk) { + $query->setParameter('objectids', $chunk, IQueryBuilder::PARAM_STR_ARRAY); + $result = $query->executeQuery(); + while ($row = $result->fetch()) { + $objectId = $row['objectid']; + $mapping[$objectId][] = $row['systemtagid']; + } - $result = $query->execute(); - while ($row = $result->fetch()) { - $objectId = $row['objectid']; - $mapping[$objectId][] = $row['systemtagid']; + $result->closeCursor(); } - $result->closeCursor(); - return $mapping; } @@ -129,10 +97,11 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { $objectIds = []; - $result = $query->execute(); + $result = $query->executeQuery(); while ($row = $result->fetch()) { $objectIds[] = $row['objectid']; } + $result->closeCursor(); return $objectIds; } @@ -140,12 +109,33 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { /** * {@inheritdoc} */ - public function assignTags(string $objId, string $objectType, $tagIds) { + public function assignTags(string $objId, string $objectType, $tagIds): void { if (!\is_array($tagIds)) { $tagIds = [$tagIds]; } $this->assertTagsExist($tagIds); + $this->connection->beginTransaction(); + + $query = $this->connection->getQueryBuilder(); + $query->select('systemtagid') + ->from(self::RELATION_TABLE) + ->where($query->expr()->in('systemtagid', $query->createNamedParameter($tagIds, IQueryBuilder::PARAM_INT_ARRAY))) + ->andWhere($query->expr()->eq('objecttype', $query->createNamedParameter($objectType))) + ->andWhere($query->expr()->eq('objectid', $query->createNamedParameter($objId))); + $result = $query->executeQuery(); + $rows = $result->fetchAll(); + $existingTags = []; + foreach ($rows as $row) { + $existingTags[] = $row['systemtagid']; + } + //filter only tags that do not exist in db + $tagIds = array_diff($tagIds, $existingTags); + if (empty($tagIds)) { + // no tags to insert so return here + $this->connection->commit(); + return; + } $query = $this->connection->getQueryBuilder(); $query->insert(self::RELATION_TABLE) @@ -166,6 +156,9 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { } } + $this->updateEtagForTags($tagIds); + + $this->connection->commit(); if (empty($tagsAssigned)) { return; } @@ -181,7 +174,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { /** * {@inheritdoc} */ - public function unassignTags(string $objId, string $objectType, $tagIds) { + public function unassignTags(string $objId, string $objectType, $tagIds): void { if (!\is_array($tagIds)) { $tagIds = [$tagIds]; } @@ -196,7 +189,9 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ->setParameter('objectid', $objId) ->setParameter('objecttype', $objectType) ->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY) - ->execute(); + ->executeStatement(); + + $this->updateEtagForTags($tagIds); $this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent( MapperEvent::EVENT_UNASSIGN, @@ -207,6 +202,21 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { } /** + * Update the etag for the given tags. + * + * @param string[] $tagIds + */ + private function updateEtagForTags(array $tagIds): void { + // Update etag after assigning tags + $md5 = md5(json_encode(time())); + $query = $this->connection->getQueryBuilder(); + $query->update('systemtag') + ->set('etag', $query->createNamedParameter($md5)) + ->where($query->expr()->in('id', $query->createNamedParameter($tagIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->execute(); + } + + /** * {@inheritdoc} */ public function haveTag($objIds, string $objectType, string $tagId, bool $all = true): bool { @@ -235,7 +245,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ->setParameter('tagid', $tagId) ->setParameter('objecttype', $objectType); - $result = $query->execute(); + $result = $query->executeQuery(); $row = $result->fetch(\PDO::FETCH_NUM); $result->closeCursor(); @@ -243,7 +253,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { return ((int)$row[0] === \count($objIds)); } - return (bool) $row; + return (bool)$row; } /** @@ -253,7 +263,7 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { * * @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist */ - private function assertTagsExist($tagIds) { + private function assertTagsExist(array $tagIds): void { $tags = $this->tagManager->getTagsByIds($tagIds); if (\count($tags) !== \count($tagIds)) { // at least one tag missing, bail out @@ -269,4 +279,89 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ); } } + + /** + * {@inheritdoc} + */ + public function setObjectIdsForTag(string $tagId, string $objectType, array $objectIds): void { + $currentObjectIds = $this->getObjectIdsForTags($tagId, $objectType); + $removedObjectIds = array_diff($currentObjectIds, $objectIds); + $addedObjectIds = array_diff($objectIds, $currentObjectIds); + + $this->connection->beginTransaction(); + $query = $this->connection->getQueryBuilder(); + $query->delete(self::RELATION_TABLE) + ->where($query->expr()->eq('systemtagid', $query->createNamedParameter($tagId, IQueryBuilder::PARAM_INT))) + ->andWhere($query->expr()->eq('objecttype', $query->createNamedParameter($objectType))) + ->executeStatement(); + $this->connection->commit(); + + foreach ($removedObjectIds as $objectId) { + $this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent( + MapperEvent::EVENT_UNASSIGN, + $objectType, + (string)$objectId, + [(int)$tagId] + )); + } + + if (empty($objectIds)) { + return; + } + + $this->connection->beginTransaction(); + $query = $this->connection->getQueryBuilder(); + $query->insert(self::RELATION_TABLE) + ->values([ + 'systemtagid' => $query->createNamedParameter($tagId, IQueryBuilder::PARAM_INT), + 'objecttype' => $query->createNamedParameter($objectType), + 'objectid' => $query->createParameter('objectid'), + ]); + + foreach (array_unique($objectIds) as $objectId) { + $query->setParameter('objectid', (string)$objectId); + $query->executeStatement(); + } + + $this->updateEtagForTags([$tagId]); + $this->connection->commit(); + + // Dispatch assign events for new object ids + foreach ($addedObjectIds as $objectId) { + $this->dispatcher->dispatch(MapperEvent::EVENT_ASSIGN, new MapperEvent( + MapperEvent::EVENT_ASSIGN, + $objectType, + (string)$objectId, + [(int)$tagId] + )); + } + + // Dispatch unassign events for removed object ids + foreach ($removedObjectIds as $objectId) { + $this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent( + MapperEvent::EVENT_UNASSIGN, + $objectType, + (string)$objectId, + [(int)$tagId] + )); + } + } + + /** + * {@inheritdoc} + */ + public function getAvailableObjectTypes(): array { + $query = $this->connection->getQueryBuilder(); + $query->selectDistinct('objecttype') + ->from(self::RELATION_TABLE); + + $result = $query->executeQuery(); + $objectTypes = []; + while ($row = $result->fetch()) { + $objectTypes[] = $row['objecttype']; + } + $result->closeCursor(); + + return $objectTypes; + } } |