From 3cc12d6d42279d7d3b374e868c82979ba966d920 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 22 Nov 2024 17:29:51 +0100 Subject: [PATCH 01/30] :sparkles: Start to develop edit multi relation feature from multi parent feature layer --- icons/EditMultiRelationFeatures.png | Bin 0 -> 999 bytes toolboxes/toolbox.js | 57 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 icons/EditMultiRelationFeatures.png diff --git a/icons/EditMultiRelationFeatures.png b/icons/EditMultiRelationFeatures.png new file mode 100644 index 0000000000000000000000000000000000000000..d521c4ab900f39c349fb558c2d6e584f1e567e70 GIT binary patch literal 999 zcmV`=k4}3#OFEh)9PA(KOA_b-nB4@zXtC?7t>lT2|-bpN{H6 zh`a|xWS^Z!I{iK{m>M_(VEIrY(uEMXk?h07L>g70DkS0^P)ecq>SfH%&f3OnYSv=Q zcXc2|f#m`LfafmS;~jQ1N+|%qfgca$MF{|tlaqdUb5J5;J(vDm(jbkokS{1>jE?;L z3oDGMa4vJYOeTY&)W8Q{PykR$EeLe#zu5A1-?!8jI(zYS@HqhT4857`!%TJtRiP@h zwnSlB7LvDbz%b?usitNPYBz3%VOkZ6^h^1!Xj3AQ5XofH@(kUxyWNf`S4eS(E*J6C z)D(gNo__i4-nULi-)w0!7K6ET=W2>uJayqAEGLz6WW&ptN-RHV2OP*IsuC8`_|H`yTDOvB;%SZW% zu2Y==I=uj0jzNNRj)%kl;`N(X2n2#Sxc?AzUB{ifw=h0FX4{sPm7zVp4@9K%1$=sz zhHxXo6v^9MqK?&2xas(UN-0Cvu3uT|Fy;%&IsXug#%s#U%RO}{BEsK&z2rN+N~tAn zqkzx!=Ac8V`*{2AKLmq8B=&TGF@~Yk0MZj_+it~*GPJcs{X`GJ=mE=RX=t zN=gt3H~WWb8Z!)I7C+Q&bES7{UA?PAnWhN features.length < 2, + done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); } + }, + dynamic: 0, + done: false + } + } + }), + new Step({ run: async (inputs, context) => { + const relations = Array.from( + new Set( + (await Promise.allSettled(inputs.features.map(feature => { + return getLayersDependencyFeatures(inputs.layer.getId(), { + // @since g3w-client-plugin-editin@v3.7.0 + relations: inputs.layer.getRelations().getArray().filter(r => + inputs.layer.getId() === r.getFather() && // get only child relation features of current editing layer + getEditingLayerById(r.getChild()) && // child layer is in editing + 'ONE' !== r.getType() // exclude ONE relation (Join 1:1) + ), + feature, + filterType: 'fid', + }); + }))).filter(({status }) => 'fulfilled' === status).map(({ value }) => value).flat() + ) + ); + //In case of multi relation in editing + if (relations.length > 1) { + alert('Choose relations') + } + + return $promisify(Promise.resolve(inputs, context)); + }}), + new OpenFormStep({ multi: true }), + ], + }), + }, // Move Feature (is_vector) && capabilities.includes('change_feature') && { id: 'movefeature', From f60d8c589b9de3fbd22b2f88d777f515da789ba6 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 25 Nov 2024 12:19:31 +0100 Subject: [PATCH 02/30] :sparkles: Start to develop edit multi relation feature from multi parent feature layer --- components/Toolbox.vue | 2 +- components/UserMessage.vue | 1 + toolboxes/toolbox.js | 116 +++++++++++++++++++++++-------------- workflows/index.js | 6 +- 4 files changed, 79 insertions(+), 46 deletions(-) diff --git a/components/Toolbox.vue b/components/Toolbox.vue index 5e1fd24b..2f1441e9 100644 --- a/components/Toolbox.vue +++ b/components/Toolbox.vue @@ -98,7 +98,7 @@ diff --git a/components/UserMessage.vue b/components/UserMessage.vue index 65c8e88f..ef14f92c 100644 --- a/components/UserMessage.vue +++ b/components/UserMessage.vue @@ -24,6 +24,7 @@ diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index ebc190bf..bd8754ab 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -80,15 +80,21 @@ export class ToolBox extends G3WObject { constructor(layer, dependencies = []) { super(); - const is_vector = [undefined, Layer.LayerTypes.VECTOR].includes(layer.getType()); - const geometryType = is_vector && layer.getGeometryType(); - const is_point = is_vector && Geometry.isPointGeometryType(geometryType); - const is_line = is_vector && Geometry.isLineGeometryType(geometryType); - const is_poly = is_vector && Geometry.isPolygonGeometryType(geometryType); - const is_table = Layer.LayerTypes.TABLE === layer.getType(); - const isMultiGeometry = geometryType && Geometry.isMultiGeometry(geometryType); - const iconGeometry = is_vector && (is_point ? 'Point' : is_line ? 'Line' : 'Polygon'); - + const is_vector = [undefined, Layer.LayerTypes.VECTOR].includes(layer.getType()); + const geometryType = is_vector && layer.getGeometryType(); + const is_point = is_vector && Geometry.isPointGeometryType(geometryType); + const is_line = is_vector && Geometry.isLineGeometryType(geometryType); + const is_poly = is_vector && Geometry.isPolygonGeometryType(geometryType); + const is_table = Layer.LayerTypes.TABLE === layer.getType(); + const isMultiGeometry = geometryType && Geometry.isMultiGeometry(geometryType); + const iconGeometry = is_vector && (is_point ? 'Point' : is_line ? 'Line' : 'Polygon'); + //@since 3.9.0 Check if layer has relation layers editable + const editable_relations = layer.getRelations().getArray() + .filter(relation => { + const l = CatalogLayersStoresRegistry.getLayerById(getRelationId({ layerId: layer.getId(), relation })); + return l.isEditable() && l.config.editing.visible; + }) + .map(r => r); this._start = false; /** constraint loading features to a filter set */ @@ -103,7 +109,7 @@ export class ToolBox extends G3WObject { * _states: [ * { * id: unique key - * state: [state] // example: history contsins features state + * state: [state] // example: history contains features state * // array because a tool can apply changes to more than one features at time (split di una feature) * }, * { @@ -409,7 +415,7 @@ export class ToolBox extends G3WObject { }), }, // @since 3.9.0 Edit Attributes of relations features to Multi features - (is_vector) && capabilities.includes('change_attr_feature') && { + (is_vector) && capabilities.includes('change_attr_feature') && editable_relations.filter(r => 'ONE' !== r.getType()).length > 0 && { id: 'editmultiattributesrelationfeatures', type: ['change_attr_feature'], name: "editing.tools.update_multi_features", @@ -429,39 +435,65 @@ export class ToolBox extends G3WObject { description: `editing.workflow.steps.${ApplicationState.ismobile ? 'selectDrawBoxAtLeast2Feature' : 'selectMultiPointSHIFTAtLeast2Feature'}`, buttonnext: { disabled: true, - condition:({ features=[] }) => features.length < 2, - done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); } + condition: ({ features = [] }) => features.length < 2, + done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); } }, dynamic: 0, - done: false + done: false } } }), new Step({ run: async (inputs, context) => { - const relations = Array.from( - new Set( - (await Promise.allSettled(inputs.features.map(feature => { - return getLayersDependencyFeatures(inputs.layer.getId(), { - // @since g3w-client-plugin-editin@v3.7.0 - relations: inputs.layer.getRelations().getArray().filter(r => - inputs.layer.getId() === r.getFather() && // get only child relation features of current editing layer - getEditingLayerById(r.getChild()) && // child layer is in editing - 'ONE' !== r.getType() // exclude ONE relation (Join 1:1) - ), - feature, - filterType: 'fid', - }); - }))).filter(({status }) => 'fulfilled' === status).map(({ value }) => value).flat() - ) - ); + GUI.setModal(true); + const relations = editable_relations.filter(r => 'ONE' !== r.getType()); + //get relation features from feature parent layer + await Promise.allSettled(inputs.features.map(feature => getLayersDependencyFeatures(inputs.layer.getId(), { + relations, + feature, + filterType: 'fid', + }))) + //In case of multi relation in editing if (relations.length > 1) { alert('Choose relations') } + //start child workflow + const workflow = new Workflow({ + type: 'editmultiattributes', + steps: [ + new OpenFormStep({ multi: true }), + ], + }); + //Relations layer + const rLayer = getEditingLayerById(relations[0].getChild()); + + const fields = getRelationFieldsFromRelation({ + layerId: relations[0].getChild(), + relation: relations[0] + }); + + const options = { + context: { + session: Workflow.Stack.getCurrent().getSession(), // get parent workflow + excludeFields: fields.ownField, // array of fields to be excluded + }, + inputs: { + features: rLayer.readFeatures(), + layer: rLayer + } + }; + try { + await promisify(workflow.start(options)); + } catch(e) { + console.warn(e); + } + + workflow.stop(); + + GUI.setModal(false); return $promisify(Promise.resolve(inputs, context)); }}), - new OpenFormStep({ multi: true }), ], }), }, @@ -1254,7 +1286,7 @@ export class ToolBox extends G3WObject { this.state._tools.forEach(tool => { Object.assign(tool, { disabledtoolsoftools: [], - enabled: false, + enabled: !!tool.enabled, active: false, message: null, messages: tool.op.getMessages(), @@ -1828,7 +1860,7 @@ export class ToolBox extends G3WObject { * * @param bool */ - setEditing(bool=true) { + setEditing(bool = true) { this.setEnable(bool); this.state.editing.on = bool; this.enableTools(bool); @@ -1853,7 +1885,7 @@ export class ToolBox extends G3WObject { * * @returns {boolean} */ - setEnable(bool=false) { + setEnable(bool = false) { this.state.enabled = bool; return this.state.enabled; } @@ -2056,9 +2088,9 @@ export class ToolBox extends G3WObject { const disabledtools = this.state._disabledtools || []; tools .forEach(tool => { - const enabled = undefined !== tool.enable ? tool.enable : bool; - tool.enabled = (bool && disabledtools.length) - ? disabledtools.indexOf(tool.getId()) === -1 + const enabled = undefined === tool.enable ? bool : tool.enable; + tool.enabled = (bool && disabledtools.length > 0) + ? !disabledtools.includes(tool.getId()) : toRawType(enabled) === 'Boolean' ? enabled : enabled({ bool, tool }); @@ -2823,11 +2855,11 @@ export class ToolBox extends G3WObject { if ((Layer.LayerTypes.VECTOR === this.state._layerType) && this.state._getFeaturesOption.filter.bbox) { const fnc = () => { if ( - //added ApplicationState.online - ApplicationState.online - && this.state.editing.canEdit - && this.state.selected //need to be selected - && 0 === GUI.getContentLength() + //added ApplicationState.online + ApplicationState.online + && this.state.editing.canEdit + && this.state.selected //need to be selected + && 0 === GUI.getContentLength() ) { this.state._getFeaturesOption.filter.bbox = GUI.getService('map').getMapBBOX(); this.state.loading = true; diff --git a/workflows/index.js b/workflows/index.js index 2ebb81b3..a281dbc8 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -479,7 +479,7 @@ export class OpenFormStep extends Step { formStructure: inputs.layer.hasFormStructure() && inputs.layer.getLayerEditingFormStructure() || undefined, modal: true, push: this._options.push || this._isContentChild, /** @since v3.7 force push content on top without clear previous content */ - showgoback: undefined !== this._options.showgoback ? this._options.showgoback : !this._isContentChild, /** @since v3.7 force show back button */ + showgoback: undefined === this._options.showgoback ? !this._isContentChild : this._options.showgoback, /** @since v3.7 force show back button */ /** @TODO make it straightforward: `headerComponent` vs `buttons` ? */ headerComponent: this._saveAll && { template: /* html */ ` @@ -775,8 +775,8 @@ export class OpenFormStep extends Step { if (contextService && false === this._isContentChild) { contextService.setUpdate(false, { force: false }); } - - GUI.closeForm({ pop: this.push || this._isContentChild }); + //@since 3.9.0 add GUI.getContentLength() in case of edit multi relationfeatures tool + GUI.closeForm({ pop: this.push || this._isContentChild && GUI.getContentLength() > 1 }); g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').resetCurrentLayout(); From 14406e18226591f8cf12ec8f7204a1f308471eed Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 25 Nov 2024 14:21:48 +0100 Subject: [PATCH 03/30] :sparkles: Add reset method step --- toolboxes/toolbox.js | 10 ++++++---- workflows/index.js | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index bd8754ab..744ec738 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -402,11 +402,12 @@ export class ToolBox extends G3WObject { description: `editing.workflow.steps.${ApplicationState.ismobile ? 'selectDrawBoxAtLeast2Feature' : 'selectMultiPointSHIFTAtLeast2Feature'}`, buttonnext: { disabled: true, - condition:({ features=[] }) => features.length < 2, - done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); } + condition:({ features = [] }) => features.length < 2, + done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); }, }, dynamic: 0, - done: false + done: false, + reset() { this.dynamic = 0; }, } } }), @@ -439,7 +440,8 @@ export class ToolBox extends G3WObject { done: () => { Workflow.Stack.getCurrent().clearUserMessagesSteps(); } }, dynamic: 0, - done: false + done: false, + reset() { this.dynamic = 0; }, } } }), diff --git a/workflows/index.js b/workflows/index.js index a281dbc8..9f96a2f8 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -1092,6 +1092,7 @@ export class SelectElementsStep extends Step { } stop() { + Object.values(this.getSteps() || {}).forEach(s => s.reset && s.reset() ); this._selectInteractions.forEach(i => this.removeInteraction(i)); if (this._vectorLayer) { From d695f3f8be9eedfc6149553eeb71091f89237aae Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 25 Nov 2024 15:50:05 +0100 Subject: [PATCH 04/30] Clean code - spaces --- g3wsdk/workflow/workflow.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/g3wsdk/workflow/workflow.js b/g3wsdk/workflow/workflow.js index 32e5a525..bd4a9c21 100644 --- a/g3wsdk/workflow/workflow.js +++ b/g3wsdk/workflow/workflow.js @@ -368,10 +368,10 @@ export class Workflow extends G3WObject { if (showUserMessage) { GUI.showUserMessage({ - title: 'plugins.editing.workflow.title.steps', - type: 'tool', + title: 'plugins.editing.workflow.title.steps', + type: 'tool', position: 'left', - size: 'small', + size: 'small', closable: false, hooks: { body: { From 1286a0c0c12d4e00f9285f35c4756475bf7dd1cd Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 25 Nov 2024 15:55:09 +0100 Subject: [PATCH 05/30] :bug: Add user message to copy paste from other layer --- toolboxes/toolbox.js | 55 ++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 744ec738..5253cfe7 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -580,7 +580,6 @@ export class ToolBox extends G3WObject { steps: [ new Step({ layer, - help: 'editing.steps.help.draw_new_feature', run(inputs, context) { return $promisify(new Promise((resolve, reject) => { const originalLayer = inputs.layer; @@ -610,35 +609,42 @@ export class ToolBox extends G3WObject { className: 'btn-success', callback: async () => { try { + const feature = await $promisify(async () => { + GUI.showUserMessage({ + type: 'tool', + message: 'plugins.editing.workflow.steps.selectPoint', + size: 'small', + autoclose: false, + closable: false + }); //get selected layer const layer = layers.find(l => l.selected); - const feature = await $promisify(async () => { const features = await (new Promise(async resolve => { this.addInteraction( layer.external - ? new PickFeaturesInteraction({ layer: GUI.getService('map').getLayerById(layer.id) }) - : new g3wsdk.ol.interactions.PickCoordinatesInteraction(), { - 'picked': async e => { - try { - resolve(convertToGeometry( - layer.external - ? e.features // external layer - : ((await DataRouterService.getData('query:coordinates', { // TOC/PROJECT layer - inputs: { - coordinates: e.coordinate, - query_point_tolerance: ProjectsRegistry.getCurrentProject().getQueryPointTolerance(), - layerIds: [layer.id], - multilayers: false - }, - outputs: null - })).data[0] || { features: [] }).features, - geometryType, - )) - } catch(e) { - console.warn(e); - } + ? new PickFeaturesInteraction({ layer: GUI.getService('map').getLayerById(layer.id) }) + : new g3wsdk.ol.interactions.PickCoordinatesInteraction(), { + 'picked': async e => { + try { + resolve(convertToGeometry( + layer.external + ? e.features // external layer + : ((await DataRouterService.getData('query:coordinates', { // TOC/PROJECT layer + inputs: { + coordinates: e.coordinate, + query_point_tolerance: ProjectsRegistry.getCurrentProject().getQueryPointTolerance(), + layerIds: [layer.id], + multilayers: false + }, + outputs: null + })).data[0] || { features: [] }).features, + geometryType, + )) + } catch(e) { + console.warn(e); } } + } ); })); @@ -703,6 +709,9 @@ export class ToolBox extends G3WObject { //hide user message step })); }, + stop() { + GUI.closeUserMessage(); + } }), openFormStep, ], From d2f36cbe8480af04ae4f44d55aa67d2d1f0ce6bd Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 26 Nov 2024 08:54:27 +0100 Subject: [PATCH 06/30] Clean code - spaces --- toolboxes/toolbox.js | 96 ++++++++++++++++++++++---------------------- 1 file changed, 49 insertions(+), 47 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 5253cfe7..2e0a89bb 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -445,57 +445,59 @@ export class ToolBox extends G3WObject { } } }), - new Step({ run: async (inputs, context) => { - GUI.setModal(true); - const relations = editable_relations.filter(r => 'ONE' !== r.getType()); - //get relation features from feature parent layer - await Promise.allSettled(inputs.features.map(feature => getLayersDependencyFeatures(inputs.layer.getId(), { - relations, - feature, - filterType: 'fid', - }))) - - //In case of multi relation in editing - if (relations.length > 1) { - alert('Choose relations') - } - //start child workflow - const workflow = new Workflow({ - type: 'editmultiattributes', - steps: [ - new OpenFormStep({ multi: true }), - ], - }); - //Relations layer - const rLayer = getEditingLayerById(relations[0].getChild()); - - const fields = getRelationFieldsFromRelation({ - layerId: relations[0].getChild(), - relation: relations[0] - }); - - const options = { - context: { - session: Workflow.Stack.getCurrent().getSession(), // get parent workflow - excludeFields: fields.ownField, // array of fields to be excluded - }, - inputs: { - features: rLayer.readFeatures(), - layer: rLayer + new Step({ + run: async (inputs, context) => { + GUI.setModal(true); + const relations = editable_relations.filter(r => 'ONE' !== r.getType()); + //get relation features from feature parent layer + await Promise.allSettled(inputs.features.map(feature => getLayersDependencyFeatures(inputs.layer.getId(), { + relations, + feature, + filterType: 'fid', + }))) + + //In case of multi relation in editing + if (relations.length > 1) { + alert('Choose relations') } - }; + //start child workflow + const workflow = new Workflow({ + type: 'editmultiattributes', + steps: [ + new OpenFormStep({ multi: true }), + ], + }); + //Relations layer + const rLayer = getEditingLayerById(relations[0].getChild()); - try { - await promisify(workflow.start(options)); - } catch(e) { - console.warn(e); - } + const fields = getRelationFieldsFromRelation({ + layerId: relations[0].getChild(), + relation: relations[0] + }); + + const options = { + context: { + session: Workflow.Stack.getCurrent().getSession(), // get parent workflow + excludeFields: fields.ownField, // array of fields to be excluded + }, + inputs: { + features: rLayer.readFeatures(), + layer: rLayer + } + }; + + try { + await promisify(workflow.start(options)); + } catch(e) { + console.warn(e); + } - workflow.stop(); + workflow.stop(); - GUI.setModal(false); - return $promisify(Promise.resolve(inputs, context)); - }}), + GUI.setModal(false); + return $promisify(Promise.resolve(inputs, context)); + } + }), ], }), }, From 7e7da7e6257a15471a006b96fcc6982e2570b63b Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 26 Nov 2024 15:01:52 +0100 Subject: [PATCH 07/30] Use steps message to address user to follow right flow --- toolboxes/toolbox.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 2e0a89bb..9606106a 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -582,6 +582,17 @@ export class ToolBox extends G3WObject { steps: [ new Step({ layer, + //@since 3.9.0 to show user message steps + steps: { + chooselayer: { + description: `editing.modal.tools.copyfeaturefromotherlayer.title`, + done: false, + }, + selectgeometry: { + description: `editing.workflow.steps.selectPoint`, + done: false, + } + }, run(inputs, context) { return $promisify(new Promise((resolve, reject) => { const originalLayer = inputs.layer; @@ -610,15 +621,10 @@ export class ToolBox extends G3WObject { label: 'Ok', className: 'btn-success', callback: async () => { + //set choose layer step done + this.setUserMessageStepDone('chooselayer'); try { const feature = await $promisify(async () => { - GUI.showUserMessage({ - type: 'tool', - message: 'plugins.editing.workflow.steps.selectPoint', - size: 'small', - autoclose: false, - closable: false - }); //get selected layer const layer = layers.find(l => l.selected); const features = await (new Promise(async resolve => { @@ -711,9 +717,6 @@ export class ToolBox extends G3WObject { //hide user message step })); }, - stop() { - GUI.closeUserMessage(); - } }), openFormStep, ], From 51c3e1af31b1829b9acb0a02eb5007274a36762c Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 26 Nov 2024 15:45:45 +0100 Subject: [PATCH 08/30] :recycle: Use class instead base or inherit --- deprecated.js | 102 +++++++++++++++++++++++++------------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/deprecated.js b/deprecated.js index 8d47e8f9..9ce6c95f 100644 --- a/deprecated.js +++ b/deprecated.js @@ -1,93 +1,97 @@ import { promisify } from '../../utils/promisify'; +class Queque { + constructor() { this.tasks = []; } + addTask(task) { this.tasks.push(task); } + run(reverse = false) { while (this.tasks.length) { const task = reverse ? this.tasks.pop() : this.tasks.shift(); task(); } } + flush() { return this.tasks.splice(0); } + getLength() { return this.tasks.length; } + clear() { this.run(); this.tasks = []; } +} + + /** * Class Flow of workflow step by step * * ORIGINAL SOURCE: g3w-client/src/core/workflow/flow.js@v3.9.1 * ORIGINAL SOURCE: g3w-client/src/core/workflow/queque.js@v3.9.1 */ -export function Flow() { - console.warn('[G3W-CLIENT] g3wsdk.core.workflow.Flow is deprecated'); - - class Queque { - constructor() { this.tasks = []; } - addTask(task) { this.tasks.push(task); } - run(reverse = false) { while (this.tasks.length) { const task = reverse ? this.tasks.pop() : this.tasks.shift(); task(); } } - flush() { return this.tasks.splice(0); } - getLength() { return this.tasks.length; } - clear() { this.run(); this.tasks = []; } +export class Flow extends g3wsdk.core.G3WObject { + constructor() { + super(); + console.warn('[G3W-CLIENT] g3wsdk.core.workflow.Flow is deprecated'); + this.steps = []; + this.counter = 0; + this.context = null; + this.queques = { + end: new Queque(), + micro: new Queque() + }; + this.inputs; + this.d; + this._workflow; } - let steps = []; - let inputs; - let counter = 0; - let context = null; - let d; - let _workflow; - this.queques = { - end: new Queque(), - micro: new Queque() - }; //start workflow - this.start = function(workflow) { - d = $.Deferred(); - if (counter > 0) { + start(workflow) { + this.d = $.Deferred(); + if (this.counter > 0) { console.log("reset workflow before restarting"); } - _workflow = workflow; - inputs = workflow.getInputs(); - context = workflow.getContext(); - steps = workflow.getSteps(); + this._workflow = workflow; + this.inputs = workflow.getInputs(); + this.context = workflow.getContext(); + this.steps = workflow.getSteps(); // check if there are steps - if (steps && steps.length) { + if (this.steps && this.steps.length) { //run step (first) - this.runStep(steps[0], inputs, context); + this.runStep(this.steps[0], this.inputs, this.context); } // return a promise that will be reolved if all step go right - return d.promise(); + return this.d.promise(); }; //run step - this.runStep = function(step, inputs) { + runStep(step, inputs) { //run step that run task - _workflow.setMessages({ + this._workflow.setMessages({ help: step.state.help }); const runMicroTasks = this.queques.micro.getLength(); - step.run(inputs, context, this.queques) + step.run(inputs, this.context, this.queques) .then(outputs => { runMicroTasks && this.queques.micro.run(); this.onDone(outputs); }) - .fail(error => this.onError(error)); + .fail(e => this.onError(e)); }; //check if all step are resolved - this.onDone = function(outputs) { - counter++; - if (counter === steps.length) { - counter = 0; - d.resolve(outputs); + onDone(outputs) { + this.counter++; + if (this.counter === this.steps.length) { + this.counter = 0; + this.d.resolve(outputs); return; } - this.runStep(steps[counter], outputs); + this.runStep(this.steps[this.counter], outputs); }; // in case of error - this.onError = function(err) { - counter = 0; + onError(e) { + this.counter = 0; this.clearQueques(); - d.reject(err); + this.d.reject(e); }; // stop flow - this.stop = function() { + stop() { const d = $.Deferred(); - steps[counter].isRunning() ? steps[counter].stop() : null; + this.steps[counter].isRunning() ? this.steps[this.counter].stop() : null; this.clearQueques(); - if (counter > 0) { + if (this.counter > 0) { // set counter to 0 - counter = 0; + this.counter = 0; // reject flow d.reject(); } else { @@ -97,15 +101,13 @@ export function Flow() { return d.promise(); }; - this.clearQueques = function(){ + clearQueques(){ this.queques.micro.clear(); this.queques.end.clear(); } - g3wsdk.core.utils.base(this) } -g3wsdk.core.utils.inherit(Flow, g3wsdk.core.G3WObject); /** * ORIGINAL SOURCE: g3w-client/src/services/editing.js@v3.9.1 From 04543e1ab4ccfbcf11c9e97ea7a60111b6c26fcb Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 2 Dec 2024 17:03:38 +0100 Subject: [PATCH 09/30] :sparkles: Add close form on deep relations --- g3wsdk/workflow/step.js | 26 +- g3wsdk/workflow/workflow.js | 69 ++-- workflows/index.js | 607 +++++++++++++++++++----------------- 3 files changed, 374 insertions(+), 328 deletions(-) diff --git a/g3wsdk/workflow/step.js b/g3wsdk/workflow/step.js index 8485e4dd..652f76e0 100644 --- a/g3wsdk/workflow/step.js +++ b/g3wsdk/workflow/step.js @@ -31,9 +31,14 @@ export class Step extends G3WObject { this._options = options; + //store promise of current running step when call run this._run = (options.run || this.run || (async () => true)).bind(this); + //store promise of current running step when call stop this._stop = (options.stop || this.stop || (async () => true)).bind(this); + /** since 3.9.0 */ + this._rejectRun = null; + /** * @FIXME add description */ @@ -102,7 +107,7 @@ export class Step extends G3WObject { * @since g3w-client-plugin-editing@v3.8.0 */ if (options.onStop) { - this.on('run', options.onStop); + this.on('stop', options.onStop); } /** @@ -389,7 +394,7 @@ export class Step extends G3WObject { */ registerEscKeyEvent(callback) { if (callback) { - this.on('run', () => this.bindEscKeyUp(callback)); + this.on('run', () => this.bindEscKeyUp(callback)); this.on('stop', () => this.unbindEscKeyUp()); } } @@ -410,10 +415,12 @@ export class Step extends G3WObject { */ __run(inputs, context) { return $promisify(async() => { + //set step inputs this.setInputs(inputs); + //set step context this.setContext(context); - const step = this; + const step = this; const toolsOfTools = { snap: { @@ -470,15 +477,18 @@ export class Step extends G3WObject { try { this.state.running = true; // change state to running - return await promisify(this._run(inputs, context)); - } catch (e) { + const runPromise = promisify(this._run(inputs, context)); + return await (this._rejectRun ? runPromise : Promise.race([new Promise((_, reject) => this._rejectRun = reject), runPromise])); + } catch(e) { console.warn(e); this.state.error = e; return Promise.reject(e); } finally { + //always call stop this.__stop(); + this._rejectRun = null; } - }); + }) } /** @@ -489,8 +499,8 @@ export class Step extends G3WObject { * * @fires stop */ - __stop() { - this._stop(this._inputs, this._context); // stop task + async __stop() { + await this._stop(this._inputs, this._context); // stop task this.state.running = false; // remove running state if (this._workflow) { this._workflow._toolsoftool.forEach(t => t.options.stop()); diff --git a/g3wsdk/workflow/workflow.js b/g3wsdk/workflow/workflow.js index bd4a9c21..7facb32b 100644 --- a/g3wsdk/workflow/workflow.js +++ b/g3wsdk/workflow/workflow.js @@ -187,16 +187,6 @@ export class Workflow extends G3WObject { } } - /** - * @FIXME add description - */ - removeChild() { - if (this._child) { - Workflow.Stack.removeAt(this._child.getStackIndex()); - } - this._child = null; - } - /** * @param input.key * @param input.value @@ -298,7 +288,7 @@ export class Workflow extends G3WObject { /** * @FIXME add description */ - reject () { + reject() { if (this._promise) { this._promise.reject(); } @@ -314,19 +304,28 @@ export class Workflow extends G3WObject { } } + /** + * Method to run steps of workflow + * @param step + * @param inputs + * @return {Promise} + */ async runStep(step, inputs) { try { + //set step message this.setMessages({ help: step.state.help }); + //run step const outputs = await promisify(step.__run(inputs, this.getContext())); // onDone → check if all step is resolved this._stepIndex++; + //check if is the last of workflow steps if (this._stepIndex === this.getSteps().length) { this._stepIndex = 0; return outputs; } else { return this.runStep(this.getSteps()[this._stepIndex], outputs); } - } catch (e) { + } catch(e) { //In case of reject this._stepIndex = 0; return Promise.reject(e); } @@ -358,13 +357,15 @@ export class Workflow extends G3WObject { ) { Workflow.Stack.getCurrent().addChild(this) } - + + //get stack index this._stackIndex = Workflow.Stack.push(this); + //get steps this._steps = options.steps || this._steps; - + //for each step assign current workflow to _workflow (this._steps || []).forEach(s => s._workflow = this); - const showUserMessage = Object.keys(this._userMessageSteps).length; + const showUserMessage = Object.keys(this._userMessageSteps).length > 0; if (showUserMessage) { GUI.showUserMessage({ @@ -383,19 +384,20 @@ export class Workflow extends G3WObject { } }); } - //emit start + //emit start Workflow this.emit('start'); try { console.assert(0 === this._stepIndex, `reset workflow before restarting: ${this._stepIndex}`) //start flow of workflow const outputs = await this.runStep(this.getSteps()[this._stepIndex], this.getInputs()); + //In case of show user message (tool steps) if (showUserMessage) { setTimeout(() => { this.clearUserMessagesSteps(); resolve(outputs); }, 500); } else { resolve(outputs); } - } catch (e) { + } catch(e) { console.warn(e); if (showUserMessage) { this.clearUserMessagesSteps(); @@ -403,6 +405,7 @@ export class Workflow extends G3WObject { reject(e); } + //in case of worflow that need to run once time, stop workflow if (this.runOnce) { this.stop(); } @@ -414,45 +417,51 @@ export class Workflow extends G3WObject { * * @fires stop */ - stop() { + async stop() { return $promisify(new Promise(async (resolve, reject) => { + this._promise = null; try { // stop child workflow if (this._child) { - await promisify(this._child.stop()); + await promisify(this._child.stop()); } } catch(e) { console.warn(e); } - - // ensure that child is always removed - this.removeChild(); - - Workflow.Stack.removeAt(this.getStackIndex()); + //remove child + this._child = null; // stop flow try { - if (this.getSteps()[this._stepIndex].isRunning()) { + //get current step + const step = this.getSteps()[this._stepIndex]; + //check if it is running + if (step.isRunning()) { //clear messages steps this.clearMessages(); - //stop a current step - this.getSteps()[this._stepIndex].__stop(); + //reject a current step + step._rejectRun(); + //wait stop + await new Promise((_, reject) => { step.once('stop', reject)}) } // reset counter and reject flow if (this._stepIndex > 0) { this._stepIndex = 0; reject(); - return Promise.reject(); } else { resolve(); } - } catch (e) { + } catch(e) { console.warn(e); reject(e); } + //remove workflow from stack + Workflow.Stack.removeAt(this.getStackIndex()); + + //emit stop Workflow this.emit('stop'); })); @@ -634,5 +643,5 @@ Workflow.Stack = { removeAt(i) { workflows.splice(i, 1); }, insertAt(i, w) { workflows[i] = w; }, getAt(i) { return workflows[i]; }, - clear() { while (workflows.length) { (workflows.pop()).stop(); } }, + async clear() { await this.getFirst().stop(); workflows.splice(0); } }; \ No newline at end of file diff --git a/workflows/index.js b/workflows/index.js index 9f96a2f8..71a76275 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -402,91 +402,93 @@ export class OpenFormStep extends Step { * @returns {*} */ run(inputs, context) { - const promise = new Promise(async (resolve, reject) => { - this._isContentChild = Workflow.Stack.getLength() > 1; - this.layerId = inputs.layer.getId(); - - GUI.setLoadingContent(false); + return $promisify(async () => { + const promise = new Promise(async (resolve, reject) => { + //@since 3.9.0 + this._rejectRun = reject; + this._isContentChild = Workflow.Stack.getLength() > 1; + this.layerId = inputs.layer.getId(); - GUI.getService('map').disableClickMapControls(true); + GUI.setLoadingContent(false); + GUI.getService('map').disableClickMapControls(true); + if (!this._multi && Array.isArray(inputs.features[inputs.features.length - 1])) { + resolve(); + return; + } - if (!this._multi && Array.isArray(inputs.features[inputs.features.length - 1])) { - resolve(); - return; - } + g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').setCurrentLayout(); - g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').setCurrentLayout(); + const layerName = inputs.layer.getName(); + this._features = this._multi ? inputs.features : [inputs.features[inputs.features.length - 1]]; + this._originalFeatures = this._features.map(f => f.clone()); - const layerName = inputs.layer.getName(); - this._features = this._multi ? inputs.features : [inputs.features[inputs.features.length - 1]]; - this._originalFeatures = this._features.map(f => f.clone()); + // create a child relation feature set a father relation field value + if (this._isContentChild) { + context.fatherValue = context.fatherValue || []; // are array + (context.fatherField || []).forEach((field, i) => { + this._features[0].set(field, context.fatherValue[i]); + this._originalFeatures[0].set(field, context.fatherValue[i]); + }); + } - // create a child relation feature set a father relation field value - if (this._isContentChild) { - context.fatherValue = context.fatherValue || []; // are array - (context.fatherField || []).forEach((field, i) => { - this._features[0].set(field, context.fatherValue[i]); - this._originalFeatures[0].set(field, context.fatherValue[i]); + const fields = getFormFields({ + inputs, + context, + feature: this._features[0], + isChild: this._isContentChild, + multi: this._multi, }); - } - const fields = getFormFields({ - inputs, - context, - feature: this._features[0], - isChild: this._isContentChild, - multi: this._multi, - }); - - // set fields. Useful getParentFormData - Workflow.Stack.getCurrent().setInput({ key: 'fields', value: fields }); - - // whether disable relations editing (ref: "editmultiattributes") - const feature = !this._multi && inputs.features && inputs.features[inputs.features.length - 1]; - const layerId = !this._multi && inputs.layer.getId(); - - // @since g3w-client-plugin-editing@v3.7.2 - // skip relations that don't have a form structure - if (feature && !feature.isNew() && inputs.layer.getLayerEditingFormStructure()) { - await getLayersDependencyFeatures(inputs.layer.getId(), { - // @since g3w-client-plugin-editin@v3.7.0 - relations: inputs.layer.getRelations().getArray().filter(r => - inputs.layer.getId() === r.getFather() && // get only child relation features of current editing layer - getEditingLayerById(r.getChild()) && // child layer is in editing - 'ONE' !== r.getType() // exclude ONE relation (Join 1:1) - ), - feature, - filterType: 'fid', - }); - } + // set fields. Useful getParentFormData + Workflow.Stack.getCurrent().setInput({ key: 'fields', value: fields }); + + // whether disable relations editing (ref: "editmultiattributes") + const feature = !this._multi && inputs.features && inputs.features[inputs.features.length - 1]; + const layerId = !this._multi && inputs.layer.getId(); + + // @since g3w-client-plugin-editing@v3.7.2 + // skip relations that don't have a form structure + if (feature && !feature.isNew() && inputs.layer.getLayerEditingFormStructure()) { + await getLayersDependencyFeatures(inputs.layer.getId(), { + // @since g3w-client-plugin-editin@v3.7.0 + relations: inputs.layer.getRelations().getArray().filter(r => + inputs.layer.getId() === r.getFather() && // get only child relation features of current editing layer + getEditingLayerById(r.getChild()) && // child layer is in editing + 'ONE' !== r.getType() // exclude ONE relation (Join 1:1) + ), + feature, + filterType: 'fid', + }); + } - /** ORIGINAL SOURCE: g3w-client-plugin-editing/form/editingform.js@v3.7.8 */ - /** ORIGINAL SOURCE: g3w-client-plugin-editing/form/editingformservice.js@v3.7.8 */ - const formService = GUI.showForm({ - feature: this._originalFeatures[0], - title: "plugins.editing.editing_attributes", - name: layerName, - crumb: { title: layerName }, - id: `form_${layerName}`, - dataid: layerName, - layer: inputs.layer, - isnew: this._originalFeatures.length > 1 ? false : this._originalFeatures[0].isNew(), // specify if is a new feature - parentData: getParentFormData(), - fields, - context_inputs: this._multi ? false: { context, inputs }, - formStructure: inputs.layer.hasFormStructure() && inputs.layer.getLayerEditingFormStructure() || undefined, - modal: true, - push: this._options.push || this._isContentChild, /** @since v3.7 force push content on top without clear previous content */ - showgoback: undefined === this._options.showgoback ? !this._isContentChild : this._options.showgoback, /** @since v3.7 force show back button */ - /** @TODO make it straightforward: `headerComponent` vs `buttons` ? */ - headerComponent: this._saveAll && { - template: /* html */ ` -
+ /** ORIGINAL SOURCE: g3w-client-plugin-editing/form/editingform.js@v3.7.8 */ + /** ORIGINAL SOURCE: g3w-client-plugin-editing/form/editingformservice.js@v3.7.8 */ + const formService = GUI.showForm({ + feature: this._originalFeatures[0], + title: "plugins.editing.editing_attributes", + name: layerName, + crumb: { title: layerName }, + id: `form_${layerName}`, + dataid: layerName, + layer: inputs.layer, + isnew: this._originalFeatures.length > 1 ? false : this._originalFeatures[0].isNew(), // specify if is a new feature + parentData: getParentFormData(), + fields, + context_inputs: this._multi ? false: { context, inputs }, + formStructure: inputs.layer.hasFormStructure() && inputs.layer.getLayerEditingFormStructure() || undefined, + modal: true, + push: this._options.push || this._isContentChild, /** @since v3.7 force push content on top without clear previous content */ + showgoback: undefined === this._options.showgoback ? !this._isContentChild : this._options.showgoback, /** @since v3.7 force show back button */ + /** @TODO make it straightforward: `headerComponent` vs `buttons` ? */ + headerComponent: this._saveAll && { + template: /* html */ ` +
+ +
+ + + +
`, - name: 'Saveall', - /** @TODO figure out who populate these props (ie. core client code?) */ - props: { update: { type: Boolean }, valid: { type: Boolean } }, - data() { - return { - enabled: Workflow.Stack._workflows.slice(0, Workflow.Stack.getLength() - 1) - .every(w => { - const valid = ((w.getContext().service instanceof FormService) ? w.getContext().service.getState() : {}).valid; - return valid || undefined === valid; - }), - }; - }, - computed: { - /** @returns {boolean} whether disable save all button (eg. when parent or current form is not valid/ updated) */ - disabled() { - return !this.enabled || !(this.valid && this.update); + name: 'Saveall', + /** @TODO figure out who populate these props (ie. core client code?) */ + props: { update: { type: Boolean }, valid: { type: Boolean } }, + data() { + return { + enabled: Workflow.Stack._workflows.slice(0, Workflow.Stack.getLength() - 1) + .every(w => { + const valid = ((w.getContext().service instanceof FormService) ? w.getContext().service.getState() : {}).valid; + return valid || undefined === valid; + }), + isChild: Workflow.Stack.getLength() > 1, + }; }, - }, - methods: { - async saveAll() { - //Set loading content - GUI.setLoadingContent(true); - //Disable form - GUI.disableContent(true); - await Promise.allSettled( - [...Workflow.Stack._workflows] - .reverse() - .filter(w => "function" === typeof w.getLastStep()._saveAll) // need to filter only workflow that - .map( w => new Promise(async (resolve) => { - const task = w.getLastStep(); - const fields = w.getContext().service.state.fields.filter(f => task._multi ? null !== f.value : true); - // skip when no fields - if (0 === fields.length) { return } - await Workflow.Stack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); - task._features.forEach(f => task.getInputs().layer.setFieldsWithValues(f, fields)); - const newFeatures = task._features.map(f => f.clone()); - //Is a relation form - if (task._isContentChild) { - task.getInputs().relationFeatures = { newFeatures, originalFeatures: task._originalFeatures }; - } - await task.fireEvent('saveform', { newFeatures, originalFeatures: task._originalFeatures }); - newFeatures.forEach((f, i) => task.getContext().session.pushUpdate(task.layerId, f, task._originalFeatures[i])); - await handleRelation1_1LayerFields({ layerId: task.layerId, features: newFeatures, fields, task }); - task.fireEvent('savedfeature', newFeatures); // called after saved - task.fireEvent(`savedfeature_${task.layerId}`, newFeatures); // called after saved using layerId - task.getContext().session.save(); - return resolve(); - })) - ) - try { - await promisify(g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').service.commit({ modal: false })); - [...Workflow.Stack._workflows] - .reverse() - .filter(w => "function" === typeof w.getLastStep()._saveAll) - .forEach(w => { - const service = w.getContext().service; //form service - //need to set update form false because already saved on server - service.setUpdate(false, { force: false }); - const feature = service.feature; - // Check if the feature is new. - // In this case, after commit, need to set new to false, and force update to false. - if (feature.isNew()) { - feature.state.new = false; - service.force.update = false; - } - Object.entries( - w.getInputs().layer.getEditingSource().readFeatures() - .find(f => f.getUid() === feature.getUid()) //Find current form editing feature by unique id of feature uid - .getProperties() //get properties - ) - .forEach(([k, v]) => { - const field = service.getFields().find(f => k === f.name); - //if field exists (geometry field is discarded) - if (field) { - field.value = field._value = v; - } - }) - }) - } catch(e) { - console.warn(e); + computed: { + /** @returns {boolean} whether disable save all button (eg. when parent or current form is not valid/ updated) */ + disabled() { + return !this.enabled || !(this.valid && this.update); + }, + }, + methods: { + async saveAll() { + //Set loading content + GUI.setLoadingContent(true); + //Disable form + GUI.disableContent(true); + await Promise.allSettled( + [...Workflow.Stack._workflows] + .reverse() + .filter(w => "function" === typeof w.getLastStep()._saveAll) // need to filter only workflow that + .map( w => new Promise(async (resolve) => { + const task = w.getLastStep(); + const fields = w.getContext().service.state.fields.filter(f => task._multi ? null !== f.value : true); + // skip when no fields + if (0 === fields.length) { return } + await Workflow.Stack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); + task._features.forEach(f => task.getInputs().layer.setFieldsWithValues(f, fields)); + const newFeatures = task._features.map(f => f.clone()); + //Is a relation form + if (task._isContentChild) { + task.getInputs().relationFeatures = { newFeatures, originalFeatures: task._originalFeatures }; + } + await task.fireEvent('saveform', { newFeatures, originalFeatures: task._originalFeatures }); + newFeatures.forEach((f, i) => task.getContext().session.pushUpdate(task.layerId, f, task._originalFeatures[i])); + await handleRelation1_1LayerFields({ layerId: task.layerId, features: newFeatures, fields, task }); + task.fireEvent('savedfeature', newFeatures); // called after saved + task.fireEvent(`savedfeature_${task.layerId}`, newFeatures); // called after saved using layerId + task.getContext().session.save(); + return resolve(); + })) + ) + try { + await promisify(g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').service.commit({ modal: false })); + [...Workflow.Stack._workflows] + .reverse() + .filter(w => "function" === typeof w.getLastStep()._saveAll) + .forEach(w => { + const service = w.getContext().service; //form service + //need to set update form false because already saved on server + service.setUpdate(false, { force: false }); + const feature = service.feature; + // Check if the feature is new. + // In this case, after commit, need to set new to false, and force update to false. + if (feature.isNew()) { + feature.state.new = false; + service.force.update = false; + } + Object.entries( + w.getInputs().layer.getEditingSource().readFeatures() + .find(f => f.getUid() === feature.getUid()) //Find current form editing feature by unique id of feature uid + .getProperties() //get properties + ) + .forEach(([k, v]) => { + const field = service.getFields().find(f => k === f.name); + //if field exists (geometry field is discarded) + if (field) { + field.value = field._value = v; + } + }) + }) + } catch(e) { + console.warn(e); + } + //set loading content false + GUI.setLoadingContent(false); + //enable form + GUI.disableContent(false); + }, + /** + * @since 3.9.0 + * Close editing form + */ + async closeForm() { + Workflow.Stack.clear(); } - //set loading content false - GUI.setLoadingContent(false); - //enable form - GUI.disableContent(false); }, }, - }, - buttons: [ - { - id: 'save', - title: this._isContentChild - ? Workflow.Stack.getParent().getBackButtonLabel() || "plugins.editing.form.buttons.save_and_back" // get custom back label from parent - : "plugins.editing.form.buttons.save", - type: "save", - class: "btn-success", - // save features - cbk: async (fields) => { - fields = this._multi ? fields.filter(f => null !== f.value) : fields; - - // skip when no fields - if (0 === fields.length) { - GUI.setModal(false); - resolve(inputs); - return; - } + buttons: [ + { + id: 'save', + title: this._isContentChild + ? Workflow.Stack.getParent().getBackButtonLabel() || "plugins.editing.form.buttons.save_and_back" // get custom back label from parent + : "plugins.editing.form.buttons.save", + type: "save", + class: "btn-success", + // save features + cbk: async (fields) => { + fields = this._multi ? fields.filter(f => null !== f.value) : fields; + + // skip when no fields + if (0 === fields.length) { + GUI.setModal(false); + resolve(inputs); + return; + } - const newFeatures = []; + const newFeatures = []; - // @since 3.5.15 - GUI.setLoadingContent(true); - GUI.disableContent(true); + // @since 3.5.15 + GUI.setLoadingContent(true); + GUI.disableContent(true); - await Workflow.Stack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); + await Workflow.Stack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); - GUI.setLoadingContent(false); - GUI.disableContent(false); + GUI.setLoadingContent(false); + GUI.disableContent(false); - this._features.forEach(f => { - inputs.layer.setFieldsWithValues(f, fields); - newFeatures.push(f.clone()); - }); + this._features.forEach(f => { + inputs.layer.setFieldsWithValues(f, fields); + newFeatures.push(f.clone()); + }); - if (this._isContentChild) { - inputs.relationFeatures = { - newFeatures, - originalFeatures: this._originalFeatures - }; - } + if (this._isContentChild) { + inputs.relationFeatures = { + newFeatures, + originalFeatures: this._originalFeatures + }; + } - await this.fireEvent('saveform', { newFeatures, originalFeatures: this._originalFeatures}); + await this.fireEvent('saveform', { newFeatures, originalFeatures: this._originalFeatures}); - newFeatures.forEach((f, i) => context.session.pushUpdate(this.layerId, f, this._originalFeatures[i])); + newFeatures.forEach((f, i) => context.session.pushUpdate(this.layerId, f, this._originalFeatures[i])); - // check and handle if layer has relation 1:1 - await handleRelation1_1LayerFields({ - layerId: this.layerId, - features: newFeatures, - fields, - task: this, - }); + // check and handle if layer has relation 1:1 + await handleRelation1_1LayerFields({ + layerId: this.layerId, + features: newFeatures, + fields, + task: this, + }); - GUI.setModal(false); + GUI.setModal(false); - this.fireEvent('savedfeature', newFeatures); // called after saved - this.fireEvent(`savedfeature_${this.layerId}`, newFeatures); // called after saved using layerId - // In case of save of child it means that child is updated so also parent - if (this._isContentChild) { - Workflow.Stack.getParents().forEach(w => w.getContextService().setUpdate(true, { force: true })); - } - //@TODO add field unique new value id not set - resolve(inputs); - } - }, - { - id: 'cancel', - title: "plugins.editing.form.buttons.cancel", - type: "cancel", - class: "btn-danger", - /// buttons in case of change - eventButtons: { - update: { - false : { - id: 'close', - title: "close", - type: "cancel", - class: "btn-danger", + this.fireEvent('savedfeature', newFeatures); // called after saved + this.fireEvent(`savedfeature_${this.layerId}`, newFeatures); // called after saved using layerId + // In case of save of child it means that child is updated so also parent + if (this._isContentChild) { + Workflow.Stack.getParents().forEach(w => w.getContextService().setUpdate(true, { force: true })); } + + //@TODO add field unique new value id not set + resolve(inputs); } }, - cbk: () => { - if (!this._isContentChild) { - GUI.setModal(false); - this.fireEvent('cancelform', inputs.features); // fire event cancel form to emit to subscrivers - } - reject(inputs); + { + id: 'cancel', + title: "plugins.editing.form.buttons.cancel", + type: "cancel", + class: "btn-danger", + /// buttons in case of change + eventButtons: { + update: { + false : { + id: 'close', + title: "close", + type: "cancel", + class: "btn-danger", + } + } + }, + cbk: reject } - } - ] - }); + ] + }); - // Overwrite click on relation. - // Open FormRelation.vue component - formService.handleRelation = async e => { - // Skip when multi editing features - // It is not possible to manage relationss when we edit multi-features - if (this._multi) { - GUI.showUserMessage({ type: 'info', message: 'plugins.editing.errors.editing_multiple_relations', duration: 3000, autoclose: true }); - return; + // Overwrite click on relation. + // Open FormRelation.vue component + formService.handleRelation = async e => { + // Skip when multi editing features + // It is not possible to manage relationss when we edit multi-features + if (this._multi) { + GUI.showUserMessage({ type: 'info', message: 'plugins.editing.errors.editing_multiple_relations', duration: 3000, autoclose: true }); + return; + } + GUI.setLoadingContent(true); + //set unique values for relation layer based on unique fields + //@TODO need a find a way to call once and not every time we open a relation + await setLayerUniqueFieldValues(inputs.layer.getRelationById(e.relation.name).getChild()); + formService.setCurrentComponentById(e.relation.name); + GUI.setLoadingContent(false); } - GUI.setLoadingContent(true); - //set unique values for relation layer based on unique fields - //@TODO need a find a way to call once and not every time we open a relation - await setLayerUniqueFieldValues(inputs.layer.getRelationById(e.relation.name).getChild()); - formService.setCurrentComponentById(e.relation.name); - GUI.setLoadingContent(false); - } - formService.addComponents([ - // custom form components - ...(g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').state.formComponents[layerId] || []), - // relation components (exlcude ONE relation + layer is the father get relation layers that set in editing on g3w-admin) - ...getRelationsInEditingByFeature({ - layerId, - relations: this._multi ? [] : inputs.layer.getRelations().getArray().filter(r => r.getType() !== 'ONE' && r.getFather() === layerId), - feature: this._multi ? false : inputs.features[inputs.features.length - 1], - }).map(({ relation, relations }) => ({ - title: "plugins.editing.edit_relation", - name: relation.name, - id: relation.id, - header: false, // hide a header form - component: Vue.extend({ - mixins: [ require('../components/FormRelation.vue') ], - name: `relation_${Date.now()}`, - data() { - return { layerId, relation, relations }; - }, - }), - })) - ]); + formService.addComponents([ + // custom form components + ...(g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').state.formComponents[layerId] || []), + // relation components (exlcude ONE relation + layer is the father get relation layers that set in editing on g3w-admin) + ...getRelationsInEditingByFeature({ + layerId, + relations: this._multi ? [] : inputs.layer.getRelations().getArray().filter(r => r.getType() !== 'ONE' && r.getFather() === layerId), + feature: this._multi ? false : inputs.features[inputs.features.length - 1], + }).map(({ relation, relations }) => ({ + title: "plugins.editing.edit_relation", + name: relation.name, + id: relation.id, + header: false, // hide a header form + component: Vue.extend({ + mixins: [ require('../components/FormRelation.vue') ], + name: `relation_${Date.now()}`, + data() { + return { layerId, relation, relations }; + }, + }), + })) + ]); + + // fire openform event + this.fireEvent('openform', + { + layerId: this.layerId, + session: context.session, + feature: this._originalFeature, + formService + } + ); - // fire openform event - this.fireEvent('openform', - { - layerId: this.layerId, - session: context.session, - feature: this._originalFeature, - formService + // set context service to form Service in case of a single task (i.e., no workflow) + if (Workflow.Stack.getCurrent()) { + Workflow.Stack.getCurrent().setContextService(formService); } - ); - // set context service to form Service in case of a single task (i.e., no workflow) - if (Workflow.Stack.getCurrent()) { - Workflow.Stack.getCurrent().setContextService(formService); - } + //listen eventually field relation 1:1 changes value + listenRelation1_1FieldChange({ layerId: this.layerId, fields }).then(d => this._unwatchs = d); - //listen eventually field relation 1:1 changes value - listenRelation1_1FieldChange({ layerId: this.layerId, fields }).then(d => this._unwatchs = d); + this.disableSidebar(true); + }); - this.disableSidebar(true); - }) - return $promisify(async () => { setAndUnsetSelectedFeaturesStyle({ promise: $promisify(promise), inputs, style: this.selectStyle }); return promise; }); @@ -761,7 +784,6 @@ export class OpenFormStep extends Step { 2 === Workflow.Stack.getLength() && //open features table Workflow.Stack.getParent().isType('edittable') ); - // when the last feature of features is Array // and is resolved without setting form service // Ex. copy multiple features from another layer @@ -775,6 +797,7 @@ export class OpenFormStep extends Step { if (contextService && false === this._isContentChild) { contextService.setUpdate(false, { force: false }); } + //@since 3.9.0 add GUI.getContentLength() in case of edit multi relationfeatures tool GUI.closeForm({ pop: this.push || this._isContentChild && GUI.getContentLength() > 1 }); @@ -787,6 +810,10 @@ export class OpenFormStep extends Step { this._unwatchs.forEach(unwatch => unwatch()); this._unwatchs = []; + if (!this._isContentChild) { + GUI.setModal(false); + this.fireEvent('cancelform', this.getInputs().features); // fire event cancel form to emit to subscrivers + } } } From 84a592280bc86b469d4175f20c353561a91716fb Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 3 Dec 2024 09:15:11 +0100 Subject: [PATCH 10/30] Align close button behavior with dev --- workflows/index.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/workflows/index.js b/workflows/index.js index 71a76275..1d54e541 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -700,7 +700,13 @@ export class OpenFormStep extends Step { } } }, - cbk: reject + cbk: () => { + if (!this._isContentChild) { + GUI.setModal(false); + this.fireEvent('cancelform', inputs.features); // fire event cancel form to emit to subscrivers + } + reject(inputs); + } } ] }); @@ -809,11 +815,6 @@ export class OpenFormStep extends Step { this.layerId = null; this._unwatchs.forEach(unwatch => unwatch()); this._unwatchs = []; - - if (!this._isContentChild) { - GUI.setModal(false); - this.fireEvent('cancelform', this.getInputs().features); // fire event cancel form to emit to subscrivers - } } } From fec6b77e2780dd3df41417b175154289a1cb2fc3 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 3 Dec 2024 09:48:02 +0100 Subject: [PATCH 11/30] Remove setModal false --- workflows/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/workflows/index.js b/workflows/index.js index 9f96a2f8..23f935b0 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -603,7 +603,6 @@ export class OpenFormStep extends Step { // skip when no fields if (0 === fields.length) { - GUI.setModal(false); resolve(inputs); return; } From 611b4ce68e3bb5cca6877edfa7ec9808698c5fc5 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 3 Dec 2024 10:01:15 +0100 Subject: [PATCH 12/30] Create internal step method stopRun --- g3wsdk/workflow/step.js | 9 +++++++++ g3wsdk/workflow/workflow.js | 6 ++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/g3wsdk/workflow/step.js b/g3wsdk/workflow/step.js index 652f76e0..87e6de57 100644 --- a/g3wsdk/workflow/step.js +++ b/g3wsdk/workflow/step.js @@ -508,6 +508,15 @@ export class Step extends G3WObject { this.emit('stop'); } + /** + * @since 3.9.0 + * @return {Promise} + */ + async stopRun() { + this._rejectRun(); + await new Promise((resolve) => this.once('stop', resolve)) + } + /** * @FIXME add description */ diff --git a/g3wsdk/workflow/workflow.js b/g3wsdk/workflow/workflow.js index 7facb32b..8e8132aa 100644 --- a/g3wsdk/workflow/workflow.js +++ b/g3wsdk/workflow/workflow.js @@ -441,10 +441,8 @@ export class Workflow extends G3WObject { if (step.isRunning()) { //clear messages steps this.clearMessages(); - //reject a current step - step._rejectRun(); - //wait stop - await new Promise((_, reject) => { step.once('stop', reject)}) + //wait stop run + await step.stopRun(); } // reset counter and reject flow if (this._stepIndex > 0) { From daf91d9bd2dd098d07db8525bcac70f8ae10d389 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Thu, 5 Dec 2024 15:26:44 +0100 Subject: [PATCH 13/30] :sparkles: Show modal select window to choose whild features of select relation you want edit --- toolboxes/toolbox.js | 73 +++++++++++++++++++++++++++++++++++++++----- workflows/index.js | 10 ++++-- 2 files changed, 72 insertions(+), 11 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 9606106a..22f6ea51 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -424,10 +424,10 @@ export class ToolBox extends G3WObject { /** ORIGINAL SOURCE: g3w-client-plugin-editing/workflows/editmultifeatureattributesworkflow.js@v3.7.1 */ op: new Workflow({ layer, - type: 'editmultiattributesrelationfeatures', - helpMessage: 'editing.tools.update_multi_features', + type: 'editmultiattributesrelationfeatures', + helpMessage: 'editing.tools.update_multi_features', registerEscKeyEvent: true, - runOnce: true, + runOnce: false, steps: [ new SelectElementsStep({ type: 'multiple', @@ -455,11 +455,65 @@ export class ToolBox extends G3WObject { feature, filterType: 'fid', }))) + //get first relation layer id + let relationLayerId = relations[0].getChild(); //In case of multi relation in editing if (relations.length > 1) { - alert('Choose relations') + //ser relation layer id + try { + await new Promise((resolve, reject) => { + const vueInstance = new (Vue.extend({ + name: 'multi-relations-fetures', + template: `
+ +
+ `, + data() { + return { + relations: this.$options.relations, + relationId: this.$options.relationId + } + } + }))({ relations, relationId: relations[0].state.id }) + + GUI.showModalDialog({ + title: tPlugin('editing.relations'), + className: 'modal-left', + closeButton: false, + message: vueInstance.$mount().$el, + buttons: { + cancel: { + label: 'Cancel', + className: 'btn-danger', + callback() { reject(); } + }, + ok: { + label: 'Ok', + className: 'btn-success', + callback: async () => { + //set relation layer id to editin + relationLayerId = relations.find(r => vueInstance.relationId === r.state.id).getChild(); + resolve(); + } + } + } + }).on('hide.bs.modal', () => vueInstance.$destroy()); //destroy vue instance after dialog is a closed + //hide user message step + }) + } catch(e) { + console.warn(e); + GUI.setModal(false); + return $promisify(Promise.reject(e)); + } } + //start child workflow const workflow = new Workflow({ type: 'editmultiattributes', @@ -468,7 +522,7 @@ export class ToolBox extends G3WObject { ], }); //Relations layer - const rLayer = getEditingLayerById(relations[0].getChild()); + const rLayer = getEditingLayerById(relationLayerId); const fields = getRelationFieldsFromRelation({ layerId: relations[0].getChild(), @@ -477,16 +531,19 @@ export class ToolBox extends G3WObject { const options = { context: { - session: Workflow.Stack.getCurrent().getSession(), // get parent workflow - excludeFields: fields.ownField, // array of fields to be excluded + session: Workflow.Stack.getCurrent().getSession(), // get parent workflow + excludeFields: fields.ownField, // array of fields to be excluded + isContentChild: false, //@since 3.9.0 force child to flase }, inputs: { features: rLayer.readFeatures(), layer: rLayer } - }; + } try { + //set eventually unique values + await setLayerUniqueFieldValues(relationLayerId); await promisify(workflow.start(options)); } catch(e) { console.warn(e); diff --git a/workflows/index.js b/workflows/index.js index 23f935b0..ce07a906 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -403,7 +403,8 @@ export class OpenFormStep extends Step { */ run(inputs, context) { const promise = new Promise(async (resolve, reject) => { - this._isContentChild = Workflow.Stack.getLength() > 1; + //@since 3.9.0 can set isContentChild attribute to force it (case edit relation features from multi parent features) + this._isContentChild = undefined === context.isContentChild ? Workflow.Stack.getLength() > 1 : context.isContentChild; this.layerId = inputs.layer.getId(); GUI.setLoadingContent(false); @@ -648,7 +649,10 @@ export class OpenFormStep extends Step { this.fireEvent(`savedfeature_${this.layerId}`, newFeatures); // called after saved using layerId // In case of save of child it means that child is updated so also parent if (this._isContentChild) { - Workflow.Stack.getParents().forEach(w => w.getContextService().setUpdate(true, { force: true })); + Workflow.Stack.getParents() + //filter only with has getContextService to be sure + .filter(w => w.getContextService() && w.getContextService().setUpdate) + .forEach(w => w.getContextService().setUpdate(true, { force: true })); } //@TODO add field unique new value id not set resolve(inputs); @@ -771,7 +775,7 @@ export class OpenFormStep extends Step { const contextService = is_parent_table && Workflow.Stack.getCurrent().getContextService(); // force update parent form update - if (contextService && false === this._isContentChild) { + if (contextService && contextService.setUpdate && false === this._isContentChild) { contextService.setUpdate(false, { force: false }); } //@since 3.9.0 add GUI.getContentLength() in case of edit multi relationfeatures tool From 66822c83d69caa3db6aa0dddc32279b97a4a465a Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 6 Dec 2024 12:06:26 +0100 Subject: [PATCH 14/30] :bug: get relation only editable, not also visible --- toolboxes/toolbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 22f6ea51..2c5d0aee 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -92,7 +92,7 @@ export class ToolBox extends G3WObject { const editable_relations = layer.getRelations().getArray() .filter(relation => { const l = CatalogLayersStoresRegistry.getLayerById(getRelationId({ layerId: layer.getId(), relation })); - return l.isEditable() && l.config.editing.visible; + return l.isEditable(); }) .map(r => r); this._start = false; From 4dcfa5ac175e591dd12b75ef359106941dc6309c Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 6 Dec 2024 12:22:53 +0100 Subject: [PATCH 15/30] :globe_with_meridians:translation --- i18n/de.js | 1 + i18n/en.js | 1 + i18n/fi.js | 1 + i18n/fr.js | 1 + i18n/it.js | 1 + i18n/pl.js | 1 + i18n/ro.js | 1 + i18n/se.js | 1 + toolboxes/toolbox.js | 4 ++-- 9 files changed, 10 insertions(+), 2 deletions(-) diff --git a/i18n/de.js b/i18n/de.js index de265b8d..d7ad6480 100644 --- a/i18n/de.js +++ b/i18n/de.js @@ -42,6 +42,7 @@ export default { update_feature: "Feature-Attribut aktualisieren", update_multi_features: "Attribute ausgewählter Features aktualisieren", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Bearbeiten Sie Beziehungsdatensätze von einem oder mehreren übergeordneten features", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/en.js b/i18n/en.js index 2b72cc7b..5f5a6cae 100644 --- a/i18n/en.js +++ b/i18n/en.js @@ -44,6 +44,7 @@ export default { update_feature: "Update feature attribute", update_multi_features: "Update attributes of selected features", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Edit relations records from one or mode parent features", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/fi.js b/i18n/fi.js index f1fd6af5..486a5126 100644 --- a/i18n/fi.js +++ b/i18n/fi.js @@ -42,6 +42,7 @@ export default { update_feature: "Päivitä ominaisuus", update_multi_features: "Muokkaa valittujen ominaisuuksien attribuutteja", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Edit relations records from one or mode parent features", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/fr.js b/i18n/fr.js index e495c1e9..4990dbd3 100644 --- a/i18n/fr.js +++ b/i18n/fr.js @@ -42,6 +42,7 @@ export default { update_feature: "Modifier les attributs des fonctionnalités", update_multi_features: "Modifier les attributs des fonctionnalités sélectionnées", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Modifier les enregistrements de relation à partir d'une ou plusieurs entités parents", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/it.js b/i18n/it.js index 9bffae24..6681e9fe 100644 --- a/i18n/it.js +++ b/i18n/it.js @@ -44,6 +44,7 @@ export default { update_feature: "Modifica attributi elemento", update_multi_features: "Modifica gli attributi degli elementi selezionati", update_multi_features_relations: "Modifica gli attributi di tutte le relazioni selezionate", + update_multi_features_relations_from_parents : "Edita i record relazionati di uno o più padri", copyfeaturefromexternallayer: "Crea elemento da un livello esterno" }, toolsoftool: { diff --git a/i18n/pl.js b/i18n/pl.js index 65f2b92c..e61416b0 100644 --- a/i18n/pl.js +++ b/i18n/pl.js @@ -39,6 +39,7 @@ export default { update_feature: "Update feature attribute", update_multi_features: "Update attributes of selected features", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Edit relations records from one or mode parent features", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/ro.js b/i18n/ro.js index 5a2a092d..3555cc26 100644 --- a/i18n/ro.js +++ b/i18n/ro.js @@ -42,6 +42,7 @@ export default { update_feature: "Actualizează atributul entității", update_multi_features: "Actualizează atributele entităților selectate", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Editați înregistrările relațiilor de la una sau mai multe caracteristici părinte", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/i18n/se.js b/i18n/se.js index 43b60a11..48663441 100644 --- a/i18n/se.js +++ b/i18n/se.js @@ -42,6 +42,7 @@ export default { update_feature: "Uppdatera egenskap", update_multi_features: "Ändra attributen för de valda funktionerna", update_multi_features_relations: "Update attributes of all selected relations", + update_multi_features_relations_from_parents : "Edit relations records from one or mode parent features", copyfeaturefromexternallayer: "Create Feature from added layer" }, toolsoftool: { diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 2c5d0aee..19f7b59e 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -419,13 +419,13 @@ export class ToolBox extends G3WObject { (is_vector) && capabilities.includes('change_attr_feature') && editable_relations.filter(r => 'ONE' !== r.getType()).length > 0 && { id: 'editmultiattributesrelationfeatures', type: ['change_attr_feature'], - name: "editing.tools.update_multi_features", + name: "editing.tools.update_multi_features_relations_from_parents", icon: "EditMultiRelationFeatures.png", /** ORIGINAL SOURCE: g3w-client-plugin-editing/workflows/editmultifeatureattributesworkflow.js@v3.7.1 */ op: new Workflow({ layer, type: 'editmultiattributesrelationfeatures', - helpMessage: 'editing.tools.update_multi_features', + helpMessage: 'editing.tools.update_multi_features_relations_from_parents', registerEscKeyEvent: true, runOnce: false, steps: [ From 7b9a44aa7cc951c704a1b31823c1744449fdd2b1 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 6 Dec 2024 12:42:49 +0100 Subject: [PATCH 16/30] :lipstick: run Once --- toolboxes/toolbox.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 19f7b59e..073758a8 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -427,7 +427,7 @@ export class ToolBox extends G3WObject { type: 'editmultiattributesrelationfeatures', helpMessage: 'editing.tools.update_multi_features_relations_from_parents', registerEscKeyEvent: true, - runOnce: false, + runOnce: true, steps: [ new SelectElementsStep({ type: 'multiple', From f015f978697d171e960b7dc63a8bf128f267a254 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 10 Dec 2024 10:37:34 +0100 Subject: [PATCH 17/30] Remove copy feature form external layer tool. Unused --- toolboxes/toolbox.js | 42 ------------------------------------------ 1 file changed, 42 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 073758a8..4d0f83c1 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -1277,48 +1277,6 @@ export class ToolBox extends G3WObject { registerEscKeyEvent: true }), }, - // Copy Features from external layer - (is_line || is_poly) && capabilities.includes('add_feature') && { - id: 'copyfeaturefromexternallayer', - type: ['add_feature'], - name: "editing.tools.copyfeaturefromexternallayer", - icon: "copyPolygonFromFeature.png", - visible: tool => { - const map = GUI.getService('map'); - const type = this.getLayer().getGeometryType(); - const has_same_geom = layer => { - // check if tool is visible and the layer is a Vector - const features = 'VECTOR' === layer.getType() && layer.getSource().getFeatures(); - return features && features.length ? isSameBaseGeometryType(features[0].getGeometry().getType(), type) : true; - }; - map.onbefore('loadExternalLayer', layer => !tool.visible && (tool.visible = has_same_geom(layer))); - map.onafter('unloadExternalLayer', layer => { - const features = tool.visible && 'VECTOR' === layer.getType() && layer.getSource().getFeatures(); - if (features && features.length && isSameBaseGeometryType(features[0].getGeometry().getType(), type)) { - tool.visible = map.getExternalLayers().find(l => undefined !== has_same_geom(l)); - } - }); - return false; - }, - /** ORIGINAL SOURCE: g3w-client-plugin-editing/workflows/addfeaturefrommapvectorlayersworkflow.js@v3.7.1 */ - op: new Workflow({ - layer, - type: 'addfeaturefrommapvectorlayers', - runOnce: true, - steps: [ - new SelectElementsStep({ - layer, - type: 'external', - help: 'editing.steps.help.copy' - }, false), - new OpenFormStep({ - layer, - help: 'editing.steps.help.copy' - }), - ], - registerEscKeyEvent: true - }), - }, // Add Table feature (alphanumerical layer - No geometry) is_table && capabilities.includes('add_feature') && { id: 'addfeature', From 77381c152dcece37c3d48ac2ccfced8d48fb0677 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Tue, 10 Dec 2024 10:41:05 +0100 Subject: [PATCH 18/30] :recycle: Handle start toolbox session uniform --- toolboxes/toolbox.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 4d0f83c1..e612d3f4 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -1616,7 +1616,7 @@ export class ToolBox extends G3WObject { this.startLoading(); this.setFeaturesOptions({ filter }); try { - handlerAfterSessionGetFeatures(await promisify(this._session.start(this.state._getFeaturesOption))) + await handlerAfterSessionGetFeatures(promisify(this._session.start(this.state._getFeaturesOption))) } catch(e) { console.warn(e); this.setEditing(false); @@ -1629,12 +1629,12 @@ export class ToolBox extends G3WObject { if (!is_started && !GIVE_ME_A_NAME) { this._start = true; this.startLoading(); - this._session.start(this.state._getFeaturesOption).then(handlerAfterSessionGetFeatures) + await handlerAfterSessionGetFeatures(promisify(this._session.start(this.state._getFeaturesOption))) } if (is_started && !this._start) { this.startLoading(); - this._session.getFeatures(this.state._getFeaturesOption).then(handlerAfterSessionGetFeatures); + await handlerAfterSessionGetFeatures(promisify(this._session.getFeatures(this.state._getFeaturesOption))) this._start = true; } From 96bbe9bb7554b32cd128effa0412ccd0636e45aa Mon Sep 17 00:00:00 2001 From: volterra79 Date: Wed, 11 Dec 2024 10:56:47 +0100 Subject: [PATCH 19/30] Comment getEditingMediaFields to copy also attach (media, pdf,etcc.) of clone of feature --- toolboxes/toolbox.js | 3 ++- utils/handleSplitFeature.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index e612d3f4..58f03a11 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -887,7 +887,8 @@ export class ToolBox extends G3WObject { feature.getGeometry().translate(deltaXY.x, deltaXY.y) } // set media fields to null - layer.getEditingMediaFields({}).forEach(f => feature.set(f, null)); + //@since 3.9.0 Comment + //layer.getEditingMediaFields({}).forEach(f => feature.set(f, null)); /** * evaluated geometry expression */ diff --git a/utils/handleSplitFeature.js b/utils/handleSplitFeature.js index 84e2a0fc..74362c30 100644 --- a/utils/handleSplitFeature.js +++ b/utils/handleSplitFeature.js @@ -52,7 +52,8 @@ export async function handleSplitFeature({ newFeature.setGeometry(splittedGeometry); // set media fields to null - layer.getEditingMediaFields({}).forEach(f => newFeature.set(f, null)); + //@since 3.9.0 Commented + //layer.getEditingMediaFields({}).forEach(f => newFeature.set(f, null)); feature = new Feature({ feature: newFeature }); From b733b10ee421786bfec14fb498143dcd5a5640a6 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Wed, 11 Dec 2024 15:59:56 +0100 Subject: [PATCH 20/30] :bug: In case of no relations, show user message and stop --- toolboxes/toolbox.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 58f03a11..2103126d 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -524,6 +524,17 @@ export class ToolBox extends G3WObject { //Relations layer const rLayer = getEditingLayerById(relationLayerId); + if (0 === rLayer.readFeatures().length) { + GUI.setModal(false); + + GUI.showUserMessage({ + type: 'warning', + message: 'plugins.editing.no_relations_found', + autoclose: true, + }) + return $promisify(Promise.reject()); + } + const fields = getRelationFieldsFromRelation({ layerId: relations[0].getChild(), relation: relations[0] From 29fa953de03011428001b318a7a29f4467850be1 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Thu, 12 Dec 2024 17:51:52 +0100 Subject: [PATCH 21/30] :sparkles: Referred to https://github.com/g3w-suite/g3w-admin/pull/991 --- components/FormRelation.vue | 6 +++--- deprecated.js | 6 +++--- g3wsdk/editing/editor.js | 37 +++++++++++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/components/FormRelation.vue b/components/FormRelation.vue index 582afb10..3c85c584 100644 --- a/components/FormRelation.vue +++ b/components/FormRelation.vue @@ -572,14 +572,14 @@ * * @since g3w-client-plugin-editing@v3.7.4 */ - onCommit({ new_relations = {} }) { + onCommit({ relations = {} }) { const relationLayer = getEditingLayerById(this.relation.child); // there is a new relation saved on server - if (new_relations[relationLayer.getId()] && Array.isArray(new_relations[relationLayer.getId()].new)) { + if (relations[relationLayer.getId()] && Array.isArray(relations[relationLayer.getId()].new)) { this._new_relations_ids = [ ...(this._new_relations_ids || []), - ...new_relations[relationLayer.getId()].new.map(({ clientid, id }) => ({ clientid, id })) + ...relations[relationLayer.getId()].new.map(({ clientid, id }) => ({ clientid, id })) ] } }, diff --git a/deprecated.js b/deprecated.js index 9ce6c95f..21bc13c0 100644 --- a/deprecated.js +++ b/deprecated.js @@ -814,15 +814,15 @@ export class Session extends g3wsdk.core.G3WObject { return; } - const { new_relations = {} } = response.response; // check if new relations are saved on server + const { relations = {} } = response.response; // check if new relations are saved on server // sync server data with local data - for (const id in new_relations) { + for (const id in relations) { Session.Registry .getSession(id) // get session of relation by id .getEditor() .applyCommitResponse({ // apply commit response to current editing relation layer - response: new_relations[id], + response: relations[id], result: true }); } diff --git a/g3wsdk/editing/editor.js b/g3wsdk/editing/editor.js index af687128..a0f2cd07 100644 --- a/g3wsdk/editing/editor.js +++ b/g3wsdk/editing/editor.js @@ -8,6 +8,8 @@ import { ToolBox } from '../../toolboxes/toolbox'; import { promisify, $promisify } from '../../utils/promisify'; +import { getRelationsInEditing } from "../../utils/getRelationsInEditing"; +import {getRelationId} from "editing/utils/getRelationId"; const { ApplicationState, G3WObject } = g3wsdk.core; const { FeaturesStore } = g3wsdk.core.layer.features; @@ -320,7 +322,7 @@ export default class Editor extends G3WObject { // properties - properties of feature returned by server response.response.new.forEach(({ clientid, id, properties } = {}) => { //get feature from current layer in editing - const feature = this._featuresstore.getFeatureById(clientid); + const feature = this.getEditingSource().getFeatureById(clientid); // set new id feature.setId(id); //set properties @@ -346,6 +348,33 @@ export default class Editor extends G3WObject { }); + //@since 3.9.0 take in account update properties returned by server (Useful in case of media input changes) + (response.response.update || []).forEach(({ id, properties } = {}) => { + //get feature from current layer in editing + const feature = this.getEditingSource().getFeatureById(id); + //set properties + feature.setProperties(properties); + //Loop on eventual relation updated or created + relations.forEach(r => { // handle relations (if provided) + Object + .entries(r) + .forEach(([ id, opts = {}]) => { // id - relation layer id, opts - Object contain relation properties + //get the editing source of relation layer + const source = ToolBox.get(id).getSession().getEditor().getEditingSource(); + // handle value to relation field saved on server + (opts.ids || []).forEach(id => { + const rFeature = source.getFeatureById(id); + if (rFeature) { + opts.fatherField.forEach((ff, i) => {// loop relation ids + rFeature.set(opts.childField[i], feature.get(ff)) // set father feature `value` and `name` + }) + } + }) + }); + }); + + }); + const features = this.readEditingFeatures(); features.forEach(f => f.clearState()); // reset state of the editing features (update, new etc..) @@ -439,10 +468,10 @@ export default class Editor extends G3WObject { */ stop() { return $promisify(async () => { - const response = await promisify(this._layer.unlock()); + const { result } = await promisify(this._layer.unlock()); this.clear(); - return response; - }); + return result; + }) } /** From 2997529cf5a1d0c4f9371b22834c165407a595e0 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Thu, 12 Dec 2024 17:53:11 +0100 Subject: [PATCH 22/30] :bug: Unlock relations layers in case of editing relation by parent layer --- toolboxes/toolbox.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 2103126d..847751a3 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -1686,9 +1686,6 @@ export class ToolBox extends G3WObject { //eventually reset start resolve feature waiting promise this.startResolve = null; - //set start to false - this._start = false - this.state.editing.on = false; if (this.state._constraints.scale) { this._handleScaleConstraint(true); @@ -1728,6 +1725,9 @@ export class ToolBox extends G3WObject { try { await promisify(this._session.stop()); + //set start to false + this._start = false + this.state.editing.on = false; this.state.enabled = false; this.stopLoading(); this.state._getFeaturesOption = {}; @@ -1738,7 +1738,7 @@ export class ToolBox extends G3WObject { // clear layer unique field values g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').state.uniqueFieldsValues[this.getId()] = {}; return true; - } catch (e) { + } catch(e) { console.warn(e); return Promise.reject(e); } @@ -1792,16 +1792,16 @@ export class ToolBox extends G3WObject { return; } - const { new_relations = {} } = response.response; // check if new relations are saved on server + const { relations = {} } = response.response; // check if relations are saved on server // sync server data with local data - for (const id in new_relations) { + for (const id in relations) { const toolbox = ToolBox.get(id) toolbox .getSession() .getEditor() .applyCommitResponse({ // apply commit response to current editing relation layer - response: new_relations[id], + response: relations[id], result: true }); } @@ -2937,7 +2937,6 @@ export class ToolBox extends G3WObject { console.warn(e); return Promise.reject(e); } finally { - if (!this.inEditing()) { return; } if (ApplicationState.online) { this._stopSessionChildren(this.state.id); } From 127b1c6441fa0c3ecc0ec3615d2bcb04b25af81e Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 13 Dec 2024 13:50:58 +0100 Subject: [PATCH 23/30] Remove return Promise.reject because cause a vue error --- components/FormRelation.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/components/FormRelation.vue b/components/FormRelation.vue index 3c85c584..2d50d603 100644 --- a/components/FormRelation.vue +++ b/components/FormRelation.vue @@ -906,11 +906,10 @@ await promise; } catch (e) { console.trace('START TOOL FAILED', e); - return Promise.reject(e); } finally { relationtool.state.active = false; } - } catch (e) { + } catch(e) { console.warn(e); } }, From 2290dee494be9b87389a9aa7fffde0089e40f4f1 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 13 Dec 2024 14:36:19 +0100 Subject: [PATCH 24/30] Remove unusefull new --- components/FormRelation.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/FormRelation.vue b/components/FormRelation.vue index 2d50d603..c4beaf0f 100644 --- a/components/FormRelation.vue +++ b/components/FormRelation.vue @@ -396,7 +396,7 @@ const is_vector = (external || layer.isGeoLayer()) this.runAddRelationWorkflow({ workflow: is_vector - ? new this._add_link_workflow.selectandcopy({ + ? this._add_link_workflow.selectandcopy({ copyLayer: layer, isVector: true, help: 'editing.steps.help.copy', @@ -421,7 +421,7 @@ */ addVectorRelation() { this.runAddRelationWorkflow({ - workflow: new this._add_link_workflow.add(), + workflow: this._add_link_workflow.add(), isVector: Layer.LayerTypes.VECTOR === this._layerType, }); this.show_vector_tools = false; @@ -473,7 +473,7 @@ this.resize(); } else { this.runAddRelationWorkflow({ - workflow: new this._add_link_workflow.add(), + workflow: this._add_link_workflow.add(), isVector: Layer.LayerTypes.VECTOR === this._layerType, }); } @@ -1028,7 +1028,7 @@ this.disabled = true; const is_vector = Layer.LayerTypes.VECTOR === this._layerType; - const workflow = new this._add_link_workflow.link( is_vector ? { + const workflow = this._add_link_workflow.link( is_vector ? { selectStyle: SELECTED_STYLES[this.getLayer().getGeometryType()] } : {}); const options = this._createWorkflowOptions(); From e4e482ad35184aa9ddb15874d7e613d174ed9548 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 16 Dec 2024 17:15:34 +0100 Subject: [PATCH 25/30] :lipstick: display save and close --- workflows/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/workflows/index.js b/workflows/index.js index 210144b6..b98bf929 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -485,7 +485,7 @@ export class OpenFormStep extends Step { /** @TODO make it straightforward: `headerComponent` vs `buttons` ? */ headerComponent: this._saveAll && { template: /* html */ ` -
+
Date: Fri, 20 Dec 2024 12:51:22 +0100 Subject: [PATCH 26/30] :bug: Rollback _rejectRun attribute --- g3wsdk/workflow/step.js | 19 ++----------------- g3wsdk/workflow/workflow.js | 3 ++- workflows/index.js | 2 -- 3 files changed, 4 insertions(+), 20 deletions(-) diff --git a/g3wsdk/workflow/step.js b/g3wsdk/workflow/step.js index 87e6de57..58ae21be 100644 --- a/g3wsdk/workflow/step.js +++ b/g3wsdk/workflow/step.js @@ -36,9 +36,6 @@ export class Step extends G3WObject { //store promise of current running step when call stop this._stop = (options.stop || this.stop || (async () => true)).bind(this); - /** since 3.9.0 */ - this._rejectRun = null; - /** * @FIXME add description */ @@ -477,18 +474,15 @@ export class Step extends G3WObject { try { this.state.running = true; // change state to running - const runPromise = promisify(this._run(inputs, context)); - return await (this._rejectRun ? runPromise : Promise.race([new Promise((_, reject) => this._rejectRun = reject), runPromise])); + return await promisify(this._run(inputs, context)); } catch(e) { console.warn(e); this.state.error = e; return Promise.reject(e); } finally { - //always call stop this.__stop(); - this._rejectRun = null; } - }) + }); } /** @@ -508,15 +502,6 @@ export class Step extends G3WObject { this.emit('stop'); } - /** - * @since 3.9.0 - * @return {Promise} - */ - async stopRun() { - this._rejectRun(); - await new Promise((resolve) => this.once('stop', resolve)) - } - /** * @FIXME add description */ diff --git a/g3wsdk/workflow/workflow.js b/g3wsdk/workflow/workflow.js index 8e8132aa..e1907ccf 100644 --- a/g3wsdk/workflow/workflow.js +++ b/g3wsdk/workflow/workflow.js @@ -442,12 +442,13 @@ export class Workflow extends G3WObject { //clear messages steps this.clearMessages(); //wait stop run - await step.stopRun(); + await step.__stop(); } // reset counter and reject flow if (this._stepIndex > 0) { this._stepIndex = 0; reject(); + return Promise.reject(); } else { resolve(); } diff --git a/workflows/index.js b/workflows/index.js index b98bf929..bf6ee876 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -404,8 +404,6 @@ export class OpenFormStep extends Step { run(inputs, context) { return $promisify(async () => { const promise = new Promise(async (resolve, reject) => { - //@since 3.9.0 - this._rejectRun = reject; //@since 3.9.0 can set isContentChild attribute to force it (case edit relation features from multi parent features) this._isContentChild = undefined === context.isContentChild ? Workflow.Stack.getLength() > 1 : context.isContentChild; this.layerId = inputs.layer.getId(); From c6c2b6f72eebda5491519a2734c68b2c9b57f9cf Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 30 Dec 2024 11:49:53 +0100 Subject: [PATCH 27/30] :bug: Fix unselect feature after close depp relation form --- workflows/index.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/workflows/index.js b/workflows/index.js index bf6ee876..e9757b25 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -403,10 +403,25 @@ export class OpenFormStep extends Step { */ run(inputs, context) { return $promisify(async () => { - const promise = new Promise(async (resolve, reject) => { - //@since 3.9.0 can set isContentChild attribute to force it (case edit relation features from multi parent features) - this._isContentChild = undefined === context.isContentChild ? Workflow.Stack.getLength() > 1 : context.isContentChild; - this.layerId = inputs.layer.getId(); + //@since 3.9.0 can set isContentChild attribute to force it + // (case edit relation features from multi-parent features) + this._isContentChild = undefined === context.isContentChild ? Workflow.Stack.getLength() > 1 : context.isContentChild; + this.layerId = inputs.layer.getId(); + this._features = this._multi ? inputs.features : [inputs.features[inputs.features.length - 1]]; + this._originalFeatures = this._features.map(f => f.clone()); + + //@since 3.9.0 promise + const promise = new Promise((resolve) => { + g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').subscribe(`closeform_${this.layerId}`, () => { + resolve(); + return { once: true }; // once close form, remove subscribing + }) + }) + + //set selected features + setAndUnsetSelectedFeaturesStyle({ promise: $promisify(promise), inputs, style: this.selectStyle }); + + return new Promise(async (resolve, reject) => { GUI.setLoadingContent(false); @@ -420,8 +435,6 @@ export class OpenFormStep extends Step { g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').setCurrentLayout(); const layerName = inputs.layer.getName(); - this._features = this._multi ? inputs.features : [inputs.features[inputs.features.length - 1]]; - this._originalFeatures = this._features.map(f => f.clone()); // create a child relation feature set a father relation field value if (this._isContentChild) { @@ -509,13 +522,13 @@ export class OpenFormStep extends Step { style = "background-color: #fff; display: flex; justify-content: flex-end; width: 100%;" >
@@ -771,9 +784,6 @@ export class OpenFormStep extends Step { this.disableSidebar(true); }); - - setAndUnsetSelectedFeaturesStyle({ promise: $promisify(promise), inputs, style: this.selectStyle }); - return promise; }); } From 66f23659cd79d29c8a2f305869d9041719add212 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Mon, 30 Dec 2024 15:00:22 +0100 Subject: [PATCH 28/30] :recycle: Move dissolve method to g3w-client core --- toolboxes/toolbox.js | 3 +- utils/dissolve.js | 81 -------------------------------------------- 2 files changed, 1 insertion(+), 83 deletions(-) delete mode 100644 utils/dissolve.js diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index 847751a3..b0a63963 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -25,7 +25,6 @@ import { promisify, $promisify } from '../utils/promisify import { unlinkRelation } from '../utils/unlinkRelation'; import { splitFeatures } from '../utils/splitFeatures'; import { isSameBaseGeometryType } from '../utils/isSameBaseGeometryType'; -import { dissolve } from '../utils/dissolve'; import { PickFeaturesInteraction } from '../interactions/pickfeaturesinteraction'; import { @@ -59,7 +58,7 @@ const { const { ProjectsRegistry } = g3wsdk.core.project; const { DataRouterService } = g3wsdk.core.data; const { CatalogLayersStoresRegistry } = g3wsdk.core.catalog; -const { Geometry } = g3wsdk.core.geoutils; +const { Geometry, dissolve } = g3wsdk.core.geoutils; const { removeZValueToOLFeatureGeometry } = g3wsdk.core.geoutils.Geometry; const { tPlugin } = g3wsdk.core.i18n; const { Layer } = g3wsdk.core.layer; diff --git a/utils/dissolve.js b/utils/dissolve.js deleted file mode 100644 index 98de4a85..00000000 --- a/utils/dissolve.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * ORIGINAL SOURCE: g3w-client/src/utils/dissolve.js@v3.10.2 - * - * @param { Object } opts - * @param { Array } opts.features - * @param { number } opts.index - * @param { boolean } opts.clone - * - * @returns dissolved feature - * - * @since g3w-client-plugin-editing@v3.9.0 - */ -export function dissolve({ - features = [], - index = 0, - clone = false, -} = {}) { - - const parser = new jsts.io.OL3Parser(); - const featuresLength = features.length; - - - /** In case no features to dissolve */ - if (0 === featuresLength) { - return null; - } - - /** In the case of single feature, return feature */ - if (1 === featuresLength) { - return features[0]; - } - - let jstsdissolvedFeatureGeometry; - - const baseFeature = clone ? features[index].clone() : features[index]; - const baseFeatureGeometry = baseFeature.getGeometry(); - const baseFeatureGeometryType = baseFeatureGeometry.getType(); - - // check if it can build a LineString - if ('LineString' === baseFeatureGeometryType) { - const lineMerger = new jsts.operation.linemerge.LineMerger(); - for (let i = 0; i < featuresLength; i++) { - lineMerger.addLineString( - new jsts.geom.GeometryFactory().createLineString(parser.read(features[i].getGeometry()).getCoordinates()) - ); - } - const mergedLineString = lineMerger.getMergedLineStrings(); - jstsdissolvedFeatureGeometry = 1 === mergedLineString.size() ? mergedLineString.toArray()[0] : null; - } - - if ('LineString' !== baseFeatureGeometryType) { - jstsdissolvedFeatureGeometry = parser.read(baseFeatureGeometry); - for (let i = 0; i < featuresLength ; i++) { - if (index !== i) { - jstsdissolvedFeatureGeometry = jstsdissolvedFeatureGeometry.union(parser.read(features[i].getGeometry())) - } - } - } - - /** In case of no dissolved geometry */ - if (!jstsdissolvedFeatureGeometry) { - return null; - } - - const dissolvedFeatureGeometry = parser.write(jstsdissolvedFeatureGeometry); - const dissolvedFeatureGeometryType = dissolvedFeatureGeometry.getType(); - const dissolvedFeatureGeometryCoordinates = dissolvedFeatureGeometryType === baseFeatureGeometryType - ? dissolvedFeatureGeometry.getCoordinates() - : -1 !== baseFeatureGeometryType.indexOf('Multi') && dissolvedFeatureGeometryType === baseFeatureGeometryType.replace('Multi', '') - ? [dissolvedFeatureGeometry.getCoordinates()] - : null; - - /** In case of null feature dissolved coordinates */ - if (null === dissolvedFeatureGeometryCoordinates) { - return null; - } - - baseFeature.getGeometry().setCoordinates(dissolvedFeatureGeometryCoordinates); - - return baseFeature; -} \ No newline at end of file From 05386bb3c39d50e0bf33fb8cbbf36fa92fd0366b Mon Sep 17 00:00:00 2001 From: volterra79 Date: Thu, 2 Jan 2025 11:18:01 +0100 Subject: [PATCH 29/30] :art: css panel-body --- components/Toolbox.vue | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/components/Toolbox.vue b/components/Toolbox.vue index 2f1441e9..d2a22fc7 100644 --- a/components/Toolbox.vue +++ b/components/Toolbox.vue @@ -615,7 +615,7 @@ padding: 13px; color: currentColor !important; font-size: 1.1em; - margin: 0px; + margin: 0; } .panel-title { font-weight: bold; @@ -655,7 +655,6 @@ margin-bottom: 5px; font-size: 1.1em; color: #000; - margin-top: 5px; } .snap-tool { display: flex; @@ -671,6 +670,9 @@ .tools-of-tool-snap label span { color: #222d32 !important; } + .panel-body { + padding: 15px; + } .panel-body.disabled { opacity: .7; } From a84e72a92a55c46ddee925f2c0629bc4732a4c63 Mon Sep 17 00:00:00 2001 From: volterra79 Date: Fri, 3 Jan 2025 11:18:21 +0100 Subject: [PATCH 30/30] :bug: Fix close deep form on relation --- g3wsdk/workflow/workflow.js | 2 +- toolboxes/toolbox.js | 2 +- workflows/index.js | 21 ++++++++++++++------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/g3wsdk/workflow/workflow.js b/g3wsdk/workflow/workflow.js index e1907ccf..fdebb319 100644 --- a/g3wsdk/workflow/workflow.js +++ b/g3wsdk/workflow/workflow.js @@ -642,5 +642,5 @@ Workflow.Stack = { removeAt(i) { workflows.splice(i, 1); }, insertAt(i, w) { workflows[i] = w; }, getAt(i) { return workflows[i]; }, - async clear() { await this.getFirst().stop(); workflows.splice(0); } + async clear() { workflows.splice(0); } }; \ No newline at end of file diff --git a/toolboxes/toolbox.js b/toolboxes/toolbox.js index b0a63963..a4447c3d 100644 --- a/toolboxes/toolbox.js +++ b/toolboxes/toolbox.js @@ -543,7 +543,7 @@ export class ToolBox extends G3WObject { context: { session: Workflow.Stack.getCurrent().getSession(), // get parent workflow excludeFields: fields.ownField, // array of fields to be excluded - isContentChild: false, //@since 3.9.0 force child to flase + isContentChild: false, //@since 3.9.0 force child to false }, inputs: { features: rLayer.readFeatures(), diff --git a/workflows/index.js b/workflows/index.js index e9757b25..3c051823 100644 --- a/workflows/index.js +++ b/workflows/index.js @@ -499,7 +499,7 @@ export class OpenFormStep extends Step {
1, + isChild: Workflow.Stack.getLength() > 1 && !(2 === Workflow.Stack.getLength() && Workflow.Stack.getFirst().isType('edittable')) }; }, computed: { @@ -625,7 +625,16 @@ export class OpenFormStep extends Step { * Close editing form */ async closeForm() { + //get current active tool + const tool = g3wsdk.core.plugin.PluginsRegistry.getPlugin('editing').state.toolboxselected.getActiveTool(); + //stop active tool and wait + await promisify(tool.stop()); + //clear all workflow stacks Workflow.Stack.clear(); + //check if the tool needs to run on time. If not, start again + if (!tool.getOperator().runOnce) { + tool.start(); + } } }, }, @@ -686,7 +695,7 @@ export class OpenFormStep extends Step { this.fireEvent('savedfeature', newFeatures); // called after saved this.fireEvent(`savedfeature_${this.layerId}`, newFeatures); // called after saved using layerId - // In case of save of child it means that child is updated so also parent + // In case of save of child, it means that child is updated so also parent if (this._isContentChild) { Workflow.Stack.getParents() //filter only with has getContextService to be sure @@ -714,10 +723,7 @@ export class OpenFormStep extends Step { } }, cbk: () => { - if (!this._isContentChild) { - GUI.setModal(false); - this.fireEvent('cancelform', inputs.features); // fire event cancel form to emit to subscrivers - } + this.fireEvent('cancelform', inputs.features); // fire event cancel form to emit to subscribers reject(inputs); } } @@ -805,6 +811,7 @@ export class OpenFormStep extends Step { // Ex. copy multiple features from another layer if (is_parent_table) { GUI.getService('map').disableClickMapControls(false); + GUI.setModal(false); } const contextService = is_parent_table && Workflow.Stack.getCurrent().getContextService();