From 89869984e854c64b454dedce145d9a846ef5c416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Tue, 26 Nov 2024 11:34:22 +0100 Subject: [PATCH 1/4] Metadata editor / Support add embedded multilingual elements for metadata schemas that require to duplicate the element for each language --- .../java/org/fao/geonet/kernel/EditLib.java | 64 ++++++++++++++---- .../ISO19115_3_2018SchemaPlugin.java | 36 ++++++++++ .../schema/iso19139/ISO19139SchemaPlugin.java | 27 ++++++++ .../schema/MultilingualSchemaPlugin.java | 59 +++++++++++++++++ .../api/records/editing/AjaxEditUtils.java | 66 ++++++++++++++----- .../records/editing/MetadataEditingApi.java | 28 ++++++-- 6 files changed, 244 insertions(+), 36 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/EditLib.java b/core/src/main/java/org/fao/geonet/kernel/EditLib.java index 873b9c3bcdf..516185a3fe8 100644 --- a/core/src/main/java/org/fao/geonet/kernel/EditLib.java +++ b/core/src/main/java/org/fao/geonet/kernel/EditLib.java @@ -3,7 +3,7 @@ //=== EditLib //=== //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -35,7 +35,6 @@ import java.io.StringReader; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.collections.CollectionUtils; @@ -50,6 +49,7 @@ import org.fao.geonet.kernel.schema.MetadataAttribute; import org.fao.geonet.kernel.schema.MetadataSchema; import org.fao.geonet.kernel.schema.MetadataType; +import org.fao.geonet.kernel.schema.MultilingualSchemaPlugin; import org.fao.geonet.kernel.schema.SchemaPlugin; import org.fao.geonet.utils.Xml; import org.jaxen.JaxenException; @@ -223,6 +223,45 @@ public Element addElement(MetadataSchema mdSchema, Element el, String qname) thr return child; } + /** + * Creates an element with a name (qname) as child of an element (el). + * + * If the element to create is multilingual and the related metadata schemas requires to duplicate it, + * one child per metadata language is added. + * + * See {@link org.fao.geonet.kernel.schema.MultilingualSchemaPlugin#duplicateElementsForMultilingual()} + * + * @param mdSchema Metadata schema + * @param el Parent element to add the new element. + * @param qname Name of the new element to add. + * @param languages Languages to add to the new elements. + * @return List of child elements added. For non-multilingual, contains 1 element. + * @throws Exception + */ + public List addElements(MetadataSchema mdSchema, Element el, + String qname, List languages) throws Exception { + + List result = new ArrayList<>(); + + MultilingualSchemaPlugin multilingualSchemaPlugin = (MultilingualSchemaPlugin) mdSchema.getSchemaPlugin(); + + if (!languages.isEmpty() && + multilingualSchemaPlugin.duplicateElementsForMultilingual() && + multilingualSchemaPlugin.isMultilingualElementType(mdSchema.getElementType(qname, el.getName()))) { + for(String language : languages) { + Element child = addElement(mdSchema, el, qname); + ((MultilingualSchemaPlugin) mdSchema.getSchemaPlugin()).addTranslationToElement(child, language, ""); + result.add(child); + } + return result; + } else { + // If no multilingual management is required, process the single element. + result.add(addElement(mdSchema, el, qname)); + } + + return result; + } + /** * Adds XML fragment to the metadata record in the last element of the type of the element in * its parent. @@ -968,7 +1007,7 @@ public void clearVersion(String id) { //-------------------------------------------------------------------------- private List filterOnQname(List children, String qname) { - Vector result = new Vector(); + Vector result = new Vector<>(); for (Element child : children) { if (child.getQualifiedName().equals(qname)) { result.add(child); @@ -1166,7 +1205,7 @@ public List searchChildren(String chName, Element md, String schema) th // boolean hasContent = false; - Vector holder = new Vector(); + Vector holder = new Vector<>(); MetadataSchema mdSchema = scm.getSchema(schema); String chUQname = getUnqualifiedName(chName); @@ -1222,12 +1261,12 @@ public void expandElements(String schema, Element md) throws Exception { MetadataType thisType = mdSchema.getTypeInfo(typeName); if (thisType.hasContainers) { - Vector holder = new Vector(); + Vector holder = new Vector<>(); for (String chName: thisType.getAlElements()) { if (edit_CHOICE_GROUP_SEQUENCE_in(chName)) { List elems = searchChildren(chName, md, schema); - if (elems.size() > 0) { + if (!elems.isEmpty()) { holder.addAll(elems); } } else { @@ -1246,7 +1285,7 @@ public void expandElements(String schema, Element md) throws Exception { * For each container element - descend and collect children. */ private Vector getContainerChildren(Element md) { - Vector result = new Vector(); + Vector result = new Vector<>(); @SuppressWarnings("unchecked") List chChilds = md.getChildren(); @@ -1268,7 +1307,7 @@ private Vector getContainerChildren(Element md) { public void contractElements(Element md) { //--- contract container children at each level in the XML tree - Vector children = new Vector(); + Vector children = new Vector<>(); @SuppressWarnings("unchecked") List childs = md.getContent(); for (Content obj : childs) { @@ -1276,9 +1315,9 @@ public void contractElements(Element md) { Element mdCh = (Element) obj; String mdName = mdCh.getName(); if (edit_CHOICE_GROUP_SEQUENCE_in(mdName)) { - if (mdCh.getChildren().size() > 0) { + if (!mdCh.getChildren().isEmpty()) { Vector chChilds = getContainerChildren(mdCh); - if (chChilds.size() > 0) { + if (!chChilds.isEmpty()) { children.addAll(chChilds); } } @@ -1525,7 +1564,7 @@ private void insertLast(Element md, String childName, String childNS, Element ch @SuppressWarnings("unchecked") List list = md.getChildren(); - List v = new ArrayList(); + List v = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { Element el = list.get(i); @@ -1606,7 +1645,8 @@ public Element createElement(String schema, Element child, Element parent) throw MetadataSchema mds = scm.getSchema(schema); MetadataType mdt = getType(mds, parent); - int min = -1, max = -1; + int min = -1; + int max = -1; for (int i = 0; i < mdt.getElementCount(); i++) { if (childQName.equals(mdt.getElementAt(i))) { diff --git a/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java b/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java index aa73fd0d2fd..d765a91abc5 100644 --- a/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java +++ b/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java @@ -30,6 +30,7 @@ import org.fao.geonet.kernel.schema.LinkPatternStreamer.RawLinkPatternStreamer; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; +import org.jdom.Attribute; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.Namespace; @@ -579,6 +580,41 @@ public Element processElement(Element el, } + @Override + public boolean duplicateElementsForMultilingual() { + return false; + } + + @Override + public List getMetadataLanguages(Element metadata) { + try { + Xml.selectNodes(metadata, ".//mdb:defaultLocale/lan:PT_Locale/@id", allNamespaces.asList()) + .stream() + .filter(node -> node instanceof Attribute) + .map(node -> ((Attribute)node).getValue()) + .filter(s -> s != null && !s.isBlank()) + .filter(Objects::nonNull) + .collect(Collectors.toList()).addAll( + Xml.selectNodes(metadata, ".//mdb:otherLocale/lan:PT_Locale/@id", allNamespaces.asList()) + .stream() + .filter(node -> node instanceof Attribute) + .map(node -> ((Attribute)node).getValue()) + .filter(s -> s != null && !s.isBlank()) + .filter(Objects::nonNull) + .collect(Collectors.toList()) + ); + } catch (JDOMException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + @Override + public boolean isMultilingualElementType(String elementType) { + // Not required in ISO schemas, only required for schemas where duplicateElementsForMultilingual returns true. + return false; + } + /** * Checks if an element requires processing in {@link #processElement(Element, String, String, String)}. * diff --git a/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java b/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java index a5eef93bb38..f7345c8a1ae 100644 --- a/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java +++ b/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java @@ -607,6 +607,33 @@ public Element processElement(Element el, } + @Override + public boolean duplicateElementsForMultilingual() { + return false; + } + + @Override + public List getMetadataLanguages(Element metadata) { + try { + return Xml.selectNodes(metadata, ".//gmd:locale/gmd:PT_Locale/@id", allNamespaces.asList()) + .stream() + .filter(node -> node instanceof Attribute) + .map(node -> ((Attribute)node).getValue()) + .filter(s -> s != null && !s.isBlank()) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } catch (JDOMException e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } + + @Override + public boolean isMultilingualElementType(String elementType) { + // Not required in ISO schemas, only required for schemas where duplicateElementsForMultilingual returns true. + return false; + } + /** * Checks if an element requires processing in {@link #processElement(Element, String, String, String)}. * diff --git a/schemas/schema-core/src/main/java/org/fao/geonet/kernel/schema/MultilingualSchemaPlugin.java b/schemas/schema-core/src/main/java/org/fao/geonet/kernel/schema/MultilingualSchemaPlugin.java index 511e51b1096..effd23e06aa 100644 --- a/schemas/schema-core/src/main/java/org/fao/geonet/kernel/schema/MultilingualSchemaPlugin.java +++ b/schemas/schema-core/src/main/java/org/fao/geonet/kernel/schema/MultilingualSchemaPlugin.java @@ -26,6 +26,7 @@ import org.jdom.Element; import org.jdom.JDOMException; +import java.util.ArrayList; import java.util.List; /** @@ -40,8 +41,66 @@ public interface MultilingualSchemaPlugin { */ public abstract List getTranslationForElement(Element element, String languageIdentifier); + /** + * Updates an element with the related multilingual information using the language and value provided. + * + * @param element XML element to update. + * @param languageIdentifier Language identifier. + * @param value Value for the element. + */ public abstract void addTranslationToElement(Element element, String languageIdentifier, String value); + /** + * Remove all multilingual aspect of an element. + * + * @param element XML element to update. + * @param mdLang Metadata languages. + * @return + * @throws JDOMException + */ public abstract Element removeTranslationFromElement(Element element, List mdLang) throws JDOMException; + /** + * Retrieves the list of metadata languages used in the metadata. + * @param metadata + * @return + */ + public abstract List getMetadataLanguages(Element metadata); + + /** + * Checks if an element type is multilingual. For example, in DCAT schema, rdf:PlainLiteral type. + * + * @param elementType Element type to check. + * @return true if the element type is multilingual, otherwise false. + */ + public abstract boolean isMultilingualElementType(String elementType); + + + /** + * Flag to indicate when adding an element to the metadata editor, if should be duplicated for each metadata language. + * For example, in DCAT schema adding vcard:organization-name in a metadata that has English and French languages + * (similar case for Dublin Core), should duplicate the element for each language: + * + * + * + * + * For ISO profiles should be set to false, as the multilingual elements are not duplicated. Multilingual values + * are added as children elements, requiring a different processing. Adding gmd:organisationName in a metadata + * that has English and French languages: + * + * + * + * + * + * + * + * + * + * + * + * + * + * @return + */ + public abstract boolean duplicateElementsForMultilingual(); } diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java b/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java index cf06d7c9e34..11d538ff125 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -36,6 +36,7 @@ import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.UpdateDatestamp; import org.fao.geonet.kernel.schema.MetadataSchema; +import org.fao.geonet.kernel.schema.MultilingualSchemaPlugin; import org.fao.geonet.kernel.schema.SchemaPlugin; import org.fao.geonet.lib.Lib; import org.fao.geonet.schema.iso19139.ISO19139Namespaces; @@ -340,8 +341,16 @@ public Element getMetadataEmbedded(ServiceContext srvContext, String id, boolean /** * For Ajax Editing : adds an element or an attribute to a metadata element ([add] link). + * + * @param session User session. + * @param id Metadata identifier. + * @param ref Reference of the parent element to add the element. + * @param name Name of the element or attribute to add, with the namespace + * @param childName Empty for inserting element, `geonet:attribute` for attributes. + * @return + * @throws Exception */ - public synchronized Element addElementEmbedded(UserSession session, String id, String ref, String name, String childName) throws Exception { + public synchronized List addElementEmbedded(UserSession session, String id, String ref, String name, String childName) throws Exception { Lib.resource.checkEditPrivilege(context, id); String schema = dataManager.getMetadataSchema(id); //--- get metadata from session @@ -362,10 +371,13 @@ public synchronized Element addElementEmbedded(UserSession session, String id, S md.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); } - Element child = null; + List children = new ArrayList<>(); MetadataSchema mds = dataManager.getSchema(schema); + if (childName != null) { if (childName.equals("geonet:attribute")) { + Element child = null; + String defaultValue = ""; @SuppressWarnings("unchecked") List attributeDefs = el.getChildren(Edit.RootChild.ATTRIBUTE, Edit.NAMESPACE); @@ -385,9 +397,11 @@ public synchronized Element addElementEmbedded(UserSession session, String id, S el.setAttribute(new Attribute(attInfo.two(), defaultValue, attInfo.one())); child = el; + children.add(child); + } else { //--- normal element - child = editLib.addElement(mds, el, name); + Element child = editLib.addElement(mds, el, name); if (!childName.equals("")) { //--- or element String uChildName = editLib.getUnqualifiedName(childName); @@ -403,20 +417,35 @@ public synchronized Element addElementEmbedded(UserSession session, String id, S //--- add mandatory sub-tags editLib.fillElement(schema, child, orChild); } + + children.add(child); } } else { - child = editLib.addElement(mds, el, name); + List metadataLanguages = new ArrayList<>(); + if (mds.getSchemaPlugin() instanceof MultilingualSchemaPlugin) { + // Metadata languages are only required if the schema plugin requires to duplicate the added + // element for each language and the element to add is multilingual. + // See {@link org.fao.geonet.kernel.schema.MultilingualSchemaPlugin#duplicateElementsForMultilingual()} + metadataLanguages = ((MultilingualSchemaPlugin) mds.getSchemaPlugin()).getMetadataLanguages(md); + } + + children = editLib.addElements(mds, el, name, metadataLanguages); } + //--- now enumerate the new child (if not a simple attribute) - if (childName == null || !childName.equals("geonet:attribute")) { - int iRef = editLib.findMaximumRef(md); - editLib.enumerateTreeStartingAt(child, iRef + 1, Integer.parseInt(ref)); - editLib.expandTree(mds, child); + if ((childName == null || !childName.equals("geonet:attribute")) && (children != null)) { + for (Element c: children) { + int iRef = editLib.findMaximumRef(md); + editLib.enumerateTreeStartingAt(c, iRef + 1, Integer.parseInt(ref)); + editLib.expandTree(mds, c); + } } - if (info != null) { - //--- remove and re-attach the info element to the child - child.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); - child.addContent(info); + if ((info != null) && (children != null)) { + for (Element c: children) { + //--- remove and re-attach the info element to the child + c.removeChild(Edit.RootChild.INFO, Edit.NAMESPACE); + c.addContent((Element) info.clone()); + } } /* When adding an gmx:Anchor to an element, due to the following code gets also a gco:CharacterString in EditLib. @@ -436,9 +465,11 @@ public synchronized Element addElementEmbedded(UserSession session, String id, S Element child = isoPlugin.createBasicTypeCharacterString(); element.addContent(child); */ - if (childName != null && childName.equals("gmx:Anchor")) { - if (child.getChild("CharacterString", ISO19139Namespaces.GCO) != null) { - child.removeChild("CharacterString", ISO19139Namespaces.GCO); + if (childName != null && childName.equals("gmx:Anchor") && (children != null)) { + for (Element c: children) { + if (c.getChild("CharacterString", ISO19139Namespaces.GCO) != null) { + c.removeChild("CharacterString", ISO19139Namespaces.GCO); + } } } @@ -451,8 +482,7 @@ public synchronized Element addElementEmbedded(UserSession session, String id, S setMetadataIntoSession(session, (Element) md.clone(), id); // Return element added - return child; - + return children; } /** diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java index 16aaee8d1d5..0551c66936d 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -42,6 +42,7 @@ import org.fao.geonet.api.records.MetadataUtils; import org.fao.geonet.api.records.model.Direction; import org.fao.geonet.api.tools.i18n.LanguageUtils; +import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; import org.fao.geonet.domain.*; @@ -558,7 +559,7 @@ public void cancelEdits(@Parameter(description = API_PARAM_RECORD_UUID, required public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required = true) @PathVariable String metadataUuid, @Parameter(description = "Reference of the insertion point.", required = true) @RequestParam String ref, @Parameter(description = "Name of the element to add (with prefix)", required = true) @RequestParam String name, - @Parameter(description = "Use geonet:attribute for attributes or child name.", required = false) @RequestParam(required = false) String child, + @Parameter(description = "Empty for inserting element, `geonet:attribute` for attributes.", required = false) @RequestParam(required = false) String child, @Parameter(description = "Should attributes be shown on the editor snippet?", required = false) @RequestParam(defaultValue = "false") boolean displayAttributes, @Parameter(hidden = true) @RequestParam Map allRequestParams, HttpServletRequest request, HttpServletResponse response, @@ -580,11 +581,26 @@ public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required // -- Note that the metadata-embedded.xsl stylesheet // -- only applies the templating to the added element, not to // -- the entire metadata so performance should not be a big issue - Element elResp = new AjaxEditUtils(context).addElementEmbedded(ApiUtils.getUserSession(httpSession), + List elResp = new AjaxEditUtils(context).addElementEmbedded(ApiUtils.getUserSession(httpSession), String.valueOf(metadata.getId()), ref, name, child); - EditLib.tagForDisplay(elResp); - Element md = (Element) findRoot(elResp).clone(); - EditLib.removeDisplayTag(elResp); + Element md = null; + + EditLib editLib = context.getBean(DataManager.class).getEditLib(); + + for(Element el: elResp) { + if (md == null) { + EditLib.tagForDisplay(el); + md = (Element) findRoot(el).clone(); + EditLib.removeDisplayTag(el); + } else { + Element el2 = editLib.findElement(md, el.getChild("element", Edit.NAMESPACE).getAttribute("ref").getValue()); + EditLib.tagForDisplay(el2); + md = (Element) md.clone(); + EditLib.removeDisplayTag(el2); + } + + } + buildEditorForm(allRequestParams.get("currTab"), httpSession, allRequestParams, request, md, metadata.getDataInfo().getSchemaId(), context, applicationContext, true, true, response); From fa4496129a641ec82480820f9d3e8c89ffedce5d Mon Sep 17 00:00:00 2001 From: Francois Prunayre Date: Tue, 26 Nov 2024 14:04:29 +0100 Subject: [PATCH 2/4] Metadata editor / API / Ordering up and down / Add support for plugin without container element (eg. DCAT-AP). Current API only support to move one element up or down. In DCAT-AP, multilingual record requires to move a group of element containing all translations up or down. --- .../api/records/editing/AjaxEditUtils.java | 84 +++++++++++++++---- 1 file changed, 70 insertions(+), 14 deletions(-) diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java b/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java index 11d538ff125..a1af85ff127 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/AjaxEditUtils.java @@ -622,8 +622,6 @@ private Pair parseAttributeName(String attributeName, String public synchronized void swapElementEmbedded(UserSession session, String id, String ref, boolean down) throws Exception { Lib.resource.checkEditPrivilege(context, id); - dataManager.getMetadataSchema(id); - //--- get metadata from session Element md = getMetadataFromSession(session, id); @@ -635,29 +633,87 @@ public synchronized void swapElementEmbedded(UserSession session, String id, Str throw new IllegalStateException(EditLib.MSG_ELEMENT_NOT_FOUND_AT_REF + ref); //--- swap the elements - int iSwapIndex = -1; - @SuppressWarnings("unchecked") + // For DCAT records, swap all elements of a translation group + // ie. all elements sibling with same name and different language code List list = elSwap.getParentElement().getChildren(elSwap.getName(), elSwap.getNamespace()); - int i = -1; - for (Element element : list) { - i++; - if (element == elSwap) { - iSwapIndex = i; - break; - } + String schemaId = dataManager.getMetadataSchema(id); + MetadataSchema metadataSchema = dataManager.getSchema(schemaId); + SchemaPlugin schemaPlugin = metadataSchema.getSchemaPlugin(); + List languages = new ArrayList<>(); + if (schemaPlugin instanceof MultilingualSchemaPlugin) { + languages = ((MultilingualSchemaPlugin) schemaPlugin).getMetadataLanguages(md); } - if (iSwapIndex == -1) + // Get element index and first index of the group + int swapIndex = getElementSwapIndex(list, elSwap); + int groupSwapIndex = getGroupSwapIndex(list, swapIndex, languages); + + if (swapIndex == -1) throw new IllegalStateException("Index not found for element --> " + elSwap); - if (down) swapElements(elSwap, list.get(iSwapIndex + 1)); - else swapElements(elSwap, list.get(iSwapIndex - 1)); + // Swap the element or all the group of element up or down + if (groupSwapIndex == -1) { + swapElements(elSwap, list.get(down ? swapIndex + 1 : swapIndex - 1)); + } else { + for (int i = 0; i < languages.size(); i ++) { + int currentElementToSwapIndex = groupSwapIndex + i; + Element topElement = list.get(currentElementToSwapIndex); + Element bottomElement = list.get(currentElementToSwapIndex + languages.size()); + if (down) { + swapElements(topElement, bottomElement); + } else { + swapElements(bottomElement, topElement); + } + } + } //--- store the metadata in the session again setMetadataIntoSession(session, (Element) md.clone(), id); + } + + /** + * Swap index is the target element from the API call. + * Can be one element of a group of translations. + * Collect here the index of the first element of the group. + * + *

