diff --git a/components/FormRelation.vue b/components/FormRelation.vue index 4ec80815..93c59d24 100644 --- a/components/FormRelation.vue +++ b/components/FormRelation.vue @@ -3,51 +3,105 @@ + \ No newline at end of file diff --git a/components/Table.vue b/components/Table.vue index eef67c3a..44416bac 100644 --- a/components/Table.vue +++ b/components/Table.vue @@ -3,174 +3,358 @@ + + diff --git a/form/editingform.js b/form/editingform.js index 3d9664fd..c3e78657 100644 --- a/form/editingform.js +++ b/form/editingform.js @@ -1,37 +1,54 @@ -const {base, inherit} = g3wsdk.core.utils; -const {GUI} = g3wsdk.gui; -const {FormComponent} = g3wsdk.gui.vue; +const { base, inherit } = g3wsdk.core.utils; +const { GUI } = g3wsdk.gui; +const { FormComponent } = g3wsdk.gui.vue; const EditingFormService = require('./editingformservice'); -function EditingFormComponent(options={}) { +function EditingFormComponent(options = {}) { + base(this, options); - const EditingService = require('../services/editingservice'); + + const EditingService = require('../services/editingservice'); const relationsOptions = options.context_inputs || null; - const {layer} = options; - const layerId = layer.getId(); - if (relationsOptions) { - const feature = relationsOptions.inputs.features[relationsOptions.inputs.features.length-1]; - const promise = feature.isNew() ? Promise.resolve() : EditingService.getLayersDependencyFeatures(layerId, { - feature, - filterType: 'fid' - }); - promise.then(()=> { + const layerId = options.layer.getId(); + const feature = ( + relationsOptions && + relationsOptions.inputs && + relationsOptions.inputs.features && + relationsOptions.inputs.features[relationsOptions.inputs.features.length - 1] + ); + + if (feature) { + ( + feature.isNew() + ? Promise.resolve() + : EditingService.getLayersDependencyFeatures(layerId, { feature, filterType: 'fid' }) + ).then(() => { + relationsOptions.formEventBus = this.getService().getEventBus(); - const service = new EditingFormService(relationsOptions); - const RelationComponents = service.buildRelationComponents(); - const customFormComponents = EditingService.getFormComponentsById(layerId); - //check if add components to add - customFormComponents.length && this.addFormComponents(customFormComponents); + + const service = new EditingFormService(relationsOptions); + const RelationComponents = service.buildRelationComponents(); + const customFormComponents = EditingService.getFormComponentsById(layerId); + + // check if add components to add + if (customFormComponents.length > 0) { + this.addFormComponents(customFormComponents); + } + // add relation component - RelationComponents.length && this.addFormComponents(RelationComponents); - this.getService().handleRelation = async function({relation, layerId, feature}){ + if (RelationComponents.length > 0) { + this.addFormComponents(RelationComponents); + } + + // overwrite click on relation handler + this.getService().handleRelation = async function({ relation }) { GUI.setLoadingContent(true); - const {name: relationId} = relation; - await EditingService.setLayerUniqueFieldValues(layer.getRelationById(relationId).getChild()); - this.setCurrentComponentById(relationId); + await EditingService.setLayerUniqueFieldValues(options.layer.getRelationById(relation.name).getChild()); + this.setCurrentComponentById(relation.name); GUI.setLoadingContent(false); }; - }) + + }); } } diff --git a/form/editingformservice.js b/form/editingformservice.js index 5e5dbef1..1199abb3 100644 --- a/form/editingformservice.js +++ b/form/editingformservice.js @@ -1,29 +1,44 @@ -const {GUI} = g3wsdk.gui; -const RelationComponent = require('../components/FormRelation.vue'); -const EditingFormService = function(options={}) { +const { GUI } = g3wsdk.gui; +const RelationComponent = require('../components/FormRelation.vue'); + +const EditingFormService = function(options = {}) { + const EditingService = require('../services/editingservice'); + this.state = { relations: [] }; - const {layer, features} = options.inputs || {}; - // get back to Father function + + const { layer, features } = options.inputs || {}; + + /** + * get back to Father function + */ this._formEventBus = options.formEventBus || null; + const layerId = layer.getId(); - // get feature - const feature = features[features.length - 1]; + // get only relation with type not ONE and layer is the father let relations = layer.getRelations().getArray().filter(relation => relation.getType() !== 'ONE' && relation.getFather() === layerId); - /** - * get relation layers that set in editing on g3w-admin - */ - relations = EditingService.getRelationsInEditing({layerId, relations , feature}); - this.hasRelations = () => !!relations.length; + + // get relation layers that set in editing on g3w-admin + relations = EditingService.getRelationsInEditing({ + layerId, + relations , + feature: features[features.length - 1], + }); + + this.hasRelations = () => relations.length > 0; + this.buildRelationComponents = function() { + const self = this; + const relationComponents = []; - relations.forEach(relation => { + + relations.forEach(({ relation, relations }) => { const relationComponent = Vue.extend({ - mixins: [RelationComponent], + mixins: [ RelationComponent ], name: `relation_${Date.now()}`, methods: { getService() { @@ -33,23 +48,26 @@ const EditingFormService = function(options={}) { data() { return { layerId, - relation: relation.relation, - relations: relation.relations, + relation, + relations, resourcesurl: GUI.getResourcesUrl(), formeventbus: self._formEventBus - } + }; }, }); relationComponents.push({ - title: "plugins.editing.edit_relation", - name: relation.relation.name, - id: relation.relation.id, - header: false, // not show to header form + title: "plugins.editing.edit_relation", + name: relation.name, + id: relation.id, + header: false, // hide header form component: relationComponent }) }); + return relationComponents; + }; + }; module.exports = EditingFormService; diff --git a/services/editingservice.js b/services/editingservice.js index cb0dd694..84af0565 100644 --- a/services/editingservice.js +++ b/services/editingservice.js @@ -908,14 +908,29 @@ proto._getRelationLayerId = function({layerId, relation}={}){ return relation.getChild() === layerId ? relation.getFather() : relation.getChild(); }; +/** + * + * @param layerId + * @param relation + * @param feature + * @param layerType + */ proto.getRelationsByFeature = function({layerId, relation, feature, layerType}={}) { + //ownField and relationField are Array @since v3.7.0 const {ownField, relationField} = this._getRelationFieldsFromRelation({ layerId, relation }); - const featureValue = feature.get(relationField); + //get features of relation child layers const features = this._getFeaturesByLayerId(layerId); - return features.filter(feature => feature.get(ownField) == featureValue); + //Loop relation fields + const featuresValues = relationField.map(rField => feature.get(rField)); + return features.filter(feature => { + return ownField.reduce((bool, oField, index) => { + return bool && feature.get(oField) == featuresValues[index] + }, true) + }); + }; proto.registerLeavePage = function(bool){ @@ -1118,16 +1133,26 @@ proto.fatherInEditing = function(layerId) { return inEditing; }; +/** + * + * @param layerId + * @param relation + * @returns {{ownField: *, relationField: *}} + * @private + */ proto._getRelationFieldsFromRelation = function({layerId, relation} = {}) { const childId = relation.getChild ? relation.getChild() : relation.child; const isChild = childId !== layerId; - const _fatherField = relation.getFatherField ? relation.getFatherField() : relation.fatherField; - const _childField = relation.getChildField ? relation.getChildField() : relation.childField; - const ownField = isChild ? _fatherField : _childField; - const relationField = isChild ? _childField : _fatherField; + const _fatherField = relation.getFatherField ? + relation.getFatherField() : + relation.fatherField; + const _childField = relation.getChildField ? + relation.getChildField() : + relation.childField; + return { - ownField, - relationField + ownField: isChild ? _fatherField : _childField, + relationField: isChild ? _childField : _fatherField } }; @@ -1185,11 +1210,18 @@ proto.getLayersDependencyFeaturesFromSource = function({layerId, relation, featu layerId, relation }); - const featureValue = feature.get(relationField); - const find = operator === 'eq' ? features.find(featureSource => { - const featureSourceValue = featureSource.get(ownField) ; - return featureSourceValue == featureValue; - }): false; + //get features Values + const featureValues = relationField.map(rField => feature.get(rField)); + const find = operator === 'eq' ? + + ownField.reduce((bool, oField, index) => { + return features.find(featureSource => { + return bool && featureSource.get(oField) == featureValues[index]; + }) + }, true) : + + false; + resolve(find); }) }; @@ -1757,12 +1789,16 @@ proto.getExternalLayersWithSameGeometryOfLayer = function(layer){ const geometryType = layer.getGeometryType(); return this._mapService.getExternalLayers().filter(externalLayer => { const features = externalLayer.getSource().getFeatures(); - if (features && features.length) { + if (features && features.length > 0) { return features[0].getGeometry() ? - (geometryType === features[0].getGeometry().getType()) - || - isSameBaseGeometryType(geometryType, features[0].getGeometry().getType()) : false; - } else return false; + ( + (geometryType === features[0].getGeometry().getType()) || + isSameBaseGeometryType(geometryType, features[0].getGeometry().getType()) + ) : + false; + } else { + return false; + } }); }; diff --git a/services/relationservice.js b/services/relationservice.js index 659a63ae..1214b644 100644 --- a/services/relationservice.js +++ b/services/relationservice.js @@ -28,9 +28,10 @@ const RelationService = function(layerId, options = {}) { this.relations = relations; //editing service (main service of plugin) this._editingService; - this._isExternalFieldRequired = false; // this._relationLayerId is layer id of relation layer - this._relationLayerId = this.relation.child === this._parentLayerId ? this.relation.father : this.relation.child; + this._relationLayerId = this.relation.child === this._parentLayerId ? + this.relation.father : + this.relation.child; // layer in relation const relationLayer = this.getLayer(); this._layerType = relationLayer.getType(); @@ -41,19 +42,34 @@ const RelationService = function(layerId, options = {}) { layerId: this._parentLayerId, relation: this.relation }); - // check if father is editable field. It is useful to fill relation filed of relation feature - this._isFatherFieldEditable = this._parentLayer.isEditingFieldEditable(fatherRelationField); + + // Store father relation fields editable and pk + this._fatherRelationFields = { + //get editable fields + editable: fatherRelationField.filter(fRField => this._parentLayer.isEditingFieldEditable(fRField)), + //check if father field is a pk and is not editable + pk: fatherRelationField.find(fRField => { + return this._parentLayer.isPkField(fRField) && !this._parentLayer.isEditingFieldEditable(fRField) + }), + } + //check if external fields this._isExternalFieldRequired = this._checkIfExternalFieldRequired(); - // check if parent field is editable. If not get the id of parent feature so the server can genratate the right value - // to fill the field of relation layer feature when commit - this._currentParentFeatureRelationFieldValue = this._isFatherFieldEditable ? - this.getCurrentWorkflowData().feature.get(fatherRelationField) : - this.getCurrentWorkflowData().feature.getId(); + // check if parent field is editable. If not get the id of parent feature so the server can generate the right value + // to fill the field with relation layer feature when commit + + this._currentParentFeatureRelationFieldsValue = fatherRelationField.reduce((accumulator, fField) => { + accumulator[fField] = fField === this._fatherRelationFields.pk ? //check if isPk + this.getCurrentWorkflowData().feature.getId() : + this.getCurrentWorkflowData().feature.get(fField); + return accumulator; + }, {}) + + /////////////////////////////////////// this._relationTools = []; this._add_link_workflow = null; - //get editing contstraint type - this.capabilities= { + //get editing constraint type + this.capabilities = { parent: this._parentLayer.getEditingCapabilities(), relation: this._parentLayer.getEditingCapabilities() }; @@ -85,10 +101,18 @@ const RelationService = function(layerId, options = {}) { const proto = RelationService.prototype; -proto.getEditingCapabilities = function(){ +/** + * Return editing capabilities + * @returns {*|{parent: *, relation: *}} + */ +proto.getEditingCapabilities = function() { return this.capabilities; }; +/** + * + * @private + */ proto._setAddLinkWorkflow = function() { const add_link_workflow = { [Layer.LayerTypes.VECTOR]: { @@ -101,25 +125,52 @@ proto._setAddLinkWorkflow = function() { add: require('../workflows/addtablefeatureworkflow') } }; + this._add_link_workflow = add_link_workflow[this._layerType]; }; +/** + * + * @returns {LinkRelationWorflow} + * @private + */ proto._getLinkFeatureWorkflow = function() { return new this._add_link_workflow.link(); }; +/** + * + * @returns {AddFeatureWorflow} + * @private + */ proto._getAddFeatureWorkflow = function() { return new this._add_link_workflow.add(); }; +/** + * + * @param options + * @returns {SelectAndCopyFeaturesFromOtherLayerWorflow} + * @private + */ proto._getSelectCopyWorkflow = function(options={}){ return new this._add_link_workflow.selectandcopy(options) }; +/** + * + * @returns {[]} + */ proto.getRelationTools = function() { return this._relationTools }; +/** + * + * @param relation + * @returns {*} + * @private + */ proto._highlightRelationSelect = function(relation) { const originalStyle = this.getLayer().getEditingLayer().getStyle(); const geometryType = this.getLayer().getGeometryType(); @@ -151,10 +202,18 @@ proto._highlightRelationSelect = function(relation) { }) }); } + relation.setStyle(style); + return originalStyle; }; +/** + * + * @param relationtool + * @param index + * @returns {Promise} + */ proto.startTool = function(relationtool, index) { if (relationtool.state.id === 'movefeature') GUI.hideContent(true); return new Promise((resolve, reject) => { @@ -166,20 +225,31 @@ proto.startTool = function(relationtool, index) { resolve(); }) .fail(err => reject(err)) - .always(()=>GUI.hideContent(false)); + .always(() => GUI.hideContent(false)); }) }; /** - * force parent workflow form service to be update + * force parent workflow form service to update */ proto.forceParentsFromServiceWorkflowToUpdated = function() { const workflowParents = WorkflowsStack.getParents() || [this.getCurrentWorkflow()]; - workflowParents.forEach(workflow => workflow.getContext().service.setUpdate(true, { - force: true - })); + workflowParents.forEach(workflow => { + //check if workflow has service (form service) + if (workflow.getContextService()) { + workflow + .getContextService() + .setUpdate(true, {force: true}) + } + }); }; +/** + * Method to start table tool + * @param relationtool + * @param index + * @returns {*} + */ proto.startTableTool = function(relationtool, index) { const d = $.Deferred(); const relation = this.relations[index]; @@ -210,10 +280,13 @@ proto.startTableTool = function(relationtool, index) { const workflow = new EditTableFeatureWorkflow(); workflow.start(options) .then(() => { + //get relation layer fields const fields = this._getRelationFieldsValue(relationfeature); fields.forEach(_field => { relation.fields.forEach(field => { - if (field.name === _field.name) field.value = _field.value; + if (field.name === _field.name) { + field.value = _field.value; + } }) }); d.resolve(true); @@ -226,6 +299,12 @@ proto.startTableTool = function(relationtool, index) { return d.promise() }; +/** + * + * @param relationtool + * @param index + * @returns {*} + */ proto.startVectorTool = function(relationtool, index) { const d = $.Deferred(); const relation = this.relations[index]; @@ -248,7 +327,8 @@ proto.startVectorTool = function(relationtool, index) { const promise =(workflow instanceof workflows.DeleteFeatureWorkflow || workflow instanceof workflows.EditFeatureAttributesWorkflow ) && workflow.startFromLastStep(options) || workflow.start(options); workflow.bindEscKeyUp(() => relationfeature.setStyle(this._originalLayerStyle)); - promise.then(outputs => { + promise + .then(outputs => { if (relationtool.getId() === 'deletefeature') { relationfeature.setStyle(this._originalLayerStyle); this.getCurrentWorkflowData().session.pushDelete(this._relationLayerId, relationfeature); @@ -271,17 +351,30 @@ proto.startVectorTool = function(relationtool, index) { GUI.setModal(true); relationfeature.setStyle(originalStyle); }); + return d.promise() }; +/** + * + * @returns {*} + */ proto.getLayer = function() { return this.getEditingService().getLayerById(this._relationLayerId); }; +/** + * + * @returns {*} + */ proto.getEditingLayer = function() { return this.getEditingService().getEditingLayer(this._relationLayerId); }; +/** + * + * @returns {*|EditingService|{}} + */ proto.getEditingService = function() { this._editingService = this._editingService || require('./editingservice'); return this._editingService; @@ -298,31 +391,48 @@ proto.updateExternalKeyValueRelations = function(input) { layerId: this._relationLayerId, relation: this.relation }); - // check if parent form input that is changing is the field in relation of the current feature relation Layer - if (this._isFatherFieldEditable && input.name === relationField) { - // chnage currentParentFieature relation value - this._currentParentFeatureRelationFieldValue = input.value; + // get if parent form input that is changing + // is the field in relation of the current feature relation Layer + if (this._fatherRelationFields.editable.length > 0 && relationField.find(rField => rField === input.name)) { + // change currentParentFieature relation value + this._currentParentFeatureRelationFieldsValue[input.name] = input.value; // loop all features relations this.relations.forEach(relation => { + //get relation fields const fields = relation.fields; // field relation field of current relation feature - const field = fields.find(field => field.name === ownField); - if (field) field.value = this._currentParentFeatureRelationFieldValue; - relation = this._getRelationFeature(relation.id); - const originalRelation = relation.clone(); - relation.set(ownField, input.value); - if (!relation.isNew()) session.pushUpdate(this._relationLayerId, relation, originalRelation); + const field = fields.find(field => ownField.indexOf(field.name) !== -1); + //if field is find + if (field) { + field.value = this._currentParentFeatureRelationFieldsValue[field.name]; + relation = this._getRelationFeature(relation.id); + const originalRelation = relation.clone(); + relation.set(field.name, input.value); + if (!relation.isNew()) { + session.pushUpdate(this._relationLayerId, relation, originalRelation); + } + } }) } }; +/** + * + * @param relation + * @returns {*} + * @private + */ proto._getRelationFieldsValue = function(relation) { - const layer = this.getLayer(); - return layer.getFieldsWithValues(relation, { - relation: true - }); + return this.getLayer() + .getFieldsWithValues(relation, {relation: true}); }; +/** + * + * @param relation + * @returns {{id: *, fields: *}} + * @private + */ proto._createRelationObj = function(relation) { return { fields: this._getRelationFieldsValue(relation), @@ -330,11 +440,24 @@ proto._createRelationObj = function(relation) { } }; +/** + * + * @param type + * @param options + */ proto.emitEventToParentWorkFlow = function(type, options={}) { //type=set-main-component event name to set table parent visible - type && this._parentWorkFlow.getContextService().getEventBus().$emit(type, options) + if (type) { + this._parentWorkFlow.getContextService().getEventBus().$emit(type, options); + } }; +/** + * + * @param type + * @returns {*|string} + * @private + */ proto._getRelationAsFatherStyleColor = function(type) { const fatherLayer = this.getEditingLayer(this._parentLayerId); const fatherLayerStyle = fatherLayer.getStyle(); @@ -350,7 +473,13 @@ proto._getRelationAsFatherStyleColor = function(type) { fatherLayerStyleColor = fatherLayerStyle.getFill() || fatherLayerStyle.getStroke(); break; } - return fatherLayerStyleColor && fatherLayerStyleColor.getColor() || '#000000'; + return ( + ( + fatherLayerStyleColor && + fatherLayerStyleColor.getColor() + ) || + '#000000' + ); }; /** @@ -368,6 +497,7 @@ proto.addRelationFromOtherLayer = function({layer, external}){ external }); } + this.runAddRelationWorkflow({ workflow, isVector @@ -378,11 +508,10 @@ proto.addRelationFromOtherLayer = function({layer, external}){ * add relation method */ proto.addRelation = function() { - const isVector = this._layerType === Layer.LayerTypes.VECTOR; - const workflow = this._getAddFeatureWorkflow(); + this.runAddRelationWorkflow({ - workflow, - isVector + workflow: this._getAddFeatureWorkflow(), + isVector: this._layerType === Layer.LayerTypes.VECTOR }) }; @@ -407,51 +536,79 @@ proto.runAddRelationWorkflow = function({workflow, isVector=false}={}){ promise .then(outputs => { const {newFeatures, originalFeatures} = outputs.relationFeatures; - const setRelationFieldValue = value =>{ - newFeatures.forEach((newFeature, index) =>{ + + /** + * Set Relation child feature value + * @param oIndex + * @param value + */ + const setRelationFieldValue = ({oIndex, value}) => { + newFeatures.forEach((newFeature, index) => { const originalFeature = originalFeatures[index]; - newFeature.set(ownField, value); - if (parentFeature.isNew()) originalFeature.set(ownField, value); + newFeature.set(ownField[oIndex], value); + if (parentFeature.isNew()) { + originalFeature.set(ownField[oIndex], value); + } this.getLayer().getEditingSource().updateFeature(newFeature); session.pushUpdate(this._relationLayerId, newFeature, originalFeature); }) }; - setRelationFieldValue(this._currentParentFeatureRelationFieldValue); - if (parentFeature.isNew() && this._isFatherFieldEditable) { + Object + .entries(this._currentParentFeatureRelationFieldsValue) + .forEach(([field, value]) => { + setRelationFieldValue({ + oIndex: relationField.findIndex(rField => field === rField), + value + }); + }) + //check if parent features is new and if parent layer has editable fields + if (parentFeature.isNew() && this._fatherRelationFields.editable.length > 0) { const keyRelationFeatureChange = parentFeature.on('propertychange', evt => { if (parentFeature.isNew()) { - if (evt.key === relationField) { - const value = evt.target.get(relationField); - setRelationFieldValue(value, true); + //check if input is relation field + if (relationField.indexOf(evt.key) !== -1) { + //get input value + const value = evt.target.get(evt.key); + //set value to relation field + setRelationFieldValue({ + oIndex: relationField.findIndex(rField => evt.key === rField), + value + }); } - } else ol.Observable.unByKey(keyRelationFeatureChange); + } else { + ol.Observable.unByKey(keyRelationFeatureChange); + } }) } - newFeatures.forEach(newFeature =>{ - const newRelation = this._createRelationObj(newFeature); - this.relations.push(newRelation); + newFeatures.forEach(newFeature => { + this.relations.push(this._createRelationObj(newFeature)); }); + this.emitEventToParentWorkFlow(); }) - .fail((inputs) => { + .fail(inputs => { + if (inputs && inputs.relationFeatures) { /** * needed in case of save all pressed on openformtask */ - const {relationFeatures:{newFeatures=[]}} = inputs; - newFeatures.forEach(newFeature =>{ - const newRelation = this._createRelationObj(newFeature); - this.relations.push(newRelation); + const {relationFeatures: { newFeatures=[] } } = inputs; + newFeatures.forEach(newFeature => { + this.relations.push(this._createRelationObj(newFeature)); }); } + session.rollbackDependecies([this._relationLayerId]) }) - .always(()=>{ + .always(() => { + workflow.stop(); if (isVector) { + workflow.unbindEscKeyUp(); GUI.hideContent(false); GUI.setModal(true); + } }) }; @@ -461,24 +618,30 @@ proto.runAddRelationWorkflow = function({workflow, isVector=false}={}){ */ proto.linkRelation = function() { const isVector = this._layerType === Layer.LayerTypes.VECTOR; - if (isVector) { - GUI.setModal(false); - GUI.hideContent(true); - } const workflow = this._getLinkFeatureWorkflow(); const options = this._createWorkflowOptions(); const session = options.context.session; - const {ownField} = this.getEditingService()._getRelationFieldsFromRelation({ + const {ownField, relationField} = this.getEditingService()._getRelationFieldsFromRelation({ layerId: this._relationLayerId, relation: this.relation }); - //add options to exclude features - options.context.exclude = { - value: this._currentParentFeatureRelationFieldValue, - field: ownField - }; - if (isVector) options.context.style = this.getUnlinkedStyle(); - const feature = this.getCurrentWorkflowData().feature; + + //add options to exclude features from link + options.context.excludeFeatures = relationField.reduce((accumulator, rField, index) => { + accumulator[ownField[index]] = this._currentParentFeatureRelationFieldsValue[rField]; + return accumulator; + }, {}); + + + //check if a vector layer + if (isVector) { + GUI.setModal(false); + GUI.hideContent(true); + options.context.style = this.getUnlinkedStyle(); + } + + const {feature} = this.getCurrentWorkflowData(); + const dependencyOptions = { relations: [this.relation], feature, @@ -486,18 +649,21 @@ proto.linkRelation = function() { filterType: isVector ? 'bbox' : 'fid' }; const getRelationFeatures = () => this.getEditingService().getLayersDependencyFeatures(this._parentLayerId, dependencyOptions); + let preWorkflowStart; + if (isVector) { const mapService = this.getEditingService().getMapService(); options.context.beforeRun = async () => { + //show map spinner mapService.showMapSpinner(); - await new Promise((resolve) =>{ - setTimeout(()=>{ - resolve(); - }) - }); + + await new Promise((resolve) => setTimeout(resolve)); + await getRelationFeatures(); + //hide mapSpinner mapService.hideMapSpinner(); + GUI.showUserMessage({ type: 'info', size: 'small', @@ -505,48 +671,75 @@ proto.linkRelation = function() { closable: false }) }; - preWorkflowStart = new Promise((resolve)=> { + + preWorkflowStart = new Promise((resolve) => { + workflow.bindEscKeyUp(); - const promise = workflow.start(options); + resolve({ - promise, + promise: workflow.start(options), showContent: true }) }); - } else preWorkflowStart = new Promise((resolve) => { - getRelationFeatures() - .then(()=>{ - resolve({}) - }) - }); + } else { + + preWorkflowStart = new Promise((resolve) => { + getRelationFeatures() + .then(() => resolve({})) + }); + } preWorkflowStart.then(({promise, showContent=false}={})=> { let linked = false; + promise = promise || workflow.start(options); - promise.then(outputs => { - if (outputs.features.length) { - outputs.features.forEach(relation => { - const relationAlreadyLinked = this.relations.find(rel => rel.id === relation.getId()); - if (!relationAlreadyLinked) { - linked = linked || true; - const originalRelation = relation.clone(); - relation.set(ownField, this._currentParentFeatureRelationFieldValue); - this.getCurrentWorkflowData().session.pushUpdate(this._relationLayerId , relation, originalRelation); - this.relations.push(this._createRelationObj(relation)); - this.emitEventToParentWorkFlow(); - } else GUI.notify.warning(t("editing.relation_already_added")); - }); - } - }).fail(err => { - session.rollbackDependecies([this._relationLayerId]); - }).always(() =>{ - if (showContent) { - GUI.closeUserMessage(); - GUI.hideContent(false); - workflow.unbindEscKeyUp(); - } - linked && this.forceParentsFromServiceWorkflowToUpdated(); - workflow.stop(); + + promise + .then(outputs => { + + if (outputs.features.length > 0) { + + //loop on features selected + outputs.features.forEach(relation => { + + if (undefined === this.relations.find(rel => rel.id === relation.getId())) { + + linked = linked || true; + + const originalRelation = relation.clone(); + + Object + .entries(this._currentParentFeatureRelationFieldsValue) + .forEach(([field, value]) => { + relation.set(ownField[relationField.findIndex(rField => field === rField)], value); + }) + + this.getCurrentWorkflowData() + .session + .pushUpdate(this._relationLayerId , relation, originalRelation); + + this.relations.push(this._createRelationObj(relation)); + + this.emitEventToParentWorkFlow(); + + } else { + // in case already present + GUI.notify.warning(t("editing.relation_already_added")); + } + }); + } + }) + .fail(() => session.rollbackDependecies([this._relationLayerId])) + .always(() => { + if (showContent) { + GUI.closeUserMessage(); + GUI.hideContent(false); + workflow.unbindEscKeyUp(); + } + if (linked) { + this.forceParentsFromServiceWorkflowToUpdated(); + } + workflow.stop(); }); }) }; @@ -562,73 +755,140 @@ proto._checkIfExternalFieldRequired = function() { layerId: this._relationLayerId, relation: this.relation }); - return this.getEditingService().isFieldRequired(this._relationLayerId, ownField); + + //ownField is an array + return ownField.reduce((bool, oField) => { + return bool || this.getEditingService() + .isFieldRequired(this._relationLayerId, oField); + }, false); }; +/** + * + * @returns {boolean|*} + */ proto.isRequired = function() { return this._isExternalFieldRequired; }; +/** + * + * @param featureId + * @returns {*} + * @private + */ proto._getRelationFeature = function(featureId) { - const layer = this.getLayer(); - return layer.getEditingSource().getFeatureById(featureId); + return this.getLayer().getEditingSource().getFeatureById(featureId); }; +/** + * Method to unlink relation + * @param index + * @param dialog + * @returns JQuery Promise + */ proto.unlinkRelation = function(index, dialog=true) { const d = $.Deferred(); const {ownField} = this.getEditingService()._getRelationFieldsFromRelation({ layerId: this._relationLayerId, relation: this.relation }); - const unlink = () =>{ + const unlink = () => { const relation = this.relations[index]; const feature = this.getLayer().getEditingSource().getFeatureById(relation.id); const originalRelation = feature.clone(); - feature.set(ownField, null); + //loop on ownField (Array field child relation) + ownField.forEach(oField => feature.set(oField, null)) + this.getCurrentWorkflowData().session.pushUpdate(this._relationLayerId, feature, originalRelation); this.relations.splice(index, 1); this.forceParentsFromServiceWorkflowToUpdated(); d.resolve(true); }; if (dialog) { - GUI.dialog.confirm(t("editing.messages.unlink_relation"), result => { - if (result) unlink() ; - else d.reject(false); - }) - } else unlink(); + + GUI.dialog.confirm( + t("editing.messages.unlink_relation"), + result => { + if (result) { + unlink() ; + } else { + d.reject(false); + } + } + ) + } else { + unlink(); + } return d.promise(); }; +/** + * + * @returns {*} + */ proto.getCurrentWorkflow = function() { return this.getEditingService().getCurrentWorkflow(); }; +/** + * + * @returns {*} + */ proto.getCurrentWorkflowData = function() { return this.getEditingService().getCurrentWorkflowData(); }; +/** + * + * @param options + * @returns {{parentFeature, inputs: {features: *[], layer: *}, context: {fatherValue: *, session: *, fatherField: *, excludeFields: *[]}}} + * @private + */ proto._createWorkflowOptions = function(options={}) { - const {ownField} = this.getEditingService()._getRelationFieldsFromRelation({ + + const {ownField, relationField} = this.getEditingService()._getRelationFieldsFromRelation({ layerId: this._relationLayerId, relation: this.relation }); + const { + feature: parentFeature, + session + } = this.getCurrentWorkflowData(); + + const fatherValue = []; + const fatherField = []; + + Object + .entries(this._currentParentFeatureRelationFieldsValue) + .forEach(([field, value]) => { + const index = relationField.findIndex(rField => field === rField); + fatherField.push(ownField[index]); + fatherValue.push(value); + }) + const workflow_options = { - parentFeature: this.getCurrentWorkflowData().feature, + parentFeature, //get parent feature context: { - session: this.getCurrentWorkflowData().session, - excludeFields: [ownField], - fatherValue: this._currentParentFeatureRelationFieldValue, // field of father relation layer - fatherField: ownField // value of father relationField + session, //get parent workfow + excludeFields: ownField, //ownField is an Array to exclude + fatherValue, + fatherField }, inputs: { features: options.features || [], layer: this.getLayer() } }; + return workflow_options; }; +/** + * + * @returns {ol.style.Style} + */ proto.getUnlinkedStyle = function() { let style; const geometryType = this.getLayer().getGeometryType(); @@ -670,16 +930,20 @@ proto.getUnlinkedStyle = function() { }) }) } + return style; }; +/** + * + * @param relation + * @returns {*[]} + */ proto.relationFields = function(relation) { - const attributes = []; - relation.fields.forEach(field => { - const value = field.value; - attributes.push({label: field.label, value}) - }); - return attributes + return relation.fields.map(({label, value}) => ({ + label, + value + })) }; module.exports = RelationService; diff --git a/services/tableservice.js b/services/tableservice.js index 0bbcf881..ac1d598a 100644 --- a/services/tableservice.js +++ b/services/tableservice.js @@ -22,29 +22,46 @@ const TableService = function(options = {}) { capabilities }; + /** + * Init function service + * + */ this.init = function() { //filter the original feature based on if is a relation - this._features = !this._isrelation ? this._features : this._features.filter(feature => - feature.get(this._foreignKey) !== this._fatherValue - ); - // set values - if (this._features.length) { + this._features = !this._isrelation ? + this._features: + this._features + .filter(feature => feature.get(this._foreignKey) !== this._fatherValue); + // check if there are features + if (this._features.length > 0) { + + //get ba bse feature as template const baseFeature = this._features[0]; + + //get properties of the feature const properties = Object.keys(baseFeature.getProperties()); - this.state.headers = this.state.headers.filter(header => properties.indexOf(header.name) !== -1); + + //set headers of table + this.state.headers = this.state.headers + .filter(header => properties.indexOf(header.name) !== -1); + + //extract get headers name const headers = this.state.headers.map(header => header.name); - this.state.features = this._features.map(feature => { - const properties = feature.getProperties(); - const orderedProperties = {}; - headers.forEach(header => { - orderedProperties[header] = properties[header] - }); - orderedProperties.__gis3w_feature_uid = feature.getUid(); - return orderedProperties; + + //set features in order of the properties + this.state.features = this._features + .map(feature => { + const properties = feature.getProperties(); + const orderedProperties = {}; + headers.forEach(header => orderedProperties[header] = properties[header]); + orderedProperties.__gis3w_feature_uid = feature.getUid(); + return orderedProperties; }); } }; + this.init(); + base(this); }; @@ -52,6 +69,11 @@ inherit(TableService, G3WObject); const proto = TableService.prototype; +/** + * + * @param name + * @returns {boolean} + */ proto.isMediaField = function(name) { let isMedia = false; for (let i=0; i < this.state.headers.length; i++) { @@ -64,23 +86,34 @@ proto.isMediaField = function(name) { return isMedia; }; +/** + * + */ proto.save = function() { this._promise.resolve(); }; +/* + + */ proto.cancel = function() { this._promise.reject(); }; - +/** + * + * @param uid feature uid + * @returns {Promise} + */ proto.deleteFeature = function(uid) { const EditingService = require('./editingservice'); const layer = this._inputs.layer; const layerId = layer.getId(); const childRelations = layer.getChildren(); - const relationinediting = childRelations.length && EditingService._filterRelationsInEditing({ + const relationinediting = childRelations.length && EditingService._filterRelationsInEditing({ layerId, relations: layer.getRelations().getArray() }).length > 0; + return new Promise((resolve, reject) =>{ GUI.dialog.confirm(`

${t('editing.messages.delete_feature')}

${ relationinediting ?t('editing.messages.delete_feature_relations') : ''}
`, (result) => { @@ -103,6 +136,11 @@ proto.deleteFeature = function(uid) { }) }; +/** + * + * @param uid + * @returns {Promise} + */ proto.copyFeature = function(uid){ return new Promise((resolve, reject) =>{ const feature = this._features.find(feature => feature.getUid() === uid).cloneNew(); @@ -129,6 +167,10 @@ proto.copyFeature = function(uid){ }) }; +/** + * + * @param uid + */ proto.editFeature = function(uid) { let index; const feature = this._features.find((feature, featureIndex) => { @@ -156,6 +198,10 @@ proto.editFeature = function(uid) { .always(() => this._workflow.stop()) }; +/** + * + * @param featuresIndex + */ proto.linkFeatures = function(featuresIndex=[]){ const features = featuresIndex.map(index => this._features[index]); this._promise.resolve({ @@ -163,6 +209,10 @@ proto.linkFeatures = function(featuresIndex=[]){ }) }; +/** + * + * @param index + */ proto.linkFeature = function(index) { const feature = this._features[index]; this._promise.resolve({ diff --git a/workflows/steps/tasks/deletefeaturetask.js b/workflows/steps/tasks/deletefeaturetask.js index a5c2250e..0377e96b 100644 --- a/workflows/steps/tasks/deletefeaturetask.js +++ b/workflows/steps/tasks/deletefeaturetask.js @@ -20,47 +20,76 @@ proto.run = function(inputs, context) { const feature = inputs.features[0]; const EditingService = require('../../../services/editingservice'); const RelationService = require('../../../services/relationservice'); + const relations = EditingService._filterRelationsInEditing({ layerId, - relations: originaLayer.getRelations() ? originaLayer.getRelations().getArray() : [] + relations: originaLayer.getRelations() ? + originaLayer.getRelations().getArray() : + [] }).filter(relation => { + //get Relation Id const relationId = EditingService._getRelationId({ layerId, relation }); + const relationLayer = EditingService.getLayerById(relationId); + const {ownField} = EditingService._getRelationFieldsFromRelation({ layerId: relationId, relation }); - const field = relationLayer.getEditingFields().find((field) => { - return field.name === ownField; - }); - return !field.validate.required; + + //Exclude relation child layer that has at least one + //editing field required because when unlink relation feature from + // delete father, when try to commit update relation, we receive an error + //due missing value /null to required field. + + return ownField.reduce((bool, oField) => { + const field = relationLayer.getEditingFields() + .find((field) => field.name === oField); + return bool || !field.validate.required; + }, false) + }); - const promise = relations.length ? EditingService.getLayersDependencyFeatures(layerId, { - feature, - relations - }) : Promise.resolve(); + + const promise = relations.length > 0 ? + EditingService.getLayersDependencyFeatures(layerId, {feature, relations}) : + Promise.resolve(); + + //promise return features relations and add to relation layer child promise.then(() => { + + //get data features const relationsInEditing = EditingService.getRelationsInEditing({ layerId, relations, feature, }); + inputs.features = [feature]; - relationsInEditing.forEach((relationInEditing) => { + + relationsInEditing.forEach(relationInEditing => { + //relation is an instance of Relation. + //relations are relations features const {relation, relations} = relationInEditing; + const relationService = new RelationService(layerId, { relation, relations }); + const relationsLength = relations.length; + + //Unlink relation features related to layer id for (let index = 0; index < relationsLength ; index++) { + //unlink relationService.unlinkRelation(0, false) } }); + session.pushDelete(layerId, feature); + d.resolve(inputs); }); diff --git a/workflows/steps/tasks/linkrelationtask.js b/workflows/steps/tasks/linkrelationtask.js index ef589c7e..8a894096 100644 --- a/workflows/steps/tasks/linkrelationtask.js +++ b/workflows/steps/tasks/linkrelationtask.js @@ -19,46 +19,51 @@ proto.run = function(inputs, context) { this._originalLayerStyle = editingLayer.getStyle(); const beforeRun = context.beforeRun; const promise = beforeRun && typeof beforeRun === 'function' ? beforeRun() : Promise.resolve(); - const {field, value} = context.exclude; + + const {excludeFeatures} = context; + const style = context.style; + this._features = editingLayer.getSource().getFeatures(); - this._features = field ? this._features.filter(feature => { - return feature.get(field) != value; - }) : this._features; - style && this._features.forEach(feature =>{ - feature.setStyle(style) - }); + + if (excludeFeatures) { + this._features = this._features + .filter(feature => { + return Object + .entries(excludeFeatures) + .reduce((bool, [field, value]) => bool && feature.get(field) != value, true) + }) + } + + if (style) { + this._features.forEach(feature =>feature.setStyle(style)); + } promise.then(()=> { this.pickFeatureInteraction = new PickFeatureInteraction({ layers: [editingLayer], features: this._features }); this.addInteraction(this.pickFeatureInteraction); - this.pickFeatureInteraction.on('picked', (e) => { - const relation = e.feature; + this.pickFeatureInteraction.on('picked', evt => { + const relation = evt.feature; inputs.features.push(relation); GUI.setModal(true); d.resolve(inputs); }); - }).catch(err =>{ - console.log(err) - d.reject(); - }); + }).catch(err => d.reject(err)); return d.promise() }; -// metodo eseguito alla disattivazione del tool proto.stop = function() { GUI.setModal(true); this.removeInteraction(this.pickFeatureInteraction); - this._features.forEach(feature => { - feature.setStyle(this._originalLayerStyle); - }); + this._features + .forEach(feature => feature.setStyle(this._originalLayerStyle)); + this.pickFeatureInteraction = null; this._features = null; this._originalLayerStyle = null; return true; }; - module.exports = LinkRelationTask; diff --git a/workflows/steps/tasks/openformtask.js b/workflows/steps/tasks/openformtask.js index ba4515e8..a121a410 100644 --- a/workflows/steps/tasks/openformtask.js +++ b/workflows/steps/tasks/openformtask.js @@ -4,6 +4,7 @@ const {GUI} = g3wsdk.gui; const {WorkflowsStack} = g3wsdk.core.workflow; const EditingTask = require('./editingtask'); const EditingFormComponent = require('../../../form/editingform'); +const EditTableFeaturesWorkflow = require('../../edittableworkflow'); function OpenFormTask(options={}) { this._edit_relations = options.edit_relations === undefined ? true : options._edit_relations; @@ -41,11 +42,13 @@ proto._getForm = async function(inputs, context) { * In case of create a child relation feature set a father relation field value */ if (this._isContentChild) { - const {fatherValue, fatherField} = context; - if (typeof fatherField !== "undefined") { - feature.set(fatherField, fatherValue); - this._originalFeatures[0].set(fatherField, fatherValue); - } + //Are array + const {fatherValue=[], fatherField=[]} = context; + fatherField.forEach((fField, index) => { + feature.set(fField, fatherValue[index]); + this._originalFeatures[0].set(fField, fatherValue[index]); + }) + } this._fields = await this.getFormFields({ inputs, @@ -85,13 +88,13 @@ proto._cancelFnc = function(promise, inputs) { * @param fieldssetAndUnsetSelectedFeaturesStyle * @returns {Promise} */ -proto.saveAll = function(fields){ +proto.saveAll = function(fields) { return new Promise(async (resolve, reject) => { const {session} = this.getContext(); const inputs = this.getInputs(); fields = this._multi ? fields.filter(field => field.value !== null) : fields; if (fields.length) { - await WorkflowsStack.getCurrent().getContext().service.saveDefaultExpressionFieldsNotDependencies(); + await WorkflowsStack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); const newFeatures = []; this._features.forEach(feature =>{ this._originalLayer.setFieldsWithValues(feature, fields); @@ -134,7 +137,7 @@ proto._saveFeatures = async function({fields, promise, session, inputs}){ GUI.disableContent(true); - await WorkflowsStack.getCurrent().getContext().service.saveDefaultExpressionFieldsNotDependencies(); + await WorkflowsStack.getCurrent().getContextService().saveDefaultExpressionFieldsNotDependencies(); GUI.setLoadingContent(false); GUI.disableContent(false); @@ -142,7 +145,6 @@ proto._saveFeatures = async function({fields, promise, session, inputs}){ /** * */ - this._features.forEach(feature =>{ this._originalLayer.setFieldsWithValues(feature, fields); newFeatures.push(feature.clone()); @@ -157,20 +159,22 @@ proto._saveFeatures = async function({fields, promise, session, inputs}){ newFeatures, originalFeatures: this._originalFeatures }).then(()=> { - newFeatures.forEach((newFeature, index)=>{ + newFeatures.forEach((newFeature, index) => { session.pushUpdate(this.layerId, newFeature, this._originalFeatures[index]); }); 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 mean that child is updated so also parent + // In case of save of child it means that child is updated so also parent this._isContentChild && WorkflowsStack.getParents().forEach(workflow => workflow.getContext().service.setUpdate(true, { force: true })); promise.resolve(inputs); }) } else { + GUI.setModal(false); + promise.resolve(inputs); } }; @@ -309,7 +313,14 @@ proto.stop = function() { // when the last feature of features is Array // and is resolved without setting form service // Ex. copy multiple feature from other layer - if (!this._isContentChild) { + if ( + false === this._isContentChild || // no child worklow + ( + //case edit feature of a table (edit layer alphanumeric) + WorkflowsStack.getLength() === 2 && //open features table + WorkflowsStack.getParent() instanceof EditTableFeaturesWorkflow + ) + ) { service.disableMapControlsConflict(false); context = WorkflowsStack.getCurrent().getContextService(); } diff --git a/workflows/steps/tasks/opentabletask.js b/workflows/steps/tasks/opentabletask.js index 6ebcddb1..2bf2f76b 100644 --- a/workflows/steps/tasks/opentabletask.js +++ b/workflows/steps/tasks/opentabletask.js @@ -21,16 +21,18 @@ proto.run = function(inputs, context) { const headers = originalLayer.getEditingFields(); this._isContentChild = WorkflowsStack.getLength() > 1; const foreignKey = this._isContentChild && context.excludeFields ? context.excludeFields[0] : null; - const exclude = this._isContentChild && context.exclude; + const excludeFeatures = this._isContentChild && context.excludeFeatures; const capabilities = originalLayer.getEditingCapabilities(); const editingLayer = originalLayer.getEditingLayer(); let features = editingLayer.readEditingFeatures(); - if (exclude && features.length) { - const {value} = exclude; - features = features.filter(feature => { - const featureValue = feature.get(foreignKey); - return featureValue != value; - }) + if (excludeFeatures && features.length > 0) { + + features = features + .filter(feature => { + return Object + .entries(excludeFeatures) + .reduce((bool, [field, value]) => bool && feature.get(field) != value, true) + }) } const content = new TableComponent({ title: `${layerName}`,