aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/SystemTag/SystemTagObjectMapper.php
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/SystemTag/SystemTagObjectMapper.php')
-rw-r--r--lib/private/SystemTag/SystemTagObjectMapper.php209
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;
+ }
}