From 2390a3c62ae52a0cd2e39c8c419d273a5ddb7fae Mon Sep 17 00:00:00 2001
From: christophe mangeat <christophe.mangeat@camptocamp.com>
Date: Wed, 18 Dec 2024 18:20:43 +0100
Subject: [PATCH] from
 https://github.com/geonetwork/core-geonetwork/issues/7431

---
 .../plugin/iso19139.che/convert/functions.xsl |  90 ----
 .../plugin/iso19139.che/extract-relations.xsl | 185 ++++++--
 .../iso19139.che/index-fields/index.xsl       | 428 +++++++++++++-----
 .../iso19139.che/process/onlinesrc-add.xsl    | 351 +++++++++-----
 .../iso19139.che/process/onlinesrc-remove.xsl |  93 +++-
 5 files changed, 765 insertions(+), 382 deletions(-)
 delete mode 100644 iso19139.che/src/main/plugin/iso19139.che/convert/functions.xsl

diff --git a/iso19139.che/src/main/plugin/iso19139.che/convert/functions.xsl b/iso19139.che/src/main/plugin/iso19139.che/convert/functions.xsl
deleted file mode 100644
index ea1b73f3f6..0000000000
--- a/iso19139.che/src/main/plugin/iso19139.che/convert/functions.xsl
+++ /dev/null
@@ -1,90 +0,0 @@
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-                xmlns:date="http://exslt.org/dates-and-times"
-                xmlns:joda="java:org.fao.geonet.domain.ISODate"
-                xmlns:mime="java:org.fao.geonet.util.MimeTypeFinder"
-                version="2.0">
-
-  <!-- ================================================================== -->
-
-  <xsl:template name="fixSingle">
-    <xsl:param name="value"/>
-
-    <xsl:choose>
-      <xsl:when test="string-length(string($value))=1">
-        <xsl:value-of select="concat('0',$value)"/>
-      </xsl:when>
-      <xsl:otherwise>
-        <xsl:value-of select="$value"/>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-  <!-- ================================================================== -->
-
-  <xsl:template name="getMimeTypeFile">
-    <xsl:param name="datadir"/>
-    <xsl:param name="fname"/>
-    <xsl:value-of select="mime:detectMimeTypeFile($datadir,$fname)"/>
-  </xsl:template>
-
-  <!-- ==================================================================== -->
-
-  <xsl:template name="getMimeTypeUrl">
-    <xsl:param name="linkage"/>
-    <xsl:value-of select="mime:detectMimeTypeUrl($linkage)"/>
-  </xsl:template>
-
-  <!-- ==================================================================== -->
-  <xsl:template name="fixNonIso">
-    <xsl:param name="value"/>
-
-    <xsl:variable name="now" select="date:date-time()"/>
-    <xsl:choose>
-      <xsl:when
-        test="$value='' or lower-case($value)='unknown' or lower-case($value)='current' or lower-case($value)='now'">
-        <xsl:variable name="miy" select="date:month-in-year($now)"/>
-        <xsl:variable name="month">
-          <xsl:call-template name="fixSingle">
-            <xsl:with-param name="value" select="$miy"/>
-          </xsl:call-template>
-        </xsl:variable>
-        <xsl:variable name="dim" select="date:day-in-month($now)"/>
-        <xsl:variable name="day">
-          <xsl:call-template name="fixSingle">
-            <xsl:with-param name="value" select="$dim"/>
-          </xsl:call-template>
-        </xsl:variable>
-        <xsl:value-of select="concat(date:year($now),'-',$month,'-',$day,'T23:59:59')"/>
-      </xsl:when>
-      <xsl:otherwise>
-        <xsl:value-of select="$value"/>
-      </xsl:otherwise>
-    </xsl:choose>
-  </xsl:template>
-
-  <!-- ==================================================================== -->
-
-  <xsl:template name="newGmlTime">
-    <xsl:param name="begin"/>
-    <xsl:param name="end"/>
-
-
-    <xsl:variable name="value1">
-      <xsl:call-template name="fixNonIso">
-        <xsl:with-param name="value" select="normalize-space($begin)"/>
-      </xsl:call-template>
-    </xsl:variable>
-
-    <xsl:variable name="value2">
-      <xsl:call-template name="fixNonIso">
-        <xsl:with-param name="value" select="normalize-space($end)"/>
-      </xsl:call-template>
-    </xsl:variable>
-
-    <!-- must be a full ISODateTimeFormat - so parse it and make sure it is
-             returned as a long format using the joda Java Time library -->
-    <xsl:variable name="output" select="joda:parseISODateTimes($value1,$value2)"/>
-    <xsl:value-of select="$output"/>
-
-  </xsl:template>
-</xsl:stylesheet>
diff --git a/iso19139.che/src/main/plugin/iso19139.che/extract-relations.xsl b/iso19139.che/src/main/plugin/iso19139.che/extract-relations.xsl
index 1dc8ad3806..9f0ba91aae 100644
--- a/iso19139.che/src/main/plugin/iso19139.che/extract-relations.xsl
+++ b/iso19139.che/src/main/plugin/iso19139.che/extract-relations.xsl
@@ -1,4 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2001-2016 Food and Agriculture Organization of the
+  ~ United Nations (FAO-UN), United Nations World Food Programme (WFP)
+  ~ and United Nations Environment Programme (UNEP)
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or (at
+  ~ your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful, but
+  ~ WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+  ~
+  ~ Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
+  ~ Rome - Italy. email: geonetwork@osgeo.org
+  -->
+
 <!--
   Create a simple XML tree for relation description.
   <relations>
@@ -9,15 +32,40 @@
                 xmlns:gmd="http://www.isotc211.org/2005/gmd"
                 xmlns:gco="http://www.isotc211.org/2005/gco"
                 xmlns:gmx="http://www.isotc211.org/2005/gmx"
-                xmlns:geonet="http://www.fao.org/geonetwork"
-                xmlns:che="http://www.geocat.ch/2008/che"
+                xmlns:xlink="http://www.w3.org/1999/xlink"
                 xmlns:util="java:org.fao.geonet.util.XslUtil"
+                xmlns:digestUtils="java:org.apache.commons.codec.digest.DigestUtils"
                 xmlns:gn-fn-rel="http://geonetwork-opensource.org/xsl/functions/relations"
+                xmlns:che="http://www.geocat.ch/2008/che"
                 version="2.0"
                 exclude-result-prefixes="#all">
 
   <!-- Convert an element gco:CharacterString
   to the GN localized string structure -->
+  <xsl:template mode="get-iso19139-localized-string" match="*">
+
+    <xsl:variable name="mainLanguage">
+      <xsl:call-template name="langId_from_gmdlanguage19139">
+          <xsl:with-param name="gmdlanguage" select="ancestor::metadata/*[@gco:isoType='gmd:MD_Metadata' or name()='gmd:MD_Metadata']/gmd:language"/>
+      </xsl:call-template>
+    </xsl:variable>
+
+    <xsl:for-each select="gco:CharacterString|gmx:Anchor|
+                          gmd:PT_FreeText/*/gmd:LocalisedCharacterString">
+      <xsl:variable name="localeId"
+                    select="substring-after(@locale, '#')"/>
+
+      <value lang="{if (@locale)
+                  then ancestor::metadata/*[@gco:isoType='gmd:MD_Metadata' or name()='gmd:MD_Metadata']/gmd:locale/*[@id = $localeId]/gmd:languageCode/*/@codeListValue
+                  else if ($mainLanguage) then $mainLanguage else $lang}">
+        <xsl:copy-of select="@xlink:href"/>
+        <xsl:value-of select="."/>
+      </value>
+    </xsl:for-each>
+  </xsl:template>
+
+  <!-- Convert an element
+  to the GN localized url structure -->
   <xsl:template mode="get-iso19139-localized-url" match="*">
 
     <xsl:variable name="metadata"
@@ -46,26 +94,38 @@
 
 
   <!-- Relation contained in the metadata record has to be returned
-  It could be document or thumbnails
+  It could be a document or thumbnails
   -->
   <xsl:template mode="relation"
                 match="metadata[gmd:MD_Metadata or *[contains(@gco:isoType, 'MD_Metadata')]]"
                 priority="299">
 
-    <xsl:if test="count(*/descendant::*[name(.) = 'gmd:graphicOverview']/*) > 0">
+    <xsl:variable name="mainLanguage">
+      <xsl:call-template name="langId_from_gmdlanguage19139">
+          <xsl:with-param name="gmdlanguage" select="*/gmd:language"/>
+      </xsl:call-template>
+    </xsl:variable>
+
+    <xsl:if test="count(*//gmd:graphicOverview) > 0">
       <thumbnails>
-        <xsl:for-each select="*/descendant::*[name(.) = 'gmd:graphicOverview']/*">
+        <xsl:for-each select="*//gmd:graphicOverview">
           <item>
             <id>
-              <xsl:value-of select="gmd:fileName/gco:CharacterString"/>
+              <xsl:value-of select="gmd:MD_BrowseGraphic/gmd:fileName/gco:CharacterString"/>
             </id>
+            <idx>
+              <xsl:value-of select="position()"/>
+            </idx>
+            <hash>
+              <xsl:value-of select="digestUtils:md5Hex(normalize-space(.))"/>
+            </hash>
             <url>
               <xsl:apply-templates mode="get-iso19139-localized-string"
-                                   select="gmd:fileName"/>
+                                   select="gmd:MD_BrowseGraphic/gmd:fileName"/>
             </url>
             <title>
               <xsl:apply-templates mode="get-iso19139-localized-string"
-                                   select="gmd:fileDescription"/>
+                                   select="gmd:MD_BrowseGraphic/gmd:fileDescription"/>
             </title>
             <type>thumbnail</type>
           </item>
@@ -73,48 +133,85 @@
       </thumbnails>
     </xsl:if>
 
-    <xsl:variable name="links" select="*/descendant::*[name(.) = 'gmd:onLine']/*[
-                                    gmd:linkage/gmd:URL!='' or
-                                    gmd:linkage/che:PT_FreeURL//che:LocalisedURL[text() != ''] or
-                                    gmd:linkage/che:LocalisedURL!='']"/>
-
+    <xsl:variable name="links" select="*/gmd:onLine"/>
     <xsl:if test="count($links) > 0">
       <onlines>
         <xsl:for-each select="$links">
-
+          <xsl:if test="gmd:CI_OnlineResource[gmd:linkage/gmd:URL!='' or gmd:linkage/che:PT_FreeURL//che:LocalisedURL[text() != ''] or gmd:linkage/che:LocalisedURL!='']">
           <item>
