diff options
Diffstat (limited to 'apps/workflowengine/src/components/Checks')
9 files changed, 358 insertions, 376 deletions
diff --git a/apps/workflowengine/src/components/Checks/FileMimeType.vue b/apps/workflowengine/src/components/Checks/FileMimeType.vue index 86f1a6b8cb1..6817b128e27 100644 --- a/apps/workflowengine/src/components/Checks/FileMimeType.vue +++ b/apps/workflowengine/src/components/Checks/FileMimeType.vue @@ -1,97 +1,87 @@ <!-- - - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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 <http://www.gnu.org/licenses/>. - - - --> - + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div> - <NcMultiselect :value="currentValue" + <NcSelect :model-value="currentValue" :placeholder="t('workflowengine', 'Select a file type')" label="label" - track-by="pattern" :options="options" - :multiple="false" - :tagging="false" + :clearable="false" @input="setValue"> - <template slot="singleLabel" slot-scope="props"> - <span v-if="props.option.icon" class="option__icon" :class="props.option.icon" /> - <img v-else - class="option__icon-img" - :src="props.option.iconUrl" - alt=""> - <span class="option__title option__title_single">{{ props.option.label }}</span> + <template #option="option"> + <span v-if="option.icon" class="option__icon" :class="option.icon" /> + <span v-else class="option__icon-img"> + <img :src="option.iconUrl" alt=""> + </span> + <span class="option__title"> + <NcEllipsisedOption :name="String(option.label)" /> + </span> </template> - <template slot="option" slot-scope="props"> - <span v-if="props.option.icon" class="option__icon" :class="props.option.icon" /> - <img v-else - class="option__icon-img" - :src="props.option.iconUrl" - alt=""> - <span class="option__title">{{ props.option.label }}</span> + <template #selected-option="selectedOption"> + <span v-if="selectedOption.icon" class="option__icon" :class="selectedOption.icon" /> + <span v-else class="option__icon-img"> + <img :src="selectedOption.iconUrl" alt=""> + </span> + <span class="option__title"> + <NcEllipsisedOption :name="String(selectedOption.label)" /> + </span> </template> - </NcMultiselect> + </NcSelect> <input v-if="!isPredefined" + :value="currentValue.id" type="text" - :value="currentValue.pattern" :placeholder="t('workflowengine', 'e.g. httpd/unix-directory')" @input="updateCustom"> </div> </template> <script> -import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect' -import valueMixin from './../../mixins/valueMixin' +import NcEllipsisedOption from '@nextcloud/vue/components/NcEllipsisedOption' +import NcSelect from '@nextcloud/vue/components/NcSelect' import { imagePath } from '@nextcloud/router' export default { name: 'FileMimeType', components: { - NcMultiselect, + NcEllipsisedOption, + NcSelect, + }, + props: { + modelValue: { + type: String, + default: '', + }, }, - mixins: [ - valueMixin, - ], + + emits: ['update:model-value'], + data() { return { predefinedTypes: [ { icon: 'icon-folder', label: t('workflowengine', 'Folder'), - pattern: 'httpd/unix-directory', + id: 'httpd/unix-directory', }, { icon: 'icon-picture', label: t('workflowengine', 'Images'), - pattern: '/image\\/.*/', + id: '/image\\/.*/', }, { iconUrl: imagePath('core', 'filetypes/x-office-document'), label: t('workflowengine', 'Office documents'), - pattern: '/(vnd\\.(ms-|openxmlformats-|oasis\\.opendocument).*)$/', + id: '/(vnd\\.(ms-|openxmlformats-|oasis\\.opendocument).*)$/', }, { iconUrl: imagePath('core', 'filetypes/application-pdf'), label: t('workflowengine', 'PDF documents'), - pattern: 'application/pdf', + id: 'application/pdf', }, ], + newValue: '', } }, computed: { @@ -99,7 +89,7 @@ export default { return [...this.predefinedTypes, this.customValue] }, isPredefined() { - const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.pattern) + const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.id) if (matchingPredefined) { return true } @@ -109,59 +99,74 @@ export default { return { icon: 'icon-settings-dark', label: t('workflowengine', 'Custom MIME type'), - pattern: '', + id: '', } }, currentValue() { - const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.pattern) + const matchingPredefined = this.predefinedTypes.find((type) => this.newValue === type.id) if (matchingPredefined) { return matchingPredefined } return { icon: 'icon-settings-dark', label: t('workflowengine', 'Custom mimetype'), - pattern: this.newValue, + id: this.newValue, } }, }, + watch: { + modelValue() { + this.updateInternalValue() + }, + }, + methods: { validateRegex(string) { const regexRegex = /^\/(.*)\/([gui]{0,3})$/ const result = regexRegex.exec(string) return result !== null }, + updateInternalValue() { + this.newValue = this.modelValue + }, setValue(value) { if (value !== null) { - this.newValue = value.pattern - this.$emit('input', this.newValue) + this.newValue = value.id + this.$emit('update:model-value', this.newValue) } }, updateCustom(event) { - this.newValue = event.target.value - this.$emit('input', this.newValue) + this.newValue = event.target.value || event.detail[0] + this.$emit('update:model-value', this.newValue) }, }, } </script> <style scoped lang="scss"> - .multiselect, input[type='text'] { - width: 100%; - } - .multiselect >>> .multiselect__content-wrapper li>span, - .multiselect >>> .multiselect__single { - display: flex; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } +.v-select, +input[type='text'] { + width: 100%; +} + +input[type=text] { + min-height: 48px; +} - .option__icon { - display: inline-block; - min-width: 30px; - background-position: left; - } +.option__icon, +.option__icon-img { + display: inline-block; + min-width: 30px; + background-position: center; + vertical-align: middle; +} + +.option__icon-img { + text-align: center; +} - .option__icon-img { - margin-right: 14px; - } +.option__title { + display: inline-flex; + width: calc(100% - 36px); + vertical-align: middle; +} </style> diff --git a/apps/workflowengine/src/components/Checks/FileSystemTag.vue b/apps/workflowengine/src/components/Checks/FileSystemTag.vue index 1c99be3816d..e71b0cd259a 100644 --- a/apps/workflowengine/src/components/Checks/FileSystemTag.vue +++ b/apps/workflowengine/src/components/Checks/FileSystemTag.vue @@ -1,52 +1,37 @@ <!-- - - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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 <http://www.gnu.org/licenses/>. - - - --> - + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> - <MultiselectTags v-model="newValue" + <NcSelectTags v-model="newValue" :multiple="false" @input="update" /> </template> <script> -import MultiselectTags from '@nextcloud/vue/dist/Components/NcMultiselectTags.js' +import NcSelectTags from '@nextcloud/vue/components/NcSelectTags' export default { name: 'FileSystemTag', components: { - MultiselectTags, + NcSelectTags, }, props: { - value: { + modelValue: { type: String, default: '', }, }, + + emits: ['update:model-value'], + data() { return { newValue: [], } }, watch: { - value() { + modelValue() { this.updateValue() }, }, @@ -55,19 +40,15 @@ export default { }, methods: { updateValue() { - if (this.value !== '') { - this.newValue = this.value + if (this.modelValue !== '') { + this.newValue = parseInt(this.modelValue) } else { this.newValue = null } }, update() { - this.$emit('input', this.newValue || '') + this.$emit('update:model-value', this.newValue || '') }, }, } </script> - -<style scoped> - -</style> diff --git a/apps/workflowengine/src/components/Checks/RequestTime.vue b/apps/workflowengine/src/components/Checks/RequestTime.vue index d8bfaff63a5..5b1a4ef1cfa 100644 --- a/apps/workflowengine/src/components/Checks/RequestTime.vue +++ b/apps/workflowengine/src/components/Checks/RequestTime.vue @@ -1,3 +1,7 @@ +<!-- + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div class="timeslot"> <input v-model="newValue.startTime" @@ -12,33 +16,31 @@ <p v-if="!valid" class="invalid-hint"> {{ t('workflowengine', 'Please enter a valid time span') }} </p> - <NcMultiselect v-show="valid" + <NcSelect v-show="valid" v-model="newValue.timezone" + :clearable="false" :options="timezones" @input="update" /> </div> </template> <script> -import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect' +import NcSelect from '@nextcloud/vue/components/NcSelect' import moment from 'moment-timezone' -import valueMixin from '../../mixins/valueMixin' const zones = moment.tz.names() export default { name: 'RequestTime', components: { - NcMultiselect, + NcSelect, }, - mixins: [ - valueMixin, - ], props: { - value: { + modelValue: { type: String, - default: '', + default: '[]', }, }, + emits: ['update:model-value'], data() { return { timezones: zones, @@ -48,21 +50,31 @@ export default { endTime: null, timezone: moment.tz.guess(), }, + stringifiedValue: '[]', } }, - mounted() { - this.validate() + watch: { + modelValue() { + this.updateInternalValue() + }, + }, + beforeMount() { + // this is necessary to keep so the value is re-applied when a different + // check is being removed. + this.updateInternalValue() }, methods: { - updateInternalValue(value) { + updateInternalValue() { try { - const data = JSON.parse(value) + const data = JSON.parse(this.modelValue) if (data.length === 2) { this.newValue = { startTime: data[0].split(' ', 2)[0], endTime: data[1].split(' ', 2)[0], timezone: data[0].split(' ', 2)[1], } + this.stringifiedValue = `["${this.newValue.startTime} ${this.newValue.timezone}","${this.newValue.endTime} ${this.newValue.timezone}"]` + this.validate() } } catch (e) { // ignore invalid values @@ -84,8 +96,8 @@ export default { this.newValue.timezone = moment.tz.guess() } if (this.validate()) { - const output = `["${this.newValue.startTime} ${this.newValue.timezone}","${this.newValue.endTime} ${this.newValue.timezone}"]` - this.$emit('input', output) + this.stringifiedValue = `["${this.newValue.startTime} ${this.newValue.timezone}","${this.newValue.endTime} ${this.newValue.timezone}"]` + this.$emit('update:model-value', this.stringifiedValue) } }, }, @@ -104,7 +116,7 @@ export default { margin-bottom: 5px; } - .multiselect::v-deep .multiselect__tags:not(:hover):not(:focus):not(:active) { + .multiselect:deep(.multiselect__tags:not(:hover):not(:focus):not(:active)) { border: 1px solid transparent; } @@ -112,9 +124,10 @@ export default { width: 50%; margin: 0; margin-bottom: 5px; + min-height: 48px; &.timeslot--start { - margin-right: 5px; + margin-inline-end: 5px; width: calc(50% - 5px); } } diff --git a/apps/workflowengine/src/components/Checks/RequestURL.vue b/apps/workflowengine/src/components/Checks/RequestURL.vue index 1a5b5cc7f87..21b3a9cacbe 100644 --- a/apps/workflowengine/src/components/Checks/RequestURL.vue +++ b/apps/workflowengine/src/components/Checks/RequestURL.vue @@ -1,75 +1,72 @@ <!-- - - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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 <http://www.gnu.org/licenses/>. - - - --> - + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div> - <NcMultiselect :value="currentValue" + <NcSelect v-model="newValue" + :value="currentValue" :placeholder="t('workflowengine', 'Select a request URL')" label="label" - track-by="pattern" - group-values="children" - group-label="label" + :clearable="false" :options="options" - :multiple="false" - :tagging="false" @input="setValue"> - <template slot="singleLabel" slot-scope="props"> - <span class="option__icon" :class="props.option.icon" /> - <span class="option__title option__title_single">{{ props.option.label }}</span> + <template #option="option"> + <span class="option__icon" :class="option.icon" /> + <span class="option__title"> + <NcEllipsisedOption :name="String(option.label)" /> + </span> </template> - <template slot="option" slot-scope="props"> - <span class="option__icon" :class="props.option.icon" /> - <span class="option__title">{{ props.option.label }} {{ props.option.$groupLabel }}</span> + <template #selected-option="selectedOption"> + <span class="option__icon" :class="selectedOption.icon" /> + <span class="option__title"> + <NcEllipsisedOption :name="String(selectedOption.label)" /> + </span> </template> - </NcMultiselect> + </NcSelect> <input v-if="!isPredefined" type="text" - :value="currentValue.pattern" + :value="currentValue.id" :placeholder="placeholder" @input="updateCustom"> </div> </template> <script> -import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect' -import valueMixin from '../../mixins/valueMixin' +import NcEllipsisedOption from '@nextcloud/vue/components/NcEllipsisedOption' +import NcSelect from '@nextcloud/vue/components/NcSelect' +import valueMixin from '../../mixins/valueMixin.js' export default { name: 'RequestURL', components: { - NcMultiselect, + NcEllipsisedOption, + NcSelect, }, mixins: [ valueMixin, ], + props: { + modelValue: { + type: String, + default: '', + }, + operator: { + type: String, + default: '', + }, + }, + + emits: ['update:model-value'], + data() { return { newValue: '', predefinedTypes: [ { - label: t('workflowengine', 'Predefined URLs'), - children: [ - { pattern: 'webdav', label: t('workflowengine', 'Files WebDAV') }, - ], + icon: 'icon-files-dark', + id: 'webdav', + label: t('workflowengine', 'Files WebDAV'), }, ], } @@ -79,30 +76,23 @@ export default { return [...this.predefinedTypes, this.customValue] }, placeholder() { - if (this.check.operator === 'matches' || this.check.operator === '!matches') { + if (this.operator === 'matches' || this.operator === '!matches') { return '/^https\\:\\/\\/localhost\\/index\\.php$/i' } return 'https://localhost/index.php' }, matchingPredefined() { return this.predefinedTypes - .map(groups => groups.children) - .flat() - .find((type) => this.newValue === type.pattern) + .find((type) => this.newValue === type.id) }, isPredefined() { return !!this.matchingPredefined }, customValue() { return { - label: t('workflowengine', 'Others'), - children: [ - { - icon: 'icon-settings-dark', - label: t('workflowengine', 'Custom URL'), - pattern: '', - }, - ], + icon: 'icon-settings-dark', + label: t('workflowengine', 'Custom URL'), + id: '', } }, currentValue() { @@ -112,7 +102,7 @@ export default { return { icon: 'icon-settings-dark', label: t('workflowengine', 'Custom URL'), - pattern: this.newValue, + id: this.newValue, } }, }, @@ -125,25 +115,37 @@ export default { setValue(value) { // TODO: check if value requires a regex and set the check operator according to that if (value !== null) { - this.newValue = value.pattern - this.$emit('input', this.newValue) + this.newValue = value.id + this.$emit('update:model-value', this.newValue) } }, updateCustom(event) { this.newValue = event.target.value - this.$emit('input', this.newValue) + this.$emit('update:model-value', this.newValue) }, }, } </script> <style scoped lang="scss"> - .multiselect, input[type='text'] { + .v-select, + input[type='text'] { width: 100%; } + input[type='text'] { + min-height: 48px; + } + .option__icon { display: inline-block; min-width: 30px; - background-position: left; + background-position: center; + vertical-align: middle; + } + + .option__title { + display: inline-flex; + width: calc(100% - 36px); + vertical-align: middle; } </style> diff --git a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue index c4a5265ac99..825a112f6fc 100644 --- a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue +++ b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue @@ -1,76 +1,64 @@ <!-- - - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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 <http://www.gnu.org/licenses/>. - - - --> - + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div> - <NcMultiselect :value="currentValue" + <NcSelect v-model="currentValue" :placeholder="t('workflowengine', 'Select a user agent')" label="label" - track-by="pattern" :options="options" - :multiple="false" - :tagging="false" + :clearable="false" @input="setValue"> - <template slot="singleLabel" slot-scope="props"> - <span class="option__icon" :class="props.option.icon" /> - <!-- v-html can be used here as t() always passes our translated strings though DOMPurify.sanitize --> - <!-- eslint-disable-next-line vue/no-v-html --> - <span class="option__title option__title_single" v-html="props.option.label" /> + <template #option="option"> + <span class="option__icon" :class="option.icon" /> + <span class="option__title"> + <NcEllipsisedOption :name="String(option.label)" /> + </span> </template> - <template slot="option" slot-scope="props"> - <span class="option__icon" :class="props.option.icon" /> - <!-- eslint-disable-next-line vue/no-v-html --> - <span v-if="props.option.$groupLabel" class="option__title" v-html="props.option.$groupLabel" /> - <!-- eslint-disable-next-line vue/no-v-html --> - <span v-else class="option__title" v-html="props.option.label" /> + <template #selected-option="selectedOption"> + <span class="option__icon" :class="selectedOption.icon" /> + <span class="option__title"> + <NcEllipsisedOption :name="String(selectedOption.label)" /> + </span> </template> - </NcMultiselect> + </NcSelect> <input v-if="!isPredefined" + v-model="newValue" type="text" - :value="currentValue.pattern" @input="updateCustom"> </div> </template> <script> -import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect' -import valueMixin from '../../mixins/valueMixin' +import NcEllipsisedOption from '@nextcloud/vue/components/NcEllipsisedOption' +import NcSelect from '@nextcloud/vue/components/NcSelect' +import valueMixin from '../../mixins/valueMixin.js' export default { name: 'RequestUserAgent', components: { - NcMultiselect, + NcEllipsisedOption, + NcSelect, }, mixins: [ valueMixin, ], + props: { + modelValue: { + type: String, + default: '', + }, + }, + emits: ['update:model-value'], data() { return { newValue: '', predefinedTypes: [ - { pattern: 'android', label: t('workflowengine', 'Android client'), icon: 'icon-phone' }, - { pattern: 'ios', label: t('workflowengine', 'iOS client'), icon: 'icon-phone' }, - { pattern: 'desktop', label: t('workflowengine', 'Desktop client'), icon: 'icon-desktop' }, - { pattern: 'mail', label: t('workflowengine', 'Thunderbird & Outlook addons'), icon: 'icon-mail' }, + { id: 'android', label: t('workflowengine', 'Android client'), icon: 'icon-phone' }, + { id: 'ios', label: t('workflowengine', 'iOS client'), icon: 'icon-phone' }, + { id: 'desktop', label: t('workflowengine', 'Desktop client'), icon: 'icon-desktop' }, + { id: 'mail', label: t('workflowengine', 'Thunderbird & Outlook addons'), icon: 'icon-mail' }, ], } }, @@ -80,7 +68,7 @@ export default { }, matchingPredefined() { return this.predefinedTypes - .find((type) => this.newValue === type.pattern) + .find((type) => this.newValue === type.id) }, isPredefined() { return !!this.matchingPredefined @@ -89,18 +77,23 @@ export default { return { icon: 'icon-settings-dark', label: t('workflowengine', 'Custom user agent'), - pattern: '', + id: '', } }, - currentValue() { - if (this.matchingPredefined) { - return this.matchingPredefined - } - return { - icon: 'icon-settings-dark', - label: t('workflowengine', 'Custom user agent'), - pattern: this.newValue, - } + currentValue: { + get() { + if (this.matchingPredefined) { + return this.matchingPredefined + } + return { + icon: 'icon-settings-dark', + label: t('workflowengine', 'Custom user agent'), + id: this.newValue, + } + }, + set(value) { + this.newValue = value + }, }, }, methods: { @@ -112,43 +105,37 @@ export default { setValue(value) { // TODO: check if value requires a regex and set the check operator according to that if (value !== null) { - this.newValue = value.pattern - this.$emit('input', this.newValue) + this.newValue = value.id + this.$emit('update:model-value', this.newValue) } }, - updateCustom(event) { - this.newValue = event.target.value - this.$emit('input', this.newValue) + updateCustom() { + this.newValue = this.currentValue.id + this.$emit('update:model-value', this.newValue) }, }, } </script> <style scoped> - .multiselect, input[type='text'] { + .v-select, + input[type='text'] { width: 100%; } - .multiselect .multiselect__content-wrapper li>span { - display: flex; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - .multiselect::v-deep .multiselect__single { - width: 100%; - display: flex; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + input[type='text'] { + min-height: 48px; } + .option__icon { display: inline-block; min-width: 30px; - background-position: left; + background-position: center; + vertical-align: middle; } + .option__title { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; + display: inline-flex; + width: calc(100% - 36px); + vertical-align: middle; } </style> diff --git a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue index ba55d88c81c..f9606b7ca26 100644 --- a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue +++ b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue @@ -1,44 +1,31 @@ <!-- - - @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - - - - @author Julius Härtl <jus@bitgrid.net> - - - - @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 <http://www.gnu.org/licenses/>. - - - --> - + - SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div> - <NcMultiselect :value="currentValue" + <NcSelect :aria-label-combobox="t('workflowengine', 'Select groups')" + :aria-label-listbox="t('workflowengine', 'Groups')" + :clearable="false" :loading="status.isLoading && groups.length === 0" + :placeholder="t('workflowengine', 'Type to search for group …')" :options="groups" - :multiple="false" + :model-value="currentValue" label="displayname" - track-by="id" - @search-change="searchAsync" - @input="(value) => $emit('input', value.id)" /> + @search="searchAsync" + @input="update" /> </div> </template> <script> -import NcMultiselect from '@nextcloud/vue/dist/Components/NcMultiselect' -import axios from '@nextcloud/axios' +import { translate as t } from '@nextcloud/l10n' import { generateOcsUrl } from '@nextcloud/router' +import axios from '@nextcloud/axios' +import NcSelect from '@nextcloud/vue/components/NcSelect' + const groups = [] +const wantedGroups = [] const status = { isLoading: false, } @@ -46,10 +33,10 @@ const status = { export default { name: 'RequestUserGroup', components: { - NcMultiselect, + NcSelect, }, props: { - value: { + modelValue: { type: String, default: '', }, @@ -58,28 +45,52 @@ export default { default: () => { return {} }, }, }, + emits: ['update:model-value'], data() { return { groups, status, + wantedGroups, + newValue: '', } }, computed: { - currentValue() { - return this.groups.find(group => group.id === this.value) || null + currentValue: { + get() { + return this.groups.find(group => group.id === this.newValue) || null + }, + set(value) { + this.newValue = value + }, + }, + }, + watch: { + modelValue() { + this.updateInternalValue() }, }, async mounted() { + // If empty, load first chunk of groups if (this.groups.length === 0) { await this.searchAsync('') } - if (this.currentValue === null) { - await this.searchAsync(this.value) + // If a current group is set but not in our list of groups then search for that group + if (this.currentValue === null && this.newValue) { + await this.searchAsync(this.newValue) } }, methods: { + t, + searchAsync(searchQuery) { if (this.status.isLoading) { + if (searchQuery) { + // The first 20 groups are loaded up front (indicated by an + // empty searchQuery parameter), afterwards we may load + // groups that have not been fetched yet, but are used + // in existing rules. + this.enqueueWantedGroup(searchQuery) + } return } @@ -92,21 +103,54 @@ export default { }) }) this.status.isLoading = false + this.findGroupByQueue() }, (error) => { console.error('Error while loading group list', error.response) }) }, + async updateInternalValue() { + if (!this.newValue) { + await this.searchAsync(this.modelValue) + } + this.newValue = this.modelValue + }, addGroup(group) { const index = this.groups.findIndex((item) => item.id === group.id) if (index === -1) { this.groups.push(group) } }, + hasGroup(group) { + const index = this.groups.findIndex((item) => item.id === group) + return index > -1 + }, + update(value) { + this.newValue = value.id + this.$emit('update:model-value', this.newValue) + }, + enqueueWantedGroup(expectedGroupId) { + const index = this.wantedGroups.findIndex((groupId) => groupId === expectedGroupId) + if (index === -1) { + this.wantedGroups.push(expectedGroupId) + } + }, + async findGroupByQueue() { + let nextQuery + do { + nextQuery = this.wantedGroups.shift() + if (this.hasGroup(nextQuery)) { + nextQuery = undefined + } + } while (!nextQuery && this.wantedGroups.length > 0) + if (nextQuery) { + await this.searchAsync(nextQuery) + } + }, }, } </script> <style scoped> - .multiselect { - width: 100%; - } +.v-select { + width: 100%; +} </style> diff --git a/apps/workflowengine/src/components/Checks/file.js b/apps/workflowengine/src/components/Checks/file.js index b244199c2cc..568efc81cd3 100644 --- a/apps/workflowengine/src/components/Checks/file.js +++ b/apps/workflowengine/src/components/Checks/file.js @@ -1,29 +1,12 @@ /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license AGPL-3.0-or-later - * - * 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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { stringValidator, validateIPv4, validateIPv6 } from '../../helpers/validators' -import FileMimeType from './FileMimeType' -import FileSystemTag from './FileSystemTag' +import { stringValidator, validateIPv4, validateIPv6 } from '../../helpers/validators.js' +import { registerCustomElement } from '../../helpers/window.js' +import FileMimeType from './FileMimeType.vue' +import FileSystemTag from './FileSystemTag.vue' const stringOrRegexOperators = () => { return [ @@ -52,7 +35,7 @@ const FileChecks = [ class: 'OCA\\WorkflowEngine\\Check\\FileMimeType', name: t('workflowengine', 'File MIME type'), operators: stringOrRegexOperators, - component: FileMimeType, + element: registerCustomElement(FileMimeType, 'oca-workflowengine-checks-file_mime_type'), }, { @@ -98,7 +81,7 @@ const FileChecks = [ { operator: 'is', name: t('workflowengine', 'is tagged with') }, { operator: '!is', name: t('workflowengine', 'is not tagged with') }, ], - component: FileSystemTag, + element: registerCustomElement(FileSystemTag, 'oca-workflowengine-file_system_tag'), }, ] diff --git a/apps/workflowengine/src/components/Checks/index.js b/apps/workflowengine/src/components/Checks/index.js index 11db7fafa9c..fc52f95f78a 100644 --- a/apps/workflowengine/src/components/Checks/index.js +++ b/apps/workflowengine/src/components/Checks/index.js @@ -1,26 +1,9 @@ /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license AGPL-3.0-or-later - * - * 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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import FileChecks from './file' -import RequestChecks from './request' +import FileChecks from './file.js' +import RequestChecks from './request.js' export default [...FileChecks, ...RequestChecks] diff --git a/apps/workflowengine/src/components/Checks/request.js b/apps/workflowengine/src/components/Checks/request.js index c5ed0ece439..b91f00baef0 100644 --- a/apps/workflowengine/src/components/Checks/request.js +++ b/apps/workflowengine/src/components/Checks/request.js @@ -1,29 +1,13 @@ /** - * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license AGPL-3.0-or-later - * - * 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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import RequestUserAgent from './RequestUserAgent' -import RequestTime from './RequestTime' -import RequestURL from './RequestURL' -import RequestUserGroup from './RequestUserGroup' +import { registerCustomElement } from '../../helpers/window.js' +import RequestUserAgent from './RequestUserAgent.vue' +import RequestTime from './RequestTime.vue' +import RequestURL from './RequestURL.vue' +import RequestUserGroup from './RequestUserGroup.vue' const RequestChecks = [ { @@ -35,7 +19,7 @@ const RequestChecks = [ { operator: 'matches', name: t('workflowengine', 'matches') }, { operator: '!matches', name: t('workflowengine', 'does not match') }, ], - component: RequestURL, + element: registerCustomElement(RequestURL, 'oca-workflowengine-checks-request_url'), }, { class: 'OCA\\WorkflowEngine\\Check\\RequestTime', @@ -44,7 +28,7 @@ const RequestChecks = [ { operator: 'in', name: t('workflowengine', 'between') }, { operator: '!in', name: t('workflowengine', 'not between') }, ], - component: RequestTime, + element: registerCustomElement(RequestTime, 'oca-workflowengine-checks-request_time'), }, { class: 'OCA\\WorkflowEngine\\Check\\RequestUserAgent', @@ -55,16 +39,16 @@ const RequestChecks = [ { operator: 'matches', name: t('workflowengine', 'matches') }, { operator: '!matches', name: t('workflowengine', 'does not match') }, ], - component: RequestUserAgent, + element: registerCustomElement(RequestUserAgent, 'oca-workflowengine-checks-request_user_agent'), }, { class: 'OCA\\WorkflowEngine\\Check\\UserGroupMembership', - name: t('workflowengine', 'User group membership'), + name: t('workflowengine', 'Group membership'), operators: [ { operator: 'is', name: t('workflowengine', 'is member of') }, { operator: '!is', name: t('workflowengine', 'is not member of') }, ], - component: RequestUserGroup, + element: registerCustomElement(RequestUserGroup, 'oca-workflowengine-checks-request_user_group'), }, ] |