summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2019-09-02 11:35:33 +0200
committerJulius Härtl <jus@bitgrid.net>2019-09-10 09:01:24 +0200
commit1742f97acf62df08d2527120200f6e70736f33ec (patch)
tree3efbdde4e2a167004a70f65910c8e72ad8f2b4aa
parent69ac169fd9ff3b798e6744a685d2292a1a8a565b (diff)
downloadnextcloud-server-1742f97acf62df08d2527120200f6e70736f33ec.tar.gz
nextcloud-server-1742f97acf62df08d2527120200f6e70736f33ec.zip
Allow placeholder and validation without custom vue component
Signed-off-by: Julius Härtl <jus@bitgrid.net>
-rw-r--r--apps/workflowengine/src/components/Check.vue41
-rw-r--r--apps/workflowengine/src/components/Rule.vue4
-rw-r--r--apps/workflowengine/src/components/Values/FileMimeType.vue71
-rw-r--r--apps/workflowengine/src/components/Values/file.js30
-rw-r--r--apps/workflowengine/src/store.js6
-rw-r--r--apps/workflowengine/src/workflowengine.js2
6 files changed, 140 insertions, 14 deletions
diff --git a/apps/workflowengine/src/components/Check.vue b/apps/workflowengine/src/components/Check.vue
index 8583288016f..bd3c471fc3c 100644
--- a/apps/workflowengine/src/components/Check.vue
+++ b/apps/workflowengine/src/components/Check.vue
@@ -6,9 +6,9 @@
<Multiselect :disabled="!currentOption" v-model="currentOperator" :options="operators"
label="name" track-by="operator" :allow-empty="false"
:placeholder="t('workflowengine', 'Select a comparator')" @input="updateCheck" />
- <component :is="currentOption.component" v-if="currentOperator && currentComponent" v-model="check.value" :disabled="!currentOption" />
- <input v-else v-model="check.value" type="text"
- @input="updateCheck" :disabled="!currentOption">
+ <component :is="currentOption.component" v-if="currentOperator && currentComponent" v-model="check.value" :disabled="!currentOption" :check="check" @valid="valid=true && validate()" @invalid="valid=false && validate()" />
+ <input v-else v-model="check.value" type="text" :class="{ invalid: !valid }"
+ @input="updateCheck" :disabled="!currentOption" :placeholder="valuePlaceholder">
<Actions>
<ActionButton v-if="deleteVisible || !currentOption" icon="icon-delete" @click="$emit('remove')" />
</Actions>
@@ -34,6 +34,10 @@ export default {
check: {
type: Object,
required: true
+ },
+ rule: {
+ type: Object,
+ required: true
}
},
data() {
@@ -41,7 +45,8 @@ export default {
deleteVisible: false,
currentOption: null,
currentOperator: null,
- options: []
+ options: [],
+ valid: true,
}
},
computed: {
@@ -56,6 +61,11 @@ export default {
if (!this.currentOption) { return [] }
const currentComponent = this.Checks[this.currentOption.class].component
return currentComponent
+ },
+ valuePlaceholder() {
+ if (this.currentOption && this.currentOption.placeholder) {
+ return this.currentOption.placeholder(this.check)
+ }
}
},
mounted() {
@@ -63,6 +73,11 @@ export default {
this.currentOption = this.Checks[this.check.class]
this.currentOperator = this.operators.find((operator) => operator.operator === this.check.operator)
},
+ watch: {
+ 'check.operator': function () {
+ this.validate()
+ }
+ },
methods: {
showDelete() {
this.deleteVisible = true
@@ -70,12 +85,27 @@ export default {
hideDelete() {
this.deleteVisible = false
},
+ validate() {
+ if (this.currentOption && this.currentOption.validate) {
+ if(this.currentOption.validate(this.check)) {
+ this.valid = true
+ } else {
+ this.valid = false
+ }
+ }
+ this.$store.dispatch('setValid', { rule: this.rule, valid: this.rule.valid && this.valid })
+ return this.valid
+ },
updateCheck() {
if (this.check.class !== this.currentOption.class) {
this.currentOperator = this.operators[0]
}
this.check.class = this.currentOption.class
this.check.operator = this.currentOperator.operator
+
+ if (!this.validate()) {
+ return
+ }
this.$emit('update', this.check)
}
}
@@ -107,4 +137,7 @@ export default {
margin-top: -5px;
margin-bottom: -5px;
}
+ .invalid {
+ border: 1px solid var(--color-error) !important;
+ }
</style>
diff --git a/apps/workflowengine/src/components/Rule.vue b/apps/workflowengine/src/components/Rule.vue
index f1551d985ca..88129525eb3 100644
--- a/apps/workflowengine/src/components/Rule.vue
+++ b/apps/workflowengine/src/components/Rule.vue
@@ -8,7 +8,7 @@
</p>
<p v-for="check in rule.checks">
<span>{{ t('workflowengine', 'and') }}</span>
- <Check :check="check" @update="updateRule" @remove="removeCheck(check)" />
+ <Check :check="check" :rule="rule" @update="updateRule" @remove="removeCheck(check)" />
</p>
<p>
<span />
@@ -74,7 +74,7 @@ export default {
return this.$store.getters.getOperationForRule(this.rule)
},
ruleStatus() {
- if (this.error) {
+ if (this.error || !this.rule.valid) {
return {
title: t('workflowengine', 'The configuration is invalid'),
class: 'icon-close-white invalid',
diff --git a/apps/workflowengine/src/components/Values/FileMimeType.vue b/apps/workflowengine/src/components/Values/FileMimeType.vue
index 75729af8073..56d8aa6098e 100644
--- a/apps/workflowengine/src/components/Values/FileMimeType.vue
+++ b/apps/workflowengine/src/components/Values/FileMimeType.vue
@@ -1,47 +1,104 @@
<template>
- <input type="text" v-model="test">
+ <div>
+
+ <multiselect
+ :value="currentValue"
+ placeholder="Select a file type"
+ label="label"
+ track-by="pattern"
+ :options="options" :multiple="false" :tagging="false" @input="setValue">
+ <template slot="singleLabel" slot-scope="props">
+ <span class="option__icon" :class="props.option.icon"></span>
+ <span class="option__title option__title_single">{{ props.option.label }}</span>
+ </template>
+ <template slot="option" slot-scope="props">
+ <span class="option__icon" :class="props.option.icon"></span>
+ <span class="option__title">{{ props.option.label }}</span>
+ </template>
+ </multiselect>
+ <input type="text" :value="currentValue.pattern" @input="updateCustom"/>
+ </div>
</template>
<script>
import { Multiselect } from 'nextcloud-vue'
export default {
- name: 'SizeValue',
+ name: 'FileMimeType',
components: {
Multiselect
},
data() {
return {
- test: 'test',
+ value: '',
predefinedTypes: [
{
icon: 'icon-picture',
- label: 'Images',
+ label: t('workflowengine', 'Images'),
pattern: '/image\\/.*/'
},
{
icon: 'icon-category-office',
- label: 'Office documents',
+ label: t('workflowengine', 'Office documents'),
pattern: '/(vnd\\.(ms-|openxmlformats-).*))$/'
},
{
icon: 'icon-filetype-file',
- label: 'PDF documents',
+ label: t('workflowengine', 'PDF documents'),
pattern: 'application/pdf'
}
]
}
},
+ computed: {
+ options() {
+ return [...this.predefinedTypes, this.customValue]
+ },
+ customValue() {
+ const matchingPredefined = this.predefinedTypes.find((type) => this.value.pattern === type.pattern)
+ return {
+ icon: 'icon-settings-dark',
+ label: t('workflowengine', 'Custom pattern'),
+ pattern: '',
+ }
+ },
+ currentValue() {
+ const matchingPredefined = this.predefinedTypes.find((type) => this.value === type.pattern)
+ if (matchingPredefined) {
+ return matchingPredefined
+ }
+ return {
+ icon: 'icon-settings-dark',
+ label: t('workflowengine', 'Custom pattern'),
+ pattern: this.value,
+ }
+ }
+ },
methods: {
validateRegex(string) {
var regexRegex = /^\/(.*)\/([gui]{0,3})$/
var result = regexRegex.exec(string)
return result !== null
+ },
+ setValue (value) {
+ // TODO: check if value requires a regex and set the check operator according to that
+ if (value !== null) {
+ this.value = value.pattern
+ }
+ },
+ updateCustom (event) {
+ console.log(event)
+ this.value = event.target.value
}
}
}
</script>
<style scoped>
-
+ .multiselect::v-deep .multiselect__single {
+ display: flex;
+ }
+ input, .multiselect {
+ width: 100%;
+ }
</style>
diff --git a/apps/workflowengine/src/components/Values/file.js b/apps/workflowengine/src/components/Values/file.js
index 4a473dbc13c..fa6851cc5c2 100644
--- a/apps/workflowengine/src/components/Values/file.js
+++ b/apps/workflowengine/src/components/Values/file.js
@@ -40,6 +40,36 @@ const FileChecks = Object.values(OCA.WorkflowEngine.Plugins).map((plugin) => {
// new way of registering checks
+
+const validateRegex = function(string) {
+ var regexRegex = /^\/(.*)\/([gui]{0,3})$/
+ var result = regexRegex.exec(string)
+ return result !== null
+}
+
+FileChecks.push({
+ class: 'OCA\\WorkflowEngine\\Check\\FileName',
+ name: t('workflowengine', 'File name'),
+ operators: [
+ { operator: 'is', name: t('workflowengine', 'is') },
+ { operator: '!is', name: t('workflowengine', 'is not') },
+ { operator: 'matches', name: t('workflowengine', 'matches') },
+ { operator: '!matches', name: t('workflowengine', 'does not match') }
+ ],
+ placeholder: (check) => {
+ if (check.operator === 'matches' || check.operator === '!matches') {
+ return '/^dummy-.+$/i'
+ }
+ return 'filename.txt'
+ },
+ validate: (check) => {
+ if (check.operator === 'matches' || check.operator === '!matches') {
+ return validateRegex(check.value)
+ }
+ return true
+ }
+})
+
FileChecks.push({
class: 'OCA\\WorkflowEngine\\Check\\FileMimeType',
name: t('workflowengine', 'File MIME type'),
diff --git a/apps/workflowengine/src/store.js b/apps/workflowengine/src/store.js
index b38bef0e6ea..a6af51b65d8 100644
--- a/apps/workflowengine/src/store.js
+++ b/apps/workflowengine/src/store.js
@@ -58,7 +58,7 @@ const store = new Vuex.Store({
},
mutations: {
addRule(state, rule) {
- state.rules.push(rule)
+ state.rules.push({ ...rule, valid: true })
},
updateRule(state, rule) {
const index = state.rules.findIndex((item) => rule.id === item.id)
@@ -129,6 +129,10 @@ const store = new Vuex.Store({
await confirmPassword()
await axios.delete(getApiUrl(`/${rule.id}`))
context.commit('removeRule', rule)
+ },
+ setValid (context, { rule, valid }) {
+ rule.valid = valid
+ context.commit('updateRule', rule)
}
},
getters: {
diff --git a/apps/workflowengine/src/workflowengine.js b/apps/workflowengine/src/workflowengine.js
index cae85c3c24a..6f96f454262 100644
--- a/apps/workflowengine/src/workflowengine.js
+++ b/apps/workflowengine/src/workflowengine.js
@@ -16,6 +16,8 @@ import {Operators} from './services/Operation';
* The component should handle the v-model directive properly,
* so it needs a value property to receive data and emit an input
* event once the data has changed
+ * @property {callable} placeholder - Return a placeholder of no custom component is used
+ * @property {callable} validate - validate a check if no custom component is used
**/
/**