-            <xsl:variable name="langCode">
-              <xsl:value-of select="concat('#', upper-case(util:twoCharLangCode($lang, 'EN')))"/>
-            </xsl:variable>
-            <xsl:variable name="url" select="gmd:linkage/gmd:URL"/>
-            <id>
-              <xsl:value-of select="$url"/>
-            </id>
-            <title>
-              <xsl:apply-templates mode="get-iso19139-localized-string"
-                                   select="gmd:name"/>
-            </title>
-            <url>
-              <xsl:apply-templates mode="get-iso19139-localized-url"
-                                   select="gmd:linkage"/>
-            </url>
-            <function>
-              <xsl:value-of select="gmd:function/*/@codeListValue"/>
-            </function>
-            <applicationProfile>
-              <xsl:value-of select="gmd:applicationProfile/gco:CharacterString"/>
-            </applicationProfile>
-            <description>
-              <xsl:apply-templates mode="get-iso19139-localized-string"
-                                   select="gmd:description"/>
-            </description>
-            <protocol>
-              <xsl:value-of select="gn-fn-rel:translate(gmd:protocol, $langCode)"/>
-            </protocol>
-            <type>onlinesrc</type>
-          </item>
+              <xsl:variable name="langCode">
+                <xsl:value-of select="concat('#', upper-case(util:twoCharLangCode($lang, 'EN')))"/>
+              </xsl:variable>
+              <xsl:variable name="url" select="gmd:CI_OnlineResource/gmd:linkage/gmd:URL"/>
+              <id>
+                <xsl:value-of select="$url"/>
+              </id>
+              <idx>
+                <xsl:value-of select="position()"/>
+              </idx>
+              <hash>
+                <xsl:value-of select="digestUtils:md5Hex(normalize-space(.))"/>
+              </hash>
+              <title>
+                <xsl:apply-templates mode="get-iso19139-localized-string"
+                                     select="gmd:CI_OnlineResource/gmd:name"/>
+              </title>
+              <url>
+                <xsl:apply-templates mode="get-iso19139-localized-url" select="gmd:linkage"/>
+              </url>
+              <function>
+                <xsl:value-of select="gmd:CI_OnlineResource/gmd:function/*/@codeListValue"/>
+              </function>
+              <applicationProfile>
+                <xsl:value-of select="gmd:CI_OnlineResource/gmd:applicationProfile/*/text()"/>
+              </applicationProfile>
+              <description>
+                <xsl:apply-templates mode="get-iso19139-localized-string"
+                                     select="gmd:CI_OnlineResource/gmd:description"/>
+              </description>
+              <protocol>
+                <xsl:value-of select="gn-fn-rel:translate(gmd:CI_OnlineResource/gmd:protocol, $langCode)"/>
+              </protocol>
+              <mimeType>
+                <xsl:value-of select="if (gmd:CI_OnlineResource/*/gmx:MimeFileType)
+                                  then gmd:CI_OnlineResource/*/gmx:MimeFileType/@type
+                                  else if (starts-with(gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString, 'WWW:DOWNLOAD:'))
+                                  then replace(gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString, 'WWW:DOWNLOAD:', '')
+                                  else ''"/>
+              </mimeType>
+              <type>onlinesrc</type>
+            </item>
+          </xsl:if>
         </xsl:for-each>
       </onlines>
     </xsl:if>
+<!--
+    <xsl:if test="count(*//gco:CharacterString[contains(., 'http')] > 0">
+      <embeddedLinks>
+        <xsl:for-each select="*//gco:CharacterString[contains(., 'http')]">
+          <xsl:analyze-string select="."
+                              regex="(regextforurl)*">
+
+            <xsl:matching-substring>
+              <item>
+                <xsl:variable name="langCode">
+                  <xsl:value-of select="concat('#', upper-case(util:twoCharLangCode($lang, 'EN')))"/>
+                </xsl:variable>
+                <xsl:variable name="url" select="regex-group(1)"/>
+                <id>
+                  <xsl:value-of select="regex-group(1)"/>
+                </id>
+                <url>
+                  <value lang="{$mainLanguage}">
+                    <xsl:value-of select="regex-group(1)"/>
+                  </value>
+                </url>
+                <type>embeddedLinks</type>
+              </item>
+            </xsl:matching-substring>
+          </xsl:analyze-string>
+        </xsl:for-each>
+      </embeddedLinks>
+    </xsl:if>-->
   </xsl:template>
 </xsl:stylesheet>
diff --git a/iso19139.che/src/main/plugin/iso19139.che/index-fields/index.xsl b/iso19139.che/src/main/plugin/iso19139.che/index-fields/index.xsl
index f24d092e61..14161bb0d7 100644
--- a/iso19139.che/src/main/plugin/iso19139.che/index-fields/index.xsl
+++ b/iso19139.che/src/main/plugin/iso19139.che/index-fields/index.xsl
@@ -34,6 +34,7 @@
                 xmlns:che="http://www.geocat.ch/2008/che"
                 xmlns:gn-fn-index="http://geonetwork-opensource.org/xsl/functions/index"
                 xmlns:index="java:org.fao.geonet.kernel.search.EsSearchManager"
+                xmlns:digestUtils="java:org.apache.commons.codec.digest.DigestUtils"
                 xmlns:util="java:org.fao.geonet.util.XslUtil"
                 xmlns:date-util="java:org.fao.geonet.utils.DateUtil"
                 xmlns:daobs="http://daobs.org"
@@ -54,7 +55,7 @@
               encoding="utf-8"
               escape-uri-attributes="yes"/>
 
-
+  <xsl:param name="fastIndexMode" select="true()"/>
 
   <!-- If identification creation, publication and revision date
     should be indexed as a temporal extent information (eg. in INSPIRE
@@ -72,6 +73,7 @@
   <!-- Parent may be encoded using an associatedResource.
   Define which association type should be considered as parent. -->
   <xsl:variable name="parentAssociatedResourceType" select="'partOfSeamlessDatabase'"/>
+  <xsl:variable name="childrenAssociatedResourceType" select="'isComposedOf'"/>
 
   <xsl:template match="/">
     <xsl:apply-templates mode="index"/>
@@ -139,6 +141,7 @@
 
     <!-- Create a first document representing the main record. -->
     <doc>
+
       <xsl:copy-of select="gn-fn-index:add-field('docType', 'metadata')"/>
 
       <!-- Index the metadata document as XML -->
@@ -156,6 +159,10 @@
         <xsl:copy-of select="gn-fn-index:add-multilingual-field('standardVersion', ., $allLanguages)"/>
       </xsl:for-each>
 
+      <xsl:for-each select="gmd:hierarchyLevelName">
+        <xsl:copy-of select="gn-fn-index:add-multilingual-field('resourceTypeName', ., $allLanguages)"/>
+      </xsl:for-each>
+
       <!-- Since GN sets the timezone in system/server/timeZone setting as Java system default
         timezone we can rely on XSLT functions to get current date in the right timezone -->
       <indexingDate>
@@ -191,19 +198,6 @@
       </xsl:for-each>
 
       <!-- # Resource type -->
-      <xsl:choose>
-        <xsl:when test="$isDataset">
-          <resourceType>dataset</resourceType>
-        </xsl:when>
-        <xsl:otherwise>
-          <xsl:for-each select="gmd:hierarchyLevel/*/@codeListValue[normalize-space(.) != '']">
-            <resourceType>
-              <xsl:value-of select="."/>
-            </resourceType>
-          </xsl:for-each>
-        </xsl:otherwise>
-      </xsl:choose>
-
       <xsl:variable name="isMapDigital"
                     select="count(gmd:identificationInfo/*/gmd:citation/*/gmd:presentationForm[*/@codeListValue = 'mapDigital']) > 0"/>
       <xsl:variable name="isStatic"
@@ -216,16 +210,25 @@
       <xsl:choose>
         <xsl:when test="$isDataset and $isMapDigital and
                             ($isStatic or $isInteractive or $isPublishedWithWMCProtocol)">
-          <resourceType>map</resourceType>
           <xsl:choose>
             <xsl:when test="$isStatic">
-              <resourceType>map/static</resourceType>
+              <resourceType>map-static</resourceType>
             </xsl:when>
             <xsl:when test="$isInteractive or $isPublishedWithWMCProtocol">
-              <resourceType>map/interactive</resourceType>
+              <resourceType>map-interactive</resourceType>
             </xsl:when>
           </xsl:choose>
         </xsl:when>
+        <xsl:when test="$isDataset">
+          <resourceType>dataset</resourceType>
+        </xsl:when>
+        <xsl:otherwise>
+          <xsl:for-each select="gmd:hierarchyLevel/*/@codeListValue[normalize-space(.) != '']">
+            <resourceType>
+              <xsl:value-of select="."/>
+            </resourceType>
+          </xsl:for-each>
+        </xsl:otherwise>
       </xsl:choose>
 
 
@@ -280,32 +283,34 @@
                           select="string(gmd:date[1]/gco:Date|gmd:date[1]/gco:DateTime)"/>
 
             <xsl:variable name="zuluDateTime" as="xs:string?">
-              <xsl:if test="gn-fn-index:is-isoDate(.)">
+              <xsl:if test="gn-fn-index:is-isoDate($date)">
                 <xsl:value-of select="date-util:convertToISOZuluDateTime(normalize-space($date))"/>
               </xsl:if>
             </xsl:variable>
 
             <xsl:choose>
               <xsl:when test="$zuluDateTime != ''">
+                <!-- Store original date information for the resource, instead of $zuluDateTime,
+                     to avoid timezone shifts when used for facet filters -->
                 <xsl:element name="{$dateType}DateForResource">
-                  <xsl:value-of select="$zuluDateTime"/>
+                  <xsl:value-of select="$date"/>
                 </xsl:element>
                 <xsl:element name="{$dateType}YearForResource">
-                  <xsl:value-of select="substring($zuluDateTime, 0, 5)"/>
+                  <xsl:value-of select="substring($date, 0, 5)"/>
                 </xsl:element>
                 <xsl:element name="{$dateType}MonthForResource">
-                  <xsl:value-of select="substring($zuluDateTime, 0, 8)"/>
+                  <xsl:value-of select="substring($date, 0, 8)"/>
                 </xsl:element>
               </xsl:when>
               <xsl:otherwise>
                 <indexingErrorMsg type="object">
                   {
-                  "string": "indexingErrorMsg-invalidDateFormat",
-                  "type": "warning",
-                  "values": {
-                  "dateType": "<xsl:value-of select="util:escapeForJson($dateType)"/>",
-                  "date": "<xsl:value-of select="util:escapeForJson($date)"/>"
-                  }
+                    "string": "indexingErrorMsg-invalidDateFormat",
+                    "type": "warning",
+                    "values": {
+                      "dateType": "<xsl:value-of select="util:escapeForJson($dateType)"/>",
+                      "date": "<xsl:value-of select="util:escapeForJson($date)"/>"
+                    }
                   }
                 </indexingErrorMsg>
               </xsl:otherwise>
@@ -343,9 +348,9 @@
             </xsl:for-each-group>
           </xsl:if>
 
-          <xsl:for-each select="gmd:identifier/*">
+          <xsl:for-each select="gmd:identifier/*[string(gmd:code/*)]">
             <resourceIdentifier type="object">{
-              "code": "<xsl:value-of select="gn-fn-index:json-escape(gmd:code/(gco:CharacterString|gmx:Anchor))"/>",
+              "code": "<xsl:value-of select="util:escapeForJson(gmd:code/(gco:CharacterString|gmx:Anchor))"/>",
               "codeSpace": "<xsl:value-of select="gmd:codeSpace/(gco:CharacterString|gmx:Anchor)"/>",
               "link": "<xsl:value-of select="gmd:code/gmx:Anchor/@xlink:href"/>"
               }</resourceIdentifier>
@@ -402,7 +407,7 @@
             <xsl:if test="normalize-space(../../gmd:fileDescription) != ''">,
               "nameObject": <xsl:value-of select="gn-fn-index:add-multilingual-field('name', ../../gmd:fileDescription, $allLanguages, true())"/>
             </xsl:if>
-          }</overview>
+            }</overview>
         </xsl:for-each>
 
         <xsl:for-each
