diff --git a/apps/workflowengine/appinfo/routes.php b/apps/workflowengine/appinfo/routes.php
index 3798c2a852caea916f7085528342b6de84abaae7..ea44fb6cda1b88765fb5b43a799f9a64e7327834 100644
--- a/apps/workflowengine/appinfo/routes.php
+++ b/apps/workflowengine/appinfo/routes.php
@@ -25,5 +25,6 @@ return [
 	],
 	'ocs-resources' => [
 		'global_workflows' => ['url' => '/api/v1/workflows/global'],
+		'user_workflows' => ['url' => '/api/v1/workflows/user'],
 	],
 ];
diff --git a/apps/workflowengine/css/admin.scss b/apps/workflowengine/css/admin.scss
deleted file mode 100644
index 523bb7f8ba62b8e7cbab516a34d8d47a6b6129a0..0000000000000000000000000000000000000000
--- a/apps/workflowengine/css/admin.scss
+++ /dev/null
@@ -1,86 +0,0 @@
-.workflowengine {
-	.pull-right {
-		float: right
-	}
-
-	.invalid-input {
-		border-color: var(--color-error);
-	}
-
-	.operation {
-		padding: 5px 5px 20px;
-		margin-bottom: 20px;
-		border-bottom: var(--color-border) 1px solid;
-		border-left: rgba(0, 0, 0, 0) 2px solid;
-
-		&.modified {
-			border-left: var(--color-warning) 2px solid;
-		}
-
-		button {
-			margin-bottom: 0;
-		}
-
-		span.info {
-			padding: 7px;
-			color: var(--color-border);
-		}
-
-		.msg {
-			border-radius: 3px;
-			margin: 3px 3px 3px 0;
-			padding: 5px;
-			transition: opacity .5s;
-		}
-
-		.check {
-			padding-left: 5px;
-			&:hover {
-				background-color: var(--color-background-dark);
-			}
-		}
-
-		.button-delete,
-		.button-delete-check {
-			opacity: 0.5;
-			padding: 11px;
-
-			&:hover,
-			&:focus {
-				opacity: 1;
-				cursor: pointer;
-			}
-		}
-	}
-
-	.rules {
-		.icon-loading-small {
-			display: inline-block;
-			margin-right: 5px;
-		}
-
-		.operation:nth-last-child(2) {
-			margin-bottom: 5px;
-		}
-	}
-
-	.operation-header {
-		display: flex;
-		margin-left: 5px;
-
-		.operation-name {
-			width: 100%;
-			max-width: 500px;
-			align-self: flex-start;
-			padding: 10px;
-		}
-
-		.select2-container {
-			align-self: flex-end;
-		}
-
-		.icon-delete {
-			margin-left: auto;
-		}
-	}
-}
diff --git a/apps/workflowengine/css/multiselect.css b/apps/workflowengine/css/multiselect.css
deleted file mode 100644
index 0c3b8b009eb24bb36aa7457e39c2619b2b1ff279..0000000000000000000000000000000000000000
--- a/apps/workflowengine/css/multiselect.css
+++ /dev/null
@@ -1,12 +0,0 @@
-#workflowengine .multiselect .multiselect__single {
-	display: flex;
-}
-
-#workflowengine .option__icon {
-	min-width: 25px;
-}
-
-#workflowengine input,
-#workflowengine .multiselect {
-	width: 100%;
-}
diff --git a/apps/workflowengine/js/workflowengine.js b/apps/workflowengine/js/workflowengine.js
index 00f319c94c8aa8e3cb42a1a1adb0123859b07636..34684abd5edea3d983d7bf3de56d9db3cd020a07 100644
Binary files a/apps/workflowengine/js/workflowengine.js and b/apps/workflowengine/js/workflowengine.js differ
diff --git a/apps/workflowengine/js/workflowengine.js.map b/apps/workflowengine/js/workflowengine.js.map
index ea545fe501248c3afa22c0f6526a925975cc91b9..d13b6b66df1427ba04ba5d3ac4aac8ab3d81196d 100644
Binary files a/apps/workflowengine/js/workflowengine.js.map and b/apps/workflowengine/js/workflowengine.js.map differ
diff --git a/apps/workflowengine/lib/Controller/UserWorkflowsController.php b/apps/workflowengine/lib/Controller/UserWorkflowsController.php
index 179e6b1ad11bdf00023f0abd24f1a79bd1a0ecd6..3e907d22696b6a81ed93ea9ab180f1584a6b7de9 100644
--- a/apps/workflowengine/lib/Controller/UserWorkflowsController.php
+++ b/apps/workflowengine/lib/Controller/UserWorkflowsController.php
@@ -79,8 +79,8 @@ class UserWorkflowsController extends AWorkflowController {
 	 * @throws OCSBadRequestException
 	 * @throws OCSForbiddenException
 	 */
-	public function create(string $class, string $name, array $checks, string $operation): DataResponse {
-		return parent::create($class, $name, $checks, $operation);
+	public function create(string $class, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
+		return parent::create($class, $name, $checks, $operation, $entity, $events);
 	}
 
 	/**
@@ -88,8 +88,8 @@ class UserWorkflowsController extends AWorkflowController {
 	 * @throws OCSBadRequestException
 	 * @throws OCSForbiddenException
 	 */
-	public function update(int $id, string $name, array $checks, string $operation): DataResponse {
-		return parent::update($id, $name, $checks, $operation);
+	public function update(int $id, string $name, array $checks, string $operation, string $entity, array $events): DataResponse {
+		return parent::update($id, $name, $checks, $operation, $entity, $events);
 	}
 
 	/**
diff --git a/apps/workflowengine/lib/Manager.php b/apps/workflowengine/lib/Manager.php
index 07438b2f7cb130cfd8733895510e6c6941dd9045..1c2c76a94c4dd7f7ecf141e6f8e803cbfece62e6 100644
--- a/apps/workflowengine/lib/Manager.php
+++ b/apps/workflowengine/lib/Manager.php
@@ -561,6 +561,8 @@ class Manager implements IManager {
 
 			$operation['checks'][] = $check;
 		}
+		$operation['events'] = json_decode($operation['events'], true);
+
 
 		return $operation;
 	}
diff --git a/apps/workflowengine/lib/Service/RuleMatcher.php b/apps/workflowengine/lib/Service/RuleMatcher.php
index bcfcd5dd21909b102c569acf5839ca78f640d48d..95c68b6337091d031f7357b89973f70fe4350a4b 100644
--- a/apps/workflowengine/lib/Service/RuleMatcher.php
+++ b/apps/workflowengine/lib/Service/RuleMatcher.php
@@ -24,12 +24,9 @@ declare(strict_types=1);
 
 namespace OCA\WorkflowEngine\Service;
 
-use OCA\WorkflowEngine\AppInfo\Application;
-use OCA\WorkflowEngine\Entity\File;
 use OCA\WorkflowEngine\Helper\ScopeContext;
 use OCA\WorkflowEngine\Manager;
 use OCP\AppFramework\QueryException;
-use OCP\Files\Node;
 use OCP\Files\Storage\IStorage;
 use OCP\IL10N;
 use OCP\IServerContainer;
@@ -128,7 +125,7 @@ class RuleMatcher implements IRuleMatcher {
 				list($entity, $subject) = $entityInfo;
 				$checkInstance->setEntitySubject($entity, $subject);
 			}
-		} else {
+		} else if(!$checkInstance instanceof ICheck) {
 			// Check is invalid
 			throw new \UnexpectedValueException($this->l->t('Check %s is invalid or does not exist', $check['class']));
 		}
diff --git a/apps/workflowengine/src/components/Check.vue b/apps/workflowengine/src/components/Check.vue
index 4f68e3944956dc76b52ef7f468fffeee00622b3e..10828c1dd8aaec09e0c8361c519c4ee370d77217 100644
--- a/apps/workflowengine/src/components/Check.vue
+++ b/apps/workflowengine/src/components/Check.vue
@@ -11,6 +11,7 @@
 		<Multiselect v-model="currentOperator"
 			:disabled="!currentOption"
 			:options="operators"
+			class="comparator"
 			label="name"
 			track-by="operator"
 			:allow-empty="false"
@@ -21,6 +22,7 @@
 			v-model="check.value"
 			:disabled="!currentOption"
 			:check="check"
+			class="option"
 			@input="updateCheck"
 			@valid="(valid=true) && validate()"
 			@invalid="(valid=false) && validate()" />
@@ -30,9 +32,10 @@
 			:class="{ invalid: !valid }"
 			:disabled="!currentOption"
 			:placeholder="valuePlaceholder"
+			class="option"
 			@input="updateCheck">
 		<Actions v-if="deleteVisible || !currentOption">
-			<ActionButton icon="icon-delete" @click="$emit('remove')" />
+			<ActionButton icon="icon-close" @click="$emit('remove')" />
 		</Actions>
 	</div>
 </template>
@@ -73,17 +76,16 @@ export default {
 		}
 	},
 	computed: {
-		Checks() {
+		checks() {
 			return this.$store.getters.getChecksForEntity(this.rule.entity)
 		},
 		operators() {
 			if (!this.currentOption) { return [] }
-			return this.Checks[this.currentOption.class].operators
+			return this.checks[this.currentOption.class].operators
 		},
 		currentComponent() {
 			if (!this.currentOption) { return [] }
-			const currentComponent = this.Checks[this.currentOption.class].component
-			return currentComponent
+			return this.checks[this.currentOption.class].component
 		},
 		valuePlaceholder() {
 			if (this.currentOption && this.currentOption.placeholder) {
@@ -98,8 +100,8 @@ export default {
 		}
 	},
 	mounted() {
-		this.options = Object.values(this.Checks)
-		this.currentOption = this.Checks[this.check.class]
+		this.options = Object.values(this.checks)
+		this.currentOption = this.checks[this.check.class]
 		this.currentOperator = this.operators.find((operator) => operator.operator === this.check.operator)
 	},
 	methods: {
@@ -111,13 +113,8 @@ export default {
 		},
 		validate() {
 			if (this.currentOption && this.currentOption.validate) {
-				if (this.currentOption.validate(this.check)) {
-					this.valid = true
-				} else {
-					this.valid = false
-				}
+				this.valid = !!this.currentOption.validate(this.check)
 			}
-			this.$store.dispatch('setValid', { rule: this.rule, valid: this.rule.valid && this.valid })
 			return this.valid
 		},
 		updateCheck() {
@@ -128,7 +125,7 @@ export default {
 			this.check.operator = this.currentOperator.operator
 
 			if (!this.validate()) {
-				return
+				this.check.invalid = !this.valid
 			}
 			this.$emit('update', this.check)
 		}
@@ -142,14 +139,30 @@ export default {
 		flex-wrap: wrap;
 		width: 100%;
 		padding-right: 20px;
-		& > *:not(.icon-delete) {
+		& > *:not(.close) {
 			width: 180px;
 		}
+		& > .comparator {
+			min-width: 130px;
+			width: 130px;
+		}
+		& > .option {
+			min-width: 230px;
+			width: 230px;
+		}
 		& > .multiselect,
 		& > input[type=text] {
 			margin-right: 5px;
 			margin-bottom: 5px;
 		}
+
+		.multiselect::v-deep .multiselect__content-wrapper li>span,
+		.multiselect::v-deep .multiselect__single {
+			display: block;
+			white-space: nowrap;
+			overflow: hidden;
+			text-overflow: ellipsis;
+		}
 	}
 	input[type=text] {
 		margin: 0;
@@ -157,14 +170,12 @@ export default {
 	::placeholder {
 		font-size: 10px;
 	}
-	.icon-delete {
+	button.action-item.action-item--single.icon-close {
+		height: 44px;
+		width: 44px;
 		margin-top: -5px;
 		margin-bottom: -5px;
 	}
-	button.action-item.action-item--single.icon-delete {
-		height: 34px;
-		width: 34px;
-	}
 	.invalid {
 		border: 1px solid var(--color-error) !important;
 	}
diff --git a/apps/workflowengine/src/components/Checks/FileMimeType.vue b/apps/workflowengine/src/components/Checks/FileMimeType.vue
index 2f2487c9adf43ba2affc61a85db4691d66d0b87b..e91636f5130a0a54da5d881f435627aea5dcd697 100644
--- a/apps/workflowengine/src/components/Checks/FileMimeType.vue
+++ b/apps/workflowengine/src/components/Checks/FileMimeType.vue
@@ -32,17 +32,20 @@
 			:tagging="false"
 			@input="setValue">
 			<template slot="singleLabel" slot-scope="props">
-				<span class="option__icon" :class="props.option.icon" />
+				<span v-if="props.option.icon" class="option__icon" :class="props.option.icon" />
+				<img v-else :src="props.option.iconUrl">
 				<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 v-if="props.option.icon" class="option__icon" :class="props.option.icon" />
+				<img v-else :src="props.option.iconUrl">
 				<span class="option__title">{{ props.option.label }}</span>
 			</template>
 		</Multiselect>
 		<input v-if="!isPredefined"
 			type="text"
 			:value="currentValue.pattern"
+			:placeholder="t('workflowengine', 'e.g. httpd/unix-directory')"
 			@input="updateCustom">
 	</div>
 </template>
@@ -68,12 +71,12 @@ export default {
 					pattern: '/image\\/.*/'
 				},
 				{
-					icon: 'icon-category-office',
+					iconUrl: OC.imagePath('core', 'filetypes/x-office-document'),
 					label: t('workflowengine', 'Office documents'),
 					pattern: '/(vnd\\.(ms-|openxmlformats-).*))$/'
 				},
 				{
-					icon: 'icon-filetype-file',
+					iconUrl: OC.imagePath('core', 'filetypes/application-pdf'),
 					label: t('workflowengine', 'PDF documents'),
 					pattern: 'application/pdf'
 				}
@@ -130,3 +133,15 @@ export default {
 	}
 }
 </script>
+<style scoped>
+	.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;
+	}
+</style>
diff --git a/apps/workflowengine/src/components/Checks/FileSystemTag.vue b/apps/workflowengine/src/components/Checks/FileSystemTag.vue
index d3fd440f1b19e6613912f11eb1df8924f2f49d85..268f8c4e33fe7e572471e1f49d8eb020b7fbfbe4 100644
--- a/apps/workflowengine/src/components/Checks/FileSystemTag.vue
+++ b/apps/workflowengine/src/components/Checks/FileSystemTag.vue
@@ -23,7 +23,7 @@
 <template>
 	<MultiselectTag v-model="newValue"
 		:multiple="false"
