aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private/SystemTag
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private/SystemTag')
-rw-r--r--lib/private/SystemTag/ManagerFactory.php12
-rw-r--r--lib/private/SystemTag/SystemTagManager.php56
-rw-r--r--lib/private/SystemTag/SystemTagObjectMapper.php33
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]
+ ));
+ }
}
/**