@@ -521,7 +526,9 @@
                                           gmd:code/(gco:CharacterString|gmx:Anchor)"/>
 
             <xsl:variable name="thesaurusId"
-                          select="normalize-space($thesaurusRef/text())"/>
+                          select="if ($thesaurusRef != '')
+                                  then normalize-space($thesaurusRef/text())
+                                  else util:getThesaurusIdByTitle($thesaurusTitle)"/>
 
             <xsl:variable name="thesaurusUri"
                           select="$thesaurusRef/@xlink:href"/>
@@ -531,12 +538,19 @@
 
             <xsl:variable name="keywords"
                           select="current-group()/gmd:keyword[*/normalize-space() != '']"/>
+
             <thesaurus>
               <info type="{$thesaurusType}"
                     field="{$thesaurusFieldName}"
                     id="{$thesaurusId}"
                     uri="{$thesaurusUri}"
                     title="{$thesaurusTitle}">
+                <xsl:if test="not(starts-with($thesaurusTitle, 'otherKeywords'))">
+                  <multilingualTitle>
+                    <xsl:copy-of select="gn-fn-index:add-multilingual-field('multilingualTitle',
+                            gmd:thesaurusName/*/gmd:title, $allLanguages, false(), true())"/>
+                  </multilingualTitle>
+                </xsl:if>
               </info>
               <keywords>
                 <xsl:for-each select="$keywords">
@@ -566,12 +580,12 @@
                       <errors>
                         <indexingErrorMsg type="object">
                           {
-                          "string": "indexingErrorMsg-keywordNotFoundInThesaurus",
-                          "type": "warning",
-                          "values": {
-                          "keyword": "<xsl:value-of select="util:escapeForJson((*/text())[1])"/>",
-                          "thesaurus": "<xsl:value-of select="util:escapeForJson($thesaurusId)"/>"
-                          }
+                            "string": "indexingErrorMsg-keywordNotFoundInThesaurus",
+                            "type": "warning",
+                            "values": {
+                              "keyword": "<xsl:value-of select="util:escapeForJson((*/text())[1])"/>",
+                              "thesaurus": "<xsl:value-of select="util:escapeForJson($thesaurusId)"/>"
+                            }
                           }
                         </indexingErrorMsg>
                       </errors>
@@ -607,6 +621,25 @@
             </thesaurus>
           </xsl:for-each-group>
 
+          <xsl:variable name="geoDescription"
+                        select="//gmd:geographicElement/*/gmd:geographicIdentifier/
+                                  */gmd:code[*/normalize-space(.) != '']
+                                |//gmd:EX_Extent/gmd:description[*/normalize-space(.) != '']"/>
+          <xsl:if test="$geoDescription">
+            <thesaurus>
+              <info type="place"/>
+              <keywords>
+                <xsl:for-each select="$geoDescription">
+                  <keyword>
+                    <values>
+                      <xsl:copy-of select="gn-fn-index:add-multilingual-field('keyword',
+                          ., $allLanguages, false(), true())"/>
+                    </values>
+                  </keyword>
+                </xsl:for-each>
+              </keywords>
+            </thesaurus>
+          </xsl:if>
         </xsl:variable>
 
         <xsl:call-template name="build-all-keyword-fields">
@@ -614,7 +647,7 @@
         </xsl:call-template>
 
 
-        <xsl:for-each select="gmd:topicCategory/gmd:MD_TopicCategoryCode">
+        <xsl:for-each select="gmd:topicCategory/gmd:MD_TopicCategoryCode[string(.)]">
           <xsl:variable name="value" as="node()">
             <xsl:copy>
               <xsl:attribute name="codeListValue" select="."/>
@@ -635,12 +668,30 @@
 
           <xsl:for-each select="gmd:distance/gco:Distance[. != '']">
             <resolutionDistance>
-              <xsl:value-of select="concat(., ' ', @uom)"/>
+              <xsl:value-of select="if (contains(@uom, '#'))
+                                    then concat(., ' ', tokenize(@uom, '#')[2])
+                                    else  concat(., ' ', @uom)"/>
             </resolutionDistance>
           </xsl:for-each>
         </xsl:for-each>
 
 
+        <xsl:for-each select="gmd:resourceMaintenance/*">
+          <maintenance type="object">{
+            "frequency": "<xsl:value-of select="*:maintenanceAndUpdateFrequency/*/@codeListValue"/>"
+            <xsl:for-each select="gmd:dateOfNextUpdate[*/text() != '']">
+              ,"nextUpdateDate": "<xsl:value-of select="*/text()"/>"
+            </xsl:for-each>
+            <xsl:for-each select="gmd:userDefinedMaintenanceFrequency[*/text() != '']">
+              ,"userDefinedFrequency": "<xsl:value-of select="*/text()"/>"
+            </xsl:for-each>
+            <xsl:for-each select="gmd:maintenanceNote[*/text() != '']">
+              ,"noteObject":
+              <xsl:value-of select="gn-fn-index:add-multilingual-field('maintenanceNote', ., $allLanguages, true())"/>
+            </xsl:for-each>
+          }</maintenance>
+        </xsl:for-each>
+
         <xsl:for-each select="gmd:resourceConstraints/*">
           <xsl:variable name="fieldPrefix"
                         select="if (@gco:isoType)
@@ -650,7 +701,6 @@
           <xsl:for-each select="gmd:otherConstraints">
             <xsl:copy-of select="gn-fn-index:add-multilingual-field(concat($fieldPrefix, 'OtherConstraints'), ., $allLanguages)"/>
           </xsl:for-each>
-
           <xsl:for-each select="gmd:useLimitation">
             <xsl:copy-of select="gn-fn-index:add-multilingual-field(concat($fieldPrefix, 'UseLimitation'), ., $allLanguages)"/>
           </xsl:for-each>
@@ -685,7 +735,10 @@
         <xsl:for-each select="*/gmd:EX_Extent">
           <xsl:copy-of select="gn-fn-index:add-multilingual-field('extentDescription', gmd:description, $allLanguages)"/>
 
-          <!-- TODO: index bounding polygon -->
+          <xsl:for-each select=".//gmd:geographicIdentifier">
+            <xsl:copy-of select="gn-fn-index:add-multilingual-field('extentIdentifier', */gmd:code, $allLanguages)"/>
+          </xsl:for-each>
+
           <xsl:variable name="bboxes"
                         select=".//gmd:EX_GeographicBoundingBox[
                                 ./gmd:westBoundLongitude/gco:Decimal castable as xs:decimal and
@@ -732,6 +785,10 @@
                 <xsl:choose>
                   <xsl:when test="$e = $w and $s = $n">
                     <location><xsl:value-of select="concat($s, ',', $w)"/></location>
