summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--db_structure.xml124
-rw-r--r--lib/private/server.php29
-rw-r--r--lib/private/systemtag/systemtag.php90
-rw-r--r--lib/private/systemtag/systemtagmanager.php264
-rw-r--r--lib/private/systemtag/systemtagobjectmapper.php213
-rw-r--r--lib/public/iservercontainer.php18
-rw-r--r--lib/public/systemtag/isystemtag.php2
-rw-r--r--lib/public/systemtag/isystemtagmanager.php (renamed from lib/public/systemtag/isystemtagsmanager.php)10
-rw-r--r--lib/public/systemtag/isystemtagobjectmapper.php10
-rw-r--r--tests/lib/systemtag/systemtagmanager.php396
-rw-r--r--tests/lib/systemtag/systemtagobjectmapper.php327
11 files changed, 1478 insertions, 5 deletions
diff --git a/db_structure.xml b/db_structure.xml
index 8135746233e..637e95d6ef4 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -1064,6 +1064,130 @@
</table>
<table>
+ <!--
+ List of system-wide tags
+ -->
+ <name>*dbprefix*systemtag</name>
+
+ <declaration>
+
+ <field>
+ <name>id</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <autoincrement>1</autoincrement>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <!-- Tag name -->
+ <field>
+ <name>name</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <!-- Visibility: 0 user-not-visible, 1 user-visible -->
+ <field>
+ <name>visibility</name>
+ <type>integer</type>
+ <default>1</default>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+
+ <!-- Editable: 0 user-not-editable, 1 user-editable -->
+ <field>
+ <name>editable</name>
+ <type>integer</type>
+ <default>1</default>
+ <notnull>true</notnull>
+ <length>1</length>
+ </field>
+
+ <index>
+ <name>tag_ident</name>
+ <unique>true</unique>
+ <field>
+ <name>name</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>visibility</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>editable</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ </declaration>
+ </table>
+
+ <table>
+
+ <!--
+ System tag to object associations per object type.
+ -->
+ <name>*dbprefix*systemtag_object_mapping</name>
+
+ <declaration>
+
+ <!-- object id (ex: file id for files)-->
+ <field>
+ <name>objectid</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <!-- object type (ex: "files")-->
+ <field>
+ <name>objecttype</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <!-- Foreign Key systemtag::id -->
+ <field>
+ <name>systemtagid</name>
+ <type>integer</type>
+ <default>0</default>
+ <notnull>true</notnull>
+ <unsigned>true</unsigned>
+ <length>4</length>
+ </field>
+
+ <index>
+ <unique>true</unique>
+ <name>mapping</name>
+ <field>
+ <name>objecttype</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>objectid</name>
+ <sorting>ascending</sorting>
+ </field>
+ <field>
+ <name>systemtagid</name>
+ <sorting>ascending</sorting>
+ </field>
+ </index>
+
+ </declaration>
+
+ </table>
+
+ <table>
<!--
Namespaced Key-Value Store for arbitrary data.
diff --git a/lib/private/server.php b/lib/private/server.php
index ea5937dfea4..8171a00b3d1 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -138,6 +138,12 @@ class Server extends SimpleContainer implements IServerContainer {
$tagMapper = $c->query('TagMapper');
return new TagManager($tagMapper, $c->getUserSession());
});
+ $this->registerService('SystemTagManager', function (Server $c) {
+ return new SystemTag\SystemTagManager($c->getDatabaseConnection());
+ });
+ $this->registerService('SystemTagObjectMapper', function (Server $c) {
+ return new SystemTag\SystemTagObjectMapper($c->getDatabaseConnection(), $c->getSystemTagManager());
+ });
$this->registerService('RootFolder', function (Server $c) {
// TODO: get user and user manager from container as well
$user = \OC_User::getUser();
@@ -582,6 +588,29 @@ class Server extends SimpleContainer implements IServerContainer {
}
/**
+ * Returns the system-tag manager
+ *
+ * @return \OCP\SystemTag\ISystemTagManager
+ *
+ * @since 9.0.0
+ */
+ public function getSystemTagManager() {
+ return $this->query('SystemTagManager');
+ }
+
+ /**
+ * Returns the system-tag object mapper
+ *
+ * @return \OCP\SystemTag\ISystemTagObjectMapper
+ *
+ * @since 9.0.0
+ */
+ public function getSystemTagObjectMapper() {
+ return $this->query('SystemTagObjectMapper');
+ }
+
+
+ /**
* Returns the avatar manager, used for avatar functionality
*
* @return \OCP\IAvatarManager
diff --git a/lib/private/systemtag/systemtag.php b/lib/private/systemtag/systemtag.php
new file mode 100644
index 00000000000..b79cc33cfb2
--- /dev/null
+++ b/lib/private/systemtag/systemtag.php
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @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 \OCP\IDBConnection;
+
+class SystemTag implements \OCP\SystemTag\ISystemTag {
+
+ /**
+ * @var string
+ */
+ private $id;
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var bool
+ */
+ private $userVisible;
+
+ /**
+ * @var bool
+ */
+ private $userAssignable;
+
+ /**
+ * Constructor.
+ *
+ * @param string $id tag id
+ * @param string $name tag name
+ * @param bool $userVisible whether the tag is user visible
+ * @param bool $userAssignable whether the tag is user assignable
+ */
+ public function __construct($id, $name, $userVisible, $userAssignable) {
+ $this->id = $id;
+ $this->name = $name;
+ $this->userVisible = $userVisible;
+ $this->userAssignable = $userAssignable;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId() {
+ return $this->id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName() {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isUserVisible() {
+ return $this->userVisible;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isUserAssignable() {
+ return $this->userAssignable;
+ }
+}
diff --git a/lib/private/systemtag/systemtagmanager.php b/lib/private/systemtag/systemtagmanager.php
new file mode 100644
index 00000000000..e307825ecfa
--- /dev/null
+++ b/lib/private/systemtag/systemtagmanager.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @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 \OCP\IDBConnection;
+use \OCP\SystemTag\TagNotFoundException;
+use \OCP\SystemTag\TagAlreadyExistsException;
+
+use \Doctrine\DBAL\Exception\UniqueConstraintViolationException;
+
+class SystemTagManager implements \OCP\SystemTag\ISystemTagManager {
+
+ const TAG_TABLE = 'systemtag';
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ /**
+ * Prepared query for selecting tags directly
+ *
+ * @var \OCP\DB\QueryBuilder\IQueryBuilder
+ */
+ private $selectTagQuery;
+
+ /**
+ * Constructor.
+ *
+ * @param IDBConnection $connection database connection
+ */
+ public function __construct(IDBConnection $connection) {
+ $this->connection = $connection;
+
+ $query = $this->connection->getQueryBuilder();
+ $this->selectTagQuery = $query->select('*')
+ ->from(self::TAG_TABLE)
+ ->where($query->expr()->eq('name', $query->createParameter('name')))
+ ->andWhere($query->expr()->eq('visibility', $query->createParameter('visibility')))
+ ->andWhere($query->expr()->eq('editable', $query->createParameter('editable')));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTagsById($tagIds) {
+ if (!is_array($tagIds)) {
+ $tagIds = [$tagIds];
+ }
+
+ $tags = [];
+
+ // note: not all databases will fail if it's a string or starts with a number
+ foreach ($tagIds as $tagId) {
+ if (!is_numeric($tagId)) {
+ throw new \InvalidArgumentException('Tag id must be integer');
+ }
+ }
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')
+ ->from(self::TAG_TABLE)
+ ->where($query->expr()->in('id', $query->createParameter('tagids')))
+ ->addOrderBy('name', 'ASC')
+ ->addOrderBy('visibility', 'ASC')
+ ->addOrderBy('editable', 'ASC')
+ ->setParameter('tagids', $tagIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
+
+ $result = $query->execute();
+ while ($row = $result->fetch()) {
+ $tags[$row['id']] = $this->createSystemTagFromRow($row);
+ }
+
+ $result->closeCursor();
+
+ if (count($tags) !== count($tagIds)) {
+ throw new TagNotFoundException(
+ 'Tag(s) with id(s) ' . json_encode(array_diff($tagIds, array_keys($tags))) . ' not found'
+ );
+ }
+
+ return $tags;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getAllTags($visibilityFilter = null, $nameSearchPattern = null) {
+ $tags = [];
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select('*')
+ ->from(self::TAG_TABLE);
+
+ if (!is_null($visibilityFilter)) {
+ $query->andWhere($query->expr()->eq('visibility', $query->createNamedParameter((int)$visibilityFilter)));
+ }
+
+ if (!empty($nameSearchPattern)) {
+ $query->andWhere(
+ $query->expr()->like(
+ 'name',
+ $query->expr()->literal('%' . $this->connection->escapeLikeParameter($nameSearchPattern). '%')
+ )
+ );
+ }
+
+ $query
+ ->addOrderBy('name', 'ASC')
+ ->addOrderBy('visibility', 'ASC')
+ ->addOrderBy('editable', 'ASC');
+
+ $result = $query->execute();
+ while ($row = $result->fetch()) {
+ $tags[$row['id']] = $this->createSystemTagFromRow($row);
+ }
+
+ $result->closeCursor();
+
+ return $tags;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTag($tagName, $userVisible, $userAssignable) {
+ $userVisible = (int)$userVisible;
+ $userAssignable = (int)$userAssignable;
+
+ $result = $this->selectTagQuery
+ ->setParameter('name', $tagName)
+ ->setParameter('visibility', $userVisible)
+ ->setParameter('editable', $userAssignable)
+ ->execute();
+
+ $row = $result->fetch();
+ $result->closeCursor();
+ if (!$row) {
+ throw new TagNotFoundException(
+ 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') does not exist'
+ );
+ }
+
+ return $this->createSystemTagFromRow($row);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function createTag($tagName, $userVisible, $userAssignable) {
+ $userVisible = (int)$userVisible;
+ $userAssignable = (int)$userAssignable;
+
+ $query = $this->connection->getQueryBuilder();
+ $query->insert(self::TAG_TABLE)
+ ->values([
+ 'name' => $query->createNamedParameter($tagName),
+ 'visibility' => $query->createNamedParameter($userVisible),
+ 'editable' => $query->createNamedParameter($userAssignable),
+ ]);
+
+ try {
+ $query->execute();
+ } catch (UniqueConstraintViolationException $e) {
+ throw new TagAlreadyExistsException(
+ 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
+ 0,
+ $e
+ );
+ }
+
+ $tagId = $this->connection->lastInsertId('*PREFIX*' . self::TAG_TABLE);
+
+ return new SystemTag(
+ (int)$tagId,
+ $tagName,
+ (bool)$userVisible,
+ (bool)$userAssignable
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function updateTag($tagId, $tagName, $userVisible, $userAssignable) {
+ $userVisible = (int)$userVisible;
+ $userAssignable = (int)$userAssignable;
+
+ $query = $this->connection->getQueryBuilder();
+ $query->update(self::TAG_TABLE)
+ ->set('name', $query->createParameter('name'))
+ ->set('visibility', $query->createParameter('visibility'))
+ ->set('editable', $query->createParameter('editable'))
+ ->where($query->expr()->eq('id', $query->createParameter('tagid')))
+ ->setParameter('name', $tagName)
+ ->setParameter('visibility', $userVisible)
+ ->setParameter('editable', $userAssignable)
+ ->setParameter('tagid', $tagId);
+
+ try {
+ if ($query->execute() === 0) {
+ throw new TagNotFoundException(
+ 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') does not exist'
+ );
+ }
+ } catch (UniqueConstraintViolationException $e) {
+ throw new TagAlreadyExistsException(
+ 'Tag ("' . $tagName . '", '. $userVisible . ', ' . $userAssignable . ') already exists',
+ 0,
+ $e
+ );
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function deleteTags($tagIds) {
+ if (!is_array($tagIds)) {
+ $tagIds = [$tagIds];
+ }
+
+ // delete relationships first
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(SystemTagObjectMapper::RELATION_TABLE)
+ ->where($query->expr()->in('systemtagid', $query->createParameter('tagids')))
+ ->setParameter('tagids', $tagIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
+ ->execute();
+
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(self::TAG_TABLE)
+ ->where($query->expr()->in('id', $query->createParameter('tagids')))
+ ->setParameter('tagids', $tagIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY);
+
+ if ($query->execute() === 0) {
+ throw new TagNotFoundException(
+ 'Tag does not exist'
+ );
+ }
+ }
+
+ private function createSystemTagFromRow($row) {
+ return new SystemTag((int)$row['id'], $row['name'], (bool)$row['visibility'], (bool)$row['editable']);
+ }
+}
diff --git a/lib/private/systemtag/systemtagobjectmapper.php b/lib/private/systemtag/systemtagobjectmapper.php
new file mode 100644
index 00000000000..f2adc2e3ebd
--- /dev/null
+++ b/lib/private/systemtag/systemtagobjectmapper.php
@@ -0,0 +1,213 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @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 \OCP\SystemTag\ISystemTagManager;
+use \OCP\IDBConnection;
+use \OCP\SystemTag\TagNotFoundException;
+use \Doctrine\DBAL\Exception\UniqueConstraintViolationException;
+
+class SystemTagObjectMapper implements \OCP\SystemTag\ISystemTagObjectMapper {
+
+ const RELATION_TABLE = 'systemtag_object_mapping';
+
+ /**
+ * @var ISystemTagManager
+ */
+ private $tagManager;
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ /**
+ * Constructor.
+ *
+ * @param IDBConnection $connection database connection
+ * @param ISystemTagManager $tagManager system tag manager
+ */
+ public function __construct(IDBConnection $connection, ISystemTagManager $tagManager) {
+ $this->connection = $connection;
+ $this->tagManager = $tagManager;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTagIdsForObjects($objIds, $objectType) {
+ if (!is_array($objIds)) {
+ $objIds = [$objIds];
+ }
+
+ $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, \Doctrine\DBAL\Connection::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, $objectType) {
+ if (!is_array($tagIds)) {
+ $tagIds = [$tagIds];
+ }
+
+ $this->assertTagsExist($tagIds);
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select($query->createFunction('DISTINCT(objectid)'))
+ ->from(self::RELATION_TABLE)
+ ->where($query->expr()->in('systemtagid', $query->createParameter('tagids')))
+ ->andWhere($query->expr()->eq('objecttype', $query->createParameter('objecttype')))
+ ->setParameter('tagids', $tagIds, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
+ ->setParameter('objecttype', $objectType);
+
+ $objectIds = [];
+
+ $result = $query->execute();
+ while ($row = $result->fetch()) {
+ $objectIds[] = $row['objectid'];
+ }
+
+ return $objectIds;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function assignTags($objId, $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'),
+ ]);
+
+ foreach ($tagIds as $tagId) {
+ try {
+ $query->setParameter('tagid', $tagId);
+ $query->execute();
+ } catch (UniqueConstraintViolationException $e) {
+ // ignore existing relations
+ }
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function unassignTags($objId, $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, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
+ ->execute();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function haveTag($objIds, $objectType, $tagId, $all = true) {
+ $this->assertTagsExist([$tagId]);
+
+ $query = $this->connection->getQueryBuilder();
+ $query->select($query->createFunction('COUNT(1)'))
+ ->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, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY)
+ ->setParameter('tagid', $tagId)
+ ->setParameter('objecttype', $objectType)
+ ->setMaxResults(1);
+
+ $result = $query->execute();
+ $row = $result->fetch(\PDO::FETCH_NUM);
+ $result->closeCursor();
+ if ($all) {
+ return ((int)$row[0] === count($objIds));
+ } else {
+ return (int)$row[0] > 0;
+ }
+ }
+
+ /**
+ * 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->getTagsById($tagIds);
+ if (count($tags) !== count($tagIds)) {
+ // at least one tag missing, bail out
+ $foundTagIds = array_map(
+ function($tag) {
+ return $tag->getId();
+ },
+ $tags
+ );
+ $missingTagIds = array_diff($tagIds, $foundTagIds);
+ throw new TagNotFoundException('Tags ' . json_encode($missingTagIds) . ' do not exist');
+ }
+ }
+}
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index d85f812b2e7..7cb2672254b 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -470,4 +470,22 @@ interface IServerContainer {
* @since 8.2.0
*/
public function getNotificationManager();
+
+ /**
+ * Returns the system-tag manager
+ *
+ * @return \OCP\SystemTag\ISystemTagManager
+ *
+ * @since 9.0.0
+ */
+ public function getSystemTagManager();
+
+ /**
+ * Returns the system-tag object mapper
+ *
+ * @return \OCP\SystemTag\ISystemTagObjectMapper
+ *
+ * @since 9.0.0
+ */
+ public function getSystemTagObjectMapper();
}
diff --git a/lib/public/systemtag/isystemtag.php b/lib/public/systemtag/isystemtag.php
index 76a812f38dc..26609fd8af7 100644
--- a/lib/public/systemtag/isystemtag.php
+++ b/lib/public/systemtag/isystemtag.php
@@ -62,7 +62,7 @@ interface ISystemTag {
*
* @since 9.0.0
*/
- public function isUserAsssignable();
+ public function isUserAssignable();
}
diff --git a/lib/public/systemtag/isystemtagsmanager.php b/lib/public/systemtag/isystemtagmanager.php
index df59cc48d52..2020ec52900 100644
--- a/lib/public/systemtag/isystemtagsmanager.php
+++ b/lib/public/systemtag/isystemtagmanager.php
@@ -31,9 +31,11 @@ interface ISystemTagManager {
/**
* Returns the tag objects matching the given tag ids.
*
- * @param array|string $tagIds The ID or array of IDs of the tags to retrieve
+ * @param array|string $tagIds id or array of unique ids of the tag to retrieve
*
- * @return \OCP\SystemTag\ISystemTag[] array of system tags or empty array if none found
+ * @return \OCP\SystemTag\ISystemTag[] array of system tags with tag id as key
+ *
+ * @throws \OCP\SystemTag\TagNotFoundException if at least one given tag id did no exist
*
* @since 9.0.0
*/
@@ -72,14 +74,14 @@ interface ISystemTagManager {
/**
* Returns all known tags, optionally filtered by visibility.
*
- * @param bool $visibleOnly whether to only return user visible tags
+ * @param bool|null $visibilityFilter filter by visibility if non-null
* @param string $nameSearchPattern optional search pattern for the tag name
*
* @return \OCP\SystemTag\ISystemTag[] array of system tags or empty array if none found
*
* @since 9.0.0
*/
- public function getAllTags($visibleOnly = false, $nameSearchPattern = null);
+ public function getAllTags($visibilityFilter = null, $nameSearchPattern = null);
/**
* Updates the given tag
diff --git a/lib/public/systemtag/isystemtagobjectmapper.php b/lib/public/systemtag/isystemtagobjectmapper.php
index 8c6c27c4846..e2ac1fab124 100644
--- a/lib/public/systemtag/isystemtagobjectmapper.php
+++ b/lib/public/systemtag/isystemtagobjectmapper.php
@@ -69,6 +69,11 @@ interface ISystemTagObjectMapper {
/**
* Assign the given tags to the given object.
*
+ * If at least one of the given tag ids doesn't exist, none of the tags
+ * will be assigned.
+ *
+ * If the relationship already existed, fail silently.
+ *
* @param string $objId object id
* @param string $objectType object type
* @param string|array $tagIds tag id or array of tag ids to assign
@@ -83,6 +88,11 @@ interface ISystemTagObjectMapper {
/**
* Unassign the given tags from the given object.
*
+ * If at least one of the given tag ids doesn't exist, none of the tags
+ * will be unassigned.
+ *
+ * If the relationship did not exist in the first place, fail silently.
+ *
* @param string $objId object id
* @param string $objectType object type
* @param string|array $tagIds tag id or array of tag ids to unassign
diff --git a/tests/lib/systemtag/systemtagmanager.php b/tests/lib/systemtag/systemtagmanager.php
new file mode 100644
index 00000000000..4a19d85b0c4
--- /dev/null
+++ b/tests/lib/systemtag/systemtagmanager.php
@@ -0,0 +1,396 @@
+<?php
+
+/**
+ * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ *
+*/
+
+namespace Test\SystemTag;
+
+use \OCP\SystemTag\ISystemTagManager;
+use \OCP\SystemTag\TagNotFoundException;
+use \OCP\SystemTag\TagAlreadyExistsException;
+use \OCP\IDBConnection;
+
+class TestSystemTagManager extends \Test\TestCase {
+
+ /**
+ * @var ISystemTagManager
+ **/
+ private $tagManager;
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->connection = \OC::$server->getDatabaseConnection();
+ $this->tagManager = new \OC\SystemTag\SystemTagManager($this->connection);
+ }
+
+ public function tearDown() {
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(\OC\SystemTag\SystemTagObjectMapper::RELATION_TABLE)->execute();
+ $query->delete(\OC\SystemTag\SystemTagManager::TAG_TABLE)->execute();
+ }
+
+ public function getAllTagsDataProvider() {
+ return [
+ [
+ // no tags at all
+ []
+ ],
+ [
+ // simple
+ [
+ ['one', false, false],
+ ['two', false, false],
+ ]
+ ],
+ [
+ // duplicate names, different flags
+ [
+ ['one', false, false],
+ ['one', true, false],
+ ['one', false, true],
+ ['one', true, true],
+ ['two', false, false],
+ ['two', false, true],
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getAllTagsDataProvider
+ */
+ public function testGetAllTags($testTags) {
+ $testTagsById = [];
+ foreach ($testTags as $testTag) {
+ $tag = $this->tagManager->createTag($testTag[0], $testTag[1], $testTag[2]);
+ $testTagsById[$tag->getId()] = $tag;
+ }
+
+ $tagList = $this->tagManager->getAllTags();
+
+ $this->assertCount(count($testTags), $tagList);
+
+ foreach ($testTagsById as $testTagId => $testTag) {
+ $this->assertTrue(isset($tagList[$testTagId]));
+ $this->assertSameTag($tagList[$testTagId], $testTag);
+ }
+ }
+
+ public function getAllTagsFilteredDataProvider() {
+ return [
+ [
+ [
+ // no tags at all
+ ],
+ null,
+ null,
+ []
+ ],
+ // filter by visibile only
+ [
+ // none visible
+ [
+ ['one', false, false],
+ ['two', false, false],
+ ],
+ true,
+ null,
+ []
+ ],
+ [
+ // one visible
+ [
+ ['one', true, false],
+ ['two', false, false],
+ ],
+ true,
+ null,
+ [
+ ['one', true, false],
+ ]
+ ],
+ [
+ // one invisible
+ [
+ ['one', true, false],
+ ['two', false, false],
+ ],
+ false,
+ null,
+ [
+ ['two', false, false],
+ ]
+ ],
+ // filter by name pattern
+ [
+ [
+ ['one', true, false],
+ ['one', false, false],
+ ['two', true, false],
+ ],
+ null,
+ 'on',
+ [
+ ['one', true, false],
+ ['one', false, false],
+ ]
+ ],
+ // filter by name pattern and visibility
+ [
+ // one visible
+ [
+ ['one', true, false],
+ ['two', true, false],
+ ['one', false, false],
+ ],
+ true,
+ 'on',
+ [
+ ['one', true, false],
+ ]
+ ],
+ // filter by name pattern in the middle
+ [
+ // one visible
+ [
+ ['abcdefghi', true, false],
+ ['two', true, false],
+ ],
+ null,
+ 'def',
+ [
+ ['abcdefghi', true, false],
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @dataProvider getAllTagsFilteredDataProvider
+ */
+ public function testGetAllTagsFiltered($testTags, $visibilityFilter, $nameSearch, $expectedResults) {
+ foreach ($testTags as $testTag) {
+ $tag = $this->tagManager->createTag($testTag[0], $testTag[1], $testTag[2]);
+ }
+
+ $testTagsById = [];
+ foreach ($expectedResults as $expectedTag) {
+ $tag = $this->tagManager->getTag($expectedTag[0], $expectedTag[1], $expectedTag[2]);
+ $testTagsById[$tag->getId()] = $tag;
+ }
+
+ $tagList = $this->tagManager->getAllTags($visibilityFilter, $nameSearch);
+
+ $this->assertCount(count($testTagsById), $tagList);
+
+ foreach ($testTagsById as $testTagId => $testTag) {
+ $this->assertTrue(isset($tagList[$testTagId]));
+ $this->assertSameTag($tagList[$testTagId], $testTag);
+ }
+ }
+
+ public function oneTagMultipleFlagsProvider() {
+ return [
+ ['one', false, false],
+ ['one', true, false],
+ ['one', false, true],
+ ['one', true, true],
+ ];
+ }
+
+ /**
+ * @dataProvider oneTagMultipleFlagsProvider
+ * @expectedException \OCP\SystemTag\TagAlreadyExistsException
+ */
+ public function testCreateDuplicate($name, $userVisible, $userAssignable) {
+ try {
+ $this->tagManager->createTag($name, $userVisible, $userAssignable);
+ } catch (\Exception $e) {
+ $this->assertTrue(false, 'No exception thrown for the first create call');
+ }
+ $this->tagManager->createTag($name, $userVisible, $userAssignable);
+ }
+
+ /**
+ * @dataProvider oneTagMultipleFlagsProvider
+ */
+ public function testGetExistingTag($name, $userVisible, $userAssignable) {
+ $tag1 = $this->tagManager->createTag($name, $userVisible, $userAssignable);
+ $tag2 = $this->tagManager->getTag($name, $userVisible, $userAssignable);
+
+ $this->assertSameTag($tag1, $tag2);
+ }
+
+ /**
+ * @dataProvider oneTagMultipleFlagsProvider
+ */
+ public function testGetExistingTagById($name, $userVisible, $userAssignable) {
+ $tag1 = $this->tagManager->createTag('one', true, false);
+ $tag2 = $this->tagManager->createTag('two', false, true);
+
+ $tagList = $this->tagManager->getTagsById([$tag1->getId(), $tag2->getId()]);
+
+ $this->assertCount(2, $tagList);
+
+ $this->assertSameTag($tag1, $tagList[$tag1->getId()]);
+ $this->assertSameTag($tag2, $tagList[$tag2->getId()]);
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testGetNonExistingTag() {
+ $this->tagManager->getTag('nonexist', false, false);
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testGetNonExistingTagsById() {
+ $tag1 = $this->tagManager->createTag('one', true, false);
+ $this->tagManager->getTagsById([$tag1->getId(), 100, 101]);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ */
+ public function testGetInvalidTagIdFormat() {
+ $tag1 = $this->tagManager->createTag('one', true, false);
+ $this->tagManager->getTagsById([$tag1->getId() . 'suffix']);
+ }
+
+ public function updateTagProvider() {
+ return [
+ [
+ // update name
+ ['one', true, true],
+ ['two', true, true]
+ ],
+ [
+ // update one flag
+ ['one', false, true],
+ ['one', true, true]
+ ],
+ [
+ // update all flags
+ ['one', false, false],
+ ['one', true, true]
+ ],
+ [
+ // update all
+ ['one', false, false],
+ ['two', true, true]
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider updateTagProvider
+ */
+ public function testUpdateTag($tagCreate, $tagUpdated) {
+ $tag1 = $this->tagManager->createTag(
+ $tagCreate[0],
+ $tagCreate[1],
+ $tagCreate[2]
+ );
+ $this->tagManager->updateTag(
+ $tag1->getId(),
+ $tagUpdated[0],
+ $tagUpdated[1],
+ $tagUpdated[2]
+ );
+ $tag2 = $this->tagManager->getTag(
+ $tagUpdated[0],
+ $tagUpdated[1],
+ $tagUpdated[2]
+ );
+
+ $this->assertEquals($tag2->getId(), $tag1->getId());
+ $this->assertEquals($tag2->getName(), $tagUpdated[0]);
+ $this->assertEquals($tag2->isUserVisible(), $tagUpdated[1]);
+ $this->assertEquals($tag2->isUserAssignable(), $tagUpdated[2]);
+ }
+
+ /**
+ * @dataProvider updateTagProvider
+ * @expectedException \OCP\SystemTag\TagAlreadyExistsException
+ */
+ public function testUpdateTagDuplicate($tagCreate, $tagUpdated) {
+ $this->tagManager->createTag(
+ $tagCreate[0],
+ $tagCreate[1],
+ $tagCreate[2]
+ );
+ $tag2 = $this->tagManager->createTag(
+ $tagUpdated[0],
+ $tagUpdated[1],
+ $tagUpdated[2]
+ );
+
+ // update to match the first tag
+ $this->tagManager->updateTag(
+ $tag2->getId(),
+ $tagCreate[0],
+ $tagCreate[1],
+ $tagCreate[2]
+ );
+ }
+
+ public function testDeleteTags() {
+ $tag1 = $this->tagManager->createTag('one', true, false);
+ $tag2 = $this->tagManager->createTag('two', false, true);
+
+ $this->tagManager->deleteTags([$tag1->getId(), $tag2->getId()]);
+
+ $this->assertEmpty($this->tagManager->getAllTags());
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testDeleteNonExistingTag() {
+ $this->tagManager->deleteTags([100]);
+ }
+
+ public function testDeleteTagRemovesRelations() {
+ $tag1 = $this->tagManager->createTag('one', true, false);
+ $tag2 = $this->tagManager->createTag('two', true, true);
+
+ $tagMapper = new \OC\SystemTag\SystemTagObjectMapper($this->connection, $this->tagManager);
+
+ $tagMapper->assignTags(1, 'testtype', $tag1->getId());
+ $tagMapper->assignTags(1, 'testtype', $tag2->getId());
+ $tagMapper->assignTags(2, 'testtype', $tag1->getId());
+
+ $this->tagManager->deleteTags($tag1->getId());
+
+ $tagIdMapping = $tagMapper->getTagIdsForObjects(
+ [1, 2],
+ 'testtype'
+ );
+
+ $this->assertEquals([
+ 1 => [$tag2->getId()],
+ 2 => [],
+ ], $tagIdMapping);
+ }
+
+ private function assertSameTag($tag1, $tag2) {
+ $this->assertEquals($tag1->getId(), $tag2->getId());
+ $this->assertEquals($tag1->getName(), $tag2->getName());
+ $this->assertEquals($tag1->isUserVisible(), $tag2->isUserVisible());
+ $this->assertEquals($tag1->isUserAssignable(), $tag2->isUserAssignable());
+ }
+
+}
diff --git a/tests/lib/systemtag/systemtagobjectmapper.php b/tests/lib/systemtag/systemtagobjectmapper.php
new file mode 100644
index 00000000000..107496f9a15
--- /dev/null
+++ b/tests/lib/systemtag/systemtagobjectmapper.php
@@ -0,0 +1,327 @@
+<?php
+
+/**
+ * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ *
+*/
+
+namespace Test\SystemTag;
+
+use \OCP\SystemTag\ISystemTag;
+use \OCP\SystemTag\ISystemTagManager;
+use \OCP\SystemTag\ISystemTagObjectMapper;
+use \OCP\SystemTag\TagNotFoundException;
+use \OCP\SystemTag\TagAlreadyExistsException;
+use \OCP\IDBConnection;
+use \OC\SystemTag\SystemTag;
+
+class TestSystemTagObjectMapper extends \Test\TestCase {
+
+ /**
+ * @var ISystemTagManager
+ **/
+ private $tagManager;
+
+ /**
+ * @var ISystemTagObjectMapper
+ **/
+ private $tagMapper;
+
+ /**
+ * @var IDBConnection
+ */
+ private $connection;
+
+ /**
+ * @var ISystemTag
+ */
+ private $tag1;
+
+ /**
+ * @var ISystemTag
+ */
+ private $tag2;
+
+ /**
+ * @var ISystemTag
+ */
+ private $tag3;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->connection = \OC::$server->getDatabaseConnection();
+
+ $this->tagManager = $this->getMockBuilder('OCP\SystemTag\ISystemTagManager')
+ ->getMock();
+
+ $this->tagMapper = new \OC\SystemTag\SystemTagObjectMapper($this->connection, $this->tagManager);
+
+ $this->tag1 = new SystemTag(1, 'testtag1', false, false);
+ $this->tag2 = new SystemTag(2, 'testtag2', true, false);
+ $this->tag3 = new SystemTag(3, 'testtag3', false, false);
+
+ $this->tagManager->expects($this->any())
+ ->method('getTagsById')
+ ->will($this->returnCallback(function($tagIds) {
+ $result = [];
+ if (in_array(1, $tagIds)) {
+ $result[1] = $this->tag1;
+ }
+ if (in_array(2, $tagIds)) {
+ $result[2] = $this->tag2;
+ }
+ if (in_array(3, $tagIds)) {
+ $result[3] = $this->tag3;
+ }
+ return $result;
+ }));
+
+ $this->tagMapper->assignTags(1, 'testtype', $this->tag1->getId());
+ $this->tagMapper->assignTags(1, 'testtype', $this->tag2->getId());
+ $this->tagMapper->assignTags(2, 'testtype', $this->tag1->getId());
+ $this->tagMapper->assignTags(3, 'anothertype', $this->tag1->getId());
+ }
+
+ public function tearDown() {
+ $query = $this->connection->getQueryBuilder();
+ $query->delete(\OC\SystemTag\SystemTagObjectMapper::RELATION_TABLE)->execute();
+ $query->delete(\OC\SystemTag\SystemTagManager::TAG_TABLE)->execute();
+ }
+
+ public function testGetTagsForObjects() {
+ $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
+ [1, 2, 3, 4],
+ 'testtype'
+ );
+
+ $this->assertEquals([
+ 1 => [$this->tag1->getId(), $this->tag2->getId()],
+ 2 => [$this->tag1->getId()],
+ 3 => [],
+ 4 => [],
+ ], $tagIdMapping);
+ }
+
+ public function testGetObjectsForTags() {
+ $objectIds = $this->tagMapper->getObjectIdsForTags(
+ [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()],
+ 'testtype'
+ );
+
+ $this->assertEquals([
+ 1,
+ 2,
+ ], $objectIds);
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testGetObjectsForNonExistingTag() {
+ $this->tagMapper->getObjectIdsForTags(
+ [100],
+ 'testtype'
+ );
+ }
+
+ public function testAssignUnassignTags() {
+ $this->tagMapper->unassignTags(1, 'testtype', [$this->tag1->getId()]);
+
+ $tagIdMapping = $this->tagMapper->getTagIdsForObjects(1, 'testtype');
+ $this->assertEquals([
+ 1 => [$this->tag2->getId()],
+ ], $tagIdMapping);
+
+ $this->tagMapper->assignTags(1, 'testtype', [$this->tag1->getId()]);
+ $this->tagMapper->assignTags(1, 'testtype', $this->tag3->getId());
+
+ $tagIdMapping = $this->tagMapper->getTagIdsForObjects(1, 'testtype');
+
+ $this->assertEquals([
+ 1 => [$this->tag1->getId(), $this->tag2->getId(), $this->tag3->getId()],
+ ], $tagIdMapping);
+ }
+
+ public function testReAssignUnassignTags() {
+ // reassign tag1
+ $this->tagMapper->assignTags(1, 'testtype', [$this->tag1->getId()]);
+
+ // tag 3 was never assigned
+ $this->tagMapper->unassignTags(1, 'testtype', [$this->tag3->getId()]);
+
+ $this->assertTrue(true, 'No error when reassigning/unassigning');
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testAssignNonExistingTags() {
+ $this->tagMapper->assignTags(1, 'testtype', [100]);
+ }
+
+ public function testAssignNonExistingTagInArray() {
+ $caught = false;
+ try {
+ $this->tagMapper->assignTags(1, 'testtype', [100, $this->tag3->getId()]);
+ } catch (TagNotFoundException $e) {
+ $caught = true;
+ }
+
+ $this->assertTrue($caught, 'Exception thrown');
+
+ $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
+ [1],
+ 'testtype'
+ );
+
+ $this->assertEquals([
+ 1 => [$this->tag1->getId(), $this->tag2->getId()],
+ ], $tagIdMapping, 'None of the tags got assigned');
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testUnassignNonExistingTags() {
+ $this->tagMapper->unassignTags(1, 'testtype', [100]);
+ }
+
+ public function testUnassignNonExistingTagsInArray() {
+ $caught = false;
+ try {
+ $this->tagMapper->unassignTags(1, 'testtype', [100, $this->tag1->getId()]);
+ } catch (TagNotFoundException $e) {
+ $caught = true;
+ }
+
+ $this->assertTrue($caught, 'Exception thrown');
+
+ $tagIdMapping = $this->tagMapper->getTagIdsForObjects(
+ [1],
+ 'testtype'
+ );
+
+ $this->assertEquals([
+ 1 => [$this->tag1->getId(), $this->tag2->getId()],
+ ], $tagIdMapping, 'None of the tags got unassigned');
+ }
+
+ public function testHaveTagAllMatches() {
+ $this->assertTrue(
+ $this->tagMapper->haveTag(
+ [1],
+ 'testtype',
+ $this->tag1->getId(),
+ true
+ ),
+ 'object 1 has the tag tag1'
+ );
+
+ $this->assertTrue(
+ $this->tagMapper->haveTag(
+ [1, 2],
+ 'testtype',
+ $this->tag1->getId(),
+ true
+ ),
+ 'object 1 and object 2 ALL have the tag tag1'
+ );
+
+ $this->assertFalse(
+ $this->tagMapper->haveTag(
+ [1, 2],
+ 'testtype',
+ $this->tag2->getId(),
+ true
+ ),
+ 'object 1 has tag2 but not object 2, so not ALL of them'
+ );
+
+ $this->assertFalse(
+ $this->tagMapper->haveTag(
+ [2],
+ 'testtype',
+ $this->tag2->getId(),
+ true
+ ),
+ 'object 2 does not have tag2'
+ );
+
+ $this->assertFalse(
+ $this->tagMapper->haveTag(
+ [3],
+ 'testtype',
+ $this->tag2->getId(),
+ true
+ ),
+ 'object 3 does not have tag1 due to different type'
+ );
+ }
+
+ public function testHaveTagAtLeastOneMatch() {
+ $this->assertTrue(
+ $this->tagMapper->haveTag(
+ [1],
+ 'testtype',
+ $this->tag1->getId(),
+ false
+ ),
+ 'object1 has the tag tag1'
+ );
+
+ $this->assertTrue(
+ $this->tagMapper->haveTag(
+ [1, 2],
+ 'testtype',
+ $this->tag1->getId(),
+ false
+ ),
+ 'object 1 and object 2 both the tag tag1'
+ );
+
+ $this->assertTrue(
+ $this->tagMapper->haveTag(
+ [1, 2],
+ 'testtype',
+ $this->tag2->getId(),
+ false
+ ),
+ 'at least object 1 has the tag tag2'
+ );
+
+ $this->assertFalse(
+ $this->tagMapper->haveTag(
+ [2],
+ 'testtype',
+ $this->tag2->getId(),
+ false
+ ),
+ 'object 2 does not have tag2'
+ );
+
+ $this->assertFalse(
+ $this->tagMapper->haveTag(
+ [3],
+ 'testtype',
+ $this->tag2->getId(),
+ false
+ ),
+ 'object 3 does not have tag1 due to different type'
+ );
+ }
+
+ /**
+ * @expectedException \OCP\SystemTag\TagNotFoundException
+ */
+ public function testHaveTagNonExisting() {
+ $this->tagMapper->haveTag(
+ [1],
+ 'testtype',
+ 100
+ );
+ }
+}