diff options
author | Julius Härtl <jus@bitgrid.net> | 2019-08-06 17:40:30 +0200 |
---|---|---|
committer | Julius Härtl <jus@bitgrid.net> | 2019-09-10 09:01:22 +0200 |
commit | ad976c66fd9a78e6d90224091634b04a25a08f40 (patch) | |
tree | 7ad9280b669d4fa8991867302d3e8b684b4be496 /apps/workflowengine | |
parent | 9eb7a318649b3ca2bf85ccdb1db5ecb3be0bde70 (diff) | |
download | nextcloud-server-ad976c66fd9a78e6d90224091634b04a25a08f40.tar.gz nextcloud-server-ad976c66fd9a78e6d90224091634b04a25a08f40.zip |
Unified workflow management
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'apps/workflowengine')
-rw-r--r-- | apps/workflowengine/src/admin.js | 2 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Check.vue | 104 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Event.vue | 84 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Operation.vue | 83 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Operations/ConvertToPdf.vue | 45 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Operations/Tag.vue | 29 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Rule.vue | 209 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Values/FileMimeType.vue | 39 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Values/SizeValue.vue | 28 | ||||
-rw-r--r-- | apps/workflowengine/src/components/Workflow.vue | 146 | ||||
-rw-r--r-- | apps/workflowengine/src/filemimetypeplugin.js | 5 | ||||
-rw-r--r-- | apps/workflowengine/src/filesizeplugin.js | 5 | ||||
-rw-r--r-- | apps/workflowengine/src/services/Operation.js | 141 | ||||
-rw-r--r-- | apps/workflowengine/src/workflowengine.js | 9 |
14 files changed, 926 insertions, 3 deletions
diff --git a/apps/workflowengine/src/admin.js b/apps/workflowengine/src/admin.js index 92f485a8b4c..fb2af941436 100644 --- a/apps/workflowengine/src/admin.js +++ b/apps/workflowengine/src/admin.js @@ -382,4 +382,4 @@ import OperationsTemplate from './templates/operations.handlebars'; this.collection.each(this.renderOperation, this); } }); -})(); +})();
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Check.vue b/apps/workflowengine/src/components/Check.vue new file mode 100644 index 00000000000..c8c7c46aa87 --- /dev/null +++ b/apps/workflowengine/src/components/Check.vue @@ -0,0 +1,104 @@ +<template> + <div class="check" @click="showDelete" v-click-outside="hideDelete"> + <Multiselect v-model="currentOption" :options="options" label="name" + track-by="class" :allow-empty="false" :placeholder="t('workflowengine', 'Select a filter')" + @input="updateCheck" ref="checkSelector"></Multiselect> + <Multiselect v-if="currentOption" v-model="currentOperator" @input="updateCheck" + :options="operators" label="name" track-by="operator" + :allow-empty="false" :placeholder="t('workflowengine', 'Select a comparator')"></Multiselect> + <component v-if="currentOperator && currentComponent" :is="currentOption.component()" v-model="check.value" /> + <input v-else-if="currentOperator" type="text" v-model="check.value" @input="updateCheck" /> + <Actions> + <ActionButton icon="icon-delete" v-if="deleteVisible || !currentOption" @click="$emit('remove')" /> + </Actions> + </div> +</template> + +<script> + import { Multiselect, Actions, ActionButton } from 'nextcloud-vue' + import ClickOutside from 'vue-click-outside'; + + export default { + name: 'Check', + components: { + ActionButton, + Actions, + Multiselect + }, + directives: { + ClickOutside + }, + props: { + check: { + type: Object, + required: true + } + }, + data() { + return { + deleteVisible: false, + currentOption: null, + currentOperator: null, + options: [], + } + }, + mounted() { + this.options = Object.values(OCA.WorkflowEngine.Plugins).map((plugin) => { + if (plugin.component) { + return {...plugin.getCheck(), component: plugin.component} + } + return plugin.getCheck() + }) + this.currentOption = this.options.find((option) => option.class === this.check.class) + this.currentOperator = this.operators.find((operator) => operator.operator === this.check.operator) + this.$nextTick(() => { + this.$refs.checkSelector.$el.focus() + }) + }, + computed: { + operators() { + if (!this.currentOption) + return [] + return this.options.find((item) => item.class === this.currentOption.class).operators + }, + currentComponent() { + if (!this.currentOption) + return [] + let currentComponent = this.options.find((item) => item.class === this.currentOption.class).component + return currentComponent && currentComponent() + } + }, + methods: { + showDelete() { + this.deleteVisible = true + }, + hideDelete() { + this.deleteVisible = false + }, + 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 + this.$emit('update', this.check) + } + } + } +</script> + +<style scoped lang="scss"> + .check { + display: flex; + & > .multiselect, + & > input[type=text] { + margin-right: 5px; + } + } + input[type=text] { + margin: 0; + } + ::placeholder { + font-size: 10px; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Event.vue b/apps/workflowengine/src/components/Event.vue new file mode 100644 index 00000000000..fd5097cecfc --- /dev/null +++ b/apps/workflowengine/src/components/Event.vue @@ -0,0 +1,84 @@ +<template> + <div> + <Multiselect :value="currentEvent" :options="allEvents" label="name" track-by="id" :allow-empty="false" :disabled="allEvents.length <= 1" @input="updateEvent"> + <template slot="singleLabel" slot-scope="props"> + <span class="option__icon" :class="props.option.icon"></span> + <span class="option__title option__title_single">{{ props.option.name }}</span> + </template> + <template slot="option" slot-scope="props"> + <span class="option__icon" :class="props.option.icon"></span> + <span class="option__title">{{ props.option.name }}</span> + </template> + </Multiselect> + </div> +</template> + +<script> + import { Multiselect } from 'nextcloud-vue' + import { eventService, operationService } from '../services/Operation' + + export default { + name: "Event", + components: { + Multiselect + }, + props: { + rule: { + type: Object, + required: true + } + }, + computed: { + currentEvent() { + if (typeof this.rule.event === 'undefined') { + return this.allEvents.length > 0 ? this.allEvents[0] : null + } + return this.allEvents.find(event => event.id === this.rule.event) + }, + allEvents() { + return this.operation.events.map((eventName) => eventService.get(eventName)) + }, + operation() { + return operationService.get(this.rule.class) + } + }, + methods: { + updateEvent(event) { + this.$set(this.rule, 'event', event.id) + this.$emit('update', this.rule) + } + } + } +</script> + +<style scoped> + .multiselect::v-deep .multiselect__single { + display: flex; + } + .multiselect:not(.multiselect--active)::v-deep .multiselect__tags { + background-color: var(--color-main-background) !important; + border: 1px solid transparent; + } + + .multiselect::v-deep .multiselect__tags .multiselect__single { + background-color: var(--color-main-background) !important; + } + + .multiselect:not(.multiselect--disabled)::v-deep .multiselect__tags .multiselect__single { + background-image: var(--icon-triangle-s-000); + background-repeat: no-repeat; + background-position: right center; + } + + input { + border: 1px solid transparent; + } + + .option__title { + margin-left: 5px; + color: var(--color-main-text); + } + .option__title_single { + font-weight: 900; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Operation.vue b/apps/workflowengine/src/components/Operation.vue new file mode 100644 index 00000000000..f7a8f56cede --- /dev/null +++ b/apps/workflowengine/src/components/Operation.vue @@ -0,0 +1,83 @@ +<template> + <div class="actions__item" :class="{'colored': !!color}" :style="{ backgroundColor: color }"> + <div class="icon" :class="icon"></div> + <h3>{{ title }}</h3> + <small>{{ description }}</small> + <slot /> + </div> +</template> + +<script> + export default { + name: "Operation", + props: { + icon: { + type: String, + required: true + }, + title: { + type: String, + required: true + }, + description: { + type: String, + required: true + }, + color: { + type: String, + required: false + }, + } + } +</script> + +<style scoped lang="scss"> + .actions__item { + display: flex; + flex-direction: column; + flex-grow: 1; + margin-left: -1px; + padding: 10px; + border-radius: var(--border-radius-large); + max-width: 230px; + margin-right: 20px; + } + .icon { + display: block; + width: 100%; + height: 50px; + background-size: 50px 50px; + background-position: center center; + margin-top: 10px; + margin-bottom: 20px; + } + h3, small { + padding: 6px; + text-align: center; + display: block; + } + h3 { + margin: 0; + padding: 0; + font-weight: 500; + } + small { + font-size: 10pt; + } + .icon-block { + background-image: url(/apps-extra/files_accesscontrol/img/app-dark.svg); + } + + .icon-convert-pdf { + background-image: url(/apps-extra/workflow_pdf_converter/img/app-dark.svg); + } + + .colored { + .icon { + filter: invert(1); + } + * { + color: var(--color-primary-text) + } + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Operations/ConvertToPdf.vue b/apps/workflowengine/src/components/Operations/ConvertToPdf.vue new file mode 100644 index 00000000000..62c53a4ee6c --- /dev/null +++ b/apps/workflowengine/src/components/Operations/ConvertToPdf.vue @@ -0,0 +1,45 @@ +<template> + <multiselect :options="options" track-by="id" label="text" v-model="value"></multiselect> +</template> + +<script> + const pdfConvertOptions = [ + { + id: 'keep;preserve', + text: t('workflow_pdf_converter', 'Keep original, preserve existing PDFs'), + }, + { + id: 'keep;overwrite', + text: t('workflow_pdf_converter', 'Keep original, overwrite existing PDF'), + }, + { + id: 'delete;preserve', + text: t('workflow_pdf_converter', 'Delete original, preserve existing PDFs'), + }, + { + id: 'delete;overwrite', + text: t('workflow_pdf_converter', 'Delete original, overwrite existing PDF'), + }, + ] + + import { Multiselect } from 'nextcloud-vue' + export default { + name: "ConvertToPdf", + components: {Multiselect}, + data() { + return { + options: pdfConvertOptions, + value: pdfConvertOptions[0] + } + } + } +</script> + +<style scoped> + .multiselect { + width: 100%; + max-width: 300px; + margin: auto; + text-align: center; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Operations/Tag.vue b/apps/workflowengine/src/components/Operations/Tag.vue new file mode 100644 index 00000000000..74e4e4f977b --- /dev/null +++ b/apps/workflowengine/src/components/Operations/Tag.vue @@ -0,0 +1,29 @@ +<template> + <multiselect :options="options" v-model="value" track-by="id" label="title" :multiple="true" :tagging="true" @input="$emit('input', value.map(item => item.id))"></multiselect> +</template> + +<script> + // TODO: fetch tags from endpoint + const tags = [{id: 3, title: 'foo'}, {id: 4, title: 'bar'}] + + import { Multiselect } from 'nextcloud-vue' + export default { + name: "Tag", + components: {Multiselect}, + data() { + return { + options: tags, + value: null + } + } + } +</script> + +<style scoped> + .multiselect { + width: 100%; + max-width: 300px; + margin: auto; + text-align: center; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Rule.vue b/apps/workflowengine/src/components/Rule.vue new file mode 100644 index 00000000000..818e15610fa --- /dev/null +++ b/apps/workflowengine/src/components/Rule.vue @@ -0,0 +1,209 @@ +<template> + <div class="section rule"> + <!-- TODO: icon-confirm --> + <div class="trigger icon-confirm"> + <p> + <span>{{ t('workflowengine', 'When') }}</span> + <Event :rule="rule" @update="updateRule"></Event> + </p> + <p v-for="check in rule.checks"> + <span>{{ t('workflowengine', 'and') }}</span> + <Check :check="check" @update="updateRule" @remove="removeCheck(check)"></Check> + </p> + <p> + <span> </span> + <input v-if="lastCheckComplete" type="button" class="check--add" @click="rule.checks.push({class: null, operator: null, value: null})" value="Add a new filter"/> + </p> + </div> + <div class="action"> + <div class="buttons"> + <button class="status-button icon" :class="ruleStatus.class" v-tooltip="ruleStatus.tooltip" @click="saveRule">{{ ruleStatus.title }}</button> + <Actions> + <ActionButton v-if="rule.id === -1" icon="icon-close" @click="$emit('cancel')">Cancel</ActionButton> + <ActionButton v-else icon="icon-delete" @click="deleteRule">Delete</ActionButton> + </Actions> + </div> + <Operation :icon="operation.icon" :title="operation.title" :description="operation.description"> + <component v-if="operation.options" :is="operation.options" v-model="operation.operation" @input="updateOperation" /> + </Operation> + </div> + </div> +</template> + +<script> + import { Actions, ActionButton, Tooltip } from 'nextcloud-vue' + import Event from './Event' + import Check from './Check' + import Operation from './Operation' + import { operationService } from '../services/Operation' + import axios from 'nextcloud-axios' + import confirmPassword from 'nextcloud-password-confirmation' + + export default { + name: 'Rule', + components: { + Operation, Check, Event, Actions, ActionButton + }, + directives: { + Tooltip + }, + props: { + rule: { + type: Object, + required: true, + } + }, + data () { + return { + editing: false, + operationService, + checks: [], + error: null, + dirty: this.rule.id === -1, + checking: false + } + }, + computed: { + operation() { + return this.operationService.get(this.rule.class) + }, + ruleStatus() { + if (this.error) { + return { title: 'Invalid', class: 'icon-close-white invalid', tooltip: { placement: 'bottom', show: true, content: escapeHTML(this.error.data) } } + } + if (!this.dirty || this.checking) { + return { title: 'Active', class: 'icon icon-checkmark' } + } + return { title: 'Save', class: 'icon-confirm-white primary' } + + + }, + lastCheckComplete() { + const lastCheck = this.rule.checks[this.rule.checks.length-1] + return typeof lastCheck === 'undefined' || lastCheck.class !== null + } + }, + methods: { + updateOperation(operation) { + this.$set(this.rule, 'operation', operation) + }, + async updateRule() { + this.checking = true + if (!this.dirty) { + this.dirty = true + } + try { + let result = await axios.post(OC.generateUrl(`/apps/workflowengine/operations/test`), this.rule) + this.error = null + this.checking = false + } catch (e) { + console.error('Failed to update operation') + this.error = e.response + this.checking = false + } + }, + async saveRule() { + try { + await confirmPassword() + let result + if (this.rule.id === -1) { + result = await axios.post(OC.generateUrl(`/apps/workflowengine/operations`), this.rule) + this.rule.id = result.id + } else { + result = await axios.put(OC.generateUrl(`/apps/workflowengine/operations/${this.rule.id}`), this.rule) + } + this.$emit('update', result.data) + this.dirty = false + this.error = null + } catch (e) { + console.error('Failed to update operation') + this.error = e.response + } + }, + async deleteRule() { + try { + await confirmPassword() + await axios.delete(OC.generateUrl(`/apps/workflowengine/operations/${this.rule.id}`)) + this.$emit('delete') + } catch (e) { + console.error('Failed to delete operation') + this.error = e.response + } + }, + removeCheck(check) { + const index = this.rule.checks.findIndex(item => item === check) + if (index !== -1) { + this.rule.checks.splice(index, 1) + } + } + } + } +</script> + +<style scoped lang="scss"> + button.icon { + padding-left: 32px; + background-position: 10px center; + } + + .status-button { + transition: 0.5s ease all; + } + .status-button.primary { + padding-left: 32px; + background-position: 10px center; + } + .status-button:not(.primary) { + background-color: var(--color-main-background); + } + .status-button.invalid { + background-color: var(--color-warning); + color: #fff; + } + + .rule { + display: flex; + .trigger, .action { + flex-grow: 1; + min-height: 100px; + width: 50%; + } + .action { + position: relative; + .buttons { + position: absolute; + right: 0; + } + } + .icon-confirm { + background-position: right center; + } + } + .trigger p, .action p { + display: flex; + align-items: center; + margin-bottom: 5px; + + & > span { + min-width: 50px; + text-align: right; + color: var(--color-text-light); + padding-right: 5px; + } + .multiselect { + flex-grow: 1; + max-width: 300px; + } + } + + .check--add { + background-position: 7px center; + background-color: transparent; + padding-left: 6px; + width: 160px; + border-radius: var(--border-radius); + font-weight: normal; + text-align: left; + font-size: 1em; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Values/FileMimeType.vue b/apps/workflowengine/src/components/Values/FileMimeType.vue new file mode 100644 index 00000000000..70b8f0d984b --- /dev/null +++ b/apps/workflowengine/src/components/Values/FileMimeType.vue @@ -0,0 +1,39 @@ +<template> + <input type="text" /> +</template> + +<script> + import { Multiselect } from 'nextcloud-vue' + + export default { + name: 'SizeValue', + components: { + Multiselect + }, + data() { + return { + predefinedTypes: [ + { + icon: 'icon-picture', + label: 'Images', + pattern: '/image\\/.*/' + }, + { + icon: 'icon-category-office', + label: 'Office documents', + pattern: '/(vnd\\.(ms-|openxmlformats-).*))$/' + }, + { + icon: 'icon-filetype-file', + label: 'PDF documents', + pattern: 'application/pdf' + } + ] + } + } + } +</script> + +<style scoped> + +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Values/SizeValue.vue b/apps/workflowengine/src/components/Values/SizeValue.vue new file mode 100644 index 00000000000..bc4378702e1 --- /dev/null +++ b/apps/workflowengine/src/components/Values/SizeValue.vue @@ -0,0 +1,28 @@ +<template> + <input type="text" placeholder="1 MB" @input="$emit('input', newValue)" v-model="newValue"> +</template> + +<script> + export default { + name: "SizeValue", + props: { + value: { + type: String + } + }, + data() { + return { + newValue: this.value + } + }, + watch: { + value() { + this.newValue = this.value + } + } + } +</script> + +<style scoped> + +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/components/Workflow.vue b/apps/workflowengine/src/components/Workflow.vue new file mode 100644 index 00000000000..9993b328827 --- /dev/null +++ b/apps/workflowengine/src/components/Workflow.vue @@ -0,0 +1,146 @@ +<template> + <div> + <div class="section"> + <h2>{{ t('workflowengine', 'Workflows') }}</h2> + + <div class="actions" v-if="!hasUnsavedRule"> + <Operation v-for="operation in getMainOperations" :key="operation.class" + :icon="operation.icon" :title="operation.title" :description="operation.description" :color="operation.color" + @click.native="createNewRule(operation)"></Operation> + </div> + <div class="actions__more" v-if="!hasUnsavedRule && hasMoreOperations"> + <button class="icon" :class="showMoreOperations ? 'icon-triangle-n' : 'icon-triangle-s'" + @click="showMoreOperations=!showMoreOperations"> + {{ showMoreOperations ? t('workflowengine', 'Show less') : t('workflowengine', 'Show more') }} + </button> + </div> + <transition name="slide"> + <div class="actions" v-if="!hasUnsavedRule && showMoreOperations && hasMoreOperations"> + <Operation v-for="operation in getMoreOperations" :key="operation.class" :icon="operation.icon" :title="operation.title" :description="operation.description" + @click.native="createNewRule(operation)"></Operation> + </div> + </transition> + </div> + + <transition-group name="slide"> + <Rule v-for="rule in rules" :key="rule.id" :rule="rule" @delete="removeRule(rule)" @cancel="removeRule(rule)" @update="updateRule"></Rule> + </transition-group> + + </div> +</template> + +<script> + import Rule from './Rule' + import Operation from './Operation' + import { operationService } from '../services/Operation' + import axios from 'nextcloud-axios' + + const ACTION_LIMIT = 3 + + export default { + name: 'Workflow', + components: { + Operation, + Rule + }, + async mounted() { + const { data } = await axios.get(OC.generateUrl('/apps/workflowengine/operations')) + this.rules = data + }, + data() { + return { + operations: operationService, + showMoreOperations: false, + rules: [] + } + }, + computed: { + hasMoreOperations() { + return Object.keys(this.operations.getAll()).length > ACTION_LIMIT + }, + getMainOperations() { + return Object.values(this.operations.getAll()).slice(0, ACTION_LIMIT) + }, + getMoreOperations() { + return Object.values(this.operations.getAll()).slice(ACTION_LIMIT) + + }, + hasUnsavedRule() { + return !!this.rules.find((rule) => rule.id === -1) + } + }, + methods: { + updateRule(rule) { + let index = this.rules.findIndex((item) => rule === item) + this.$set(this.rules, index, rule) + }, + removeRule(rule) { + let index = this.rules.findIndex((item) => rule === item) + this.rules.splice(index, 1) + }, + showAllOperations() { + + }, + createNewRule(operation) { + this.rules.unshift({ + id: -1, + class: operation.class, + name: '', // unused in the new ui, there for legacy reasons + checks: [], + operation: operation.operation || '' + }) + } + } + } +</script> + +<style scoped lang="scss"> + .actions { + display: flex; + .action__item { + border: 1px solid var(--color-border); + } + } + + .actions__more { + text-align: center; + } + + button.icon { + padding-left: 32px; + background-position: 10px center; + } + + + .slide-enter-active { + -moz-transition-duration: 0.3s; + -webkit-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + transition-duration: 0.3s; + -moz-transition-timing-function: ease-in; + -webkit-transition-timing-function: ease-in; + -o-transition-timing-function: ease-in; + transition-timing-function: ease-in; + } + + .slide-leave-active { + -moz-transition-duration: 0.3s; + -webkit-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + transition-duration: 0.3s; + -moz-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + -webkit-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + -o-transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + transition-timing-function: cubic-bezier(0, 1, 0.5, 1); + } + + .slide-enter-to, .slide-leave { + max-height: 500px; + overflow: hidden; + } + + .slide-enter, .slide-leave-to { + overflow: hidden; + max-height: 0; + } +</style>
\ No newline at end of file diff --git a/apps/workflowengine/src/filemimetypeplugin.js b/apps/workflowengine/src/filemimetypeplugin.js index 17c092d209f..6b929b2aad5 100644 --- a/apps/workflowengine/src/filemimetypeplugin.js +++ b/apps/workflowengine/src/filemimetypeplugin.js @@ -17,6 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ +import FileMimeType from './components/Values/FileMimeType' (function() { @@ -65,6 +66,10 @@ var regexRegex = /^\/(.*)\/([gui]{0,3})$/, result = regexRegex.exec(string); return result !== null; + }, + + component: function () { + return FileMimeType } }; })(); diff --git a/apps/workflowengine/src/filesizeplugin.js b/apps/workflowengine/src/filesizeplugin.js index 0efa9d00edf..ebd72dcd2f4 100644 --- a/apps/workflowengine/src/filesizeplugin.js +++ b/apps/workflowengine/src/filesizeplugin.js @@ -17,7 +17,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ - +import SizeValue from './components/Values/SizeValue' (function() { OCA.WorkflowEngine = OCA.WorkflowEngine || {}; @@ -49,6 +49,9 @@ .tooltip({ placement: 'bottom' }); + }, + component: function () { + return SizeValue } }; })(); diff --git a/apps/workflowengine/src/services/Operation.js b/apps/workflowengine/src/services/Operation.js new file mode 100644 index 00000000000..1de7d2a95ad --- /dev/null +++ b/apps/workflowengine/src/services/Operation.js @@ -0,0 +1,141 @@ +import ConvertToPdf from './../components/Operations/ConvertToPdf' +import Tag from './../components/Operations/Tag' +class OperationService { + + constructor() { + this.operations = {} + } + registerOperation (operation) { + this.operations[operation.class] = Object.assign({ + color: 'var(--color-primary)' + }, operation) + } + + getAll() { + return this.operations + } + + get(className) { + return this.operations[className] + } + +} + +class EventService { + + constructor() { + this.events = {} + } + registerEvent(event) { + this.events[event.id] = event + } + + getAll() { + return this.events + } + + get(id) { + return this.events[id] + } + +} + +const operationService = new OperationService() +const eventService = new EventService() + + +const ALL_CHECKS = [ + 'OCA\\WorkflowEngine\\Check\\FileMimeType', + 'OCA\\WorkflowEngine\\Check\\FileName', + 'OCA\\WorkflowEngine\\Check\\FileSize', + 'OCA\\WorkflowEngine\\Check\\FileSystemTags', + 'OCA\\WorkflowEngine\\Check\\RequestRemoteAddress', + 'OCA\\WorkflowEngine\\Check\\RequestTime', + 'OCA\\WorkflowEngine\\Check\\RequestURL', + 'OCA\\WorkflowEngine\\Check\\RequestUserAgent', + 'OCA\\WorkflowEngine\\Check\\UserGroupMembership' +] + +/** + * TODO: move to separate apps + * TODO: fetch from initial state api + **/ +const EVENT_FILE_ACCESS = 'EVENT_FILE_ACCESS' +const EVENT_FILE_CHANGED = 'EVENT_FILE_CHANGED' +const EVENT_FILE_TAGGED = 'EVENT_FILE_TAGGED' + +eventService.registerEvent({ + id: EVENT_FILE_ACCESS, + name: 'File is accessed', + icon: 'icon-desktop', + checks: ALL_CHECKS, +}) + +eventService.registerEvent({ + id: EVENT_FILE_CHANGED, + name: 'File was updated', + icon: 'icon-folder', + checks: ALL_CHECKS, +}) + + +eventService.registerEvent({ + id: EVENT_FILE_TAGGED, + name: 'File was tagged', + icon: 'icon-tag', + checks: ALL_CHECKS, +}) + +operationService.registerOperation({ + class: 'OCA\\FilesAccessControl\\Operation', + title: 'Block access', + description: 'todo', + icon: 'icon-block', + color: 'var(--color-error)', + events: [ + EVENT_FILE_ACCESS + ], + operation: 'deny' +}) + +operationService.registerOperation({ + class: 'OCA\\FilesAutomatedTagging\\Operation', + title: 'Tag a file', + description: 'todo', + icon: 'icon-tag', + events: [ + EVENT_FILE_CHANGED, + EVENT_FILE_TAGGED + ], + options: Tag + +}) + +operationService.registerOperation({ + class: 'OCA\\WorkflowPDFConverter\\Operation', + title: 'Convert to PDF', + description: 'todo', + color: '#dc5047', + icon: 'icon-convert-pdf', + events: [ + EVENT_FILE_CHANGED, + //EVENT_FILE_TAGGED + ], + options: ConvertToPdf +}) + + +const legacyChecks = Object.values(OCA.WorkflowEngine.Plugins).map((plugin) => { + if (plugin.component) { + return {...plugin.getCheck(), component: plugin.component} + } + return plugin.getCheck() +}).reduce((obj, item) => { + obj[item.class] = item + return obj +}, {}) + +export { + eventService, + operationService +}
\ No newline at end of file diff --git a/apps/workflowengine/src/workflowengine.js b/apps/workflowengine/src/workflowengine.js index 207d2311bcc..6c7af2a446e 100644 --- a/apps/workflowengine/src/workflowengine.js +++ b/apps/workflowengine/src/workflowengine.js @@ -1,4 +1,3 @@ -import './admin' import './filemimetypeplugin' import './filenameplugin' import './filesizeplugin' @@ -10,3 +9,11 @@ import './requestuseragentplugin' import './usergroupmembershipplugin' window.OCA.WorkflowEngine = OCA.WorkflowEngine + +import Vue from 'vue'; + +Vue.prototype.t = t; + +import Settings from './components/Workflow'; +const View = Vue.extend(Settings) +new View({}).$mount('#workflowengine')
\ No newline at end of file |