+                    <geom type="object">
+                      <xsl:text>{"type": "Point", "coordinates": </xsl:text>
+                      <xsl:value-of select="concat('[', $w, ',', $s, ']}')"/>
+                    </geom>
                   </xsl:when>
                   <xsl:when
                     test="($e = $w and $s != $n) or ($e != $w and $s = $n)">
@@ -802,9 +859,9 @@
               <xsl:otherwise>
                 <indexingErrorMsg type="object">
                   {
-                  "string": "indexingErrorMsg-invalidBounds",
-                  "type": "warning",
-                  "values": { }
+                    "string": "indexingErrorMsg-invalidBounds",
+                    "type": "warning",
+                    "values": { }
                   }
                 </indexingErrorMsg>
               </xsl:otherwise>
@@ -815,12 +872,12 @@
                           and $start &gt; $end">
               <indexingErrorMsg type="object">
                 {
-                "string": "indexingErrorMsg-temporalRangeLowerGreaterThanUpper",
-                "type": "warning",
-                "values": {
-                "lowerBound": "<xsl:value-of select="util:escapeForJson($start)"/>",
-                "upperBound": "<xsl:value-of select="util:escapeForJson($end)"/>"
-                }
+                  "string": "indexingErrorMsg-temporalRangeLowerGreaterThanUpper",
+                  "type": "warning",
+                  "values": {
+                    "lowerBound": "<xsl:value-of select="util:escapeForJson($start)"/>",
+                    "upperBound": "<xsl:value-of select="util:escapeForJson($end)"/>"
+                  }
                 }
               </indexingErrorMsg>
             </xsl:if>
@@ -837,19 +894,22 @@
             <xsl:variable name="max"
                           select="gmd:maximumValue/*/text()"/>
 
-            <resourceVerticalRange type="object">{
-              "gte": "<xsl:value-of select="normalize-space($min)"/>"
-              <xsl:if test="$min &lt; $max">
-                ,"lte": "<xsl:value-of select="normalize-space($max)"/>"
-              </xsl:if>
-              }</resourceVerticalRange>
+            <xsl:if test="$min castable as xs:double">
+              <resourceVerticalRange type="object">{
+                "gte": <xsl:value-of select="normalize-space($min)"/>
+                <xsl:if test="$max castable as xs:double
+                              and xs:double($min) &lt; xs:double($max)">
+                  ,"lte": <xsl:value-of select="normalize-space($max)"/>
+                </xsl:if>
+                }</resourceVerticalRange>
+            </xsl:if>
           </xsl:for-each>
         </xsl:for-each>
 
 
 
         <!-- Service information -->
-        <xsl:for-each select="srv:serviceType/gco:LocalName">
+        <xsl:for-each select="srv:serviceType/gco:LocalName[string(text())]">
           <serviceType>
             <xsl:value-of select="text()"/>
           </serviceType>
@@ -891,10 +951,10 @@
           </xsl:if>
 
           <crsDetails type="object">{
-            "code": "<xsl:value-of select="gn-fn-index:json-escape((gmd:code/*/text())[1])"/>",
-            "codeSpace": "<xsl:value-of select="gn-fn-index:json-escape(gmd:codeSpace/*/text())"/>",
-            "name": "<xsl:value-of select="gn-fn-index:json-escape($crsLabel)"/>",
-            "url": "<xsl:value-of select="gn-fn-index:json-escape(gmd:code/*/@xlink:href)"/>"
+            "code": "<xsl:value-of select="util:escapeForJson((gmd:code/*/text())[1])"/>",
+            "codeSpace": "<xsl:value-of select="util:escapeForJson((gmd:codeSpace/*/text())[1])"/>",
+            "name": "<xsl:value-of select="util:escapeForJson($crsLabel)"/>",
+            "url": "<xsl:value-of select="util:escapeForJson(gmd:code/*/@xlink:href)"/>"
             }</crsDetails>
         </xsl:for-each>
       </xsl:for-each>
@@ -904,6 +964,48 @@
       <xsl:variable name="legalTextList"
                     select="if ($isService) then $eu9762009 else $eu10892010"/>
 
+      <xsl:for-each-group select="gmd:dataQualityInfo/*/gmd:report/*/gmd:result"
+                          group-by="*/gmd:specification/gmd:CI_Citation/
+    gmd:title/(gco:CharacterString|gmx:Anchor)">
+        <xsl:variable name="title" select="current-grouping-key()"/>
+        <xsl:variable name="matchingEUText"
+                      select="if ($inspireRegulationLaxCheck)
+                              then daobs:search-in-contains($legalTextList/*, $title)
+                              else daobs:search-in($legalTextList/*, $title)"/>
+
+        <xsl:variable name="pass"
+                      select="*/gmd:pass/gco:Boolean"/>
+
+        <xsl:if test="count($matchingEUText) = 1">
+          <inspireConformResource>
+            <xsl:value-of select="$pass"/>
+          </inspireConformResource>
+        </xsl:if>
+
+        <xsl:if test="string($title)">
+          <specificationConformance type="object">{
+            "title": "<xsl:value-of select="util:escapeForJson($title)" />",
+            <xsl:if test="gn-fn-index:is-isoDate((*/gmd:specification/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:date/gco:Date)[1])">
+              "date": "<xsl:value-of select="(*/gmd:specification/gmd:CI_Citation/gmd:date/gmd:CI_Date/gmd:date/gco:Date)[1]" />",
+            </xsl:if>
+            <xsl:if test="*/gmd:specification/*/gmd:title/*/@xlink:href">
+              "link": "<xsl:value-of select="*/gmd:specification/*/gmd:title/*/@xlink:href"/>",
+            </xsl:if>
+            <xsl:if test="*/gmd:explanation/*/text() != ''">
+              "explanation": "<xsl:value-of select="util:escapeForJson((*/gmd:explanation/*/text())[1])" />",
+            </xsl:if>
+            "pass": "<xsl:value-of select="$pass" />"
+            }
+          </specificationConformance>
+        </xsl:if>
+
+        <xsl:element name="conformTo_{replace(normalize-space($title), '[^a-zA-Z0-9]', '')}">
+          <xsl:value-of select="$pass"/>
+        </xsl:element>
+      </xsl:for-each-group>
+
+
+
       <xsl:for-each select="gmd:contentInfo/*/gmd:featureCatalogueCitation[@uuidref != '']">
         <xsl:variable name="xlink"
                       select="@xlink:href"/>
@@ -917,9 +1019,10 @@
                           then 'remote'
                         else 'catalog'"/>",
           "to": "<xsl:value-of select="@uuidref"/>",
-          "title": "<xsl:value-of select="gn-fn-index:json-escape(@xlink:title)"/>",
+          "title": "<xsl:value-of select="util:escapeForJson(@xlink:title)"/>",
           "url": "<xsl:value-of select="$xlink"/>"
           }</recordLink>
+        <hasfeaturecat><xsl:value-of select="@uuidref"/></hasfeaturecat>
       </xsl:for-each>
 
 
@@ -939,7 +1042,7 @@
                           then 'remote'
                         else 'catalog'"/>",
             "to": "<xsl:value-of select="@uuidref"/>",
-            "title": "<xsl:value-of select="gn-fn-index:json-escape(@xlink:title)"/>",
+            "title": "<xsl:value-of select="util:escapeForJson(@xlink:title)"/>",
             "url": "<xsl:value-of select="$xlink"/>"
             }</recordLink>
         </xsl:for-each>
@@ -948,44 +1051,106 @@
                                 gmd:statement, $allLanguages)"/>
 
 
-        <xsl:for-each select="gmd:report/*[gmd:nameOfMeasure/gco:CharacterString != '']">
+        <xsl:for-each select="gmd:report/*[gmd:nameOfMeasure/gco:CharacterString != ''
+                                          or gmd:measureDescription/gco:CharacterString != '']/gmd:result/gmd:DQ_QuantitativeResult">
           <xsl:variable name="name"
-                        select="(gmd:nameOfMeasure/gco:CharacterString)[1]"/>
+                        select="(../../gmd:nameOfMeasure/gco:CharacterString)[1]"/>
           <xsl:variable name="value"
-                        select="(gmd:result/gmd:DQ_QuantitativeResult/gmd:value)[1]"/>
+                        select="(gmd:value)[1]"/>
           <xsl:variable name="unit"
-                        select="(gmd:result/gmd:DQ_QuantitativeResult/gmd:valueUnit//gml:identifier)[1]"/>
+                        select="(gmd:valueUnit//(gml:identifier|gml320:identifier))[1]"/>
           <xsl:variable name="description"
-                        select="(gmd:measureDescription/gco:CharacterString)[1]"/>
+                        select="(../../gmd:measureDescription/gco:CharacterString)[1]"/>
+          <xsl:variable name="measureDate"
+                        select="(../../gmd:dateTime/gco:DateTime)[1]"/>
           <measure type="object">{
-            "name": "<xsl:value-of select="gn-fn-index:json-escape($name)"/>",
+            "name": "<xsl:value-of select="util:escapeForJson($name)"/>",
             <xsl:if test="$description != ''">
-              "description": "<xsl:value-of select="gn-fn-index:json-escape($description)"/>",
+              "description": "<xsl:value-of select="util:escapeForJson($description)"/>",
+            </xsl:if>
+            <xsl:if test="$measureDate != ''">
+              "date": "<xsl:value-of select="util:escapeForJson($measureDate)"/>",
             </xsl:if>
             <!-- First value only. -->
-            "value": "<xsl:value-of select="gn-fn-index:json-escape($value/gco:Record[1])"/>",
+            "value": "<xsl:value-of select="util:escapeForJson($value/gco:Record[1])"/>",
             <xsl:if test="$unit != ''">
-              "unit": "<xsl:value-of select="gn-fn-index:json-escape($unit)"/>",
+              "unit": "<xsl:value-of select="util:escapeForJson($unit)"/>",
             </xsl:if>
             "type": "<xsl:value-of select="local-name(.)"/>"
             }
           </measure>
 
-          <xsl:for-each select="gmd:result/gmd:DQ_QuantitativeResult/gmd:value/gco:Record[. != '']">
+          <xsl:for-each select="gmd:value/gco:Record[. != '']">
             <xsl:element name="measure_{gn-fn-index:build-field-name($name)}">
               <xsl:value-of select="."/>
             </xsl:element>
           </xsl:for-each>
         </xsl:for-each>
+
+        <xsl:variable name="processSteps"
+                      select="gmd:lineage/*/gmd:processStep/*[gmd:description/gco:CharacterString != '']"/>
+        <xsl:for-each select="$processSteps">
+          <processSteps type="object">{
+            "descriptionObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
+                                'description', gmd:description, $allLanguages, true())"/>
+            <xsl:if test="normalize-space(gmd:dateTime) != ''">
+              ,"date": "<xsl:value-of select="gmd:dateTime/gco:*/text()"/>"
+            </xsl:if>
+            <xsl:if test="normalize-space(gmd:source) != ''">
+              ,"source": [
+              <xsl:for-each select="gmd:source/*[gmd:description/gco:CharacterString != '']">
+                {
+                  "descriptionObject": <xsl:value-of
+                                          select="gn-fn-index:add-multilingual-field(
+                                            'description', gmd:description, $allLanguages, true())"/>
+                }
+                <xsl:if test="position() != last()">,</xsl:if>
+              </xsl:for-each>
+              ]
+            </xsl:if>
+
+            <xsl:variable name="processors"
+                          select="gmd:processor/*[gmd:organisationName/gco:CharacterString != '']"/>
+
+            <xsl:if test="count($processors) > 0">
+              ,"processor": [
+              <xsl:for-each select="$processors">
+                {
+                  "organisationObject": <xsl:value-of
+                                          select="gn-fn-index:add-multilingual-field(
+                                            'description', gmd:organisationName, $allLanguages, true())"/>
+                  <xsl:if test="gmd:individualName/gco:CharacterString/text()">
+                    ,"individual":"<xsl:value-of select="util:escapeForJson(gmd:individualName/gco:CharacterString/text())"/>"
+                  </xsl:if>
+                }
+                <xsl:if test="position() != last()">,</xsl:if>
+              </xsl:for-each>
+              ]
+            </xsl:if>
+            }</processSteps>
+        </xsl:for-each>
+
+        <xsl:for-each-group select="gmd:lineage/*/gmd:processStep/*/gmd:processor[
+                                    */gmd:organisationName/gco:CharacterString != '']"
+                            group-by="*/gmd:organisationName/gco:CharacterString">
+          <xsl:apply-templates mode="index-contact"
+                               select=".">
+            <xsl:with-param name="fieldSuffix" select="'ForProcessing'"/>
+            <xsl:with-param name="languages" select="$allLanguages"/>
+          </xsl:apply-templates>
+        </xsl:for-each-group>
+
       </xsl:for-each>
 
+      <xsl:variable name="atomProtocol" select="util:getSettingValue('system/inspire/atomProtocol')" />
 
       <xsl:for-each select="gmd:distributionInfo/*">
         <xsl:for-each
-          select="gmd:distributionFormat/*/gmd:name/gco:CharacterString[. != '']">
+          select="gmd:distributionFormat/*/gmd:name/*[. != '']">
           <xsl:copy-of select="gn-fn-index:add-field('format', .)"/>
         </xsl:for-each>
 
+
         <!-- Indexing distributor contact -->
         <xsl:for-each select="gmd:distributor/*[gmd:distributorContact]">
           <xsl:apply-templates mode="index-contact"
@@ -1042,28 +1207,40 @@
           <linkUrl>
             <xsl:value-of select="gmd:linkage/gmd:URL"/>
           </linkUrl>
-          <linkProtocol>
-            <xsl:value-of select="$protocol"/>
-          </linkProtocol>
+          <xsl:if test="normalize-space($protocol) != ''">
+            <linkProtocol>
+              <xsl:value-of select="$protocol"/>
+            </linkProtocol>
+          </xsl:if>
           <xsl:element name="linkUrlProtocol{replace($protocol[1], '[^a-zA-Z0-9]', '')}">
             <xsl:value-of select="gmd:linkage/gmd:URL"/>
           </xsl:element>
