From de2ae7457a84e37dad21a5777e88546ba0bb6358 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 30 Nov 2023 16:29:26 +0100 Subject: [PATCH 01/16] Fix language for region picker directives (#7495) --- .../catalog/components/common/map/partials/drawbbox.html | 2 +- .../resources/catalog/components/utility/UtilityDirective.js | 4 ++++ .../catalog/components/utility/partials/regionpicker.html | 1 + .../resources/catalog/templates/admin/harvest/type/csw.html | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/web-ui/src/main/resources/catalog/components/common/map/partials/drawbbox.html b/web-ui/src/main/resources/catalog/components/common/map/partials/drawbbox.html index 7babe4d873bb..f0aa3ebda3d6 100644 --- a/web-ui/src/main/resources/catalog/components/common/map/partials/drawbbox.html +++ b/web-ui/src/main/resources/catalog/components/common/map/partials/drawbbox.html @@ -3,7 +3,7 @@
-
+
diff --git a/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js b/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js index a2dc9ed6eba0..af1c19cfab1d 100644 --- a/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js +++ b/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js @@ -482,6 +482,8 @@ var addGeonames = !attrs["disableGeonames"]; scope.regionTypes = []; + scope.lang = attrs["lang"]; + function setDefault() { var defaultThesaurus = attrs["default"]; for (var t in scope.regionTypes) { @@ -736,6 +738,8 @@ } }); } + scope.lang = attrs["lang"]; + scope.$watch("regionType", function (val) { if (scope.regionType) { if (scope.regionType.id == "geonames") { diff --git a/web-ui/src/main/resources/catalog/components/utility/partials/regionpicker.html b/web-ui/src/main/resources/catalog/components/utility/partials/regionpicker.html index 19c941aab1ff..4e6b1bb255ee 100644 --- a/web-ui/src/main/resources/catalog/components/utility/partials/regionpicker.html +++ b/web-ui/src/main/resources/catalog/components/utility/partials/regionpicker.html @@ -18,6 +18,7 @@ class="form-control" autocomplete="off" data-gn-region-picker-input="" + data-lang="{{lang}}" placeholder="{{'chooseRegion' | translate}}" /> diff --git a/web-ui/src/main/resources/catalog/templates/admin/harvest/type/csw.html b/web-ui/src/main/resources/catalog/templates/admin/harvest/type/csw.html index 4be559ffff66..cdf833727d58 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/harvest/type/csw.html +++ b/web-ui/src/main/resources/catalog/templates/admin/harvest/type/csw.html @@ -94,6 +94,7 @@ data-ng-model="harvesterSelected.searches[0][c].value"/> --> +
From daccf9bf9b6ff51331776fc7c8a3cddbb8ebd642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Fri, 1 Dec 2023 12:05:08 +0100 Subject: [PATCH 02/16] Remote INSPIRE Atom Feeds harvester - Remove duplicates by dataset identifier (#7491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Remote INSPIRE Atom Feeds harvester - Remove duplicates by dataset identifier, that could be obtained from datasets referenced by different service feeds * Fix sonarlint warnings --------- Co-authored-by: Juan Luis Rodríguez --- .../harvester/InspireAtomHarvester.java | 166 +++++------------- 1 file changed, 43 insertions(+), 123 deletions(-) diff --git a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java index 6379dbf6e848..7954f45fd673 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java +++ b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/harvester/InspireAtomHarvester.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2010 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -50,6 +50,10 @@ import java.util.*; +import static java.util.Comparator.comparing; +import static java.util.stream.Collectors.collectingAndThen; +import static java.util.stream.Collectors.toCollection; + /** * Class to harvest the Atom documents referenced in the iso19139 in the catalog. @@ -67,14 +71,14 @@ public class InspireAtomHarvester { /** * Constructor. * - * @param geonetGontext GeoNetwork context. + * @param geonetContext GeoNetwork context. */ - public InspireAtomHarvester(final GeonetContext geonetGontext) { - this.gc = geonetGontext; + public InspireAtomHarvester(final GeonetContext geonetContext) { + this.gc = geonetContext; } /** - * Process the metadata to check if have an atom document referenced. In this case, the atom + * Process the metadata to check if it has an atom document referenced. In this case, the atom * document is retrieved and stored in the metadata table. */ public final Element harvest() { @@ -89,7 +93,7 @@ public final Element harvest() { String atomProtocol = sm.getValue(Settings.SYSTEM_INSPIRE_ATOM_PROTOCOL); // Using index information, as type is only available in index and not in database. - // If retrieved from database retrieves all iso19139 metadata and should apply for each result an xslt process + // If retrieved from database retrieves all iso19139 metadata and should apply for each result a xslt process // to identify if a service or dataset (slow process) List iso19139Metadata = InspireAtomUtil.searchMetadataByTypeAndProtocol(ServiceContext.get(), @@ -142,7 +146,7 @@ public final Element harvest() { * @param metadataId Metadata identifier */ public final void harvestServiceMetadata(final ServiceContext context, final String metadataId) { - Logger logger = Log.createLogger(Geonet.ATOM); + Logger localLogger = Log.createLogger(Geonet.ATOM); final InspireAtomFeedRepository repository = context.getBean(InspireAtomFeedRepository.class); DataManager dataMan = context.getBean(DataManager.class); @@ -150,27 +154,27 @@ public final void harvestServiceMetadata(final ServiceContext context, final Str final IMetadataUtils metadataUtils = gc.getBean(IMetadataUtils.class); AbstractMetadata iso19139Metadata = metadataUtils.findOne( - Specification.where((Specification) MetadataSpecs.isType(MetadataType.METADATA)) - .and((Specification) MetadataSpecs.isIso19139Schema()) - .and((Specification) MetadataSpecs.hasMetadataId(Integer.parseInt(metadataId)))); + Specification.where((Specification) MetadataSpecs.isType(MetadataType.METADATA)) + .and((Specification) MetadataSpecs.isIso19139Schema()) + .and((Specification) MetadataSpecs.hasMetadataId(Integer.parseInt(metadataId)))); Element result = new Element("response"); try { - logger.info("ATOM feed harvest started for metadata: " + metadataId); + localLogger.info("ATOM feed harvest started for metadata: " + metadataId); // Value used in metadata editor for online resources to identify an INSPIRE atom resource String atomProtocol = sm.getValue(Settings.SYSTEM_INSPIRE_ATOM_PROTOCOL); // Removes all atom information from existing metadata. Harvester will reload with updated information - logger.info("ATOM feed harvest: remove existing metadata feed"); + localLogger.info("ATOM feed harvest: remove existing metadata feed"); repository.deleteAll(InspireAtomFeedSpecs.hasMetadataId(Integer.parseInt(metadataId))); - dataMan.indexMetadata(Arrays.asList(new String[]{metadataId})); + dataMan.indexMetadata(Arrays.asList(metadataId)); // Process service metadata feeds // datasetsInformation stores the dataset information for identifier and namespace for the services feed. // This information is not available in the datasets feeds - logger.info("ATOM feed harvest: processing service metadata feeds"); + localLogger.info("ATOM feed harvest: processing service metadata feeds"); // Retrieve the service metadata referencing atom feed document Map serviceMetadataWithAtomFeed = @@ -180,13 +184,13 @@ public final void harvestServiceMetadata(final ServiceContext context, final Str processServiceMetadataFeeds(dataMan, serviceMetadataWithAtomFeed, result); // Process dataset metadata feeds related to the service metadata - logger.info("ATOM feed harvest for metadata: " + metadataId + ", processing dataset metadata feeds"); - processDatasetsMetadataFeeds(context, dataMan, datasetsInformation, result); + localLogger.info("ATOM feed harvest for metadata: " + metadataId + ", processing dataset metadata feeds"); + processDatasetsMetadataFeeds(dataMan, datasetsInformation, result); - logger.info("ATOM feed harvest finished for metadata: " + metadataId); + localLogger.info("ATOM feed harvest finished for metadata: " + metadataId); } catch (Exception x) { - logger.error("ATOM feed harvest error: " + x.getMessage()); - logger.error(x); + localLogger.error("ATOM feed harvest error: " + x.getMessage()); + localLogger.error(x); } } @@ -194,13 +198,13 @@ public final void harvestServiceMetadata(final ServiceContext context, final Str /** * Process service metadata feeds. * - * @return a Map with the datasets referenced in the service feeds (dataset-id, + * @return a List with the datasets referenced in the service feeds (dataset-id, * dataset-namespace). The namespace is only available in the service feeds. Dataset feeds seem * not containing this information. */ private List processServiceMetadataFeeds(final DataManager dataMan, - final Map serviceMetadataWithAtomFeeds, - Element result) + final Map serviceMetadataWithAtomFeeds, + Element result) throws Exception { List datasetsInformation = new ArrayList<>(); @@ -217,7 +221,7 @@ private List processServiceMetadataFeeds(final DataManager data try { String atomUrl = entry.getValue(); - logger.info("Processing feed (" + i++ + "/"+ total + ") for service metadata with uuid:" + metadataUuid); + logger.info("Processing feed (" + i++ + "/" + total + ") for service metadata with uuid:" + metadataUuid); logger.info("Atom feed Url for service metadata (" + metadataUuid + "): " + atomUrl); String atomFeedDocument = InspireAtomUtil.retrieveRemoteAtomFeedDocument(gc, atomUrl); @@ -240,12 +244,12 @@ private List processServiceMetadataFeeds(final DataManager data repository.save(inspireAtomFeed); // Index the metadata to store the atom feed information in the index - dataMan.indexMetadata(Arrays.asList(new String[]{metadataId})); + dataMan.indexMetadata(Arrays.asList(metadataId)); // Extract datasets information (identifier, namespace) from the service feed: // The namespace is only available in service feed and no in dataset feeds. - // Also NGR metadata uses MD_Identifier instead of RS_Identifier so lacks of this information + // Also, NGR metadata uses MD_Identifier instead of RS_Identifier so lacks of this information logger.debug("Extract datasets information (identifier, namespace) from service atom feed (" + atomUrl + ")"); datasetsInformation.addAll(InspireAtomUtil.extractRelatedDatasetsInfoFromServiceFeed(atomFeedDocument, dataMan)); @@ -261,31 +265,21 @@ private List processServiceMetadataFeeds(final DataManager data } } - return datasetsInformation; + // Remove duplicates by dataset identifier, that could be obtained from datasets referenced by different service feeds + return datasetsInformation.stream() + .collect(collectingAndThen(toCollection(() -> new TreeSet<>(comparing(DatasetFeedInfo::getIdentifier))), + ArrayList::new)); } - /** - * Process dataset metadata feeds. - */ - private void processDatasetsMetadataFeeds(final DataManager dataMan, - final List datasetsFeedInformation, - Element result) - throws Exception { - - processDatasetsMetadataFeeds(ServiceContext.get(), dataMan, datasetsFeedInformation, result); - } - /** * Process the feeds for a set datasets related to a service metadata. * * @param datasetsFeedInformation Datasets map (datasetid, namespace) */ - private void processDatasetsMetadataFeeds(final ServiceContext context, - final DataManager dataMan, + private void processDatasetsMetadataFeeds(final DataManager dataMan, final List datasetsFeedInformation, - final Element result) - throws Exception { + final Element result) { final InspireAtomFeedRepository repository = gc.getBean(InspireAtomFeedRepository.class); @@ -293,7 +287,7 @@ private void processDatasetsMetadataFeeds(final ServiceContext context, long i = 1; // Process the metadata retrieving the atom feed content and store it in the catalog. - for(DatasetFeedInfo datasetFeedInfo: datasetsFeedInformation) { + for (DatasetFeedInfo datasetFeedInfo : datasetsFeedInformation) { String metadataUuid = ""; try { @@ -303,15 +297,18 @@ private void processDatasetsMetadataFeeds(final ServiceContext context, String atomUrl = datasetFeedInfo.feedUrl; - logger.info("Processing feed (" + i++ + "/"+ total + ") for dataset metadata with uuid:" + metadataUuid + ", feed url: " + atomUrl); + logger.info("Processing feed (" + i++ + "/" + total + ") for dataset metadata with uuid:" + + metadataUuid + ", feed url: " + atomUrl); if (StringUtils.isEmpty(metadataUuid)) { - logger.warning("Metadata with dataset identifier (" + datasetFeedInfo.identifier + ") is not available. Skip dataset feed processing"); + logger.warning("Metadata with dataset identifier (" + datasetFeedInfo.identifier + + ") is not available. Skip dataset feed processing"); continue; } if (!atomUrl.toLowerCase().endsWith(".xml")) { - logger.warning("Atom feed Document (" + atomUrl + ") for dataset metadata (" + metadataUuid + ") is not a valid feed"); + logger.warning("Atom feed Document (" + atomUrl + ") for dataset metadata (" + metadataUuid + + ") is not a valid feed"); continue; } @@ -340,7 +337,7 @@ private void processDatasetsMetadataFeeds(final ServiceContext context, repository.save(inspireAtomFeed); // Index the metadata to store the atom feed information in the index - dataMan.indexMetadata(Arrays.asList(new String[]{metadataId})); + dataMan.indexMetadata(Arrays.asList(metadataId)); result.addContent(new Element("feed").setAttribute("uuid", metadataUuid) .setAttribute("feed", atomUrl).setAttribute("status", "ok")); } catch (Exception ex) { @@ -351,81 +348,4 @@ private void processDatasetsMetadataFeeds(final ServiceContext context, } } } - - - /** - * Process the feeds for a set datasets related to a service metadata. - * - */ - private void processDatasetsMetadataFeedsForService(final ServiceContext context, - final DataManager dataMan, - final List datasetsFeedInformation, - final Element result) - throws Exception { - - // Retrieve the metadata referencing atom feed documents. - - // Value used in metadata editor for online resources to identify an INSPIRE atom resource - String atomProtocol = gc.getBean(SettingManager.class).getValue("system/inspire/atomProtocol"); - - final InspireAtomFeedRepository repository = gc.getBean(InspireAtomFeedRepository.class); - - // Process the metadata retrieving the atom feed content and store it in the catalog. - for(DatasetFeedInfo datasetFeedInfo: datasetsFeedInformation) { - String atomDatasetId = datasetFeedInfo.identifier; - String metadataUuid = ""; - - try { - metadataUuid = InspireAtomUtil.retrieveDatasetUuidFromIdentifier( - gc.getBean(EsSearchManager.class), atomDatasetId); - - String atomDatasetNs = datasetFeedInfo.namespace; - logger.debug("Dataset, id=" + atomDatasetId + ", namespace=" + atomDatasetNs); - - if (StringUtils.isEmpty(metadataUuid)) { - logger.warning("Can't find dataset metadata with datasetIdCode:" + atomDatasetId); - continue; - } - - logger.info("Processing feed for dataset metadata with uuid:" + metadataUuid); - - String metadataId = dataMan.getMetadataId(metadataUuid); - - // Get the atom feed url for the dataset - String atomUrl = datasetFeedInfo.feedUrl; - logger.debug("Dataset, feedurl=" + atomUrl); - - // Retrieve the atom document and store it. - String atomFeedDocument = InspireAtomUtil.retrieveRemoteAtomFeedDocument(gc, atomUrl); - logger.debug("Dataset feed: " + atomFeedDocument); - - Element atomDoc = Xml.loadString(atomFeedDocument, false); - - // Skip document if not a feed - if (!atomDoc.getNamespace().equals(Geonet.Namespaces.ATOM)) { - logger.warning("Atom feed Document (" + atomUrl + ") for dataset metadata (" + metadataUuid + ") is not a valid feed"); - continue; - } - - InspireAtomFeed inspireAtomFeed = InspireAtomFeed.build(atomDoc); - inspireAtomFeed.setMetadataId(Integer.parseInt(metadataId)); - inspireAtomFeed.setAtomDatasetid(atomDatasetId); - inspireAtomFeed.setAtomDatasetns(atomDatasetNs); - inspireAtomFeed.setAtomUrl(atomUrl); - inspireAtomFeed.setAtom(atomFeedDocument); - - repository.save(inspireAtomFeed); - - // Index the metadata to store the atom feed information in the index - dataMan.indexMetadata(Arrays.asList(new String[]{metadataId})); - - result.addContent(new Element("feed").setAttribute("uuid", metadataUuid).setAttribute("feed", atomUrl).setAttribute("status", "ok")); - } catch (Exception ex) { - // Log exception and continue processing the other metadata - logger.error("Failed to process atom feed for dataset metadata: " + metadataUuid + " " + ex.getMessage()); - result.addContent(new Element("feed").setAttribute("uuid", metadataUuid).setAttribute("error", ex.getMessage()).setAttribute("status", "error")); - - } - } - } } From 6da0c707d4bd2a6b50a8e01a534ef667777285e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Fri, 1 Dec 2023 14:01:44 +0100 Subject: [PATCH 03/16] INSPIRE Atom Search - add missing filter by feed dataset identifiers (#7492) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * INSPIRE Atom Search - add missing filter by feed dataset identifiers * Fix sonarlint warnings and format code --------- Co-authored-by: Juan Luis Rodríguez --- .../services/inspireatom/AtomSearch.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomSearch.java b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomSearch.java index b88a509687c9..cd1e247b53c9 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomSearch.java +++ b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomSearch.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -30,9 +30,11 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.server.context.ServiceContext; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.search.SearchHit; import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.exception.FeatureNotEnabledException; import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.InspireAtomFeed; @@ -58,9 +60,9 @@ import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import java.util.ArrayList; import java.util.List; import java.util.Locale; +import java.util.stream.Collectors; import static org.fao.geonet.kernel.search.EsFilterBuilder.buildPermissionsFilter; import static org.fao.geonet.kernel.search.EsSearchManager.FIELDLIST_CORE; @@ -108,25 +110,26 @@ public class AtomSearch { @ApiResponse(responseCode = "204", description = "Not authenticated.") }) @ResponseStatus(OK) - @ResponseBody public Element feeds( @Parameter( description = "fileIdentifier", required = false) @RequestParam(defaultValue = "") - String fileIdentifier, + String fileIdentifier, @Parameter(hidden = true) - HttpServletRequest request) throws Exception { + HttpServletRequest request) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); boolean inspireEnable = sm.getValueAsBool(Settings.SYSTEM_INSPIRE_ENABLE); if (!inspireEnable) { Log.info(Geonet.ATOM, "Inspire is disabled"); - throw new Exception("Inspire is disabled"); + throw new FeatureNotEnabledException("Inspire is disabled"); } - List datasetIdentifiers = new ArrayList<>(); + List datasetIdentifiers; + + String datasetIdentifiersFilter = ""; // If fileIdentifier is provided search only in the related datasets if (StringUtils.isNotEmpty(fileIdentifier)) { @@ -142,9 +145,12 @@ public Element feeds( // Retrieve the datasets related to the service metadata datasetIdentifiers = InspireAtomUtil.extractRelatedDatasetsIdentifiers(schema, md, dm); - // Add query filter / TODO Migrate ? - // String values = Joiner.on(" or ").join(datasetIdentifiers); - // params.addContent(new Element("identifier").setText(values)); + String datasets = datasetIdentifiers.stream().map(StringEscapeUtils::escapeJson) + .collect(Collectors.joining("\",\"", "\"", "\"")); + + datasetIdentifiersFilter = String.format(", {\"terms\": {\n" + + " \"resourceIdentifier.code\": [%s]\n" + + " }}", datasets); } String privilegesFilter = buildPermissionsFilter(context); @@ -161,11 +167,11 @@ public Element feeds( " \"query_string\": {" + " \"query\": \"%s\"" + " }" + - " }]" + + " }%s]" + " }" + "}"; ObjectMapper objectMapper = new ObjectMapper(); - JsonNode esJsonQuery = objectMapper.readTree(String.format(jsonQuery, privilegesFilter)); + JsonNode esJsonQuery = objectMapper.readTree(String.format(jsonQuery, privilegesFilter, datasetIdentifiersFilter)); final SearchResponse result = searchMan.query( esJsonQuery, @@ -202,19 +208,18 @@ public Element feeds( @ApiResponse(responseCode = "204", description = "Not authenticated.") }) @ResponseStatus(OK) - @ResponseBody public String feedsAsHtml( @Parameter( description = "fileIdentifier", required = false) @RequestParam(defaultValue = "") - String fileIdentifier, + String fileIdentifier, @Parameter(hidden = true) - HttpServletRequest request) throws Exception { + HttpServletRequest request) throws Exception { Element feeds = feeds(fileIdentifier, request); Locale locale = languageUtils.parseAcceptLanguage(request.getLocales()); - String language = isoLanguagesMapper.iso639_2T_to_iso639_2B(locale.getISO3Language()); + String language = IsoLanguagesMapper.iso639_2T_to_iso639_2B(locale.getISO3Language()); language = XslUtil.twoCharLangCode(language, "eng").toLowerCase(); return new XsltResponseWriter(null, "atom-feeds") From bd153b0b9af09db01d0faf7d5cfc6b8708110047 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Fri, 1 Dec 2023 15:44:46 +0100 Subject: [PATCH 04/16] Validation of INSPIRE ATOM services to return API exceptions instead of error 400 (#7490) --- .../geonet/services/inspireatom/AtomDescribe.java | 11 +++++++++++ .../fao/geonet/services/inspireatom/AtomGetData.java | 12 ++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomDescribe.java b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomDescribe.java index 97ac43a91477..97091e008e1f 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomDescribe.java +++ b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomDescribe.java @@ -50,6 +50,7 @@ import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; +import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; @@ -138,6 +139,16 @@ public Element describeResource( throw new Exception("Inspire is disabled"); } + if (StringUtils.isEmpty(fileIdentifier)) { + if (StringUtils.isEmpty(spatial_dataset_identifier_code)) { + throw new MissingServletRequestParameterException("spatial_dataset_identifier_code", "String"); + } + + if (StringUtils.isEmpty(spatial_dataset_identifier_namespace)) { + throw new MissingServletRequestParameterException("spatial_dataset_identifier_namespace", "String"); + } + } + Element response = StringUtils.isEmpty(fileIdentifier) ? processDatasetFeed(spatial_dataset_identifier_code, spatial_dataset_identifier_namespace, context) diff --git a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomGetData.java b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomGetData.java index 05fdd089b8de..a9133fe38a73 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomGetData.java +++ b/inspire-atom/src/main/java/org/fao/geonet/services/inspireatom/AtomGetData.java @@ -91,18 +91,18 @@ public class AtomGetData { public Element downloadResource( @Parameter( description = "spatial_dataset_identifier_code", - required = false) - @RequestParam(defaultValue = "") + required = true) + @RequestParam String spatial_dataset_identifier_code, @Parameter( description = "spatial_dataset_identifier_namespace", - required = false) - @RequestParam(defaultValue = "") + required = true) + @RequestParam String spatial_dataset_identifier_namespace, @Parameter( description = "crs", - required = false) - @RequestParam(defaultValue = "") + required = true) + @RequestParam String crs, @Parameter(hidden = true) HttpServletRequest request, From fb203c283737685eb85a90c7044c408e8663d50b Mon Sep 17 00:00:00 2001 From: Jeroen Ticheler Date: Sun, 3 Dec 2023 21:01:37 +0100 Subject: [PATCH 05/16] Update README.md Small edits --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d33680cf452e..ed6c54d35961 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# GeoNetwork Open-source +# GeoNetwork opensource ## Build Health @@ -11,13 +11,13 @@ * An interactive Web Map Viewer to combine Web Map Services from distributed servers around the world * Online editing of metadata with a powerful template system * Scheduled harvesting and synchronization of metadata between distributed catalogs -* Support for OGC-CSW 2.0.2 ISO Profile, OAI-PMH, SRU protocols +* Support for OGC-CSW 2.0.2, ISO 1911x and DCAT-AP metadata profiles, OAI-PMH, SRU protocols * Fine-grained access control with group and user management * Multi-lingual user interface ## Documentation -The Geonetwork Manual and Online Help are included in the `docs/manual` folder. This content is compiled into html pages during a release for a publishing on docs.geonetwork-opensource.org website. +The GeoNetwork Manual and Online Help are included in the `docs/manual` folder. This content is compiled into html pages during a release for a publishing on docs.geonetwork-opensource.org website. * [docs.geonetwork-opensource.org](https://docs.geonetwork-opensource.org) @@ -28,5 +28,5 @@ The online help is compiled into html pages during a release and is included in Developer documentation located in ``README.md`` files in the code-base: * General documentation for the project as a whole is in this [README.md](README.md) -* [Software Development Documentation](/software_development/) provides instructions for setting up a development environment, building Geonetwork, compiling user documentation, and making a releases. +* [Software Development Documentation](/software_development/) provides instructions for setting up a development environment, building GeoNetwork, compiling user documentation, and making a releases. * Module specific documentation can be found in each module: From cd1d059c0264e0c80f9b1f7c3e009f072a951840 Mon Sep 17 00:00:00 2001 From: Jeroen Ticheler Date: Sun, 3 Dec 2023 21:05:24 +0100 Subject: [PATCH 06/16] Update CONTRIBUTING.md Small simplification in wording --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e8065add3b1c..93a66e2617b3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,7 +19,7 @@ Thank you for contributing to GeoNetwork: * Good housekeeping: Anytime you commit, try and clean the code around it to latest style guide. If you improve a function without comments: add comments. If you modify functionality that does not have tests: write a test. If you fix functionality without documentation: add documentation. -* History: Clean commit messages and history: avoid big commits with hundreds of files, break commits up into understandable chunks, longer verbose commit messages are encouraged. Beware of reformatting and needless whitespace changes. +* History: Clean commit messages and history: avoid big commits with hundreds of files, break commits up into understandable chunks, longer verbose commit messages are encouraged. Avoid reformatting and needless whitespace changes. * Draft: Use pull request *Draft** (or even the text "WIP") to identify work in progress. From 568e403ca766a054cdcd17926ddd35818560666b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 30 Nov 2023 11:00:48 +0100 Subject: [PATCH 07/16] GeoNetwork 4.4.x minor versions library updates: - Spring Framework: 5.3.31 - Spring Security: 5.8.8 - Spring JPA: 2.7.18 - Jackson: 2.15.3 - Jetty: 9.4.53.v20231009 --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index ac9d632a6edb..5bec5a0e06a5 100644 --- a/pom.xml +++ b/pom.xml @@ -1579,7 +1579,7 @@ request the list of hosts (but JPA cache db queries). --> * - 9.4.52.v20230823 + 9.4.53.v20231009 jetty-distribution-${jetty.version} https://repo1.maven.org/maven2/org/eclipse/jetty/jetty-distribution/${jetty.version}/${jetty.file}.tar.gz @@ -1589,9 +1589,9 @@ 1.19.0 42.6.0 - 5.3.30 - 5.8.7 - 2.7.16 + 5.3.31 + 5.8.8 + 2.7.18 2.7.0 1.5.13 5.6.15.Final @@ -1604,7 +1604,7 @@ true 2.7 2.1.1 - 2.15.2 + 2.15.3 9.1.22 2.25.1 2.17.2 From 9bab08206c7dc97d8c85ebe6aea95c392a66fb93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Mon, 4 Dec 2023 10:58:08 +0100 Subject: [PATCH 08/16] Search / Add option to show less facet values (#7497) * Add option to show less facet values * Add option to show less facet values - add additional styling --- .../components/elasticsearch/EsFacet.js | 6 +++++ .../directives/FacetDirective.js | 6 +++++ .../directives/partials/facets.html | 11 +++++++++- .../searchmanager/SearchFormDirective.js | 22 +++++++++++++++++++ .../resources/catalog/style/gn_search.less | 3 ++- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/web-ui/src/main/resources/catalog/components/elasticsearch/EsFacet.js b/web-ui/src/main/resources/catalog/components/elasticsearch/EsFacet.js index 8944549af142..732280439645 100644 --- a/web-ui/src/main/resources/catalog/components/elasticsearch/EsFacet.js +++ b/web-ui/src/main/resources/catalog/components/elasticsearch/EsFacet.js @@ -83,6 +83,9 @@ } } }; + this.getDefaultSize = function () { + return DEFAULT_SIZE; + }; this.buildDefaultQuery = function (query, size) { return { script_fields: defaultScriptedFields, @@ -392,6 +395,9 @@ facetModel.type = "terms"; facetModel.size = reqAgg.terms.size; facetModel.more = respAgg.sum_other_doc_count > 0; + facetModel.less = + respAgg.buckets && + respAgg.buckets.length > Math.min(reqAgg.terms.size, DEFAULT_SIZE); facetModel.includeFilter = reqAgg.terms.include !== undefined; facetModel.excludeFilter = reqAgg.terms.exclude !== undefined; var esFacet = this; diff --git a/web-ui/src/main/resources/catalog/components/elasticsearch/directives/FacetDirective.js b/web-ui/src/main/resources/catalog/components/elasticsearch/directives/FacetDirective.js index f4e48934d377..bae90be49a07 100644 --- a/web-ui/src/main/resources/catalog/components/elasticsearch/directives/FacetDirective.js +++ b/web-ui/src/main/resources/catalog/components/elasticsearch/directives/FacetDirective.js @@ -101,6 +101,12 @@ }); }; + FacetsController.prototype.loadLessTerms = function (facet) { + this.searchCtrl.loadLessTerms(facet).then(function (terms) { + angular.copy(terms, facet); + }); + }; + FacetsController.prototype.filterTerms = function (facet) { if (facet.meta && facet.meta.filterByTranslation) { var match = []; diff --git a/web-ui/src/main/resources/catalog/components/elasticsearch/directives/partials/facets.html b/web-ui/src/main/resources/catalog/components/elasticsearch/directives/partials/facets.html index 6b9eff784140..c3805b6b5a75 100644 --- a/web-ui/src/main/resources/catalog/components/elasticsearch/directives/partials/facets.html +++ b/web-ui/src/main/resources/catalog/components/elasticsearch/directives/partials/facets.html @@ -136,12 +136,21 @@ more + | + + less + diff --git a/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchFormDirective.js b/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchFormDirective.js index 8c34410690fe..b0d1a5ee09c5 100644 --- a/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchFormDirective.js +++ b/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchFormDirective.js @@ -544,6 +544,28 @@ ); }; + this.loadLessTerms = function (facet, moreItemsNumber) { + var request = gnESService.generateEsRequest( + $scope.finalParams, + $scope.searchObj.state, + $scope.searchObj.configId, + $scope.searchObj.filters + ); + + var itemsToRequest = facet.items.length - (moreItemsNumber || 20); + if (itemsToRequest <= 0) { + itemsToRequest = gnESFacet.getDefaultSize(); + } + return gnESClient.getTermsParamsWithNewSizeOrFilter( + request.query, + facet.key, + facet.config, + itemsToRequest, + facet.include || undefined, + facet.exclude || undefined + ); + }; + this.filterTerms = function (facet) { var request = gnESService.generateEsRequest( $scope.finalParams, diff --git a/web-ui/src/main/resources/catalog/style/gn_search.less b/web-ui/src/main/resources/catalog/style/gn_search.less index 3ef31cfb2298..990fb699d595 100644 --- a/web-ui/src/main/resources/catalog/style/gn_search.less +++ b/web-ui/src/main/resources/catalog/style/gn_search.less @@ -124,7 +124,8 @@ color: @brand-primary; } } - .gn-facet-more { + .gn-facet-more, + .gn-facet-less { margin-left: 15px; } .gn-facet-input-group { From 00aea02d8957eaae236015a9615b118b6d1e5c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Thu, 30 Nov 2023 11:42:28 +0100 Subject: [PATCH 09/16] Metadata indexing / Escape website and logo information for contacts --- .../src/main/plugin/iso19115-3.2018/index-fields/index.xsl | 4 ++-- .../iso19139/src/main/plugin/iso19139/index-fields/index.xsl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl index 97c572148ebd..0cc9d491cb8e 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index.xsl @@ -1431,8 +1431,8 @@ "role":"", "email":"", - "website":"", - "logo":"", + "website":"", + "logo":"", "individual":"", "position":"", "phone":"", diff --git a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl index a2b75ada3223..c7d7c857fcc2 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl @@ -1279,8 +1279,8 @@ "role":"", "email":"", - "website":"", - "logo":"", + "website":"", + "logo":"", "individual":"", "position":"", "phone":"", From e665cef0fa7d7e77b343985dd6a649000c0fab57 Mon Sep 17 00:00:00 2001 From: Joachim Nielandt Date: Mon, 20 Nov 2023 10:31:30 +0100 Subject: [PATCH 10/16] consistent styling of recordgroup label --- .../catalog/components/utility/partials/recordOriginLogo.html | 2 +- .../catalog/views/default/templates/recordView/metadata.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web-ui/src/main/resources/catalog/components/utility/partials/recordOriginLogo.html b/web-ui/src/main/resources/catalog/components/utility/partials/recordOriginLogo.html index 325a9cef76b4..f0d39d5f2039 100644 --- a/web-ui/src/main/resources/catalog/components/utility/partials/recordOriginLogo.html +++ b/web-ui/src/main/resources/catalog/components/utility/partials/recordOriginLogo.html @@ -12,5 +12,5 @@ aria-label="{{'sourceCatalog' | translate}}" class="gn-source-logo" /> - {{recordGroup.label | gnLocalized}} + {{recordGroup.label | gnLocalized}} diff --git a/web-ui/src/main/resources/catalog/views/default/templates/recordView/metadata.html b/web-ui/src/main/resources/catalog/views/default/templates/recordView/metadata.html index 8762e3f749b1..b6d2720bfa63 100644 --- a/web-ui/src/main/resources/catalog/views/default/templates/recordView/metadata.html +++ b/web-ui/src/main/resources/catalog/views/default/templates/recordView/metadata.html @@ -29,7 +29,7 @@

metadataLanguage

- sourceCatalog: +

sourceCatalog

: From ed93d7b8e4e4b4fe49eeed8c451810c9e2cf08cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Tue, 5 Dec 2023 07:53:46 +0100 Subject: [PATCH 11/16] Metadata workflow / Record view / reload the page with the approved version when cancelling a working copy (#7503) * Metadata workflow / Record view / reload the page with the approved version when cancelling a working copy, instead of redirecting to the search results * Metadata workflow / Record view / reload the page with the approved version when cancelling a working copy - fix confirmation message to display the metadata title --- .../metadataactions/MetadataActionService.js | 6 +++- .../components/search/mdview/mdviewModule.js | 35 +++++++++++++++++-- .../resources/catalog/locales/ca-core.json | 4 +-- .../resources/catalog/locales/cs-core.json | 4 +-- .../resources/catalog/locales/da-core.json | 4 +-- .../resources/catalog/locales/de-core.json | 4 +-- .../resources/catalog/locales/en-core.json | 2 +- .../resources/catalog/locales/es-core.json | 4 +-- .../resources/catalog/locales/fi-core.json | 4 +-- .../resources/catalog/locales/fr-core.json | 4 +-- .../resources/catalog/locales/is-core.json | 4 +-- .../resources/catalog/locales/it-core.json | 4 +-- .../resources/catalog/locales/ko-core.json | 4 +-- .../resources/catalog/locales/nl-core.json | 4 +-- .../resources/catalog/locales/pt-core.json | 4 +-- .../resources/catalog/locales/ru-core.json | 4 +-- .../resources/catalog/locales/sk-core.json | 4 +-- .../resources/catalog/locales/sv-core.json | 4 +-- .../resources/catalog/locales/zh-core.json | 4 +-- .../templates/recordView/recordView.html | 26 +++++++++----- 20 files changed, 88 insertions(+), 45 deletions(-) diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js index a76b43e8c696..84a9d9a2492f 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -293,6 +293,10 @@ return deferred.promise; }; + this.cancelWorkingCopy = function (md) { + return gnMetadataManager.remove(md.id); + }; + this.getMetadataIdToEdit = function (md) { if (!md) return; diff --git a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js index f0c1229100b0..f4ec405e0928 100644 --- a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js +++ b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewModule.js @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -59,6 +59,7 @@ "$rootScope", "$filter", "gnUtilityService", + "$window", function ( $scope, $http, @@ -77,7 +78,8 @@ gnConfigService, $rootScope, $filter, - gnUtilityService + gnUtilityService, + $window ) { $scope.formatter = gnSearchSettings.formatter; $scope.gnMetadataActions = gnMetadataActions; @@ -203,6 +205,35 @@ ); }; + $scope.cancelWorkingCopy = function (md) { + return gnMetadataActions.cancelWorkingCopy(md).then( + function (data) { + gnAlertService.addAlert({ + msg: $translate.instant("metadataRemoved", { + title: md.resourceTitle + }), + type: "success" + }); + + // Set a timeout to reload the page, to display the alert + $window.setTimeout(function () { + $window.location.href = $location + .absUrl() + .replace("/metadraf/", "/metadata/"); + $window.location.reload(); + }, 500); + }, + function (reason) { + // Data needs improvements + // See https://github.com/geonetwork/core-geonetwork/issues/723 + gnAlertService.addAlert({ + msg: reason.data.description, + type: "danger" + }); + } + ); + }; + /** * Scroll to an element in the page using it's ID * diff --git a/web-ui/src/main/resources/catalog/locales/ca-core.json b/web-ui/src/main/resources/catalog/locales/ca-core.json index 505934e42f50..bb65545c7488 100644 --- a/web-ui/src/main/resources/catalog/locales/ca-core.json +++ b/web-ui/src/main/resources/catalog/locales/ca-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/cs-core.json b/web-ui/src/main/resources/catalog/locales/cs-core.json index f665ee61ab25..ee9bb7b2a92d 100644 --- a/web-ui/src/main/resources/catalog/locales/cs-core.json +++ b/web-ui/src/main/resources/catalog/locales/cs-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Kategorie aktualizovány.", "warnPublishDraft": "Při publikování záznamů s povoleným pracovním postupem se stav změní na 'Schválit'. Jste si jistý, že chcete pokračovat?", "cancelWorkingCopy": "Zrušit pracovní kopii", - "deleteWorkingCopyRecordConfirm": "Opravdu chcete odstranit pracovní kopii '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Opravdu chcete odstranit pracovní kopii '{{resourceTitle}}'?", "workingCopy": "Pracovní kopie", "onTheWeb": "Více online informací", "pdfReportTocTitle": "Obsah", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Prohlížející nemůže zrušit publikování metadat", "reviewerNotAllowedPublishUnpublish": "Prohlížející nemá oprávnění publikovat/zrušit publikování metadat", "missingTitle": "Chybí název" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/da-core.json b/web-ui/src/main/resources/catalog/locales/da-core.json index 5b409eb93ec8..6b4e63e6d654 100644 --- a/web-ui/src/main/resources/catalog/locales/da-core.json +++ b/web-ui/src/main/resources/catalog/locales/da-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Kategorier opdateret.", "warnPublishDraft": "Når du udgiver poster med workflow aktiveret, ændres status til 'Godkend'. Er du sikker på, at du vil fortsætte?", "cancelWorkingCopy": "Annuller arbejdskopi", - "deleteWorkingCopyRecordConfirm": "Vil du virkelig fjerne arbejdskopien '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Vil du virkelig fjerne arbejdskopien '{{resourceTitle}}'?", "workingCopy": "Arbejdskopi", "onTheWeb": "Mere online information", "pdfReportTocTitle": "Indhold", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/de-core.json b/web-ui/src/main/resources/catalog/locales/de-core.json index 95fba7a1ffe8..2517a991a773 100644 --- a/web-ui/src/main/resources/catalog/locales/de-core.json +++ b/web-ui/src/main/resources/catalog/locales/de-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Kategorien aktualisiert.", "warnPublishDraft": "Wenn Sie Datensätze mit aktiviertem Workflow veröffentlichen, ändert sich der Status in 'genehmigt'. Sind Sie sicher, dass Sie fortfahren möchten?", "cancelWorkingCopy": "Arbeitskopie abbrechen", - "deleteWorkingCopyRecordConfirm": "Möchten Sie die Arbeitskopie '{{title}}' wirklich entfernen?", + "deleteWorkingCopyRecordConfirm": "Möchten Sie die Arbeitskopie '{{resourceTitle}}' wirklich entfernen?", "workingCopy": "Arbeitskopie", "onTheWeb": "Weiterführende Online-Informationen", "pdfReportTocTitle": "Inhalte", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/en-core.json b/web-ui/src/main/resources/catalog/locales/en-core.json index 5ba7ea65c8fa..a1e71bfb3fd9 100644 --- a/web-ui/src/main/resources/catalog/locales/en-core.json +++ b/web-ui/src/main/resources/catalog/locales/en-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", diff --git a/web-ui/src/main/resources/catalog/locales/es-core.json b/web-ui/src/main/resources/catalog/locales/es-core.json index 4519514f41f1..7ff51fa85da6 100644 --- a/web-ui/src/main/resources/catalog/locales/es-core.json +++ b/web-ui/src/main/resources/catalog/locales/es-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categorías actualizadas.", "warnPublishDraft": "Al publicar registros con el flujo de trabajo habilitado, el estado cambiará a 'Approve'. Estás seguro de que quieres continuar?", "cancelWorkingCopy": "Cancelar copia de trabajo", - "deleteWorkingCopyRecordConfirm": "¿Realmente desea eliminar la copia de trabajo '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "¿Realmente desea eliminar la copia de trabajo '{{resourceTitle}}'?", "workingCopy": "Copia de trabajo", "onTheWeb": "Más información en línea", "pdfReportTocTitle": "Contenido", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/fi-core.json b/web-ui/src/main/resources/catalog/locales/fi-core.json index 7ad904e7a9d6..6bb2fe99ad00 100644 --- a/web-ui/src/main/resources/catalog/locales/fi-core.json +++ b/web-ui/src/main/resources/catalog/locales/fi-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/fr-core.json b/web-ui/src/main/resources/catalog/locales/fr-core.json index c7492a7c513a..9a8d54c8726e 100644 --- a/web-ui/src/main/resources/catalog/locales/fr-core.json +++ b/web-ui/src/main/resources/catalog/locales/fr-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Catégorie mise à jour.", "warnPublishDraft": "Lors de la publication d'une fiche ayant un statut brouillon, le statut passera à approuvé. Voulez-vous continuer ?", "cancelWorkingCopy": "Annuler la copie de travail", - "deleteWorkingCopyRecordConfirm": "Voulez-vous vraiment supprimer la copie de travail ?", + "deleteWorkingCopyRecordConfirm": "Voulez-vous vraiment supprimer la copie de travail '{{ resourceTitle}}' ?", "workingCopy": "Brouillon", "onTheWeb": "Plus d'information en ligne", "pdfReportTocTitle": "Titre de la table des matières", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Les relecteurs ne sont pas autorisés à dépublier des fiches", "reviewerNotAllowedPublishUnpublish": "Les relecteurs ne sont pas autorisés à publier/dépublier des fiches", "missingTitle": "Titre manquant" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/is-core.json b/web-ui/src/main/resources/catalog/locales/is-core.json index 047b14a7eb19..6cab643fdbce 100644 --- a/web-ui/src/main/resources/catalog/locales/is-core.json +++ b/web-ui/src/main/resources/catalog/locales/is-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Flokkar uppfærðir.", "warnPublishDraft": "Þegar færslur eru birtar með vinnuflæði virkt, mun staðan breytast yfir í 'Samþykkt. Ertu viss um að þú viljir halda áfram?", "cancelWorkingCopy": "Hætta við vinnuafrit", - "deleteWorkingCopyRecordConfirm": "Ertu viss um að þú viljir fjarlægja vinnuafritið '{{titill}}'?", + "deleteWorkingCopyRecordConfirm": "Ertu viss um að þú viljir fjarlægja vinnuafritið '{{resourceTitle}}'?", "workingCopy": "Vinnuafrit", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/it-core.json b/web-ui/src/main/resources/catalog/locales/it-core.json index c7c85a7f9002..0fb2eda8cae0 100644 --- a/web-ui/src/main/resources/catalog/locales/it-core.json +++ b/web-ui/src/main/resources/catalog/locales/it-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/ko-core.json b/web-ui/src/main/resources/catalog/locales/ko-core.json index 9ff3ab75e388..adb124f10d12 100644 --- a/web-ui/src/main/resources/catalog/locales/ko-core.json +++ b/web-ui/src/main/resources/catalog/locales/ko-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "카테고리를 갱신하였습니다.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/nl-core.json b/web-ui/src/main/resources/catalog/locales/nl-core.json index c23c85edd5cb..b275fa8b9e98 100644 --- a/web-ui/src/main/resources/catalog/locales/nl-core.json +++ b/web-ui/src/main/resources/catalog/locales/nl-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categoriën geupdated.", "warnPublishDraft": "Wanneer de workflow is geactiveerd en de records worden gepubliceerd dan veranderd de status in 'Goedgekeurd'. Doorgaan?", "cancelWorkingCopy": "Annuleer concept versie", - "deleteWorkingCopyRecordConfirm": "Weet je zeker dat je de concept versie '{{title}}' wilt verwijderen?", + "deleteWorkingCopyRecordConfirm": "Weet je zeker dat je de concept versie '{{resourceTitle}}' wilt verwijderen?", "workingCopy": "Concept versie", "onTheWeb": "Meer informatie online", "pdfReportTocTitle": "Inhoudsopgave", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/pt-core.json b/web-ui/src/main/resources/catalog/locales/pt-core.json index 648ec0d75680..d2d690cc8155 100644 --- a/web-ui/src/main/resources/catalog/locales/pt-core.json +++ b/web-ui/src/main/resources/catalog/locales/pt-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categorias atualizadas.", "warnPublishDraft": "Ao publicar um registro com o fluxo de trabalho habilitado, o status vai automaticamente ser alterado para Aprovado. Tem certeza que deseja continuar?", "cancelWorkingCopy": "Cancelar Cópia de Trabalho", - "deleteWorkingCopyRecordConfirm": "Você realmente deseja remover a Cópia de Trabalho '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Você realmente deseja remover a Cópia de Trabalho '{{resourceTitle}}'?", "workingCopy": "Cópia de Trabalho", "onTheWeb": "Mais informações online", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/ru-core.json b/web-ui/src/main/resources/catalog/locales/ru-core.json index ac3e2f1e0a69..3458ed754ede 100644 --- a/web-ui/src/main/resources/catalog/locales/ru-core.json +++ b/web-ui/src/main/resources/catalog/locales/ru-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Содержание", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/sk-core.json b/web-ui/src/main/resources/catalog/locales/sk-core.json index 56ca3c9ab798..0cbec7d89966 100644 --- a/web-ui/src/main/resources/catalog/locales/sk-core.json +++ b/web-ui/src/main/resources/catalog/locales/sk-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Categories updated.", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/sv-core.json b/web-ui/src/main/resources/catalog/locales/sv-core.json index 05d323c21031..a0a080ae118e 100644 --- a/web-ui/src/main/resources/catalog/locales/sv-core.json +++ b/web-ui/src/main/resources/catalog/locales/sv-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "Uppdatareade kategorier.", "warnPublishDraft": "När du publicerar poster med arbetsflöde aktiverat kommer status att ändras till 'Godkänn'. Är du säker på att du vill fortsätta?", "cancelWorkingCopy": "Avbryt arbetskopian", - "deleteWorkingCopyRecordConfirm": "Vill du verkligen ta bort arbetskopian '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Vill du verkligen ta bort arbetskopian '{{resourceTitle}}'?", "workingCopy": "Arbetskopia", "onTheWeb": "Mer information online", "pdfReportTocTitle": "Innehåll", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/locales/zh-core.json b/web-ui/src/main/resources/catalog/locales/zh-core.json index 6d5239cc3325..08971101610a 100644 --- a/web-ui/src/main/resources/catalog/locales/zh-core.json +++ b/web-ui/src/main/resources/catalog/locales/zh-core.json @@ -546,7 +546,7 @@ "categoriesUpdated": "分类已更新。", "warnPublishDraft": "When publishing records with workflow enabled, the status will change to 'Approve'. Are you sure you want to continue?", "cancelWorkingCopy": "Cancel working copy", - "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{title}}'?", + "deleteWorkingCopyRecordConfirm": "Do you really want to remove the working copy '{{resourceTitle}}'?", "workingCopy": "Working copy", "onTheWeb": "More online information", "pdfReportTocTitle": "Contents", @@ -578,4 +578,4 @@ "reviewerNotAllowedUnpublish": "Reviewer not allowed to un-publish the metadata", "reviewerNotAllowedPublishUnpublish": "Reviewer not allowed to publish / un-publish the metadata", "missingTitle": "Missing title" -} \ No newline at end of file +} diff --git a/web-ui/src/main/resources/catalog/views/default/templates/recordView/recordView.html b/web-ui/src/main/resources/catalog/views/default/templates/recordView/recordView.html index c48b5117100d..0f84a95c23ee 100644 --- a/web-ui/src/main/resources/catalog/views/default/templates/recordView/recordView.html +++ b/web-ui/src/main/resources/catalog/views/default/templates/recordView/recordView.html @@ -99,12 +99,26 @@ + + + + + cancelWorkingCopy - From 267501eb072c99ceb97fafbabef583524f972cc6 Mon Sep 17 00:00:00 2001 From: sebr72 <48369171+sebr72@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:10:19 +0100 Subject: [PATCH 12/16] New property to define an alternate logo for pdf export (#7481) * setting for pdf top banner file name * when formatter type is pdf and metadata/pdfReport/headerLogoFileName is set, header logo should be used. when not generating PDF, header logo should not be used. when pdf logo when not set, do not try to use it / keep default behaviour. externalize PdfOrHtmlResponseWriter and declare spy as primary PdfOrHtmlResponseWriter bean: moving to testable design remove description with large banner * refactor, declare injectMetadataInDbDoNotRefreshHeader in core test facilities and use it to avoid code duplication * opportunistic cleanup reactivate tests linter no need for static here use trimmed extension --------- Co-authored-by: christophe mangeat --- core/pom.xml | 6 + .../geonet/AbstractCoreIntegrationTest.java | 54 +++++++- .../kernel/DataManagerIntegrationTest.java | 23 +--- .../api/records/formatters/FormatterApi.java | 48 +------ .../ImageReplacedElementFactory.java | 4 +- .../formatters/PdfOrHtmlResponseWriter.java | 53 ++++++++ .../fao/geonet/api/links/LinksApiTest.java | 69 +--------- .../geonet/api/records/MetadataApiTest.java | 62 +-------- .../api/records/MetadataWorkflowApiTest.java | 124 ++++-------------- .../AlternateLogoForPdfExportTest.java | 122 +++++++++++++++++ .../FormatterApiIntegrationTest.java | 59 +-------- .../records/formatters/FormatterApiTest.java | 69 ++-------- .../test/resources/formatter-test-context.xml | 6 +- .../resources/catalog/locales/en-admin.json | 2 + .../setup/sql/data/data-db-default.sql | 9 +- .../sql/migrate/v441/migrate-default.sql | 2 + .../main/webapp/xslt/skin/default/skin.xsl | 24 +++- 17 files changed, 326 insertions(+), 410 deletions(-) create mode 100644 services/src/main/java/org/fao/geonet/api/records/formatters/PdfOrHtmlResponseWriter.java create mode 100644 services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java diff --git a/core/pom.xml b/core/pom.xml index d5b30fe994b2..8eaac8090340 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -495,6 +495,12 @@ gn-schema-iso19139 ${project.version} + + org.geonetwork-opensource.schemas + gn-schema-iso19115-3.2018 + ${project.version} + test + ${project.groupId} gn-dummy-api diff --git a/core/src/test/java/org/fao/geonet/AbstractCoreIntegrationTest.java b/core/src/test/java/org/fao/geonet/AbstractCoreIntegrationTest.java index 33460b700428..5bd12fcb26f4 100644 --- a/core/src/test/java/org/fao/geonet/AbstractCoreIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/AbstractCoreIntegrationTest.java @@ -32,7 +32,9 @@ import jeeves.server.dispatchers.ServiceManager; import jeeves.server.sources.ServiceRequest; import org.fao.geonet.constants.Geonet; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.Metadata; import org.fao.geonet.domain.MetadataType; import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.Profile; @@ -41,13 +43,19 @@ import org.fao.geonet.domain.User; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; +import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.kernel.UpdateDatestamp; +import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.mef.Importer; import org.fao.geonet.kernel.mef.MEFLib; +import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.repository.AbstractSpringDataTest; import org.fao.geonet.repository.GroupRepository; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.repository.UserGroupRepository; import org.fao.geonet.repository.UserRepository; +import org.fao.geonet.schema.iso19115_3_2018.ISO19115_3_2018SchemaPlugin; +import org.fao.geonet.schema.iso19139.ISO19139SchemaPlugin; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Element; @@ -79,6 +87,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.UUID; import java.util.concurrent.TimeUnit; import static java.lang.Math.round; @@ -109,6 +118,12 @@ public abstract class AbstractCoreIntegrationTest extends AbstractSpringDataTest protected UserGroupRepository _userGroupRepo; @Autowired protected GroupRepository _groupRepo; + @Autowired + private SchemaManager schemaManager; + @Autowired + private IMetadataManager metadataManager; + @Autowired + private SourceRepository sourceRepository; protected static Element createServiceConfigParam(String name, String value) { return new Element("param") @@ -293,8 +308,7 @@ public MockHttpSession loginAsAnonymous() { return session; } - - private static Element getSample(String resource) throws IOException, JDOMException { + private Element getSample(String resource) throws IOException, JDOMException { final URL resourceUrl = AbstractCoreIntegrationTest.class.getResource(resource); return Xml.loadStream(resourceUrl.openStream()); } @@ -371,4 +385,40 @@ protected void addTestSpecificData(GeonetworkDataDirectory geonetworkDataDirecto public boolean resetLuceneIndex() { return true; } + + protected AbstractMetadata injectMetadataInDbDoNotRefreshHeader(Element sampleMetadataXml, ServiceContext context) throws Exception { + return injectMetadataInDb(sampleMetadataXml, context, false); + } + + protected AbstractMetadata injectMetadataInDb(Element sampleMetadataXml, ServiceContext context, boolean resfreshHeader) throws Exception { + String uuid = UUID.randomUUID().toString(); + String schema = schemaManager.autodetectSchema(sampleMetadataXml); + Xml.selectElement(sampleMetadataXml, + "iso19139".equals(schema) + ? "gmd:fileIdentifier/gco:CharacterString" + : "mdb:metadataIdentifier/*/mcc:code/*", + "iso19139".equals(schema) + ? ISO19139SchemaPlugin.allNamespaces.asList() + : ISO19115_3_2018SchemaPlugin.allNamespaces.asList()) + .setText(uuid); + + String source = sourceRepository.findAll().get(0).getUuid(); + final Metadata metadata = new Metadata(); + metadata + .setDataAndFixCR(sampleMetadataXml) + .setUuid(uuid); + metadata.getDataInfo() + .setRoot(sampleMetadataXml.getQualifiedName()) + .setSchemaId(schema) + .setType(MetadataType.METADATA) + .setPopularity(1000); + metadata.getSourceInfo() + .setOwner(1) + .setSourceId(source); + metadata.getHarvestInfo() + .setHarvested(false); + + return metadataManager.insertMetadata(context, metadata, sampleMetadataXml, IndexingMode.none, false, UpdateDatestamp.NO, + false, resfreshHeader); + } } diff --git a/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java index 29748c46e3a3..f1fcdf99805b 100644 --- a/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java @@ -25,7 +25,6 @@ import com.google.common.base.Optional; import com.google.common.collect.Maps; -import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; import org.fao.geonet.AbstractCoreIntegrationTest; import org.fao.geonet.constants.Geonet; @@ -40,11 +39,9 @@ import org.fao.geonet.domain.Source; import org.fao.geonet.domain.SourceType; import org.fao.geonet.domain.User; -import org.fao.geonet.kernel.datamanager.IMetadataManager; import org.fao.geonet.kernel.search.EsSearchManager; import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.repository.GroupRepository; -import org.fao.geonet.repository.MetadataCategoryRepository; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.utils.Xml; @@ -76,24 +73,12 @@ public class DataManagerIntegrationTest extends AbstractDataManagerIntegrationTe public void testDeleteMetadata() throws Exception { ServiceContext serviceContext = createContextAndLogAsAdmin(); long count = metadataRepository.count(); - String mdId = dataManager.insertMetadata( - serviceContext, - "iso19139", - new Element("MD_Metadata"), - "uuid", - serviceContext.getUserSession().getUserIdAsInt(), - "" + ReservedGroup.all.getId(), - "sourceid", - "n", - "doctype", - null, - new ISODate().getDateAndTime(), - new ISODate().getDateAndTime(), - false, - IndexingMode.none); + + int mdId = injectMetadataInDbDoNotRefreshHeader(getSampleMetadataXml(), serviceContext).getId(); + assertEquals(count + 1, metadataRepository.count()); - metadataManager.deleteMetadata(serviceContext, mdId); + metadataManager.deleteMetadata(serviceContext, String.valueOf(mdId)); assertEquals(count, metadataRepository.count()); } diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java index f7822452e2fe..a548af626bd4 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java @@ -39,7 +39,6 @@ import org.fao.geonet.Constants; import org.fao.geonet.SystemInfo; import org.fao.geonet.api.ApiUtils; -import org.fao.geonet.api.records.extent.MapRenderer; import org.fao.geonet.api.records.formatters.cache.*; import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.constants.Geonet; @@ -53,10 +52,8 @@ import org.fao.geonet.repository.MetadataRepository; import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.specification.OperationAllowedSpecs; -import org.fao.geonet.util.XslUtil; import org.fao.geonet.utils.GeonetHttpRequestFactory; import org.fao.geonet.utils.IO; -import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.jdom.JDOMException; @@ -77,7 +74,6 @@ import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.WebRequest; -import org.xhtmlrenderer.pdf.ITextRenderer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -114,6 +110,9 @@ public class FormatterApi extends AbstractFormatService implements ApplicationLi @Autowired IsoLanguagesMapper isoLanguagesMapper; + @Autowired + PdfOrHtmlResponseWriter writer; + /** * Map (canonical path to formatter dir -> Element containing all xml files in Formatter * bundle's loc directory) @@ -311,7 +310,7 @@ public void getRecordFormattedBy( if (!skipPopularityBool && approved) { context.getBean(DataManager.class).increasePopularity(context, String.valueOf(metadata.getId())); } - writeOutResponse(context, metadataUuid, + writer.writeOutResponse(context, metadataUuid, isoLanguagesMapper.iso639_2T_to_iso639_2B(locale.getISO3Language()), request.getNativeResponse(HttpServletResponse.class), formatType, bytes); } @@ -375,7 +374,7 @@ public void execXml( final String formattedMetadata = result.one().format(result.two()); byte[] bytes = formattedMetadata.getBytes(Constants.CHARSET); - writeOutResponse(context, "", lang, request.getNativeResponse(HttpServletResponse.class), formatType, bytes); + writer.writeOutResponse(context, "", lang, request.getNativeResponse(HttpServletResponse.class), formatType, bytes); } /** @@ -475,23 +474,7 @@ public void exec( context.getBean(DataManager.class).increasePopularity(context, resolvedId); } - writeOutResponse(context, resolvedId, lang, request.getNativeResponse(HttpServletResponse.class), formatType, bytes); - } - } - - private void writeOutResponse(ServiceContext context, String metadataUuid, String lang, HttpServletResponse response, FormatType formatType, byte[] formattedMetadata) throws Exception { - response.setContentType(formatType.contentType); - String filename = "metadata-" + metadataUuid + "." + formatType; - response.addHeader("Content-Disposition", "inline; filename=\"" + filename + "\""); - response.setStatus(HttpServletResponse.SC_OK); - if (formatType == FormatType.pdf) { - writerAsPDF(context, response, formattedMetadata, lang); - } else { - response.setCharacterEncoding(Constants.ENCODING); - response.setContentType(formatType.contentType); - response.setContentLength(formattedMetadata.length); - response.setHeader("Cache-Control", "no-cache"); - response.getOutputStream().write(formattedMetadata); + writer.writeOutResponse(context, resolvedId, lang, request.getNativeResponse(HttpServletResponse.class), formatType, bytes); } } @@ -506,7 +489,6 @@ private boolean hasNonStandardParameters(NativeWebRequest request) { return false; } - private String getXmlFromUrl(ServiceContext context, String lang, String url, WebRequest request) throws IOException, URISyntaxException { String adjustedUrl = url; if (!url.startsWith("http")) { @@ -535,24 +517,6 @@ private String getXmlFromUrl(ServiceContext context, String lang, String url, We return new String(ByteStreams.toByteArray(execute.getBody()), Constants.CHARSET); } - private void writerAsPDF(ServiceContext context, HttpServletResponse response, byte[] bytes, String lang) throws IOException, com.lowagie.text.DocumentException { - final String htmlContent = new String(bytes, Constants.CHARSET); - try { - XslUtil.setNoScript(); - ITextRenderer renderer = new ITextRenderer(); - String siteUrl = context.getBean(SettingManager.class).getSiteURL(lang); - MapRenderer mapRenderer = new MapRenderer(context); - renderer.getSharedContext().setReplacedElementFactory(new ImageReplacedElementFactory(siteUrl.replace("/" + lang + "/", "/eng/"), renderer.getSharedContext() - .getReplacedElementFactory(), mapRenderer)); - renderer.getSharedContext().setDotsPerPixel(13); - renderer.setDocumentFromString(htmlContent, siteUrl); - renderer.layout(); - renderer.createPDF(response.getOutputStream()); - } catch (final Exception e) { - Log.error(Geonet.FORMATTER, "Error converting formatter output to a file: " + htmlContent, e); - throw e; - } - } @VisibleForTesting Pair loadMetadataAndCreateFormatterAndParams(ServiceContext context, Key key, final NativeWebRequest request) throws Exception { diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/ImageReplacedElementFactory.java b/services/src/main/java/org/fao/geonet/api/records/formatters/ImageReplacedElementFactory.java index 099dff582aea..b078c626117a 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/ImageReplacedElementFactory.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/ImageReplacedElementFactory.java @@ -216,8 +216,8 @@ private Map getParams(String src) throws MalformedURLException { } private boolean isSupportedImageFormat(String imgUrl) { - String ext = Files.getFileExtension(imgUrl.replaceAll("\\?.*", "")); - return ext.trim().isEmpty() || getSupportedExts().contains(ext); + String trimmedExt = Files.getFileExtension(imgUrl.replaceAll("\\?.*", "")).trim(); + return trimmedExt.isEmpty() || getSupportedExts().contains(trimmedExt); } private ReplacedElement loadImage(LayoutContext layoutContext, BlockBox box, UserAgentCallback userAgentCallback, diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/PdfOrHtmlResponseWriter.java b/services/src/main/java/org/fao/geonet/api/records/formatters/PdfOrHtmlResponseWriter.java new file mode 100644 index 000000000000..8abb2d87ab5d --- /dev/null +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/PdfOrHtmlResponseWriter.java @@ -0,0 +1,53 @@ +package org.fao.geonet.api.records.formatters; + +import jeeves.server.context.ServiceContext; +import org.fao.geonet.Constants; +import org.fao.geonet.api.records.extent.MapRenderer; +import org.fao.geonet.constants.Geonet; +import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.util.XslUtil; +import org.fao.geonet.utils.Log; +import org.springframework.stereotype.Component; +import org.xhtmlrenderer.pdf.ITextRenderer; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +@Component +class PdfOrHtmlResponseWriter { + + void writeOutResponse(ServiceContext context, String metadataUuid, String lang, HttpServletResponse response, FormatType formatType, byte[] formattedMetadata) throws Exception { + response.setContentType(formatType.contentType); + String filename = "metadata-" + metadataUuid + "." + formatType; + response.addHeader("Content-Disposition", "inline; filename=\"" + filename + "\""); + response.setStatus(HttpServletResponse.SC_OK); + if (formatType == FormatType.pdf) { + writerAsPDF(context, response, formattedMetadata, lang); + } else { + response.setCharacterEncoding(Constants.ENCODING); + response.setContentType(formatType.contentType); + response.setContentLength(formattedMetadata.length); + response.setHeader("Cache-Control", "no-cache"); + response.getOutputStream().write(formattedMetadata); + } + } + + private void writerAsPDF(ServiceContext context, HttpServletResponse response, byte[] bytes, String lang) throws IOException, com.lowagie.text.DocumentException { + final String htmlContent = new String(bytes, Constants.CHARSET); + try { + XslUtil.setNoScript(); + ITextRenderer renderer = new ITextRenderer(); + String siteUrl = context.getBean(SettingManager.class).getSiteURL(lang); + MapRenderer mapRenderer = new MapRenderer(context); + renderer.getSharedContext().setReplacedElementFactory(new ImageReplacedElementFactory(siteUrl.replace("/" + lang + "/", "/eng/"), renderer.getSharedContext() + .getReplacedElementFactory(), mapRenderer)); + renderer.getSharedContext().setDotsPerPixel(13); + renderer.setDocumentFromString(htmlContent, siteUrl); + renderer.layout(); + renderer.createPDF(response.getOutputStream()); + } catch (final Exception e) { + Log.error(Geonet.FORMATTER, "Error converting formatter output to a file: " + htmlContent, e); + throw e; + } + } +} diff --git a/services/src/test/java/org/fao/geonet/api/links/LinksApiTest.java b/services/src/test/java/org/fao/geonet/api/links/LinksApiTest.java index ff94e89f6c03..9d94ddf2bfec 100644 --- a/services/src/test/java/org/fao/geonet/api/links/LinksApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/links/LinksApiTest.java @@ -22,22 +22,10 @@ */ package org.fao.geonet.api.links; -import com.google.common.collect.Lists; import jeeves.server.context.ServiceContext; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.domain.Metadata; -import org.fao.geonet.domain.MetadataType; -import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SchemaManager; -import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.datamanager.IMetadataUtils; -import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.repository.LinkRepository; -import org.fao.geonet.repository.MetadataLinkRepository; -import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.services.AbstractServiceIntegrationTest; -import org.fao.geonet.utils.Xml; -import org.jdom.Element; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -48,13 +36,6 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import javax.persistence.EntityManager; -import javax.persistence.PersistenceContext; -import java.util.Arrays; -import java.util.UUID; - -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GCO; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GMD; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; @@ -68,35 +49,13 @@ public class LinksApiTest extends AbstractServiceIntegrationTest { @Autowired private WebApplicationContext wac; - @Autowired private LinkRepository linkRepository; - @Autowired - private MetadataLinkRepository metadataLinkRepository; - - @Autowired - private SchemaManager schemaManager; - - @Autowired - private DataManager dataManager; - - @Autowired - private SourceRepository sourceRepository; - - @PersistenceContext - private EntityManager _entityManager; - - @Autowired - private IMetadataUtils metadataRepository; - - private String uuid; - private int id; private AbstractMetadata md; private MockMvc mockMvc; private ServiceContext context; - @Before public void setUp() throws Exception { this.context = createServiceContext(); @@ -105,11 +64,10 @@ public void setUp() throws Exception { @Test public void getLinks() throws Exception { - Long operationsCount = linkRepository.count(); final MockHttpSession httpSession = this.loginAsAdmin(); this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); - this.mockMvc.perform(post("/srv/api/records/links/analyze?uuid=" + this.uuid) + this.mockMvc.perform(post("/srv/api/records/links/analyze?uuid=" + md.getUuid()) .session(httpSession) .accept(MediaType.parseMediaType("application/json"))) .andExpect(status().isCreated()); @@ -124,7 +82,7 @@ public void getLinks() throws Exception { .andExpect(jsonPath("$.content", hasSize(1))) .andExpect(jsonPath("$.content[0].url").value(equalTo("http://services.sandre.eaufrance.fr/geo/ouvrage"))) .andExpect(jsonPath("$.content[0].records", hasSize(1))) - .andExpect(jsonPath("$.content[0].records[0].metadataId").value(equalTo(this.id))) + .andExpect(jsonPath("$.content[0].records[0].metadataId").value(equalTo(md.getId()))) .andExpect(jsonPath("$.content[0].records[0].metadataUuid").value(equalTo(md.getUuid())));; this.mockMvc.perform(delete("/srv/api/records/links") @@ -137,27 +95,6 @@ public void getLinks() throws Exception { private void createTestData() throws Exception { loginAsAdmin(context); - - final Element sampleMetadataXml = getSampleMetadataXml(); - this.uuid = UUID.randomUUID().toString(); - Xml.selectElement(sampleMetadataXml, "gmd:fileIdentifier/gco:CharacterString", Arrays.asList(GMD, GCO)).setText(this.uuid); - - String source = sourceRepository.findAll().get(0).getUuid(); - String schema = schemaManager.autodetectSchema(sampleMetadataXml); - final Metadata metadata = new Metadata(); - metadata.setDataAndFixCR(sampleMetadataXml).setUuid(uuid); - metadata.getDataInfo().setRoot(sampleMetadataXml.getQualifiedName()).setSchemaId(schema).setType(MetadataType.METADATA); - metadata.getDataInfo().setPopularity(1000); - metadata.getSourceInfo().setOwner(1).setSourceId(source); - metadata.getHarvestInfo().setHarvested(false); - - - this.id = dataManager.insertMetadata(context, metadata, sampleMetadataXml, IndexingMode.none, false, UpdateDatestamp.NO, - false, false).getId(); - - - dataManager.indexMetadata(Lists.newArrayList("" + this.id)); - this.md = metadataRepository.findOne(this.id); + this.md = injectMetadataInDb(getSampleMetadataXml(), context, true); } - } diff --git a/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java b/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java index 2059512fd860..0eb0bcc1607a 100644 --- a/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/MetadataApiTest.java @@ -26,17 +26,10 @@ import jeeves.server.context.ServiceContext; import org.fao.geonet.NodeInfo; import org.fao.geonet.api.ApiParams; -import org.fao.geonet.domain.Metadata; -import org.fao.geonet.domain.MetadataType; -import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SchemaManager; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.kernel.SpringLocalServiceInvoker; -import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.repository.MetadataRepository; -import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.services.AbstractServiceIntegrationTest; -import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.junit.Assert; import org.junit.Before; @@ -55,8 +48,6 @@ import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V1_ACCEPT_TYPE; import static org.fao.geonet.kernel.mef.MEFLib.Version.Constants.MEF_V2_ACCEPT_TYPE; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GCO; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GMD; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -69,26 +60,18 @@ * @author juanluisrp **/ public class MetadataApiTest extends AbstractServiceIntegrationTest { + @Autowired private WebApplicationContext wac; - @Autowired - private SchemaManager schemaManager; - @Autowired - private DataManager dataManager; - @Autowired - private SourceRepository sourceRepository; - @PersistenceContext private EntityManager _entityManager; - @Autowired private MetadataRepository metadataRepository; - private String uuid; private int id; + private String uuid; private ServiceContext context; - @Before public void setUp() throws Exception { this.context = createServiceContext(); @@ -97,38 +80,11 @@ public void setUp() throws Exception { private void createTestData() throws Exception { loginAsAdmin(context); - - final Element sampleMetadataXml = getSampleMetadataXml(); - this.uuid = UUID.randomUUID().toString(); - Xml.selectElement(sampleMetadataXml, "gmd:fileIdentifier/gco:CharacterString", Arrays.asList(GMD, GCO)).setText(this.uuid); - - String source = sourceRepository.findAll().get(0).getUuid(); - String schema = schemaManager.autodetectSchema(sampleMetadataXml); - final Metadata metadata = new Metadata(); - metadata - .setDataAndFixCR(sampleMetadataXml) - .setUuid(uuid); - metadata.getDataInfo() - .setRoot(sampleMetadataXml.getQualifiedName()) - .setSchemaId(schema) - .setType(MetadataType.METADATA) - .setPopularity(1000); - metadata.getSourceInfo() - .setOwner(1) - .setSourceId(source); - metadata.getHarvestInfo() - .setHarvested(false); - - - this.id = dataManager.insertMetadata(context, metadata, sampleMetadataXml, IndexingMode.none, false, UpdateDatestamp.NO, - false, false).getId(); - - - dataManager.indexMetadata(Lists.newArrayList("" + this.id)); - this.id = metadataRepository.findById(this.id).get().getId(); + AbstractMetadata metadata = injectMetadataInDb(getSampleMetadataXml(), context, true); + id = metadata.getId(); + uuid = metadata.getUuid(); } - @Test public void getNonExistentRecordRecord() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -180,7 +136,6 @@ public void getNonAllowedRecord() throws Exception { MEF_V2_ACCEPT_TYPE ); - mockMvc.perform(get("/srv/api/records/" + this.uuid) .session(mockHttpSession) .accept(MediaType.APPLICATION_JSON)) @@ -239,8 +194,6 @@ public void getRecord() throws Exception { } } - - @Test public void getRecordThruSpringLocalServiceInvoker() throws Exception { MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -400,7 +353,6 @@ public void getNonExistentRecordAsXml() throws Exception { @Test public void getRecordAsZip() throws Exception { - final String zipMagicNumber = "PK\u0003\u0004"; MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -433,7 +385,6 @@ public void getRecordAsZip() throws Exception { .andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION, equalTo(String.format("inline; filename=\"%s.%s\"", this.uuid, "zip")))) .andExpect(content().string(startsWith(zipMagicNumber))); - } @Test @@ -484,5 +435,4 @@ public void getNonExistentRecordAsZip() throws Exception { .andExpect(header().doesNotExist(HttpHeaders.CONTENT_TYPE)) .andExpect(content().string(isEmptyOrNullString())); } - } diff --git a/services/src/test/java/org/fao/geonet/api/records/MetadataWorkflowApiTest.java b/services/src/test/java/org/fao/geonet/api/records/MetadataWorkflowApiTest.java index 62b2d8298752..a56815862c2e 100644 --- a/services/src/test/java/org/fao/geonet/api/records/MetadataWorkflowApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/MetadataWorkflowApiTest.java @@ -6,19 +6,12 @@ import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.records.model.MetadataBatchSubmitParameter; import org.fao.geonet.domain.*; -import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.SelectionManager; -import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.kernel.setting.Settings; import org.fao.geonet.repository.MetadataStatusRepository; -import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.repository.StatusValueRepository; import org.fao.geonet.services.AbstractServiceIntegrationTest; -import org.fao.geonet.utils.Xml; -import org.jdom.Element; import org.junit.Before; import org.junit.Test; import org.locationtech.jts.util.Assert; @@ -30,12 +23,10 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; -import java.util.Arrays; import java.util.Optional; -import java.util.UUID; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GCO; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GMD; +import static org.fao.geonet.domain.StatusValue.Status.APPROVED; +import static org.fao.geonet.domain.StatusValue.Status.DRAFT; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -48,18 +39,8 @@ public class MetadataWorkflowApiTest extends AbstractServiceIntegrationTest { MetadataStatusRepository metadataStatusRepo; @Autowired StatusValueRepository statusValueRepo; - - - @Autowired - private DataManager dataManager; - @Autowired - private SourceRepository sourceRepository; - @Autowired - private SchemaManager schemaManager; - @Autowired private SettingManager settingManager; - @Autowired private StatusValueRepository statusValueRepository; @@ -70,87 +51,17 @@ public class MetadataWorkflowApiTest extends AbstractServiceIntegrationTest { @Before public void setUp() throws Exception { - this.context = createServiceContext(); - createTestData(); - } - - private void createTestData() throws Exception { - this.uuid = UUID.randomUUID().toString(); - this.uuid2 = UUID.randomUUID().toString(); - + context = createServiceContext(); loginAsAdmin(context); settingManager.setValue(Settings.METADATA_WORKFLOW_ENABLE, true); settingManager.setValue(Settings.METADATA_WORKFLOW_DRAFT_WHEN_IN_GROUP, ".*"); - final Element sampleMetadataXml = getSampleMetadataXml(); - this.uuid = UUID.randomUUID().toString(); - Xml.selectElement(sampleMetadataXml, "gmd:fileIdentifier/gco:CharacterString", Arrays.asList(GMD, GCO)).setText(this.uuid); - - String source = sourceRepository.findAll().get(0).getUuid(); - String schema = schemaManager.autodetectSchema(sampleMetadataXml); - final Metadata metadata = (Metadata) new Metadata() - .setDataAndFixCR(sampleMetadataXml) - .setUuid(uuid); - metadata.getDataInfo() - .setRoot(sampleMetadataXml.getQualifiedName()) - .setSchemaId(schema) - .setType(MetadataType.METADATA) - .setPopularity(1000); - metadata.getSourceInfo() - .setOwner(1) - .setSourceId(source); - metadata.getHarvestInfo() - .setHarvested(false); - - int id = dataManager.insertMetadata(context, metadata, sampleMetadataXml, IndexingMode.full, false, UpdateDatestamp.NO, - false, false).getId(); - - MetadataStatus metadataStatus = new MetadataStatus(); - metadataStatus.setMetadataId(id); - metadataStatus.setChangeDate(new ISODate()); - metadataStatus.setUserId(1); - metadataStatus.setChangeMessage("change message test"); - metadataStatus.setOwner(1); - metadataStatus.setUuid(uuid); - - Optional statusValue = statusValueRepository.findById(Integer.parseInt(StatusValue.Status.DRAFT)); - metadataStatus.setStatusValue(statusValue.get()); - metadataStatusRepo.save(metadataStatus); - - final Element sampleMetadataXml2 = getSampleMetadataXml(); - this.uuid2 = UUID.randomUUID().toString(); - Xml.selectElement(sampleMetadataXml2, "gmd:fileIdentifier/gco:CharacterString", Arrays.asList(GMD, GCO)).setText(this.uuid2); - - final Metadata metadata2 = (Metadata) new Metadata() - .setDataAndFixCR(sampleMetadataXml2) - .setUuid(uuid2); - metadata2.getDataInfo() - .setRoot(sampleMetadataXml.getQualifiedName()) - .setSchemaId(schema) - .setType(MetadataType.METADATA) - .setPopularity(1000); - metadata2.getSourceInfo() - .setOwner(1) - .setSourceId(source); - metadata2.getHarvestInfo() - .setHarvested(false); - - id = dataManager.insertMetadata(context, metadata2, sampleMetadataXml, IndexingMode.full, false, UpdateDatestamp.NO, - false, false).getId(); - - metadataStatus = new MetadataStatus(); - metadataStatus.setMetadataId(id); - metadataStatus.setChangeDate(new ISODate()); - metadataStatus.setUserId(1); - metadataStatus.setChangeMessage("change message test"); - metadataStatus.setOwner(1); - metadataStatus.setUuid(uuid); - - - statusValue = statusValueRepository.findById(Integer.parseInt(StatusValue.Status.APPROVED)); - metadataStatus.setStatusValue(statusValue.get()); - metadataStatusRepo.save(metadataStatus); - + AbstractMetadata metadata = injectMetadataInDbDoNotRefreshHeader(getSampleMetadataXml(), context); + injectStatusForMetadata(metadata, DRAFT); + uuid = metadata.getUuid(); + AbstractMetadata metadata2 = injectMetadataInDbDoNotRefreshHeader(getSampleMetadataXml(), context); + injectStatusForMetadata(metadata2, APPROVED); + uuid2 = metadata2.getUuid(); } @Test @@ -165,7 +76,6 @@ public void testGetStatusByType() throws Exception { .andExpect(status().is2xxSuccessful()) .andExpect(content().contentType(API_JSON_EXPECTED_ENCODING)) .andExpect(jsonPath("$.[0].uuid").value(uuid)); - } @Test @@ -186,7 +96,6 @@ public void testBatchSubmit() throws Exception { Gson gson = new Gson(); String json = gson.toJson(submitParameter); - mockMvc.perform(put("/srv/api/records/submit") .content(json) .contentType(API_JSON_EXPECTED_ENCODING) @@ -196,7 +105,20 @@ public void testBatchSubmit() throws Exception { .andExpect(status().is2xxSuccessful()) .andExpect(content().contentType(API_JSON_EXPECTED_ENCODING)) .andExpect(jsonPath("$.numberOfRecordsProcessed").value(1)); - } + private void injectStatusForMetadata(AbstractMetadata metadata, String status) { + MetadataStatus metadataStatus; + Optional statusValue; + metadataStatus = new MetadataStatus(); + metadataStatus.setMetadataId(metadata.getId()); + metadataStatus.setChangeDate(new ISODate()); + metadataStatus.setUserId(1); + metadataStatus.setChangeMessage("change message test"); + metadataStatus.setOwner(1); + metadataStatus.setUuid(metadata.getUuid()); + statusValue = statusValueRepository.findById(Integer.parseInt(status)); + metadataStatus.setStatusValue(statusValue.get()); + metadataStatusRepo.save(metadataStatus); + } } diff --git a/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java b/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java new file mode 100644 index 000000000000..be863787a89d --- /dev/null +++ b/services/src/test/java/org/fao/geonet/api/records/formatters/AlternateLogoForPdfExportTest.java @@ -0,0 +1,122 @@ +package org.fao.geonet.api.records.formatters; + +import jeeves.server.context.ServiceContext; +import org.fao.geonet.domain.AbstractMetadata; +import org.fao.geonet.domain.Setting; +import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.repository.SettingRepository; +import org.fao.geonet.services.AbstractServiceIntegrationTest; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockHttpSession; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.http.HttpServletResponse; +import java.nio.charset.StandardCharsets; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ContextConfiguration(locations = "classpath:formatter-test-context.xml") +public class AlternateLogoForPdfExportTest extends AbstractServiceIntegrationTest { + + @Autowired + private WebApplicationContext wac; + @Autowired + private SettingManager settingManager; + @Autowired + SettingRepository settingRepository; + @Autowired + private PdfOrHtmlResponseWriter responseWriterSpy; + + private ServiceContext context; + private AbstractMetadata metadata; + + @Before + public void initSiteId() { + settingManager.setValue("system/site/siteId", UUID.randomUUID().toString()); + } + + @Before + public void createTestData() throws Exception { + context = createServiceContext(); + loginAsAdmin(context); + metadata = injectMetadataInDbDoNotRefreshHeader(getSampleISO19139MetadataXml(), context); + } + + @Before + public void initWriterSpy() { + Mockito.reset(responseWriterSpy); + } + + @Test + public void whenGeneratingPdfWithPropertySetPdfLogoIsUsed() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + MockHttpSession mockHttpSession = loginAsAdmin(); + settingManager.setValue("metadata/pdfReport/headerLogoFileName", "pdf_test_banner_to_use.png"); + + String url = "/srv/api/records/" + metadata.getUuid() + "/formatters/xsl-view?output=pdf&language=fre"; + mockMvc.perform(get(url) + .session(mockHttpSession) + .accept(MediaType.ALL_VALUE)) + .andExpect(status().isOk()) + .andReturn(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); + Mockito.verify(responseWriterSpy).writeOutResponse(any(ServiceContext.class), any(String.class), any(String.class), any(HttpServletResponse.class), any(FormatType.class), captor.capture()); + assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("images/harvesting/pdf_test_banner_to_use.png")); + } + + @Test + public void whenNotGeneratingPdfWithPropertySetSiteLogoIsUsed() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + MockHttpSession mockHttpSession = loginAsAdmin(); + settingManager.setValue("metadata/pdfReport/headerLogoFileName", "pdf_test_banner_to_use.png"); + String siteId = settingManager.getValue("system/site/siteId"); + + String url = "/srv/api/records/" + metadata.getUuid() + "/formatters/xsl-view?language=fre"; + mockMvc.perform(get(url) + .session(mockHttpSession) + .accept(MediaType.ALL_VALUE)) + .andExpect(status().isOk()) + .andReturn(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); + Mockito.verify(responseWriterSpy).writeOutResponse(any(ServiceContext.class), any(String.class), any(String.class), any(HttpServletResponse.class), any(FormatType.class), captor.capture()); + assertFalse(new String(captor.getValue(), StandardCharsets.UTF_8).contains("pdf_test_banner_to_use.png")); + assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("images/logos/" + siteId + ".png")); + } + + @Test + public void whenGeneratingPdfWithPropertyNotSetSiteLogoIsUsed() throws Exception { + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + MockHttpSession mockHttpSession = loginAsAdmin(); + Optional se = settingRepository.findById("metadata/pdfReport/headerLogoFileName"); + se.ifPresent(settingRepository::delete); + String siteId = settingManager.getValue("system/site/siteId"); + + String url = "/srv/api/records/" + metadata.getUuid() + "/formatters/xsl-view?output=pdf&language=fre"; + mockMvc.perform(get(url) + .session(mockHttpSession) + .accept(MediaType.ALL_VALUE)) + .andExpect(status().isOk()) + .andReturn(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(byte[].class); + Mockito.verify(responseWriterSpy).writeOutResponse(any(ServiceContext.class), any(String.class), any(String.class), any(HttpServletResponse.class), any(FormatType.class), captor.capture()); + assertFalse(new String(captor.getValue(), StandardCharsets.UTF_8).contains("pdf_test_banner_to_use.png")); + assertTrue(new String(captor.getValue(), StandardCharsets.UTF_8).contains("images/logos/" + siteId + ".png")); + } +} diff --git a/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiIntegrationTest.java b/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiIntegrationTest.java index 001ec52c737e..b019a489d365 100644 --- a/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiIntegrationTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiIntegrationTest.java @@ -23,31 +23,17 @@ package org.fao.geonet.api.records.formatters; -import com.google.common.collect.Lists; import jeeves.config.springutil.JeevesDelegatingFilterProxy; import jeeves.server.context.ServiceContext; -import org.fao.geonet.AbstractCoreIntegrationTest; import org.fao.geonet.MockRequestFactoryGeonet; import org.fao.geonet.SystemInfo; -import org.fao.geonet.domain.Metadata; -import org.fao.geonet.domain.MetadataType; -import org.fao.geonet.kernel.DataManager; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.kernel.GeonetworkDataDirectory; -import org.fao.geonet.kernel.SchemaManager; -import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.search.IndexingMode; -import org.fao.geonet.languages.IsoLanguagesMapper; -import org.fao.geonet.repository.MetadataRepository; -import org.fao.geonet.repository.SourceRepository; -import org.fao.geonet.schema.iso19139.ISO19139Namespaces; import org.fao.geonet.services.AbstractServiceIntegrationTest; import org.fao.geonet.utils.IO; -import org.fao.geonet.utils.MockXmlRequest; import org.fao.geonet.utils.Xml; import org.jdom.Element; -import org.jdom.Namespace; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; @@ -62,12 +48,8 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; -import java.util.UUID; import static org.fao.geonet.api.records.formatters.FormatterWidth._100; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GCO; -import static org.fao.geonet.schema.iso19139.ISO19139Namespaces.GMD; import static org.junit.Assert.*; @ContextConfiguration(inheritLocations = true, locations = "classpath:formatter-test-context.xml") @@ -82,52 +64,23 @@ public class FormatterApiIntegrationTest extends AbstractServiceIntegrationTest @Autowired private GeonetworkDataDirectory dataDirectory; @Autowired - private SchemaManager schemaManager; - @Autowired private FormatterApi formatService; @Autowired private FormatterAdminApi listService; - @Autowired - private IsoLanguagesMapper mapper; - @Autowired - private SourceRepository sourceRepository; - @Autowired - private DataManager dataManager; - @Autowired - private MetadataRepository metadataRepository; private ServiceContext serviceContext; private int id; private String schema; - private String uuid; @Before public void setUp() throws Exception { - this.serviceContext = createServiceContext(); + serviceContext = createServiceContext(); loginAsAdmin(serviceContext); - - final Element sampleMetadataXml = getSampleMetadataXml(); - this.uuid = UUID.randomUUID().toString(); - Xml.selectElement(sampleMetadataXml, "gmd:fileIdentifier/gco:CharacterString", Arrays.asList(GMD, GCO)).setText(this.uuid); - - String source = sourceRepository.findAll().get(0).getUuid(); - this.schema = schemaManager.autodetectSchema(sampleMetadataXml); - final Metadata metadata = new Metadata(); - metadata.setDataAndFixCR(sampleMetadataXml).setUuid(uuid); - metadata.getDataInfo().setRoot(sampleMetadataXml.getQualifiedName()).setSchemaId(this.schema).setType(MetadataType.METADATA); - metadata.getSourceInfo().setOwner(1).setSourceId(source); - metadata.getHarvestInfo().setHarvested(false); - - this.id = dataManager.insertMetadata(serviceContext, metadata, sampleMetadataXml, IndexingMode.none, false, UpdateDatestamp.NO, - false, false).getId(); - - dataManager.indexMetadata(Lists.newArrayList("" + this.id)); - + AbstractMetadata metadata = injectMetadataInDbDoNotRefreshHeader(getSampleMetadataXml(), serviceContext); + id = metadata.getId(); + schema = metadata.getDataInfo().getSchemaId(); } - - // TODOES @Test - @Ignore public void testExec() throws Exception { final FormatterAdminApi.FormatterDataResponse formatters = listService.listFormatters(null, null, schema, false, false); @@ -163,7 +116,6 @@ public void testExec() throws Exception { } } - @Ignore @Test public void testExecXslt() throws Exception { final ServletContext context = _applicationContext.getBean(ServletContext.class); @@ -192,5 +144,4 @@ public void testExecXslt() throws Exception { assertEqualsText("fromFunction", view, "*//p"); assertEqualsText("Title", view, "*//div[@class='tr']"); } - } diff --git a/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiTest.java b/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiTest.java index 1d9f107bd7a7..e0780b4f7231 100644 --- a/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/formatters/FormatterApiTest.java @@ -23,17 +23,8 @@ package org.fao.geonet.api.records.formatters; import jeeves.server.context.ServiceContext; -import org.fao.geonet.domain.Metadata; -import org.fao.geonet.domain.MetadataType; -import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SchemaManager; -import org.fao.geonet.kernel.UpdateDatestamp; -import org.fao.geonet.kernel.search.IndexingMode; -import org.fao.geonet.repository.SourceRepository; -import org.fao.geonet.schema.iso19115_3_2018.ISO19115_3_2018SchemaPlugin; -import org.fao.geonet.schema.iso19139.ISO19139SchemaPlugin; +import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.services.AbstractServiceIntegrationTest; -import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.junit.Before; import org.junit.Test; @@ -58,15 +49,8 @@ public class FormatterApiTest extends AbstractServiceIntegrationTest { @Autowired private WebApplicationContext wac; - @Autowired - private SchemaManager schemaManager; - @Autowired - private DataManager dataManager; - @Autowired - private SourceRepository sourceRepository; private Map testDataUuidBySchema = new HashMap<>(); - private ServiceContext context; public static Collection data() throws Exception { @@ -91,46 +75,6 @@ public void setUp() throws Exception { createTestData(); } - private void createTestData() throws Exception { - loginAsAdmin(context); - loadFile(getSampleISO19139MetadataXml()); - loadFile(getSampleISO19115MetadataXml()); - } - - private void loadFile(Element sampleMetadataXml) throws Exception { - String uuid = UUID.randomUUID().toString(); - String schema = schemaManager.autodetectSchema(sampleMetadataXml); - Xml.selectElement(sampleMetadataXml, - "iso19139".equals(schema) - ? "gmd:fileIdentifier/gco:CharacterString" - : "mdb:metadataIdentifier/*/mcc:code/*", - "iso19139".equals(schema) - ? ISO19139SchemaPlugin.allNamespaces.asList() - : ISO19115_3_2018SchemaPlugin.allNamespaces.asList()) - .setText(uuid); - - String source = sourceRepository.findAll().get(0).getUuid(); - final Metadata metadata = new Metadata(); - metadata - .setDataAndFixCR(sampleMetadataXml) - .setUuid(uuid); - metadata.getDataInfo() - .setRoot(sampleMetadataXml.getQualifiedName()) - .setSchemaId(schema) - .setType(MetadataType.METADATA) - .setPopularity(1000); - metadata.getSourceInfo() - .setOwner(1) - .setSourceId(source); - metadata.getHarvestInfo() - .setHarvested(false); - - dataManager.insertMetadata(context, metadata, sampleMetadataXml, IndexingMode.none, false, UpdateDatestamp.NO, - false, false); - - testDataUuidBySchema.put(schema, uuid); - } - @Test public void checkFormatter() throws Exception { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); @@ -169,4 +113,15 @@ public void checkFormatter() throws Exception { } } } + + private void createTestData() throws Exception { + loginAsAdmin(context); + loadFile(getSampleISO19139MetadataXml()); + loadFile(getSampleISO19115MetadataXml()); + } + + private void loadFile(Element sampleMetadataXml) throws Exception { + AbstractMetadata metadata = injectMetadataInDbDoNotRefreshHeader(sampleMetadataXml, context); + testDataUuidBySchema.put(metadata.getDataInfo().getSchemaId(), metadata.getUuid()); + } } diff --git a/services/src/test/resources/formatter-test-context.xml b/services/src/test/resources/formatter-test-context.xml index b0e4b0c776d5..b9ffb173023b 100644 --- a/services/src/test/resources/formatter-test-context.xml +++ b/services/src/test/resources/formatter-test-context.xml @@ -23,7 +23,7 @@ --> + + + + FormatIntegrationTest.com diff --git a/web-ui/src/main/resources/catalog/locales/en-admin.json b/web-ui/src/main/resources/catalog/locales/en-admin.json index 16a7da1f5868..0126538fc586 100644 --- a/web-ui/src/main/resources/catalog/locales/en-admin.json +++ b/web-ui/src/main/resources/catalog/locales/en-admin.json @@ -1395,6 +1395,8 @@ "metadata/pdfReport/tocPage": "Add table of contents (TOC) page", "metadata/pdfReport/pdfName": "Report file name", "metadata/pdfReport/pdfName-help": "Report file name. The following template fields are allowed to be used: {year}, {month}, {day}, {date} and {datetime}. Template fields are replaced with the related date values. {date} uses 'yyyyMMdd' format and {datetime} uses 'yyyyMMddHHmmss' format.", + "metadata/pdfReport/headerLogoFileName": "Top banner file name", + "metadata/pdfReport/headerLogoFileName-help": "When a file is given, the pdf will be generated using this image as its top banner instead of its default one. Please add this image using the add new logo feature.", "metadata/csvReport": "Metadata selection - csv export", "metadata/csvReport/csvName": "CSV export file name", "metadata/csvReport/csvName-help": "Export metadata in CSV format file name. The following template fields are allowed to be used: {year}, {month}, {day}, {date} and {datetime}. Template fields are replaced with the related date values. {date} uses 'yyyyMMdd' format and {datetime} uses 'yyyyMMddHHmmss' format.", diff --git a/web/src/main/webapp/WEB-INF/classes/setup/sql/data/data-db-default.sql b/web/src/main/webapp/WEB-INF/classes/setup/sql/data/data-db-default.sql index ee8d2b5bb3b0..af573b706fc7 100644 --- a/web/src/main/webapp/WEB-INF/classes/setup/sql/data/data-db-default.sql +++ b/web/src/main/webapp/WEB-INF/classes/setup/sql/data/data-db-default.sql @@ -704,11 +704,12 @@ INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metada INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/coverPdf', '', 0, 12500, 'y'); INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/introPdf', '', 0, 12501, 'y'); INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/tocPage', 'false', 2, 12502, 'y'); -INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/headerLeft', '{siteInfo}', 0, 12504, 'y'); -INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/headerRight', '', 0, 12505, 'y'); -INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/footerLeft', '', 0, 12506, 'y'); -INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/footerRight', '{date}', 0, 12507, 'y'); +INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/headerLeft', '{siteInfo}', 0, 12503, 'y'); +INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/headerRight', '', 0, 12504, 'y'); +INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/footerLeft', '', 0, 12505, 'y'); +INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/footerRight', '{date}', 0, 12506, 'y'); INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/pdfName', 'metadata_{datetime}.pdf', 0, 12507, 'n'); +INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/pdfReport/headerLogoFileName', '', 0, 12508, 'y'); INSERT INTO Settings (name, value, datatype, position, internal) VALUES ('metadata/csvReport/csvName', 'metadata_{datetime}.csv', 0, 12607, 'n'); diff --git a/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v441/migrate-default.sql b/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v441/migrate-default.sql index 451ce3462139..88da11056c4f 100644 --- a/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v441/migrate-default.sql +++ b/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v441/migrate-default.sql @@ -1,2 +1,4 @@ UPDATE Settings SET value='4.4.1' WHERE name='system/platform/version'; UPDATE Settings SET value='0' WHERE name='system/platform/subVersion'; + +INSERT INTO Settings (name, value, datatype, position, internal) SELECT distinct 'metadata/pdfReport/headerLogoFileName', '', 0, 12508, 'y' from settings WHERE NOT EXISTS (SELECT name FROM Settings WHERE name = 'metadata/pdfReport/headerLogoFileName'); \ No newline at end of file diff --git a/web/src/main/webapp/xslt/skin/default/skin.xsl b/web/src/main/webapp/xslt/skin/default/skin.xsl index 880fc066f717..1d17213c7786 100644 --- a/web/src/main/webapp/xslt/skin/default/skin.xsl +++ b/web/src/main/webapp/xslt/skin/default/skin.xsl @@ -14,6 +14,9 @@ version="2.0" exclude-result-prefixes="#all"> + + + @@ -73,12 +76,21 @@
  • - - - - + + + + + + + + + + +
  • From e62ff1c0df8a1bf6e189b85ea9c22fdd575d85bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jose=20Garc=C3=ADa?= Date: Tue, 5 Dec 2023 13:32:49 +0100 Subject: [PATCH 13/16] Reset user password dialog - don't display the field to request the old password for administrators - unify UI check with backend check (#7510) --- .../main/resources/catalog/templates/admin/usergroup/users.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html index c45f10d165e9..3f59389df63b 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html +++ b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html @@ -545,7 +545,7 @@

    UserAdmin

    From 0a05bac9488c13a5fd380c7ea3747186634c1f99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Prunayre?= Date: Tue, 5 Dec 2023 13:34:56 +0100 Subject: [PATCH 14/16] Administration form validation improvements (#7533) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Administration form validation improvements * Administration form validation improvements / missing translations. * Administration form validation improvements / move confirmation message to translations --------- Co-authored-by: Jose García --- .../components/utility/UtilityDirective.js | 36 ++++++ .../catalog/js/admin/UserGroupController.js | 3 +- .../resources/catalog/locales/en-admin.json | 7 +- .../templates/admin/usergroup/users.html | 117 +++++++++++++++--- 4 files changed, 147 insertions(+), 16 deletions(-) diff --git a/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js b/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js index af1c19cfab1d..425d33df4b6c 100644 --- a/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js +++ b/web-ui/src/main/resources/catalog/components/utility/UtilityDirective.js @@ -2725,4 +2725,40 @@ } }; }); + + module.directive("equalWith", function () { + return { + require: "ngModel", + scope: { equalWith: "&" }, + link: function (scope, elem, attrs, ngModelCtrl) { + ngModelCtrl.$validators.equalWith = function (modelValue) { + return modelValue === scope.equalWith(); + }; + + scope.$watch(scope.equalWith, function (value) { + ngModelCtrl.$validate(); + }); + } + }; + }); + + module.directive("confirmOnExit", function () { + return { + link: function ($scope, elem, attrs) { + var message = attrs["confirmMessage"]; + window.onbeforeunload = function () { + if ($scope[attrs["name"]].$dirty) { + return message; + } + }; + $scope.$on("$locationChangeStart", function (event, next, current) { + if ($scope[attrs["name"]].$dirty) { + if (!confirm(message)) { + event.preventDefault(); + } + } + }); + } + }; + }); })(); diff --git a/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js b/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js index 2d80718db6d7..e321f5b67759 100644 --- a/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js +++ b/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js @@ -32,7 +32,8 @@ "gn_dbtranslation", "gn_multiselect", "gn_mdtypewidget", - "blueimp.fileupload" + "blueimp.fileupload", + "ngMessages" ]); /** diff --git a/web-ui/src/main/resources/catalog/locales/en-admin.json b/web-ui/src/main/resources/catalog/locales/en-admin.json index 0126538fc586..1379e8db5b91 100644 --- a/web-ui/src/main/resources/catalog/locales/en-admin.json +++ b/web-ui/src/main/resources/catalog/locales/en-admin.json @@ -1472,6 +1472,11 @@ "es.url": "Elasticsearch server", "es.version": "Elasticsearch version", "es.index": "Index name", - "systemPropertiesProxyConfiguration": "Using http proxy settings in system properties." + "systemPropertiesProxyConfiguration": "Using http proxy settings in system properties.", + "fieldRequired": "The value is required", + "fieldTooLong": "The value is too long", + "fieldTooShort": "The value is too short", + "fieldEmailNotValid": "A valid email address is required", + "formConfirmExit": "The form has changes, if you exit the changes will be lost. Do you want to exit on the page?" } diff --git a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html index 3f59389df63b..b2ca0621a9b9 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html +++ b/web-ui/src/main/resources/catalog/templates/admin/usergroup/users.html @@ -117,6 +117,9 @@ data-ng-keypress="updatingUser()" class="form-horizontal" role="form" + data-confirm-message="{{ 'formConfirmExit' | translate}}" + data-confirm-on-exit="" + novalidate="" >
    @@ -142,7 +145,8 @@
    @@ -160,9 +164,18 @@ data-gn-duplicate-check-apply="userOperation == 'newuser'" data-gn-duplicate-check-remote="../api/users/properties/userid?exist={value}" data-ng-disabled="!user.isUserAdminOrMore() || !isUserProfileUpdateEnabled" + data-ng-maxlength="255" + placeholder="{{'usernameHelp' | translate}}" autocomplete="off" /> -

    usernameHelp

    +
    +

    fieldTooLong

    +

    fieldRequired

    +

    @@ -227,7 +242,7 @@
    @@ -272,13 +287,22 @@ name="name" class="form-control" data-ng-model="userSelected.name" + data-ng-maxlength="255" data-ng-required="true" data-ng-disabled="!isUserProfileUpdateEnabled" /> +
    +

    fieldTooLong

    +

    fieldRequired

    +
    @@ -289,12 +313,21 @@ class="form-control" data-ng-model="userSelected.surname" data-ng-required="" + data-ng-maxlength="255" data-ng-disabled="!isUserProfileUpdateEnabled" /> +
    +

    fieldTooLong

    +

    fieldRequired

    +
    @@ -305,12 +338,25 @@ class="form-control" data-ng-model="userSelected.emailAddresses[0]" data-ng-required="true" + data-ng-maxlength="128" data-ng-disabled="!isUserProfileUpdateEnabled" /> +
    +

    fieldTooLong

    +

    fieldRequired

    +

    fieldEmailNotValid

    +

    configureYourAvatar

    -
    +
    @@ -318,78 +364,121 @@ type="text" name="org" class="form-control" + data-ng-maxlength="255" data-ng-model="userSelected.organisation" data-ng-disabled="!isUserProfileUpdateEnabled" /> +
    +

    fieldTooLong

    +
    address -
    +
    +
    +

    fieldTooLong

    +
    -
    +
    +
    +

    fieldTooLong

    +
    -
    +
    +
    +

    fieldTooLong

    +
    -
    +
    +
    +

    +
    -
    +
    +
    +

    fieldTooLong

    +
    From d7c60e3581d21152dc51f8956544fc39961fe494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Prunayre?= Date: Tue, 5 Dec 2023 13:35:27 +0100 Subject: [PATCH 15/16] Admin console / Improve form checks for group, category and source (#7449) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Admin console / Improve form checks * More consistent name column length for group, category and source (ie. 255 char) * Add form check for max length and highlight field with error on length and pattern * Improve alignment of category list when there is no icon for a category * Admin console / Improve form checks: - Sources form: display a custom message if the portal identifier exceeds the maxlength - Fix the activation of the Save button in forms: it was using 'dirty' property (incorrect) instead of the correct one $dirty - Update report forms to remove the dirty property to activate the button to create the report (not required) --------- Co-authored-by: Jose García --- .../java/org/fao/geonet/domain/Group.java | 2 +- .../catalog/js/admin/CategoriesController.js | 3 + .../catalog/js/admin/SourcesController.js | 2 + .../catalog/js/admin/UserGroupController.js | 3 + .../resources/catalog/locales/en-core.json | 5 +- .../admin/classification/categories.html | 20 +++- .../report/report-filedownload-metadata.html | 2 +- .../report/report-fileupload-metadata.html | 2 +- .../report/report-internal-metadata.html | 2 +- .../admin/report/report-updated-metadata.html | 2 +- .../templates/admin/report/report-users.html | 2 +- .../templates/admin/settings/sources.html | 94 +++++++++++++------ .../admin/settings/static-pages.html | 2 +- .../templates/admin/usergroup/groups.html | 4 +- .../templates/admin/usergroup/users.html | 2 +- .../sql/migrate/v441/migrate-default.sql | 2 + 16 files changed, 105 insertions(+), 44 deletions(-) diff --git a/domain/src/main/java/org/fao/geonet/domain/Group.java b/domain/src/main/java/org/fao/geonet/domain/Group.java index 333b36a1678a..19fe92d6eb76 100644 --- a/domain/src/main/java/org/fao/geonet/domain/Group.java +++ b/domain/src/main/java/org/fao/geonet/domain/Group.java @@ -121,7 +121,7 @@ public Group setId(int id) { * * @return group name */ - @Column(nullable = false, length = 32) + @Column(nullable = false, length = 255) public String getName() { return _name; } diff --git a/web-ui/src/main/resources/catalog/js/admin/CategoriesController.js b/web-ui/src/main/resources/catalog/js/admin/CategoriesController.js index a907d1705130..ee0f4407b08a 100644 --- a/web-ui/src/main/resources/catalog/js/admin/CategoriesController.js +++ b/web-ui/src/main/resources/catalog/js/admin/CategoriesController.js @@ -46,6 +46,9 @@ $scope.selectCategory = function (c) { $scope.cateroryUpdated = false; $scope.categorySelected = c; + + $scope.gnCategoryFrom.$setPristine(); + $timeout(function () { $("#categoryname").focus(); }, 100); diff --git a/web-ui/src/main/resources/catalog/js/admin/SourcesController.js b/web-ui/src/main/resources/catalog/js/admin/SourcesController.js index e4a3176f008d..2a6c5e86e620 100644 --- a/web-ui/src/main/resources/catalog/js/admin/SourcesController.js +++ b/web-ui/src/main/resources/catalog/js/admin/SourcesController.js @@ -65,6 +65,8 @@ source.groupOwner = source.groupOwner || null; $scope.source = source; $scope.isNew = false; + + $scope.gnSourceForm.$setPristine(); }; function filterSources() { diff --git a/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js b/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js index e321f5b67759..1fca9a8c0640 100644 --- a/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js +++ b/web-ui/src/main/resources/catalog/js/admin/UserGroupController.js @@ -296,6 +296,7 @@ var data = response.data; $scope.userSelected = data; + $scope.gnUserEdit.$setPristine(); $scope.userIsAdmin = data.profile === "Administrator"; $scope.userIsEnabled = data.enabled; @@ -792,6 +793,8 @@ // that breaks the group management. // TODO: Use custom controllers for groups and users management $scope.groupSelected = angular.copy(g); + $scope.gnGroupEdit.$setPristine(); + $scope.clear($scope.queue); delete $scope.groupSelected.langlabel; diff --git a/web-ui/src/main/resources/catalog/locales/en-core.json b/web-ui/src/main/resources/catalog/locales/en-core.json index a1e71bfb3fd9..9e68593a273c 100644 --- a/web-ui/src/main/resources/catalog/locales/en-core.json +++ b/web-ui/src/main/resources/catalog/locales/en-core.json @@ -218,7 +218,10 @@ "passwordPattern": "Password must contain at least 1 uppercase, 1 lowercase, 1 number and 1 symbol. Symbols include: `~!@#$%^&*()-_=+[]{}\\|;:'\",.<>/?');", "passwordOld": "Old password", "passwordRepeat": "Repeat password", - "groupNameMaxlength": "Group name can not exceed 32 characters!", + "groupNameMaxlength": "Group name can not exceed 255 characters!", + "categoryNameMaxlength": "Category name can not exceed 255 characters!", + "portalNameMaxlength": "Portal name can not exceed 255 characters!", + "portalIdMaxlength": "Portal identifier can not exceed 255 characters!", "groupDescriptionMaxlength": "Group description can not exceed 255 characters!", "groupEmailMaxlength": "Group email can not exceed 128 characters!", "pol": "Polski", diff --git a/web-ui/src/main/resources/catalog/templates/admin/classification/categories.html b/web-ui/src/main/resources/catalog/templates/admin/classification/categories.html index 6a3de260da4b..076bfb91ebd0 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/classification/categories.html +++ b/web-ui/src/main/resources/catalog/templates/admin/classification/categories.html @@ -32,7 +32,7 @@ ng-class="{ 'active': c === categorySelected }" data-ng-click="selectCategory(c)" > - + {{c.label[lang]}} {{"saveCategory"|translate}} @@ -91,8 +91,10 @@
    - -
    +
    + +

    + categoryNameMaxlength +

    diff --git a/web-ui/src/main/resources/catalog/templates/admin/report/report-filedownload-metadata.html b/web-ui/src/main/resources/catalog/templates/admin/report/report-filedownload-metadata.html index 32cd8f79a886..395c4dddc54a 100644 --- a/web-ui/src/main/resources/catalog/templates/admin/report/report-filedownload-metadata.html +++ b/web-ui/src/main/resources/catalog/templates/admin/report/report-filedownload-metadata.html @@ -11,7 +11,7 @@
    -
    +
    {{source.name}} ({{('source-' + source.type) | translate}}) @@ -82,7 +82,7 @@