-		label="Select a tag"
+		:label="t('workflowengine', 'Select a tag')"
 		@input="update" />
 </template>
 
diff --git a/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue b/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue
index c58f53c8e97ea3aec01cc1d45640ed6e8a3b043e..804025dc0e5c49ce92602ae04935df92bb5ed4f1 100644
--- a/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue
+++ b/apps/workflowengine/src/components/Checks/MultiselectTag/MultiselectTag.vue
@@ -56,10 +56,8 @@ export default {
 			required: true
 		},
 		value: {
-			type: Array,
-			default() {
-				return []
-			}
+			type: [String, Array],
+			default: null
 		},
 		disabled: {
 			type: Boolean,
@@ -67,7 +65,7 @@ export default {
 		},
 		multiple: {
 			type: Boolean,
-			default: true
+			default: false
 		}
 	},
 	data() {
diff --git a/apps/workflowengine/src/components/Checks/RequestTime.vue b/apps/workflowengine/src/components/Checks/RequestTime.vue
index 1d7950f64f8ff769978d2bba0206428260cdc4ba..26a4907fd1844037e3ef1f8052d792ac7da3efb6 100644
--- a/apps/workflowengine/src/components/Checks/RequestTime.vue
+++ b/apps/workflowengine/src/components/Checks/RequestTime.vue
@@ -1,15 +1,21 @@
 <template>
 	<div class="timeslot">
-		<Multiselect v-model="newValue.timezone" :options="timezones" @input="update" />
 		<input v-model="newValue.startTime"
 			type="text"
 			class="timeslot--start"
-			placeholder="08:00"
+			placeholder="e.g. 08:00"
 			@input="update">
 		<input v-model="newValue.endTime"
 			type="text"
-			placeholder="18:00"
+			placeholder="e.g. 18:00"
 			@input="update">
+		<p v-if="!valid" class="invalid-hint">
+			{{ t('workflowengine', 'Please enter a valid time span') }}
+		</p>
+		<Multiselect v-show="valid"
+			v-model="newValue.timezone"
+			:options="timezones"
+			@input="update" />
 	</div>
 </template>
 
@@ -30,7 +36,7 @@ export default {
 	props: {
 		value: {
 			type: String,
-			default: '1 MB'
+			default: ''
 		}
 	},
 	data() {
@@ -46,14 +52,17 @@ export default {
 	},
 	methods: {
 		updateInternalValue(value) {
-			var data = JSON.parse(value)
-			var startTime = data[0].split(' ', 2)[0]
-			var endTime = data[1].split(' ', 2)[0]
-			var timezone = data[0].split(' ', 2)[1]
-			this.newValue = {
-				startTime: startTime,
-				endTime: endTime,
-				timezone: timezone
+			try {
+				const data = JSON.parse(value)
+				if (data.length === 2) {
+					this.newValue = {
+						startTime: data[0].split(' ', 2)[0],
+						endTime: data[1].split(' ', 2)[0],
+						timezone: data[0].split(' ', 2)[1]
+					}
+				}
+			} catch (e) {
+				// ignore invalid values
 			}
 		},
 		validate() {
@@ -86,14 +95,23 @@ export default {
 			margin-bottom: 5px;
 		}
 
+		.multiselect::v-deep .multiselect__tags:not(:hover):not(:focus):not(:active) {
+			border: 1px solid transparent;
+		}
+
 		input[type=text] {
 			width: 50%;
 			margin: 0;
 			margin-bottom: 5px;
+
 			&.timeslot--start {
 				margin-right: 5px;
 				width: calc(50% - 5px);
 			}
 		}
+
+		.invalid-hint {
+			color: var(--color-text-maxcontrast);
+		}
 	}
 </style>
diff --git a/apps/workflowengine/src/components/Checks/RequestURL.vue b/apps/workflowengine/src/components/Checks/RequestURL.vue
index 5f337a669bdfc5e5da62dc6dd28c657e94a660f6..f63f7d29114e08cb3b1f2877f764595321aea503 100644
--- a/apps/workflowengine/src/components/Checks/RequestURL.vue
+++ b/apps/workflowengine/src/components/Checks/RequestURL.vue
@@ -137,3 +137,8 @@ export default {
 	}
 }
 </script>
+<style scoped>
+	.multiselect, input[type='text'] {
+		width: 100%;
+	}
+</style>
diff --git a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue
index f06aac2e8f76b901cbbe4b0819de5ec244b59fc0..4e0edb6bf49e2718e2bb8bf97f6dd2b4a1872850 100644
--- a/apps/workflowengine/src/components/Checks/RequestUserAgent.vue
+++ b/apps/workflowengine/src/components/Checks/RequestUserAgent.vue
@@ -27,19 +27,22 @@
 			:placeholder="t('workflowengine', 'Select a user agent')"
 			label="label"
 			track-by="pattern"
-			group-values="children"
-			group-label="label"
 			: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>
+				<!-- 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>
 			<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>
+				<!-- 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>
 		</Multiselect>
 		<input v-if="!isPredefined"
@@ -65,15 +68,10 @@ export default {
 		return {
 			newValue: '',
 			predefinedTypes: [
-				{
-					label: t('workflowengine', 'Sync clients'),
-					children: [
-						{ 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' }
-					]
-				}
+				{ 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' }
 			]
 		}
 	},
@@ -83,8 +81,6 @@ export default {
 		},
 		matchingPredefined() {
 			return this.predefinedTypes
-				.map(groups => groups.children)
-				.flat()
 				.find((type) => this.newValue === type.pattern)
 		},
 		isPredefined() {
@@ -92,14 +88,9 @@ export default {
 		},
 		customValue() {
 			return {
-				label: t('workflowengine', 'Others'),
-				children: [
-					{
-						icon: 'icon-settings-dark',
-						label: t('workflowengine', 'Custom user agent'),
-						pattern: ''
-					}
-				]
+				icon: 'icon-settings-dark',
+				label: t('workflowengine', 'Custom user agent'),
+				pattern: ''
 			}
 		},
 		currentValue() {
@@ -115,8 +106,8 @@ export default {
 	},
 	methods: {
 		validateRegex(string) {
-			var regexRegex = /^\/(.*)\/([gui]{0,3})$/
-			var result = regexRegex.exec(string)
+			const regexRegex = /^\/(.*)\/([gui]{0,3})$/
+			const result = regexRegex.exec(string)
 			return result !== null
 		},
 		setValue(value) {
@@ -133,3 +124,32 @@ export default {
 	}
 }
 </script>
+<style scoped>
+	.multiselect, 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;
+	}
+	.option__icon {
+		display: inline-block;
+		min-width: 30px;
+		background-position: left;
+	}
+	.option__title {
+		white-space: nowrap;
+		overflow: hidden;
+		text-overflow: ellipsis;
+	}
+</style>
diff --git a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue
index 1ab06d9b84d26b56ac3b139d16663e17c1fae133..f254a5185f6e89609e369253342a1643d447670a 100644
--- a/apps/workflowengine/src/components/Checks/RequestUserGroup.vue
+++ b/apps/workflowengine/src/components/Checks/RequestUserGroup.vue
@@ -22,56 +22,91 @@
 
 <template>
 	<div>
-		<Multiselect v-model="newValue"
-			:class="{'icon-loading-small': groups.length === 0}"
+		<Multiselect :value="currentValue"
+			:loading="status.isLoading && groups.length === 0"
 			:options="groups"
 			:multiple="false"
 			label="displayname"
 			track-by="id"
-			@input="setValue" />
+			@search-change="searchAsync"
+			@input="(value) => $emit('input', value.id)" />
 	</div>
 </template>
 
 <script>
 import { Multiselect } from 'nextcloud-vue/dist/Components/Multiselect'
-import valueMixin from '../../mixins/valueMixin'
 import axios from '@nextcloud/axios'
+
+const groups = []
+const status = {
+	isLoading: false
+}
+
 export default {
 	name: 'RequestUserGroup',
 	components: {
 		Multiselect
 	},
-	mixins: [
-		valueMixin
-	],
+	props: {
+		value: {
+			type: String,
+			default: ''
+		},
+		check: {
+			type: Object,
+			default: () => { return {} }
+		}
+	},
 	data() {
 		return {
-			groups: []
+			groups: groups,
+			status: status
 		}
 	},
-	beforeMount() {
-		axios.get(OC.linkToOCS('cloud', 2) + 'groups').then((response) => {
-			this.groups = response.data.ocs.data.groups.reduce((obj, item) => {
-				obj.push({
-					id: item,
-					displayname: item
-				})
-				return obj
-			}, [])
-			this.updateInternalValue(this.value)
-		}, (error) => {
-			console.error('Error while loading group list', error.response)
-		})
+	computed: {
+		currentValue() {
+			return this.groups.find(group => group.id === this.value) || null
+		}
+	},
+	async mounted() {
+		if (this.groups.length === 0) {
+			await this.searchAsync('')
+		}
+		if (this.currentValue === null) {
+			await this.searchAsync(this.value)
+		}
 	},
 	methods: {
-		updateInternalValue() {
-			this.newValue = this.groups.find(group => group.id === this.value) || null
+		searchAsync(searchQuery) {
+			if (this.status.isLoading) {
+				return
+			}
+
+			this.status.isLoading = true
+			return axios.get(OC.linkToOCS('cloud', 2) + 'groups?limit=20&search=' + encodeURI(searchQuery)).then((response) => {
+				response.data.ocs.data.groups.reduce((obj, item) => {
+					obj.push({
+						id: item,
+						displayname: item
+					})
+					return obj
+				}, []).forEach((group) => this.addGroup(group))
+				this.status.isLoading = false
+			}, (error) => {
+				console.error('Error while loading group list', error.response)
+			})
 		},
-		setValue(value) {
-			if (value !== null) {
-				this.$emit('input', this.newValue.id)
+		addGroup(group) {
+			const index = this.groups.findIndex((item) => item.id === group.id)
+			if (index === -1) {
+				this.groups.push(group)
 			}
 		}
 	}
 }
 </script>
+<style scoped>
+	.multiselect {
+		width: 100%;
+	}
+</style>
diff --git a/apps/workflowengine/src/components/Checks/file.js b/apps/workflowengine/src/components/Checks/file.js
index 76f998da0078a0e984050ed4ac10efe415e6bee9..0cc49c2d4c1f226d4b20e37dc04bdf2f74bbf10f 100644
--- a/apps/workflowengine/src/components/Checks/file.js
+++ b/apps/workflowengine/src/components/Checks/file.js
@@ -65,7 +65,7 @@ const FileChecks = [
 			{ operator: 'greater', name: t('workflowengine', 'greater') }
 		],
 		placeholder: (check) => '5 MB',
-		validate: (check) => check.value.match(/^[0-9]+[ ]?[kmgt]?b$/i) !== null
+		validate: (check) => check.value ? check.value.match(/^[0-9]+[ ]?[kmgt]?b$/i) !== null : false
 	},
 
 	{
diff --git a/apps/workflowengine/src/components/Event.vue b/apps/workflowengine/src/components/Event.vue
index a06835f5f8230fcdcf0c732859071bbf036308aa..97608fde062d716e8cb1a6dd9b3c08207d393d42 100644
--- a/apps/workflowengine/src/components/Event.vue
+++ b/apps/workflowengine/src/components/Event.vue
@@ -1,5 +1,5 @@
 <template>
-	<div>
+	<div class="event">
 		<div v-if="operation.isComplex && operation.fixedEntity !== ''" class="isComplex">
 			<img class="option__icon" :src="entity.icon">
 			<span class="option__title option__title_single">{{ operation.triggerHint }}</span>
@@ -7,14 +7,16 @@
 		<Multiselect v-else
 			:value="currentEvent"
 			:options="allEvents"
-			label="eventName"
 			track-by="id"
-			:allow-empty="false"
+			:multiple="true"
+			:auto-limit="false"
 			:disabled="allEvents.length <= 1"
 			@input="updateEvent">
-			<template slot="singleLabel" slot-scope="props">
-				<img class="option__icon" :src="props.option.entity.icon">
-				<span class="option__title option__title_single">{{ props.option.displayName }}</span>
+			<template slot="selection" slot-scope="{ values, search, isOpen }">
+				<div v-if="values.length && !isOpen" class="eventlist">
+					<img class="option__icon" :src="values[0].entity.icon">
+					<span v-for="(value, index) in values" :key="value.id" class="text option__title option__title_single">{{ value.displayName }} <span v-if="index+1 < values.length">, </span></span>
+				</div>
 			</template>
 			<template slot="option" slot-scope="props">
 				<img class="option__icon" :src="props.option.entity.icon">
@@ -49,23 +51,22 @@ export default {
 			return this.$store.getters.getEventsForOperation(this.operation)
 		},
 		currentEvent() {
-			if (!this.rule.events) {
-				return this.allEvents.length > 0 ? this.allEvents[0] : null
-			}
-			return this.allEvents.find(event => event.entity.id === this.rule.entity && this.rule.events.indexOf(event.eventName) !== -1)
+			return this.allEvents.filter(event => event.entity.id === this.rule.entity && this.rule.events.indexOf(event.eventName) !== -1)
 		}
 	},
 	methods: {
-		updateEvent(event) {
-			this.$set(this.rule, 'entity', event.entity.id)
-			this.$set(this.rule, 'events', [event.eventName])
-			this.$store.dispatch('updateRule', this.rule)
+		updateEvent(events) {
+			this.$set(this.rule, 'events', events.map(event => event.eventName))
+			this.$emit('update', this.rule)
 		}
 	}
 }
 </script>
 
 <style scoped lang="scss">
+	.event {
+		margin-bottom: 5px;
+	}
 	.isComplex {
 		img {
 			vertical-align: top;
@@ -78,6 +79,11 @@ export default {
 			display: inline-block;
 		}
 	}
+	.multiselect {
+		width: 100%;
+		max-width: 550px;
+		margin-top: 4px;
+	}
 	.multiselect::v-deep .multiselect__single {
 		display: flex;
 	}
@@ -86,8 +92,10 @@ export default {
 		border: 1px solid transparent;
 	}
 
-	.multiselect::v-deep .multiselect__tags .multiselect__single {
+	.multiselect::v-deep .multiselect__tags {
 		background-color: var(--color-main-background) !important;
+		height: auto;
+		min-height: 34px;
 	}
 
 	.multiselect:not(.multiselect--disabled)::v-deep .multiselect__tags .multiselect__single {
@@ -107,4 +115,9 @@ export default {
 	.option__title_single {
 		font-weight: 900;
 	}
+
+	.eventlist img,
+	.eventlist .text {
+		vertical-align: middle;
+	}
 </style>
diff --git a/apps/workflowengine/src/components/Operation.vue b/apps/workflowengine/src/components/Operation.vue
index ad44d376934b50637dc2fb73551c0a9e90fc27b1..ae0a67ae53d088460a05bf80d4ad40543669f96b 100644
--- a/apps/workflowengine/src/components/Operation.vue
+++ b/apps/workflowengine/src/components/Operation.vue
@@ -55,6 +55,7 @@ export default {
 	.actions__item_options {
 		width: 100%;
 		margin-top: 10px;
+		padding-left: 60px;
 	}
 	h3, small {
 		padding: 6px;
@@ -63,7 +64,7 @@ export default {
 	h3 {
 		margin: 0;
 		padding: 0;
-		font-weight: 500;
+		font-weight: 600;
 	}
 	small {
 		font-size: 10pt;
@@ -82,6 +83,7 @@ export default {
 		.actions__item__description {
 			padding-top: 5px;
 			text-align: left;
+			width: calc(100% - 105px);
 			small {
 				padding: 0;
 			}
diff --git a/apps/workflowengine/src/components/Rule.vue b/apps/workflowengine/src/components/Rule.vue
index 2be9b0fc5e5279104670781fee3978db56d3b512..703b7832afaf4d7d7673bdebb136be192fffc402 100644
--- a/apps/workflowengine/src/components/Rule.vue
+++ b/apps/workflowengine/src/components/Rule.vue
@@ -1,5 +1,5 @@
 <template>
-	<div class="section rule" :style="{ borderLeftColor: operation.color || '' }">
+	<div v-if="operation" class="section rule" :style="{ borderLeftColor: operation.color || '' }">
 		<div class="trigger">
 			<p>
 				<span>{{ t('workflowengine', 'When') }}</span>
@@ -23,28 +23,26 @@
 		</div>
 		<div class="flow-icon icon-confirm" />
 		<div class="action">
-			<div class="buttons">
-				<Actions>
-					<ActionButton v-if="rule.id < -1" icon="icon-close" @click="cancelRule">
-						{{ t('workflowengine', 'Cancel rule creation') }}
-					</ActionButton>
-					<ActionButton v-else icon="icon-close" @click="deleteRule">
-						{{ t('workflowengine', 'Remove rule') }}
-					</ActionButton>
-				</Actions>
-			</div>
 			<Operation :operation="operation" :colored="false">
 				<component :is="operation.options"
 					v-if="operation.options"
 					v-model="rule.operation"
 					@input="updateOperation" />
 			</Operation>
-			<button v-tooltip="ruleStatus.tooltip"
-				class="status-button icon"
-				:class="ruleStatus.class"
-				@click="saveRule">
-				{{ ruleStatus.title }}
-			</button>
+			<div class="buttons">
+				<button v-tooltip="ruleStatus.tooltip"
+					class="status-button icon"
+					:class="ruleStatus.class"
+					@click="saveRule">
+					{{ ruleStatus.title }}
+				</button>
+				<button v-if="rule.id < -1" @click="cancelRule">
+					{{ t('workflowengine', 'Cancel') }}
+				</button>
+				<button v-else @click="deleteRule">
+					{{ t('workflowengine', 'Delete') }}
+				</button>
+			</div>
 		</div>
 	</div>
 </template>
@@ -85,7 +83,7 @@ export default {
 			return this.$store.getters.getOperationForRule(this.rule)
 		},
 		ruleStatus() {
-			if (this.error || !this.rule.valid) {
+			if (this.error || !this.rule.valid || this.rule.checks.some((check) => check.invalid === true)) {
 				return {
 					title: t('workflowengine', 'The configuration is invalid'),
 					class: 'icon-close-white invalid',
@@ -163,11 +161,18 @@ export default {
 		background-position: 10px center;
 	}
 
+	.buttons {
+		display: block;
+		button {
+			float: right;
+			height: 34px;
+		}
+	}
+
 	.status-button {
 		transition: 0.5s ease all;
 		display: block;
-		margin: auto;
-		margin-right: 0;
+		margin: 3px 10px 3px auto;
 	}
 	.status-button.primary {
 		padding-left: 32px;
@@ -199,12 +204,6 @@ export default {
 		.action {
 			max-width: 400px;
 			position: relative;
-			.buttons {
-				position: absolute;
-				right: 0;
-				display: flex;
-				z-index: 1;
-			}
 		}
 		.icon-confirm {
 			background-position: right 27px;
@@ -238,6 +237,7 @@ export default {
 		margin: 0;
 		width: 180px;
 		border-radius: var(--border-radius);
+		color: var(--color-text-maxcontrast);
 		font-weight: normal;
 		text-align: left;
 		font-size: 1em;
diff --git a/apps/workflowengine/src/helpers/api.js b/apps/workflowengine/src/helpers/api.js
index 2b2bb40a7e28a2b74c6e42ecee1d7a731717ad10..76861d3bb35311dc716504b126e9b138790f1365 100644
--- a/apps/workflowengine/src/helpers/api.js
+++ b/apps/workflowengine/src/helpers/api.js
@@ -22,8 +22,9 @@
 
 import { loadState } from '@nextcloud/initial-state'
 
+const scopeValue = loadState('workflowengine', 'scope') === 0 ? 'global' : 'user'
+
 const getApiUrl = (url) => {
-	const scopeValue = loadState('workflowengine', 'scope') === 0 ? 'global' : 'user'
 	return OC.linkToOCS('apps/workflowengine/api/v1/workflows', 2) + scopeValue + url + '?format=json'
 }
 
diff --git a/apps/workflowengine/src/helpers/validators.js b/apps/workflowengine/src/helpers/validators.js
index 68ced4ae6faf8649dc5ba04ab1927285a16e1dd6..d64adfa1385da74ea7314f18c44f6942083fbcc7 100644
--- a/apps/workflowengine/src/helpers/validators.js
+++ b/apps/workflowengine/src/helpers/validators.js
@@ -19,23 +19,29 @@
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
  */
+const regexRegex = /^\/(.*)\/([gui]{0,3})$/
+const regexIPv4 = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|[1-2][0-9]|[1-9])$/
+const regexIPv6 = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(1([01][0-9]|2[0-8])|[1-9][0-9]|[0-9])$/
 
 const validateRegex = function(string) {
-	var regexRegex = /^\/(.*)\/([gui]{0,3})$/
-	var result = regexRegex.exec(string)
-	return result !== null
+	if (!string) {
+		return false
+	}
+	return regexRegex.exec(string) !== null
 }
 
 const validateIPv4 = function(string) {
-	var regexRegex = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\/(3[0-2]|[1-2][0-9]|[1-9])$/
-	var result = regexRegex.exec(string)
-	return result !== null
+	if (!string) {
+		return false
+	}
+	return regexIPv4.exec(string) !== null
 }
 
 const validateIPv6 = function(string) {
-	var regexRegex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))\/(1([01][0-9]|2[0-8])|[1-9][0-9]|[0-9])$/
-	var result = regexRegex.exec(string)
-	return result !== null
+	if (!string) {
+		return false
+	}
+	return regexIPv6.exec(string) !== null
 }
 
 const stringValidator = (check) => {
diff --git a/apps/workflowengine/src/store.js b/apps/workflowengine/src/store.js
index cbd9b29c81c1a63fa53afd7c1074b2998c6979f1..a18540f8035f23c3eb6a74acc346067de2b768da 100644
--- a/apps/workflowengine/src/store.js
+++ b/apps/workflowengine/src/store.js
@@ -71,7 +71,9 @@ const store = new Vuex.Store({
 			plugin = Object.assign(
 				{ color: 'var(--color-primary-element)' },
 				plugin, state.operations[plugin.id] || {})
-			Vue.set(state.operations, plugin.id, plugin)
+			if (typeof state.operations[plugin.id] !== 'undefined') {
+				Vue.set(state.operations, plugin.id, plugin)
+			}
 		}
 	},
 	actions: {
diff --git a/apps/workflowengine/templates/settings.php b/apps/workflowengine/templates/settings.php
index 2b8a825269524063714f37647424c55b103d828d..f306bb9e1f1eae14ad3b0bde0d15e5be87b04674 100644
--- a/apps/workflowengine/templates/settings.php
+++ b/apps/workflowengine/templates/settings.php
@@ -19,7 +19,6 @@
  *
  */
 use OCA\WorkflowEngine\AppInfo\Application;
-style(Application::APP_ID, 'multiselect');
 
 /** @var array $_ */
 /** @var \OCP\IL10N $l */
diff --git a/lib/public/WorkflowEngine/IEntityCheck.php b/lib/public/WorkflowEngine/IEntityCheck.php
index 7a4df0afd5f44403ca6f7d681f4b55ad148a852a..d90535b4c999317dd1ae8d36e3599aaf3d88122b 100644
--- a/lib/public/WorkflowEngine/IEntityCheck.php
+++ b/lib/public/WorkflowEngine/IEntityCheck.php
@@ -24,9 +24,6 @@ declare(strict_types=1);
 
 namespace OCP\WorkflowEngine;
 
-
-use OCP\Files\Storage\IStorage;
-
 /**
  * Interface IFileCheck
  *