+          <xsl:if test="$protocol = $atomProtocol">
+            <atomfeed><xsl:value-of select="gmd:linkage/gmd:URL"/></atomfeed>
+          </xsl:if>
           <link type="object">{
-            "protocol":"<xsl:value-of select="gn-fn-index:json-escape((gmd:protocol/*/text())[1])"/>",
+            "hash": "<xsl:value-of select="digestUtils:md5Hex(normalize-space(.))"/>",
+            "idx": <xsl:value-of select="position()"/>,
+            "protocol":"<xsl:value-of select="util:escapeForJson((gmd:protocol/*/text())[1])"/>",
+            "mimeType":"<xsl:value-of select="if (*/gmx:MimeFileType)
+                                              then util:escapeForJson(*/gmx:MimeFileType/@type)
+                                              else if (starts-with(gmd:protocol/gco:CharacterString, 'WWW:DOWNLOAD:'))
+                                              then util:escapeForJson(replace(gmd:protocol/gco:CharacterString, 'WWW:DOWNLOAD:', ''))
+                                              else ''"/>",
             "urlObject":{<xsl:value-of select="$urlObject"/>},
             <xsl:if test="normalize-space(gmd:name) != ''">
-            "nameObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
+              "nameObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
                                 'name', gmd:name, $allLanguages, true())"/>,
             </xsl:if>
             <xsl:if test="normalize-space(gmd:description) != ''">
-            "descriptionObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
+              "descriptionObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
                                 'description', gmd:description, $allLanguages, true())"/>,
             </xsl:if>
             <xsl:if test="../@gco:nilReason">
               "nilReason": "<xsl:value-of select="../@gco:nilReason"/>",
             </xsl:if>
             "function":"<xsl:value-of select="gmd:function/gmd:CI_OnLineFunctionCode/@codeListValue"/>",
-            "applicationProfile":"<xsl:value-of select="gn-fn-index:json-escape(gmd:applicationProfile/gco:CharacterString/text())"/>",
+            "applicationProfile":"<xsl:value-of select="util:escapeForJson(gmd:applicationProfile/(gco:CharacterString|gmx:Anchor)/text())"/>",
             "group": <xsl:value-of select="$transferGroup"/>
             }
             <!--Link object in Angular used to be
@@ -1077,6 +1254,16 @@
             //     applicationProfile: linkInfos[6]-->
           </link>
 
+          <xsl:if test="$operatesOnSetByProtocol and normalize-space($protocol) != ''">
+            <xsl:if test="daobs:contains($protocol, 'wms')">
+              <recordOperatedByType>view</recordOperatedByType>
+            </xsl:if>
+            <xsl:if test="daobs:contains($protocol, 'wfs') or
+                          daobs:contains($protocol, 'wcs') or
+                          daobs:contains($protocol, 'download')">
+              <recordOperatedByType>download</recordOperatedByType>
+            </xsl:if>
+          </xsl:if>
         </xsl:for-each>
       </xsl:for-each>
 
@@ -1093,7 +1280,7 @@
             <xsl:copy-of select="gn-fn-index:build-record-link(., @xlink:href, @xlink:title, 'parent')"/>
             <!--
             TODOES - Need more work with routing -->
-<!--            <recordJoin type="object">{"name": "children", "parent": "<xsl:value-of select="gn-fn-index:json-escape(.)"/>"}</recordLink>-->
+            <!--            <recordJoin type="object">{"name": "children", "parent": "<xsl:value-of select="util:escapeForJson(.)"/>"}</recordLink>-->
           </xsl:for-each>
         </xsl:when>
         <xsl:otherwise>
@@ -1114,6 +1301,13 @@
             <parentUuid><xsl:value-of select="$code"/></parentUuid>
             <xsl:copy-of select="gn-fn-index:build-record-link($code, $xlink, gmd:aggregateDataSetIdentifier/*/gmd:code/*/@xlink:title, 'parent')"/>
           </xsl:if>
+          <xsl:if test="$associationType = $childrenAssociatedResourceType">
+            <childUuid><xsl:value-of select="$code"/></childUuid>
+            <xsl:copy-of select="gn-fn-index:build-record-link(
+                                $code, $xlink,
+                                gmd:aggregateDataSetIdentifier/*/gmd:code/*/@xlink:title,
+                                 'children')"/>
+          </xsl:if>
 
           <xsl:variable name="initiativeType"
                         select="gmd:initiativeType/*/@codeListValue"/>
@@ -1125,6 +1319,7 @@
           </xsl:variable>
           <xsl:copy-of select="gn-fn-index:build-record-link($code, $xlink, gmd:aggregateDataSetIdentifier/*/gmd:code/*/@xlink:title, 'siblings', $properties)"/>
           <agg_associated><xsl:value-of select="$code"/></agg_associated>
+          <xsl:element name="{concat('agg_associated_', $associationType)}"><xsl:value-of select="$code"/></xsl:element>
         </xsl:if>
       </xsl:for-each>
 
@@ -1194,50 +1389,40 @@
     <xsl:variable name="address" select="string-join(*[1]/gmd:contactInfo/*/gmd:address/*/(
                                         gmd:deliveryPoint|gmd:postalCode|gmd:city|
                                         gmd:administrativeArea|gmd:country)/gco:CharacterString/text(), ', ')"/>
+
     <xsl:variable name="roleField"
                   select="concat(replace($role, '[^a-zA-Z0-9-]', ''),
                                  'Org', $fieldSuffix)"/>
     <xsl:variable name="orgField"
                   select="concat('Org', $fieldSuffix)"/>
+
+
     <xsl:if test="normalize-space($organisationName) != ''">
-        <xsl:copy-of select="gn-fn-index:add-multilingual-field($orgField, *[1]/gmd:organisationName[1], $languages)"/>,
-        <xsl:copy-of select="gn-fn-index:add-multilingual-field($roleField, *[1]/gmd:organisationName[1], $languages)"/>,
+      <xsl:copy-of select="gn-fn-index:add-multilingual-field(
+                            $orgField, $organisationName, $languages)"/>
+      <xsl:copy-of select="gn-fn-index:add-multilingual-field(
+                            $roleField, $organisationName, $languages)"/>
     </xsl:if>
 
-    <xsl:variable name="orgObject" select="gn-fn-index:add-multilingual-field('organisation', *[1]/gmd:organisationName[1], $languages)"/>
-
-   <xsl:choose>
-    <xsl:when test="string-length($orgObject) > 0">
-      <xsl:element name="contact{$fieldSuffix}">
-        <xsl:attribute name="type" select="'object'"/>{
-          "organisationObject": <xsl:value-of select="$orgObject"/>,
-          "role":"<xsl:value-of select="$role"/>",
-          "email":"<xsl:value-of select="gn-fn-index:json-escape($email[1])"/>",
-          "websiteObject":{<xsl:value-of select="$websiteObject"/>},
-          "logo":"<xsl:value-of select="$logo"/>",
-          "individual":"<xsl:value-of select="gn-fn-index:json-escape($individualName)"/>",
-          "position":"<xsl:value-of select="gn-fn-index:json-escape($positionName)"/>",
-          "phone":"<xsl:value-of select="gn-fn-index:json-escape($phone[1])"/>",
-          "address":"<xsl:value-of select="gn-fn-index:json-escape($address)"/>"
-        }
-      </xsl:element>
-    </xsl:when>
-    <xsl:otherwise>
-      <xsl:element name="contact{$fieldSuffix}">
-        <xsl:attribute name="type" select="'object'"/>{
-          "role":"<xsl:value-of select="$role"/>",
-          "email":"<xsl:value-of select="gn-fn-index:json-escape($email[1])"/>",
-          "websiteObject":{<xsl:value-of select="$websiteObject"/>},
-          "logo":"<xsl:value-of select="$logo"/>",
-          "individual":"<xsl:value-of select="gn-fn-index:json-escape($individualName)"/>",
-          "position":"<xsl:value-of select="gn-fn-index:json-escape($positionName)"/>",
-          "phone":"<xsl:value-of select="gn-fn-index:json-escape($phone[1])"/>",
-          "address":"<xsl:value-of select="gn-fn-index:json-escape($address)"/>"
-        }
-      </xsl:element>
-    </xsl:otherwise>
-  </xsl:choose>
-
+    <xsl:element name="contact{$fieldSuffix}">
+      <xsl:attribute name="type" select="'object'"/>{
+      <xsl:if test="$organisationName">
+        "organisationObject": <xsl:value-of select="gn-fn-index:add-multilingual-field(
+                              'organisation', $organisationName, $languages, true())"/>,
+      </xsl:if>
+      "role":"<xsl:value-of select="$role"/>",
+      "email":"<xsl:value-of select="util:escapeForJson($email[1])"/>",
+      "websiteObject":{<xsl:value-of select="$websiteObject"/>},
+      "logo":"<xsl:value-of select="util:escapeForJson($logo)"/>",
+      "individual":"<xsl:value-of select="util:escapeForJson($individualName)"/>",
+      "position":"<xsl:value-of select="util:escapeForJson($positionName)"/>",
+      "phone":"<xsl:value-of select="util:escapeForJson($phone[1])"/>",
+      "address":"<xsl:value-of select="util:escapeForJson($address)"/>"
+      <xsl:if test="@gco:nilReason">
+        ,"nilReason": "<xsl:value-of select="@gco:nilReason"/>"
+      </xsl:if>
+      }
+    </xsl:element>
   </xsl:template>
 
 
@@ -1254,7 +1439,7 @@
         <xsl:variable name="getRecordByIdId">
           <xsl:if test="@xlink:href != ''">
             <xsl:analyze-string select="@xlink:href"
-                                regex=".*[i|I][d|D]=([\w\-\.\{{\}}]*).*">
+                                regex=".*[i|I][d|D]=([a-zA-Z0-9\-\.\{{\}}]*).*">
               <xsl:matching-substring>
                 <xsl:value-of select="regex-group(1)"/>
               </xsl:matching-substring>
@@ -1280,6 +1465,7 @@
 
           <xsl:variable name="resolvedDoc">
             <xsl:if test="$processRemoteDocs
+                          and not($fastIndexMode)
                           and $xlink != ''
                           and not(@xlink:title)
                           and not(starts-with($xlink, $siteUrl))">
@@ -1303,7 +1489,7 @@
                Remote is supposed to be ISO19139. -->
               <xsl:variable name="datasetUuid"
                             select="$remoteDoc//(*[local-name(.) = 'fileIdentifier']/*/text()|
-                                                *[local-name(.) = 'metadataIdentifier']/*/*[local-name(.) = 'code']/*/text())" />
+                                                 *[local-name(.) = 'metadataIdentifier']/*/*[local-name(.) = 'code']/*/text())" />
 
               <xsl:if test="count($datasetUuid) = 1
                             and string($datasetUuid)">
@@ -1331,7 +1517,7 @@
 
           <!--
             TODOES - Need more work with routing -->
-          <!--          <recordLink type="object">{"name": "dataset", "parent": "<xsl:value-of select="gn-fn-index:json-escape(.)"/>"}</recordLink>-->
+          <!--          <recordLink type="object">{"name": "dataset", "parent": "<xsl:value-of select="util:escapeForJson(.)"/>"}</recordLink>-->
         </xsl:if>
       </xsl:for-each>
     </xsl:for-each>
