diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 2e878decb3f96..34adfd28e4f9c 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -361,6 +361,7 @@ 'OCA\\DAV\\SystemTag\\SystemTagList' => $baseDir . '/../lib/SystemTag/SystemTagList.php', 'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => $baseDir . '/../lib/SystemTag/SystemTagMappingNode.php', 'OCA\\DAV\\SystemTag\\SystemTagNode' => $baseDir . '/../lib/SystemTag/SystemTagNode.php', + 'OCA\\DAV\\SystemTag\\SystemTagObjectType' => $baseDir . '/../lib/SystemTag/SystemTagObjectType.php', 'OCA\\DAV\\SystemTag\\SystemTagPlugin' => $baseDir . '/../lib/SystemTag/SystemTagPlugin.php', 'OCA\\DAV\\SystemTag\\SystemTagsByIdCollection' => $baseDir . '/../lib/SystemTag/SystemTagsByIdCollection.php', 'OCA\\DAV\\SystemTag\\SystemTagsInUseCollection' => $baseDir . '/../lib/SystemTag/SystemTagsInUseCollection.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 5f43453cda6a7..883b1fc710c4d 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -376,6 +376,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\SystemTag\\SystemTagList' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagList.php', 'OCA\\DAV\\SystemTag\\SystemTagMappingNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagMappingNode.php', 'OCA\\DAV\\SystemTag\\SystemTagNode' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagNode.php', + 'OCA\\DAV\\SystemTag\\SystemTagObjectType' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagObjectType.php', 'OCA\\DAV\\SystemTag\\SystemTagPlugin' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagPlugin.php', 'OCA\\DAV\\SystemTag\\SystemTagsByIdCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsByIdCollection.php', 'OCA\\DAV\\SystemTag\\SystemTagsInUseCollection' => __DIR__ . '/..' . '/../lib/SystemTag/SystemTagsInUseCollection.php', diff --git a/apps/dav/lib/RootCollection.php b/apps/dav/lib/RootCollection.php index 6ede8cb683c17..751ab17bb7aa6 100644 --- a/apps/dav/lib/RootCollection.php +++ b/apps/dav/lib/RootCollection.php @@ -105,11 +105,7 @@ public function __construct() { $publicCalendarRoot = new PublicCalendarRoot($caldavBackend, $l10n, $config, $logger); - $systemTagCollection = new SystemTagsByIdCollection( - \OC::$server->getSystemTagManager(), - \OC::$server->getUserSession(), - $groupManager - ); + $systemTagCollection = Server::get(SystemTagsByIdCollection::class); $systemTagRelationsCollection = new SystemTagsRelationsCollection( \OC::$server->getSystemTagManager(), \OC::$server->getSystemTagObjectMapper(), diff --git a/apps/dav/lib/SystemTag/SystemTagNode.php b/apps/dav/lib/SystemTag/SystemTagNode.php index 06eead814b27b..ce84e66813bfd 100644 --- a/apps/dav/lib/SystemTag/SystemTagNode.php +++ b/apps/dav/lib/SystemTag/SystemTagNode.php @@ -10,8 +10,8 @@ use OCP\IUser; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\TagAlreadyExistsException; - use OCP\SystemTag\TagNotFoundException; use Sabre\DAV\Exception\Conflict; use Sabre\DAV\Exception\Forbidden; @@ -21,31 +21,7 @@ /** * DAV node representing a system tag, with the name being the tag id. */ -class SystemTagNode implements \Sabre\DAV\INode { - - /** - * @var ISystemTag - */ - protected $tag; - - /** - * @var ISystemTagManager - */ - protected $tagManager; - - /** - * User - * - * @var IUser - */ - protected $user; - - /** - * Whether to allow permissions for admins - * - * @var bool - */ - protected $isAdmin; +class SystemTagNode implements \Sabre\DAV\ICollection { protected int $numberOfFiles = -1; protected int $referenceFileId = -1; @@ -58,11 +34,13 @@ class SystemTagNode implements \Sabre\DAV\INode { * @param bool $isAdmin whether to allow operations for admins * @param ISystemTagManager $tagManager tag manager */ - public function __construct(ISystemTag $tag, IUser $user, $isAdmin, ISystemTagManager $tagManager) { - $this->tag = $tag; - $this->user = $user; - $this->isAdmin = $isAdmin; - $this->tagManager = $tagManager; + public function __construct( + protected ISystemTag $tag, + protected IUser $user, + protected bool $isAdmin, + protected ISystemTagManager $tagManager, + protected ISystemTagObjectMapper $tagMapper, + ) { } /** @@ -181,4 +159,26 @@ public function getReferenceFileId(): int { public function setReferenceFileId(int $referenceFileId): void { $this->referenceFileId = $referenceFileId; } + + public function createFile($name, $data = null) { + throw new MethodNotAllowed(); + } + + public function createDirectory($name) { + throw new MethodNotAllowed(); + } + + public function getChild($name) { + return new SystemTagObjectType($this->tag, $name, $this->tagManager, $this->tagMapper); + } + + public function childExists($name) { + $objectTypes = $this->tagMapper->getAvailableObjectTypes(); + return in_array($name, $objectTypes); + } + + public function getChildren() { + // We currently don't have a method to list allowed tag mappings types + return [new SystemTagObjectType($this->tag, 'files', $this->tagManager, $this->tagMapper)]; + } } diff --git a/apps/dav/lib/SystemTag/SystemTagObjectType.php b/apps/dav/lib/SystemTag/SystemTagObjectType.php new file mode 100644 index 0000000000000..53686a3e76426 --- /dev/null +++ b/apps/dav/lib/SystemTag/SystemTagObjectType.php @@ -0,0 +1,58 @@ +objectsIds)) { + $this->objectsIds = $this->tagMapper->getObjectIdsForTags($this->tag->getId(), $this->type); + } + + return $this->objectsIds; + } + + public function delete() { + throw new MethodNotAllowed(); + } + + public function getName() { + return $this->type; + } + + public function setName($name) { + throw new MethodNotAllowed(); + } + + public function getLastModified() { + return null; + } +} diff --git a/apps/dav/lib/SystemTag/SystemTagPlugin.php b/apps/dav/lib/SystemTag/SystemTagPlugin.php index 6098a41ab3b0d..f575989d893c1 100644 --- a/apps/dav/lib/SystemTag/SystemTagPlugin.php +++ b/apps/dav/lib/SystemTag/SystemTagPlugin.php @@ -37,6 +37,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin { // namespace public const NS_OWNCLOUD = 'http://owncloud.org/ns'; + public const NS_NEXTCLOUD = 'http://nextcloud.org/ns'; public const ID_PROPERTYNAME = '{http://owncloud.org/ns}id'; public const DISPLAYNAME_PROPERTYNAME = '{http://owncloud.org/ns}display-name'; public const USERVISIBLE_PROPERTYNAME = '{http://owncloud.org/ns}user-visible'; @@ -45,7 +46,8 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin { public const CANASSIGN_PROPERTYNAME = '{http://owncloud.org/ns}can-assign'; public const SYSTEM_TAGS_PROPERTYNAME = '{http://nextcloud.org/ns}system-tags'; public const NUM_FILES_PROPERTYNAME = '{http://nextcloud.org/ns}files-assigned'; - public const FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid'; + public const REFERENCE_FILEID_PROPERTYNAME = '{http://nextcloud.org/ns}reference-fileid'; + public const OBJECTID_PROPERTYNAME = '{http://nextcloud.org/ns}object-id'; /** * @var \Sabre\DAV\Server $server @@ -223,7 +225,7 @@ public function handleGetProperties( return; } - if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode)) { + if (!($node instanceof SystemTagNode) && !($node instanceof SystemTagMappingNode) && !($node instanceof SystemTagObjectType)) { return; } @@ -272,10 +274,16 @@ public function handleGetProperties( return $node->getNumberOfFiles(); }); - $propFind->handle(self::FILEID_PROPERTYNAME, function () use ($node): int { + $propFind->handle(self::REFERENCE_FILEID_PROPERTYNAME, function () use ($node): int { return $node->getReferenceFileId(); }); } + + if ($node instanceof SystemTagObjectType) { + $propFind->handle(self::OBJECTID_PROPERTYNAME, function () use ($node) { + return $node->getObjectsIds(); + }); + } } private function propfindForFile(PropFind $propFind, Node $node): void { @@ -372,7 +380,7 @@ public function handleUpdateProperties($path, PropPatch $propPatch) { self::USERASSIGNABLE_PROPERTYNAME, self::GROUPS_PROPERTYNAME, self::NUM_FILES_PROPERTYNAME, - self::FILEID_PROPERTYNAME, + self::REFERENCE_FILEID_PROPERTYNAME, ], function ($props) use ($node) { $tag = $node->getSystemTag(); $name = $tag->getName(); @@ -409,7 +417,7 @@ public function handleUpdateProperties($path, PropPatch $propPatch) { $this->tagManager->setTagGroups($tag, $groupIds); } - if (isset($props[self::NUM_FILES_PROPERTYNAME]) || isset($props[self::FILEID_PROPERTYNAME])) { + if (isset($props[self::NUM_FILES_PROPERTYNAME]) || isset($props[self::REFERENCE_FILEID_PROPERTYNAME])) { // read-only properties throw new Forbidden(); } diff --git a/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php b/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php index 13e79c99b65a8..b854db7b94db5 100644 --- a/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php +++ b/apps/dav/lib/SystemTag/SystemTagsByIdCollection.php @@ -11,6 +11,7 @@ use OCP\IUserSession; use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\TagNotFoundException; use Sabre\DAV\Exception\BadRequest; use Sabre\DAV\Exception\Forbidden; @@ -19,21 +20,6 @@ class SystemTagsByIdCollection implements ICollection { - /** - * @var ISystemTagManager - */ - private $tagManager; - - /** - * @var IGroupManager - */ - private $groupManager; - - /** - * @var IUserSession - */ - private $userSession; - /** * SystemTagsByIdCollection constructor. * @@ -42,13 +28,11 @@ class SystemTagsByIdCollection implements ICollection { * @param IGroupManager $groupManager */ public function __construct( - ISystemTagManager $tagManager, - IUserSession $userSession, - IGroupManager $groupManager, + private ISystemTagManager $tagManager, + private IUserSession $userSession, + private IGroupManager $groupManager, + protected ISystemTagObjectMapper $tagMapper, ) { - $this->tagManager = $tagManager; - $this->userSession = $userSession; - $this->groupManager = $groupManager; } /** @@ -180,6 +164,6 @@ public function getLastModified() { * @return SystemTagNode */ private function makeNode(ISystemTag $tag) { - return new SystemTagNode($tag, $this->userSession->getUser(), $this->isAdmin(), $this->tagManager); + return new SystemTagNode($tag, $this->userSession->getUser(), $this->isAdmin(), $this->tagManager, $this->tagMapper); } } diff --git a/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php b/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php index 0431acc553af8..81abc6c156969 100644 --- a/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php +++ b/apps/dav/lib/SystemTag/SystemTagsInUseCollection.php @@ -16,24 +16,21 @@ use OCP\Files\NotPermittedException; use OCP\IUserSession; use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; use Sabre\DAV\Exception\Forbidden; use Sabre\DAV\Exception\NotFound; use Sabre\DAV\SimpleCollection; class SystemTagsInUseCollection extends SimpleCollection { - protected IUserSession $userSession; - protected IRootFolder $rootFolder; - protected string $mediaType; - protected ISystemTagManager $systemTagManager; - protected SystemTagsInFilesDetector $systemTagsInFilesDetector; /** @noinspection PhpMissingParentConstructorInspection */ public function __construct( - IUserSession $userSession, - IRootFolder $rootFolder, - ISystemTagManager $systemTagManager, - SystemTagsInFilesDetector $systemTagsInFilesDetector, - string $mediaType = '', + protected IUserSession $userSession, + protected IRootFolder $rootFolder, + protected ISystemTagManager $systemTagManager, + protected ISystemTagObjectMapper $tagMapper, + protected SystemTagsInFilesDetector $systemTagsInFilesDetector, + protected string $mediaType = '', ) { $this->userSession = $userSession; $this->rootFolder = $rootFolder; @@ -54,7 +51,7 @@ public function getChild($name): self { if ($this->mediaType !== '') { throw new NotFound('Invalid media type'); } - return new self($this->userSession, $this->rootFolder, $this->systemTagManager, $this->systemTagsInFilesDetector, $name); + return new self($this->userSession, $this->rootFolder, $this->systemTagManager, $this->tagMapper, $this->systemTagsInFilesDetector, $name); } /** @@ -81,7 +78,7 @@ public function getChildren(): array { foreach ($result as $tagData) { $tag = new SystemTag((string)$tagData['id'], $tagData['name'], (bool)$tagData['visibility'], (bool)$tagData['editable']); // read only, so we can submit the isAdmin parameter as false generally - $node = new SystemTagNode($tag, $user, false, $this->systemTagManager); + $node = new SystemTagNode($tag, $user, false, $this->systemTagManager, $this->tagMapper); $node->setNumberOfFiles((int)$tagData['number_files']); $node->setReferenceFileId((int)$tagData['ref_file_id']); $children[] = $node; diff --git a/apps/dav/tests/unit/SystemTag/SystemTagsByIdCollectionTest.php b/apps/dav/tests/unit/SystemTag/SystemTagsByIdCollectionTest.php index 2ffbc1cf01fda..e30d8f73b6a46 100644 --- a/apps/dav/tests/unit/SystemTag/SystemTagsByIdCollectionTest.php +++ b/apps/dav/tests/unit/SystemTag/SystemTagsByIdCollectionTest.php @@ -13,7 +13,9 @@ use OCP\IUser; use OCP\IUserSession; use OCP\SystemTag\ISystemTagManager; +use OCP\SystemTag\ISystemTagObjectMapper; use OCP\SystemTag\TagNotFoundException; +use PHPUnit\Framework\MockObject\MockObject; class SystemTagsByIdCollectionTest extends \Test\TestCase { @@ -40,21 +42,31 @@ public function getNode($isAdmin = true) { $this->user->expects($this->any()) ->method('getUID') ->willReturn('testuser'); + + /** @var IUserSession|MockObject */ $userSession = $this->getMockBuilder(IUserSession::class) ->getMock(); $userSession->expects($this->any()) ->method('getUser') ->willReturn($this->user); + + /** @var IGroupManager|MockObject */ $groupManager = $this->getMockBuilder(IGroupManager::class) ->getMock(); $groupManager->expects($this->any()) ->method('isAdmin') ->with('testuser') ->willReturn($isAdmin); + + /** @var ISystemTagObjectMapper|MockObject */ + $tagMapper = $this->getMockBuilder(ISystemTagObjectMapper::class) + ->getMock(); + return new SystemTagsByIdCollection( $this->tagManager, $userSession, - $groupManager + $groupManager, + $tagMapper, ); } diff --git a/apps/systemtags/src/components/SystemTagPicker.vue b/apps/systemtags/src/components/SystemTagPicker.vue new file mode 100644 index 0000000000000..28fff28be7862 --- /dev/null +++ b/apps/systemtags/src/components/SystemTagPicker.vue @@ -0,0 +1,294 @@ + + + + + + + diff --git a/apps/systemtags/src/files_actions/bulkSystemTagsAction.ts b/apps/systemtags/src/files_actions/bulkSystemTagsAction.ts new file mode 100644 index 0000000000000..a28706bae8f74 --- /dev/null +++ b/apps/systemtags/src/files_actions/bulkSystemTagsAction.ts @@ -0,0 +1,45 @@ +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { type Node } from '@nextcloud/files' + +import { defineAsyncComponent } from 'vue' +import { FileAction } from '@nextcloud/files' +import { t } from '@nextcloud/l10n' +import TagMultipleSvg from '@mdi/svg/svg/tag-multiple.svg?raw' + +import { spawnDialog } from '@nextcloud/dialogs' +import { fetchTags } from '../services/api' + +export const action = new FileAction({ + id: 'systemtags:bulk', + displayName: () => t('systemtags', 'Manage tags'), + iconSvgInline: () => TagMultipleSvg, + + enabled(nodes) { + // Only for multiple nodes + if (nodes.length <= 1) { + return false + } + + return true + }, + + async exec() { + return null + }, + + async execBatch(nodes: Node[]) { + const tags = await fetchTags() + const response = await new Promise((resolve) => { + spawnDialog(defineAsyncComponent(() => import('../components/SystemTagPicker.vue')), { + nodes, + tags, + }, (status) => { + resolve(status as null|boolean) + }) + }) + return Array(nodes.length).fill(response) + }, +}) diff --git a/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts b/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts index 46b483129be94..2a7ee3e7eb2d1 100644 --- a/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts +++ b/apps/systemtags/src/files_actions/inlineSystemTagsAction.ts @@ -4,19 +4,10 @@ */ import type { Node } from '@nextcloud/files' import { FileAction } from '@nextcloud/files' -import { translate as t } from '@nextcloud/l10n' +import { t } from '@nextcloud/l10n' import '../css/fileEntryInlineSystemTags.scss' - -const getNodeSystemTags = function(node: Node): string[] { - const tags = node.attributes?.['system-tags']?.['system-tag'] as string|string[]|undefined - - if (tags === undefined) { - return [] - } - - return [tags].flat() -} +import { getNodeSystemTags } from '../utils' const renderTag = function(tag: string, isMore = false): HTMLElement { const tagElement = document.createElement('li') diff --git a/apps/systemtags/src/files_actions/openInFilesAction.ts b/apps/systemtags/src/files_actions/openInFilesAction.ts index 695166bbcbd79..806413de91795 100644 --- a/apps/systemtags/src/files_actions/openInFilesAction.ts +++ b/apps/systemtags/src/files_actions/openInFilesAction.ts @@ -2,8 +2,12 @@ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { translate as t } from '@nextcloud/l10n' -import { type Node, FileType, FileAction, DefaultType } from '@nextcloud/files' +import { type Node } from '@nextcloud/files' + +import { FileType, FileAction, DefaultType } from '@nextcloud/files' +import { t } from '@nextcloud/l10n' + +import { systemTagsViewId } from '../files_views/systemtagsView' export const action = new FileAction({ id: 'systemtags:open-in-files', @@ -12,7 +16,7 @@ export const action = new FileAction({ enabled(nodes, view) { // Only for the system tags view - if (view.id !== 'tags') { + if (view.id !== systemTagsViewId) { return false } // Only for single nodes diff --git a/apps/systemtags/src/files_views/systemtagsView.ts b/apps/systemtags/src/files_views/systemtagsView.ts index 9012e5e8c6b95..46e5af6c3c169 100644 --- a/apps/systemtags/src/files_views/systemtagsView.ts +++ b/apps/systemtags/src/files_views/systemtagsView.ts @@ -9,13 +9,15 @@ import { getContents } from '../services/systemtags.js' import svgTagMultiple from '@mdi/svg/svg/tag-multiple.svg?raw' +export const systemTagsViewId = 'tags' + /** * Register the system tags files view */ export function registerSystemTagsView() { const Navigation = getNavigation() Navigation.register(new View({ - id: 'tags', + id: systemTagsViewId, name: t('systemtags', 'Tags'), caption: t('systemtags', 'List of tags and their associated files and folders.'), diff --git a/apps/systemtags/src/init.ts b/apps/systemtags/src/init.ts index d0b0c4dd5da2c..54ba0d604ee82 100644 --- a/apps/systemtags/src/init.ts +++ b/apps/systemtags/src/init.ts @@ -3,11 +3,13 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ import { registerDavProperty, registerFileAction } from '@nextcloud/files' -import { action as inlineSystemTagsAction } from './files_actions/inlineSystemTagsAction.js' -import { action as openInFilesAction } from './files_actions/openInFilesAction.js' -import { registerSystemTagsView } from './files_views/systemtagsView.js' +import { action as bulkSystemTagsAction } from './files_actions/bulkSystemTagsAction' +import { action as inlineSystemTagsAction } from './files_actions/inlineSystemTagsAction' +import { action as openInFilesAction } from './files_actions/openInFilesAction' +import { registerSystemTagsView } from './files_views/systemtagsView' registerDavProperty('nc:system-tags') +registerFileAction(bulkSystemTagsAction) registerFileAction(inlineSystemTagsAction) registerFileAction(openInFilesAction) diff --git a/apps/systemtags/src/services/logger.ts b/apps/systemtags/src/services/logger.ts new file mode 100644 index 0000000000000..8cce9f5f92e8f --- /dev/null +++ b/apps/systemtags/src/services/logger.ts @@ -0,0 +1,10 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { getLoggerBuilder } from '@nextcloud/logger' + +export default getLoggerBuilder() + .setApp('systemtags') + .detectUser() + .build() diff --git a/apps/systemtags/src/utils.ts b/apps/systemtags/src/utils.ts index c7e0dcbaa5b06..456dc52ca8423 100644 --- a/apps/systemtags/src/utils.ts +++ b/apps/systemtags/src/utils.ts @@ -8,6 +8,7 @@ import camelCase from 'camelcase' import type { DAVResultResponseProps } from 'webdav' import type { BaseTag, ServerTag, Tag, TagWithId } from './types.js' +import type { Node } from '@nextcloud/files' export const defaultBaseTag: BaseTag = { userVisible: true, @@ -55,3 +56,13 @@ export const formatTag = (initialTag: Tag | ServerTag): ServerTag => { return tag as unknown as ServerTag } + +export const getNodeSystemTags = function(node: Node): string[] { + const tags = node.attributes?.['system-tags']?.['system-tag'] as string|string[]|undefined + + if (tags === undefined) { + return [] + } + + return [tags].flat() +} diff --git a/core/Migrations/Version31000Date20241018063111.php b/core/Migrations/Version31000Date20241018063111.php new file mode 100644 index 0000000000000..eda5c5c40f9de --- /dev/null +++ b/core/Migrations/Version31000Date20241018063111.php @@ -0,0 +1,42 @@ +hasTable('systemtag_object_mapping')) { + $table = $schema->getTable('systemtag_object_mapping'); + + if (!$table->hasIndex('systag_objecttype')) { + $table->addIndex(['objecttype'], 'systag_objecttype'); + } + } + + return $schema; + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 1219c0f5aa44c..ef9af33414f13 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1384,6 +1384,7 @@ 'OC\\Core\\Migrations\\Version30000Date20240814180800' => $baseDir . '/core/Migrations/Version30000Date20240814180800.php', 'OC\\Core\\Migrations\\Version30000Date20240815080800' => $baseDir . '/core/Migrations/Version30000Date20240815080800.php', 'OC\\Core\\Migrations\\Version30000Date20240906095113' => $baseDir . '/core/Migrations/Version30000Date20240906095113.php', + 'OC\\Core\\Migrations\\Version31000Date20241018063111' => $baseDir . '/core/Migrations/Version31000Date20241018063111.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => $baseDir . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 0e93ee8addd7e..99f8afb61f1ac 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1417,6 +1417,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version30000Date20240814180800' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240814180800.php', 'OC\\Core\\Migrations\\Version30000Date20240815080800' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240815080800.php', 'OC\\Core\\Migrations\\Version30000Date20240906095113' => __DIR__ . '/../../..' . '/core/Migrations/Version30000Date20240906095113.php', + 'OC\\Core\\Migrations\\Version31000Date20241018063111' => __DIR__ . '/../../..' . '/core/Migrations/Version31000Date20241018063111.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\ResponseDefinitions' => __DIR__ . '/../../..' . '/core/ResponseDefinitions.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php index 157948e6e0c0c..e453a1eaf1264 100644 --- a/lib/private/SystemTag/SystemTagObjectMapper.php +++ b/lib/private/SystemTag/SystemTagObjectMapper.php @@ -260,4 +260,19 @@ function (ISystemTag $tag) { ); } } + + public function getAvailableObjectTypes(): array { + $query = $this->connection->getQueryBuilder(); + $query->selectDistinct('objecttype') + ->from(self::RELATION_TABLE); + + $result = $query->executeQuery(); + $objectTypes = []; + while ($row = $result->fetch()) { + $objectTypes[] = $row['objecttype']; + } + $result->closeCursor(); + + return $objectTypes; + } } diff --git a/lib/public/SystemTag/ISystemTagObjectMapper.php b/lib/public/SystemTag/ISystemTagObjectMapper.php index f6e46ad4d846d..cd4c3495171bb 100644 --- a/lib/public/SystemTag/ISystemTagObjectMapper.php +++ b/lib/public/SystemTag/ISystemTagObjectMapper.php @@ -111,4 +111,14 @@ public function unassignTags(string $objId, string $objectType, $tagIds); * @since 9.0.0 */ public function haveTag($objIds, string $objectType, string $tagId, bool $all = true): bool; + + + /** + * Get the list of object types that have objects assigned to them. + * + * @return string[] list of object types + * + * @since 31.0.0 + */ + public function getAvailableObjectTypes(): array; } diff --git a/version.php b/version.php index 24c7289a98dca..86f352a3a7daa 100644 --- a/version.php +++ b/version.php @@ -9,7 +9,7 @@ // between betas, final and RCs. This is _not_ the public version number. Reset minor/patch level // when updating major/minor version number. -$OC_Version = [31, 0, 0, 3]; +$OC_Version = [31, 0, 0, 4]; // The human-readable string $OC_VersionString = '31.0.0 dev';