fix(editor): Prevent keyboard shortcuts to edit workflows in readonly mode (#6613)

This commit is contained in:
Csaba Tuncsik 2023-07-06 16:01:52 +02:00 committed by GitHub
parent 7515f7d52a
commit 7383e7fd48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 38 deletions

View file

@ -391,7 +391,6 @@ import {
useRootStore, useRootStore,
useWorkflowsEEStore, useWorkflowsEEStore,
useUsersStore, useUsersStore,
useSourceControlStore,
} from '@/stores'; } from '@/stores';
import { createEventBus } from 'n8n-design-system'; import { createEventBus } from 'n8n-design-system';
@ -469,7 +468,6 @@ export default defineComponent({
useSettingsStore, useSettingsStore,
useWorkflowsStore, useWorkflowsStore,
useWorkflowsEEStore, useWorkflowsEEStore,
useSourceControlStore,
), ),
workflowName(): string { workflowName(): string {
return this.workflowsStore.workflowName; return this.workflowsStore.workflowName;
@ -493,9 +491,6 @@ export default defineComponent({
return this.workflowsEEStore.getWorkflowOwnerName(`${this.workflowId}`, fallback); return this.workflowsEEStore.getWorkflowOwnerName(`${this.workflowId}`, fallback);
}, },
readOnlyEnv(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
},
}, },
async mounted() { async mounted() {
this.executionTimeout = this.rootStore.executionTimeout; this.executionTimeout = this.rootStore.executionTimeout;

View file

@ -1,8 +1,9 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { mapStores } from 'pinia';
import dateformat from 'dateformat'; import dateformat from 'dateformat';
import { VIEWS } from '@/constants'; import { VIEWS } from '@/constants';
import { useToast } from '@/composables'; import { useToast } from '@/composables';
import { useSourceControlStore } from '@/stores';
export const genericHelpers = defineComponent({ export const genericHelpers = defineComponent({
setup() { setup() {
@ -17,11 +18,15 @@ export const genericHelpers = defineComponent({
}; };
}, },
computed: { computed: {
...mapStores(useSourceControlStore),
isReadOnlyRoute(): boolean { isReadOnlyRoute(): boolean {
return ![VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW, VIEWS.LOG_STREAMING_SETTINGS].includes( return ![VIEWS.WORKFLOW, VIEWS.NEW_WORKFLOW, VIEWS.LOG_STREAMING_SETTINGS].includes(
this.$route.name as VIEWS, this.$route.name as VIEWS,
); );
}, },
readOnlyEnv(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
},
}, },
methods: { methods: {
displayTimer(msPassed: number, showMs = false): string { displayTimer(msPassed: number, showMs = false): string {
@ -49,21 +54,6 @@ export const genericHelpers = defineComponent({
const [date, time] = formattedDate.split('#'); const [date, time] = formattedDate.split('#');
return { date, time }; return { date, time };
}, },
editAllowedCheck(): boolean {
if (this.isReadOnlyRoute) {
this.showMessage({
// title: 'Workflow can not be changed!',
title: this.$locale.baseText('genericHelpers.showMessage.title'),
message: this.$locale.baseText('genericHelpers.showMessage.message'),
type: 'info',
duration: 0,
dangerouslyUseHTMLString: true,
});
return false;
}
return true;
},
/** /**
* @note Loading helpers extracted as composable in useLoadingService * @note Loading helpers extracted as composable in useLoadingService

View file

@ -102,7 +102,7 @@
@addNode="onAddNode" @addNode="onAddNode"
/> />
<canvas-controls /> <canvas-controls />
<div class="workflow-execute-wrapper" v-if="!isReadOnlyRoute"> <div class="workflow-execute-wrapper" v-if="!isReadOnlyRoute && !readOnlyEnv">
<span <span
@mouseenter="showTriggerMissingToltip(true)" @mouseenter="showTriggerMissingToltip(true)"
@mouseleave="showTriggerMissingToltip(false)" @mouseleave="showTriggerMissingToltip(false)"
@ -149,7 +149,13 @@
/> />
<n8n-icon-button <n8n-icon-button
v-if="!isReadOnlyRoute && workflowExecution && !workflowRunning && !allTriggersDisabled" v-if="
!isReadOnlyRoute &&
!readOnlyEnv &&
workflowExecution &&
!workflowRunning &&
!allTriggersDisabled
"
:title="$locale.baseText('nodeView.deletesTheCurrentExecutionData')" :title="$locale.baseText('nodeView.deletesTheCurrentExecutionData')"
icon="trash" icon="trash"
size="large" size="large"
@ -281,7 +287,6 @@ import {
useSettingsStore, useSettingsStore,
useUIStore, useUIStore,
useHistoryStore, useHistoryStore,
useSourceControlStore,
} from '@/stores'; } from '@/stores';
import * as NodeViewUtils from '@/utils/nodeViewUtils'; import * as NodeViewUtils from '@/utils/nodeViewUtils';
import { getAccountAge, getConnectionInfo, getNodeViewTab } from '@/utils'; import { getAccountAge, getConnectionInfo, getNodeViewTab } from '@/utils';
@ -483,11 +488,7 @@ export default defineComponent({
useEnvironmentsStore, useEnvironmentsStore,
useWorkflowsEEStore, useWorkflowsEEStore,
useHistoryStore, useHistoryStore,
useSourceControlStore,
), ),
readOnlyEnv(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
},
nativelyNumberSuffixedDefaults(): string[] { nativelyNumberSuffixedDefaults(): string[] {
return this.rootStore.nativelyNumberSuffixedDefaults; return this.rootStore.nativelyNumberSuffixedDefaults;
}, },
@ -635,6 +636,21 @@ export default defineComponent({
this.unregisterCustomAction('showNodeCreator'); this.unregisterCustomAction('showNodeCreator');
}, },
methods: { methods: {
editAllowedCheck(): boolean {
if (this.isReadOnlyRoute || this.readOnlyEnv) {
this.showMessage({
// title: 'Workflow can not be changed!',
title: this.$locale.baseText('genericHelpers.showMessage.title'),
message: this.$locale.baseText('genericHelpers.showMessage.message'),
type: 'info',
duration: 0,
dangerouslyUseHTMLString: true,
});
return false;
}
return true;
},
showTriggerMissingToltip(isVisible: boolean) { showTriggerMissingToltip(isVisible: boolean) {
this.showTriggerMissingTooltip = isVisible; this.showTriggerMissingTooltip = isVisible;
}, },
@ -961,7 +977,7 @@ export default defineComponent({
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
if (this.isReadOnlyRoute) { if (this.isReadOnlyRoute || this.readOnlyEnv) {
return; return;
} }
@ -1015,13 +1031,13 @@ export default defineComponent({
} else if (e.key === 'Tab') { } else if (e.key === 'Tab') {
this.onToggleNodeCreator({ this.onToggleNodeCreator({
source: NODE_CREATOR_OPEN_SOURCES.TAB, source: NODE_CREATOR_OPEN_SOURCES.TAB,
createNodeActive: !this.createNodeActive && !this.isReadOnlyRoute, createNodeActive: !this.createNodeActive && !this.isReadOnlyRoute && !this.readOnlyEnv,
}); });
} else if (e.key === this.controlKeyCode) { } else if (e.key === this.controlKeyCode) {
this.ctrlKeyPressed = true; this.ctrlKeyPressed = true;
} else if (e.key === ' ') { } else if (e.key === ' ') {
this.moveCanvasKeyPressed = true; this.moveCanvasKeyPressed = true;
} else if (e.key === 'F2' && !this.isReadOnlyRoute) { } else if (e.key === 'F2' && !this.isReadOnlyRoute && !this.readOnlyEnv) {
const lastSelectedNode = this.lastSelectedNode; const lastSelectedNode = this.lastSelectedNode;
if (lastSelectedNode !== null && lastSelectedNode.type !== STICKY_NODE_TYPE) { if (lastSelectedNode !== null && lastSelectedNode.type !== STICKY_NODE_TYPE) {
void this.callDebounced( void this.callDebounced(
@ -1067,7 +1083,10 @@ export default defineComponent({
const lastSelectedNode = this.lastSelectedNode; const lastSelectedNode = this.lastSelectedNode;
if (lastSelectedNode !== null) { if (lastSelectedNode !== null) {
if (lastSelectedNode.type === STICKY_NODE_TYPE && this.isReadOnlyRoute) { if (
lastSelectedNode.type === STICKY_NODE_TYPE &&
(this.isReadOnlyRoute || this.readOnlyEnv)
) {
return; return;
} }
this.ndvStore.activeNodeName = lastSelectedNode.name; this.ndvStore.activeNodeName = lastSelectedNode.name;
@ -1307,7 +1326,7 @@ export default defineComponent({
}, },
cutSelectedNodes() { cutSelectedNodes() {
const deleteCopiedNodes = !this.isReadOnlyRoute; const deleteCopiedNodes = !this.isReadOnlyRoute && !this.readOnlyEnv;
this.copySelectedNodes(deleteCopiedNodes); this.copySelectedNodes(deleteCopiedNodes);
if (deleteCopiedNodes) { if (deleteCopiedNodes) {
this.deleteSelectedNodes(); this.deleteSelectedNodes();
@ -2162,7 +2181,7 @@ export default defineComponent({
if (!this.suspendRecordingDetachedConnections) { if (!this.suspendRecordingDetachedConnections) {
this.historyStore.pushCommandToUndo(new AddConnectionCommand(connectionData)); this.historyStore.pushCommandToUndo(new AddConnectionCommand(connectionData));
} }
if (!this.isReadOnlyRoute) { if (!this.isReadOnlyRoute && !this.readOnlyEnv) {
NodeViewUtils.addConnectionActionsOverlay( NodeViewUtils.addConnectionActionsOverlay(
info.connection, info.connection,
() => { () => {
@ -2609,7 +2628,7 @@ export default defineComponent({
// Create connections in DOM // Create connections in DOM
this.instance?.connect({ this.instance?.connect({
uuids: uuid, uuids: uuid,
detachable: !this.isReadOnlyRoute, detachable: !this.isReadOnlyRoute && !this.readOnlyEnv,
}); });
setTimeout(() => { setTimeout(() => {

View file

@ -112,6 +112,7 @@ import { useUsersStore } from '@/stores/users.store';
import { useWorkflowsStore } from '@/stores/workflows.store'; import { useWorkflowsStore } from '@/stores/workflows.store';
import { useCredentialsStore } from '@/stores/credentials.store'; import { useCredentialsStore } from '@/stores/credentials.store';
import { useSourceControlStore } from '@/stores/sourceControl.store'; import { useSourceControlStore } from '@/stores/sourceControl.store';
import { genericHelpers } from '@/mixins/genericHelpers';
type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void }; type IResourcesListLayoutInstance = Vue & { sendFiltersTelemetry: (source: string) => void };
@ -123,6 +124,7 @@ const StatusFilter = {
const WorkflowsView = defineComponent({ const WorkflowsView = defineComponent({
name: 'WorkflowsView', name: 'WorkflowsView',
mixins: [genericHelpers],
components: { components: {
ResourcesListLayout, ResourcesListLayout,
WorkflowCard, WorkflowCard,
@ -174,9 +176,6 @@ const WorkflowsView = defineComponent({
}, },
]; ];
}, },
readOnlyEnv(): boolean {
return this.sourceControlStore.preferences.branchReadOnly;
},
}, },
methods: { methods: {
addWorkflow() { addWorkflow() {