diff --git a/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-add.xsl b/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-add.xsl
index 5dac142199..56b5d7b30a 100644
--- a/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-add.xsl
+++ b/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-add.xsl
@@ -25,13 +25,23 @@
 <!--
 Processing to insert or update an online resource element.
 Insert is made in first transferOptions found.
+
+Note: It assumes that it will be adding new items in
+      the first /gmd:distributionInfo
+      and first /gmd:MD_Distribution
+      and first /gmd:transferOptions
 -->
 <xsl:stylesheet xmlns:gmd="http://www.isotc211.org/2005/gmd"
                 xmlns:gco="http://www.isotc211.org/2005/gco"
+                xmlns:gmx="http://www.isotc211.org/2005/gmx"
                 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
-                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:digestUtils="java:org.apache.commons.codec.digest.DigestUtils"
+                xmlns:exslt="http://exslt.org/common"
                 xmlns:java="java:org.fao.geonet.util.XslUtil"
-                version="2.0" xmlns:che="http://www.geocat.ch/2008/che">
+                xmlns:che="http://www.geocat.ch/2008/che"
+                exclude-result-prefixes="#all"
+                version="2.0">
 
   <!-- Main properties for the link.
   Name and description may be multilingual eg. ENG#English name|FRE#Le français
@@ -42,6 +52,8 @@ Insert is made in first transferOptions found.
   <xsl:param name="desc"/>
   <xsl:param name="function"/>
   <xsl:param name="applicationProfile"/>
+  <xsl:param name="mimeType"/>
+  <xsl:param name="mimeTypeStrategy" select="'protocol'"/>
 
   <!-- Add an optional uuidref attribute to the onLine element created. -->
   <xsl:param name="uuidref"/>
@@ -51,32 +63,39 @@ Insert is made in first transferOptions found.
   in this one. -->
   <xsl:param name="extra_metadata_uuid"/>
 