+ * The list of languages is ordered. eg. "nl", "fr", "en" + *

+     *             Rivier
+     *             Rivière
+     *             River
+     *             Kwaliteit
+     *             Qualité
+     *             Qualiy
+     * 
+ *

+ */ + private int getGroupSwapIndex(List list, int swapIndex, List languages) { + String elementLanguage = list.get(swapIndex).getAttributeValue("lang", Namespace.XML_NAMESPACE); + if (list.size() == 1 || languages.size() == 1 || elementLanguage == null) { + return -1; + } + + // Consider element are always in order (see update-fixed-info.xsl) + // River index = 3, group index = 3 - (en lang index) + int indexInGroup = languages.indexOf(elementLanguage); + if (indexInGroup == -1) { + return swapIndex; // Language not declared? + } + return swapIndex - indexInGroup; + } + private static int getElementSwapIndex(List list, Element elSwap) { + int i = -1; + for (Element element : list) { + i++; + if (element == elSwap) { + return i; + } + } + return i; } /** From 3d7a65cb515f090e04d4e169a43a4e0180af7f9d Mon Sep 17 00:00:00 2001 From: Francois Prunayre Date: Mon, 9 Dec 2024 09:11:17 +0100 Subject: [PATCH 3/4] Metadata editor / Support add embedded multilingual / Simplify XPath and sonarlint. --- .../ISO19115_3_2018SchemaPlugin.java | 18 ++++-------------- .../schema/iso19139/ISO19139SchemaPlugin.java | 6 ++---- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java b/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java index d765a91abc5..261c2b1ae78 100644 --- a/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java +++ b/schemas/iso19115-3.2018/src/main/java/org/fao/geonet/schema/iso19115_3_2018/ISO19115_3_2018SchemaPlugin.java @@ -588,23 +588,13 @@ public boolean duplicateElementsForMultilingual() { @Override public List getMetadataLanguages(Element metadata) { try { - Xml.selectNodes(metadata, ".//mdb:defaultLocale/lan:PT_Locale/@id", allNamespaces.asList()) + return Xml.selectNodes(metadata, ".//*[name() = 'mdb:defaultLocale' or name() = 'mdb:otherLocale']/lan:PT_Locale/@id", allNamespaces.asList()) .stream() - .filter(node -> node instanceof Attribute) + .filter(Attribute.class::isInstance) .map(node -> ((Attribute)node).getValue()) .filter(s -> s != null && !s.isBlank()) - .filter(Objects::nonNull) - .collect(Collectors.toList()).addAll( - Xml.selectNodes(metadata, ".//mdb:otherLocale/lan:PT_Locale/@id", allNamespaces.asList()) - .stream() - .filter(node -> node instanceof Attribute) - .map(node -> ((Attribute)node).getValue()) - .filter(s -> s != null && !s.isBlank()) - .filter(Objects::nonNull) - .collect(Collectors.toList()) - ); - } catch (JDOMException e) { - e.printStackTrace(); + .collect(Collectors.toList()); + } catch (JDOMException ignored) { } return Collections.emptyList(); } diff --git a/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java b/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java index f7345c8a1ae..e3a48470065 100644 --- a/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java +++ b/schemas/iso19139/src/main/java/org/fao/geonet/schema/iso19139/ISO19139SchemaPlugin.java @@ -617,13 +617,11 @@ public List getMetadataLanguages(Element metadata) { try { return Xml.selectNodes(metadata, ".//gmd:locale/gmd:PT_Locale/@id", allNamespaces.asList()) .stream() - .filter(node -> node instanceof Attribute) + .filter(Attribute.class::isInstance) .map(node -> ((Attribute)node).getValue()) .filter(s -> s != null && !s.isBlank()) - .filter(Objects::nonNull) .collect(Collectors.toList()); - } catch (JDOMException e) { - e.printStackTrace(); + } catch (JDOMException ignored) { } return Collections.emptyList(); } From 1e51f4d0c345f6509a5431f5d48fb5e175ca1056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Wed, 11 Dec 2024 12:07:46 +0100 Subject: [PATCH 4/4] Metadata editor / Support add embedded multilingual / Check if a metadata schema is multilingual before doing a cast to MultilingualSchemaPlugin --- .../java/org/fao/geonet/kernel/EditLib.java | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/fao/geonet/kernel/EditLib.java b/core/src/main/java/org/fao/geonet/kernel/EditLib.java index 516185a3fe8..ce3733fdc85 100644 --- a/core/src/main/java/org/fao/geonet/kernel/EditLib.java +++ b/core/src/main/java/org/fao/geonet/kernel/EditLib.java @@ -243,22 +243,24 @@ public List addElements(MetadataSchema mdSchema, Element el, List result = new ArrayList<>(); - MultilingualSchemaPlugin multilingualSchemaPlugin = (MultilingualSchemaPlugin) mdSchema.getSchemaPlugin(); - - if (!languages.isEmpty() && - multilingualSchemaPlugin.duplicateElementsForMultilingual() && - multilingualSchemaPlugin.isMultilingualElementType(mdSchema.getElementType(qname, el.getName()))) { - for(String language : languages) { - Element child = addElement(mdSchema, el, qname); - ((MultilingualSchemaPlugin) mdSchema.getSchemaPlugin()).addTranslationToElement(child, language, ""); - result.add(child); + if (mdSchema.getSchemaPlugin() instanceof MultilingualSchemaPlugin) { + MultilingualSchemaPlugin multilingualSchemaPlugin = (MultilingualSchemaPlugin) mdSchema.getSchemaPlugin(); + + if (!languages.isEmpty() && + multilingualSchemaPlugin.duplicateElementsForMultilingual() && + multilingualSchemaPlugin.isMultilingualElementType(mdSchema.getElementType(qname, el.getName()))) { + for(String language : languages) { + Element child = addElement(mdSchema, el, qname); + ((MultilingualSchemaPlugin) mdSchema.getSchemaPlugin()).addTranslationToElement(child, language, ""); + result.add(child); + } + return result; } - return result; - } else { - // If no multilingual management is required, process the single element. - result.add(addElement(mdSchema, el, qname)); } + // If no multilingual management is required, process the single element. + result.add(addElement(mdSchema, el, qname)); + return result; }