You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
271 lines
7.4 KiB
271 lines
7.4 KiB
<?php |
|
declare(strict_types=1); |
|
/** |
|
* @copyright Copyright (c) 2016, ownCloud, Inc. |
|
* |
|
* @author Joas Schilling <coding@schilljs.com> |
|
* @author Roeland Jago Douma <roeland@famdouma.nl> |
|
* @author Vincent Petry <pvince81@owncloud.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/> |
|
* |
|
*/ |
|
|
|
namespace OC\SystemTag; |
|
|
|
use Doctrine\DBAL\Exception\UniqueConstraintViolationException; |
|
use OCP\DB\QueryBuilder\IQueryBuilder; |
|
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 { |
|
|
|
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; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function getTagIdsForObjects($objIds, string $objectType): array { |
|
if (!\is_array($objIds)) { |
|
$objIds = [$objIds]; |
|
} else if (empty($objIds)) { |
|
return []; |
|
} |
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
$query->select(['systemtagid', 'objectid']) |
|
->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_INT_ARRAY) |
|
->setParameter('objecttype', $objectType) |
|
->addOrderBy('objectid', 'ASC') |
|
->addOrderBy('systemtagid', 'ASC'); |
|
|
|
$mapping = []; |
|
foreach ($objIds as $objId) { |
|
$mapping[$objId] = []; |
|
} |
|
|
|
$result = $query->execute(); |
|
while ($row = $result->fetch()) { |
|
$objectId = $row['objectid']; |
|
$mapping[$objectId][] = $row['systemtagid']; |
|
} |
|
|
|
$result->closeCursor(); |
|
|
|
return $mapping; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function getObjectIdsForTags($tagIds, string $objectType, int $limit = 0, string $offset = ''): array { |
|
if (!\is_array($tagIds)) { |
|
$tagIds = [$tagIds]; |
|
} |
|
|
|
$this->assertTagsExist($tagIds); |
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
$query->selectDistinct('objectid') |
|
->from(self::RELATION_TABLE) |
|
->where($query->expr()->in('systemtagid', $query->createNamedParameter($tagIds, IQueryBuilder::PARAM_INT_ARRAY))) |
|
->andWhere($query->expr()->eq('objecttype', $query->createNamedParameter($objectType))); |
|
|
|
if ($limit) { |
|
if (\count($tagIds) !== 1) { |
|
throw new \InvalidArgumentException('Limit is only allowed with a single tag'); |
|
} |
|
|
|
$query->setMaxResults($limit) |
|
->orderBy('objectid', 'ASC'); |
|
|
|
if ($offset !== '') { |
|
$query->andWhere($query->expr()->gt('objectid', $query->createNamedParameter($offset))); |
|
} |
|
} |
|
|
|
$objectIds = []; |
|
|
|
$result = $query->execute(); |
|
while ($row = $result->fetch()) { |
|
$objectIds[] = $row['objectid']; |
|
} |
|
|
|
return $objectIds; |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function assignTags(string $objId, string $objectType, $tagIds) { |
|
if (!\is_array($tagIds)) { |
|
$tagIds = [$tagIds]; |
|
} |
|
|
|
$this->assertTagsExist($tagIds); |
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
$query->insert(self::RELATION_TABLE) |
|
->values([ |
|
'objectid' => $query->createNamedParameter($objId), |
|
'objecttype' => $query->createNamedParameter($objectType), |
|
'systemtagid' => $query->createParameter('tagid'), |
|
]); |
|
|
|
$tagsAssigned = []; |
|
foreach ($tagIds as $tagId) { |
|
try { |
|
$query->setParameter('tagid', $tagId); |
|
$query->execute(); |
|
$tagsAssigned[] = $tagId; |
|
} catch (UniqueConstraintViolationException $e) { |
|
// ignore existing relations |
|
} |
|
} |
|
|
|
if (empty($tagsAssigned)) { |
|
return; |
|
} |
|
|
|
$this->dispatcher->dispatch(MapperEvent::EVENT_ASSIGN, new MapperEvent( |
|
MapperEvent::EVENT_ASSIGN, |
|
$objectType, |
|
$objId, |
|
$tagsAssigned |
|
)); |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function unassignTags(string $objId, string $objectType, $tagIds) { |
|
if (!\is_array($tagIds)) { |
|
$tagIds = [$tagIds]; |
|
} |
|
|
|
$this->assertTagsExist($tagIds); |
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
$query->delete(self::RELATION_TABLE) |
|
->where($query->expr()->eq('objectid', $query->createParameter('objectid'))) |
|
->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) |
|
->andWhere($query->expr()->in('systemtagid', $query->createParameter('tagids'))) |
|
->setParameter('objectid', $objId) |
|
->setParameter('objecttype', $objectType) |
|
->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY) |
|
->execute(); |
|
|
|
$this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent( |
|
MapperEvent::EVENT_UNASSIGN, |
|
$objectType, |
|
$objId, |
|
$tagIds |
|
)); |
|
} |
|
|
|
/** |
|
* {@inheritdoc} |
|
*/ |
|
public function haveTag($objIds, string $objectType, string $tagId, bool $all = true): bool { |
|
$this->assertTagsExist([$tagId]); |
|
|
|
if (!\is_array($objIds)) { |
|
$objIds = [$objIds]; |
|
} |
|
|
|
$query = $this->connection->getQueryBuilder(); |
|
|
|
if (!$all) { |
|
// If we only need one entry, we make the query lighter, by not |
|
// counting the elements |
|
$query->select('*') |
|
->setMaxResults(1); |
|
} else { |
|
$query->select($query->func()->count($query->expr()->literal(1))); |
|
} |
|
|
|
$query->from(self::RELATION_TABLE) |
|
->where($query->expr()->in('objectid', $query->createParameter('objectids'))) |
|
->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype'))) |
|
->andWhere($query->expr()->eq('systemtagid', $query->createParameter('tagid'))) |
|
->setParameter('objectids', $objIds, IQueryBuilder::PARAM_STR_ARRAY) |
|
->setParameter('tagid', $tagId) |
|
->setParameter('objecttype', $objectType); |
|
|
|
$result = $query->execute(); |
|
$row = $result->fetch(\PDO::FETCH_NUM); |
|
$result->closeCursor(); |
|
|
|
if ($all) { |
|
return ((int)$row[0] === \count($objIds)); |
|
} |
|
|
|
return (bool) $row; |
|
} |
|
|
|
/** |
|
* Asserts that all the given tag ids exist. |
|
* |
|
* @param string[] $tagIds tag ids to check |
|
* |
|
* @throws \OCP\SystemTag\TagNotFoundException if at least one tag did not exist |
|
*/ |
|
private function assertTagsExist($tagIds) { |
|
$tags = $this->tagManager->getTagsByIds($tagIds); |
|
if (\count($tags) !== \count($tagIds)) { |
|
// at least one tag missing, bail out |
|
$foundTagIds = array_map( |
|
function(ISystemTag $tag) { |
|
return $tag->getId(); |
|
}, |
|
$tags |
|
); |
|
$missingTagIds = array_diff($tagIds, $foundTagIds); |
|
throw new TagNotFoundException( |
|
'Tags not found', 0, null, $missingTagIds |
|
); |
|
} |
|
} |
|
}
|
|
|