Loading lib/Controller/ViewController.php +0 −7 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ declare(strict_types=1); 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; Loading @@ -46,9 +45,6 @@ class ViewController extends Controller { /** @var AppointmentConfigService */ private $appointmentConfigService; /** @var CategoriesService */ private $categoriesService; /** @var IInitialState */ private $initialStateService; Loading @@ -64,7 +60,6 @@ class ViewController extends Controller { IRequest $request, IConfig $config, AppointmentConfigService $appointmentConfigService, CategoriesService $categoriesService, IInitialState $initialStateService, IAppManager $appManager, ?string $userId, Loading @@ -72,7 +67,6 @@ class ViewController extends Controller { parent::__construct($appName, $request); $this->config = $config; $this->appointmentConfigService = $appointmentConfigService; $this->categoriesService = $categoriesService; $this->initialStateService = $initialStateService; $this->appManager = $appManager; $this->userId = $userId; Loading Loading @@ -143,7 +137,6 @@ class ViewController extends Controller { $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'); Loading lib/Service/CategoriesService.phpdeleted 100644 → 0 +0 −144 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Calendar App * * @copyright 2023 Claus-Justus Heine <himself@claus-justus-heine.de> * * @author Claus-Justus Heine <himself@claus-justus-heine.de> * * 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 <http://www.gnu.org/licenses/>. * */ 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<int, Category>} */ 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; } } src/components/Editor/Properties/PropertySelectMultiple.vue +10 −37 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -102,10 +99,6 @@ export default { type: Boolean, default: false, }, customLabelHeading: { type: String, default: 'Custom Categories', }, }, data() { return { Loading @@ -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) } Loading @@ -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 = [] } Loading @@ -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 }, }, } </script> src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue +2 −5 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ <template> <span class="property-select-multiple-colored-tag"> <div v-if="!isGroupLabel" class="property-select-multiple-colored-tag__color-indicator" :style="{ 'background-color': color }" /> <div class="property-select-multiple-colored-tag__color-indicator" :style="{ 'background-color': color}" /> <span class="property-select-multiple-colored-tag__label">{{ label }}</span> </span> </template> Loading @@ -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 }) Loading @@ -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) Loading src/mixins/EditorMixin.js +0 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -315,11 +314,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 * Loading Loading
lib/Controller/ViewController.php +0 −7 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ declare(strict_types=1); 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; Loading @@ -46,9 +45,6 @@ class ViewController extends Controller { /** @var AppointmentConfigService */ private $appointmentConfigService; /** @var CategoriesService */ private $categoriesService; /** @var IInitialState */ private $initialStateService; Loading @@ -64,7 +60,6 @@ class ViewController extends Controller { IRequest $request, IConfig $config, AppointmentConfigService $appointmentConfigService, CategoriesService $categoriesService, IInitialState $initialStateService, IAppManager $appManager, ?string $userId, Loading @@ -72,7 +67,6 @@ class ViewController extends Controller { parent::__construct($appName, $request); $this->config = $config; $this->appointmentConfigService = $appointmentConfigService; $this->categoriesService = $categoriesService; $this->initialStateService = $initialStateService; $this->appManager = $appManager; $this->userId = $userId; Loading Loading @@ -143,7 +137,6 @@ class ViewController extends Controller { $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'); Loading
lib/Service/CategoriesService.phpdeleted 100644 → 0 +0 −144 Original line number Diff line number Diff line <?php declare(strict_types=1); /** * Calendar App * * @copyright 2023 Claus-Justus Heine <himself@claus-justus-heine.de> * * @author Claus-Justus Heine <himself@claus-justus-heine.de> * * 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 <http://www.gnu.org/licenses/>. * */ 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<int, Category>} */ 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; } }
src/components/Editor/Properties/PropertySelectMultiple.vue +10 −37 Original line number Diff line number Diff line Loading @@ -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" Loading Loading @@ -102,10 +99,6 @@ export default { type: Boolean, default: false, }, customLabelHeading: { type: String, default: 'Custom Categories', }, }, data() { return { Loading @@ -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) } Loading @@ -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 = [] } Loading @@ -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 }, }, } </script>
src/components/Editor/Properties/PropertySelectMultipleColoredOption.vue +2 −5 Original line number Diff line number Diff line Loading @@ -23,7 +23,7 @@ <template> <span class="property-select-multiple-colored-tag"> <div v-if="!isGroupLabel" class="property-select-multiple-colored-tag__color-indicator" :style="{ 'background-color': color }" /> <div class="property-select-multiple-colored-tag__color-indicator" :style="{ 'background-color': color}" /> <span class="property-select-multiple-colored-tag__label">{{ label }}</span> </span> </template> Loading @@ -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 }) Loading @@ -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) Loading
src/mixins/EditorMixin.js +0 −6 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -315,11 +314,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 * Loading