diff --git a/lib/Controller/ViewController.php b/lib/Controller/ViewController.php index fde305e5b2..f82e7c9df2 100644 --- a/lib/Controller/ViewController.php +++ b/lib/Controller/ViewController.php @@ -26,7 +26,6 @@ namespace OCA\Calendar\Controller; use OCA\Calendar\Service\Appointments\AppointmentConfigService; -use OCA\Calendar\Service\CategoriesService; use OCP\App\IAppManager; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\FileDisplayResponse; @@ -46,9 +45,6 @@ class ViewController extends Controller { /** @var AppointmentConfigService */ private $appointmentConfigService; - /** @var CategoriesService */ - private $categoriesService; - /** @var IInitialState */ private $initialStateService; @@ -64,7 +60,6 @@ public function __construct(string $appName, IRequest $request, IConfig $config, AppointmentConfigService $appointmentConfigService, - CategoriesService $categoriesService, IInitialState $initialStateService, IAppManager $appManager, ?string $userId, @@ -72,7 +67,6 @@ public function __construct(string $appName, parent::__construct($appName, $request); $this->config = $config; $this->appointmentConfigService = $appointmentConfigService; - $this->categoriesService = $categoriesService; $this->initialStateService = $initialStateService; $this->appManager = $appManager; $this->userId = $userId; @@ -143,7 +137,6 @@ public function index():TemplateResponse { $this->initialStateService->provideInitialState('appointmentConfigs', $this->appointmentConfigService->getAllAppointmentConfigurations($this->userId)); $this->initialStateService->provideInitialState('disable_appointments', $disableAppointments); $this->initialStateService->provideInitialState('can_subscribe_link', $canSubscribeLink); - $this->initialStateService->provideInitialState('categories', $this->categoriesService->getCategories($this->userId)); $this->initialStateService->provideInitialState('show_resources', $showResources); return new TemplateResponse($this->appName, 'main'); diff --git a/lib/Service/CategoriesService.php b/lib/Service/CategoriesService.php deleted file mode 100644 index 0c8e744783..0000000000 --- a/lib/Service/CategoriesService.php +++ /dev/null @@ -1,144 +0,0 @@ - - * - * @author Claus-Justus Heine - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library 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 along with this library. If not, see . - * - */ - -namespace OCA\Calendar\Service; - -use OCP\Calendar\ICalendarQuery; -use OCP\Calendar\IManager as ICalendarManager; -use OCP\IL10N; -use OCP\SystemTag\ISystemTag; -use OCP\SystemTag\ISystemTagManager; - -/** - * @psalm-type Category = array{label: string, value: string} - * @psalm-type CategoryGroup = array{group: string, options: array} - */ -class CategoriesService { - /** @var ICalendarManager */ - private $calendarManager; - - /** @var ISystemTagManager */ - private $systemTagManager; - - /** @var IL10N */ - private $l; - - public function __construct(ICalendarManager $calendarManager, - ISystemTagManager $systemTagManager, - IL10N $l10n) { - $this->calendarManager = $calendarManager; - $this->systemTagManager = $systemTagManager; - $this->l = $l10n; - } - - /** - * This is a simplistic brute-force extraction of all already used - * categories from all events accessible to the given user. - * - * @return array - */ - private function getUsedCategories(string $userId): array { - $categories = []; - $principalUri = 'principals/users/' . $userId; - $query = $this->calendarManager->newQuery($principalUri); - $query->addSearchProperty(ICalendarQuery::SEARCH_PROPERTY_CATEGORIES); - $calendarObjects = $this->calendarManager->searchForPrincipal($query); - foreach ($calendarObjects as $objectInfo) { - foreach ($objectInfo['objects'] as $calendarObject) { - if (isset($calendarObject['CATEGORIES'])) { - $categories[] = explode(',', $calendarObject['CATEGORIES'][0][0]); - } - } - } - - // Avoid injecting "broken" categories into the UI (avoid empty - // categories and categories surrounded by spaces) - $categories = array_filter(array_map(fn ($label) => trim($label), array_unique(array_merge(...$categories)))); - - return $categories; - } - - /** - * Return a grouped array with all previously used categories, all system - * tags and all categories found in the iCalendar RFC. - * - * @return CategoryGroup[] - */ - public function getCategories(string $userId): array { - $systemTags = $this->systemTagManager->getAllTags(true); - - $systemTagCategoryLabels = []; - /** @var ISystemTag $systemTag */ - foreach ($systemTags as $systemTag) { - if (!$systemTag->isUserAssignable() || !$systemTag->isUserVisible()) { - continue; - } - $systemTagCategoryLabels[] = $systemTag->getName(); - } - sort($systemTagCategoryLabels); - $systemTagCategoryLabels = array_values(array_filter(array_unique($systemTagCategoryLabels))); - - $rfcCategoryLabels = [ - $this->l->t('Anniversary'), - $this->l->t('Appointment'), - $this->l->t('Business'), - $this->l->t('Education'), - $this->l->t('Holiday'), - $this->l->t('Meeting'), - $this->l->t('Miscellaneous'), - $this->l->t('Non-working hours'), - $this->l->t('Not in office'), - $this->l->t('Personal'), - $this->l->t('Phone call'), - $this->l->t('Sick day'), - $this->l->t('Special occasion'), - $this->l->t('Travel'), - $this->l->t('Vacation'), - ]; - sort($rfcCategoryLabels); - $rfcCategoryLabels = array_values(array_filter(array_unique($rfcCategoryLabels))); - - $standardCategories = array_merge($systemTagCategoryLabels, $rfcCategoryLabels); - $customCategoryLabels = array_values(array_filter($this->getUsedCategories($userId), fn ($label) => !in_array($label, $standardCategories))); - - $categories = [ - [ - 'group' => $this->l->t('Custom Categories'), - 'options' => array_map(fn (string $label) => [ 'label' => $label, 'value' => $label ], $customCategoryLabels), - ], - [ - 'group' => $this->l->t('Collaborative Tags'), - 'options' => array_map(fn (string $label) => [ 'label' => $label, 'value' => $label ], $systemTagCategoryLabels), - ], - [ - 'group' => $this->l->t('Standard Categories'), - 'options' => array_map(fn (string $label) => [ 'label' => $label, 'value' => $label ], $rfcCategoryLabels), - ], - ]; - - return $categories; - } -} diff --git a/src/components/Editor/Properties/PropertySelectMultiple.vue b/src/components/Editor/Properties/PropertySelectMultiple.vue index 21f4523cb0..5bc93fd2d8 100644 --- a/src/components/Editor/Properties/PropertySelectMultiple.vue +++ b/src/components/Editor/Properties/PropertySelectMultiple.vue @@ -42,9 +42,6 @@ :multiple="true" :taggable="true" track-by="label" - group-values="options" - group-label="group" - :group-select="false" label="label" @select="selectValue" @tag="tag" @@ -102,10 +99,6 @@ export default { type: Boolean, default: false, }, - customLabelHeading: { - type: String, - default: 'Custom Categories', - }, }, data() { return { @@ -118,56 +111,45 @@ export default { }, options() { const options = this.propModel.options.slice() - let customOptions = options.find((optionGroup) => optionGroup.group === this.customLabelHeading) - if (!customOptions) { - customOptions = { - group: this.customLabelHeading, - options: [], - } - options.unshift(customOptions) - } for (const category of (this.selectionData ?? [])) { - if (this.findOption(category, options)) { + if (options.find(option => option.value === category.value)) { continue } // Add pseudo options for unknown values - customOptions.options.push({ + options.push({ value: category.value, label: category.label, }) } for (const category of this.value) { - const categoryOption = { value: category, label: category } - if (!this.findOption(categoryOption, options)) { - customOptions.options.push(categoryOption) + if (!options.find(option => option.value === category)) { + options.push({ value: category, label: category }) } } if (this.customLabelBuffer) { for (const category of this.customLabelBuffer) { - if (!this.findOption(category, options)) { - customOptions.options.push(category) + if (!options.find(option => option.value === category.value)) { + options.push(category) } } } - for (const optionGroup of options) { - optionGroup.options = optionGroup.options.sort((a, b) => { + return options + .sort((a, b) => { return a.label.localeCompare( b.label, getLocale().replace('_', '-'), { sensitivity: 'base' }, ) }) - } - return options }, }, created() { for (const category of this.value) { - const option = this.findOption({ value: category }, this.options) + const option = this.options.find(option => option.value === category) if (option) { this.selectionData.push(option) } @@ -190,7 +172,7 @@ export default { // store removed custom options to keep it in the option list const options = this.propModel.options.slice() - if (!this.findOption(value, options)) { + if (!options.find(option => option.value === value.value)) { if (!this.customLabelBuffer) { this.customLabelBuffer = [] } @@ -205,15 +187,6 @@ export default { this.selectionData.push({ value, label: value }) this.$emit('add-single-value', value) }, - findOption(value, availableOptions) { - for (const optionGroup of availableOptions) { - const option = optionGroup.options.find(option => option.value === value.value) - if (option) { - return option - } - } - return undefined - }, }, } diff --git a/src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue b/src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue index 825d0e3ebf..546cdce327 100644 --- a/src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue +++ b/src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue @@ -23,7 +23,7 @@ @@ -41,9 +41,6 @@ export default { }, }, computed: { - isGroupLabel() { - return this.option.$isLabel && this.option.$groupLabel - }, label() { const option = this.option logger.debug('Option render', { option }) @@ -51,7 +48,7 @@ export default { return this.option } - return this.option.$groupLabel ? this.option.$groupLabel : this.option.label + return this.option.label }, colorObject() { return uidToColor(this.label) diff --git a/src/mixins/EditorMixin.js b/src/mixins/EditorMixin.js index 71c33fb74d..32a719db91 100644 --- a/src/mixins/EditorMixin.js +++ b/src/mixins/EditorMixin.js @@ -33,7 +33,6 @@ import { } from 'vuex' import { translate as t } from '@nextcloud/l10n' import { removeMailtoPrefix } from '../utils/attendee.js' -import { loadState } from '@nextcloud/initial-state' /** * This is a mixin for the editor. It contains common Vue stuff, that is @@ -327,11 +326,6 @@ export default { rfcProps() { return getRFCProperties() }, - categoryOptions() { - const categories = { ...this.rfcProps.categories } - categories.options = loadState('calendar', 'categories', []) - return categories - }, /** * Returns whether or not this event can be downloaded from the server * diff --git a/src/views/EditSidebar.vue b/src/views/EditSidebar.vue index 3bb367521c..3a3797ebf9 100644 --- a/src/views/EditSidebar.vue +++ b/src/views/EditSidebar.vue @@ -150,9 +150,8 @@ diff --git a/tests/php/unit/Controller/ViewControllerTest.php b/tests/php/unit/Controller/ViewControllerTest.php index bdf1621bd6..1742d7bdb3 100755 --- a/tests/php/unit/Controller/ViewControllerTest.php +++ b/tests/php/unit/Controller/ViewControllerTest.php @@ -29,7 +29,6 @@ use ChristophWurst\Nextcloud\Testing\TestCase; use OCA\Calendar\Db\AppointmentConfig; use OCA\Calendar\Service\Appointments\AppointmentConfigService; -use OCA\Calendar\Service\CategoriesService; use OCP\App\IAppManager; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Services\IInitialState; @@ -54,9 +53,6 @@ class ViewControllerTest extends TestCase { /** @var AppointmentConfigService|MockObject */ private $appointmentContfigService; - /** @var CategoriesService|MockObject */ - private $categoriesService; - /** @var IInitialState|MockObject */ private $initialStateService; @@ -75,7 +71,6 @@ protected function setUp(): void { $this->appManager = $this->createMock(IAppManager::class); $this->config = $this->createMock(IConfig::class); $this->appointmentContfigService = $this->createMock(AppointmentConfigService::class); - $this->categoriesService = $this->createMock(CategoriesService::class); $this->initialStateService = $this->createMock(IInitialState::class); $this->userId = 'user123'; $this->appData = $this->createMock(IAppData::class); @@ -85,7 +80,6 @@ protected function setUp(): void { $this->request, $this->config, $this->appointmentContfigService, - $this->categoriesService, $this->initialStateService, $this->appManager, $this->userId, @@ -143,19 +137,7 @@ public function testIndex(): void { ->method('getAllAppointmentConfigurations') ->with($this->userId) ->willReturn([new AppointmentConfig()]); - $this->categoriesService->expects(self::once()) - ->method('getCategories') - ->with('user123') - ->willReturn([ - [ - 'group' => 'Test', - 'options' => [ - 'label' => 'hawaii', - 'value' => 'pizza', - ], - ], - ]); - $this->initialStateService->expects(self::exactly(22)) + $this->initialStateService->expects(self::exactly(21)) ->method('provideInitialState') ->withConsecutive( ['app_version', '1.0.0'], @@ -178,15 +160,6 @@ public function testIndex(): void { ['appointmentConfigs', [new AppointmentConfig()]], ['disable_appointments', false], ['can_subscribe_link', false], - ['categories', [ - [ - 'group' => 'Test', - 'options' => [ - 'label' => 'hawaii', - 'value' => 'pizza', - ], - ], - ]], ['show_resources', true], ); @@ -254,19 +227,7 @@ public function testIndexViewFix(string $savedView, string $expectedView): void ->method('getAllAppointmentConfigurations') ->with($this->userId) ->willReturn([new AppointmentConfig()]); - $this->categoriesService->expects(self::once()) - ->method('getCategories') - ->with('user123') - ->willReturn([ - [ - 'group' => 'Test', - 'options' => [ - 'label' => 'hawaii', - 'value' => 'pizza', - ], - ], - ]); - $this->initialStateService->expects(self::exactly(22)) + $this->initialStateService->expects(self::exactly(21)) ->method('provideInitialState') ->withConsecutive( ['app_version', '1.0.0'], @@ -289,15 +250,6 @@ public function testIndexViewFix(string $savedView, string $expectedView): void ['appointmentConfigs', [new AppointmentConfig()]], ['disable_appointments', false], ['can_subscribe_link', false], - ['categories', [ - [ - 'group' => 'Test', - 'options' => [ - 'label' => 'hawaii', - 'value' => 'pizza', - ], - ], - ]], ['show_resources', true], ); diff --git a/tests/php/unit/Service/CategoriesServiceTest.php b/tests/php/unit/Service/CategoriesServiceTest.php deleted file mode 100644 index 65f015a93b..0000000000 --- a/tests/php/unit/Service/CategoriesServiceTest.php +++ /dev/null @@ -1,146 +0,0 @@ - - * - * @author 2023 Christoph Wurst - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * 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 - * along with this program. If not, see . - */ - -namespace OCA\Calendar\Tests\Unit\Service; - -use ChristophWurst\Nextcloud\Testing\ServiceMockObject; -use ChristophWurst\Nextcloud\Testing\TestCase; -use OCA\Calendar\Service\CategoriesService; -use OCP\SystemTag\ISystemTag; -use function array_column; - -class CategoriesServiceTest extends TestCase { - private ServiceMockObject $serviceMock; - private CategoriesService $service; - - protected function setUp(): void { - parent::setUp(); - - $this->serviceMock = $this->createServiceMock(CategoriesService::class); - $this->service = $this->serviceMock->getService(); - - $this->serviceMock->getParameter('l10n') - ->method('t') - ->willReturnArgument(0); - } - - public function testGetCategoriesDefaultsOnly(): void { - $categories = $this->service->getCategories('user123'); - - self::assertCount(3, $categories); - self::assertEquals( - [ - 'Custom Categories', - 'Collaborative Tags', - 'Standard Categories', - ], - array_column($categories, 'group') - ); - self::assertCount(0, $categories[0]['options']); - self::assertCount(0, $categories[1]['options']); - self::assertCount(15, $categories[2]['options']); - } - - public function testGetUsedCategories(): void { - $this->serviceMock->getParameter('calendarManager') - ->expects(self::once()) - ->method('searchForPrincipal') - ->willReturn([ - [ - 'objects' => [], - ], - [ - 'objects' => [ - [ - 'CATEGORIES' => [ - [ - '', - [], - ] - ], - ], - ], - ], - [ - 'objects' => [ - [ - 'CATEGORIES' => [ - [ - 'pizza,party', - [], - ] - ], - ], - ], - ], - [ - 'objects' => [ - [ - 'CATEGORIES' => [ - [ - 'pizza,hawaii', - [], - ] - ], - ], - ], - ], - ]); - - $categories = $this->service->getCategories('user123'); - - self::assertArrayHasKey(0, $categories); - self::assertCount(3, $categories[0]['options']); - self::assertEquals(['pizza', 'party', 'hawaii'], array_column($categories[0]['options'], 'label')); - } - - public function testGetSystemTagsAsCategories(): void { - $tag1 = $this->createMock(ISystemTag::class); - $tag1->method('isUserAssignable')->willReturn(false); - $tag1->method('isUserVisible')->willReturn(true); - $tag2 = $this->createMock(ISystemTag::class); - $tag2->method('isUserAssignable')->willReturn(false); - $tag2->method('isUserVisible')->willReturn(false); - $tag3 = $this->createMock(ISystemTag::class); - $tag3->method('isUserAssignable')->willReturn(true); - $tag3->method('isUserVisible')->willReturn(true); - $tag3->method('getName')->willReturn('fun'); - $this->serviceMock->getParameter('systemTagManager') - ->expects(self::once()) - ->method('getAllTags') - ->with(true) - ->willReturn([ - $tag1, - $tag2, - $tag3, - ]); - - $categories = $this->service->getCategories('user123'); - - self::assertArrayHasKey(1, $categories); - self::assertCount(1, $categories[1]['options']); - self::assertEquals(['fun'], array_column($categories[1]['options'], 'label')); - } -}