From c2c1d93856363d9ca55ceaee7d09bb96f44130f6 Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Thu, 22 Oct 2026 21:24:19 +0200 Subject: [PATCH 1/5] fix(admin): correct tree filters, translations, and service match The GetFeatureInfo and GetMap filter labels were swapped. Accepting a non-default translation left the form clean, so Save never sent the node. Load-from-service matched a layer name across services. #491 #504 #227 --- .../tree-nodes/tree-nodes.component.html | 4 +- .../tree-nodes/tree-nodes.component.spec.ts | 88 +++++++++++++++++++ .../tree-nodes/tree-nodes.component.ts | 25 ++++-- 3 files changed, 109 insertions(+), 8 deletions(-) diff --git a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.html b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.html index 1dceeba4..38c0af8f 100644 --- a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.html +++ b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.html @@ -244,7 +244,7 @@ @if (currentNodeCartography?.applyFilterToGetFeatureInfo) { - {{ 'entity.layer.applyFilterToGetMap' | translate }} + {{ 'entity.layer.filterInfoByMunicipality' | translate }} @for (option of filterOptions; track option) { @@ -257,7 +257,7 @@ @if (currentNodeCartography?.applyFilterToGetMap) { - {{ 'entity.layer.filterInfoByMunicipality' | translate }} + {{ 'entity.layer.applyFilterToGetMap' | translate }} @for (option of filterOptions; track option) { diff --git a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.spec.ts b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.spec.ts index b9e4c50e..c1845e84 100644 --- a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.spec.ts +++ b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.spec.ts @@ -2510,4 +2510,92 @@ describe('TreeNodesComponent', () => { }); }); + describe('non-default translation accept', () => { + function persistedLeaf(): void { + component['currentNodeId'] = 8; + component['newElement'] = false; + component.currentNodeType = constants.treeDomainKey.cartography; + component.treeNodeForm.patchValue({ + id: 8, + name: 'Leaf', + description: 'Desc', + nodeType: constants.treeDomainKey.cartography, + }); + component.treeNodeForm.markAsPristine(); + } + + it('sends a node update when a non-default name translation is accepted', async () => { + persistedLeaf(); + component.nameTranslations.set(8, new Map()); + const accepted = new Map([['es', { name: 'Nombre' }]]); + jest.spyOn(component['utils'], 'openTranslationDialog').mockResolvedValue({ + event: 'Accept', + data: accepted, + }); + const updates: any[] = []; + component.sendNodeUpdated.subscribe((node) => updates.push(node)); + + await component.onTreeNodeNameTranslationButtonClicked(); + + expect(updates).toHaveLength(1); + expect(updates[0].nameTranslationsModified).toBe(true); + expect(updates[0].status).toBe('Modified'); + }); + + it('sends a node update when a non-default description translation is accepted', async () => { + persistedLeaf(); + component.descriptionTranslations.set(8, new Map()); + const accepted = new Map([['es', { description: 'Descripcion' }]]); + jest.spyOn(component['utils'], 'openTranslationDialog').mockResolvedValue({ + event: 'Accept', + data: accepted, + }); + const updates: any[] = []; + component.sendNodeUpdated.subscribe((node) => updates.push(node)); + + await component.onTreeNodeDescriptionTranslationButtonClicked(); + + expect(updates).toHaveLength(1); + expect(updates[0].descriptionTranslationsModified).toBe(true); + expect(updates[0].status).toBe('Modified'); + }); + }); + + describe('load from service cartography match', () => { + it('matches a repeated capabilities name to the cartography of the selected service', () => { + component.layersList = [ + { id: 1, name: 'Other service', layers: ['AD.Address'], serviceId: 10 }, + { id: 2, name: 'Selected service', layers: ['AD.Address'], serviceId: 20 }, + ]; + + const node = component['createNewNodeWithCapabilities']({ Name: 'AD.Address', Title: 'Address' }, 20); + + expect(node.cartography.id).toBe(2); + expect(node.cartographyName).toBe('Selected service'); + }); + }); + + describe('filter select labels', () => { + function labelFor(controlName: string): string { + const select = fixture.nativeElement.querySelector( + `mat-select[formcontrolname="${controlName}"]` + ); + return select?.closest('mat-form-field')?.querySelector('mat-label')?.textContent?.trim() ?? ''; + } + + it('labels GetFeatureInfo as municipality information and GetMap as map requests', () => { + component.currentNodeCartography = { + applyFilterToGetFeatureInfo: true, + applyFilterToGetMap: true, + }; + setNodeContext(constants.codeValue.treeType.cartography, constants.treeDomainKey.cartography, { + nodeId: 1, + detectChanges: true, + }); + + expect(labelFor('filterGetFeatureInfo')).toBe('entity.layer.filterInfoByMunicipality'); + expect(labelFor('filterGetMap')).toBe('entity.layer.applyFilterToGetMap'); + }); + }); + }); diff --git a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.ts b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.ts index ae499f0f..8e28ae9a 100644 --- a/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.ts +++ b/src/app/components/trees/trees-form/tree-nodes/tree-nodes.component.ts @@ -1710,6 +1710,7 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { nameTranslations: dialogResult.data, nameTranslationsModified: true }); + this.markPersistedTranslationModified(); } } @@ -1743,9 +1744,19 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { descriptionTranslations: dialogResult.data, descriptionTranslationsModified: true }); + this.markPersistedTranslationModified(); } } + private markPersistedTranslationModified(): void { + this.treeNodeForm.markAsDirty(); + if (this.newElement || this.treeNodeForm.get('id')?.value < 0) { + return; + } + this.treeNodeForm.patchValue({ status: "Modified" }); + this.updateNode(); + } + getAllCartographies = (): Observable => { return this.cartographyService.fetchAllItems(); }; @@ -1991,14 +2002,14 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { })); if (capabilitiesResult.success) { const groupLayersResult = this.changeServiceDataByCapabilities(capabilitiesResult.asJson); - this.createNodesWithCapabilities(groupLayersResult, data, null); + this.createNodesWithCapabilities(groupLayersResult, data, null, service.id); } } } } } - private createNodesWithCapabilities(groupLayersResult: Array, existingNodes: Array, parentId?) { + private createNodesWithCapabilities(groupLayersResult: Array, existingNodes: Array, parentId: number | null | undefined, serviceId: number) { groupLayersResult.forEach(element => { let newNode: any = {}; let name = element.Title; @@ -2010,7 +2021,7 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { if (element.Layer) { newNode = this.createNewFolderWithCapabilities(element); } else { - newNode = this.createNewNodeWithCapabilities(element); + newNode = this.createNewNodeWithCapabilities(element, serviceId); } if (newNode) { @@ -2033,7 +2044,7 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { } if (childrenLayers) { - this.createNodesWithCapabilities(childrenLayers, existingNodes, newNode.id); + this.createNodesWithCapabilities(childrenLayers, existingNodes, newNode.id, serviceId); } } } @@ -2064,7 +2075,7 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { return newFolder; } - private createNewNodeWithCapabilities(capability) { + private createNewNodeWithCapabilities(capability, serviceId: number) { const newNode: any = {}; let layersLyr; @@ -2080,7 +2091,9 @@ export class TreeNodesComponent implements OnInit, OnDestroy, OnChanges { if (!layersLyr) { return null; } - const cartography = this.layersList.find(element => element.layers.join() == layersLyr.join()); + const cartography = this.layersList.find(element => + element.layers.join() == layersLyr.join() && this.linkedServiceId(element) === serviceId + ); if (!cartography) { return null; } From 3c9d71b339634d0091b03c8c33cd6909847ecd61 Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Thu, 22 Oct 2026 21:24:20 +0200 Subject: [PATCH 2/5] fix(admin): store the child query parameter name Child task selects submitted the visible label. A parameter whose name and label differ was saved under the label. #506 --- ...ask-more-info-advanced-form.component.html | 8 ++-- ...-more-info-advanced-form.component.spec.ts | 38 +++++++++++++++++++ .../task-more-info-advanced-form.component.ts | 8 +++- .../models/task-more-info-parameter.model.ts | 5 ++- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.html b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.html index 23b3c316..5075bc4c 100644 --- a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.html +++ b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.html @@ -219,7 +219,7 @@

