From 610abc68c8ba0ff9e589295e8646960951b2dc2e Mon Sep 17 00:00:00 2001 From: Stanislav Vishnevetskiy Date: Mon, 15 Oct 2018 14:55:06 +0300 Subject: [PATCH] composition readonly implementation Issue-ID: SDC-1591 Change-Id: I4a009fe28fb32d9c2407d69087ab9c17f8ac5b2d Signed-off-by: Stanislav Vishnevetskiy --- .../src/main/frontend/package.json | 1 + .../resources/scss/features/_composition.scss | 3 + .../features/version/composition/Composition.js | 4 +- .../version/composition/CompositionView.js | 69 ++++++++++- .../composition/components/CompositionButton.js | 9 +- .../components/CompositionButtonsPanel.js | 8 +- .../version/composition/compositionConstants.js | 6 + .../src/features/version/composition/readOnly.js | 138 +++++++++++++++++++++ workflow-designer-ui/src/main/frontend/yarn.lock | 4 + 9 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 workflow-designer-ui/src/main/frontend/src/features/version/composition/readOnly.js diff --git a/workflow-designer-ui/src/main/frontend/package.json b/workflow-designer-ui/src/main/frontend/package.json index 62b06cbe..31d9ed01 100644 --- a/workflow-designer-ui/src/main/frontend/package.json +++ b/workflow-designer-ui/src/main/frontend/package.json @@ -26,6 +26,7 @@ "http-proxy-middleware": "^0.17.4", "inherits": "^2.0.3", "lodash.assign": "^4.2.0", + "lodash.foreach": "^4.5.0", "lodash.isempty": "^4.4.0", "lodash.isequal": "^4.5.0", "lodash.map": "^4.6.0", diff --git a/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss b/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss index 6554219c..f4421770 100644 --- a/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss +++ b/workflow-designer-ui/src/main/frontend/resources/scss/features/_composition.scss @@ -55,6 +55,9 @@ fill: $blue; cursor: pointer; } + &.disabled { + fill: $gray; + } .svg-icon { width: 25px; height: 23px; diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js index c9fc4b55..b91cedaa 100644 --- a/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js +++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/Composition.js @@ -23,6 +23,7 @@ import { getWorkflowName } from '../../workflow/workflowSelectors'; import { activitiesSelector } from 'features/activities/activitiesSelectors'; import { getInputOutputForComposition } from 'features/version/inputOutput/inputOutputSelectors'; import { getVersionInfo } from 'features/version/general/generalSelectors'; +import { getIsCertified } from 'features/version/general/generalSelectors'; function mapStateToProps(state) { return { composition: getComposition(state), @@ -30,7 +31,8 @@ function mapStateToProps(state) { versionName: getVersionInfo(state).name, activities: activitiesSelector(state), inputOutput: getInputOutputForComposition(state), - errors: getErrors(state) + errors: getErrors(state), + isReadOnly: getIsCertified(state) }; } diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js index eac30837..3ec0cb2e 100644 --- a/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js +++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/CompositionView.js @@ -28,9 +28,37 @@ import { I18n } from 'react-redux-i18n'; import { PROCESS_DEFAULT_ID, COMPOSITION_ERROR_COLOR, - COMPOSITION_VALID_COLOR + COMPOSITION_VALID_COLOR, + CAMUNDA_PANEL_INPUTS_NAMES } from './compositionConstants'; +import readOnly from './readOnly'; +function setStatusToElement(type, status, parent) { + let elements = parent.getElementsByTagName(type); + for (let item of elements) { + if (item.name !== 'selectedExtensionElement') { + item.readOnly = status; + item.disabled = status; + } + } +} + +function disablePanelInputs(status) { + let panel = document.getElementById('js-properties-panel'); + + if (panel) { + setStatusToElement('input', status, panel); + setStatusToElement('button', status, panel); + setStatusToElement('select', status, panel); + + CAMUNDA_PANEL_INPUTS_NAMES.map(name => { + const div = document.getElementById(name); + if (div) { + div.setAttribute('contenteditable', !status); + } + }); + } +} class CompositionView extends Component { static propTypes = { compositionUpdate: PropTypes.func, @@ -41,7 +69,8 @@ class CompositionView extends Component { inputOutput: PropTypes.object, activities: PropTypes.array, validationUpdate: PropTypes.func, - errors: PropTypes.array + errors: PropTypes.array, + isReadOnly: PropTypes.bool }; constructor() { @@ -53,9 +82,10 @@ class CompositionView extends Component { this.state = { diagram: false }; + this.versionChanged = false; } componentDidUpdate(prevProps) { - const { errors } = this.props; + const { errors, isReadOnly, versionName, composition } = this.props; if (!isEqual(prevProps.errors, errors)) { errors.map(item => { this.modeling.setColor([item.element], { @@ -65,22 +95,43 @@ class CompositionView extends Component { }); }); } + if (prevProps.isReadOnly !== isReadOnly) { + this.modeler.get('readOnly').readOnly(isReadOnly); + disablePanelInputs(isReadOnly); + } + + if (prevProps.versionName !== versionName) { + this.versionChanged = true; + } + if ( + !isEqual(prevProps.composition, composition) && + this.versionChanged + ) { + this.setDiagramToBPMN(composition); + this.versionChanged = false; + } } componentDidMount() { const { composition, activities, inputOutput, - validationUpdate + validationUpdate, + isReadOnly } = this.props; + const readOnlyModule = { + __init__: ['readOnly'], + readOnly: ['type', readOnly] + }; this.modeler = new CustomModeler({ propertiesPanel: { parent: '#js-properties-panel' }, additionalModules: [ propertiesPanelModule, - propertiesProviderModule + propertiesProviderModule, + readOnlyModule ], moddleExtensions: { camunda: camundaModuleDescriptor @@ -96,10 +147,14 @@ class CompositionView extends Component { this.modeler.attachTo('#' + this.generatedId); this.setDiagramToBPMN(composition ? composition : newDiagramXML); this.modeler.on('element.out', () => this.exportDiagramToStore()); + this.modeler.on('element.click', this.handleCompositionStatus); this.bpmnContainer.current.click(); this.modeling = this.modeler.get('modeling'); + this.modeler.get('readOnly').readOnly(isReadOnly); } - + handleCompositionStatus = () => { + disablePanelInputs(this.props.isReadOnly); + }; getActivityInputsOutputs = selectedValue => { const selectedActivity = this.props.activities.find( el => el.name === selectedValue @@ -132,6 +187,7 @@ class CompositionView extends Component { this.props.inputOutput, this.modeler.get('moddle') ); + disablePanelInputs(this.props.isReadOnly); }); }; setDefaultIdAndName = businessObject => { @@ -213,6 +269,7 @@ class CompositionView extends Component { id="js-properties-panel" /> ( -
+const CompositionButton = ({ onClick, name, title, disabled }) => ( +
{} : onClick} + className={`diagram-btn ${disabled ? 'disabled' : ''}`}>
); @@ -27,7 +29,8 @@ CompositionButton.propTypes = { onClick: PropTypes.func, className: PropTypes.string, name: PropTypes.string, - title: PropTypes.string + title: PropTypes.string, + disabled: PropTypes.bool }; export default CompositionButton; diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js index add64902..0292fd4e 100644 --- a/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js +++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/components/CompositionButtonsPanel.js @@ -19,14 +19,16 @@ import CompositionButton from './CompositionButton'; const Divider = () =>
; -const CompositionButtons = ({ onClean, onUpload, onDownload }) => ( +const CompositionButtons = ({ onClean, onUpload, onDownload, isReadOnly }) => (
+ ( /> ( CompositionButtons.propTypes = { onClean: PropTypes.func, onUpload: PropTypes.func, - onDownload: PropTypes.func + onDownload: PropTypes.func, + isReadOnly: PropTypes.bool }; export default CompositionButtons; diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js index 79c7e8e6..7aaa656e 100644 --- a/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js +++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/compositionConstants.js @@ -27,3 +27,9 @@ export const PROCESS_DEFAULT_ID = 'Process_1'; export const COMPOSITION_ERROR_COLOR = '#f0c2c2'; export const COMPOSITION_VALID_COLOR = 'white'; + +export const CAMUNDA_PANEL_INPUTS_NAMES = [ + 'camunda-parameterType-text', + 'camunda-documentation', + 'camunda-name' +]; diff --git a/workflow-designer-ui/src/main/frontend/src/features/version/composition/readOnly.js b/workflow-designer-ui/src/main/frontend/src/features/version/composition/readOnly.js new file mode 100644 index 00000000..828244b6 --- /dev/null +++ b/workflow-designer-ui/src/main/frontend/src/features/version/composition/readOnly.js @@ -0,0 +1,138 @@ +import forEach from 'lodash.foreach'; + +const HIGH_PRIORITY = 10001; + +function ReadOnly( + eventBus, + contextPad, + dragging, + directEditing, + editorActions, + modeling, + palette, + paletteProvider +) { + this._readOnly = false; + this._eventBus = eventBus; + + let self = this; + eventBus.on('readOnly.changed', HIGH_PRIORITY, function(e) { + self._readOnly = e.readOnly; + + if (e.readOnly) { + directEditing.cancel(); + contextPad.close(); + dragging.cancel(); + } + + palette._update(); + }); + + function intercept(obj, fnName, cb) { + var fn = obj[fnName]; + obj[fnName] = function() { + return cb.call(this, fn, arguments); + }; + } + + function ignoreWhenReadOnly(obj, fnName) { + intercept(obj, fnName, function(fn, args) { + if (self._readOnly) { + return; + } + + return fn.apply(this, args); + }); + } + + function throwIfReadOnly(obj, fnName) { + intercept(obj, fnName, function(fn, args) { + if (self._readOnly) { + throw new Error('model is read-only'); + } + + return fn.apply(this, args); + }); + } + + ignoreWhenReadOnly(contextPad, 'open'); + + ignoreWhenReadOnly(dragging, 'init'); + + ignoreWhenReadOnly(directEditing, 'activate'); + + ignoreWhenReadOnly(editorActions._actions, 'undo'); + ignoreWhenReadOnly(editorActions._actions, 'redo'); + ignoreWhenReadOnly(editorActions._actions, 'copy'); + ignoreWhenReadOnly(editorActions._actions, 'paste'); + ignoreWhenReadOnly(editorActions._actions, 'removeSelection'); + // BpmnEditorActions + ignoreWhenReadOnly(editorActions._actions, 'spaceTool'); + ignoreWhenReadOnly(editorActions._actions, 'lassoTool'); + ignoreWhenReadOnly(editorActions._actions, 'globalConnectTool'); + ignoreWhenReadOnly(editorActions._actions, 'distributeElements'); + ignoreWhenReadOnly(editorActions._actions, 'alignElements'); + ignoreWhenReadOnly(editorActions._actions, 'directEditing'); + + throwIfReadOnly(modeling, 'moveShape'); + throwIfReadOnly(modeling, 'updateAttachment'); + throwIfReadOnly(modeling, 'moveElements'); + throwIfReadOnly(modeling, 'moveConnection'); + throwIfReadOnly(modeling, 'layoutConnection'); + throwIfReadOnly(modeling, 'createConnection'); + throwIfReadOnly(modeling, 'createShape'); + throwIfReadOnly(modeling, 'createLabel'); + throwIfReadOnly(modeling, 'appendShape'); + throwIfReadOnly(modeling, 'removeElements'); + throwIfReadOnly(modeling, 'distributeElements'); + throwIfReadOnly(modeling, 'removeShape'); + throwIfReadOnly(modeling, 'removeConnection'); + throwIfReadOnly(modeling, 'replaceShape'); + throwIfReadOnly(modeling, 'pasteElements'); + throwIfReadOnly(modeling, 'alignElements'); + throwIfReadOnly(modeling, 'resizeShape'); + throwIfReadOnly(modeling, 'createSpace'); + throwIfReadOnly(modeling, 'updateWaypoints'); + throwIfReadOnly(modeling, 'reconnectStart'); + throwIfReadOnly(modeling, 'reconnectEnd'); + + intercept(paletteProvider, 'getPaletteEntries', function(fn, args) { + var entries = fn.apply(this, args); + if (self._readOnly) { + let allowedEntries = ['hand-tool']; + + forEach(entries, function(value, key) { + if (allowedEntries.indexOf(key) === -1) { + delete entries[key]; + } + }); + } + return entries; + }); +} + +ReadOnly.$inject = [ + 'eventBus', + 'contextPad', + 'dragging', + 'directEditing', + 'editorActions', + 'modeling', + 'palette', + 'paletteProvider' +]; + +module.exports = ReadOnly; + +ReadOnly.prototype.readOnly = function(readOnly) { + var newValue = !!readOnly, + oldValue = !!this._readOnly; + + if (readOnly === undefined || newValue === oldValue) { + return oldValue; + } + + this._readOnly = newValue; + this._eventBus.fire('readOnly.changed', { readOnly: newValue }); + return newValue; +}; diff --git a/workflow-designer-ui/src/main/frontend/yarn.lock b/workflow-designer-ui/src/main/frontend/yarn.lock index 4235c3d0..c4287580 100644 --- a/workflow-designer-ui/src/main/frontend/yarn.lock +++ b/workflow-designer-ui/src/main/frontend/yarn.lock @@ -7020,6 +7020,10 @@ lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" +lodash.foreach@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" -- 2.16.6