From f38fb20e467ab1a86319681f8b23454ff1891186 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Tue, 19 Apr 2022 13:05:38 +0200 Subject: [PATCH] Cleanup tags component - Port to LoggerInterface - Use IDBConnection and IQueryBuilder instead of raw SQL and OC_DB - Use IEventListener instead of hooks Todos: - [ ] Finish IQueryBuilder port - [ ] Remove OC_DB as this is the only thing that still use that Signed-off-by: Carl Schwan --- core/Application.php | 18 +- lib/private/TagManager.php | 89 ++++++++-- lib/private/Tags.php | 331 +++++++++++++------------------------ lib/public/ITags.php | 32 ++-- 4 files changed, 222 insertions(+), 248 deletions(-) diff --git a/core/Application.php b/core/Application.php index 34932cab18399..4e56502f1d1c0 100644 --- a/core/Application.php +++ b/core/Application.php @@ -49,7 +49,11 @@ use OC\DB\MissingPrimaryKeyInformation; use OC\DB\SchemaWrapper; use OC\Metadata\FileEventListener; +use OC\TagManager; use OCP\AppFramework\App; +use OCP\AppFramework\Bootstrap\IBootContext; +use OCP\AppFramework\Bootstrap\IBootstrap; +use OCP\AppFramework\Bootstrap\IRegistrationContext; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\Events\Node\NodeDeletedEvent; use OCP\Files\Events\Node\NodeWrittenEvent; @@ -307,14 +311,20 @@ function (GenericEvent $event) use ($container) { $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedFilesCleanupListener::class); $eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedWebAuthnCleanupListener::class); + /** @var IEventDispatcher $eventDispatcher */ + $newEventDispatcher = \OC::$server->get(IEventDispatcher::class); + // Metadata /** @var IConfig $config */ $config = $container->get(IConfig::class); if ($config->getSystemValueBool('enable_file_metadata', true)) { - $eventDispatcher = \OC::$server->get(IEventDispatcher::class); - $eventDispatcher->addServiceListener(NodeDeletedEvent::class, FileEventListener::class); - $eventDispatcher->addServiceListener(NodeRemovedFromCache::class, FileEventListener::class); - $eventDispatcher->addServiceListener(NodeWrittenEvent::class, FileEventListener::class); + $newEventDispatcher->addServiceListener(NodeDeletedEvent::class, FileEventListener::class); + $newEventDispatcher->addServiceListener(NodeRemovedFromCache::class, FileEventListener::class); + $newEventDispatcher->addServiceListener(NodeWrittenEvent::class, FileEventListener::class); } + + // Tags + $newEventDispatcher->addServiceListener(UserDeletedEvent::class, TagManager::class); + } } diff --git a/lib/private/TagManager.php b/lib/private/TagManager.php index 8c9dca98c92eb..3c2bb5bb92ad0 100644 --- a/lib/private/TagManager.php +++ b/lib/private/TagManager.php @@ -28,26 +28,27 @@ use OC\Tagging\TagMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; use OCP\IDBConnection; use OCP\ITagManager; use OCP\ITags; use OCP\IUserSession; +use OCP\User\Events\UserDeletedEvent; +use OCP\Db\Exception as DBException; +use Psr\Log\LoggerInterface; -class TagManager implements ITagManager { +class TagManager implements ITagManager, IEventListener { + private TagMapper $mapper; + private IUserSession $userSession; + private IDBConnection $connection; + private LoggerInterface $logger; - /** @var TagMapper */ - private $mapper; - - /** @var IUserSession */ - private $userSession; - - /** @var IDBConnection */ - private $connection; - - public function __construct(TagMapper $mapper, IUserSession $userSession, IDBConnection $connection) { + public function __construct(TagMapper $mapper, IUserSession $userSession, IDBConnection $connection, LoggerInterface $logger) { $this->mapper = $mapper; $this->userSession = $userSession; $this->connection = $connection; + $this->logger = $logger; } /** @@ -72,7 +73,7 @@ public function load($type, $defaultTags = [], $includeShared = false, $userId = } $userId = $this->userSession->getUser()->getUId(); } - return new Tags($this->mapper, $userId, $type, $defaultTags); + return new Tags($this->mapper, $userId, $type, $this->logger, $this->connection, $defaultTags); } /** @@ -97,4 +98,68 @@ public function getUsersFavoritingObject(string $objectType, int $objectId): arr return $users; } + + public function handle(Event $event): void { + if (!($event instanceof UserDeletedEvent)) { + return; + } + + // Find all objectid/tagId pairs. + $user = $event->getUser(); + $qb = $this->connection->getQueryBuilder(); + $qb->select('id') + ->from('vcategory') + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID()))); + try { + $result = $qb->executeQuery(); + } catch (DBException $e) { + $this->logger->error($e->getMessage(), [ + 'app' => 'core', + 'exception' => $e, + ]); + } + + $tagsIds = array_map(fn(array $row) => (int)$row['id'], $result->fetchAll()); + $result->closeCursor(); + + if (count($tagsIds) === 0) { + return; + } + + // Clean vcategory_to_object table + $qb = $this->connection->getQueryBuilder(); + $qb = $qb->delete('vcategory_to_object') + ->where($qb->expr()->in('categoryid', $qb->createParameter('chunk'))); + + // Clean vcategory + $qb1 = $this->connection->getQueryBuilder(); + $qb1 = $qb1->delete('vcategory') + ->where($qb1->expr()->in('uid', $qb1->createParameter('chunk'))); + + foreach (array_chunk($tagsIds, 1000) as $tagChunk) { + $qb->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY); + $qb1->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY); + try { + $qb->executeStatement(); + $qb1->executeStatement(); + } catch (DBException $e) { + $this->logger->error($e->getMessage(), [ + 'app' => 'core', + 'exception' => $e, + ]); + } + } + + foreach (array_chunk($tagsIds, 1000) as $tagChunk) { + $qb->setParameter('chunk', $tagChunk, IQueryBuilder::PARAM_INT_ARRAY); + try { + $qb->executeStatement(); + } catch (DBException $e) { + $this->logger->error($e->getMessage(), [ + 'app' => 'core', + 'exception' => $e, + ]); + } + } + } } diff --git a/lib/private/Tags.php b/lib/private/Tags.php index c11d83e41cff3..9dc748c6c8005 100644 --- a/lib/private/Tags.php +++ b/lib/private/Tags.php @@ -32,72 +32,52 @@ use OC\Tagging\Tag; use OC\Tagging\TagMapper; +use OCP\DB\Exception; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventListener; +use OCP\IDBConnection; use OCP\ILogger; use OCP\ITags; +use OCP\Share_Backend; +use OCP\User\Events\UserDeletedEvent; +use Psr\Log\LoggerInterface; -class Tags implements ITags { - - /** - * Tags - * - * @var array - */ - private $tags = []; - +class Tags implements ITags, IEventListener { /** * Used for storing objectid/categoryname pairs while rescanning. - * - * @var array - */ - private static $relations = []; - - /** - * Type - * - * @var string - */ - private $type; - - /** - * User - * - * @var string */ - private $user; + private static array $relations = []; + private string $type; + private string $user; + private IDBConnection $db; + private LoggerInterface $logger; + private array $tags = []; /** * Are we including tags for shared items? - * - * @var bool */ - private $includeShared = false; + private bool $includeShared = false; /** * The current user, plus any owners of the items shared with the current * user, if $this->includeShared === true. - * - * @var array */ - private $owners = []; + private array $owners = []; /** - * The Mapper we're using to communicate our Tag objects to the database. - * - * @var TagMapper + * The Mapper we are using to communicate our Tag objects to the database. */ - private $mapper; + private TagMapper $mapper; /** * The sharing backend for objects of $this->type. Required if * $this->includeShared === true to determine ownership of items. - * - * @var \OCP\Share_Backend */ - private $backend; + private ?Share_Backend $backend = null; - public const TAG_TABLE = '*PREFIX*vcategory'; - public const RELATION_TABLE = '*PREFIX*vcategory_to_object'; + public const TAG_TABLE = 'vcategory'; + public const RELATION_TABLE = 'vcategory_to_object'; /** * Constructor. @@ -109,7 +89,7 @@ class Tags implements ITags { * * since 20.0.0 $includeShared isn't used anymore */ - public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) { + public function __construct(TagMapper $mapper, string $user, string $type, LoggerInterface $logger, IDBConnection $connection, array $defaultTags = [],) { $this->mapper = $mapper; $this->user = $user; $this->type = $type; @@ -119,6 +99,8 @@ public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) if (count($defaultTags) > 0 && count($this->tags) === 0) { $this->addMultiple($defaultTags, true); } + $this->db = $connection; + $this->logger = $logger; } /** @@ -126,7 +108,7 @@ public function __construct(TagMapper $mapper, $user, $type, $defaultTags = []) * * @return boolean */ - public function isEmpty() { + public function isEmpty(): bool { return count($this->tags) === 0; } @@ -137,7 +119,7 @@ public function isEmpty() { * @param string $id The ID of the tag that is going to be mapped * @return array|false */ - public function getTag($id) { + public function getTag(string $id) { $key = $this->getTagById($id); if ($key !== false) { return $this->tagMap($this->tags[$key]); @@ -154,9 +136,9 @@ public function getTag($id) { * ['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'], * ] * - * @return array + * @return array */ - public function getTags() { + public function getTags(): array { if (!count($this->tags)) { return []; } @@ -181,7 +163,7 @@ public function getTags() { * @param string $user The user whose tags are to be checked. * @return array An array of Tag objects. */ - public function getTagsForUser($user) { + public function getTagsForUser(string $user): array { return array_filter($this->tags, function ($tag) use ($user) { return $tag->getOwner() === $user; @@ -193,23 +175,26 @@ function ($tag) use ($user) { * Get the list of tags for the given ids. * * @param array $objIds array of object ids - * @return array|boolean of tags id as key to array of tag names + * @return array|false of tags id as key to array of tag names * or false if an error occurred */ public function getTagsForObjects(array $objIds) { $entries = []; try { - $conn = \OC::$server->getDatabaseConnection(); $chunks = array_chunk($objIds, 900, false); + $qb = $this->db->getQueryBuilder(); + $qb->select('category', 'categoryid', 'objid') + ->from(self::RELATION_TABLE, 'r') + ->join('r', self::TAG_TABLE, 't', $qb->expr()->eq('t.categoryid', 'r.id')) + ->where($qb->expr()->eq('r.uid', $qb->createParameter('uid'))) + ->andWhere($qb->expr()->eq('type', $qb->createParameter('type'))) + ->andWhere($qb->expr()->in('objid', $qb->createParameter('chunk'))); foreach ($chunks as $chunk) { - $result = $conn->executeQuery( - 'SELECT `category`, `categoryid`, `objid` ' . - 'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' . - 'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)', - [$this->user, $this->type, $chunk], - [null, null, IQueryBuilder::PARAM_INT_ARRAY] - ); + $qb->setParameter('uid', $this->user, IQueryBuilder::PARAM_STR); + $qb->setParameter('type', $this->type, IQueryBuilder::PARAM_STR); + $qb->setParameter('chunk', $chunk, IQueryBuilder::PARAM_INT_ARRAY); + $result = $qb->executeQuery(); while ($row = $result->fetch()) { $objId = (int)$row['objid']; if (!isset($entries[$objId])) { @@ -219,9 +204,8 @@ public function getTagsForObjects(array $objIds) { } } } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $this->logger->error($e->getMessage(), [ + 'exception' => $e, 'app' => 'core', ]); return false; @@ -236,18 +220,17 @@ public function getTagsForObjects(array $objIds) { * Throws an exception if the tag could not be found. * * @param string $tag Tag id or name. - * @return array|false An array of object ids or false on error. + * @return int[]|false An array of object ids or false on error. * @throws \Exception */ public function getIdsForTag($tag) { - $result = null; $tagId = false; if (is_numeric($tag)) { $tagId = $tag; } elseif (is_string($tag)) { $tag = trim($tag); if ($tag === '') { - \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'Cannot use empty tag names', ['app' => 'core']); return false; } $tagId = $this->getTagId($tag); @@ -261,28 +244,22 @@ public function getIdsForTag($tag) { } $ids = []; - $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE - . '` WHERE `categoryid` = ?'; - try { - $stmt = \OC_DB::prepare($sql); - $result = $stmt->execute([$tagId]); - if ($result === null) { - $stmt->closeCursor(); - \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR); - return false; - } - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $qb = $this->db->getQueryBuilder(); + $qb->select('objid') + ->from(self::RELATION_TABLE) + ->where($qb->expr()->eq('categoryid', $qb->createNamedParameter($tagId, IQueryBuilder::PARAM_STR))); + $result = $qb->executeQuery(); + } catch (Exception $e) { + $this->logger->error($e->getMessage(), [ 'app' => 'core', + 'exception' => $e, ]); return false; } if (!is_null($result)) { - while ($row = $result->fetchRow()) { + while ($row = $result->fetch()) { $ids[] = (int)$row['objid']; } $result->closeCursor(); @@ -308,9 +285,8 @@ public function userHasTag($name, $user) { * Checks whether a tag is saved for or shared with the current user. * * @param string $name The tag name to check for. - * @return bool */ - public function hasTag($name) { + public function hasTag(string $name): bool { return $this->getTagId($name) !== false; } @@ -320,15 +296,16 @@ public function hasTag($name) { * @param string $name A string with a name of the tag * @return false|int the id of the added tag or false on error. */ - public function add($name) { + public function add(string $name) { $name = trim($name); if ($name === '') { - \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'Cannot add an empty tag', ['app' => 'core']); return false; } if ($this->userHasTag($name, $this->user)) { - \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG); + // TODO use unique db properties instead of an additional check + $this->logger->debug(__METHOD__ . 'Tag with name already exists', ['app' => 'core']); return false; } try { @@ -336,14 +313,13 @@ public function add($name) { $tag = $this->mapper->insert($tag); $this->tags[] = $tag; } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $this->logger->error($e->getMessage(), [ + 'exception' => $e, 'app' => 'core', ]); return false; } - \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'Added an tag with ' . $tag->getId(), ['app' => 'core']); return $tag->getId(); } @@ -354,12 +330,12 @@ public function add($name) { * @param string $to The new name of the tag. * @return bool */ - public function rename($from, $to) { + public function rename($from, string $to): bool { $from = trim($from); $to = trim($to); if ($to === '' || $from === '') { - \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'Cannot use an empty tag names', ['app' => 'core']); return false; } @@ -369,13 +345,13 @@ public function rename($from, $to) { $key = $this->getTagByName($from); } if ($key === false) { - \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'Tag ' . $from . 'does not exist', ['app' => 'core']); return false; } $tag = $this->tags[$key]; if ($this->userHasTag($to, $tag->getOwner())) { - \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG); + $this->logger->debug(__METHOD__ . 'A tag named' . $to . 'already exists for user' . $tag->getOwner(), ['app' => 'core']); return false; } @@ -383,9 +359,8 @@ public function rename($from, $to) { $tag->setName($to); $this->tags[$key] = $this->mapper->update($tag); } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $this->logger->error($e->getMessage(), [ + 'exception' => $e, 'app' => 'core', ]); return false; @@ -402,7 +377,7 @@ public function rename($from, $to) { * @param int|null $id int Optional object id to add to this|these tag(s) * @return bool Returns false on error. */ - public function addMultiple($names, $sync = false, $id = null) { + public function addMultiple(array $names, bool $sync = false, ?int $id = null): bool { if (!is_array($names)) { $names = [$names]; } @@ -430,122 +405,50 @@ public function addMultiple($names, $sync = false, $id = null) { /** * Save the list of tags and their object relations */ - protected function save() { - if (is_array($this->tags)) { - foreach ($this->tags as $tag) { - try { - if (!$this->mapper->tagExists($tag)) { - $this->mapper->insert($tag); - } - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); - } - } - - // reload tags to get the proper ids. - $this->tags = $this->mapper->loadTags($this->owners, $this->type); - \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true), - ILogger::DEBUG); - // Loop through temporarily cached objectid/tagname pairs - // and save relations. - $tags = $this->tags; - // For some reason this is needed or array_search(i) will return 0..? - ksort($tags); - $dbConnection = \OC::$server->getDatabaseConnection(); - foreach (self::$relations as $relation) { - $tagId = $this->getTagId($relation['tag']); - \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG); - if ($tagId) { - try { - $dbConnection->insertIfNotExist(self::RELATION_TABLE, - [ - 'objid' => $relation['objid'], - 'categoryid' => $tagId, - 'type' => $this->type, - ]); - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); - } - } - } - self::$relations = []; // reset - } else { - \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! ' - . print_r($this->tags, true), ILogger::ERROR); - } - } - - /** - * Delete tags and tag/object relations for a user. - * - * For hooking up on post_deleteUser - * - * @param array $arguments - */ - public static function post_deleteUser($arguments) { - // Find all objectid/tagId pairs. - $result = null; - try { - $stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` ' - . 'WHERE `uid` = ?'); - $result = $stmt->execute([$arguments['uid']]); - if ($result === null) { - \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR); - } - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); - } - - if (!is_null($result)) { + protected function save(): void { + foreach ($this->tags as $tag) { try { - $stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` ' - . 'WHERE `categoryid` = ?'); - while ($row = $result->fetchRow()) { - try { - $stmt->execute([$row['id']]); - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); - } + if (!$this->mapper->tagExists($tag)) { + $this->mapper->insert($tag); } - $result->closeCursor(); } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $this->logger->error($e->getMessage(), [ + 'exception' => $e, 'app' => 'core', ]); } } - try { - $stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` ' - . 'WHERE `uid` = ?'); - $result = $stmt->execute([$arguments['uid']]); - if ($result === null) { - \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR); + + // reload tags to get the proper ids. + $this->tags = $this->mapper->loadTags($this->owners, $this->type); + $this->logger->debug(__METHOD__ . 'tags' . print_r($this->tags, true), ['app' => 'core']); + // Loop through temporarily cached objectid/tagname pairs + // and save relations. + $tags = $this->tags; + // For some reason this is needed or array_search(i) will return 0..? + ksort($tags); + foreach (self::$relations as $relation) { + $tagId = $this->getTagId($relation['tag']); + $this->logger->debug(__METHOD__ . 'catid ' . $relation['tag'] . ' ' . $tagId, ['app' => 'core']); + if ($tagId) { + $qb = $this->db->getQueryBuilder(); + $qb->insert(self::RELATION_TABLE) + ->values([ + 'objid' => $qb->createNamedParameter($relation['objid'], IQueryBuilder::PARAM_INT), + 'categoryid' => $qb->createNamedParameter($tagId, IQueryBuilder::PARAM_INT), + 'type' => $qb->createNamedParameter($this->type), + ]); + try { + $qb->executeStatement(); + } catch (Exception $e) { + $this->logger->error($e->getMessage(), [ + 'exception' => $e, + 'app' => 'core', + ]); + } } - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, - 'app' => 'core', - ]); } + self::$relations = []; // reset } /** @@ -554,28 +457,21 @@ public static function post_deleteUser($arguments) { * @param array $ids The ids of the objects * @return boolean Returns false on error. */ - public function purgeObjects(array $ids) { + public function purgeObjects(array $ids): bool { if (count($ids) === 0) { // job done ;) return true; } $updates = $ids; + $qb = $this->db->getQueryBuilder(); + $qb->delete(self::RELATION_TABLE) + ->where($qb->expr()->in('objid', $qb->createNamedParameter($ids))); try { - $query = 'DELETE FROM `' . self::RELATION_TABLE . '` '; - $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids) - 1) . '?) '; - $query .= 'AND `type`= ?'; - $updates[] = $this->type; - $stmt = \OC_DB::prepare($query); - $result = $stmt->execute($updates); - if ($result === null) { - \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR); - return false; - } - } catch (\Exception $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => __METHOD__, - 'level' => ILogger::ERROR, + $qb->executeStatement(); + } catch (Exception $e) { + $this->logger->error($e->getMessage(), [ 'app' => 'core', + 'exception' => $e, ]); return false; } @@ -822,4 +718,7 @@ private function tagMap(Tag $tag) { 'type' => $tag->getType() ]; } + + public function handle(Event $event): void { + } } diff --git a/lib/public/ITags.php b/lib/public/ITags.php index 03bcb845f4e5f..ac76e9dd79809 100644 --- a/lib/public/ITags.php +++ b/lib/public/ITags.php @@ -32,9 +32,6 @@ use OC\Tags; -// FIXME: Where should I put this? Or should it be implemented as a Listener? -\OC_Hook::connect('OC_User', 'post_deleteUser', Tags::class, 'post_deleteUser'); - /** * Class for easily tagging objects by their id * @@ -55,11 +52,9 @@ interface ITags { /** * Check if any tags are saved for this type and user. - * - * @return boolean * @since 6.0.0 */ - public function isEmpty(); + public function isEmpty(): bool; /** * Returns an array mapping a given tag's properties to its values: @@ -69,34 +64,40 @@ public function isEmpty(); * @return array|false * @since 8.0.0 */ - public function getTag($id); + public function getTag(string $id); /** * Get the tags for a specific user. * * This returns an array with id/name maps: + * + * ```php * [ * ['id' => 0, 'name' = 'First tag'], * ['id' => 1, 'name' = 'Second tag'], * ] + * ``` * - * @return array + * @return array * @since 6.0.0 */ - public function getTags(); + public function getTags(): array; /** * Get a list of tags for the given item ids. * * This returns an array with object id / tag names: + * + * ```php * [ * 1 => array('First tag', 'Second tag'), * 2 => array('Second tag'), * 3 => array('Second tag', 'Third tag'), * ] + * ``` * * @param array $objIds item ids - * @return array|boolean with object id as key and an array + * @return array|false with object id as key and an array * of tag names as value or false if an error occurred * @since 8.0.0 */ @@ -117,10 +118,9 @@ public function getIdsForTag($tag); * Checks whether a tag is already saved. * * @param string $name The name to check for. - * @return bool * @since 6.0.0 */ - public function hasTag($name); + public function hasTag(string $name): bool; /** * Checks whether a tag is saved for the given user, @@ -131,7 +131,7 @@ public function hasTag($name); * @return bool * @since 8.0.0 */ - public function userHasTag($name, $user); + public function userHasTag(string $name, string $user): bool; /** * Add a new tag. @@ -140,7 +140,7 @@ public function userHasTag($name, $user); * @return int|false the id of the added tag or false if it already exists. * @since 6.0.0 */ - public function add($name); + public function add(string $name); /** * Rename tag. @@ -150,7 +150,7 @@ public function add($name); * @return bool * @since 6.0.0 */ - public function rename($from, $to); + public function rename($from, string $to): bool; /** * Add a list of new tags. @@ -162,7 +162,7 @@ public function rename($from, $to); * @return bool Returns false on error. * @since 6.0.0 */ - public function addMultiple($names, $sync = false, $id = null); + public function addMultiple(array $names, bool $sync = false, ?int $id = null): bool; /** * Delete tag/object relations from the db