diff options
Diffstat (limited to 'lib/private/SystemTag')
-rw-r--r-- | lib/private/SystemTag/ManagerFactory.php | 12 | ||||
-rw-r--r-- | lib/private/SystemTag/SystemTagManager.php | 56 | ||||
-rw-r--r-- | lib/private/SystemTag/SystemTagObjectMapper.php | 33 |
3 files changed, 96 insertions, 5 deletions
diff --git a/lib/private/SystemTag/ManagerFactory.php b/lib/private/SystemTag/ManagerFactory.php index 0fd2ca5fd36..4d3a1396529 100644 --- a/lib/private/SystemTag/ManagerFactory.php +++ b/lib/private/SystemTag/ManagerFactory.php @@ -9,7 +9,11 @@ declare(strict_types=1); namespace OC\SystemTag; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\IDBConnection; +use OCP\IGroupManager; use OCP\IServerContainer; +use OCP\IUserSession; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ISystemTagManagerFactory; use OCP\SystemTag\ISystemTagObjectMapper; @@ -36,9 +40,11 @@ class ManagerFactory implements ISystemTagManagerFactory { */ public function getManager(): ISystemTagManager { return new SystemTagManager( - $this->serverContainer->getDatabaseConnection(), - $this->serverContainer->getGroupManager(), + $this->serverContainer->get(IDBConnection::class), + $this->serverContainer->get(IGroupManager::class), $this->serverContainer->get(IEventDispatcher::class), + $this->serverContainer->get(IUserSession::class), + $this->serverContainer->get(IAppConfig::class), ); } @@ -50,7 +56,7 @@ class ManagerFactory implements ISystemTagManagerFactory { */ public function getObjectMapper(): ISystemTagObjectMapper { return new SystemTagObjectMapper( - $this->serverContainer->getDatabaseConnection(), + $this->serverContainer->get(IDBConnection::class), $this->getManager(), $this->serverContainer->get(IEventDispatcher::class), ); diff --git a/lib/private/SystemTag/SystemTagManager.php b/lib/private/SystemTag/SystemTagManager.php index 4f05d40c34c..4b421fa033a 100644 --- a/lib/private/SystemTag/SystemTagManager.php +++ b/lib/private/SystemTag/SystemTagManager.php @@ -11,14 +11,18 @@ namespace OC\SystemTag; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IUser; +use OCP\IUserSession; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ManagerEvent; use OCP\SystemTag\TagAlreadyExistsException; +use OCP\SystemTag\TagCreationForbiddenException; use OCP\SystemTag\TagNotFoundException; +use OCP\SystemTag\TagUpdateForbiddenException; /** * Manager class for system tags @@ -36,6 +40,8 @@ class SystemTagManager implements ISystemTagManager { protected IDBConnection $connection, protected IGroupManager $groupManager, protected IEventDispatcher $dispatcher, + private IUserSession $userSession, + private IAppConfig $appConfig, ) { $query = $this->connection->getQueryBuilder(); $this->selectTagQuery = $query->select('*') @@ -102,7 +108,7 @@ class SystemTagManager implements ISystemTagManager { if (!empty($nameSearchPattern)) { $query->andWhere( - $query->expr()->like( + $query->expr()->iLike( 'name', $query->createNamedParameter('%' . $this->connection->escapeLikeParameter($nameSearchPattern) . '%') ) @@ -114,7 +120,7 @@ class SystemTagManager implements ISystemTagManager { ->addOrderBy('visibility', 'ASC') ->addOrderBy('editable', 'ASC'); - $result = $query->execute(); + $result = $query->executeQuery(); while ($row = $result->fetch()) { $tags[$row['id']] = $this->createSystemTagFromRow($row); } @@ -145,6 +151,19 @@ class SystemTagManager implements ISystemTagManager { } public function createTag(string $tagName, bool $userVisible, bool $userAssignable): ISystemTag { + $user = $this->userSession->getUser(); + if (!$this->canUserCreateTag($user)) { + throw new TagCreationForbiddenException(); + } + + // Check if tag already exists (case-insensitive) + $existingTags = $this->getAllTags(null, $tagName); + foreach ($existingTags as $existingTag) { + if (mb_strtolower($existingTag->getName()) === mb_strtolower($tagName)) { + throw new TagAlreadyExistsException('Tag ' . $tagName . ' already exists'); + } + } + // Length of name column is 64 $truncatedTagName = substr($tagName, 0, 64); $query = $this->connection->getQueryBuilder(); @@ -197,8 +216,14 @@ class SystemTagManager implements ISystemTagManager { ); } + $user = $this->userSession->getUser(); + if (!$this->canUserUpdateTag($user)) { + throw new TagUpdateForbiddenException(); + } + $beforeUpdate = array_shift($tags); // Length of name column is 64 + $newName = trim($newName); $truncatedNewName = substr($newName, 0, 64); $afterUpdate = new SystemTag( $tagId, @@ -209,6 +234,15 @@ class SystemTagManager implements ISystemTagManager { $color ); + // Check if tag already exists (case-insensitive) + $existingTags = $this->getAllTags(null, $truncatedNewName); + foreach ($existingTags as $existingTag) { + if (mb_strtolower($existingTag->getName()) === mb_strtolower($truncatedNewName) + && $existingTag->getId() !== $tagId) { + throw new TagAlreadyExistsException('Tag ' . $truncatedNewName . ' already exists'); + } + } + $query = $this->connection->getQueryBuilder(); $query->update(self::TAG_TABLE) ->set('name', $query->createParameter('name')) @@ -319,6 +353,24 @@ class SystemTagManager implements ISystemTagManager { return false; } + public function canUserCreateTag(?IUser $user): bool { + if ($user === null) { + // If no user given, allows only calls from CLI + return \OC::$CLI; + } + + if ($this->appConfig->getValueBool('systemtags', 'restrict_creation_to_admin', false) === false) { + return true; + } + + return $this->groupManager->isAdmin($user->getUID()); + } + + public function canUserUpdateTag(?IUser $user): bool { + // We currently have no different permissions for updating tags than for creating them + return $this->canUserCreateTag($user); + } + public function canUserSeeTag(ISystemTag $tag, ?IUser $user): bool { // If no user, then we only show public tags if (!$user && $tag->getAccessLevel() === ISystemTag::ACCESS_LEVEL_PUBLIC) { diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php index 09a7ce1a6ed..1fa5975dafb 100644 --- a/lib/private/SystemTag/SystemTagObjectMapper.php +++ b/lib/private/SystemTag/SystemTagObjectMapper.php @@ -284,6 +284,10 @@ 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) @@ -292,6 +296,15 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { ->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; } @@ -312,6 +325,26 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { $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] + )); + } } /** |