From bb3d5aa5a10e1974cd2cc8e5bc51170ed23b155f Mon Sep 17 00:00:00 2001 From: gaetanbrl Date: Wed, 28 Aug 2024 16:40:39 +0200 Subject: [PATCH 1/3] disabled no editable field --- .../data/featuregrid/editors/AutocompleteEditor.jsx | 3 ++- .../data/featuregrid/editors/DateTimeEditor.jsx | 3 ++- .../data/featuregrid/editors/DropDownEditor.jsx | 3 ++- .../components/data/featuregrid/editors/FormatEditor.jsx | 4 +++- .../components/data/featuregrid/editors/NumberEditor.jsx | 4 +++- .../components/data/featuregrid/enhancers/editor.js | 8 +++++--- .../data/featuregrid/renderers/CellRenderer.jsx | 1 + web/client/components/misc/AutocompleteCombobox.jsx | 9 ++++++--- .../components/misc/combobox/ControlledCombobox.jsx | 3 ++- web/client/utils/FeatureGridUtils.js | 5 ++++- 10 files changed, 30 insertions(+), 13 deletions(-) diff --git a/web/client/components/data/featuregrid/editors/AutocompleteEditor.jsx b/web/client/components/data/featuregrid/editors/AutocompleteEditor.jsx index 73958e2133..dac0f942ae 100644 --- a/web/client/components/data/featuregrid/editors/AutocompleteEditor.jsx +++ b/web/client/components/data/featuregrid/editors/AutocompleteEditor.jsx @@ -31,7 +31,8 @@ class AutocompleteEditor extends AttributeEditor { autocompleteStreamFactory: PropTypes.func, url: PropTypes.string, typeName: PropTypes.string, - value: PropTypes.string + value: PropTypes.string, + disabled: PropTypes.bool }; static defaultProps = { isValid: () => true, diff --git a/web/client/components/data/featuregrid/editors/DateTimeEditor.jsx b/web/client/components/data/featuregrid/editors/DateTimeEditor.jsx index 7e15feb058..6388535316 100644 --- a/web/client/components/data/featuregrid/editors/DateTimeEditor.jsx +++ b/web/client/components/data/featuregrid/editors/DateTimeEditor.jsx @@ -35,7 +35,8 @@ class DateTimeEditor extends AttributeEditor { calendar: PropTypes.bool, time: PropTypes.bool, onChange: PropTypes.func, - onBlur: PropTypes.func + onBlur: PropTypes.func, + disabled: PropTypes.bool }; static contextTypes = { diff --git a/web/client/components/data/featuregrid/editors/DropDownEditor.jsx b/web/client/components/data/featuregrid/editors/DropDownEditor.jsx index 2dbc8f484c..3f0bcc83f4 100644 --- a/web/client/components/data/featuregrid/editors/DropDownEditor.jsx +++ b/web/client/components/data/featuregrid/editors/DropDownEditor.jsx @@ -40,7 +40,8 @@ class DropDownEditor extends AttributeEditor { filter: PropTypes.string, values: PropTypes.array, labels: PropTypes.array, - emptyValue: PropTypes.string + emptyValue: PropTypes.string, + disabled: PropTypes.bool }; static defaultProps = { isValid: () => true, diff --git a/web/client/components/data/featuregrid/editors/FormatEditor.jsx b/web/client/components/data/featuregrid/editors/FormatEditor.jsx index c83d819368..47f11228f8 100644 --- a/web/client/components/data/featuregrid/editors/FormatEditor.jsx +++ b/web/client/components/data/featuregrid/editors/FormatEditor.jsx @@ -23,7 +23,8 @@ export default class FormatEditor extends React.Component { dataType: PropTypes.string, column: PropTypes.object, formatRegex: PropTypes.string, - onTemporaryChanges: PropTypes.func + onTemporaryChanges: PropTypes.func, + disabled: PropTypes.bool }; static defaultProps = { @@ -61,6 +62,7 @@ export default class FormatEditor extends React.Component { render() { return ( { + getEditor: (desc, field) => { const generalProps = { onTemporaryChanges: props.gridEvents && props.gridEvents.onTemporaryChanges, autocompleteEnabled: props.autocompleteEnabled, url: props.url, - typeName: props.typeName + typeName: props.typeName, + disabled: field && field?.editable === false }; const regexProps = {attribute: desc.name, url: props.url, typeName: props.typeName}; const rules = props.customEditorsOptions && props.customEditorsOptions.rules || []; @@ -169,7 +170,8 @@ const featuresToGrid = compose( if (!isNil(editor)) { return editor; } - return props.editors(desc.localType, generalProps); + const typeEditor = props.editors(desc.localType, generalProps); + return typeEditor; }, getFilterRenderer: getFilterRendererFunc, getFormatter: (desc) => getFormatter(desc, (props.fields ?? []).find(f => f.name === desc.name)), diff --git a/web/client/components/data/featuregrid/renderers/CellRenderer.jsx b/web/client/components/data/featuregrid/renderers/CellRenderer.jsx index d8157f745c..d517942656 100644 --- a/web/client/components/data/featuregrid/renderers/CellRenderer.jsx +++ b/web/client/components/data/featuregrid/renderers/CellRenderer.jsx @@ -28,6 +28,7 @@ class CellRenderer extends React.Component { const isValid = isProperty ? this.context.isValid(this.props.rowData.get(this.props.column.key), this.props.column.key) : true; const className = (isModified ? ['modified'] : []) .concat(isValid ? [] : ['invalid']).join(" "); + console.log(this.props); return ; } } diff --git a/web/client/components/misc/AutocompleteCombobox.jsx b/web/client/components/misc/AutocompleteCombobox.jsx index d4d8fb4b13..eeaefc23bc 100644 --- a/web/client/components/misc/AutocompleteCombobox.jsx +++ b/web/client/components/misc/AutocompleteCombobox.jsx @@ -38,7 +38,8 @@ const streamEnhancer = mapPropsStream(props$ => { busy: data.busy, dropUp: props.dropUp, attribute: props.column && props.column.key, - changeAttribute: props.changeAttribute + changeAttribute: props.changeAttribute, + disabled: props.disabled })); }); @@ -46,13 +47,13 @@ const streamEnhancer = mapPropsStream(props$ => { const PagedComboboxEnhanced = streamEnhancer( ({ open, toggle, select, focus, change, value, valuesCount, loadNextPage, loadPrevPage, maxFeatures, currentPage, - busy, data, loading = false, dropUp = false, attribute, changeAttribute}) => { + busy, data, loading = false, dropUp = false, attribute, changeAttribute, disabled = false}) => { const numberOfPages = Math.ceil(valuesCount / maxFeatures); return (); + selectedValue={value} loading={loading} disabled={disabled}/>); }); // state enhancer for local props @@ -60,8 +61,10 @@ const addStateHandlers = compose( withStateHandlers((props) => ({ delayDebounce: 0, performFetch: false, + disabled: false, open: false, openOnFocus: props.openOnFocus, + disabled: props.disabled, currentPage: 1, maxFeatures: 5, url: props.url, diff --git a/web/client/components/misc/combobox/ControlledCombobox.jsx b/web/client/components/misc/combobox/ControlledCombobox.jsx index e83ca8727e..61e05a30ce 100644 --- a/web/client/components/misc/combobox/ControlledCombobox.jsx +++ b/web/client/components/misc/combobox/ControlledCombobox.jsx @@ -13,7 +13,7 @@ import PagedCombobox from './PagedCombobox'; // state enhancer for local props import addState from './addState'; -const ControlledCombobox = addState(({ toggle, select, focus, change, value, busy, data, loading = false, filter, open }) => { +const ControlledCombobox = addState(({ toggle, select, focus, change, value, busy, data, loading = false, filter, open, disabled }) => { return (); }); diff --git a/web/client/utils/FeatureGridUtils.js b/web/client/utils/FeatureGridUtils.js index b5406acd93..0af95d50ec 100644 --- a/web/client/utils/FeatureGridUtils.js +++ b/web/client/utils/FeatureGridUtils.js @@ -134,6 +134,9 @@ export const featureTypeToGridColumns = ( getAttributeFields(describe).filter(e => !(columnSettings[e.name] && columnSettings[e.name].hide)).map((desc) => { const option = options.find(o => o.name === desc.name); const field = fields.find(f => f.name === desc.name); + if(field && field.name === "id_emprise") { + field.editable = false; + } let columnProp = { sortable, key: desc.name, @@ -144,7 +147,7 @@ export const featureTypeToGridColumns = ( headerRenderer: getHeaderRenderer(), showTitleTooltip: !!option?.description, resizable, - editable, + editable: field.editable === false ? false : editable, filterable, editor: getEditor(desc, field), formatter: getFormatter(desc, field), From 6d110a8575fd054fe4fa3fce806baf75ac6776d6 Mon Sep 17 00:00:00 2001 From: gaetanbrl Date: Thu, 29 Aug 2024 17:20:56 +0200 Subject: [PATCH 2/3] force set value from field param, linter --- web/client/actions/featuregrid.js | 8 ++++++++ .../featuregrid/renderers/CellRenderer.jsx | 1 - .../components/misc/AutocompleteCombobox.jsx | 1 - web/client/epics/featuregrid.js | 17 +++++++++++++++-- web/client/plugins/featuregrid/gridEvents.js | 6 +++--- .../plugins/tocitemssettings/tabs/Fields.jsx | 18 ++++++++++++++++-- web/client/utils/FeatureGridUtils.js | 5 +---- 7 files changed, 43 insertions(+), 13 deletions(-) diff --git a/web/client/actions/featuregrid.js b/web/client/actions/featuregrid.js index ec62c14b6c..6eb7bddcd2 100644 --- a/web/client/actions/featuregrid.js +++ b/web/client/actions/featuregrid.js @@ -65,6 +65,7 @@ export const LOAD_MORE_FEATURES = "LOAD_MORE_FEATURES"; export const GRID_QUERY_RESULT = 'FEATUREGRID:QUERY_RESULT'; export const SET_TIME_SYNC = "FEATUREGRID:SET_TIME_SYNC"; export const SET_PAGINATION = "FEATUREGRID:SET_PAGINATION"; +export const GRID_ROW_UPDATE = "FEATUREGRID:GRID_ROW_UPDATE"; export function toggleShowAgain() { return { @@ -262,6 +263,13 @@ export function featureModified(features, updated) { updated }; } +export function gridRowUpdate(features, updated) { + return { + type: GRID_ROW_UPDATE, + features, + updated + }; +} export function createNewFeatures(features) { return { type: CREATE_NEW_FEATURE, diff --git a/web/client/components/data/featuregrid/renderers/CellRenderer.jsx b/web/client/components/data/featuregrid/renderers/CellRenderer.jsx index d517942656..d8157f745c 100644 --- a/web/client/components/data/featuregrid/renderers/CellRenderer.jsx +++ b/web/client/components/data/featuregrid/renderers/CellRenderer.jsx @@ -28,7 +28,6 @@ class CellRenderer extends React.Component { const isValid = isProperty ? this.context.isValid(this.props.rowData.get(this.props.column.key), this.props.column.key) : true; const className = (isModified ? ['modified'] : []) .concat(isValid ? [] : ['invalid']).join(" "); - console.log(this.props); return ; } } diff --git a/web/client/components/misc/AutocompleteCombobox.jsx b/web/client/components/misc/AutocompleteCombobox.jsx index eeaefc23bc..d6fa9608f9 100644 --- a/web/client/components/misc/AutocompleteCombobox.jsx +++ b/web/client/components/misc/AutocompleteCombobox.jsx @@ -61,7 +61,6 @@ const addStateHandlers = compose( withStateHandlers((props) => ({ delayDebounce: 0, performFetch: false, - disabled: false, open: false, openOnFocus: props.openOnFocus, disabled: props.disabled, diff --git a/web/client/epics/featuregrid.js b/web/client/epics/featuregrid.js index 16b9a27a94..f7b0f79483 100644 --- a/web/client/epics/featuregrid.js +++ b/web/client/epics/featuregrid.js @@ -110,7 +110,9 @@ import { setPagination, launchUpdateFilterFunc, LAUNCH_UPDATE_FILTER_FUNC, SET_LAYER, - SET_VIEWPORT_FILTER, setViewportFilter + SET_VIEWPORT_FILTER, setViewportFilter, + GRID_ROW_UPDATE, + featureModified } from '../actions/featuregrid'; import { @@ -144,7 +146,8 @@ import { getAttributeFilters, selectedLayerSelector, multiSelect, - paginationSelector, isViewportFilterActive, viewportFilter + paginationSelector, isViewportFilterActive, viewportFilter, + getLayerById } from '../selectors/featuregrid'; import { error, warning } from '../actions/notifications'; @@ -902,6 +905,16 @@ export const onFeatureGridCreateNewFeature = (action$) => action$.ofType(CREATE_ .switchMap( () => { return Rx.Observable.of(drawSupportReset()); }); + +export const handleFeatureChanges = (action$, store) => action$.ofType(CREATE_NEW_FEATURE, GRID_ROW_UPDATE) + .flatMap(({features, updated}) => { + const layer = getLayerById(store.getState(), selectedLayerIdSelector(store.getState())); + // force value from fields + layer.fields.filter(f => f.value && f.source).forEach(f => { + updated[f.name] = f.value; + }); + return Rx.Observable.of(featureModified(features, updated)); + }); /** * control highlight support on view mode. * @memberof epics.featuregrid diff --git a/web/client/plugins/featuregrid/gridEvents.js b/web/client/plugins/featuregrid/gridEvents.js index b4ae578953..0d5a9b3f2c 100644 --- a/web/client/plugins/featuregrid/gridEvents.js +++ b/web/client/plugins/featuregrid/gridEvents.js @@ -2,9 +2,9 @@ import { sort, selectFeatures, deselectFeatures, - featureModified, updateFilter, - activateTemporaryChanges + activateTemporaryChanges, + gridRowUpdate } from '../../actions/featuregrid'; const range = (start, end) => Array.from({length: (end + 1 - start)}, (v, k) => k + start); @@ -17,7 +17,7 @@ export default { let features = range(fromRow, toRow).map(r => rowGetter(r)).filter(f => Object.keys(updated || {}).filter(k => f.properties[k] !== updated[k]).length > 0 ); - return featureModified(features, updated); + return gridRowUpdate(features, updated); }, onRowsToggled: (rows, rowGetter) => selectFeatures(rows.map(r => rowGetter(r.rowIdx)), false), onRowsSelected: (rows, rowGetter) => selectFeatures(rows.map(r => rowGetter(r.rowIdx)), true), diff --git a/web/client/plugins/tocitemssettings/tabs/Fields.jsx b/web/client/plugins/tocitemssettings/tabs/Fields.jsx index 0a7c02ef95..1e4ecf578b 100644 --- a/web/client/plugins/tocitemssettings/tabs/Fields.jsx +++ b/web/client/plugins/tocitemssettings/tabs/Fields.jsx @@ -5,18 +5,32 @@ import LayerFields, {hasFields} from '../../../components/TOC/fragments/LayerFie import {connect} from 'react-redux'; import { createStructuredSelector } from 'reselect'; import { currentLocaleSelector } from '../../../selectors/locale'; +import { userSelector } from '../../../selectors/security'; +import { find, get } from 'lodash'; export {hasFields}; export default connect(createStructuredSelector({ - currentLocale: currentLocaleSelector + currentLocale: currentLocaleSelector, + userInfos: userSelector }), { updateLayerProperties: changeLayerProperties } -)(({element = {}, updateLayerProperties = () => {}, ...props}) => { +)(({element = {}, updateLayerProperties = () => {}, userInfos, ...props}) => { const layer = element; const updateFields = (fields) => { + // insert fields value from selected user prop source + fields?.forEach?.((field) => { + if (field.source) { + field.value = get(userInfos, field.source) || find(userInfos.attribute, ["name", field.source])?.value; + field.editable = false; + } else if (!field.source && field.value) { + field.value = null; + field.editable = null; + } + }); + // update state updateLayerProperties(layer.id, {fields}); }; return ; diff --git a/web/client/utils/FeatureGridUtils.js b/web/client/utils/FeatureGridUtils.js index 0af95d50ec..c8bd40eaf7 100644 --- a/web/client/utils/FeatureGridUtils.js +++ b/web/client/utils/FeatureGridUtils.js @@ -134,9 +134,6 @@ export const featureTypeToGridColumns = ( getAttributeFields(describe).filter(e => !(columnSettings[e.name] && columnSettings[e.name].hide)).map((desc) => { const option = options.find(o => o.name === desc.name); const field = fields.find(f => f.name === desc.name); - if(field && field.name === "id_emprise") { - field.editable = false; - } let columnProp = { sortable, key: desc.name, @@ -147,7 +144,7 @@ export const featureTypeToGridColumns = ( headerRenderer: getHeaderRenderer(), showTitleTooltip: !!option?.description, resizable, - editable: field.editable === false ? false : editable, + editable: field?.editable === false ? false : editable, filterable, editor: getEditor(desc, field), formatter: getFormatter(desc, field), From bdb8f0edfb91bb5d199813702edb5d78d85199d4 Mon Sep 17 00:00:00 2001 From: gaetanbrl Date: Thu, 7 Nov 2024 15:26:49 +0100 Subject: [PATCH 3/3] Allow or disable geometry layer edition Fix karma Toolbar test fix linter --- .../TOC/fragments/LayerFields/index.jsx | 5 +- .../data/featuregrid/toolbars/Toolbar.jsx | 17 +++---- .../toolbars/__tests__/Toolbar-test.jsx | 47 ++++++++++++------- .../plugins/featuregrid/panels/index.jsx | 4 +- web/client/selectors/featuregrid.js | 22 ++++++++- 5 files changed, 64 insertions(+), 31 deletions(-) diff --git a/web/client/components/TOC/fragments/LayerFields/index.jsx b/web/client/components/TOC/fragments/LayerFields/index.jsx index b0f226f990..208da3744a 100644 --- a/web/client/components/TOC/fragments/LayerFields/index.jsx +++ b/web/client/components/TOC/fragments/LayerFields/index.jsx @@ -59,12 +59,13 @@ const LayerFields = ({ layer, updateFields = () => {}, ...props }) => { }; }, []); - const onChange = (name, attribute, value) => { + const onChange = (name, attribute, value, isGeometryType = false) => { const newFields = fields.map((field) => { if (field.name === name) { return { ...field, - [attribute]: value + [attribute]: value, + ...(isGeometryType ? {isGeometry: true} : {}) }; } return field; diff --git a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx index 47a5b15e19..d8784094c1 100644 --- a/web/client/components/data/featuregrid/toolbars/Toolbar.jsx +++ b/web/client/components/data/featuregrid/toolbars/Toolbar.jsx @@ -61,20 +61,20 @@ const standardButtons = { visible={mode === "EDIT" && !hasChanges && !hasNewFeatures} onClick={events.switchViewMode} glyph="arrow-left"/>), - addFeature: ({disabled, mode, hasNewFeatures, hasChanges, hasSupportedGeometry = true, events = {}}) => ( (), - drawFeature: ({isDrawing = false, disabled, isSimpleGeom, mode, selectedCount, hasGeometry, hasSupportedGeometry = true, events = {}}) => ( (), @@ -103,12 +103,12 @@ const standardButtons = { visible={mode === "EDIT" && hasChanges || hasNewFeatures} onClick={events.clearFeatureEditing} glyph="remove-square"/>), - deleteGeometry: ({disabled, mode, hasGeometry, selectedCount, hasSupportedGeometry = true, events = {}}) => ( (), gridSettings: ({disabled, isColumnsOpen, selectedCount, mode, events = {}}) => ( events.setTimeSync && events.setTimeSync(!timeSync)} glyph="time" />), - snapToFeature: ({snapping, availableSnappingLayers = [], isSnappingLoading, snappingConfig, mode, mapType, editorHeight, pluginCfg, events = {}}) => ( ( { events.toggleSnapping && events.toggleSnapping(!snapping); }} @@ -317,6 +317,7 @@ export default React.memo((props = {}) => { setShowPopoverSync(getApi().getItem("showPopoverSync") !== null && pluginCfg?.showPopoverSync ? getApi().getItem("showPopoverSync") === "true" : pluginCfg?.showPopoverSync); } }, [props.mode]); + // const canEditGeometry = props.isEditingAllowed && return ( {sortBy(buttons.concat(toolbarItems), ["position"]).map(({Component}) => setShowPopoverSync(false)} mode={props?.mode ?? "VIEW"} disabled={props.disableToolbar} />)} diff --git a/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx b/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx index b89bb5cb72..e84afef7bd 100644 --- a/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx +++ b/web/client/components/data/featuregrid/toolbars/__tests__/Toolbar-test.jsx @@ -149,16 +149,21 @@ describe('Featuregrid toolbar component', () => { createFeature: () => {} }; spyOn(events, "createFeature"); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); const el = document.getElementsByClassName("featuregrid-toolbar")[0]; expect(el).toExist(); let addButton = document.getElementById("fg-add-feature"); expect(isVisibleButton(addButton)).toBe(false); addButton.click(); expect(events.createFeature).toHaveBeenCalled(); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); + addButton = document.getElementById("fg-add-feature"); + ReactDOM.render(, document.getElementById("container")); addButton = document.getElementById("fg-add-feature"); expect(isVisibleButton(addButton)).toBe(true); + ReactDOM.render(, document.getElementById("container")); + addButton = document.getElementById("fg-add-feature"); + expect(isVisibleButton(addButton)).toBe(false); ReactDOM.render(, document.getElementById("container")); addButton = document.getElementById("fg-add-feature"); expect(isVisibleButton(addButton)).toBe(false); @@ -172,26 +177,29 @@ describe('Featuregrid toolbar component', () => { startDrawingFeature: () => {} }; spyOn(events, "startDrawingFeature"); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); const el = document.getElementsByClassName("featuregrid-toolbar")[0]; expect(el).toExist(); let button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(false); button.click(); expect(events.startDrawingFeature).toHaveBeenCalled(); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(true); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(false); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); + button = document.getElementById("fg-draw-feature"); + expect(isVisibleButton(button)).toBe(false); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(true); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(true); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-draw-feature"); expect(isVisibleButton(button)).toBe(false); }); @@ -257,16 +265,19 @@ describe('Featuregrid toolbar component', () => { expect(isVisibleButton(button)).toBe(false); button.click(); expect(events.deleteGeometry).toHaveBeenCalled(); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-delete-geometry"); expect(isVisibleButton(button)).toBe(false); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); + button = document.getElementById("fg-delete-geometry"); + expect(isVisibleButton(button)).toBe(false); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-delete-geometry"); expect(isVisibleButton(button)).toBe(true); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-delete-geometry"); expect(isVisibleButton(button)).toBe(false); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); button = document.getElementById("fg-delete-geometry"); expect(isVisibleButton(button)).toBe(false); }); @@ -390,15 +401,15 @@ describe('Featuregrid toolbar component', () => { }); describe('snap tool button', () => { it('visibility', () => { - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); expect(document.getElementById("snap-button")).toExist(); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); expect(document.getElementById("snap-button")).toNotExist(); }); it('active/inactive state', () => { - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); expect(document.getElementById("snap-button").className.split(' ')).toInclude('btn-success'); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); expect(document.getElementById("snap-button").className.split(' ')).toNotInclude('btn-success'); }); it('handler', () => { @@ -406,10 +417,10 @@ describe('Featuregrid toolbar component', () => { toggleSnapping: () => { } }; const spy = spyOn(events, "toggleSnapping"); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); document.getElementById("snap-button").click(); expect(spy.calls[0].arguments[0]).toBe(false); - ReactDOM.render(, document.getElementById("container")); + ReactDOM.render(, document.getElementById("container")); document.getElementById("snap-button").click(); expect(spy.calls[1].arguments[0]).toBe(true); }); diff --git a/web/client/plugins/featuregrid/panels/index.jsx b/web/client/plugins/featuregrid/panels/index.jsx index d07fc5af1b..b515a81881 100644 --- a/web/client/plugins/featuregrid/panels/index.jsx +++ b/web/client/plugins/featuregrid/panels/index.jsx @@ -47,7 +47,8 @@ import { timeSyncActive, isViewportFilterActive, isFilterByViewportSupported, - selectedLayerSelector + selectedLayerSelector, + canEditGeometrySelector } from '../../../selectors/featuregrid'; import { mapLayoutValuesSelector } from '../../../selectors/maplayout'; import {isCesium, mapTypeSelector} from '../../../selectors/maptype'; @@ -95,6 +96,7 @@ const Toolbar = connect( disableZoomAll: (state) => state && state.featuregrid.virtualScroll || featureCollectionResultSelector(state).features.length === 0, isSearchAllowed: (state) => !isCesium(state), isEditingAllowed: isEditingAllowedSelector, + canEditGeometry: canEditGeometrySelector, hasSupportedGeometry, isFilterActive, showTimeSyncButton: showTimeSync, diff --git a/web/client/selectors/featuregrid.js b/web/client/selectors/featuregrid.js index 8f460fa483..cdf6275d27 100644 --- a/web/client/selectors/featuregrid.js +++ b/web/client/selectors/featuregrid.js @@ -6,7 +6,7 @@ * LICENSE file in the root directory of this source tree. */ -import { head, get, isObject } from 'lodash'; +import { head, get, isObject, find } from 'lodash'; import { getLayerFromId } from './layers'; import { findGeometryProperty } from '../utils/ogc/WFS/base'; @@ -14,7 +14,7 @@ import { currentLocaleSelector } from './locale'; import { isSimpleGeomType } from '../utils/MapUtils'; import { toChangesMap } from '../utils/FeatureGridUtils'; import { layerDimensionSelectorCreator } from './dimension'; -import { isUserAllowedSelectorCreator } from './security'; +import { isUserAllowedSelectorCreator, userGroupsEnabledSelector, userRoleSelector } from './security'; import {isCesium, mapTypeSelector} from './maptype'; import { attributesSelector, describeSelector } from './query'; import { createShallowSelectorCreator } from "../utils/ReselectUtils"; @@ -202,6 +202,24 @@ export const isEditingAllowedSelector = (state) => { })(state); return (canEdit || isAllowed) && !isCesium(state); }; +// geometry can be editing according to geom field setting +export const canEditGeometrySelector = (state) => { + const fields = selectedLayerFieldsSelector(state); + const geomField = find(fields, {isGeometry: true}); + const canEdit = isEditingAllowedSelector(state); + + const userRole = userRoleSelector(state); + const userGroups = userGroupsEnabledSelector(state); + const spreadList = [userRole, ...userGroups]; + + let geomEditors = geomField?.allowedEditorsRoles; + if (!geomEditors) { + return canEdit; + } + geomEditors = geomEditors.split(","); + return geomEditors.some(value => spreadList.includes(value)); +}; + export const paginationSelector = state => get(state, "featuregrid.pagination"); export const useLayerFilterSelector = state => get(state, "featuregrid.useLayerFilter", true);