{{ 'entity.task.moreInfoAdvanced.includedTasks' @for (p of taskView.childParameters; track p) { + [value]="p.name"> {{ p.label }} } @@ -346,7 +346,7 @@

{{ 'entity.task.moreInfoAdvanced.includedTasks' @for (p of node.childParameters; track p) { + [value]="p.name"> {{ p.label }} } @@ -406,7 +406,7 @@

{{ 'entity.task.moreInfoAdvanced.includedTasks' @for (p of node.childParameters; track p) { + [value]="p.name"> {{ p.label }} } @@ -461,7 +461,7 @@

{{ 'entity.task.moreInfoAdvanced.includedTasks' @for (p of node.childParameters; track p) { + [value]="p.name"> {{ p.label }} } diff --git a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.spec.ts b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.spec.ts index 8b8c1401..8de3f0f1 100644 --- a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.spec.ts +++ b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.spec.ts @@ -187,6 +187,44 @@ describe('TaskMoreInfoAdvancedFormComponent', () => { }); }); + it('saves the query parameter name when the label differs', () => { + const namedQuery = { + id: 501, + name: 'Named query', + typeId: 5, + typeName: 'Consulta', + properties: { + parameters: [{name: 'code', label: 'Layer code', value: 'ignored'}] + } + }; + component.entityToEdit = { + id: 100, + name: 'Advanced info', + groupId: 1, + cartographyId: 10, + properties: { + parameters: [{label: 'featureCode', value: 'feature_code'}], + childTaskOrderIds: [namedQuery.id] + } + } as any; + (component as any).cartographies = [{id: 10, name: 'Base map'}]; + (component as any).allCandidateTasks = [namedQuery]; + (component as any).templateChildTasks = new Map(); + component.postFetchData(); + + const option = component.getChildParameters(namedQuery as any)[0]; + (component as any).childTaskParameterMappings.set(namedQuery.id, [ + {miaParam: 'featureCode', childParam: option.name} + ]); + + const task = component.createObject(100) as any; + + expect(option.label).toBe('Layer code'); + expect(task.properties.childTaskParameters).toEqual({ + '501': {code: 'feature_code'} + }); + }); + it('deserializes nested template child parameter mappings on edit', () => { setupForm({ childTaskOrderIds: [templateTask.id], diff --git a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.ts b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.ts index 927d6a83..dec6a991 100644 --- a/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.ts +++ b/src/app/components/tasks-more-info-advanced/task-form/task-more-info-advanced-form.component.ts @@ -1084,15 +1084,19 @@ export class TaskMoreInfoAdvancedFormComponent extends BaseFormComponent; + const explicitName = typeof parameter['name'] === 'string' && parameter['name'].length > 0 + ? parameter['name'] + : null; const label = typeof parameter['label'] === 'string' && parameter['label'].length > 0 ? parameter['label'] - : (typeof parameter['name'] === 'string' && parameter['name'].length > 0 ? parameter['name'] : null); + : explicitName; if (!label) { return null; } return { ...(parameter as unknown as Partial), - label + label, + name: explicitName ?? label, } as TaskMoreInfoParameter; } diff --git a/src/app/domain/task/models/task-more-info-parameter.model.ts b/src/app/domain/task/models/task-more-info-parameter.model.ts index ab522f87..bd5740c1 100644 --- a/src/app/domain/task/models/task-more-info-parameter.model.ts +++ b/src/app/domain/task/models/task-more-info-parameter.model.ts @@ -5,6 +5,9 @@ export class TaskMoreInfoParameter { /** label */ public label: string; + /** Parameter name used as the mapping key. Falls back to the label when a name is absent. */ + public name = ''; + public order: number | null; /** value */ @@ -34,7 +37,7 @@ export class TaskMoreInfoParameter { public static fromObject(source: any): TaskMoreInfoParameter { const parameter = new TaskMoreInfoParameter(); const propertiesToCopy = [ - 'label', 'order', 'value', 'description', 'provided' + 'label', 'name', 'order', 'value', 'description', 'provided' ]; propertiesToCopy.forEach(prop => { if (source[prop] !== undefined) { From b81a7c4d04c33084ad09e652bc05074106f338ab Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Thu, 22 Oct 2026 21:24:20 +0200 Subject: [PATCH 3/5] fix(admin): trim the service URL before GetCapabilities A pasted URL with surrounding spaces was sent and stored unchanged. #233 --- .../service-form/service-form.component.spec.ts | 13 +++++++++++++ .../service/service-form/service-form.component.ts | 6 +++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/app/components/service/service-form/service-form.component.spec.ts b/src/app/components/service/service-form/service-form.component.spec.ts index 56f06f13..95f92df7 100644 --- a/src/app/components/service/service-form/service-form.component.spec.ts +++ b/src/app/components/service/service-form/service-form.component.spec.ts @@ -1060,5 +1060,18 @@ describe('ServiceFormComponent', () => { expect(component.loneDetail()).toBe(false); }); }); + + it('trims surrounding spaces from the service URL before GetCapabilities', () => { + component.entityForm.patchValue({ + serviceURL: ' https://example.test/wms ', + type: 'WMS', + authenticationMode: 'none', + }); + + const probe = component['capabilitiesProbe'](); + + expect(probe.url).toBe('https://example.test/wms'); + expect(component.entityForm.get('serviceURL')?.value).toBe('https://example.test/wms'); + }); }); diff --git a/src/app/components/service/service-form/service-form.component.ts b/src/app/components/service/service-form/service-form.component.ts index 0a9b6f20..b4962ac8 100644 --- a/src/app/components/service/service-form/service-form.component.ts +++ b/src/app/components/service/service-form/service-form.component.ts @@ -393,8 +393,12 @@ export class ServiceFormComponent extends BaseFormComponent implements private capabilitiesProbe(): ServiceCapabilitiesProbe { const raw = this.entityForm.getRawValue(); + const url = typeof raw.serviceURL === 'string' ? raw.serviceURL.trim() : raw.serviceURL; + if (url !== raw.serviceURL) { + this.entityForm.get('serviceURL')!.setValue(url); + } const probe: ServiceCapabilitiesProbe = { - url: raw.serviceURL, + url, type: raw.type, authenticationMode: raw.authenticationMode, user: raw.user, From fa879e03f311953f16b6ed7e716df9a995ca62d7 Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Thu, 22 Oct 2026 21:24:20 +0200 Subject: [PATCH 4/5] fix(admin): use feature-type copy on WFS cartography tabs Development-only styles, filters, feature information, and legend strings still said WMS layer when the linked service was WFS. #492 --- .../layers-form/layers-form.component.html | 8 ++--- .../layers-form/layers-form.component.spec.ts | 32 +++++++++++++++++++ .../layers-form/layers-form.component.ts | 5 +++ src/assets/i18n/ca.json | 4 +++ src/assets/i18n/en.json | 4 +++ src/assets/i18n/es.json | 4 +++ src/assets/i18n/fr.json | 4 +++ src/assets/i18n/oc-aranes.json | 4 +++ 8 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/app/components/layers/layers-form/layers-form.component.html b/src/app/components/layers/layers-form/layers-form.component.html index 4a4bb736..9251ae2d 100644 --- a/src/app/components/layers/layers-form/layers-form.component.html +++ b/src/app/components/layers/layers-form/layers-form.component.html @@ -311,7 +311,7 @@ - {{ 'entity.cartography.featureInformation.layerAll' | translate }} + {{ cartographyCopyKey('entity.cartography.featureInformation.layerAll') | translate }} @if (entityForm.get('queryableFeatureAvailable').value !== true) { - {{ 'entity.cartography.filters.applyFilterToGetMap' | translate }} + {{ cartographyCopyKey('entity.cartography.filters.applyFilterToGetMap') | translate }} {{ 'entity.cartography.filters.applyFilterToSpatialSelection' | translate }} @@ -399,7 +399,7 @@

{{ 'entity.cartography.experimentalSitnaProfileNotice' | translate }}

-

{{ 'entity.cartography.legend.sitnaControlCapabilities' | translate }}

+

{{ cartographyCopyKey('entity.cartography.legend.sitnaControlCapabilities') | translate }}

@@ -452,7 +452,7 @@
- {{ 'entity.cartography.styles.useAllStyles' | translate }} + {{ cartographyCopyKey('entity.cartography.styles.useAllStyles') | translate }}

{{ 'entity.cartography.styles.useAllStylesHint' | translate }}

diff --git a/src/app/components/layers/layers-form/layers-form.component.spec.ts b/src/app/components/layers/layers-form/layers-form.component.spec.ts index 830836e0..113c8881 100644 --- a/src/app/components/layers/layers-form/layers-form.component.spec.ts +++ b/src/app/components/layers/layers-form/layers-form.component.spec.ts @@ -896,6 +896,38 @@ describe('LayersFormComponent', () => { expect(territoriesTab).not.toContain('sitmun-cartography-form-entity'); }); + it('uses feature-type wording when the linked service is WFS', () => { + const flags = TestBed.inject(FeatureFlagService); + expect(flags.isFeatureEnabled('LAYERS_LEGEND_TAB_FEATURE')).toBe(true); + expect(flags.isFeatureEnabled('LAYERS_CARTOGRAPHY_STYLES_TAB_FEATURE')).toBe(true); + expect(flags.isFeatureEnabled('LAYERS_FILTERS_TAB_FEATURE')).toBe(true); + expect(flags.isFeatureEnabled('LAYERS_FEATURE_INFORMATION_TAB_FEATURE')).toBe(true); + + component.services = [{ id: 7, type: 'WFS' }]; + component.entityForm.patchValue({ serviceId: 7 }); + + expect(component.cartographyCopyKey('entity.cartography.styles.useAllStyles')) + .toBe('entity.cartography.styles.useAllStyles.wfs'); + expect(component.cartographyCopyKey('entity.cartography.filters.applyFilterToGetMap')) + .toBe('entity.cartography.filters.applyFilterToGetMap.wfs'); + expect(component.cartographyCopyKey('entity.cartography.featureInformation.layerAll')) + .toBe('entity.cartography.featureInformation.layerAll.wfs'); + expect(component.cartographyCopyKey('entity.cartography.legend.sitnaControlCapabilities')) + .toBe('entity.cartography.legend.sitnaControlCapabilities.wfs'); + expect(layersFormTemplate).toContain("cartographyCopyKey('entity.cartography.styles.useAllStyles')"); + expect(layersFormTemplate).toContain("cartographyCopyKey('entity.cartography.filters.applyFilterToGetMap')"); + expect(layersFormTemplate).toContain("cartographyCopyKey('entity.cartography.featureInformation.layerAll')"); + expect(layersFormTemplate).toContain("cartographyCopyKey('entity.cartography.legend.sitnaControlCapabilities')"); + }); + + it('keeps layer wording when the linked service is WMS', () => { + component.services = [{ id: 7, type: 'WMS' }]; + component.entityForm.patchValue({ serviceId: 7 }); + + expect(component.cartographyCopyKey('entity.cartography.styles.useAllStyles')) + .toBe('entity.cartography.styles.useAllStyles'); + }); + it('wraps Permissions and Trees relation grids in matTabContent', () => { const permissionsTab = layersFormTemplate.match( /entity\.cartography\.permissions\.header[\s\S]*?<\/mat-tab>/, diff --git a/src/app/components/layers/layers-form/layers-form.component.ts b/src/app/components/layers/layers-form/layers-form.component.ts index 3e97073e..bd4e8ccb 100644 --- a/src/app/components/layers/layers-form/layers-form.component.ts +++ b/src/app/components/layers/layers-form/layers-form.component.ts @@ -859,6 +859,11 @@ export class LayersFormComponent extends BaseFormComponent item.id === this.entityForm?.get('serviceId')?.value); + return service?.type === 'WFS' ? `${key}.wfs` : key; + } + serviceIsProxied(): boolean { const serviceId = this.entityForm?.get('serviceId')?.value; return this.services.find((service) => service.id === serviceId)?.isProxied === true; diff --git a/src/assets/i18n/ca.json b/src/assets/i18n/ca.json index 51600ff4..714ce350 100644 --- a/src/assets/i18n/ca.json +++ b/src/assets/i18n/ca.json @@ -347,6 +347,7 @@ "entity.cartography.featureInformation.header": "Informació alfanumèrica", "entity.cartography.featureInformation.invalidLayers": "Capes no vàlides", "entity.cartography.featureInformation.layerAll": "Utilitza totes les capes", + "entity.cartography.featureInformation.layerAll.wfs": "Utilitza tots els tipus d'entitat", "entity.cartography.featureInformation.layerFilter": "Utilitza només aquestes capes", "entity.cartography.featureInformation.parameters.format": "Format", "entity.cartography.featureInformation.parameters.name": "Nom", @@ -356,6 +357,7 @@ "entity.cartography.featureInformation.queryable": "La capa o les capes del servei poden ser consultades per a la recuperació d'informació alfanumèrica", "entity.cartography.filters.applyFilterToGetFeatureInfo": "Aplica filtres a les peticions d'informació de la feature", "entity.cartography.filters.applyFilterToGetMap": "Aplica filtres a les peticions de mapa", + "entity.cartography.filters.applyFilterToGetMap.wfs": "Aplica filtres a les peticions del tipus d'entitat", "entity.cartography.filters.applyFilterToSpatialSelection": "Aplica filtres a les peticions de selecció espacial", "entity.cartography.filters.header": "Filtres", "entity.cartography.filters.parameters.column": "Columna", @@ -381,6 +383,7 @@ "entity.cartography.layerSet": "Capa o capes del servei utilitzades", "entity.cartography.layerSet.short": "Capes del servei", "entity.cartography.legend.sitnaControlCapabilities": "Visor: el control de llegenda del mapa SITNA (sitna.legend) mostra el panell de llegenda integrat per a les capes de la taula de continguts / capes de treball. Fa servir la simbologia i els títols de les capacitats OGC de cada servei (per exemple gràfics de llegenda WMS), amb div opcional (el forat per defecte sol ser tc-slot-legend). No llegeix el tipus de llegenda ni la ubicació de llegenda d’aquest formulari.", + "entity.cartography.legend.sitnaControlCapabilities.wfs": "Visor: el control de llegenda del mapa SITNA (sitna.legend) mostra el panell de llegenda integrat per als tipus d'entitat de la taula de continguts / tipus d'entitat de treball. Fa servir la simbologia i els títols de les capacitats OGC de cada servei (per exemple gràfics de llegenda del tipus d'entitat), amb div opcional (el forat per defecte sol ser tc-slot-legend). No llegeix el tipus de llegenda ni la ubicació de llegenda d’aquest formulari.", "entity.cartography.legend.tab": "Llegenda", "entity.cartography.maximumScale": "Escala màxima", "entity.cartography.metadataURL": "Lloc de la metadata de la capa", @@ -439,6 +442,7 @@ "entity.cartography.styles.parameters.url": "URL", "entity.cartography.styles.parameters.width": "Amplada", "entity.cartography.styles.useAllStyles": "Fes servir tots els estils WMS del servei (no només els de la taula)", + "entity.cartography.styles.useAllStyles.wfs": "Fes servir tots els estils del tipus d'entitat del servei (no només els de la taula)", "entity.cartography.styles.useAllStylesHint": "Si està desmarcat, es limita als estils amb nom que configureu a la taula (estil per defecte, enllaços de llegenda, etc.). Si està marcat, la cartografia es tracta com si exposés tots els estils que el servei publica per al conjunt de capes; consulteu la documentació de domini sobre useAllStyles.", "entity.cartography.territories.header": "Territoris", "entity.cartography.territories.parameters.code": "Codi", diff --git a/src/assets/i18n/en.json b/src/assets/i18n/en.json index 0f8a7c8c..575ecf72 100644 --- a/src/assets/i18n/en.json +++ b/src/assets/i18n/en.json @@ -347,6 +347,7 @@ "entity.cartography.featureInformation.header": "Alphanumeric information", "entity.cartography.featureInformation.invalidLayers": "Invalid layers", "entity.cartography.featureInformation.layerAll": "Use all layers", + "entity.cartography.featureInformation.layerAll.wfs": "Use all feature types", "entity.cartography.featureInformation.layerFilter": "Use only these layers", "entity.cartography.featureInformation.parameters.format": "Format", "entity.cartography.featureInformation.parameters.name": "Name", @@ -356,6 +357,7 @@ "entity.cartography.featureInformation.queryable": "Layer or layers from the service can be queried for alphanumeric information", "entity.cartography.filters.applyFilterToGetFeatureInfo": "Apply filters to get feature info requests", "entity.cartography.filters.applyFilterToGetMap": "Apply filters to map requests", + "entity.cartography.filters.applyFilterToGetMap.wfs": "Apply filters to feature type requests", "entity.cartography.filters.applyFilterToSpatialSelection": "Apply filters to spatial selection requests", "entity.cartography.filters.header": "Filters", "entity.cartography.filters.parameters.column": "Column", @@ -381,6 +383,7 @@ "entity.cartography.layerSet": "Layer or layers from the service used", "entity.cartography.layerSet.short": "Service Layers", "entity.cartography.legend.sitnaControlCapabilities": "Viewer: the SITNA map legend control (sitna.legend) shows the built-in legend panel for layers in the table of contents / work layers. It uses symbology and titles from each service’s OGC capabilities (for example WMS legend graphics), with optional div placement (default layout slot is often tc-slot-legend). It does not read the legend type or legend location fields on this form.", + "entity.cartography.legend.sitnaControlCapabilities.wfs": "Viewer: the SITNA map legend control (sitna.legend) shows the built-in legend panel for feature types in the table of contents / work feature types. It uses symbology and titles from each service’s OGC capabilities (for example feature type legend graphics), with optional div placement (default layout slot is often tc-slot-legend). It does not read the legend type or legend location fields on this form.", "entity.cartography.legend.tab": "Legend", "entity.cartography.maximumScale": "Maximum scale", "entity.cartography.metadataURL": "Layer metadata location", @@ -439,6 +442,7 @@ "entity.cartography.styles.parameters.url": "URL", "entity.cartography.styles.parameters.width": "Width", "entity.cartography.styles.useAllStyles": "Use every WMS style from the service (not only the list below)", + "entity.cartography.styles.useAllStyles.wfs": "Use every feature type style from the service (not only the list below)", "entity.cartography.styles.useAllStylesHint": "When unchecked, restrict to the named styles you configure in the table (default style, legend links, etc.). When checked, the cartography is treated as exposing all styles the service publishes for the layer set—see domain documentation for useAllStyles.", "entity.cartography.territories.header": "Territories", "entity.cartography.territories.parameters.code": "Code", diff --git a/src/assets/i18n/es.json b/src/assets/i18n/es.json index 0c81837f..0d10750c 100644 --- a/src/assets/i18n/es.json +++ b/src/assets/i18n/es.json @@ -347,6 +347,7 @@ "entity.cartography.featureInformation.header": "Información alfanumérica", "entity.cartography.featureInformation.invalidLayers": "Capas no válidas", "entity.cartography.featureInformation.layerAll": "Utilizar todas las capas", + "entity.cartography.featureInformation.layerAll.wfs": "Utilizar todos los tipos de entidad", "entity.cartography.featureInformation.layerFilter": "Utilizar solo estas capas", "entity.cartography.featureInformation.parameters.format": "Formato", "entity.cartography.featureInformation.parameters.name": "Nombre", @@ -356,6 +357,7 @@ "entity.cartography.featureInformation.queryable": "La capa o capas del servicio pueden ser consultadas para la recuperación de información alfanumérica", "entity.cartography.filters.applyFilterToGetFeatureInfo": "Aplicar filtros a las peticiones de información de features", "entity.cartography.filters.applyFilterToGetMap": "Aplicar filtros a las peticiones de mapas", + "entity.cartography.filters.applyFilterToGetMap.wfs": "Aplicar filtros a las peticiones del tipo de entidad", "entity.cartography.filters.applyFilterToSpatialSelection": "Aplicar filtros a las peticiones de selección espacial", "entity.cartography.filters.header": "Filtros", "entity.cartography.filters.parameters.column": "Columna", @@ -381,6 +383,7 @@ "entity.cartography.layerSet": "Capa o capas del servicio utilizadas", "entity.cartography.layerSet.short": "Capas del servicio", "entity.cartography.legend.sitnaControlCapabilities": "Visor: el control de leyenda del mapa SITNA (sitna.legend) muestra el panel de leyenda integrado para las capas de la tabla de contenidos / capas de trabajo. Usa la simbología y los títulos de las capacidades OGC de cada servicio (por ejemplo gráficos de leyenda WMS), con div opcional (el hueco por defecto suele ser tc-slot-legend). No utiliza el tipo de leyenda ni la ubicación de leyenda de este formulario.", + "entity.cartography.legend.sitnaControlCapabilities.wfs": "Visor: el control de leyenda del mapa SITNA (sitna.legend) muestra el panel de leyenda integrado para los tipos de entidad de la tabla de contenidos / tipos de entidad de trabajo. Usa la simbología y los títulos de las capacidades OGC de cada servicio (por ejemplo gráficos de leyenda del tipo de entidad), con div opcional (el hueco por defecto suele ser tc-slot-legend). No utiliza el tipo de leyenda ni la ubicación de leyenda de este formulario.", "entity.cartography.legend.tab": "Leyenda", "entity.cartography.maximumScale": "Escala máxima", "entity.cartography.metadataURL": "URL de la metadata de la capa", @@ -439,6 +442,7 @@ "entity.cartography.styles.parameters.url": "URL Leyenda", "entity.cartography.styles.parameters.width": "Ancho Leyenda", "entity.cartography.styles.useAllStyles": "Usar todos los estilos WMS del servicio (no solo los de la tabla)", + "entity.cartography.styles.useAllStyles.wfs": "Usar todos los estilos del tipo de entidad del servicio (no solo los de la tabla)", "entity.cartography.styles.useAllStylesHint": "Si está desmarcado, se limita a los estilos con nombre que configure en la tabla (estilo predeterminado, enlaces de leyenda, etc.). Si está marcado, la cartografía se considera como si publicara todos los estilos que el servicio ofrece para el conjunto de capas; consulte la documentación de dominio sobre useAllStyles.", "entity.cartography.territories.header": "Territorios", "entity.cartography.territories.parameters.code": "Código", diff --git a/src/assets/i18n/fr.json b/src/assets/i18n/fr.json index 259aa9be..083bdd12 100644 --- a/src/assets/i18n/fr.json +++ b/src/assets/i18n/fr.json @@ -347,6 +347,7 @@ "entity.cartography.featureInformation.header": "Information alphanumérique", "entity.cartography.featureInformation.invalidLayers": "Couches non valides", "entity.cartography.featureInformation.layerAll": "Utiliser tous les calques", + "entity.cartography.featureInformation.layerAll.wfs": "Utiliser tous les types d'entité", "entity.cartography.featureInformation.layerFilter": "Utiliser uniquement ces calques", "entity.cartography.featureInformation.parameters.format": "Format", "entity.cartography.featureInformation.parameters.name": "Nom", @@ -356,6 +357,7 @@ "entity.cartography.featureInformation.queryable": "La couche ou les couches du service peuvent être interrogées pour récupérer des informations alphanumériques", "entity.cartography.filters.applyFilterToGetFeatureInfo": "Appliquer les filtres aux requêtes d'information de la caractéristique", "entity.cartography.filters.applyFilterToGetMap": "Appliquer les filtres aux requêtes de carte", + "entity.cartography.filters.applyFilterToGetMap.wfs": "Appliquer les filtres aux requêtes du type d'entité", "entity.cartography.filters.applyFilterToSpatialSelection": "Appliquer les filtres aux requêtes de sélection spatiale", "entity.cartography.filters.header": "Filtres", "entity.cartography.filters.parameters.column": "Colonne", @@ -381,6 +383,7 @@ "entity.cartography.layerSet": "Couche ou couches du service utilisées", "entity.cartography.layerSet.short": "Couches du service", "entity.cartography.legend.sitnaControlCapabilities": "Visionneuse : le contrôle de légende cartographique SITNA (sitna.legend) affiche le panneau de légende intégré pour les couches de la table des matières / couches de travail. Il s’appuie sur la symbologie et les titres des capacités OGC de chaque service (par ex. graphiques de légende WMS), avec une div optionnelle (emplacement par défaut souvent tc-slot-legend). Il n’utilise pas le type de légende ni l’URL de légende de ce formulaire.", + "entity.cartography.legend.sitnaControlCapabilities.wfs": "Visionneuse : le contrôle de légende cartographique SITNA (sitna.legend) affiche le panneau de légende intégré pour les types d'entité de la table des matières / types d'entité de travail. Il s’appuie sur la symbologie et les titres des capacités OGC de chaque service (par ex. graphiques de légende du type d'entité), avec une div optionnelle (emplacement par défaut souvent tc-slot-legend). Il n’utilise pas le type de légende ni l’URL de légende de ce formulaire.", "entity.cartography.legend.tab": "Légende", "entity.cartography.maximumScale": "Échelle maximale", "entity.cartography.metadataURL": "Lieu de la metadata du calque", @@ -439,6 +442,7 @@ "entity.cartography.styles.parameters.url": "URL", "entity.cartography.styles.parameters.width": "Largeur", "entity.cartography.styles.useAllStyles": "Utiliser tous les styles WMS du service (pas seulement ceux du tableau)", + "entity.cartography.styles.useAllStyles.wfs": "Utiliser tous les styles du type d'entité du service (pas seulement ceux du tableau)", "entity.cartography.styles.useAllStylesHint": "Si la case est décochée, limitez-vous aux styles nommés configurés dans le tableau (style par défaut, liens de légende, etc.). Si elle est cochée, la cartographie est considérée comme exposant tous les styles publiés par le service pour le jeu de couches ; voir la documentation métier pour useAllStyles.", "entity.cartography.territories.header": "Territoires", "entity.cartography.territories.parameters.code": "Code", diff --git a/src/assets/i18n/oc-aranes.json b/src/assets/i18n/oc-aranes.json index a24d1819..7da78583 100644 --- a/src/assets/i18n/oc-aranes.json +++ b/src/assets/i18n/oc-aranes.json @@ -347,6 +347,7 @@ "entity.cartography.featureInformation.header": "Informació alfanumèrica", "entity.cartography.featureInformation.invalidLayers": "Capes non vàlides", "entity.cartography.featureInformation.layerAll": "Utilitza totes les capes", + "entity.cartography.featureInformation.layerAll.wfs": "Utilitza toti es tipes d'entitat", "entity.cartography.featureInformation.layerFilter": "Utilitza només aquestes capes", "entity.cartography.featureInformation.parameters.format": "Format", "entity.cartography.featureInformation.parameters.name": "Nom", @@ -356,6 +357,7 @@ "entity.cartography.featureInformation.queryable": "La capa o les capes deth servici poden ser consultades per a la recuperació d'informació alfanumèrica", "entity.cartography.filters.applyFilterToGetFeatureInfo": "Aplicar filtres a les peticions d'informació de la feature", "entity.cartography.filters.applyFilterToGetMap": "Aplicar filtres a les peticions de mapa", + "entity.cartography.filters.applyFilterToGetMap.wfs": "Aplicar filtres a les peticions deth tipe d'entitat", "entity.cartography.filters.applyFilterToSpatialSelection": "Aplicar filtres a les peticions de selecció espacial", "entity.cartography.filters.header": "Filtres", "entity.cartography.filters.parameters.column": "Columna", @@ -381,6 +383,7 @@ "entity.cartography.layerSet": "Capa o capes deth servici utilizadas", "entity.cartography.layerSet.short": "Capes deth servici", "entity.cartography.legend.sitnaControlCapabilities": "Visualizador: eth control de legenda deth mapa SITNA (sitna.legend) mòstra eth panèl de legenda integrat entà es capes dera taula de contenguts / capes de trabalh. Emplegue era simbologia e es títols dera capacitat OGC de cada servici (per exemple gràfics de legenda WMS), amb una div opcional (eth slot per defecte soent eth tc-slot-legend). No emplega eth tipe de legenda nera URL de legenda d’aqueste formulari.", + "entity.cartography.legend.sitnaControlCapabilities.wfs": "Visualizador: eth control de legenda deth mapa SITNA (sitna.legend) mòstra eth panèl de legenda integrat entà es tipes d'entitat dera taula de contenguts / tipes d'entitat de trabalh. Emplegue era simbologia e es títols dera capacitat OGC de cada servici (per exemple gràfics de legenda deth tipe d'entitat), amb una div opcional (eth slot per defecte soent eth tc-slot-legend). No emplega eth tipe de legenda nera URL de legenda d’aqueste formulari.", "entity.cartography.legend.tab": "Legenda", "entity.cartography.maximumScale": "Escala màxima", "entity.cartography.metadataURL": "Lloc de la metadata de la capa", @@ -439,6 +442,7 @@ "entity.cartography.styles.parameters.url": "URL", "entity.cartography.styles.parameters.width": "Mesura", "entity.cartography.styles.useAllStyles": "Emplegue tots es estils WMS deth servici (pas sonque es dera taula)", + "entity.cartography.styles.useAllStyles.wfs": "Emplegue toti es estils deth tipe d'entitat deth servici (pas sonque es dera taula)", "entity.cartography.styles.useAllStylesHint": "Se es desmarca, se limita a es estils amb nom que configure en era taula (estil per defecte, enllaços de legenda, etc.). Se es marca, era cartografia es tracta coma s’expossés tots es estils que eth servici publica entà eth conjunt de capes; consulte era documentacion de domeni subre useAllStyles.", "entity.cartography.territories.header": "Territòris", "entity.cartography.territories.parameters.code": "Còdi", From 7e74750d488c0a58cc10677cb64cbe131dd0cd6c Mon Sep 17 00:00:00 2001 From: Francisco J Lopez-Pellicer Date: Thu, 22 Oct 2026 21:24:20 +0200 Subject: [PATCH 5/5] docs(admin): record the six form fixes --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cddb20ca..32e908b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **Trees**: The GetFeatureInfo filter is labeled as the feature-information filter, and the GetMap filter is labeled as the map filter ([#491](https://github.com/sitmun/sitmun-admin-app/issues/491)). +- **Trees**: Accepting a name or description translation on a saved node marks the form dirty and sends the node update ([#504](https://github.com/sitmun/sitmun-admin-app/issues/504)). +- **Trees**: Loading layers from a service attaches the cartography of that service when two services share a layer name ([#227](https://github.com/sitmun/sitmun-admin-app/issues/227)). +- **More Info Advanced**: A child task mapping stores the query parameter name. The option text stays the label ([#506](https://github.com/sitmun/sitmun-admin-app/issues/506)). +- **Services**: GetCapabilities trims surrounding spaces from the service URL and keeps the trimmed value on the form ([#233](https://github.com/sitmun/sitmun-admin-app/issues/233)). +- **Layers**: On a WFS cartography, the development-only styles, filters, feature-information, and legend copy says feature type. WMS copy is unchanged ([#492](https://github.com/sitmun/sitmun-admin-app/issues/492)). - **Languages**: Set as Default no longer stays disabled after open. Default-language lookups use a single `/api/` slash so Spring Security 7 accepts `findByName`. - **Grids**: AG Grid 32 registers `CommunityFeaturesModule` so text/select cell editors mount. Date columns use the Instant-safe `datePicker` editor with `cellDataType: false` so Positions Alta/Baja edit no longer throws in `DateStringCellEditor`. - **Tests**: Jest specs match Angular 22 realities (`ng-reflect-*` opt-in, HostListener rename, `@if (; as)` syntax, jsdom Location, FileReader under Node 24) and use a 30s default timeout.