-  <!-- Target element to update. The key is based on the concatenation
-  of URL+Protocol+Name -->
-  <xsl:param name="updateKey"/>
-
-  <!-- The default language is also added as gmd:locale
-  for multilingual metadata records. -->
-  <xsl:variable name="mainLanguage"
-                select="/*/gmd:language/gco:CharacterString/text()|
-                        /*/gmd:language/gmd:LanguageCode/@codeListValue"/>
+  <!-- Target element to update.
+      updateKey is used to identify the resource name to be updated - it is for backwards compatibility.  Will not be used if resourceHash is set.
+                The key is based on the concatenation of URL+Protocol+Name
+      resourceHash is hash value of the object to be removed which will ensure the correct value is removed. It will override the usage of updateKey
+      resourceIdx is the index location of the object to be removed - can be used when duplicate entries exists to ensure the correct one is removed.
+  -->
 
-  <xsl:variable name="mainLanguageId"
-                select="concat('#', upper-case(java:twoCharLangCode($mainLanguage)))"/>
+  <xsl:param name="updateKey" select="''"/>
+  <xsl:param name="resourceHash" select="''"/>
+  <xsl:param name="resourceIdx" select="''"/>
 
-  <xsl:variable name="isMultilingual"
-                select="count(/*/gmd:locale[*/gmd:languageCode/*/@codeListValue != $mainLanguage]) > 0"/>
+  <xsl:variable name="update_flag">
+    <xsl:value-of select="boolean($updateKey != '' or $resourceHash != '' or $resourceIdx != '')"/>
+  </xsl:variable>
 
   <xsl:variable name="mainLang">
     <xsl:value-of
-            select="(gmd:MD_Metadata|*[@gco:isoType='gmd:MD_Metadata'])/gmd:language/gmd:LanguageCode/@codeListValue"/>
+      select="(gmd:MD_Metadata|*[@gco:isoType='gmd:MD_Metadata'])/gmd:language/gmd:LanguageCode/@codeListValue"/>
   </xsl:variable>
 
-  <xsl:template match="gmd:MD_Metadata|*[@gco:isoType='gmd:MD_Metadata']">
+  <xsl:variable name="isMultilingual"
+                select="count(/*/gmd:locale[*/gmd:languageCode/*/@codeListValue != $mainLang]) > 0"/>
+
+  <xsl:variable name="mainLanguageId"
+                select="concat('#', upper-case(java:twoCharLangCode($mainLang)))"/>
+
+  <!-- Add new gmd:onLine and consider cases where parent elements don't exist -->
+  <!--  <gmd:distributionInfo> does not exist-->
+  <xsl:template match="gmd:MD_Metadata[not(gmd:distributionInfo) and $update_flag = false()]|*[@gco:isoType='gmd:MD_Metadata' and not(gmd:distributionInfo) and $update_flag = false()]">
     <xsl:copy>
       <xsl:apply-templates select="@*"/>
       <xsl:apply-templates
-              select="gmd:fileIdentifier|
+        select="gmd:fileIdentifier|
                 gmd:language|
                 gmd:characterSet|
                 gmd:parentIdentifier|
@@ -96,43 +115,17 @@ Insert is made in first transferOptions found.
 
       <gmd:distributionInfo>
         <gmd:MD_Distribution>
-          <xsl:apply-templates
-                  select="gmd:distributionInfo/gmd:MD_Distribution/gmd:distributionFormat"/>
-          <xsl:apply-templates
-                  select="gmd:distributionInfo/gmd:MD_Distribution/gmd:distributor"/>
           <gmd:transferOptions>
             <gmd:MD_DigitalTransferOptions>
-              <xsl:apply-templates
-                      select="gmd:distributionInfo/gmd:MD_Distribution/
-                          gmd:transferOptions[1]/gmd:MD_DigitalTransferOptions/gmd:unitsOfDistribution"/>
-              <xsl:apply-templates
-                      select="gmd:distributionInfo/gmd:MD_Distribution/
-                          gmd:transferOptions[1]/gmd:MD_DigitalTransferOptions/gmd:transferSize"/>
-              <xsl:apply-templates
-                      select="gmd:distributionInfo/gmd:MD_Distribution/
-                          gmd:transferOptions[1]/gmd:MD_DigitalTransferOptions/gmd:onLine"/>
-
-
-              <xsl:if test="$updateKey = ''">
-                <xsl:call-template name="createOnlineSrc"/>
-              </xsl:if>
-
-              <xsl:apply-templates
-                      select="gmd:distributionInfo/gmd:MD_Distribution/
-                          gmd:transferOptions[1]/gmd:MD_DigitalTransferOptions/gmd:offLine"/>
+              <xsl:call-template name="createOnlineSrc"/>
             </gmd:MD_DigitalTransferOptions>
           </gmd:transferOptions>
-
-
-          <xsl:apply-templates
-                  select="gmd:distributionInfo/gmd:MD_Distribution/
-                      gmd:transferOptions[position() > 1]"/>
-
         </gmd:MD_Distribution>
       </gmd:distributionInfo>
 
+
       <xsl:apply-templates
-              select="gmd:dataQualityInfo|
+        select="gmd:dataQualityInfo|
                 gmd:portrayalCatalogueInfo|
                 gmd:metadataConstraints|
                 gmd:applicationSchemaInfo|
@@ -145,29 +138,87 @@ Insert is made in first transferOptions found.
     </xsl:copy>
   </xsl:template>
 
+  <!--  <gmd:MD_Distribution> does not exist-->
+  <xsl:template match="*/gmd:distributionInfo[not(gmd:MD_Distribution) and $update_flag = false() and position() = 1]">
+    <xsl:copy>
+      <xsl:apply-templates select="node()|@*"/>
+      <gmd:MD_Distribution>
+        <gmd:transferOptions>
+          <gmd:MD_DigitalTransferOptions>
+            <xsl:call-template name="createOnlineSrc"/>
+          </gmd:MD_DigitalTransferOptions>
+        </gmd:transferOptions>
+      </gmd:MD_Distribution>
+    </xsl:copy>
+  </xsl:template>
 
-  <!-- Updating the link matching the update key. -->
-  <xsl:template match="gmd:onLine[
-                        normalize-space($updateKey) = concat(
-                        (if ($isMultilingual)
-                        then gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup/che:LocalisedURL[@locale = $mainLanguageId]
-                        else gmd:CI_OnlineResource/gmd:linkage/gmd:URL),
-                        gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString,
-                        gmd:CI_OnlineResource/gmd:name/gco:CharacterString)
-                       ]">
-    <xsl:call-template name="createOnlineSrc"/>
+  <!--  <gmd:transferOptions> does not exist-->
+  <xsl:template match="*/gmd:distributionInfo[1]/gmd:MD_Distribution[not(gmd:transferOptions) and $update_flag = false() and position() = 1]">
+    <xsl:copy>
+      <xsl:apply-templates select="node()|@*"/>
+      <gmd:transferOptions>
+        <gmd:MD_DigitalTransferOptions>
+          <xsl:call-template name="createOnlineSrc"/>
+        </gmd:MD_DigitalTransferOptions>
+      </gmd:transferOptions>
+    </xsl:copy>
   </xsl:template>
-<!-- TMP TO REMOVE when gco:characterString is added in multilingual elements
-  <xsl:template match="gmd:onLine[
-                        normalize-space($updateKey) = concat(
-                        gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup/che:LocalisedURL[@locale = '#DE'],
-                        gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString,
-                        gmd:CI_OnlineResource/gmd:name/gmd:PT_FreeText/gmd:textGroup/gmd:LocalisedCharacterString[@locale = '#DE')
-                        ]">
-    <xsl:call-template name="createOnlineSrc"/>
+
+  <!--  <gmd:MD_DigitalTransferOptions> does not exist-->
+  <xsl:template match="*/gmd:distributionInfo[1]/gmd:MD_Distribution[1]/gmd:transferOptions[not(gmd:MD_DigitalTransferOptions) and $update_flag = false() and position() = 1]">
+    <xsl:copy>
+      <xsl:apply-templates select="node()|@*"/>
+      <gmd:MD_DigitalTransferOptions>
+        <xsl:call-template name="createOnlineSrc"/>
+      </gmd:MD_DigitalTransferOptions>
+    </xsl:copy>
   </xsl:template>
--->
 
+  <!--  Add new gmd:gmd:onLine-->
+  <xsl:template match="*/gmd:distributionInfo[1]/gmd:MD_Distribution[1]/gmd:transferOptions[1]/gmd:MD_DigitalTransferOptions[$update_flag = false() and position() = 1]">
+    <xsl:copy>
+      <xsl:apply-templates select="@*"/>
+      <xsl:apply-templates
+        select="gmd:unitsOfDistribution|
+                gmd:transferSize|
+                gmd:onLine"/>
+
+      <xsl:call-template name="createOnlineSrc"/>
+
+      <xsl:apply-templates select="gmd:offLine"/>
+    </xsl:copy>
+  </xsl:template>
+
+  <!-- End of inserting gmd:onLine -->
+
+
+  <!-- Updating the gmd:onLine based on update parameters -->
+  <!-- Note: first part of the match needs to match the xsl:for-each select from extract-relations.xsl in order to get the position() to match -->
+  <!-- The unique identifier is marked with resourceIdx which is the position index and resourceHash which is hash code of the current node (combination of url, resource name, and description) -->
+  <!-- Template to match all gmd:onLine elements -->
+  <xsl:template match="//gmd:MD_DigitalTransferOptions/gmd:onLine" priority="2">
+    <!-- Calculate the global position of the current gmd:onLine element -->
+    <xsl:variable name="position" select="count(//gmd:MD_DigitalTransferOptions/gmd:onLine[current() >> .]) + 1" />
+
+    <xsl:choose>
+      <xsl:when test="gmd:CI_OnlineResource[gmd:linkage/gmd:URL != ''] and
+                      ($resourceIdx = '' or $position = xs:integer($resourceIdx)) and
+                      ($resourceHash != '' or ($updateKey != '' and normalize-space($updateKey) = concat(
+                          (if ($isMultilingual)
+                          then gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup/che:LocalisedURL[@locale = $mainLanguageId]
+                          else gmd:CI_OnlineResource/gmd:linkage/gmd:URL),
+                          gmd:CI_OnlineResource/gmd:protocol/*,
+                          gmd:CI_OnlineResource/gmd:name/gco:CharacterString)))
+                     and ($resourceHash = '' or digestUtils:md5Hex(normalize-space(.)) = $resourceHash)">
+        <xsl:call-template name="createOnlineSrc"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:copy>
+          <xsl:apply-templates select="@*|node()"/>
+        </xsl:copy>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
 
   <xsl:template name="createOnlineSrc">
     <!-- Add all online source from the target metadata to the
@@ -186,18 +237,17 @@ Insert is made in first transferOptions found.
     <xsl:variable name="separator" select="'\|'"/>
     <xsl:variable name="useOnlyPTFreeText">
       <xsl:value-of
-              select="count(//*[gmd:PT_FreeText and not(gco:CharacterString)]) > 0"/>
+        select="count(//*[gmd:PT_FreeText and not(gco:CharacterString)]) > 0"/>
     </xsl:variable>
 
 
     <xsl:if test="$url">
       <!-- In case the protocol is an OGC protocol
-      the name parameter may contains a list of layers
+      the name parameter may contain a list of layers
       separated by comma.
       In that case on one online element is added per
       layer/featureType.
       -->
-
       <xsl:choose>
         <xsl:when test="starts-with($protocol, 'OGC:') and $name != ''">
           <xsl:for-each select="tokenize($name, ',')">
@@ -211,7 +261,7 @@ Insert is made in first transferOptions found.
                   <xsl:choose>
                     <!--Multilingual-->
                     <xsl:when test="contains($url, '#')">
-                      <xsl:attribute name="xsi:type">che:PT_FreeURL_PropertyType</xsl:attribute>
+                      <xsl:attribute name="xs:type">che:PT_FreeURL_PropertyType</xsl:attribute>
 
                       <xsl:for-each select="tokenize($url, $separator)">
                         <xsl:variable name="urlLang"
@@ -254,9 +304,7 @@ Insert is made in first transferOptions found.
                   </xsl:choose>
                 </gmd:linkage>
                 <gmd:protocol>
-                  <gco:CharacterString>
-                    <xsl:value-of select="$protocol"/>
-                  </gco:CharacterString>
+                  <xsl:call-template name="setProtocol"/>
                 </gmd:protocol>
 
                 <xsl:if test="$applicationProfile != ''">
@@ -269,7 +317,7 @@ Insert is made in first transferOptions found.
                           <xsl:variable name="nameValue"
                                         select="substring-after(., '#')"></xsl:variable>
                           <xsl:if
-                                  test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
+                            test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
                             <gco:CharacterString>
                               <xsl:value-of select="$nameValue"/>
                             </gco:CharacterString>
@@ -284,10 +332,10 @@ Insert is made in first transferOptions found.
                                           select="substring-after(., '#')"></xsl:variable>
 
                             <xsl:if
-                                    test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
+                              test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
                               <gmd:textGroup>
                                 <gmd:LocalisedCharacterString
-                                        locale="{concat('#', $nameLang)}">
+                                  locale="{concat('#', $nameLang)}">
                                   <xsl:value-of select="$nameValue"/>
                                 </gmd:LocalisedCharacterString>
                               </gmd:textGroup>
@@ -305,28 +353,105 @@ Insert is made in first transferOptions found.
                   </gmd:applicationProfile>
                 </xsl:if>
 
-                <xsl:if test=". != ''">
+                <xsl:variable name="curName" select="."></xsl:variable>
+                <xsl:if test="$curName != ''">
                   <gmd:name>
-                    <gco:CharacterString>
-                      <xsl:value-of select="."/>
-                    </gco:CharacterString>
+                    <xsl:choose>
+
+                      <!--Multilingual-->
+                      <xsl:when test="contains($curName, '#')">
+                        <xsl:for-each select="tokenize($curName, $separator)">
+                          <xsl:variable name="nameLang"
+                                        select="substring-before(., '#')"></xsl:variable>
+                          <xsl:variable name="nameValue"
+                                        select="substring-after(., '#')"></xsl:variable>
+                          <xsl:if
+                            test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
+                            <gco:CharacterString>
+                              <xsl:value-of select="$nameValue"/>
+                            </gco:CharacterString>
+                          </xsl:if>
+                        </xsl:for-each>
+
+                        <gmd:PT_FreeText>
+                          <xsl:for-each select="tokenize($curName, $separator)">
+                            <xsl:variable name="nameLang"
+                                          select="substring-before(., '#')"></xsl:variable>
+                            <xsl:variable name="nameValue"
+                                          select="substring-after(., '#')"></xsl:variable>
+
+                            <xsl:if
+                              test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
+                              <gmd:textGroup>
+                                <gmd:LocalisedCharacterString
+                                  locale="{concat('#', $nameLang)}">
+                                  <xsl:value-of select="$nameValue"/>
+                                </gmd:LocalisedCharacterString>
+                              </gmd:textGroup>
+                            </xsl:if>
+
+                          </xsl:for-each>
+                        </gmd:PT_FreeText>
+                      </xsl:when>
+                      <xsl:otherwise>
+                        <gco:CharacterString>
+                          <xsl:value-of select="$curName"/>
+                        </gco:CharacterString>
+                      </xsl:otherwise>
+                    </xsl:choose>
                   </gmd:name>
                 </xsl:if>
 
-                <xsl:if test="tokenize($desc, ',')[position() = $pos] != ''">
+                <xsl:variable name="curDesc" select="tokenize($desc, ',')[position() = $pos]"></xsl:variable>
+                <xsl:if test="$curDesc != ''">
                   <gmd:description>
-                    <gco:CharacterString>
-                      <xsl:value-of select="tokenize($desc, ',')[position() = $pos]"/>
-                    </gco:CharacterString>
+                    <xsl:choose>
+                      <xsl:when test="contains($curDesc, '#')">
+                        <xsl:for-each select="tokenize($curDesc, $separator)">
+                          <xsl:variable name="descLang"
+                                        select="substring-before(., '#')"></xsl:variable>
+                          <xsl:variable name="descValue"
+                                        select="substring-after(., '#')"></xsl:variable>
+                          <xsl:if
+                            test="$useOnlyPTFreeText = 'false' and $descLang = $mainLang">
+                            <gco:CharacterString>
+                              <xsl:value-of select="$descValue"/>
+                            </gco:CharacterString>
+                          </xsl:if>
+                        </xsl:for-each>
+
+                        <gmd:PT_FreeText>
+                          <xsl:for-each select="tokenize($desc, $separator)">
+                            <xsl:variable name="descLang"
+                                          select="substring-before(., '#')"></xsl:variable>
+                            <xsl:variable name="descValue"
+                                          select="substring-after(., '#')"></xsl:variable>
+                            <xsl:if
+                              test="$useOnlyPTFreeText = 'true' or $descLang != $mainLang">
+                              <gmd:textGroup>
+                                <gmd:LocalisedCharacterString
+                                  locale="{concat('#', $descLang)}">
+                                  <xsl:value-of select="$descValue"/>
+                                </gmd:LocalisedCharacterString>
+                              </gmd:textGroup>
+                            </xsl:if>
+                          </xsl:for-each>
+                        </gmd:PT_FreeText>
+                      </xsl:when>
+                      <xsl:otherwise>
+                        <gco:CharacterString>
+                          <xsl:value-of select="$curDesc"/>
+                        </gco:CharacterString>
+                      </xsl:otherwise>
+                    </xsl:choose>
                   </gmd:description>
                 </xsl:if>
 
-
                 <xsl:if test="$function != ''">
                   <gmd:function>
                     <gmd:CI_OnLineFunctionCode
-                            codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/codelist/ML_gmxCodelists.xml#CI_OnLineFunctionCode"
-                            codeListValue="{$function}"/>
+                      codeList="http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#CI_OnLineFunctionCode"
+                      codeListValue="{$function}"/>
                   </gmd:function>
                 </xsl:if>
               </gmd:CI_OnlineResource>
@@ -336,7 +461,6 @@ Insert is made in first transferOptions found.
         <xsl:otherwise>
           <!-- ... the name is simply added in the newly
           created online element. -->
-
           <gmd:onLine>
             <xsl:if test="$uuidref">
               <xsl:attribute name="uuidref" select="$uuidref"/>
@@ -348,7 +472,7 @@ Insert is made in first transferOptions found.
 
                   <!--Multilingual-->
                   <xsl:when test="contains($url, '#')">
-                    <xsl:attribute name="xsi:type">che:PT_FreeURL_PropertyType</xsl:attribute>
+                    <xsl:attribute name="xs:type">che:PT_FreeURL_PropertyType</xsl:attribute>
 
                     <xsl:for-each select="tokenize($url, $separator)">
                       <xsl:variable name="urlLang"
@@ -393,9 +517,7 @@ Insert is made in first transferOptions found.
 
               <xsl:if test="$protocol != ''">
                 <gmd:protocol>
-                  <gco:CharacterString>
-                    <xsl:value-of select="$protocol"/>
-                  </gco:CharacterString>
+                  <xsl:call-template name="setProtocol"/>
                 </gmd:protocol>
               </xsl:if>
 
@@ -409,7 +531,7 @@ Insert is made in first transferOptions found.
                         <xsl:variable name="nameValue"
                                       select="substring-after(., '#')"></xsl:variable>
                         <xsl:if
-                                test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
+                          test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
                           <gco:CharacterString>
                             <xsl:value-of select="$nameValue"/>
                           </gco:CharacterString>
@@ -424,10 +546,10 @@ Insert is made in first transferOptions found.
                                         select="substring-after(., '#')"></xsl:variable>
 
                           <xsl:if
-                                  test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
+                            test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
                             <gmd:textGroup>
                               <gmd:LocalisedCharacterString
-                                      locale="{concat('#', $nameLang)}">
+                                locale="{concat('#', $nameLang)}">
                                 <xsl:value-of select="$nameValue"/>
                               </gmd:LocalisedCharacterString>
                             </gmd:textGroup>
@@ -447,9 +569,7 @@ Insert is made in first transferOptions found.
 
               <xsl:if test="$name != ''">
                 <gmd:name>
-
                   <xsl:choose>
-
                     <!--Multilingual-->
                     <xsl:when test="contains($name, '#')">
                       <xsl:for-each select="tokenize($name, $separator)">
@@ -457,8 +577,9 @@ Insert is made in first transferOptions found.
                                       select="substring-before(., '#')"></xsl:variable>
                         <xsl:variable name="nameValue"
                                       select="substring-after(., '#')"></xsl:variable>
+
                         <xsl:if
-                                test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
+                          test="$useOnlyPTFreeText = 'false' and $nameLang = $mainLang">
                           <gco:CharacterString>
                             <xsl:value-of select="$nameValue"/>
                           </gco:CharacterString>
@@ -473,10 +594,10 @@ Insert is made in first transferOptions found.
                                         select="substring-after(., '#')"></xsl:variable>
 
                           <xsl:if
-                                  test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
+                            test="$useOnlyPTFreeText = 'true' or $nameLang != $mainLang">
                             <gmd:textGroup>
                               <gmd:LocalisedCharacterString
-                                      locale="{concat('#', $nameLang)}">
+                                locale="{concat('#', $nameLang)}">
                                 <xsl:value-of select="$nameValue"/>
                               </gmd:LocalisedCharacterString>
                             </gmd:textGroup>
@@ -504,7 +625,7 @@ Insert is made in first transferOptions found.
                         <xsl:variable name="descValue"
                                       select="substring-after(., '#')"></xsl:variable>
                         <xsl:if
-                                test="$useOnlyPTFreeText = 'false' and $descLang = $mainLang">
+                          test="$useOnlyPTFreeText = 'false' and $descLang = $mainLang">
                           <gco:CharacterString>
                             <xsl:value-of select="$descValue"/>
                           </gco:CharacterString>
@@ -518,10 +639,10 @@ Insert is made in first transferOptions found.
                           <xsl:variable name="descValue"
                                         select="substring-after(., '#')"></xsl:variable>
                           <xsl:if
-                                  test="$useOnlyPTFreeText = 'true' or $descLang != $mainLang">
+                            test="$useOnlyPTFreeText = 'true' or $descLang != $mainLang">
                             <gmd:textGroup>
                               <gmd:LocalisedCharacterString
-                                      locale="{concat('#', $descLang)}">
+                                locale="{concat('#', $descLang)}">
                                 <xsl:value-of select="$descValue"/>
                               </gmd:LocalisedCharacterString>
                             </gmd:textGroup>
@@ -541,8 +662,8 @@ Insert is made in first transferOptions found.
               <xsl:if test="$function != ''">
                 <gmd:function>
                   <gmd:CI_OnLineFunctionCode
-                          codeList="http://standards.iso.org/ittf/PubliclyAvailableStandards/ISO_19139_Schemas/resources/codelist/ML_gmxCodelists.xml#CI_OnLineFunctionCode"
-                          codeListValue="{$function}"/>
+                    codeList="http://standards.iso.org/iso/19139/resources/gmxCodelists.xml#CI_OnLineFunctionCode"
+                    codeListValue="{$function}"/>
                 </gmd:function>
               </xsl:if>
             </gmd:CI_OnlineResource>
@@ -552,6 +673,26 @@ Insert is made in first transferOptions found.
     </xsl:if>
   </xsl:template>
 
+  <xsl:template name="setProtocol">
+    <xsl:choose>
+      <xsl:when test="$mimeTypeStrategy = 'mimeType' and $mimeType != ''">
+        <gmx:MimeFileType type="{$mimeType}">
+          <xsl:value-of select="$protocol"/>
+        </gmx:MimeFileType>
+      </xsl:when>
+      <xsl:when test="$mimeTypeStrategy = 'protocol' and $mimeType != ''">
+        <gco:CharacterString>
+          <xsl:value-of select="concat($protocol, ':', $mimeType)"/>
+        </gco:CharacterString>
+      </xsl:when>
+      <xsl:otherwise>
+        <gco:CharacterString>
+          <xsl:value-of select="$protocol"/>
+        </gco:CharacterString>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
   <xsl:template match="extra" priority="2"/>
 
   <xsl:template match="@*|node()">
diff --git a/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-remove.xsl b/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-remove.xsl
index 910eb9fd21..a8eab07639 100644
--- a/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-remove.xsl
+++ b/iso19139.che/src/main/plugin/iso19139.che/process/onlinesrc-remove.xsl
@@ -1,4 +1,27 @@
 <?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright (C) 2001-2016 Food and Agriculture Organization of the
+  ~ United Nations (FAO-UN), United Nations World Food Programme (WFP)
+  ~ and United Nations Environment Programme (UNEP)
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or (at
+  ~ your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful, but
+  ~ WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+  ~ General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+  ~
+  ~ Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
+  ~ Rome - Italy. email: geonetwork@osgeo.org
+  -->
+
 <!--
 Stylesheet used to remove a reference to a online resource.
 -->
@@ -6,35 +29,61 @@ Stylesheet used to remove a reference to a online resource.
                 xmlns:gco="http://www.isotc211.org/2005/gco"
                 xmlns:che="http://www.geocat.ch/2008/che"
                 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+                xmlns:xs="http://www.w3.org/2001/XMLSchema"
+                xmlns:digestUtils="java:org.apache.commons.codec.digest.DigestUtils"
+                xmlns:exslt="http://exslt.org/common"
+                exclude-result-prefixes="#all"
                 version="2.0">
 
-  <xsl:param name="url"/>
-  <xsl:param name="name"/>
+  <!--
+      Usage:
+        url is used to identify the resource url to be removed - it is for backwards compatibility.  Will not be used if resourceHash is set.
+        name is used to identify the resource name to be removed - it is for backwards compatibility.  Will not be used if resourceHash is set.
+        resourceHash is hash value of the object to be removed which will ensure the correct value is removed. It will override the usage of url/name
+        resourceIdx is the index location of the object to be removed - can be used when duplicate entries exists to ensure the correct one is removed.
+
+      example:
+        onlinesrc-remove?url=http://geonetwork.org/resource.txt&name=test
+    -->
+
+  <xsl:param name="resourceHash" select="''"/>
+  <xsl:param name="resourceIdx" select="''"/>
+  <xsl:param name="url" select="''"/>
+  <xsl:param name="name" select="''"/>
+
+  <!-- Remove the gmd:onLine define in url parameter  -->
+  <!-- Note: first part of the match needs to match the xsl:for-each select from extract-relations.xsl in order to get the position() to match -->
+  <!-- The unique identifier is marked with resourceIdx which is the position index and resourceHash which is hash code of the current node (combination of url, resource name, and description) -->
+  <xsl:template match="//gmd:MD_DigitalTransferOptions/gmd:onLine" priority="2">
 
-  <!-- Do a copy of every nodes and attributes -->
+    <!-- Calculate the global position of the current gmd:onLine element -->
+    <xsl:variable name="position" select="count(//gmd:MD_DigitalTransferOptions/gmd:onLine[current() >> .]) + 1" />
+
+    <xsl:if test="not(
+                      gmd:CI_OnlineResource[gmd:linkage/gmd:URL != ''] and
+                      ($resourceIdx = '' or $position = xs:integer($resourceIdx)) and
+                      ($resourceHash != '' or ($url != null and (normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:name/gco:CharacterString) = normalize-space($name)
+                                                                or normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and count(gmd:CI_OnlineResource/gmd:name/gmd:PT_FreeText/gmd:textGroup[gmd:LocalisedCharacterString = $name]) > 0
+                                                                or count(gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup[normalize-space(che:LocalisedURL) = normalize-space($url)]) > 0 and normalize-space(gmd:CI_OnlineResource/gmd:name/gco:CharacterString) = normalize-space($name)
+                                                                or count(gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup[normalize-space(che:LocalisedURL) = normalize-space($url)]) > 0 and count(gmd:CI_OnlineResource/gmd:name/gmd:PT_FreeText/gmd:textGroup[gmd:LocalisedCharacterString = $name]) > 0
+                                                                or normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:protocol/*) = 'WWW:DOWNLOAD-1.0-http--download')
+                                                                or normalize-space(gmd:CI_OnlineResource/gmd:linkage/che:LocalisedURL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:protocol/*) = 'WWW:DOWNLOAD-1.0-http--download'))
+                        and ($resourceHash = '' or digestUtils:md5Hex(normalize-space(.)) = $resourceHash)
+                   )">
+      <xsl:copy>
+        <xsl:apply-templates select="@*|node()"/>
+      </xsl:copy>
+    </xsl:if>
+  </xsl:template>
+
+  <!-- Do a copy of every node and attribute -->
   <xsl:template match="@*|node()">
     <xsl:copy>
       <xsl:apply-templates select="@*|node()"/>
     </xsl:copy>
   </xsl:template>
 
-  <!-- Remove geonet:* elements. -->
-  <xsl:template
-    match="geonet:*|gmd:onLine[normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:name/gco:CharacterString) = $name]"
-    priority="2"/>
-  <xsl:template
-    match="geonet:*|gmd:onLine[count(gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup[normalize-space(che:LocalisedURL) = normalize-space($url)]) > 0 and count(gmd:CI_OnlineResource/gmd:name/gmd:PT_FreeText/gmd:textGroup[normalize-space(gmd:LocalisedCharacterString) = normalize-space($name)]) > 0]"
-    priority="2"/>
-  <xsl:template
-    match="geonet:*|gmd:onLine[count(gmd:CI_OnlineResource/gmd:linkage/che:PT_FreeURL/che:URLGroup[normalize-space(che:LocalisedURL) = normalize-space($url)]) > 0 and normalize-space(gmd:CI_OnlineResource/gmd:name/gco:CharacterString) = $name]"
-    priority="2"/>
-  <xsl:template
-    match="geonet:*|gmd:onLine[normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:name//gmd:LocalisedCharacterString) = $name]"
-    priority="2"/>
-  <xsl:template
-    match="geonet:*|gmd:onLine[normalize-space(gmd:CI_OnlineResource/gmd:linkage/gmd:URL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString) = 'WWW:DOWNLOAD-1.0-http--download']"
-    priority="2"/>
-  <xsl:template
-    match="geonet:*|gmd:onLine[normalize-space(gmd:CI_OnlineResource/gmd:linkage/che:LocalisedURL) = $url and normalize-space(gmd:CI_OnlineResource/gmd:protocol/gco:CharacterString) = 'WWW:DOWNLOAD-1.0-http--download']"
-    priority="2"/>
+  <!-- Always remove geonet:* elements. -->
+  <xsl:template match="geonet:*" priority="2"/>
+
 </xsl:stylesheet>