Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/server.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulius Härtl <jus@bitgrid.net>2019-08-06 18:40:30 +0300
committerJulius Härtl <jus@bitgrid.net>2019-09-10 10:01:22 +0300
commitad976c66fd9a78e6d90224091634b04a25a08f40 (patch)
tree7ad9280b669d4fa8991867302d3e8b684b4be496 /apps/workflowengine/src
parent9eb7a318649b3ca2bf85ccdb1db5ecb3be0bde70 (diff)
Unified workflow management
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Diffstat (limited to 'apps/workflowengine/src')
-rw-r--r--apps/workflowengine/src/admin.js2
-rw-r--r--apps/workflowengine/src/components/Check.vue104
-rw-r--r--apps/workflowengine/src/components/Event.vue84
-rw-r--r--apps/workflowengine/src/components/Operation.vue83
-rw-r--r--apps/workflowengine/src/components/Operations/ConvertToPdf.vue45
-rw-r--r--apps/workflowengine/src/components/Operations/Tag.vue29
-rw-r--r--apps/workflowengine/src/components/Rule.vue209
-rw-r--r--apps/workflowengine/src/components/Values/FileMimeType.vue39
-rw-r--r--apps/workflowengine/src/components/Values/SizeValue.vue28
-rw-r--r--apps/workflowengine/src/components/Workflow.vue146
-rw-r--r--apps/workflowengine/src/filemimetypeplugin.js5
-rw-r--r--apps/workflowengine/src/filesizeplugin.js5
-rw-r--r--apps/workflowengine/src/services/Operation.js141
-rw-r--r--apps/workflowengine/src/workflowengine.js9
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