diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 79c4ed02a0c..e4f7920f8c5 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,15 +6,22 @@ Ask in a comment if you have troubles with any of them. --> # Checklist -- [ ] I have read the [contribution guidelines](https://github.com/geonetwork/core-geonetwork/blob/main/CONTRIBUTING.md +- [ ] I have read the [contribution guidelines](https://github.com/geonetwork/core-geonetwork/blob/main/CONTRIBUTING.md) - [ ] *Pull request* provided for `main` branch, backports managed with label - [ ] *Good housekeeping* of code, cleaning up comments, tests, and documentation - [ ] *Clean commit history* broken into understandable chucks, avoiding big commits with hundreds of files, cautious of reformatting and whitespace changes - [ ] *Clean commit message*s, longer verbose messages are encouraged - [ ] *API Changes* are identified in commit messages -- [ ] *Testing* provided for features or enhancements using [automatic tests](https://github.com/geonetwork/core-geonetwork/blob/main/software_development/TESTING.md)) -- [ ] *User documentation* provided for new features or enhancements in [mannual](https://github.com/geonetwork/core-geonetwork/tree/main/docs/manual) +- [ ] *Testing* provided for features or enhancements using [automatic tests](https://github.com/geonetwork/core-geonetwork/blob/main/software_development/TESTING.md) +- [ ] *User documentation* provided for new features or enhancements in [manual](https://github.com/geonetwork/core-geonetwork/tree/main/docs/manual) - [ ] *Build documentation* provided for development instructions in `README.md` files - [ ] *Library management* using `pom.xml` dependency management. Update build documentation with intended library use and library tutorials or documentation + + + diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000000..272cd05d974 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,25 @@ +name: ♻ Backport +on: + pull_request_target: + types: + - closed + - labeled + +permissions: + contents: read + +jobs: + backport: + permissions: + contents: write + pull-requests: write + issues: write + runs-on: ubuntu-20.04 + name: Backport + steps: + - name: Backport Bot + id: backport + if: github.event.pull_request.merged && ( ( github.event.action == 'closed' && contains( join( github.event.pull_request.labels.*.name ), 'backport') ) || contains( github.event.label.name, 'backport' ) ) + uses: m-kuhn/backport@v1.2.7 + with: + github_token: ${{ secrets.GH_TOKEN_BOT }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index dbda173e220..40ddc29fa23 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -43,7 +43,7 @@ jobs: show-progress: 'false' - name: Setup Java JDK - uses: actions/setup-java@v3.12.0 + uses: actions/setup-java@v4.1.0 with: java-version: 11 # Java distribution. See the list of supported distributions in README file @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,11 +80,8 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 - name: Remove SNAPSHOT jars from repository run: | find ~/.m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} - - name: Remove Schema 3.8 jars from repository - run: | - find ~/.m2/repository -name "*3.8*" -type d | xargs rm -rf {} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index be8cf41fff1..c65fbd51148 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -22,7 +22,7 @@ jobs: with: fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.x - name: mkdocs install diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8a94bcedc4b..8d473643803 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,31 +22,28 @@ jobs: submodules: 'recursive' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3.12.0 + uses: actions/setup-java@v4.1.0 with: distribution: 'temurin' java-version: ${{ matrix.jdk }} cache: 'maven' - name: Setup Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.x - name: mkdocs install working-directory: docs/manual run: pip install --upgrade pip && pip install -r requirements.txt - name: Set up Maven - uses: stCarolas/setup-maven@v4 + uses: stCarolas/setup-maven@v5 with: - maven-version: 3.6.3 + maven-version: 3.8.3 - name: Build with Maven run: | - mvn -B -ntp -V install -DskipTests=true -Dmaven.javadoc.skip=true -Pwith-doc + mvn -B -ntp -V install -DskipTests=true -Dmaven.javadoc.skip=true -Drelease -Pwith-doc - name: Remove SNAPSHOT jars from repository run: | find ~/.m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} - - name: Remove Schema 3.8 jars from repository - run: | - find ~/.m2/repository -name "*3.8*" -type d | xargs rm -rf {} QA: runs-on: ubuntu-22.04 @@ -58,22 +55,19 @@ jobs: submodules: 'recursive' show-progress: 'false' - name: Set up JDK - uses: actions/setup-java@v3.12.0 + uses: actions/setup-java@v4.1.0 with: distribution: 'temurin' java-version: 11 cache: 'maven' - name: Set up Maven - uses: stCarolas/setup-maven@v4 + uses: stCarolas/setup-maven@v5 with: - maven-version: 3.6.3 + maven-version: 3.8.3 - name: Test with maven run: | mvn -B resources:resources@copy-index-schema-to-source -f web - mvn -B -ntp -V -fae verify -Pit + mvn -B -ntp -V -fae verify -Drelesae -Pit - name: Remove SNAPSHOT jars from repository run: | find ~/.m2/repository -name "*SNAPSHOT*" -type d | xargs rm -rf {} - - name: Remove Schema 3.8 jars from repository - run: | - find ~/.m2/repository -name "*3.8*" -type d | xargs rm -rf {} diff --git a/.github/workflows/mvn-dep-tree.yml b/.github/workflows/mvn-dep-tree.yml index 74bd2f9cad7..0ac94031a83 100644 --- a/.github/workflows/mvn-dep-tree.yml +++ b/.github/workflows/mvn-dep-tree.yml @@ -20,7 +20,7 @@ jobs: show-progress: 'false' - name: Setup Java JDK - uses: actions/setup-java@v3.12.0 + uses: actions/setup-java@v4.1.0 with: java-version: 11 # Java distribution. See the list of supported distributions in README file @@ -30,4 +30,4 @@ jobs: cache: maven - name: Submit Dependency Snapshot - uses: advanced-security/maven-dependency-submission-action@v3 + uses: advanced-security/maven-dependency-submission-action@v4 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 00000000000..98cf13704c4 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '26 10 * * 5' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecard on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 33abdc2a46b..5788e6859bd 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -19,13 +19,13 @@ jobs: submodules: 'recursive' show-progress: 'false' - name: Set up JDK 11 - uses: actions/setup-java@v3.12.0 + uses: actions/setup-java@v4.1.0 with: distribution: 'temurin' java-version: '11' cache: 'maven' - name: Cache SonarCloud packages - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar @@ -33,6 +33,12 @@ jobs: - name: Build GN run: mvn -B package -DskipTests + - name: Set up JDK 21 # Sonarcloud analyzer needs at least JDK 17 + uses: actions/setup-java@v4.1.0 + with: + distribution: 'temurin' + java-version: '21' + cache: 'maven' - name: Analyze with Sonar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any diff --git a/.gitignore b/.gitignore index 60446e3e3c0..36df50f9368 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ eclipse/ es/elasticsearch-* es/es-dashboards/kibana-* es/es-dashboards/data/nodes/ +es/es-dashboards/data/index/ harvesters/harvester_*.log idea/ jcs_caching/ @@ -36,6 +37,7 @@ out/ package-lock.json rebel.xml release/jetty/* +release-build.sh schemas/*/doc/*/*.rst schematrons/.build target/ @@ -69,6 +71,7 @@ web/src/main/webapp/WEB-INF/data/data/metadata_data web/src/main/webapp/WEB-INF/data/data/metadata_subversion web/src/main/webapp/WEB-INF/data/data/resources/htmlcache web/src/main/webapp/WEB-INF/data/data/resources/images +web/src/main/webapp/WEB-INF/data/data/resources/schemapublication web/src/main/webapp/WEB-INF/data/data/resources/xml web/src/main/webapp/WEB-INF/data/data/upload/ web/src/main/webapp/WEB-INF/data/harvester_* @@ -80,7 +83,6 @@ web/src/main/webapp/WEB-INF/data/wro4j*.db web/src/main/webapp/WEB-INF/data/wro4j-cache* web/src/main/webapp/WEB-INF/data_* web/src/main/webapp/WEB-INF/metadata_subversion/ -web/src/main/webapp/WEB-INF/server.prop web/src/main/webapp/WEB-INF/prebuilt web/src/main/webapp/data/ web/src/main/webapp/doc/en diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 93a66e2617b..1bff3df03f5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,31 +1,31 @@ # Contributing -Thank you for contributing to GeoNetwork: +Thank you for contributing to GeoNetwork! * Free-software: GeoNetwork is free-software, using the [GNU GENERAL PUBLIC LICENSE](LICENSE.md). Contributions provided by you, or your employer, are required to be compatible with this free-software license. * Pull-request: GeoNetwork uses a pull-request workflow to review and accept changes. Pull-requests must be submitted against the *main* branch first, and may be back ported as required. # Pull requests -* Pull request is required, even if you have commit access, so the tests are run and other developer can check your code. +* Pull request is required, even if you have commit access, so the tests are run and another developer can check your code. * Pull requests must be applied to `main`, before being backported. -* Before merging a pull request, should be defined the following properties: +* Before merging a pull request, the following properties should be defined: - Milestone to include the change. - - Add the label `changelog` when the change is relevant to be added to the release changelog file . - - Add the label(s) to the backport to previous branch(es), when the change is a bug fix or if it is a small improvement that may be relevant to the backport. + - Add the label `changelog` when the change is relevant to be added to the release changelog file. + - Add `backport ` to indicate when the change is a bug fix or when it is a small improvement that is relevant to be backported. -* 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. +* Good housekeeping: Anytime you commit, try and clean the code around it according to the 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. Avoid 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. +* Draft: Use pull request **Draft** (or even the text "WIP") to identify _Work In Progress_. -* Rebase / Squash and merge: No merge commits with current branch, use Rebase or Squash and merge! +* Rebase / Squash and merge: Do not merge commits with the current branch, use Rebase or Squash and merge! -* API Change: Please identify any API change or behavior changes in commit messages. +* API Changes: Please identify any API change or behavior changes in commit messages. Also make sure that a [process for deprecation](PROCESS_FOR_DEPRECATION.md) of a feature is carefully dealt with. * Review: Review is required by another person, or more than one! Don't be shy asking for help or reviewing. diff --git a/PROCESS_FOR_DEPRECATION.md b/PROCESS_FOR_DEPRECATION.md new file mode 100644 index 00000000000..f80f7086c3f --- /dev/null +++ b/PROCESS_FOR_DEPRECATION.md @@ -0,0 +1,6 @@ +* Proposal for a Process for Deprecation (draft) + +# Feature deprecation process + +This page describes considerations and steps to take when removing a product feature. This is the process of depreciation or 'deprecating a feature'. + diff --git a/README.md b/README.md index ed6c54d3596..65f57590c6a 100644 --- a/README.md +++ b/README.md @@ -30,3 +30,6 @@ 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. * Module specific documentation can be found in each module: + +## Open Source Security Foundation (OpenSSF) best practices status +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/8626/badge)](https://www.bestpractices.dev/projects/8626) diff --git a/SECURITY.md b/SECURITY.md index 85ce30ec6b0..fda55f12dad 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,20 +2,20 @@ The GeoNetwork community takes the security of the software and all services based on the software product seriously. On this page you can find the versions for which the community provides security patches. -If you believe you have found a security vulnerability in the software or an implementation of the software, please report it to geonetwork@osgeo.org as described below. Do not publish the vulnerability in any public forums (such as twitter, email list or issue tracker). +If you believe you have found a security vulnerability in the software or an implementation of the software, please report it [here](https://github.com/geonetwork/core-geonetwork/security/advisories/new) as described below. Do not publish the vulnerability in any public forums (such as Twitter/X, email list or issue tracker). ## Supported Versions Each GeoNetwork release is supported with bug fixes for a limited period, with patch releases made approximately every three to six months. -- We recommend to update to latest incremental release as soon as possible to address security vulnarabilities. +- We recommend to update to latest incremental release as soon as possible to address security vulnerabilities. - Some overlap is provided when major versions are announced with both a current version and a maintenance version being made available to provide time for organizations to upgrade. -| Version | Supported | Comment | -|---------| ------------------ |------------------------ | -| 4.4.x | :white_check_mark: | Current version | -| 4.2.x | :white_check_mark: | Maintenance version | -| 3.12.x | :white_check_mark: | Maintenance version | +| Version | Supported | Comment | +|---------|--------------------|---------------------| +| 4.4.x | :white_check_mark: | Latest version | +| 4.2.x | :white_check_mark: | Stable version | +| 3.12.x | :white_check_mark: | Maintenance version | If your organisation is making use of a GeoNetwork version that is no longer in use by the community all is not lost. You can volunteer on the developer list to make additional releases, or engage with one of our [Commercial Support](https://www.osgeo.org/service-providers/?p=geonetwork) providers. @@ -23,8 +23,8 @@ If your organisation is making use of a GeoNetwork version that is no longer in If you encounter a security vulnerability in GeoNetwork please take care to report in a responsible fashion: -* Keep exploit details out of mailing list and issue tracker (send details to the Project Steering Committee via geonetwork@osgeo.org) +* Keep exploit details out of mailing list and issue tracker (instead provide details to the Project Steering Committee via the GitHub [Report a vulnerability](https://github.com/geonetwork/core-geonetwork/security/advisories/new) option link at the top of this page or send an email to geonetwork@osgeo.org) * Be prepared to work with community members on a solution -* Keep in mind community members are volunteers and an extensive fix may require fundraising / resources +* Keep in mind that community members are volunteers and an extensive fix may require fundraising / resources For more information see [How to contribute](https://github.com/geonetwork/core-geonetwork/wiki/How-to-contribute). diff --git a/cachingxslt/pom.xml b/cachingxslt/pom.xml index 012982b6469..2c7d55b23c1 100644 --- a/cachingxslt/pom.xml +++ b/cachingxslt/pom.xml @@ -31,7 +31,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT diff --git a/common/pom.xml b/common/pom.xml index 5d8b9aca7a5..275b1544a3b 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -31,7 +31,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT @@ -175,8 +175,6 @@ org.json json - 20140107 - compile org.bitbucket.cowwoc diff --git a/common/src/main/java/org/fao/geonet/Logger.java b/common/src/main/java/org/fao/geonet/Logger.java index 920d13e548f..76590b8d698 100644 --- a/common/src/main/java/org/fao/geonet/Logger.java +++ b/common/src/main/java/org/fao/geonet/Logger.java @@ -23,8 +23,6 @@ package org.fao.geonet; -//============================================================================= - import org.apache.logging.log4j.core.appender.FileAppender; /** @@ -37,35 +35,52 @@ public interface Logger { * * @return check if debug logging is enabled */ - public boolean isDebugEnabled(); + boolean isDebugEnabled(); /** * Log debug message used indicate module troubleshoot module activity. * * @param message debug message used to provide in */ - public void debug(String message); + void debug(String message); + + void debug(String message, Throwable throwable); + + void debug(String message, Object... object); /** * Log information message indicating module progress. * * @param message information message indicating progress */ - public void info(String message); + void info(String message); + + void info(String message, Throwable throwable); - /** Log warning message indicating potentially harmful situation, module + void info(String message, Object... object); + + /** + * Log warning message indicating potentially harmful situation, module * will continue to try and complete current activity. * * @param message Warning message indicating potentially harmful situation */ - public void warning(String message); + void warning(String message); + + void warning(String message, Throwable throwable); + + void warning(String message, Object... object); /** * Log error message indicating module cannot continue current activity. * * @param message Error message */ - public void error(String message); + void error(String message); + + void error(String message, Throwable throwable); + + void error(String message, Object... object); /** * Log error message using provided throwable, indicating module cannot continue @@ -73,51 +88,49 @@ public interface Logger { * * @param ex Cause of error condition. */ - public void error(Throwable ex); + void error(Throwable ex); /** * Log severe message, indicating application cannot continue to operate. * * @param message severe message */ - public void fatal(String message); + void fatal(String message); /** * Functional module used for logging messages (for example {@code jeeves.engine}). * * @return functional module used for logging messages. */ - public String getModule(); + String getModule(); /** * Configure logger with log4j {@link FileAppender}, used for output. - * + *

* The file appender is also responsible for log file location provided by {@link #getFileAppender()}. * * @param fileAppender Log4j FileAppender */ - public void setAppender(FileAppender fileAppender); + void setAppender(FileAppender fileAppender); /** * The log file name from the file appender for this module. - * + *

* Note both module and fallback module are provided allowing providing a better opportunity * to learn the log file location. Harvesters use the log file name parent directory as a good * location to create {@code /harvester_logs/} folder. - * + *

* Built-in configuration uses log file location {@code logs/geonetwork.log} relative to the current directory, or relative to system property {@code log_file}. * * @return logfile location of {@code logs/geonetwork.log} file */ - public String getFileAppender(); + String getFileAppender(); /** * Access to omodule logging level, providing + * * @return */ - public org.apache.logging.log4j.Level getThreshold(); + org.apache.logging.log4j.Level getThreshold(); } - -//============================================================================= - diff --git a/common/src/main/java/org/fao/geonet/utils/Log.java b/common/src/main/java/org/fao/geonet/utils/Log.java index 094dfb4942e..df0269aaf14 100644 --- a/common/src/main/java/org/fao/geonet/utils/Log.java +++ b/common/src/main/java/org/fao/geonet/utils/Log.java @@ -24,22 +24,18 @@ package org.fao.geonet.utils; -import org.apache.log4j.Priority; import org.apache.log4j.bridge.AppenderWrapper; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.core.Appender; +import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.appender.FileAppender; import org.apache.logging.log4j.core.appender.RollingFileAppender; -import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.config.Configuration; import org.apache.logging.log4j.core.config.LoggerConfig; import java.io.File; -import java.util.Enumeration; - -//============================================================================= /** * Jeeves logging integration, defining functional logger categories by module @@ -125,8 +121,12 @@ public static void debug(String module, Object message) { LogManager.getLogger(module).debug(message); } - public static void debug(String module, Object message, Exception e) { - LogManager.getLogger(module).debug(message, e); + public static void debug(String module, String message, Object... objects) { + LogManager.getLogger(module).debug(message, objects); + } + + public static void debug(String module, String message, Throwable throwable) { + LogManager.getLogger(module).debug(message, throwable); } public static boolean isDebugEnabled(String module) { @@ -157,10 +157,15 @@ public static void info(String module, Object message) { LogManager.getLogger(module).info(message); } - public static void info(String module, Object message, Throwable t) { - LogManager.getLogger(module).info(message, t); + public static void info(String module, String message, Object... objects) { + LogManager.getLogger(module).info(message, objects); + } + + public static void info(String module, String message, Throwable throwable) { + LogManager.getLogger(module).info(message, throwable); } + //--------------------------------------------------------------------------- public static void warning(String module, Object message) { @@ -182,6 +187,14 @@ public static void error(String module, Object message, Throwable t) { LogManager.getLogger(module).error(message, t); } + public static void error(String module, String message, Object... objects) { + LogManager.getLogger(module).error(message, objects); + } + + public static void error(String module, String message, Throwable throwable) { + LogManager.getLogger(module).error(message, throwable); + } + //--------------------------------------------------------------------------- public static void fatal(String module, Object message) { @@ -225,18 +238,58 @@ public void debug(String message) { Log.debug(module, message); } + @Override + public void debug(String message, Throwable throwable) { + Log.debug(module, message, throwable); + } + + @Override + public void debug(String message, Object... object) { + Log.debug(module, message, object); + } + public void info(String message) { Log.info(module, message); } + @Override + public void info(String message, Throwable throwable) { + Log.info(module, message, throwable); + } + + @Override + public void info(String message, Object... object) { + Log.info(module, message, object); + } + public void warning(String message) { Log.warning(module, message); } + @Override + public void warning(String message, Throwable throwable) { + Log.warning(module, message, throwable); + } + + @Override + public void warning(String message, Object... object) { + + } + public void error(String message) { Log.error(module, message); } + @Override + public void error(String message, Throwable throwable) { + Log.error(module, message, throwable); + } + + @Override + public void error(String message, Object... object) { + Log.error(module, message, object); + } + public void fatal(String message) { Log.fatal(module, message); } @@ -279,7 +332,7 @@ public String getFileAppender() { } } LoggerConfig fallbackConfig = configuration.getLoggers().get(fallbackModule); - if( fallbackConfig != null) { + if (fallbackConfig != null) { for (Appender appender : fallbackConfig.getAppenders().values()) { File file = toLogFile(appender); if (file != null && file.exists()) { diff --git a/core/pom.xml b/core/pom.xml index 8eaac809034..a3b490a3c17 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -340,10 +340,6 @@ org.apache.xmlgraphics xmlgraphics-commons - - commons-fileupload - commons-fileupload - commons-collections commons-collections @@ -583,9 +579,10 @@ spring-test compile + - org.elasticsearch.client - elasticsearch-rest-high-level-client + co.elastic.clients + elasticsearch-java @@ -699,6 +696,7 @@ test 9300 9200 + -Xmx2g diff --git a/core/src/main/java/jeeves/guiservices/profiles/Get.java b/core/src/main/java/jeeves/guiservices/profiles/Get.java deleted file mode 100644 index 32beb02a690..00000000000 --- a/core/src/main/java/jeeves/guiservices/profiles/Get.java +++ /dev/null @@ -1,59 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This library is free software; you can redistribute it and/or -//=== modify it under the terms of the GNU Lesser General Public -//=== License as published by the Free Software Foundation; either -//=== version 2.1 of the License, or (at your option) any later version. -//=== -//=== This library is distributed in the hope that it will be useful, -//=== but WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== Lesser General Public License for more details. -//=== -//=== You should have received a copy of the GNU Lesser General Public -//=== License along with this library; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package jeeves.guiservices.profiles; - -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.fao.geonet.domain.Profile; -import org.jdom.Element; - -import java.nio.file.Path; - -//============================================================================= - -/** - * A simple service that returns all user profiles in the system - */ - -public class Get implements Service { - public void init(Path appPath, ServiceConfig params) throws Exception { - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - public Element exec(Element params, ServiceContext context) throws Exception { - Profile profile = context.getUserSession().getProfile(); - - return (Element) context.getProfileManager().getProfilesElement(profile); - } -} - -//============================================================================= - diff --git a/core/src/main/java/jeeves/server/context/BasicContext.java b/core/src/main/java/jeeves/server/context/BasicContext.java index da210ed0ecf..00d1769b4c6 100644 --- a/core/src/main/java/jeeves/server/context/BasicContext.java +++ b/core/src/main/java/jeeves/server/context/BasicContext.java @@ -143,21 +143,61 @@ public void debug(final String message) { logger.debug(message); } + @Override + public void debug(String message, Throwable throwable) { + logger.debug(message, throwable); + } + + @Override + public void debug(String message, Object... object) { + logger.debug(message, object); + } + @Override public void info(final String message) { logger.info(message); } + @Override + public void info(String message, Throwable throwable) { + logger.info(message, throwable); + } + + @Override + public void info(String message, Object... object) { + logger.info(message, object); + } + @Override public void warning(final String message) { logger.warning(message); } + @Override + public void warning(String message, Throwable throwable) { + logger.warning(message, throwable); + } + + @Override + public void warning(String message, Object... object) { + logger.warning(message, object); + } + @Override public void error(final String message) { logger.error(message); } + @Override + public void error(String message, Throwable throwable) { + logger.error(message, throwable); + } + + @Override + public void error(String message, Object... object) { + logger.error(message, object); + } + @Override public void error(Throwable ex) { logger.error(ex); @@ -200,6 +240,3 @@ public String getNodeId() { return NodeInfo.DEFAULT_NODE; } } - -//============================================================================= - diff --git a/core/src/main/java/jeeves/services/Ping.java b/core/src/main/java/jeeves/services/Ping.java deleted file mode 100644 index c975bfdd22c..00000000000 --- a/core/src/main/java/jeeves/services/Ping.java +++ /dev/null @@ -1,52 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This library is free software; you can redistribute it and/or -//=== modify it under the terms of the GNU Lesser General Public -//=== License as published by the Free Software Foundation; either -//=== version 2.1 of the License, or (at your option) any later version. -//=== -//=== This library is distributed in the hope that it will be useful, -//=== but WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== Lesser General Public License for more details. -//=== -//=== You should have received a copy of the GNU Lesser General Public -//=== License along with this library; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package jeeves.services; - -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.jdom.Element; - -import java.nio.file.Path; - -//============================================================================= - -public class Ping implements Service { - public void init(Path appPath, ServiceConfig params) throws Exception { - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - public Element exec(Element params, ServiceContext context) throws Exception { - return (Element) params.clone(); - } -} - -//============================================================================= - diff --git a/core/src/main/java/jeeves/services/RegExpReplace.java b/core/src/main/java/jeeves/services/RegExpReplace.java deleted file mode 100644 index a172f82c4e5..00000000000 --- a/core/src/main/java/jeeves/services/RegExpReplace.java +++ /dev/null @@ -1,141 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This library is free software; you can redistribute it and/or -//=== modify it under the terms of the GNU Lesser General Public -//=== License as published by the Free Software Foundation; either -//=== version 2.1 of the License, or (at your option) any later version. -//=== -//=== This library is distributed in the hope that it will be useful, -//=== but WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== Lesser General Public License for more details. -//=== -//=== You should have received a copy of the GNU Lesser General Public -//=== License along with this library; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package jeeves.services; - -import jeeves.constants.Jeeves; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.fao.geonet.utils.Xml; -import org.jdom.Element; - -import java.nio.file.Path; -import java.util.Iterator; -import java.util.List; -import java.util.StringTokenizer; -import java.util.Vector; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -//============================================================================= - -/** - * This service reads a configuration xml file containing pattern-replacement pairs and - * applies all the pairs to each text element of the output. The configuration is read from - * xml/regexp.xml and is formatted: - */ - -public class RegExpReplace implements Service { - private Vector patterns; - private Vector replacements; - - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - - @SuppressWarnings("unchecked") - public void init(Path appPath, ServiceConfig params) throws Exception { - String file = params.getMandatoryValue(Jeeves.Config.FILE); - Element config = Xml.loadFile(appPath.resolve(file)); - - patterns = new Vector(); - replacements = new Vector(); - - // read all pattern-replacement pairs - for (Element expr : (List) config.getChildren()) { - String pattern = expr.getAttributeValue(Jeeves.Attr.PATTERN); - String replacement = expr.getAttributeValue(Jeeves.Attr.REPLACEMENT); - - patterns.add(Pattern.compile(pattern)); - replacements.add(getRealText(replacement)); - } - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - public Element exec(Element params, ServiceContext context) throws Exception { - replaceInElement(params); - - return params; - } - - //-------------------------------------------------------------------------- - //--- replace some entities - - private String getRealText(String text) { - String noLt = replace(text, "<", "<"); - String noGt = replace(noLt, ">", ">"); - String noAmp = replace(noGt, "&", "&"); - - return noAmp; - } - - //-------------------------------------------------------------------------- - - private String replace(String text, String delim, String replacement) { - StringBuffer result = new StringBuffer(); - StringTokenizer st = new StringTokenizer(text, delim, true); - - while (st.hasMoreTokens()) { - String token = st.nextToken(); - - if (token.equals(delim)) result.append(replacement); - else result.append(token); - } - - return result.toString(); - } - - //-------------------------------------------------------------------------- - //--- apply all pattern-replacement pairs - - @SuppressWarnings("unchecked") - private void replaceInElement(Element e) { - if (e.getChildren().size() != 0) - for (Iterator iter = e.getChildren().iterator(); iter.hasNext(); ) - replaceInElement(iter.next()); - else { - String text = e.getText(); - - for (int i = 0; i < patterns.size(); i++) { - Pattern pattern = (Pattern) patterns.get(i); - String replacement = (String) replacements.get(i); - Matcher matcher = pattern.matcher(text); - - text = matcher.replaceAll(replacement); - } - e.setText(text); - } - } -} - -//============================================================================= - diff --git a/core/src/main/java/jeeves/services/db/Select.java b/core/src/main/java/jeeves/services/db/Select.java deleted file mode 100644 index f1bf4a206d1..00000000000 --- a/core/src/main/java/jeeves/services/db/Select.java +++ /dev/null @@ -1,396 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This library is free software; you can redistribute it and/or -//=== modify it under the terms of the GNU Lesser General Public -//=== License as published by the Free Software Foundation; either -//=== version 2.1 of the License, or (at your option) any later version. -//=== -//=== This library is distributed in the hope that it will be useful, -//=== but WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== Lesser General Public License for more details. -//=== -//=== You should have received a copy of the GNU Lesser General Public -//=== License along with this library; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package jeeves.services.db; - -import jeeves.constants.Jeeves; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.fao.geonet.utils.Xml; -import org.jdom.Element; - -import javax.sql.DataSource; - -import java.io.StringReader; -import java.nio.file.Path; -import java.sql.*; -import java.text.DecimalFormat; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.Date; - -//============================================================================= - -/** - * Performs a generic query - */ - -public class Select implements Service { - private static final String DEFAULT_DATE_FORMAT = "dd-MM-yyyy"; - private static final String DEFAULT_TIME_FORMAT = "HH:mm:ss"; - private static final String DEFAULT_TIMESTAMP_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; - - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - private String query; - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - private Vector inFields; - private Vector outFields; - - public static Vector scanInFields(Element params, Vector inFields, Element result, ServiceContext context) throws Exception { - // build argument list - Vector vArgs = new Vector(); - - for (Element field : inFields) { - // extract field attributes and value - String name = field.getAttributeValue(Jeeves.Attr.NAME); - String mandatory = field.getAttributeValue(Jeeves.Attr.MANDATORY); - String nullText = field.getAttributeValue(Jeeves.Attr.NULL); - String format = field.getAttributeValue(Jeeves.Attr.FORMAT); - String sqlValue = field.getAttributeValue(Jeeves.Attr.VALUE); - String forward = field.getAttributeValue(Jeeves.Attr.FORWARD); - String value = params.getChildText(name); - - // check if field name is defined - if (name == null) - throw new IllegalArgumentException("undefined field name: " + Xml.getString(field)); - - // get value from sqlValue if needed - if (sqlValue != null) { - Element el = select(context, sqlValue); - value = el.getChild(Jeeves.Elem.RECORD).getChildText(name); - } - // check if mandatory fields are present - boolean mFlag; - if (mandatory == null || mandatory.equals(Jeeves.Text.FALSE)) - mFlag = false; - else if (mandatory.equals(Jeeves.Text.TRUE)) - mFlag = true; - else - throw new IllegalArgumentException("bad mandatory attribute value: " + mandatory); - if (mFlag && value == null) - throw new IllegalArgumentException("mandatory parameter missing: " + name); - - // add field to select arguments - if (value == null || (nullText != null && nullText.equals(value))) - vArgs.add(null); - else { - // forward field to output if needed - boolean fFlag; - if (forward == null || forward.equals(Jeeves.Text.FALSE)) - fFlag = false; - else if (forward.equals(Jeeves.Text.TRUE)) - fFlag = true; - else - throw new IllegalArgumentException("bad forward attribute value: " + forward); - if (fFlag && result != null) - result.addContent(new Element(name).setText(value)); - - String type = field.getAttributeValue(Jeeves.Attr.TYPE); - - if (type == null || type.equals(Jeeves.Attr.Type.STRING)) - vArgs.add(value); - - else if (type.equals(Jeeves.Attr.Type.INT)) { - if (format == null) - vArgs.add(Integer.valueOf(value)); - else { - DecimalFormat df = new DecimalFormat(format); - Number n = df.parse(value); - vArgs.add(Integer.valueOf(n.intValue())); - } - } else if (type.equals(Jeeves.Attr.Type.DOUBLE)) { - if (format == null) - vArgs.add(new Double(value)); - else { - DecimalFormat df = new DecimalFormat(format); - Number n = df.parse(value); - vArgs.add(new Double(n.doubleValue())); - } - } else if (type.equals(Jeeves.Attr.Type.DATE)) { - SimpleDateFormat df = (format == null) ? new SimpleDateFormat(DEFAULT_DATE_FORMAT) : new SimpleDateFormat(format); - Date date = df.parse(value); - vArgs.add(new java.sql.Date(date.getTime())); // directly passing date does not work, at least with MySql - } else - throw new IllegalArgumentException("bad field type: " + type); - } - } - return vArgs; - } - - /* ************************** Methods for simplifying JPA migration ****************************************** */ - - public static Hashtable scanOutFields(Vector outFields) throws Exception { - Hashtable formats = new Hashtable(); - - // scan fields info - for (Element field : outFields) { - // build Field - String name = field.getAttributeValue(Jeeves.Attr.NAME); - if (name == null) - throw new IllegalArgumentException("undefined field name: " + Xml.getString(field)); - - String format = field.getAttributeValue(Jeeves.Attr.FORMAT); - if (format != null) - formats.put(name, format); - } - return formats; - } - - /** - * This method is taken from the old Dbms class to allow reading as Element like a lot of xsl - * require. This is to ease the migration to all JPA database access. - */ - public static Element select(ServiceContext context, String query) - throws SQLException { - return selectFull(context, query, Collections.emptyMap()); - } - - /** - * This method is taken from the old Dbms class to allow reading as Element like a lot of xsl - * require. This is to ease the migration to all JPA database access. - */ - public static Element selectFull(ServiceContext context, String query, Map formats, Object... args) - throws SQLException { - - DataSource datasource = context.getBean(DataSource.class); - - Connection conn = null; - PreparedStatement stmt = null; - ResultSet resultSet = null; - - try { - conn = datasource.getConnection(); - stmt = conn.prepareStatement(query); - if (args != null) { - for (int i = 0; i < args.length; i++) { - setObject(stmt, i, args[i]); - } - } - resultSet = stmt.executeQuery(); - - Element result = buildResponse(resultSet, formats); - - return result; - } finally { - try { - if (resultSet != null) { - resultSet.close(); - } - } finally { - try { - if (stmt != null) { - stmt.close(); - } - } finally { - if (conn != null) { - conn.close(); - } - } - } - } - } - - private static void setObject(PreparedStatement stmt, int i, Object obj) throws SQLException { - if (obj instanceof String) { - String s = (String) obj; - - if (s.length() < 4000) - stmt.setString(i + 1, s); - else - stmt.setCharacterStream(i + 1, new StringReader(s), s.length()); - } else - stmt.setObject(i + 1, obj); - } - - private static Element buildResponse(ResultSet rs, Map formats) throws SQLException { - ResultSetMetaData md = rs.getMetaData(); - - int colNum = md.getColumnCount(); - - // --- retrieve name and type of fields - - Vector vHeaders = new Vector(); - Vector vTypes = new Vector(); - - for (int i = 0; i < colNum; i++) { - vHeaders.add(md.getColumnLabel(i + 1).toLowerCase()); - vTypes.add(Integer.valueOf(md.getColumnType(i + 1))); - } - - // --- build the jdom tree - - Element root = new Element(Jeeves.Elem.RESPONSE); - - while (rs.next()) { - Element record = new Element(Jeeves.Elem.RECORD); - - for (int i = 0; i < colNum; i++) { - String name = vHeaders.get(i).toString(); - int type = ((Integer) vTypes.get(i)).intValue(); - record.addContent(buildElement(rs, i, name, type, formats)); - } - root.addContent(record); - } - return root; - } - - private static Element buildElement(ResultSet rs, int col, String name, int type, Map formats) throws SQLException { - String value = null; - - switch (type) { - case Types.DATE: - Date date = rs.getDate(col + 1); - if (date == null) value = null; - else { - String format = formats.get(name); - SimpleDateFormat df = (format == null) ? new SimpleDateFormat(DEFAULT_DATE_FORMAT) : new SimpleDateFormat(format); - value = df.format(date); - } - break; - - case Types.TIME: - Time time = rs.getTime(col + 1); - if (time == null) value = null; - else { - String format = formats.get(name); - SimpleDateFormat df = (format == null) ? new SimpleDateFormat(DEFAULT_TIME_FORMAT) : new SimpleDateFormat(format); - value = df.format(time); - } - break; - - case Types.TIMESTAMP: - Timestamp timestamp = rs.getTimestamp(col + 1); - if (timestamp == null) value = null; - else { - String format = formats.get(name); - SimpleDateFormat df = (format == null) ? new SimpleDateFormat(DEFAULT_TIMESTAMP_FORMAT) : new SimpleDateFormat(format); - value = df.format(timestamp); - } - break; - - case Types.TINYINT: - case Types.SMALLINT: - case Types.INTEGER: - case Types.BIGINT: - long l = rs.getLong(col + 1); - if (rs.wasNull()) value = null; - else { - String format = formats.get(name); - if (format == null) value = l + ""; - else { - DecimalFormat df = new DecimalFormat(format); - value = df.format(l); - } - } - break; - - case Types.DECIMAL: - case Types.FLOAT: - case Types.DOUBLE: - case Types.REAL: - case Types.NUMERIC: - double n = rs.getDouble(col + 1); - - if (rs.wasNull()) - value = null; - else { - String format = formats.get(name); - - if (format == null) { - value = n + ""; - - //--- this fix is mandatory for oracle - //--- that shit returns integers like xxx.0 - - if (value.endsWith(".0")) - value = value.substring(0, value.length() - 2); - } else { - DecimalFormat df = new DecimalFormat(format); - value = df.format(n); - } - } - break; - - default: - value = rs.getString(col + 1); - if (value != null) { - value = stripIllegalChars(value); - } - - break; - } - return new Element(name).setText(value); - } - - private static String stripIllegalChars(String input) { - String output = input; - for (int i = 127; i < 160; i++) { - String c = String.valueOf((char) i); - if (output.contains(c)) { - output = output.replaceAll(c, ""); - } - } - - return output; - } - - public void init(Path appPath, ServiceConfig params) throws Exception { - query = params.getMandatoryValue(Jeeves.Config.QUERY); - List inList = params.getChildren(Jeeves.Config.IN_FIELDS, Jeeves.Config.FIELD); - - inFields = new Vector(); - if (inList != null) { - for (Element field : inList) { - inFields.add(field); - } - } - - List outList = params.getChildren(Jeeves.Config.OUT_FIELDS, Jeeves.Config.FIELD); - outFields = new Vector(); - if (outList != null) { - for (Element field : outList) { - outFields.add(field); - } - } - } - - public Element exec(Element params, ServiceContext context) throws Exception { - Vector vArgs = scanInFields(params, inFields, null, context); - Hashtable formats = scanOutFields(outFields); - return selectFull(context, query, formats, vArgs.toArray()); - } -} - -//============================================================================= - diff --git a/core/src/main/java/jeeves/services/http/Get.java b/core/src/main/java/jeeves/services/http/Get.java deleted file mode 100644 index c33fe4f87af..00000000000 --- a/core/src/main/java/jeeves/services/http/Get.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the - * United Nations (FAO-UN), United Nations World Food Programme (WFP) - * and United Nations Environment Programme (UNEP) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - * - * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, - * Rome - Italy. email: geonetwork@osgeo.org - */ - -//============================================================================= - -package jeeves.services.http; - -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.context.ServiceContext; - -import org.fao.geonet.Constants; -import org.fao.geonet.utils.Xml; -import org.jdom.Element; - -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.file.Path; -import java.util.List; - -//============================================================================= - -/** - * Returns a specific record given its id - */ - -public class Get implements Service { - public static final String URL_PARAM_NAME = "url"; - - private String configUrl; - - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - - public void init(Path appPath, ServiceConfig params) throws Exception { - configUrl = params.getValue(URL_PARAM_NAME); - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - @SuppressWarnings("unchecked") - public Element exec(Element params, ServiceContext context) throws Exception { - // read url - String sUrl = params.getChildText(URL_PARAM_NAME); - if (sUrl == null) sUrl = configUrl; - if (sUrl == null) - throw new IllegalArgumentException("The '" + URL_PARAM_NAME + "' configuration parameter is missing"); - - // add other parameters to HTTP request - boolean first = new URL(sUrl).getQuery() == null; - StringBuffer sb = new StringBuffer(sUrl); - for (Element child : (List) params.getChildren()) { - // skip the url parameter - if (child.getName().equals(URL_PARAM_NAME)) continue; - - if (first) { - first = false; - sb.append('?'); - } else { - sb.append('&'); - } - sb.append(child.getName()).append('=').append(URLEncoder.encode(child.getText(), Constants.ENCODING)); - } - URL url = new URL(sb.toString()); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - InputStream input = conn.getInputStream(); - - return Xml.loadStream(input); - - /* FIXME: use this instead of previous line to dump HTTP response body - ByteArrayOutputStream output = new ByteArrayOutputStream(); - byte buffer[] = new byte[BUFSIZE]; - for (int nRead; (nRead = input.read(buffer, 0, BUFSIZE)) > 0; ) - output.write(buffer, 0, nRead); - output.close(); - conn.disconnect(); - String sResult = output.toString(); - System.out.println("HTTP response body:\n" + sResult); - Element result = Xml.loadString(sResult, false); // do not validate - return result; - */ - } -} - diff --git a/core/src/main/java/jeeves/services/session/Put.java b/core/src/main/java/jeeves/services/session/Put.java deleted file mode 100644 index 5f82970b320..00000000000 --- a/core/src/main/java/jeeves/services/session/Put.java +++ /dev/null @@ -1,92 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This library is free software; you can redistribute it and/or -//=== modify it under the terms of the GNU Lesser General Public -//=== License as published by the Free Software Foundation; either -//=== version 2.1 of the License, or (at your option) any later version. -//=== -//=== This library is distributed in the hope that it will be useful, -//=== but WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== Lesser General Public License for more details. -//=== -//=== You should have received a copy of the GNU Lesser General Public -//=== License along with this library; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package jeeves.services.session; - -import jeeves.constants.Jeeves; -import jeeves.interfaces.Service; -import jeeves.server.ServiceConfig; -import jeeves.server.UserSession; -import jeeves.server.context.ServiceContext; - -import org.jdom.Element; - -import java.nio.file.Path; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.List; - -//============================================================================= - -/** - * Stores input fields in the session - */ - -public class Put implements Service { - String groupName; - HashSet inFields; - - //-------------------------------------------------------------------------- - //--- - //--- Init - //--- - //-------------------------------------------------------------------------- - - public void init(Path appPath, ServiceConfig params) throws Exception { - groupName = params.getMandatoryValue(Jeeves.Config.GROUP); - List l = params.getChildren(Jeeves.Config.IN_FIELDS, Jeeves.Config.FIELD); - if (l != null) { - inFields = new HashSet(); - for (Element field : l) { - inFields.add(field.getName()); - } - } - } - - //-------------------------------------------------------------------------- - //--- - //--- Service - //--- - //-------------------------------------------------------------------------- - - @SuppressWarnings("unchecked") - public Element exec(Element params, ServiceContext context) throws Exception { - UserSession session = context.getUserSession(); - - Hashtable group = (Hashtable) session.getProperty(groupName); - if (group == null) { - group = new Hashtable(); - } - for (Element child : (List) params.getChildren()) { - if (inFields == null || inFields.contains(child.getName())) { - group.put(child.getName(), child); - } - } - session.setProperty(groupName, group); - - return params; - } -} - -//============================================================================= - diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java index dfcd55a1b51..c2c20f8c898 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java @@ -97,7 +97,7 @@ public final ResourceHolder getResource(ServiceContext context, String metadataU } protected static AccessManager getAccessManager(final ServiceContext context) { - return context.getBean(AccessManager.class); + return ApplicationContextHolder.get().getBean(AccessManager.class); } public static int getAndCheckMetadataId(String metadataUuid, Boolean approved) throws Exception { diff --git a/core/src/main/java/org/fao/geonet/constants/Edit.java b/core/src/main/java/org/fao/geonet/constants/Edit.java index d3e6faecc26..5f9560f75e9 100644 --- a/core/src/main/java/org/fao/geonet/constants/Edit.java +++ b/core/src/main/java/org/fao/geonet/constants/Edit.java @@ -74,6 +74,7 @@ public static final class Elem { public static final String TITLE = "title"; public static final String IS_HARVESTED = "isHarvested"; public static final String HARVEST_INFO = "harvestInfo"; + public static final String OWNERID = "ownerId"; public static final String OWNERNAME = "ownername"; public static final String GROUPOWNERNAME = "groupOwnerName"; public static final String POPULARITY = "popularity"; diff --git a/core/src/main/java/org/fao/geonet/constants/Geonet.java b/core/src/main/java/org/fao/geonet/constants/Geonet.java index f17ce79992d..057626e6940 100644 --- a/core/src/main/java/org/fao/geonet/constants/Geonet.java +++ b/core/src/main/java/org/fao/geonet/constants/Geonet.java @@ -630,7 +630,7 @@ public static final class Namespaces { public static class IndexFieldNames { public static final String HASXLINKS = "_hasxlinks"; - public static final String XLINK = "_xlink"; + public static final String XLINK = "xlink"; public static final String ROOT = "_root"; public static final String SCHEMA = "documentStandard"; public static final String DATABASE_CREATE_DATE = "createDate"; diff --git a/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.java b/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.java index 971e1c072a0..361c7fc816b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.java +++ b/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.java @@ -27,15 +27,13 @@ import com.google.common.base.Function; import com.google.common.collect.Lists; import com.google.common.collect.Maps; - -import org.locationtech.jts.util.Assert; - import org.fao.geonet.Constants; import org.fao.geonet.constants.Geonet; import org.fao.geonet.exceptions.TermNotFoundException; import org.fao.geonet.kernel.search.keyword.KeywordRelation; import org.fao.geonet.languages.IsoLanguagesMapper; import org.fao.geonet.utils.Log; +import org.locationtech.jts.util.Assert; import org.openrdf.model.GraphException; import org.openrdf.model.URI; import org.openrdf.sesame.config.AccessDeniedException; @@ -46,6 +44,8 @@ import org.openrdf.sesame.repository.local.LocalRepository; import org.springframework.beans.factory.annotation.Autowired; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; @@ -59,9 +59,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - /** * @author Jesse on 2/27/2015. */ @@ -221,8 +218,7 @@ public synchronized URI addElement(KeywordBean keyword) throws IOException, Acce } @Override - public synchronized Thesaurus removeElement(KeywordBean keyword) throws MalformedQueryException, QueryEvaluationException, - IOException, AccessDeniedException { + public synchronized Thesaurus removeElement(KeywordBean keyword) throws AccessDeniedException { throw new UnsupportedOperationException(); } @@ -237,8 +233,7 @@ public synchronized Thesaurus removeElement(String uri) throws AccessDeniedExcep } @Override - public synchronized URI updateElement(KeywordBean keyword, boolean replace) throws AccessDeniedException, IOException, - MalformedQueryException, QueryEvaluationException, GraphException { + public synchronized URI updateElement(KeywordBean keyword, boolean replace) throws AccessDeniedException { throw new UnsupportedOperationException(); } @@ -266,12 +261,12 @@ public Thesaurus updateCode(KeywordBean bean, String newcode) throws AccessDenie } @Override - public synchronized Thesaurus updateCode(String namespace, String oldcode, String newcode) throws AccessDeniedException, IOException { + public synchronized Thesaurus updateCode(String namespace, String oldcode, String newcode) throws AccessDeniedException { throw new UnsupportedOperationException(); } @Override - public synchronized Thesaurus updateCodeByURI(String olduri, String newuri) throws AccessDeniedException, IOException { + public synchronized Thesaurus updateCodeByURI(String olduri, String newuri) throws AccessDeniedException { throw new UnsupportedOperationException(); } @@ -287,8 +282,7 @@ public IsoLanguagesMapper getIsoLanguageMapper() { } @Override - public synchronized void addRelation(String subject, KeywordRelation related, String relatedSubject) throws AccessDeniedException, - IOException, MalformedQueryException, QueryEvaluationException, GraphException { + public synchronized void addRelation(String subject, KeywordRelation related, String relatedSubject) throws AccessDeniedException { throw new UnsupportedOperationException(); } diff --git a/core/src/main/java/org/fao/geonet/kernel/DataManager.java b/core/src/main/java/org/fao/geonet/kernel/DataManager.java index 0ee36b1774e..4605016efd2 100644 --- a/core/src/main/java/org/fao/geonet/kernel/DataManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/DataManager.java @@ -3,7 +3,7 @@ //=== DataManager //=== //============================================================================= -//=== 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) //=== @@ -143,11 +143,6 @@ public void init(ServiceContext context, Boolean force) throws Exception { } } - @Deprecated - public synchronized void rebuildIndexXLinkedMetadata(final ServiceContext context) throws Exception { - metadataIndexer.rebuildIndexXLinkedMetadata(context); - } - @Deprecated public synchronized void rebuildIndexForSelection(final ServiceContext context, String bucket, boolean clearXlink) throws Exception { metadataIndexer.rebuildIndexForSelection(context, bucket, clearXlink); diff --git a/core/src/main/java/org/fao/geonet/kernel/SelectionManager.java b/core/src/main/java/org/fao/geonet/kernel/SelectionManager.java index cd400331964..a42a9e982e9 100644 --- a/core/src/main/java/org/fao/geonet/kernel/SelectionManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/SelectionManager.java @@ -23,13 +23,14 @@ package org.fao.geonet.kernel; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.search.SearchHit; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.constants.Edit; import org.fao.geonet.constants.Geonet; @@ -248,8 +249,9 @@ public void selectAll(String type, ServiceContext context, UserSession session) EsSearchManager searchManager = context.getBean(EsSearchManager.class); searchResponse = searchManager.query(request.get("query"), FIELDLIST_UUID, 0, maxhits); List uuidList = new ArrayList(); - for (SearchHit h : Arrays.asList(searchResponse.getHits().getHits())) { - uuidList.add((String) h.getSourceAsMap().get(Geonet.IndexFieldNames.UUID)); + ObjectMapper objectMapper = new ObjectMapper(); + for (Hit h : (List) searchResponse.hits().hits()) { + uuidList.add((String) objectMapper.convertValue(h.source(), Map.class).get(Geonet.IndexFieldNames.UUID)); } if (selection != null) { diff --git a/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java b/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java index d9937ed8958..efaeaf60a89 100644 --- a/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java +++ b/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java @@ -76,6 +76,7 @@ public class Thesaurus { private static final String DEFAULT_THESAURUS_NAMESPACE = "http://custom.shared.obj.ch/concept#"; private static final String RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + private static final String RDF_SCHEMA_NAMESPACE = "http://www.w3.org/2000/01/rdf-schema#"; private static final String SKOS_NAMESPACE = "http://www.w3.org/2004/02/skos/core#"; @@ -360,7 +361,8 @@ public boolean hasConceptScheme(String uri) { try { return performRequest(query).getRowCount() > 0; } catch (Exception e) { - Log.error(Geonet.THESAURUS_MAN, "Error retrieving concept scheme for " + thesaurusFile + ". Error is: " + e.getMessage()); + Log.error(Geonet.THESAURUS_MAN, + String.format("Error retrieving concept scheme for %s. Error is: %s", thesaurusFile, e.getMessage())); throw new RuntimeException(e); } } @@ -380,7 +382,8 @@ public List getConceptSchemes() { } return ret; } catch (Exception e) { - Log.error(Geonet.THESAURUS_MAN, "Error retrieving concept schemes for " + thesaurusFile + ". Error is: " + e.getMessage()); + Log.error(Geonet.THESAURUS_MAN, String.format( + "Error retrieving concept schemes for %s. Error is: %s", thesaurusFile, e.getMessage())); return Collections.emptyList(); } } @@ -452,8 +455,7 @@ public synchronized URI addElement(KeywordBean keyword) throws IOException, Acce /** * Remove keyword from thesaurus. */ - public synchronized Thesaurus removeElement(KeywordBean keyword) throws MalformedQueryException, - QueryEvaluationException, IOException, AccessDeniedException { + public synchronized Thesaurus removeElement(KeywordBean keyword) throws AccessDeniedException { String namespace = keyword.getNameSpaceCode(); String code = keyword.getRelativeCode(); @@ -518,8 +520,7 @@ private String toiso639_1_Lang(String lang) { * languages) and the coordinates will only be updated if they are non-empty * strings. */ - public synchronized URI updateElement(KeywordBean keyword, boolean replace) throws AccessDeniedException, IOException, - MalformedQueryException, QueryEvaluationException, GraphException { + public synchronized URI updateElement(KeywordBean keyword, boolean replace) throws AccessDeniedException { THESAURUS_SEARCH_CACHE.invalidateAll(); // Get thesaurus graph @@ -661,7 +662,7 @@ public Thesaurus updateCode(KeywordBean bean, String newcode) throws AccessDenie * Update concept code by creating URI from namespace and code. This is recommended when * thesaurus concept identifiers contains # eg. http://vocab.nerc.ac.uk/collection/P07/current#CFV13N44 */ - public synchronized Thesaurus updateCode(String namespace, String oldcode, String newcode) throws AccessDeniedException, IOException { + public synchronized Thesaurus updateCode(String namespace, String oldcode, String newcode) throws AccessDeniedException { Graph myGraph = repository.getGraph(); ValueFactory myFactory = myGraph.getValueFactory(); @@ -679,7 +680,7 @@ public synchronized Thesaurus updateCode(String namespace, String oldcode, Strin * * eg. http://vocab.nerc.ac.uk/collection/P07/current/CFV13N44/ */ - public synchronized Thesaurus updateCodeByURI(String olduri, String newuri) throws AccessDeniedException, IOException { + public synchronized Thesaurus updateCodeByURI(String olduri, String newuri) throws AccessDeniedException { Graph myGraph = repository.getGraph(); ValueFactory myFactory = myGraph.getValueFactory(); @@ -894,7 +895,11 @@ private void retrieveDublinCore(Element thesaurusEl) { // } private void retrieveMultiLingualTitles(Element thesaurusEl) { try { - String xpathTitles = "skos:ConceptScheme/dc:title[@xml:lang]|skos:ConceptScheme/dcterms:title[@xml:lang]|rdf:Description[rdf:type/@rdf:resource = 'http://www.w3.org/2004/02/skos/core#ConceptScheme']/dc:title[@xml:lang]"; + String xpathTitles = "skos:ConceptScheme/dc:title[@xml:lang]" + + "|skos:ConceptScheme/dcterms:title[@xml:lang]" + + "|skos:ConceptScheme/rdfs:label[@xml:lang]" + + "|skos:ConceptScheme/skos:prefLabel[@xml:lang]" + + "|rdf:Description[rdf:type/@rdf:resource = 'http://www.w3.org/2004/02/skos/core#ConceptScheme']/dc:title[@xml:lang]"; multilingualTitles.clear(); multilingualTitles.putAll(retrieveMultilingualField(thesaurusEl, xpathTitles)); } catch (Exception e) { @@ -944,25 +949,23 @@ private void retrieveThesaurusInformation(Path thesaurusFile, String defaultTitl try { Element thesaurusEl = Xml.loadFile(thesaurusFile); - List theNSs = new ArrayList<>(); - Namespace rdfNamespace = Namespace.getNamespace("rdf", RDF_NAMESPACE); - theNSs.add(rdfNamespace); - theNSs.add(Namespace.getNamespace("skos", SKOS_NAMESPACE)); - theNSs.add(Namespace.getNamespace("dc", DC_NAMESPACE)); - theNSs.add(Namespace.getNamespace("dcterms", DCTERMS_NAMESPACE)); + List theNSs = getThesaurusNamespaces(); this.defaultNamespace = null; retrieveMultiLingualTitles(thesaurusEl); retrieveDublinCore(thesaurusEl); Element titleEl = Xml.selectElement(thesaurusEl, - "skos:ConceptScheme/dc:title|skos:ConceptScheme/dcterms:title|" + - "skos:Collection/dc:title|skos:Collection/dcterms:title|" + - "rdf:Description/dc:title|rdf:Description/dcterms:title", theNSs); + "skos:ConceptScheme/dc:title|skos:ConceptScheme/dcterms:title" + + "|skos:ConceptScheme/rdfs:label|skos:ConceptScheme/skos:prefLabel" + + "|skos:Collection/dc:title|skos:Collection/dcterms:title" + + "|rdf:Description/dc:title|rdf:Description/dcterms:title", theNSs); if (titleEl != null) { this.title = titleEl.getValue(); - this.defaultNamespace = titleEl.getParentElement().getAttributeValue("about", rdfNamespace); + this.defaultNamespace = titleEl + .getParentElement() + .getAttributeValue("about", Namespace.getNamespace("rdf", RDF_NAMESPACE)); } else { this.title = defaultTitle; this.defaultNamespace = DEFAULT_THESAURUS_NAMESPACE; @@ -1027,11 +1030,13 @@ private void retrieveThesaurusInformation(Path thesaurusFile, String defaultTitl } if (Log.isDebugEnabled(Geonet.THESAURUS_MAN)) { - Log.debug(Geonet.THESAURUS_MAN, "Thesaurus information: " + this.title + " (" + this.date + ")"); + Log.debug(Geonet.THESAURUS_MAN, String.format( + "Thesaurus information: %s (%s)", this.title, this.date)); } } catch (Exception ex) { if (!ignoreMissingError) - Log.error(Geonet.THESAURUS_MAN, "Error getting thesaurus info for " + thesaurusFile + ". Error is: " + ex.getMessage()); + Log.error(Geonet.THESAURUS_MAN, String.format( + "Error getting thesaurus info for %s. Error is: %s", thesaurusFile, ex.getMessage())); } } @@ -1102,8 +1107,7 @@ public IsoLanguagesMapper getIsoLanguageMapper() { * @param subject the keyword that is related to the other keyword * @param related the relation between the two keywords */ - public synchronized void addRelation(String subject, KeywordRelation related, String relatedSubject) throws AccessDeniedException, IOException, - MalformedQueryException, QueryEvaluationException, GraphException { + public synchronized void addRelation(String subject, KeywordRelation related, String relatedSubject) throws AccessDeniedException { THESAURUS_SEARCH_CACHE.invalidateAll(); Graph myGraph = repository.getGraph(); @@ -1126,7 +1130,7 @@ public synchronized void addRelation(String subject, KeywordRelation related, St * @return keyword */ public KeywordBean getKeyword(String uri, String... languages) { - String cacheKey = "getKeyword" + uri + Arrays.stream(languages).collect(Collectors.joining("")); + String cacheKey = "getKeyword" + uri + String.join("", languages); Object cacheValue = THESAURUS_SEARCH_CACHE.getIfPresent(cacheKey); if (cacheValue != null) { return (KeywordBean) cacheValue; @@ -1370,6 +1374,7 @@ private ArrayList classifyTermWithNoBroaderTerms(KeywordBean term) private List getThesaurusNamespaces() { List theNSs = new ArrayList<>(); theNSs.add(Namespace.getNamespace("rdf", RDF_NAMESPACE)); + theNSs.add(Namespace.getNamespace("rdfs", RDF_SCHEMA_NAMESPACE)); theNSs.add(Namespace.getNamespace("skos", SKOS_NAMESPACE)); theNSs.add(Namespace.getNamespace("dc", DC_NAMESPACE)); theNSs.add(Namespace.getNamespace("dcterms", DCTERMS_NAMESPACE)); diff --git a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java index 762afc9b551..fd22881c373 100644 --- a/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java +++ b/core/src/main/java/org/fao/geonet/kernel/XmlSerializer.java @@ -54,22 +54,6 @@ * (id, data, lastChangeDate). */ public abstract class XmlSerializer { - private static InheritableThreadLocal configThreadLocal = new InheritableThreadLocal(); - - public static ThreadLocalConfiguration getThreadLocal(boolean setIfNotPresent) { - ThreadLocalConfiguration config = configThreadLocal.get(); - if (config == null && setIfNotPresent) { - config = new ThreadLocalConfiguration(); - configThreadLocal.set(config); - } - - return config; - } - - public static void clearThreadLocal() { - configThreadLocal.set(null); - } - public static void removeFilteredElement(Element metadata, final MetadataSchemaOperationFilter filter, List namespaces) throws JDOMException { @@ -82,6 +66,7 @@ public static void removeFilteredElement(Element metadata, List nodes = Xml.selectNodes(metadata, xpath, namespaces); + for (Object object : nodes) { if (object instanceof Element) { Element element = (Element) object; @@ -125,7 +110,7 @@ public boolean resolveXLinks() { return false; } - String xlR = _settingManager.getValue(Settings.SYSTEM_XLINKRESOLVER_ENABLE); + String xlR = _settingManager.getValue(Settings.SYSTEM_XLINKRESOLVER_ENABLE); if (xlR != null) { boolean isEnabled = xlR.equals("true"); if (isEnabled) Log.debug(Geonet.DATA_MANAGER, "XLink Resolver enabled."); @@ -217,7 +202,8 @@ public Element removeHiddenElements(boolean isIndexingTask, AbstractMetadata met } } } - if (filterEditOperationElements || (getThreadLocal(false) != null && getThreadLocal(false).forceFilterEditOperation)) { + + if (filterEditOperationElements) { removeFilteredElement(metadataXml, editFilter, namespaces); } } @@ -292,7 +278,7 @@ protected void deleteDb(String id) throws Exception { public abstract void delete(String id, ServiceContext context) throws Exception; - /* API to be overridden by extensions */ + /* API to be overridden by extensions */ public abstract void update(String id, Element xml, String changeDate, boolean updateDateStamp, String uuid, ServiceContext context) @@ -310,16 +296,4 @@ public abstract AbstractMetadata insert(AbstractMetadata metadata, Element dataX public abstract Element selectNoXLinkResolver(String id, boolean isIndexingTask, boolean applyOperationsFilters) throws Exception; - - public static class ThreadLocalConfiguration { - private boolean forceFilterEditOperation = false; - - public boolean isForceFilterEditOperation() { - return forceFilterEditOperation; - } - - public void setForceFilterEditOperation(boolean forceFilterEditOperation) { - this.forceFilterEditOperation = forceFilterEditOperation; - } - } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java index 3ee6843f0e2..f7ef10c9c7c 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataIndexer.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2011 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) //=== @@ -24,11 +24,9 @@ package org.fao.geonet.kernel.datamanager; import java.io.IOException; -import java.util.Calendar; import java.util.List; import org.fao.geonet.domain.AbstractMetadata; -import org.fao.geonet.kernel.search.ISearchManager; import org.fao.geonet.kernel.search.IndexingMode; import org.jdom.Element; import org.springframework.data.jpa.domain.Specification; @@ -65,11 +63,6 @@ public interface IMetadataIndexer { */ int batchDeleteMetadataAndUpdateIndex(Specification specification) throws Exception; - /** - * Search for all records having XLinks (ie. indexed with _hasxlinks flag), clear the cache and reindex all records found. - */ - void rebuildIndexXLinkedMetadata(ServiceContext context) throws Exception; - /** * Reindex all records in current selection. */ diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java index b5fb4cfbbe0..9b39ce6118b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataIndexer.java @@ -200,31 +200,6 @@ public int batchDeleteMetadataAndUpdateIndex(Specification toIndex = searchManager.getDocsWithXLinks(); - - if (Log.isDebugEnabled(Geonet.DATA_MANAGER)) - Log.debug(Geonet.DATA_MANAGER, "Will index " + toIndex.size() + " records with XLinks"); - if (toIndex.size() > 0) { - // clean XLink Cache so that cache and index remain in sync - Processor.clearCache(); - - ArrayList stringIds = new ArrayList(); - for (Integer id : toIndex) { - stringIds.add(id.toString()); - } - // execute indexing operation - batchIndexInThreadPool(context, stringIds); - } - } - /** * Reindex all records in current selection. */ @@ -350,9 +325,9 @@ public void indexMetadata(final String metadataId, List xlinks = Processor.getXLinks(md); if (xlinks.size() > 0) { fields.put(Geonet.IndexFieldNames.HASXLINKS, true); - StringBuilder sb = new StringBuilder(); for (Attribute xlink : xlinks) { fields.put(Geonet.IndexFieldNames.XLINK, xlink.getValue()); + fields.put(Geonet.IndexFieldNames.XLINK, xlink.getValue().replaceAll("local://srv/api/registries/entries/(.*)\\?.*", "$1")); } Processor.detachXLink(md, getServiceContext()); } else { @@ -408,10 +383,10 @@ public void indexMetadata(final String metadataId, if (!schemaManager.existsSchema(schema)) { fields.put(IndexFields.DRAFT, "n"); fields.put(IndexFields.INDEXING_ERROR_FIELD, true); - fields.put(IndexFields.INDEXING_ERROR_MSG, String.format( - "Schema '%s' is not registered in this catalog. Install it or remove those records", - schema - )); + fields.put(IndexFields.INDEXING_ERROR_MSG, + searchManager.createIndexingErrorMsgObject("indexingErrorMsg-schemaNotRegistered", + "error", + Map.of("record", metadataId, "schema", schema))); searchManager.index(null, md, indexKey, fields, metadataType, forceRefreshReaders, indexingMode); Log.error(Geonet.DATA_MANAGER, String.format( @@ -574,6 +549,7 @@ public void indexMetadata(final String metadataId, metadataId, indexingMode, System.currentTimeMillis() - start)); } + @Override public void indexMetadataPrivileges(String uuid, int id) throws Exception { Set operationFields = new HashSet<>(); diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java index 61ba2251061..f2f159c029c 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataManager.java @@ -867,6 +867,7 @@ private Element buildInfoElem(ServiceContext context, String id, String version) // add owner name java.util.Optional user = userRepository.findById(Integer.parseInt(owner)); if (user.isPresent()) { + addElement(info, Edit.Info.Elem.OWNERID, user.get().getId()); String ownerName = user.get().getName(); addElement(info, Edit.Info.Elem.OWNERNAME, ownerName); } @@ -1341,8 +1342,8 @@ public boolean isValid(Integer id) { } boolean hasReferencingMetadata(ServiceContext context, AbstractMetadata metadata) throws Exception { - StringBuilder query = new StringBuilder(String.format("xlink:*%s*", metadata.getUuid())); - return this.searchManager.query(query.toString(), null, 0, 0).getHits().getTotalHits().value > 0; + StringBuilder query = new StringBuilder(String.format("xlink:\"%s\"", metadata.getUuid())); + return this.searchManager.query(query.toString(), null, 0, 0).hits().total().value() > 0; } } diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java index 5d57875291d..47cfe3b5342 100644 --- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java +++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataOperations.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2011 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -167,7 +167,7 @@ public boolean setOperation(ServiceContext context, int mdId, int grpId, int opI */ @Override public boolean forceSetOperation(ServiceContext context, int mdId, int grpId, int opId) throws Exception { - Optional opAllowed = _getOperationAllowedToAdd(context, mdId, grpId, opId, false); + Optional opAllowed = getOperationAllowedToAddInternal(context, mdId, grpId, opId, false); if (opAllowed.isPresent()) { Log.trace(Geonet.DATA_MANAGER, "Operation is allowed"); @@ -199,11 +199,11 @@ public boolean forceSetOperation(ServiceContext context, int mdId, int grpId, in @Override public Optional getOperationAllowedToAdd(final ServiceContext context, final int mdId, final int grpId, final int opId) { - return _getOperationAllowedToAdd(context, mdId, grpId, opId, true); + return getOperationAllowedToAddInternal(context, mdId, grpId, opId, true); } - private Optional _getOperationAllowedToAdd(final ServiceContext context, final int mdId, final int grpId, - final int opId, boolean shouldCheckPermission) { + private Optional getOperationAllowedToAddInternal(final ServiceContext context, final int mdId, final int grpId, + final int opId, boolean shouldCheckPermission) { Log.trace(Geonet.DATA_MANAGER, "_getOperationAllowedToAdd(" + mdId + ", " + grpId + ", " + opId + ", " + shouldCheckPermission + ")"); final OperationAllowed operationAllowed = opAllowedRepo.findOneById_GroupIdAndId_MetadataIdAndId_OperationId(grpId, mdId, opId); @@ -340,14 +340,12 @@ public void copyDefaultPrivForGroup(ServiceContext context, String id, String gr setOperation(context, id, groupId, ReservedOperation.view); setOperation(context, id, groupId, ReservedOperation.notify); + setOperation(context, id, groupId, ReservedOperation.download); + setOperation(context, id, groupId, ReservedOperation.dynamic); // - // Restrictive: new and inserted records should not be editable, - // their resources can't be downloaded and any interactive maps can't be - // displayed by users in the same group + // Restrictive: new and inserted records should not be editable by users in the same group, if (fullRightsForGroup) { setOperation(context, id, groupId, ReservedOperation.editing); - setOperation(context, id, groupId, ReservedOperation.download); - setOperation(context, id, groupId, ReservedOperation.dynamic); } // Ultimately this should be configurable elsewhere } diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java b/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java index 77f7d18f00e..1200ff5e913 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/Importer.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2020 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -27,6 +27,7 @@ import com.google.common.collect.Maps; import jeeves.server.ServiceConfig; import jeeves.server.context.ServiceContext; +import org.apache.commons.lang.StringUtils; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.GeonetContext; import org.fao.geonet.MetadataResourceDatabaseMigration; @@ -52,9 +53,11 @@ import org.fao.geonet.utils.Xml; import org.fao.oaipmh.exceptions.BadArgumentException; import org.jdom.Element; +import org.jdom.JDOMException; import org.springframework.context.ApplicationContext; import javax.annotation.Nonnull; +import java.io.IOException; import java.io.InputStream; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -64,6 +67,12 @@ import static org.fao.geonet.domain.Localized.translationXmlToLangMap; public class Importer { + /** + * Private constructor to avoid instantiate the class. + */ + private Importer() { + } + @Deprecated public static List doImport(final Element params, final ServiceContext context, final Path mefFile, final Path stylePath) throws Exception { @@ -77,7 +86,7 @@ public static List doImport(final Element params, final ServiceContext c boolean validate = Util.getParam(params, Params.VALIDATE, "off").equals("on"); boolean assign = Util.getParam(params, "assign", "off").equals("on"); - return doImport(fileType, MEFLib.UuidAction.parse(uuidAction), style, source, isTemplate, new String[] { category }, groupId, + return doImport(fileType, MEFLib.UuidAction.parse(uuidAction), style, source, isTemplate, new String[]{category}, groupId, validate, assign, context, mefFile); } @@ -91,9 +100,9 @@ public static List doImport(String fileType, final MEFLib.UuidAction uui // Load preferred schema and set to iso19139 by default String preferredSchema = applicationContext.getBean(ServiceConfig.class).getValue("preferredSchema", "iso19139"); - final List metadataIdMap = new ArrayList(); - final List md = new ArrayList(); - final List fc = new ArrayList(); + final List metadataIdMap = new ArrayList<>(); + final List md = new ArrayList<>(); + final List fc = new ArrayList<>(); // Try to define MEF version from mef file not from parameter if (fileType.equals("mef")) { @@ -145,7 +154,7 @@ public void handleMetadataFiles(DirectoryStream metadataXmlFiles, Element Element metadataValidForImport; - Map> mdFiles = new HashMap>(); + Map> mdFiles = new HashMap<>(); for (Path file : metadataXmlFiles) { if (file != null && java.nio.file.Files.isRegularFile(file)) { Element metadata = Xml.loadFile(file); @@ -268,7 +277,7 @@ public void handleInfo(Element info, int index) throws Exception { throw new Exception("Unknown schema"); // Handle non MEF files insertion - if (info.getChildren().size() == 0) { + if (info.getChildren().isEmpty()) { if (category != null) { categs = new Element("categories"); for (String c : category) { @@ -351,7 +360,7 @@ public void handleInfo(Element info, int index) throws Exception { throw new Exception("Failed to import metadata with uuid '" + uuid + "'. " + e.getLocalizedMessage(), e); } - if (fc.size() != 0 && fc.get(index) != null) { + if (!fc.isEmpty() && fc.get(index) != null) { // UUID is set as @uuid in root element uuid = UUID.randomUUID().toString(); @@ -361,7 +370,10 @@ public void handleInfo(Element info, int index) throws Exception { // insert metadata // int userid = context.getUserSession().getUserIdAsInt(); - String group = null, docType = null, title = null, category = null; + String group = null; + String docType = null; + String title = null; + String category = null; boolean ufo = false; String fcId = dm .insertMetadata(context, "iso19110", fc.get(index), uuid, userid, group, source, isTemplate.codeString, docType, @@ -391,7 +403,8 @@ public void handleInfo(Element info, int index) throws Exception { final Element finalCategs = categs; final String finalGroupId = groupId; context.getBean(IMetadataManager.class).update(iMetadataId, new Updater() { - @Override public void apply(@Nonnull final Metadata metadata) { + @Override + public void apply(@Nonnull final Metadata metadata) { final MetadataDataInfo dataInfo = metadata.getDataInfo(); if (finalPopularity != null) { dataInfo.setPopularity(Integer.valueOf(finalPopularity)); @@ -487,10 +500,14 @@ public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List< DataManager dm = gc.getBean(DataManager.class); IMetadataManager metadataManager = gc.getBean(IMetadataManager.class); - if (uuid == null || uuid.equals("") || uuidAction == MEFLib.UuidAction.GENERATEUUID) { + if (StringUtils.isBlank(uuid) || uuidAction == MEFLib.UuidAction.GENERATEUUID) { String newuuid = UUID.randomUUID().toString(); source = null; + if (StringUtils.isNotBlank(uuid)) { + md.add(index, updateMetadataUuidReferences(md.get(index), uuid, newuuid)); + } + Log.debug(Geonet.MEF, "Replacing UUID " + uuid + " with " + newuuid); uuid = newuuid; @@ -521,7 +538,7 @@ public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List< String metadataId = ""; if (metadataExist && uuidAction == MEFLib.UuidAction.NOTHING) { throw new UnAuthorizedException("Record already exists. Change the import mode to overwrite or generating a new UUID.", null); - } else if (metadataExist && uuidAction == MEFLib.UuidAction.OVERWRITE){ + } else if (metadataExist && uuidAction == MEFLib.UuidAction.OVERWRITE) { if (isMdWorkflowEnable) { throw new UnAuthorizedException("Overwrite mode is not allowed when workflow is enabled. Use the metadata editor.", null); } @@ -544,7 +561,7 @@ public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List< } else { throw new UnAuthorizedException("User has no privilege to overwrite existing metadata", null); } - } else if (metadataExist && uuidAction == MEFLib.UuidAction.REMOVE_AND_REPLACE){ + } else if (metadataExist && uuidAction == MEFLib.UuidAction.REMOVE_AND_REPLACE) { if (isMdWorkflowEnable) { throw new UnAuthorizedException("Overwrite mode is not allowed when workflow is enabled. Use the metadata editor.", null); } @@ -576,7 +593,8 @@ private static String insertMetadata(String uuid, List md, String schem Log.debug(Geonet.MEF, "Adding metadata with uuid:" + uuid); int userid = context.getUserSession().getUserIdAsInt(); - String docType = null, category = null; + String docType = null; + String category = null; boolean ufo = false; String metadataId = metadataManager @@ -607,8 +625,8 @@ private static Group addPrivileges(final ServiceContext context, final DataManag @SuppressWarnings("unchecked") List list = privil.getChildren("group"); Group owner = null; - Set opAllowedToAdd = new HashSet(); - List groupsToAdd = new ArrayList(); + Set opAllowedToAdd = new HashSet<>(); + List groupsToAdd = new ArrayList<>(); for (Element group : list) { String grpName = group.getAttributeValue("name"); @@ -646,7 +664,7 @@ private static Set addOperations(final ServiceContext context, final int metadataId, final int grpId) { @SuppressWarnings("unchecked") List operations = group.getChildren("operation"); - Set toAdd = new HashSet(); + Set toAdd = new HashSet<>(); for (Element operation : operations) { String opName = operation.getAttributeValue("name"); @@ -672,6 +690,17 @@ private static Set addOperations(final ServiceContext context, return toAdd; } + /** + * Replace oldUuid references by newUuid. + * This will update metadata identifier, but also other usages + * which may be in graphicOverview URLs, resources identifier, + * metadata point of truth URL, ... + */ + private static Element updateMetadataUuidReferences(Element xml, String oldUuid, String newUuid) throws IOException, JDOMException { + String data = Xml.getString(xml); + return Xml.loadString(data.replace(oldUuid, newUuid), false); + } + } // ============================================================================= diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java index 04d8e9cff3c..b202ab051a7 100644 --- a/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java +++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEF2Exporter.java @@ -23,10 +23,11 @@ package org.fao.geonet.kernel.mef; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import com.fasterxml.jackson.databind.ObjectMapper; import jeeves.server.context.ServiceContext; import org.apache.commons.io.FileUtils; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.search.SearchHit; import org.fao.geonet.Constants; import org.fao.geonet.GeonetContext; import org.fao.geonet.ZipUtil; @@ -137,8 +138,9 @@ public static Path doExport(ServiceContext context, Set uuids, String mdSchema = null, mdTitle = null, mdAbstract = null, isHarvested = null; MetadataType mdType = null; - SearchHit[] hits = result.getHits().getHits(); - final Map source = hits[0].getSourceAsMap(); + List hits = result.hits().hits(); + ObjectMapper objectMapper = new ObjectMapper(); + final Map source = objectMapper.convertValue(hits.get(0).source(), Map.class); mdSchema = (String) source.get(Geonet.IndexFieldNames.SCHEMA); mdTitle = (String) source.get(Geonet.IndexFieldNames.RESOURCETITLE); mdAbstract = (String) source.get(Geonet.IndexFieldNames.RESOURCEABSTRACT); diff --git a/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java b/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java index 43017e4112f..e06b42e724b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/EsSearchManager.java @@ -23,6 +23,15 @@ package org.fao.geonet.kernel.search; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch.core.*; +import co.elastic.clients.elasticsearch.core.bulk.BulkOperation; +import co.elastic.clients.elasticsearch.core.bulk.UpdateOperation; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.indices.*; +import co.elastic.clients.elasticsearch.indices.ExistsRequest; +import co.elastic.clients.transport.endpoints.BooleanResponse; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -30,37 +39,18 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.action.ActionListener; -import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest; -import org.elasticsearch.action.bulk.BulkRequest; -import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.support.master.AcknowledgedResponse; -import org.elasticsearch.action.update.UpdateRequest; -import org.elasticsearch.action.update.UpdateResponse; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.indices.CreateIndexRequest; -import org.elasticsearch.client.indices.CreateIndexResponse; -import org.elasticsearch.client.indices.GetIndexRequest; -import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.index.query.QueryStringQueryBuilder; -import org.elasticsearch.index.reindex.DeleteByQueryRequest; -import org.elasticsearch.script.Script; -import org.elasticsearch.script.ScriptType; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; import org.fao.geonet.index.es.EsRestClient; -import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; import org.fao.geonet.kernel.SelectionManager; +import org.fao.geonet.kernel.datamanager.IMetadataIndexer; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.search.index.OverviewIndexFieldUpdater; import org.fao.geonet.kernel.setting.SettingInfo; @@ -82,8 +72,6 @@ import java.nio.file.StandardOpenOption; import java.util.*; -import static org.elasticsearch.rest.RestStatus.CREATED; -import static org.elasticsearch.rest.RestStatus.OK; import static org.fao.geonet.constants.Geonet.IndexFieldNames.IS_TEMPLATE; import static org.fao.geonet.kernel.search.IndexFields.INDEXING_ERROR_FIELD; import static org.fao.geonet.kernel.search.IndexFields.INDEXING_ERROR_MSG; @@ -181,10 +169,10 @@ public void setIndexType(String indexType) { } @Autowired - public EsRestClient client; + private EsRestClient client; @Autowired - OverviewIndexFieldUpdater overviewFieldUpdater; + private OverviewIndexFieldUpdater overviewFieldUpdater; private int commitInterval = 200; @@ -202,7 +190,7 @@ private Path getXSLTForIndexing(Path schemaDir, MetadataType metadataType) { if (!Files.exists(xsltForIndexing)) { throw new RuntimeException(String.format( "XSLT for schema indexing does not exist. Create file '%s'.", - xsltForIndexing.toString())); + xsltForIndexing)); } return xsltForIndexing; } @@ -226,14 +214,22 @@ private void addMDFields(Element doc, Path schemaDir, } catch (Exception e) { LOGGER.error("Indexing stylesheet contains errors: {} \n Marking the metadata as _indexingError=1 in index", e.getMessage()); doc.addContent(new Element(INDEXING_ERROR_FIELD).setText("true")); - doc.addContent(new Element(INDEXING_ERROR_MSG).setText("GNIDX-XSL||" + e.getMessage())); + doc.addContent(createIndexingErrorMsgElement("indexingErrorMsg-indexingStyleSheetError", "error", + Map.of("message", e.getMessage()))); doc.addContent(new Element(IndexFields.DRAFT).setText("n")); } } private void addMoreFields(Element doc, Multimap fields) { - fields.entries().forEach(e -> doc.addContent(new Element(e.getKey()) - .setText(String.valueOf(e.getValue())))); + ArrayList objectFields = Lists.newArrayList(INDEXING_ERROR_MSG); + fields.entries().forEach(e -> { + Element newElement = new Element(e.getKey()) + .setText(String.valueOf(e.getValue())); + if(objectFields.contains(e.getKey())) { + newElement.setAttribute("type", "object"); + } + doc.addContent(newElement); + }); } public Element makeField(String name, String value) { @@ -249,9 +245,8 @@ public void init(boolean dropIndexFirst, Optional> indices) throws if (indexList != null) { indexList.keySet().forEach(e -> { try { - if (indices.isPresent() ? - indices.get().contains(e) : - true) { + if (!indices.isPresent() || + indices.get().contains(e)) { createIndex(e, indexList.get(e), dropIndexFirst); } } catch (IOException ex) { @@ -268,47 +263,52 @@ public void init(boolean dropIndexFirst, Optional> indices) throws private void createIndex(String indexId, String indexName, boolean dropIndexFirst) throws IOException { if (dropIndexFirst) { try { - DeleteIndexRequest request = new DeleteIndexRequest(indexName); - AcknowledgedResponse deleteIndexResponse = client.getClient().indices().delete(request, RequestOptions.DEFAULT); - if (deleteIndexResponse.isAcknowledged()) { - LOGGER.debug("Index '{}' removed.", new Object[]{indexName}); + DeleteIndexRequest deleteIndexRequest = DeleteIndexRequest.of( + b -> b.index(indexName) + ); + + DeleteIndexResponse deleteIndexResponse = client.getClient().indices().delete(deleteIndexRequest); + if (deleteIndexResponse.acknowledged()) { + LOGGER.debug("Index '{}' removed.", indexName); } } catch (Exception e) { // index does not exist ? - LOGGER.debug("Error during index '{}' removal. Error is: {}", new Object[]{indexName, e.getMessage()}); + LOGGER.debug("Error during index '{}' removal. Error is: {}", indexName, e.getMessage()); } } // Check index exist first - GetIndexRequest request = new GetIndexRequest(indexName); + ExistsRequest existsRequest = ExistsRequest.of( + b -> b.index(indexName) + ); + try { - boolean exists = client.getClient().indices().exists(request, RequestOptions.DEFAULT); - if (exists && !dropIndexFirst) { + BooleanResponse exists = client.getClient().indices().exists(existsRequest); + if (exists.value() && !dropIndexFirst) { return; } - - if (!exists || dropIndexFirst) { + if (!exists.value() || dropIndexFirst) { // Check version of the index - how ? // Create it if not Path indexConfiguration = dataDirectory.getIndexConfigDir().resolve(indexId + ".json"); if (Files.exists(indexConfiguration)) { - String configuration; - try (InputStream is = Files.newInputStream(indexConfiguration, StandardOpenOption.READ)) { - configuration = IOUtils.toString(is); - } - - CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName); - createIndexRequest.source(configuration, XContentType.JSON); - CreateIndexResponse createIndexResponse = client.getClient().indices().create(createIndexRequest, RequestOptions.DEFAULT); - if (createIndexResponse.isAcknowledged()) { - LOGGER.debug("Index '{}' created", new Object[]{indexName}); - } else { - final String message = String.format("Index '%s' was not created. Error is: %s", indexName, createIndexResponse.toString()); - LOGGER.error(message); - throw new IllegalStateException(message); + try (InputStream is = Files.newInputStream(indexConfiguration, StandardOpenOption.READ)) { + CreateIndexRequest createIndexRequest = CreateIndexRequest.of( + b -> b.index(indexName).withJson(is) + ); + + CreateIndexResponse createIndexResponse = client.getClient().indices().create(createIndexRequest); + + if (createIndexResponse.acknowledged()) { + LOGGER.debug("Index '{}' created", indexName); + } else { + final String message = String.format("Index '%s' was not created. Error is: %s", indexName, createIndexResponse); + LOGGER.error(message); + throw new IllegalStateException(message); + } } } else { throw new FileNotFoundException(String.format( @@ -317,10 +317,13 @@ private void createIndex(String indexId, String indexName, boolean dropIndexFirs indexName)); } } + } catch (ElasticsearchException ex) { + LOGGER.error(ex.getMessage(), ex); + throw new IOException(ex.getMessage()); } catch (Exception cnce) { final String message = String.format("Could not connect to index '%s'. Error is %s. Is the index server up and running?", defaultIndex, cnce.getMessage()); - LOGGER.error(message); + LOGGER.error(message, cnce); throw new IOException(message); } } @@ -331,8 +334,14 @@ public void end() { public UpdateResponse updateFields(String id, Map fields) throws IOException { fields.put(Geonet.IndexFieldNames.INDEXING_DATE, new Date()); - UpdateRequest updateRequest = new UpdateRequest(defaultIndex, id).doc(fields); - return client.getClient().update(updateRequest, RequestOptions.DEFAULT); + + UpdateRequest updateRequest = UpdateRequest.of( + b -> b.index(defaultIndex) + .id(id) + .doc(fields) + ); + + return client.getClient().update(updateRequest, Void.class); } public BulkResponse updateFields(String id, Multimap fields, Set fieldsToRemove) throws IOException { @@ -342,36 +351,62 @@ public BulkResponse updateFields(String id, Multimap fields, Set } public BulkResponse updateFields(String id, Map fieldMap, Set fieldsToRemove) throws IOException { fieldMap.put(Geonet.IndexFieldNames.INDEXING_DATE, new Date()); - BulkRequest bulkrequest = new BulkRequest(); - StringBuilder script = new StringBuilder(); - fieldsToRemove.forEach(f -> - script.append(String.format("ctx._source.remove('%s');", f))); - UpdateRequest deleteFieldRequest = - new UpdateRequest(defaultIndex, id).script(new Script(ScriptType.INLINE, - "painless", - script.toString(), - Collections.emptyMap())); - bulkrequest.add(deleteFieldRequest); - UpdateRequest addFieldRequest = new UpdateRequest(defaultIndex, id) - .doc(fieldMap); - bulkrequest.add(addFieldRequest); - return client.getClient().bulk(bulkrequest, RequestOptions.DEFAULT); + StringBuilder scriptSource = new StringBuilder(); + fieldsToRemove.forEach(f -> + scriptSource.append(String.format("ctx._source.remove('%s');", f))); + + UpdateOperation deleteFieldsOperation = UpdateOperation.of( + b -> b.id(id) + .index(defaultIndex) + .action(action -> action + .scriptedUpsert(true) + .upsert(Map.of()) + .script(script -> script + .inline(inlineScript -> inlineScript + .lang("painless") + .source(scriptSource.toString()) + ) + ) + ) + ); + + UpdateOperation addFieldRequestOperation = UpdateOperation.of( + b -> b.id(id) + .index(defaultIndex) + .action(action -> action.doc(fieldMap)) + ); + + List bulkOperationList = new ArrayList<>(); + bulkOperationList.add(BulkOperation.of(b -> b.update(deleteFieldsOperation))); + bulkOperationList.add(BulkOperation.of(b -> b.update(addFieldRequestOperation))); + + BulkRequest bulkRequest = BulkRequest.of( + b -> b.index(defaultIndex) + .operations(bulkOperationList) + ); + + return client.getClient().bulk(bulkRequest); } public void updateFieldsAsynch(String id, Map fields) { fields.put(Geonet.IndexFieldNames.INDEXING_DATE, new Date()); - UpdateRequest request = new UpdateRequest(defaultIndex, id).doc(fields); - ActionListener listener = new ActionListener() { - @Override - public void onResponse(UpdateResponse updateResponse) { - } - @Override - public void onFailure(Exception e) { - } - }; - client.getClient().updateAsync(request, RequestOptions.DEFAULT, listener); + UpdateRequest updateRequest = UpdateRequest.of( + b -> b.index(defaultIndex) + .id(id) + .doc(fields) + ); + + client.getAsynchClient() + .update(updateRequest, ObjectNode.class) + .whenComplete((response, exception) -> { + if (exception != null) { + LOGGER.error("Failed to index {}", exception); + } else { + LOGGER.info("Updated fields for document {}", id); + } + }); } public UpdateResponse updateField(String id, String field, Object value) throws Exception { @@ -436,7 +471,7 @@ public void index(Path schemaDir, Element metadata, String id, private void sendDocumentsToIndex() { Map documents = new HashMap<>(listOfDocumentsToIndex); listOfDocumentsToIndex.clear(); - if (documents.size() > 0) { + if (!documents.isEmpty()) { try { final BulkResponse bulkItemResponses = client .bulkRequest(defaultIndex, documents); @@ -444,7 +479,7 @@ private void sendDocumentsToIndex() { } catch (Exception e) { LOGGER.error( "An error occurred while indexing {} documents in current indexing list. Error is {}.", - new Object[]{listOfDocumentsToIndex.size(), e.getMessage()}); + listOfDocumentsToIndex.size(), e.getMessage()); } finally { // TODO: Trigger this async ? documents.keySet().forEach(uuid -> overviewFieldUpdater.process(uuid)); @@ -454,22 +489,21 @@ private void sendDocumentsToIndex() { private void checkIndexResponse(BulkResponse bulkItemResponses, Map documents) throws IOException { - if (bulkItemResponses.hasFailures()) { - Map listErrorOfDocumentsToIndex = new HashMap<>(bulkItemResponses.getItems().length); + if (bulkItemResponses.errors()) { + Map listErrorOfDocumentsToIndex = new HashMap<>(bulkItemResponses.items().size()); List errorDocumentIds = new ArrayList<>(); // Add information in index that some items were not properly indexed - Arrays.stream(bulkItemResponses.getItems()).forEach(e -> { - if (e.status() != OK - && e.status() != CREATED) { - errorDocumentIds.add(e.getId()); + bulkItemResponses.items().forEach(e -> { + if (e.error() != null) { + errorDocumentIds.add(e.id()); ObjectMapper mapper = new ObjectMapper(); ObjectNode docWithErrorInfo = mapper.createObjectNode(); - String resourceTitle = String.format("Document #%s", e.getId()); + String resourceTitle = String.format("Document #%s", e.id()); String id = ""; String uuid = ""; String isTemplate = ""; - String failureDoc = documents.get(e.getId()); + String failureDoc = documents.get(e.id()); try { JsonNode node = mapper.readTree(failureDoc); resourceTitle = node.get("resourceTitleObject").get("default").asText(); @@ -485,27 +519,27 @@ private void checkIndexResponse(BulkResponse bulkItemResponses, docWithErrorInfo.put(IndexFields.DRAFT, "n"); docWithErrorInfo.put(INDEXING_ERROR_FIELD, true); ArrayNode errors = docWithErrorInfo.putArray(INDEXING_ERROR_MSG); - errors.add(e.getFailureMessage()); + errors.add(createIndexingErrorMsgObject(e.error().reason(), "error", Map.of())); // TODO: Report the JSON which was causing the error ? LOGGER.error("Document with error #{}: {}.", - new Object[]{e.getId(), e.getFailureMessage()}); + e.id(), e.error().reason()); LOGGER.error(failureDoc); try { - listErrorOfDocumentsToIndex.put(e.getId(), mapper.writeValueAsString(docWithErrorInfo)); + listErrorOfDocumentsToIndex.put(e.id(), mapper.writeValueAsString(docWithErrorInfo)); } catch (JsonProcessingException e1) { LOGGER.error("Generated document for the index is not properly formatted. Check document #{}: {}.", - new Object[]{e.getId(), e1.getMessage()}); + e.id(), e1.getMessage()); } } }); - if (listErrorOfDocumentsToIndex.size() > 0) { + if (!listErrorOfDocumentsToIndex.isEmpty()) { BulkResponse response = client.bulkRequest(defaultIndex, listErrorOfDocumentsToIndex); - if (response.status().getStatus() != 201) { + if (response.errors()) { LOGGER.error("Failed to save error documents {}.", - new Object[]{Arrays.toString(errorDocumentIds.toArray())}); + Arrays.toString(errorDocumentIds.toArray())); } } } @@ -611,14 +645,14 @@ public ObjectNode documentToJson(Element xml) { for (Element currentField : fields) { String name = currentField.getName(); - // JSON object may be generated in the XSL processing. - // In such case an object type attribute is set. - boolean isObject = "object".equals(currentField.getAttributeValue("type")); - if (elementNames.contains(name)) { continue; } + // JSON object may be generated in the XSL processing. + // In such case an object type attribute is set. + boolean isObject = "object".equals(currentField.getAttributeValue("type")); + // Register list of already processed names elementNames.add(name); @@ -629,16 +663,24 @@ public ObjectNode documentToJson(Element xml) { || arrayFields.contains(propertyName) || propertyName.endsWith("DateForResource") || propertyName.startsWith("cl_"); - if (isArray) { + + if (name.equals("geom")) { + try { + doc.set("geom", mapper.readTree(nodeElements.get(0).getTextNormalize())); + } catch (IOException e) { + LOGGER.error("Parsing invalid geometry for JSON node {}. Error is: {}", + nodeElements.get(0).getTextNormalize(), e.getMessage()); + } + } else if (isArray) { ArrayNode arrayNode = doc.putArray(propertyName); for (Element node : nodeElements) { if (isObject) { try { arrayNode.add( - mapper.readTree(node.getText())); + mapper.readTree(node.getTextNormalize())); } catch (IOException e) { LOGGER.error("Parsing invalid JSON node {} for property {}. Error is: {}", - new Object[]{node.getTextNormalize(), propertyName, e.getMessage()}); + node.getTextNormalize(), propertyName, e.getMessage()); } } else { arrayNode.add( @@ -649,20 +691,7 @@ public ObjectNode documentToJson(Element xml) { } } - continue; - } - - if (name.equals("geom")) { - try { - doc.set("geom", mapper.readTree(nodeElements.get(0).getTextNormalize())); - } catch (IOException e) { - LOGGER.error("Parsing invalid geometry for JSON node {}. Error is: {}", - new Object[]{nodeElements.get(0).getTextNormalize(), e.getMessage()}); - } - continue; - } - - if (isObject) { + } else if (isObject) { try { doc.set(propertyName, mapper.readTree( @@ -670,7 +699,7 @@ public ObjectNode documentToJson(Element xml) { )); } catch (IOException e) { LOGGER.error("Parsing invalid JSON node {} for property {}. Error is: {}", - new Object[]{nodeElements.get(0).getTextNormalize(), propertyName, e.getMessage()}); + nodeElements.get(0).getTextNormalize(), propertyName, e.getMessage()); } } else { doc.put(propertyName, @@ -703,9 +732,9 @@ public void forceIndexChanges() { } @Override - public boolean rebuildIndex(ServiceContext context, boolean xlinks, + public boolean rebuildIndex(ServiceContext context, boolean reset, String bucket) throws Exception { - DataManager dataMan = context.getBean(DataManager.class); + IMetadataIndexer metadataIndexer = context.getBean(IMetadataIndexer.class); IMetadataUtils metadataRepository = context.getBean(IMetadataUtils.class); if (reset) { @@ -736,7 +765,7 @@ public boolean rebuildIndex(ServiceContext context, boolean xlinks, } } for (String id : listOfIdsToIndex) { - dataMan.indexMetadata(id + "", false); + metadataIndexer.indexMetadata(id, false, IndexingMode.full); } } else { final Specification metadataSpec = @@ -746,7 +775,7 @@ public boolean rebuildIndex(ServiceContext context, boolean xlinks, Specification.where(metadataSpec) ); for (Integer id : metadataIds) { - dataMan.indexMetadata(id + "", false); + metadataIndexer.indexMetadata(id + "", false, IndexingMode.full); } } sendDocumentsToIndex(); @@ -761,7 +790,7 @@ public SearchResponse query(String luceneQuery, String filterQuery, int startPos return client.query(defaultIndex, luceneQuery, filterQuery, new HashSet<>(), new HashMap<>(), startPosition, maxRecords); } - public SearchResponse query(String luceneQuery, String filterQuery, int startPosition, int maxRecords, List> sort) throws Exception { + public SearchResponse query(String luceneQuery, String filterQuery, int startPosition, int maxRecords, List sort) throws Exception { return client.query(defaultIndex, luceneQuery, filterQuery, new HashSet<>(), new HashMap<>(), startPosition, maxRecords, sort); } @@ -777,7 +806,7 @@ public SearchResponse query(String luceneQuery, String filterQuery, Set } public SearchResponse query(JsonNode jsonRequest, Set includedFields, - int from, int size, List> sort) throws Exception { + int from, int size, List sort) throws Exception { // TODO: Review postFilterBuilder return client.query(defaultIndex, jsonRequest, null, includedFields, new HashMap<>(), from, size, sort); } @@ -797,26 +826,6 @@ public void clearIndex() throws Exception { client.deleteByQuery(defaultIndex, "*:*"); } -// public void iterateQuery(SolrQuery params, final Consumer callback) throws IOException, SolrServerException { -// final MutableLong pos = new MutableLong(0); -// final MutableLong last = new MutableLong(1); -// while (pos.longValue() < last.longValue()) { -// params.setStart(pos.intValue()); -// client.queryAndStreamResponse(params, new StreamingResponseCallback() { -// @Override -// public void streamSolrDocument(SolrDocument doc) { -// pos.add(1); -// callback.accept(doc); -// } -// -// @Override -// public void streamDocListInfo(long numFound, long start, Float maxScore) { -// last.setValue(numFound); -// } -// }); -// } -// } - static ImmutableSet docsChangeIncludedFields; static { @@ -837,10 +846,15 @@ public Map getDocsChangeDate() throws Exception { final SearchResponse response = client.query(defaultIndex, "*", null, docsChangeIncludedFields, from, size); - response.getHits().forEach(r -> docs.put(r.getId(), (String) r.getSourceAsMap().get(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE))); + ObjectMapper objectMapper = new ObjectMapper(); + + response.hits().hits().forEach(r -> { + Hit h = (Hit) r; + + docs.put(h.id(), (String) objectMapper.convertValue(h.source(), Map.class).get(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE)); + }); } catch (Exception e) { - LOGGER.error("Error while collecting all documents: {}", e.getMessage()); - e.printStackTrace(); + LOGGER.error("Error while collecting all documents: {}", e.getMessage(), e); throw e; } return docs; @@ -854,73 +868,26 @@ public ISODate getDocChangeDate(String mdId) throws Exception { final SearchResponse response = client.query(defaultIndex, "_id:" + mdId, null, docsChangeIncludedFields, from, size); - if (response.getHits().getTotalHits().value == 1) { + if (response.hits().hits().size() == 1) { + Hit hit = (Hit) response.hits().hits().get(0); + ObjectMapper objectMapper = new ObjectMapper(); + String date = - (String) response.getHits().getAt(0).getSourceAsMap().get(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE); + (String) objectMapper.convertValue(hit.source(), Map.class).get(Geonet.IndexFieldNames.DATABASE_CHANGE_DATE); return date != null ? new ISODate(date) : null; } else { return null; } } -// public SolrDocument getDocFieldValue(String query, String... field) throws IOException, SolrServerException { -// final SolrQuery params = new SolrQuery(query); -// params.setFilterQueries(DOC_TYPE + ":metadata"); -// params.setFields(field); -// QueryResponse response = client.query(params); -// final SolrDocumentList results = response.getResults(); -// if (results.size() == 0) { -// return null; -// } else { -// return results.get(0); -// } -// } -// -// public SolrDocumentList getDocsFieldValue(String query, String... field) throws IOException, SolrServerException { -// final SolrQuery params = new SolrQuery(query); -// params.setFilterQueries(DOC_TYPE + ":metadata"); -// params.setFields(field); -// QueryResponse response = client.query(params); -// return response.getResults(); -// } -// -// public List getDocsUuids(String query, Integer rows) throws IOException, SolrServerException { -// final SolrQuery solrQuery = new SolrQuery(query == null ? "*:*" : query); -// solrQuery.setFilterQueries(DOC_TYPE + ":metadata"); -// solrQuery.setFields(IndexFields.UUID); -// if (rows != null) { -// solrQuery.setRows(rows); -// } -// final List result = new ArrayList<>(); -// iterateQuery(solrQuery, doc -> -// result.add(doc.getFieldValue(IndexFields.UUID).toString())); -// return result; -// } - - @Override - public Set getDocsWithXLinks() throws Exception { -// final SolrQuery params = new SolrQuery("*:*"); -// params.setFilterQueries(DOC_TYPE + ":metadata"); -// params.setFilterQueries(Geonet.IndexFieldNames.HASXLINKS + ":1"); -// params.setFields(ID); -// Set result = new HashSet<>(); -// iterateQuery(params, -// doc -> result.add(convertInteger(doc.getFieldValue(ID)))); - return Collections.emptySet(); - } - @Override public void delete(String txt) throws Exception { - DeleteByQueryRequest request = new DeleteByQueryRequest(); - request.indices(defaultIndex); - request.setQuery(new QueryStringQueryBuilder(txt)); - request.setRefresh(true); - client.getClient().deleteByQuery(request, RequestOptions.DEFAULT); + client.deleteByQuery(defaultIndex, txt); } @Override public void delete(List metadataIds) throws Exception { - metadataIds.stream().forEach(metadataId -> { + metadataIds.forEach(metadataId -> { try { this.delete(String.format("+id:%d", metadataId)); } catch (Exception e) { @@ -929,22 +896,12 @@ public void delete(List metadataIds) throws Exception { }); } - @Override - public long getNumDocs() throws Exception { - return getNumDocs(""); - } - public long getNumDocs(String query) throws Exception { if (StringUtils.isBlank(query)) { query = "*:*"; } - - int from = 0; - SettingInfo si = ApplicationContextHolder.get().getBean(SettingInfo.class); - int size = Integer.parseInt(si.getSelectionMaxRecords()); - - final SearchResponse response = client.query(defaultIndex, query, null, docsChangeIncludedFields, from, size); - return response.getHits().getTotalHits().value; + return client.query(defaultIndex, query, null, new HashSet<>(), 0, 0) + .hits().total().value(); } public EsRestClient getClient() { @@ -978,4 +935,54 @@ public static String analyzeField(String analyzer, public boolean isIndexing() { return listOfDocumentsToIndex.size() > 0; } + + public boolean isIndexWritable(String indexName) throws IOException, ElasticsearchException { + String indexBlockRead = "index.blocks.read_only_allow_delete"; + + GetIndicesSettingsRequest request = GetIndicesSettingsRequest.of( + b -> b.index(indexName) + .name(indexBlockRead) + ); + + GetIndicesSettingsResponse settings = this.client.getClient() + .indices().getSettings(request); + + IndexState indexState = settings.get(indexBlockRead); + + return (indexState != null) && "true".equals(indexState.toString()); + } + + + /** + * Make a JSON Object that properly represents an indexingErrorMsg, to be used in the index. + * + * @param type either 'error' or 'warning' + * @param string a string that is translatable (see, e.g., en-search.json) + * @param values values that replace the placeholders in the `string` parameter + * @return a json object that represents an indexingErrorMsg + */ + public ObjectNode createIndexingErrorMsgObject(String string, String type, Map values) { + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode indexingErrorMsg = objectMapper.createObjectNode(); + indexingErrorMsg.put("string", string); + indexingErrorMsg.put("type", type); + ObjectNode valuesObject = objectMapper.createObjectNode(); + values.forEach((k,v) -> valuesObject.put(k, String.valueOf(v))); + indexingErrorMsg.set("values", valuesObject); + return indexingErrorMsg; + } + + /** + * Create an Element that represents an indexingErrorMsg object, to be used in the index. + * + * @param type either 'error' or 'warning' + * @param string a string that is translatable (see, e.g., en-search.json) + * @param values values that replace the placeholders in the `string` parameter + * @return an Element that represents an indexingErrorMsg + */ + public Element createIndexingErrorMsgElement(String string, String type, Map values) { + return new Element(INDEXING_ERROR_MSG) + .setText(createIndexingErrorMsgObject(string, type, values).toString()) + .setAttribute("type", "object"); + } } diff --git a/core/src/main/java/org/fao/geonet/kernel/search/ISearchManager.java b/core/src/main/java/org/fao/geonet/kernel/search/ISearchManager.java index a8eeafc0aac..f7d4959953b 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/ISearchManager.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/ISearchManager.java @@ -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) * @@ -23,6 +23,7 @@ package org.fao.geonet.kernel.search; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; import com.google.common.collect.Multimap; import jeeves.server.context.ServiceContext; import org.fao.geonet.domain.ISODate; @@ -34,7 +35,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; /** * Base interface for the search (Lucene or Solr). @@ -65,12 +65,9 @@ void index(Path schemaDir, Element metadata, String id, Multimap * Rebuilds the Lucene index. If xlink or from selection parameters are defined, reindex a * subset of record. Otherwise reindex all records. * - * @param xlinks Search all docs with XLinks, clear the XLinks cache and index all - * records found. * @param bucket Reindex all records from selection bucket. */ boolean rebuildIndex(ServiceContext context, - boolean xlinks, boolean reset, String bucket) throws Exception; @@ -78,8 +75,6 @@ boolean rebuildIndex(ServiceContext context, ISODate getDocChangeDate(String mdId) throws Exception; - Set getDocsWithXLinks() throws Exception; - /** * deletes a document. */ @@ -90,7 +85,5 @@ boolean rebuildIndex(ServiceContext context, */ void delete(List metadataIds) throws Exception; - long getNumDocs() throws Exception; - - Element makeField(String fieldName, String fieldValue); + boolean isIndexWritable(String indexName) throws IOException, ElasticsearchException; } diff --git a/core/src/main/java/org/fao/geonet/kernel/search/index/OverviewIndexFieldUpdater.java b/core/src/main/java/org/fao/geonet/kernel/search/index/OverviewIndexFieldUpdater.java index 19944c779e4..fdf7583e8db 100644 --- a/core/src/main/java/org/fao/geonet/kernel/search/index/OverviewIndexFieldUpdater.java +++ b/core/src/main/java/org/fao/geonet/kernel/search/index/OverviewIndexFieldUpdater.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -23,8 +23,10 @@ package org.fao.geonet.kernel.search.index; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.action.search.SearchResponse; import org.fao.geonet.kernel.search.EsSearchManager; import org.fao.geonet.util.XslUtil; import org.springframework.beans.factory.annotation.Autowired; @@ -61,11 +63,13 @@ private void processOverview(String id) { SearchResponse response = null; try { response = searchManager.query(String.format( - "+id:\"%s\" _exists_:overview.url -_exists_:overview.data", + "+_id:\"%s\" _exists_:overview.url -_exists_:overview.data", id), null, source, 0, 1); - response.getHits().forEach(hit -> { + ObjectMapper objectMapper = new ObjectMapper(); + response.hits().hits().forEach(h -> { + Hit hit = (Hit) h; AtomicBoolean updates = new AtomicBoolean(false); - Map fields = hit.getSourceAsMap(); + Map fields = objectMapper.convertValue(hit.source(), Map.class); getHitOverviews(fields) .stream() .forEach(overview -> { diff --git a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/GeonetworkOidcUserService.java b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/GeonetworkOidcUserService.java index 504c0a8fac8..97a7b917630 100644 --- a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/GeonetworkOidcUserService.java +++ b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/GeonetworkOidcUserService.java @@ -22,9 +22,11 @@ */ package org.fao.geonet.kernel.security.openidconnect; +import org.fao.geonet.kernel.security.GeonetworkAuthenticationProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest; import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService; import org.springframework.security.oauth2.core.OAuth2AuthenticationException; @@ -55,12 +57,33 @@ public class GeonetworkOidcUserService extends OidcUserService { @Autowired RoleHierarchy roleHierarchy; + @Autowired + GeonetworkAuthenticationProvider geonetworkAuthenticationProvider; + + @Autowired + protected SimpleOidcUserFactory simpleOidcUserFactory; + @Override public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser user = super.loadUser(userRequest); + Collection authorities; + + if (!oidcConfiguration.isUpdateProfile()) { + // Retrieve the authorities from the local user + try { + SimpleOidcUser simpleUser = simpleOidcUserFactory.create(user.getAttributes()); + UserDetails userDetails = geonetworkAuthenticationProvider.loadUserByUsername(simpleUser.getUsername()); + + authorities = userDetails.getAuthorities(); + } catch (Exception ex) { + authorities = createAuthorities(user); + } + } else { + authorities = createAuthorities(user); + } + OidcUserInfo userInfo = user.getUserInfo(); - Collection authorities = createAuthorities(user); //get the user name from a specific attribute (if specified) or use default. String userNameAttributeName = userRequest.getClientRegistration() diff --git a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/OidcUser2GeonetworkUser.java b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/OidcUser2GeonetworkUser.java index 3c0cf6f3d78..34a8c813d52 100644 --- a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/OidcUser2GeonetworkUser.java +++ b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/OidcUser2GeonetworkUser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Food and Agriculture Organization of the + * Copyright (C) 2023 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -76,6 +76,12 @@ public UserDetails getUserDetails(Map attributes, boolean withDbUpdate) throws E if (!StringUtils.hasText(simpleUser.getUsername())) return null; + if (!oidcConfiguration.isUpdateProfile()) { + // SimpleOidcUser.updateUser assigns the user profile to the OpenId user profile, unless + // SimpleOidcUser.profile is empty. Force the empty value, to avoid the assignment. + simpleUser.setProfile(""); + } + User user; boolean newUserFlag = false; try { @@ -90,8 +96,9 @@ public UserDetails getUserDetails(Map attributes, boolean withDbUpdate) throws E simpleUser.updateUser(user); // copy attributes from the IDToken to the GN user Map> profileGroups = oidcRoleProcessor.getProfileGroups(attributes); - user.setProfile(oidcRoleProcessor.getProfile(attributes)); - + if (newUserFlag || oidcConfiguration.isUpdateProfile()) { + user.setProfile(oidcRoleProcessor.getProfile(attributes)); + } //Apply changes to database is required. if (withDbUpdate) { @@ -124,6 +131,12 @@ public UserDetails getUserDetails(OidcIdToken idToken, Map attributes, boolean w if (!StringUtils.hasText(simpleUser.getUsername())) return null; + if (!oidcConfiguration.isUpdateProfile()) { + // SimpleOidcUser.updateUser assigns the user profile to the OpenId user profile, unless + // SimpleOidcUser.profile is empty. Force the empty value, to avoid the assignment. + simpleUser.setProfile(""); + } + User user; boolean newUserFlag = false; try { @@ -138,7 +151,9 @@ public UserDetails getUserDetails(OidcIdToken idToken, Map attributes, boolean w simpleUser.updateUser(user); // copy attributes from the IDToken to the GN user Map> profileGroups = oidcRoleProcessor.getProfileGroups(idToken); - user.setProfile(oidcRoleProcessor.getProfile(idToken)); + if (newUserFlag || oidcConfiguration.isUpdateProfile()) { + user.setProfile(oidcRoleProcessor.getProfile(idToken)); + } //Apply changes to database is required. diff --git a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCache.java b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCache.java index 4e75a1282b2..15252009821 100644 --- a/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCache.java +++ b/core/src/main/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCache.java @@ -10,19 +10,13 @@ */ public class UserInfoCache { - static Object lockobj = new Object(); + static final Object lockobj = new Object(); Map cache = new HashMap<>(); public UserInfoCacheItem getItem(String accessKey) { synchronized (lockobj) { - if (!cache.containsKey(accessKey)) - return null; - UserInfoCacheItem item = cache.get(accessKey); - if (item.isExpired()) { - cache.remove(accessKey); - return null; - } - return item; + cache.entrySet().removeIf(e -> e.getValue().isExpired()); + return cache.get(accessKey); } } diff --git a/core/src/main/java/org/fao/geonet/kernel/setting/Settings.java b/core/src/main/java/org/fao/geonet/kernel/setting/Settings.java index 01a878903fb..c3c2b209271 100644 --- a/core/src/main/java/org/fao/geonet/kernel/setting/Settings.java +++ b/core/src/main/java/org/fao/geonet/kernel/setting/Settings.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 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) * @@ -119,7 +119,6 @@ public class Settings { public static final String SYSTEM_HARVESTING_MAIL_TEMPLATE = "system/harvesting/mail/template"; public static final String SYSTEM_METADATACREATE_GENERATE_UUID = "system/metadatacreate/generateUuid"; public static final String SYSTEM_THREADEDINDEXING_MAXTHREADS = "system/threadedindexing/maxthreads"; - public static final String SYSTEM_INDEX_INDEXINGTIMERECORDLINK = "system/index/indexingTimeRecordLink"; public static final String SYSTEM_RESOURCE_PREFIX = "metadata/resourceIdentifierPrefix"; public static final String SYSTEM_INSPIRE_REMOTE_VALIDATION_URL = "system/inspire/remotevalidation/url"; public static final String SYSTEM_INSPIRE_REMOTE_VALIDATION_URL_QUERY = "system/inspire/remotevalidation/urlquery"; @@ -128,6 +127,7 @@ public class Settings { public static final String REGION_GETMAP_MAPPROJ = "region/getmap/mapproj"; public static final String REGION_GETMAP_WIDTH = "region/getmap/width"; public static final String REGION_GETMAP_SUMMARY_WIDTH = "region/getmap/summaryWidth"; + public static final String REGION_GETMAP_GEODESIC_EXTENTS = "region/getmap/useGeodesicExtents"; public static final String METADATA_WORKFLOW_ENABLE = "metadata/workflow/enable"; public static final String METADATA_WORKFLOW_DRAFT_WHEN_IN_GROUP = "metadata/workflow/draftWhenInGroup"; public static final String METADATA_WORKFLOW_ALLOW_SUBMIT_APPROVE_INVALID_MD = "metadata/workflow/allowSubmitApproveInvalidMd"; diff --git a/core/src/main/java/org/fao/geonet/lib/DbLib.java b/core/src/main/java/org/fao/geonet/lib/DbLib.java index 1352ee28439..6a85a1e334c 100644 --- a/core/src/main/java/org/fao/geonet/lib/DbLib.java +++ b/core/src/main/java/org/fao/geonet/lib/DbLib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -67,7 +67,7 @@ public void insertData(ServletContext servletContext, final ServiceContext conte runSQL(context, data); } - static public void runSQL(final ServiceContext context, final List data) { + public static void runSQL(final ServiceContext context, final List data) { TransactionManager.runInTransaction("Apply SQL statements in database", context.getApplicationContext(), TransactionManager.TransactionRequirement.CREATE_ONLY_WHEN_NEEDED, TransactionManager.CommitBehavior.ALWAYS_COMMIT, false, new TransactionTask() { @@ -88,7 +88,7 @@ public void insertData(ServletContext servletContext, Statement statement, Path runSQL(statement, data, true); } - static private void runSQL(EntityManager entityManager, List data, boolean failOnError) throws Exception { + private static void runSQL(EntityManager entityManager, List data, boolean failOnError) throws Exception { StringBuffer sb = new StringBuffer(); boolean inBlock = false; @@ -244,7 +244,7 @@ private List loadSqlDataFile(ServletContext servletContext, ApplicationC Path file = checkFilePath(servletContext, appPath, filePath, filePrefix, DatabaseType.lookup(connection).toString()); // --- load the sql data - return Lib.text.load(servletContext, appPath, file, Constants.ENCODING); + return Lib.text.load(file, Constants.ENCODING); } finally { if (connection != null) { connection.close(); @@ -258,7 +258,7 @@ private List loadSqlDataFile(ServletContext servletContext, Statement st Path file = checkFilePath(servletContext, appPath, filePath, filePrefix, DatabaseType.lookup(statement.getConnection()).toString()); // --- load the sql data - return Lib.text.load(servletContext, appPath, file, Constants.ENCODING); + return Lib.text.load(file, Constants.ENCODING); } } diff --git a/core/src/main/java/org/fao/geonet/lib/ElementLib.java b/core/src/main/java/org/fao/geonet/lib/ElementLib.java index 3da8ced307e..b3089cf5f36 100644 --- a/core/src/main/java/org/fao/geonet/lib/ElementLib.java +++ b/core/src/main/java/org/fao/geonet/lib/ElementLib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -46,7 +46,7 @@ public class ElementLib { //----------------------------------------------------------------------------- public Set getIds(Element elem) { - HashSet hs = new HashSet(); + HashSet hs = new HashSet<>(); for (Object child : elem.getChildren()) hs.add(((Element) child).getChildText("id")); @@ -57,7 +57,7 @@ public Set getIds(Element elem) { //----------------------------------------------------------------------------- public Element pruneChildren(Element elem, Set ids) { - ArrayList alToPrune = new ArrayList(); + ArrayList alToPrune = new ArrayList<>(); //--- collect elements to prune diff --git a/core/src/main/java/org/fao/geonet/lib/Lib.java b/core/src/main/java/org/fao/geonet/lib/Lib.java index 0f199b03fa0..524976fd2e8 100644 --- a/core/src/main/java/org/fao/geonet/lib/Lib.java +++ b/core/src/main/java/org/fao/geonet/lib/Lib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -24,11 +24,16 @@ package org.fao.geonet.lib; public class Lib { - public final static ElementLib element = new ElementLib(); - public final static DbLib db = new DbLib(); - public final static ResourceLib resource = new ResourceLib(); - public final static TypeLib type = new TypeLib(); - public final static NetLib net = new NetLib(); - public final static SourcesLib sources = new SourcesLib(); - public final static TextLib text = new TextLib(); + public static final ElementLib element = new ElementLib(); + public static final DbLib db = new DbLib(); + public static final ResourceLib resource = new ResourceLib(); + public static final TypeLib type = new TypeLib(); + public static final NetLib net = new NetLib(); + public static final TextLib text = new TextLib(); + + /** + * Private constructor to avoid instantiate the class. + */ + private Lib() { + } } diff --git a/core/src/main/java/org/fao/geonet/lib/NetLib.java b/core/src/main/java/org/fao/geonet/lib/NetLib.java index 6f1939a9ca8..87d53c5fc13 100644 --- a/core/src/main/java/org/fao/geonet/lib/NetLib.java +++ b/core/src/main/java/org/fao/geonet/lib/NetLib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -43,7 +43,7 @@ import java.net.Proxy; import java.net.URL; import java.net.URLConnection; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Properties; import java.util.regex.PatternSyntaxException; @@ -229,7 +229,7 @@ public URLConnection setupProxy(ServiceContext context, URL url) throws IOExcept conn = url.openConnection(proxy); if (username.trim().length() != 0) { - String encodedUserPwd = new Base64().encodeAsString((username + ":" + password).getBytes(Charset.forName("UTF-8"))); + String encodedUserPwd = new Base64().encodeAsString((username + ":" + password).getBytes(StandardCharsets.UTF_8)); conn.setRequestProperty("Accept-Charset", "UTF-8"); conn.setRequestProperty("Proxy-Authorization", "Basic " + encodedUserPwd); } diff --git a/core/src/main/java/org/fao/geonet/lib/ResourceLib.java b/core/src/main/java/org/fao/geonet/lib/ResourceLib.java index a198e7a6cbc..ef997006a8a 100644 --- a/core/src/main/java/org/fao/geonet/lib/ResourceLib.java +++ b/core/src/main/java/org/fao/geonet/lib/ResourceLib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -34,7 +34,6 @@ import org.fao.geonet.exceptions.OperationNotAllowedEx; import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.GeonetworkDataDirectory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.security.access.AccessDeniedException; import org.springframework.stereotype.Component; @@ -113,7 +112,7 @@ public void checkPrivilege(ServiceContext context, String id, denyAccess(context); } - public void denyAccess(ServiceContext context) throws Exception { + public void denyAccess(ServiceContext context) throws AccessDeniedException, OperationNotAllowedEx { if (context.getUserSession().isAuthenticated()) { throw new AccessDeniedException("User is not permitted to access this resource"); } else { @@ -134,21 +133,6 @@ public void checkEditPrivilege(ServiceContext context, String id) /** * @return the absolute path of the folder choosen to store all deleted metadata */ - @Deprecated - public Path getRemovedDir(ServiceContext context) { - GeonetContext gc = (GeonetContext) context - .getHandlerContext(Geonet.CONTEXT_NAME); - return gc.getBean(GeonetworkDataDirectory.class).getBackupDir(); - } - - /** - * See {@link #getRemovedDir(Path, String)} - */ - @Deprecated - public Path getRemovedDir(ServiceContext context, String id) { - return getRemovedDir(getRemovedDir(context), id); - } - public Path getRemovedDir(int id) { ApplicationContext appContext = ApplicationContextHolder.get(); GeonetworkDataDirectory dataDirectory = appContext.getBean(GeonetworkDataDirectory.class); @@ -172,10 +156,10 @@ public Path getRemovedDir(Path removedDir, String id) { // --- // ----------------------------------------------------------------------------- - private String pad(int group, int lenght) { + private String pad(int group, int length) { String text = Integer.toString(group); - while (text.length() < lenght) + while (text.length() < length) text = "0" + text; return text; diff --git a/core/src/main/java/org/fao/geonet/lib/SourcesLib.java b/core/src/main/java/org/fao/geonet/lib/SourcesLib.java deleted file mode 100644 index e4fa3739f2b..00000000000 --- a/core/src/main/java/org/fao/geonet/lib/SourcesLib.java +++ /dev/null @@ -1,40 +0,0 @@ -//============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the -//=== United Nations (FAO-UN), United Nations World Food Programme (WFP) -//=== and United Nations Environment Programme (UNEP) -//=== -//=== This program is free software; you can redistribute it and/or modify -//=== it under the terms of the GNU General Public License as published by -//=== the Free Software Foundation; either version 2 of the License, or (at -//=== your option) any later version. -//=== -//=== This program is distributed in the hope that it will be useful, but -//=== WITHOUT ANY WARRANTY; without even the implied warranty of -//=== MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -//=== General Public License for more details. -//=== -//=== You should have received a copy of the GNU General Public License -//=== along with this program; if not, write to the Free Software -//=== Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA -//=== -//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, -//=== Rome - Italy. email: geonetwork@osgeo.org -//============================================================================== - -package org.fao.geonet.lib; - -//============================================================================= - -public class SourcesLib { - //--------------------------------------------------------------------------- - //--- - //--- API methods - //--- - //--------------------------------------------------------------------------- - - //--------------------------------------------------------------------------- - -} - -//============================================================================= - diff --git a/core/src/main/java/org/fao/geonet/lib/TextLib.java b/core/src/main/java/org/fao/geonet/lib/TextLib.java index ca78ee34f0c..a3f7e3b6b33 100644 --- a/core/src/main/java/org/fao/geonet/lib/TextLib.java +++ b/core/src/main/java/org/fao/geonet/lib/TextLib.java @@ -1,5 +1,5 @@ //============================================================================= -//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the +//=== Copyright (C) 2001-2024 Food and Agriculture Organization of the //=== United Nations (FAO-UN), United Nations World Food Programme (WFP) //=== and United Nations Environment Programme (UNEP) //=== @@ -26,11 +26,11 @@ import org.fao.geonet.Util; import org.fao.geonet.utils.IO; -import javax.servlet.ServletContext; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -41,27 +41,25 @@ public class TextLib { private static final Random RANDOM = new Random(); - public List load(ServletContext servletContext, Path appPath, Path file) throws IOException { - return load(servletContext, appPath, file, "ISO-8859-1"); + public List load(Path file) throws IOException { + return load(file, "ISO-8859-1"); } - public List load(ServletContext servletContext, Path appPath, Path file, String encoding) throws IOException { - BufferedReader reader = IO.newBufferedReader(file, Charset.forName(encoding)); - List al = new ArrayList(); - String line = reader.readLine(); - try { + public List load(Path file, String encoding) throws IOException { + try(BufferedReader reader = IO.newBufferedReader(file, Charset.forName(encoding))) { + List al = new ArrayList<>(); + String line = reader.readLine(); + while (line != null) { al.add(line); line = reader.readLine(); } return al; - } finally { - reader.close(); } } public void save(Path file, List lines) throws IOException { - try (BufferedWriter ow = Files.newBufferedWriter(file, Charset.forName("ISO-8859-1"))) { + try (BufferedWriter ow = Files.newBufferedWriter(file, StandardCharsets.ISO_8859_1)) { for (String line : lines) { ow.write(line); ow.newLine(); diff --git a/core/src/main/java/org/fao/geonet/util/XslUtil.java b/core/src/main/java/org/fao/geonet/util/XslUtil.java index dcf3507691f..256129da48b 100644 --- a/core/src/main/java/org/fao/geonet/util/XslUtil.java +++ b/core/src/main/java/org/fao/geonet/util/XslUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2020 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) * @@ -23,6 +23,7 @@ package org.fao.geonet.util; +import co.elastic.clients.elasticsearch.core.search.Hit; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -39,20 +40,11 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; +import org.apache.commons.text.StringEscapeUtils; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.lucene.search.join.ScoreMode; -import org.elasticsearch.action.search.MultiSearchRequest; -import org.elasticsearch.action.search.MultiSearchResponse; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.index.query.BoolQueryBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.search.SearchHits; -import org.elasticsearch.search.builder.SearchSourceBuilder; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.SystemInfo; import org.fao.geonet.api.records.attachments.FilesystemStore; @@ -60,7 +52,6 @@ import org.fao.geonet.api.records.attachments.Store; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; -import org.fao.geonet.index.es.EsRestClient; import org.fao.geonet.kernel.*; import org.fao.geonet.kernel.datamanager.base.BaseMetadataUtils; import org.fao.geonet.kernel.search.CodeListTranslator; @@ -469,7 +460,7 @@ public static String getNodeId() { return ApplicationContextHolder.get().getBean(org.fao.geonet.NodeInfo.class).getId(); } - + public static String getNodeLogo(String key) { Optional source = getSource(key); return source.isPresent() ? source.get().getLogo() : ""; @@ -1549,152 +1540,27 @@ public static String getKeywordUri(String keyword, String thesaurusId, String la return ""; } - /** - * Associated resource like - *
    - *
  • parent
  • - *
  • source
  • - *
  • dataset (for service record)
  • - *
  • siblings
  • - *
  • feature catalogue
  • - *
- * are stored in current records - * BUT - * some other relations are stored in the other side of the relation record ie. - *
    - *
  • service operatingOn current = +recordOperateOn:currentUuid
  • - *
  • siblings of current = +recordLink.type:siblings +recordLink.to:currentUuid
  • - *
  • children of current = +parentUuid:currentUuid
  • - *
  • brothersAndSisters = +parentUuid:currentParentUuid
  • - *
- * Instead of relying on related API, it can make sense to index all relations - * (including bidirectional links) at indexing time to speed up rendering of - * associated resources which is slow task on search results. - * - * MetadataUtils#getRelated has the logic to search for all associated resources - * and also takes into account privileges in case of target record is not visible - * to current user. - * - * BTW in some cases, all records are public (or it is not an issue to only display - * a title of a private record) and a more direct approach can be used. - * - * @param uuid - * @return - */ - public static Element getTargetAssociatedResources(String uuid, String parentUuid) { - EsRestClient client = ApplicationContextHolder.get().getBean(EsRestClient.class); - EsSearchManager searchManager = ApplicationContextHolder.get().getBean(EsSearchManager.class); - Element recordLinks = new Element("recordLinks"); - - try { - MultiSearchRequest request = new MultiSearchRequest(); - - - SearchRequest serviceRequest = new SearchRequest(searchManager.getDefaultIndex()); - SearchSourceBuilder serviceSearchSourceBuilder = new SearchSourceBuilder(); - serviceSearchSourceBuilder.fetchSource( - new String[]{"resourceTitleObject.default"}, - null - ); - serviceSearchSourceBuilder.query(QueryBuilders.matchQuery( - "recordOperateOn", uuid)); - serviceRequest.source(serviceSearchSourceBuilder); - request.add(serviceRequest); - - - SearchRequest childrenRequest = new SearchRequest(searchManager.getDefaultIndex()); - SearchSourceBuilder childrenSearchSourceBuilder = new SearchSourceBuilder(); - childrenSearchSourceBuilder.fetchSource( - new String[]{"resourceTitleObject.default"}, - null - ); - childrenSearchSourceBuilder.query(QueryBuilders.matchQuery( - "parentUuid", uuid)); - childrenRequest.source(childrenSearchSourceBuilder); - request.add(childrenRequest); - - - SearchRequest siblingsRequest = new SearchRequest(searchManager.getDefaultIndex()); - SearchSourceBuilder siblingsSearchSourceBuilder = new SearchSourceBuilder(); - siblingsSearchSourceBuilder.fetchSource( - new String[]{"resourceTitleObject.default"}, - null - ); - BoolQueryBuilder boolQuery = QueryBuilders.boolQuery(); - List must = boolQuery.must(); - must.add(QueryBuilders.matchQuery("recordLink.type", "siblings")); - must.add(QueryBuilders.matchQuery("recordLink.to", uuid)); - siblingsSearchSourceBuilder.query( - QueryBuilders.nestedQuery( - Geonet.IndexFieldNames.RECORDLINK, - boolQuery, - ScoreMode.Avg)); - siblingsRequest.source(siblingsSearchSourceBuilder); - request.add(siblingsRequest); - - - - boolean hasParent = StringUtils.isNotEmpty(parentUuid); - if (hasParent) { - SearchRequest brothersAndSistersRequest = new SearchRequest(searchManager.getDefaultIndex()); - SearchSourceBuilder brothersAndSistersSearchSourceBuilder = new SearchSourceBuilder(); - brothersAndSistersSearchSourceBuilder.fetchSource( - new String[]{"resourceTitleObject.default"}, - null - ); - brothersAndSistersSearchSourceBuilder.query(QueryBuilders.matchQuery( - "parentUuid", parentUuid)); - brothersAndSistersRequest.source(brothersAndSistersSearchSourceBuilder); - request.add(brothersAndSistersRequest); - } - - - MultiSearchResponse response = client.getClient().msearch(request, RequestOptions.DEFAULT); - recordLinks.addContent(buildRecordLink(response.getResponses()[0].getResponse().getHits(), "services")); - recordLinks.addContent(buildRecordLink(response.getResponses()[1].getResponse().getHits(), "children")); - recordLinks.addContent(buildRecordLink(response.getResponses()[2].getResponse().getHits(), "siblings")); - - if (hasParent) { - recordLinks.addContent(buildRecordLink(response.getResponses()[3].getResponse().getHits(), "brothersAndSisters")); - } - } catch (Exception e) { - Log.error(Geonet.GEONETWORK, - "Get related document '" + uuid + "' error: " + e.getMessage(), e); - } - return recordLinks; - } - - public static Node getTargetAssociatedResourcesAsNode(String uuid, String parentUuid) { - DOMOutputter outputter = new DOMOutputter(); - try { - return outputter.output( - new Document( - getTargetAssociatedResources(uuid, parentUuid))); - } catch (Exception e) { - Log.error(Geonet.GEONETWORK, - "Get related document '" + uuid + "' error: " + e.getMessage(), e); - } - return null; - } - - private static List buildRecordLink(SearchHits hits, String type) { + private static List buildRecordLink(List hits, String type) { ObjectMapper mapper = new ObjectMapper(); SettingManager settingManager = ApplicationContextHolder.get().getBean(SettingManager.class); String recordUrlPrefix = settingManager.getNodeURL() + "api/records/"; ArrayList listOfLinks = new ArrayList<>(); + + ObjectMapper objectMapper = new ObjectMapper(); + hits.forEach(record -> { Element recordLink = new Element("recordLink"); recordLink.setAttribute("type", "object"); ObjectNode recordLinkProperties = mapper.createObjectNode(); - recordLinkProperties.put("to", record.getId()); + recordLinkProperties.put("to", record.id()); recordLinkProperties.put("origin", "catalog"); recordLinkProperties.put("created", "bySearch"); - Map titleObject = (Map) record.getSourceAsMap().get("resourceTitleObject"); + Map titleObject = (Map) objectMapper.convertValue(record.source(), Map.class).get("resourceTitleObject"); if (titleObject != null) { recordLinkProperties.put("title", titleObject.get("default")); } - recordLinkProperties.put("url", recordUrlPrefix + record.getId()); + recordLinkProperties.put("url", recordUrlPrefix + record.id()); recordLinkProperties.put("type", type); try { @@ -1706,4 +1572,8 @@ private static List buildRecordLink(SearchHits hits, String type) { }); return listOfLinks; } + + public static String escapeForJson(String value) { + return StringEscapeUtils.escapeJson(value); + } } 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 f1fcdf99805..f81456e4f84 100644 --- a/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/DataManagerIntegrationTest.java @@ -179,12 +179,12 @@ public void testDeleteBatchMetadata() throws Exception { importMetadata(serviceContext, uuid1); importMetadata(serviceContext, uuid2); assertEquals(2, metadataRepository.findAll(spec).size()); - assertEquals(2, searchManager.query(String.format("uuid:(%s OR %s)", uuid1, uuid2), null, 0, 10).getHits().getTotalHits().value); + assertEquals(2, searchManager.query(String.format("uuid:(%s OR %s)", uuid1, uuid2), null, 0, 10).hits().hits().size()); dataManager.batchDeleteMetadataAndUpdateIndex(spec); assertEquals(0, metadataRepository.findAll(spec).size()); - assertEquals(0, searchManager.query(String.format("uuid:(%s OR %s)", uuid1, uuid2), null, 0, 10).getHits().getTotalHits().value); + assertEquals(0, searchManager.query(String.format("uuid:(%s OR %s)", uuid1, uuid2), null, 0, 10).hits().hits().size()); } @Test diff --git a/core/src/test/java/org/fao/geonet/kernel/ElasticsearchIndexingTest.java b/core/src/test/java/org/fao/geonet/kernel/ElasticsearchIndexingTest.java index 6170271f79a..e13577def46 100644 --- a/core/src/test/java/org/fao/geonet/kernel/ElasticsearchIndexingTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/ElasticsearchIndexingTest.java @@ -1,7 +1,7 @@ package org.fao.geonet.kernel; +import co.elastic.clients.elasticsearch.core.SearchResponse; import jeeves.server.context.ServiceContext; -import org.elasticsearch.action.search.SearchResponse; import org.fao.geonet.AbstractCoreIntegrationTest; import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.kernel.search.EsSearchManager; @@ -47,7 +47,7 @@ public void complexDatesAreIndexedCheck() throws Exception { //THEN SearchResponse response = this.searchManager.query("_id:" + dbInsertedMetadata.getUuid() + " AND resourceTitleObject.default:holocene", null, 0, 10); - long actualHitNbr = response.getHits().getTotalHits().value; + long actualHitNbr = response.hits().hits().size(); assertEquals(String.format("Incorrect indexation of Holocene data with complex date due to: %s and %s", response, dbInsertedMetadata), 1, actualHitNbr); } @@ -59,7 +59,7 @@ private AbstractMetadata loadMetadataWithTemporalExtentUsingSimpleDates() throws private void validateIndexedExpectedData(AbstractMetadata dbInsertedSimpleDataMetadata, String resourceTitle, long expectedHitNbr) throws Exception { SearchResponse searchResponse = this.searchManager.query("resourceTitleObject.default:" + resourceTitle, null, 0, 10); - long actualHitNbr = searchResponse.getHits().getTotalHits().value; + long actualHitNbr = searchResponse.hits().hits().size(); String assertionErrorMessage = "The %s data was not indexed the expected number of times due to: %s and %s"; assertEquals(String.format(assertionErrorMessage, resourceTitle, searchResponse, dbInsertedSimpleDataMetadata), expectedHitNbr, actualHitNbr); } diff --git a/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java b/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java index b16e898db5a..e30b06d96ca 100644 --- a/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java +++ b/core/src/test/java/org/fao/geonet/kernel/XmlSerializerIntegrationTest.java @@ -143,17 +143,6 @@ public void testInternalSelectHidingWithheldSettingsDisabled() throws Exception assertHiddenElements(false, false); } - @SuppressWarnings("unchecked") - @Test - public void testInternalSelectHidingWithheldNullServiceContext() throws Exception { - setSchemaFilters(true, true); - Field field = ServiceContext.class.getDeclaredField("THREAD_LOCAL_INSTANCE"); - field.setAccessible(true); - InheritableThreadLocal threadLocalInstance = (InheritableThreadLocal) field.get(null); - threadLocalInstance.set(null); - assertHiddenElements(true); - } - @Test public void testInternalSelectHidingWithheldAdministrator() throws Exception { try (Closeable ignored = configureXmlSerializerAndServiceContext(true, false, false)) { diff --git a/core/src/test/java/org/fao/geonet/kernel/search/EsSearchManagerTest.java b/core/src/test/java/org/fao/geonet/kernel/search/EsSearchManagerTest.java new file mode 100644 index 00000000000..ec0af66d1b3 --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/search/EsSearchManagerTest.java @@ -0,0 +1,66 @@ +package org.fao.geonet.kernel.search; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import static org.junit.Assert.assertEquals; + +import org.jdom.Element; +import org.junit.Before; +import org.junit.Test; + +public class EsSearchManagerTest { + + private EsSearchManager instance; + private ObjectMapper objectMapper; + + @Before + public void setup() { + this.instance = new EsSearchManager(); + this.objectMapper = new ObjectMapper(); + } + + @Test + public void documentToJsonSimple() throws JsonProcessingException { + Element input = new Element("doc"); + Element field1 = new Element("field1").setText("content1"); + Element field2 = new Element("field2").setText("content2"); + input.addContent(field1); + input.addContent(field2); + + ObjectNode result = instance.documentToJson(input); + JsonNode expected = objectMapper.readTree(" {\"field1\":\"content1\",\"field2\":\"content2\"}"); + + assertEquals(expected, result); + } + + @Test + public void documentToJsonIndexingErrorMsg() throws JsonProcessingException { + Element input = new Element("doc"); + Element field1 = new Element("field1").setText("content1"); + String escapedJson = "{\n" + + " \"type\": \"error\",\n" + + " \"string\": \"string-reference\",\n" + + " \"values\": {\n" + + " \"value1\": \"content1\",\n" + + " \"value2\": \"content2\"\n" + + " }\n" + + "}"; + Element field2 = new Element(IndexFields.INDEXING_ERROR_MSG).setText(escapedJson); + Element nestedField1 = new Element("nestedField1").setText("nestedContent1"); + Element nestedField2 = new Element("nestedField2").setText("nestedcontent2"); + field2.addContent(nestedField1); + field2.addContent(nestedField2); + field2.setAttribute("type", "object"); + + input.addContent(field1); + input.addContent(field2); + + ObjectNode result = instance.documentToJson(input); + JsonNode expected = objectMapper.readTree("{\"field1\":\"content1\",\"indexingErrorMsg\":[{\"type\":\"error\",\"string\":\"string-reference\",\"values\":{\"value1\":\"content1\",\"value2\":\"content2\"}}]}"); + + assertEquals(expected, result); + } +} diff --git a/core/src/test/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCacheTest.java b/core/src/test/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCacheTest.java new file mode 100644 index 00000000000..475171a5ae2 --- /dev/null +++ b/core/src/test/java/org/fao/geonet/kernel/security/openidconnect/bearer/UserInfoCacheTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ +package org.fao.geonet.kernel.security.openidconnect.bearer; + +import com.google.common.collect.Lists; +import junit.framework.TestCase; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.time.Instant; +import java.util.Collections; + +public class UserInfoCacheTest extends TestCase { + + private OAuth2User user1 = new DefaultOAuth2User(Lists.newArrayList(), Collections.singletonMap("name", "frank"), "name"); + private OAuth2User user2 = new DefaultOAuth2User(Lists.newArrayList(), Collections.singletonMap("name", "jeff"), "name"); + + public void testCache() { + UserInfoCacheItem item1 = new UserInfoCacheItem("a", Instant.now().plusSeconds(1000), user1, Lists.newArrayList()); + UserInfoCacheItem item2 = new UserInfoCacheItem("b", Instant.now().plusSeconds(1000), user2, Lists.newArrayList()); + UserInfoCacheItem item3 = new UserInfoCacheItem("c", Instant.now().minusSeconds(1000), user2, Lists.newArrayList()); + + UserInfoCache cache = new UserInfoCache(); + cache.putItem(item1); + cache.putItem(item2); + cache.putItem(item3); + + assertEquals(3, cache.cache.size()); + assertEquals(item1, cache.getItem("a")); + assertEquals(item2, cache.getItem("b")); + assertNull(cache.getItem("c")); + assertEquals(2, cache.cache.size()); + } +} diff --git a/csw-server/pom.xml b/csw-server/pom.xml index f8574ce46dc..cd7b317b79d 100644 --- a/csw-server/pom.xml +++ b/csw-server/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -105,6 +105,11 @@ elasticsearch + + co.elastic.clients + elasticsearch-java + + org.springframework spring-beans @@ -221,6 +226,7 @@ test 9300 9200 + -Xmx2g diff --git a/csw-server/src/main/java/org/fao/geonet/component/csw/GetRecords.java b/csw-server/src/main/java/org/fao/geonet/component/csw/GetRecords.java index feffc0696ab..5b0fb4e224d 100644 --- a/csw-server/src/main/java/org/fao/geonet/component/csw/GetRecords.java +++ b/csw-server/src/main/java/org/fao/geonet/component/csw/GetRecords.java @@ -23,11 +23,9 @@ package org.fao.geonet.component.csw; +import co.elastic.clients.elasticsearch._types.SortOptions; import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortOrder; import org.fao.geonet.GeonetContext; import org.fao.geonet.constants.Geonet; import org.fao.geonet.csw.common.*; @@ -244,7 +242,7 @@ public Element execute(Element request, ServiceContext context) throws CatalogEx response.addContent(echoedRequest); } else { - List> sort = _sortByParser.parseSortBy(request); + List sort = _sortByParser.parseSortBy(request); response = new Element(getName() + "Response", Csw.NAMESPACE_CSW); diff --git a/csw-server/src/main/java/org/fao/geonet/csw/common/requests/CatalogRequest.java b/csw-server/src/main/java/org/fao/geonet/csw/common/requests/CatalogRequest.java index a254a26c3f0..8718d565fa8 100644 --- a/csw-server/src/main/java/org/fao/geonet/csw/common/requests/CatalogRequest.java +++ b/csw-server/src/main/java/org/fao/geonet/csw/common/requests/CatalogRequest.java @@ -186,6 +186,7 @@ public Element execute() throws Exception { public void setCredentials(String username, String password) { client.setCredentials(username, password); + client.setPreemptiveBasicAuth(true); } //--------------------------------------------------------------------------- diff --git a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SearchController.java b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SearchController.java index 7e4192220bf..f66e5e8e91d 100644 --- a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SearchController.java +++ b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SearchController.java @@ -23,14 +23,13 @@ package org.fao.geonet.kernel.csw.services.getrecords; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.GeonetContext; import org.fao.geonet.NodeInfo; @@ -53,8 +52,6 @@ import org.fao.geonet.kernel.search.EsSearchManager; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.Xml; -import org.geotools.xsd.Configuration; -import org.geotools.xsd.Parser; import org.jdom.Attribute; import org.jdom.Content; import org.jdom.Element; @@ -62,11 +59,7 @@ import org.geotools.api.filter.Filter; import org.geotools.api.filter.capability.FilterCapabilities; import org.springframework.beans.factory.annotation.Autowired; -import org.xml.sax.SAXException; -import javax.xml.parsers.ParserConfigurationException; -import java.io.IOException; -import java.io.StringReader; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -439,7 +432,7 @@ private static Element applyElementNames(ServiceContext context, Set ele */ public Element search(ServiceContext context, int startPos, int maxRecords, ResultType resultType, String outSchema, ElementSetName setName, - Element filterExpr, String filterVersion, List> sort, + Element filterExpr, String filterVersion, List sort, Set elemNames, String typeName, int maxHitsFromSummary, String strategy) throws CatalogException { @@ -462,11 +455,11 @@ public Element search(ServiceContext context, int startPos, int maxRecords, // TODO: Check to get summary or remove custom summary output try { - SearchResponse result = searchManager.query(esJsonQuery, new HashSet(), startPos-1, maxRecords, sort); + SearchResponse result = searchManager.query(esJsonQuery, new HashSet<>(), startPos-1, maxRecords, sort); - SearchHit[] hits = result.getHits().getHits(); + List hits = result.hits().hits(); - long numMatches = result.getHits().getTotalHits().value; + long numMatches = result.hits().hits().size(); if (numMatches != 0 && startPos > numMatches) { throw new InvalidParameterValueEx("startPosition", String.format( @@ -477,8 +470,10 @@ public Element search(ServiceContext context, int startPos, int maxRecords, int counter = 0; - for(SearchHit hit : hits) { - int mdId = Integer.parseInt((String) hit.getSourceAsMap().get("id")); + ObjectMapper objectMapper = new ObjectMapper(); + + for(Hit hit : hits) { + int mdId = Integer.parseInt((String) objectMapper.convertValue(hit.source(), Map.class).get("id")); AbstractMetadata metadata = metadataUtils.findOne(mdId); @@ -543,7 +538,7 @@ public Element applyElementSetName(ServiceContext context, SchemaManager schemaM outputSchema, id, schema, styleSheet.toString())); return null; } else { - Map params = new HashMap(); + Map params = new HashMap<>(); params.put("lang", displayLanguage); try { diff --git a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SortByParser.java b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SortByParser.java index 87a00f76366..0efcae035ff 100644 --- a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SortByParser.java +++ b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/SortByParser.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 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) * @@ -23,19 +23,20 @@ package org.fao.geonet.kernel.csw.services.getrecords; +import co.elastic.clients.elasticsearch._types.FieldSort; +import co.elastic.clients.elasticsearch._types.SortOptions; +import co.elastic.clients.elasticsearch._types.SortOrder; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; -import org.elasticsearch.search.sort.SortOrder; import org.fao.geonet.csw.common.Csw; import org.fao.geonet.kernel.csw.CatalogConfiguration; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import static co.elastic.clients.elasticsearch._types.SortOptions.*; + public class SortByParser { @Autowired @@ -44,7 +45,7 @@ public class SortByParser { @Autowired private CatalogConfiguration _catalogConfig; - public List> parseSortBy(Element request) { + public List parseSortBy(Element request) { Element query = request.getChild("Query", Csw.NAMESPACE_CSW); if (query == null) { return getDefaultSort(); @@ -55,14 +56,22 @@ public List> parseSortBy(Element request) { return getDefaultSort(); } - List> sortFields = new ArrayList<>(); + List sortFields = new ArrayList<>(); @SuppressWarnings("unchecked") List list = sortBy.getChildren(); for (Element el : list) { String esSortFieldName = getEsSortFieldName(el); if (!StringUtils.isEmpty(esSortFieldName)) { SortOrder esSortOrder = getEsSortOrder(el); - sortFields.add(new FieldSortBuilder(esSortFieldName).order(esSortOrder)); + + SortOptions sortFieldOptions = + new Builder() + .field(new FieldSort.Builder() + .field(esSortFieldName) + .order(esSortOrder).build()) + .build(); + + sortFields.add(sortFieldOptions); } } @@ -72,11 +81,13 @@ public List> parseSortBy(Element request) { return sortFields; } - private List> getDefaultSort() { - List> sortFields = new ArrayList<>(); - FieldSortBuilder defaultSortField = - new FieldSortBuilder(_catalogConfig.getDefaultSortField()) - .order(SortOrder.fromString(_catalogConfig.getDefaultSortOrder())); + private List getDefaultSort() { + List sortFields = new ArrayList<>(); + + SortOptions defaultSortField = SortOptions.of( + b -> b.field(fb -> fb.field(_catalogConfig.getDefaultSortField()) + .order(_catalogConfig.getDefaultSortOrder().equals("DESC")?SortOrder.Desc:SortOrder.Asc)) + ); sortFields.add(defaultSortField); return sortFields; @@ -97,6 +108,6 @@ private String getEsSortFieldName(Element el) { private SortOrder getEsSortOrder(Element el) { String order = el.getChildText("SortOrder", Csw.NAMESPACE_OGC); boolean isDescOrder = "DESC".equals(order); - return isDescOrder ? SortOrder.DESC : SortOrder.ASC; + return isDescOrder ? SortOrder.Desc : SortOrder.Asc; } } diff --git a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2Es.java b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2Es.java index ba7aff93722..2122a8c4a10 100644 --- a/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2Es.java +++ b/csw-server/src/main/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2Es.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -26,131 +26,73 @@ import org.apache.commons.lang.NotImplementedException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; +import org.apache.commons.text.StringEscapeUtils; import org.fao.geonet.constants.Geonet; import org.fao.geonet.kernel.csw.services.getrecords.IFieldMapper; import org.fao.geonet.utils.Log; -import org.geotools.filter.visitor.AbstractFilterVisitor; -import org.locationtech.jts.geom.*; -import org.locationtech.jts.io.WKTReader; -import org.geotools.api.filter.And; -import org.geotools.api.filter.BinaryComparisonOperator; -import org.geotools.api.filter.BinaryLogicOperator; -import org.geotools.api.filter.ExcludeFilter; -import org.geotools.api.filter.Filter; -import org.geotools.api.filter.Id; -import org.geotools.api.filter.IncludeFilter; -import org.geotools.api.filter.Not; -import org.geotools.api.filter.Or; -import org.geotools.api.filter.PropertyIsBetween; -import org.geotools.api.filter.PropertyIsEqualTo; -import org.geotools.api.filter.PropertyIsGreaterThan; -import org.geotools.api.filter.PropertyIsGreaterThanOrEqualTo; -import org.geotools.api.filter.PropertyIsLessThan; -import org.geotools.api.filter.PropertyIsLessThanOrEqualTo; -import org.geotools.api.filter.PropertyIsLike; -import org.geotools.api.filter.PropertyIsNil; -import org.geotools.api.filter.PropertyIsNotEqualTo; -import org.geotools.api.filter.PropertyIsNull; +import org.geotools.api.filter.*; import org.geotools.api.filter.expression.Expression; import org.geotools.api.filter.expression.Literal; import org.geotools.api.filter.expression.PropertyName; -import org.geotools.api.filter.spatial.BBOX; -import org.geotools.api.filter.spatial.Beyond; -import org.geotools.api.filter.spatial.BinarySpatialOperator; -import org.geotools.api.filter.spatial.Contains; -import org.geotools.api.filter.spatial.Crosses; -import org.geotools.api.filter.spatial.DWithin; -import org.geotools.api.filter.spatial.Disjoint; -import org.geotools.api.filter.spatial.Equals; -import org.geotools.api.filter.spatial.Intersects; -import org.geotools.api.filter.spatial.Overlaps; -import org.geotools.api.filter.spatial.Touches; -import org.geotools.api.filter.spatial.Within; -import org.geotools.api.filter.temporal.After; -import org.geotools.api.filter.temporal.AnyInteracts; -import org.geotools.api.filter.temporal.Before; -import org.geotools.api.filter.temporal.Begins; -import org.geotools.api.filter.temporal.BegunBy; -import org.geotools.api.filter.temporal.During; -import org.geotools.api.filter.temporal.EndedBy; -import org.geotools.api.filter.temporal.Ends; -import org.geotools.api.filter.temporal.Meets; -import org.geotools.api.filter.temporal.MetBy; -import org.geotools.api.filter.temporal.OverlappedBy; -import org.geotools.api.filter.temporal.TContains; -import org.geotools.api.filter.temporal.TEquals; -import org.geotools.api.filter.temporal.TOverlaps; +import org.geotools.api.filter.spatial.*; +import org.geotools.api.filter.temporal.*; import org.geotools.api.geometry.BoundingBox; +import org.geotools.filter.visitor.AbstractFilterVisitor; +import org.locationtech.jts.geom.*; +import org.locationtech.jts.io.WKTReader; import java.util.*; import java.util.regex.Pattern; /** - * Manages the translation from CSW <Filter> into a ES query. + * Manages the translation from CSW <Filter> into an ES query. */ public class CswFilter2Es extends AbstractFilterVisitor { - private final String BINARY_OPERATOR_AND = "AND"; - private final String BINARY_OPERATOR_OR = "OR"; + private static final String BINARY_OPERATOR_AND = "AND"; + private static final String BINARY_OPERATOR_OR = "OR"; - static final String SPECIAL_RE = "([" + Pattern.quote("+-&|!(){}[]^\\\"~*?:/") + "])"; - static final String SPECIAL_LIKE_RE = "(? stack = new ArrayDeque(); - - private final String templateNot = " {\"bool\": {\n" + + private static final String SPECIAL_RE = "([" + Pattern.quote("+-&|!(){}[]^\\\"~*?:/") + "])"; + private static final String SPECIAL_LIKE_RE = "(? stack = new ArrayDeque<>(); + private boolean useFilter = true; public CswFilter2Es(IFieldMapper fieldMapper) { expressionVisitor = new Expression2CswVisitor(stack, fieldMapper); @@ -214,22 +159,24 @@ protected static String convertLikePattern(PropertyIsLike filter) { : filter.getSingleChar(); result = result.replaceAll(singleCharRe, "?"); } + + result = StringEscapeUtils.escapeJson(escapeLikeLiteral(result)); return result; } public String getFilter() { - String condition = stack.isEmpty()?"":stack.pop(); + String condition = stack.isEmpty() ? "" : stack.pop(); // Check for single condition (no binary operators to wrap the query if (!condition.startsWith(" \"bool\":")) { - condition = String.format(templateAndWithFilter, condition, "%s"); + condition = String.format(TEMPLATE_AND_WITH_FILTER, condition, "%s"); } if (StringUtils.isEmpty(condition)) { // No filter - condition = "{\"bool\":{\"must\":[{\"query_string\":{\"query\":\"*\"}}],\"filter\":{\"query_string\":{\"query\":\"%s\"}}}"; + condition = "{\"bool\":{\"must\":[{\"query_string\":{\"query\":\"*\"}}],\"filter\":{\"query_string\":{\"query\":\"%s\"}}}"; } else { // Add wrapper - condition = "{" + condition + "}"; + condition = "{" + condition + "}"; } outQueryString.append(condition); @@ -237,21 +184,6 @@ public String getFilter() { return outQueryString.toString(); } - @Override - public Object visitNullFilter(Object extraData) { - return super.visitNullFilter(extraData); - } - - @Override - public Object visit(ExcludeFilter filter, Object extraData) { - return super.visit(filter, extraData); - } - - @Override - public Object visit(IncludeFilter filter, Object extraData) { - return super.visit(filter, extraData); - } - @Override public Object visit(And filter, Object extraData) { return visitBinaryLogic(filter, BINARY_OPERATOR_AND, extraData); @@ -261,9 +193,9 @@ private Object visitBinaryLogic(BinaryLogicOperator filter, String operator, Obj String filterCondition; if (operator.equals(BINARY_OPERATOR_AND)) { - filterCondition = (useFilter?templateAndWithFilter:templateAnd); + filterCondition = (useFilter ? TEMPLATE_AND_WITH_FILTER : TEMPLATE_AND); } else if (operator.equals(BINARY_OPERATOR_OR)) { - filterCondition = (useFilter?templateOrWithFilter:templateOr); + filterCondition = (useFilter ? TEMPLATE_OR_WITH_FILTER : TEMPLATE_OR); } else { throw new NotImplementedException(); } @@ -288,10 +220,10 @@ private Object visitBinaryLogic(BinaryLogicOperator filter, String operator, Obj int count = StringUtils.countMatches(filterCondition, "%s"); if (count == 1) { - filterCondition = String.format(filterCondition, String.join(",", conditionList)); + filterCondition = String.format(filterCondition, String.join(",", conditionList)); } else { - filterCondition = String.format(filterCondition, String.join(",", conditionList), "%s"); + filterCondition = String.format(filterCondition, String.join(",", conditionList), "%s"); } stack.push(filterCondition); @@ -307,7 +239,7 @@ public Object visit(Id filter, Object extraData) { @Override public Object visit(Not filter, Object extraData) { - String filterNot = templateNot; + String filterNot = TEMPLATE_NOT; filter.getFilter().accept(this, extraData); @@ -324,25 +256,32 @@ public Object visit(Or filter, Object extraData) { @Override public Object visit(PropertyIsBetween filter, Object extraData) { - String filterBetween = templateBetween; + String filterBetween = TEMPLATE_BETWEEN; - assert filter.getExpression() instanceof PropertyName; - filter.getExpression().accept(expressionVisitor, extraData); + if (!(filter.getExpression() instanceof PropertyName)) { + throw new IllegalArgumentException("Invalid expression property provided"); + } - assert filter.getLowerBoundary() instanceof Literal; - filter.getLowerBoundary().accept(expressionVisitor, extraData); + if (!(filter.getLowerBoundary() instanceof Literal)) { + throw new IllegalArgumentException("Invalid expression lower boundary literal provided"); + } + + if (!(filter.getUpperBoundary() instanceof Literal)) { + throw new IllegalArgumentException("Invalid expression upper boundary literal provided"); + } - assert filter.getUpperBoundary() instanceof Literal; + filter.getExpression().accept(expressionVisitor, extraData); + filter.getLowerBoundary().accept(expressionVisitor, extraData); filter.getUpperBoundary().accept(expressionVisitor, extraData); String dataPropertyUpperValue = stack.pop(); if (!NumberUtils.isNumber(dataPropertyUpperValue)) { - dataPropertyUpperValue = CswFilter2Es.quoteString(dataPropertyUpperValue); + dataPropertyUpperValue = StringEscapeUtils.escapeJson(CswFilter2Es.quoteString(dataPropertyUpperValue)); } String dataPropertyLowerValue = stack.pop(); if (!NumberUtils.isNumber(dataPropertyLowerValue)) { - dataPropertyLowerValue = CswFilter2Es.quoteString(dataPropertyLowerValue); + dataPropertyLowerValue = StringEscapeUtils.escapeJson(CswFilter2Es.quoteString(dataPropertyLowerValue)); } String dataPropertyName = stack.pop(); @@ -355,17 +294,15 @@ public Object visit(PropertyIsBetween filter, Object extraData) { @Override public Object visit(PropertyIsEqualTo filter, Object extraData) { + checkFilterExpressionsInBinaryComparisonOperator(filter); - assert filter.getExpression1() instanceof PropertyName; filter.getExpression1().accept(expressionVisitor, extraData); - - assert filter.getExpression2() instanceof Literal; filter.getExpression2().accept(expressionVisitor, extraData); String dataPropertyValue = stack.pop(); String dataPropertyName = stack.pop(); - final String filterEqualTo = String.format(templateMatch, dataPropertyName, dataPropertyValue); + final String filterEqualTo = String.format(TEMPLATE_MATCH, dataPropertyName, StringEscapeUtils.escapeJson(escapeLiteral(dataPropertyValue))); stack.push(filterEqualTo); return this; @@ -373,37 +310,36 @@ public Object visit(PropertyIsEqualTo filter, Object extraData) { @Override public Object visit(PropertyIsNotEqualTo filter, Object extraData) { - String filterPropertyIsNot = templatePropertyIsNot; + String filterPropertyIsNot = TEMPLATE_PROPERTY_IS_NOT; - assert filter.getExpression1() instanceof PropertyName; - filter.getExpression1().accept(expressionVisitor, extraData); + checkFilterExpressionsInBinaryComparisonOperator(filter); - assert filter.getExpression2() instanceof Literal; + filter.getExpression1().accept(expressionVisitor, extraData); filter.getExpression2().accept(expressionVisitor, extraData); String dataPropertyValue = stack.pop(); String dataPropertyName = stack.pop(); - filterPropertyIsNot = String.format(filterPropertyIsNot, dataPropertyName, dataPropertyValue); + filterPropertyIsNot = String.format(filterPropertyIsNot, dataPropertyName, + StringEscapeUtils.escapeJson(escapeLiteral(dataPropertyValue))); stack.push(filterPropertyIsNot); return this; } public Object visitRange(BinaryComparisonOperator filter, String operator, Object extraData) { - String filterRange = templateRange; + String filterRange = TEMPLATE_RANGE; - assert filter.getExpression1() instanceof PropertyName; - filter.getExpression1().accept(expressionVisitor, extraData); + checkFilterExpressionsInBinaryComparisonOperator(filter); - assert filter.getExpression2() instanceof Literal; + filter.getExpression1().accept(expressionVisitor, extraData); filter.getExpression2().accept(expressionVisitor, extraData); String dataPropertyValue = stack.pop(); String dataPropertyName = stack.pop(); if (!NumberUtils.isNumber(dataPropertyValue)) { - dataPropertyValue = CswFilter2Es.quoteString(dataPropertyValue); + dataPropertyValue = StringEscapeUtils.escapeJson(CswFilter2Es.quoteString(dataPropertyValue)); } filterRange = String.format(filterRange, dataPropertyName, operator, dataPropertyValue); @@ -423,7 +359,7 @@ public Object visit(PropertyIsGreaterThanOrEqualTo filter, Object extraData) { } @Override - public Object visit(PropertyIsLessThan filter, Object extraData) { + public Object visit(PropertyIsLessThan filter, Object extraData) { return visitRange(filter, "lt", extraData); } @@ -434,7 +370,7 @@ public Object visit(PropertyIsLessThanOrEqualTo filter, Object extraData) { @Override public Object visit(PropertyIsLike filter, Object extraData) { - String filterIsLike = templateIsLike; + String filterIsLike = TEMPLATE_IS_LIKE; String expression = convertLikePattern(filter); @@ -472,7 +408,7 @@ public Object visit(PropertyIsNil filter, Object extraData) { * @return */ private String fillTemplateSpatial(String shapeType, String coords, String relation) { - return String.format(templateSpatial, shapeType, coords, relation); + return String.format(TEMPLATE_SPATIAL, shapeType, coords, relation); } @Override @@ -495,14 +431,12 @@ public Object visit(BBOX filter, Object extraData) { } private Object addGeomFilter(BinarySpatialOperator filter, String geoOperator, Object extraData) { - if (!(filter.getExpression2() == null || filter.getExpression1() == null)) { filter.getExpression1().accept(expressionVisitor, extraData); } - // out.append(":\"").append(geoOperator).append("("); final Expression geoExpression = filter.getExpression2() == null ? filter.getExpression1() - : filter.getExpression2(); + : filter.getExpression2(); geoExpression.accept(expressionVisitor, extraData); String geom = stack.pop(); @@ -542,7 +476,7 @@ private Object addGeomFilter(BinarySpatialOperator filter, String geoOperator, O stack.push(filterSpatial); } catch (Exception ex) { Log.error(Geonet.CSW, "Error parsing geospatial object", ex); - throw new RuntimeException(ex); + throw new IllegalArgumentException("Invalid expression for spatial filter", ex); } return this; @@ -674,7 +608,7 @@ public Object visit(TOverlaps contains, Object extraData) { private String buildCoordinatesString(Coordinate[] coordinates) { List coordinatesList = new ArrayList<>(); - for(Coordinate c : coordinates) { + for (Coordinate c : coordinates) { // Use Locale.US to make Java use dot "." as decimal separator String coordsValue = String.format(Locale.US, "[%f, %f] ", c.getX(), c.getY()); @@ -684,4 +618,14 @@ private String buildCoordinatesString(Coordinate[] coordinates) { return String.join(" , ", coordinatesList); } + + private void checkFilterExpressionsInBinaryComparisonOperator(BinaryComparisonOperator filter) { + if (!(filter.getExpression1() instanceof PropertyName)) { + throw new IllegalArgumentException("Invalid expression property provided"); + } + + if (!(filter.getExpression2() instanceof Literal)) { + throw new IllegalArgumentException("Invalid expression literal provided"); + } + } } diff --git a/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2EsTest.java b/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2EsTest.java index 4b8a6024258..f8a31dabbfb 100644 --- a/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2EsTest.java +++ b/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswFilter2EsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2023 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -29,10 +29,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import org.fao.geonet.kernel.csw.services.getrecords.FilterParser; import org.fao.geonet.kernel.csw.services.getrecords.IFieldMapper; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.geotools.api.filter.Filter; import org.geotools.api.filter.capability.FilterCapabilities; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -141,12 +141,11 @@ void testPropertyIsEqualTo() throws IOException { // INPUT: final String input = "\n" // - + " \n" // - + " Title\n" // - + " Hydrological\n" // - + " \n" // - + " " // - + ""; + + " \n" // + + " Title\n" // + + " Hydrological\n" // + + " \n" // + + " "; // EXPECTED: final ObjectNode expected = EsJsonHelper.boolbdr(). // @@ -157,34 +156,109 @@ void testPropertyIsEqualTo() throws IOException { assertFilterEquals(expected, input); } + @Test + void testPropertyIsEqualToSpecialChars() throws IOException { + final String input = + "\n" // + + " \n" // + + " OnlineResourceType\n" // + + " OGC:WMS\n" // + + " \n" // + + " "; + + // EXPECTED: + final ObjectNode expected = EsJsonHelper.boolbdr(). // + must(array(queryStringPart("OnlineResourceType", "OGC\\:WMS"))). // + filter(queryStringPart()). // + bld(); + + assertFilterEquals(expected, input); + } + + @Test + void testPropertyIsLike() throws IOException { + + final String input = + "\n" // + + " \n" // + + " AnyText\n" // + + " s\\_rvice\\%\n" // + + " \n" // + + " "; + + // EXPECTED: + final ObjectNode expected = EsJsonHelper.boolbdr(). // + must(array(queryStringPart("AnyText", "s?rvice*"))). // + filter(queryStringPart()). // + bld(); + + assertFilterEquals(expected, input); + } + + @Test + void testPropertyIsLikeSpecialChars() throws IOException { + + final String input = + "\n" // + + " \n" // + + " AnyText\n" // + + " \"service\"\n" // + + " \n" // + + " "; + + // EXPECTED: + final ObjectNode expected = EsJsonHelper.boolbdr(). // + must(array(queryStringPart("AnyText", "\\\"service\\\""))). // + filter(queryStringPart()). // + bld(); + + assertFilterEquals(expected, input); + + + final String input2 = + "\n" // + + " \n" // + + " AnyText\n" // + + " OGC:WMS\\%\n" // + + " \n" // + + " "; + + // EXPECTED: + final ObjectNode expected2 = EsJsonHelper.boolbdr(). // + must(array(queryStringPart("AnyText", "OGC\\:WMS*"))). // + filter(queryStringPart()). // + bld(); + + assertFilterEquals(expected2, input2); + } + @Test void testLogicalAnd() throws IOException { // INPUT: final String input = " \n" // - + " \n" // - + " \n" // - + " Title\n" // - + " Hydrological\n" // - + " \n" // - + " \n" // - + " Title\n" // - + " Africa\n" // - + " \n" // - + " \n" // - + " \n" // - + ""; + + " \n" // + + " \n" // + + " Title\n" // + + " Hydrological\n" // + + " \n" // + + " \n" // + + " Title\n" // + + " Africa\n" // + + " \n" // + + " \n" // + + " \n"; // EXPECTED: final ObjectNode expected = EsJsonHelper.boolbdr(). // must( - array( - queryStringPart("Title", "Africa"), - queryStringPart("Title", "Hydrological"))) // + array( + queryStringPart("Title", "Africa"), + queryStringPart("Title", "Hydrological"))) // . // - filter(queryStringPart()). // - bld(); + filter(queryStringPart()). // + bld(); assertFilterEquals(expected, input); } @@ -195,23 +269,22 @@ void testSpatialBBox() throws IOException { // INPUT: final String input = // " \n" // - + " \n" // - + " \n" // - + " -180 -90\n" // - + " 180 90\n" // - + " \n" // - + " \n" // - + " \n" // - + ""; + + " \n" // + + " \n" // + + " -180 -90\n" // + + " 180 90\n" // + + " \n" // + + " \n" // + + " \n"; // EXPECTED: final ObjectNode expected = boolbdr(). // must(array(geoShape("geom", // - envelope(-180d, 90d, 180d, -90d), // - "intersects"))) // + envelope(-180d, 90d, 180d, -90d), // + "intersects"))) // . // - filter(queryStringPart()). // - bld(); + filter(queryStringPart()). // + bld(); assertFilterEquals(expected, input); } @@ -227,33 +300,33 @@ void testFilterWithAndOrAndSpatialBBox() throws IOException { // INPUT: final String input = // " \n" // - + " \n" // - + " \n" // - + " \n" // - + " Type\n" // - + " data\n" // - + " \n" // - + " \n" // - + " Type\n" // - + " dataset\n" // - + " \n" // - + " \n" // - + " Type\n" // - + " datasetcollection\n" // - + " \n" // - + " \n" // - + " Type\n" // - + " series\n" // - + " \n" // - + " \n" // - + " \n" // - + " \n" // - + " -180 -90\n" // - + " 180 90\n" // - + " \n" // - + " \n" // - + " \n" // - + " "; + + " \n" // + + " \n" // + + " \n" // + + " Type\n" // + + " data\n" // + + " \n" // + + " \n" // + + " Type\n" // + + " dataset\n" // + + " \n" // + + " \n" // + + " Type\n" // + + " datasetcollection\n" // + + " \n" // + + " \n" // + + " Type\n" // + + " series\n" // + + " \n" // + + " \n" // + + " \n" // + + " \n" // + + " -180 -90\n" // + + " 180 90\n" // + + " \n" // + + " \n" // + + " \n" // + + " "; final ObjectNode propertiesPart = boolbdr().should(array( // queryStringPart("Type", "series"), // @@ -270,10 +343,10 @@ void testFilterWithAndOrAndSpatialBBox() throws IOException { // EXPECTED: final ObjectNode expected = boolbdr(). // must(array(geoShapePart, // - propertiesPart)) // + propertiesPart)) // . // - filter(queryStringPart()). // - bld(); + filter(queryStringPart()). // + bld(); assertFilterEquals(expected, input); } diff --git a/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswSortBy2EsTest.java b/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswSortBy2EsTest.java index e233ed945a8..0d177599546 100644 --- a/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswSortBy2EsTest.java +++ b/csw-server/src/test/java/org/fao/geonet/kernel/csw/services/getrecords/es/CswSortBy2EsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2021 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) * @@ -25,9 +25,7 @@ import static junit.framework.TestCase.assertEquals; -import org.checkerframework.checker.units.qual.A; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; +import co.elastic.clients.elasticsearch._types.SortOptions; import org.fao.geonet.constants.Geonet; import org.fao.geonet.csw.common.Csw; import org.fao.geonet.kernel.csw.CatalogConfiguration; @@ -70,12 +68,12 @@ void configureDefaultSort() { Element request = createSortByBaseRequest( new Element("Empty", Geonet.Namespaces.OGC)); - List> sortFields = toTest.parseSortBy(request); + List sortFields = toTest.parseSortBy(request); assertEquals(1, sortFields.size()); - FieldSortBuilder sortField = (FieldSortBuilder)sortFields.get(0); - assertEquals(sortField.getFieldName(), "resourceTitleObject.default.keyword"); - assertEquals(sortField.order().toString(), "desc"); + SortOptions sortField = sortFields.get(0); + assertEquals(sortField.field().field(), "resourceTitleObject.default.keyword"); + assertEquals(sortField.field().order().jsonValue(), "desc"); } @Test @@ -86,12 +84,12 @@ void sortByRelevanceDESC() { .addContent(new Element("PropertyName", Geonet.Namespaces.OGC).setText("Relevance")) .addContent(new Element("SortOrder", Geonet.Namespaces.OGC).setText("DESC")))); - List> sortFields = toTest.parseSortBy(request); + List sortFields = toTest.parseSortBy(request); assertEquals(1, sortFields.size()); - FieldSortBuilder sortField = (FieldSortBuilder)sortFields.get(0); - assertEquals(sortField.getFieldName(), "_score"); - assertEquals(sortField.order().toString(), "desc"); + SortOptions sortField = sortFields.get(0); + assertEquals(sortField.field().field(), "_score"); + assertEquals(sortField.field().order().jsonValue(), "desc"); } @Test @@ -102,12 +100,12 @@ void sortByRelevanceASC() { .addContent(new Element("PropertyName", Geonet.Namespaces.OGC).setText("Relevance")) .addContent(new Element("SortOrder", Geonet.Namespaces.OGC).setText("ASC")))); - List> sortFields = toTest.parseSortBy(request); + List sortFields = toTest.parseSortBy(request); assertEquals(1, sortFields.size()); - FieldSortBuilder sortField = (FieldSortBuilder)sortFields.get(0); - assertEquals(sortField.getFieldName(), "_score"); - assertEquals(sortField.order().toString(), "asc"); + SortOptions sortField = sortFields.get(0); + assertEquals(sortField.field().field(), "_score"); + assertEquals(sortField.field().order().jsonValue(), "asc"); } @Test @@ -118,12 +116,12 @@ void sortByIndexField() { .addContent(new Element("PropertyName", Geonet.Namespaces.OGC).setText("title")) .addContent(new Element("SortOrder", Geonet.Namespaces.OGC).setText("DESC")))); - List> sortFields = toTest.parseSortBy(request); + List sortFields = toTest.parseSortBy(request); assertEquals(1, sortFields.size()); - FieldSortBuilder sortField = (FieldSortBuilder)sortFields.get(0); - assertEquals(sortField.getFieldName(), "title"); - assertEquals(sortField.order().toString(), "desc"); + SortOptions sortField = sortFields.get(0); + assertEquals(sortField.field().field(), "title"); + assertEquals(sortField.field().order().jsonValue(), "desc"); } @Test @@ -137,15 +135,15 @@ void sortByMultipleProperties() { .addContent(new Element("PropertyName", Geonet.Namespaces.OGC).setText("Relevance")) .addContent(new Element("SortOrder", Geonet.Namespaces.OGC).setText("DESC")))); - List> sortFields = toTest.parseSortBy(request); + List sortFields = toTest.parseSortBy(request); assertEquals(2, sortFields.size()); - FieldSortBuilder sortField = (FieldSortBuilder)sortFields.get(0); - assertEquals(sortField.getFieldName(), "title"); - assertEquals(sortField.order().toString(), "desc"); - FieldSortBuilder sortField2 = (FieldSortBuilder)sortFields.get(1); - assertEquals(sortField2.getFieldName(), "_score"); - assertEquals(sortField2.order().toString(), "desc"); + SortOptions sortField = sortFields.get(0); + assertEquals(sortField.field().field(), "title"); + assertEquals(sortField.field().order().jsonValue(), "desc"); + SortOptions sortField2 = sortFields.get(1); + assertEquals(sortField2.field().field(), "_score"); + assertEquals(sortField2.field().order().jsonValue(), "desc"); } private Element createSortByBaseRequest(Element SortBy) { diff --git a/datastorages/cmis/pom.xml b/datastorages/cmis/pom.xml index e664f2104d3..dd96c065dd8 100644 --- a/datastorages/cmis/pom.xml +++ b/datastorages/cmis/pom.xml @@ -28,7 +28,7 @@ gn-datastorages org.geonetwork-opensource.datastorage - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java b/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java index 87f74ab9982..769267a5a14 100644 --- a/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java +++ b/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java @@ -617,7 +617,7 @@ protected Path getBaseMetadataDir(ServiceContext context, Path metadataFullDir) } private GeonetworkDataDirectory getDataDirectory(ServiceContext context) { - return context.getBean(GeonetworkDataDirectory.class); + return ApplicationContextHolder.get().getBean(GeonetworkDataDirectory.class); } /** @@ -690,7 +690,7 @@ protected MetadataResourceExternalManagementProperties getMetadataResourceExtern if (metadataResourceExternalManagementPropertiesUrl.contains("{lang}") || metadataResourceExternalManagementPropertiesUrl.contains("{ISO3lang}")) { final IsoLanguagesMapper mapper = ApplicationContextHolder.get().getBean(IsoLanguagesMapper.class); - String contextLang = context.getLanguage() == null ? Geonet.DEFAULT_LANGUAGE : context.getLanguage(); + String contextLang = context==null || context.getLanguage() == null ? Geonet.DEFAULT_LANGUAGE : context.getLanguage(); String lang; String iso3Lang; diff --git a/datastorages/jcloud/pom.xml b/datastorages/jcloud/pom.xml index ebd05a9cbc9..7d2dc8caeee 100644 --- a/datastorages/jcloud/pom.xml +++ b/datastorages/jcloud/pom.xml @@ -28,7 +28,7 @@ gn-datastorages org.geonetwork-opensource.datastorage - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/datastorages/jcloud/src/main/java/org/fao/geonet/api/records/attachments/JCloudStore.java b/datastorages/jcloud/src/main/java/org/fao/geonet/api/records/attachments/JCloudStore.java index d7ed09b1837..c4f11d4a365 100644 --- a/datastorages/jcloud/src/main/java/org/fao/geonet/api/records/attachments/JCloudStore.java +++ b/datastorages/jcloud/src/main/java/org/fao/geonet/api/records/attachments/JCloudStore.java @@ -30,6 +30,7 @@ import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; +import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.MetadataResource; @@ -381,7 +382,7 @@ private Path getBaseMetadataDir(ServiceContext context, Path metadataFullDir) { } private GeonetworkDataDirectory getDataDirectory(ServiceContext context) { - return context.getBean(GeonetworkDataDirectory.class); + return ApplicationContextHolder.get().getBean(GeonetworkDataDirectory.class); } /** diff --git a/datastorages/pom.xml b/datastorages/pom.xml index eecab5b2dab..790fa36bffd 100644 --- a/datastorages/pom.xml +++ b/datastorages/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/datastorages/s3/pom.xml b/datastorages/s3/pom.xml index 9be23df9a98..f93af3fd4f9 100644 --- a/datastorages/s3/pom.xml +++ b/datastorages/s3/pom.xml @@ -28,7 +28,7 @@ gn-datastorages org.geonetwork-opensource.datastorage - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/docs/README.md b/docs/README.md index d97e063a283..79b0e5b4afd 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,11 +1,9 @@ # Documentation - This module contains: -* `manual` in Markdown format for mkdocs -* `manuals` in RST format for sphinx-build (vai https://github.com/geonetwork/doc) -* Change logs txt files + +* `changes/` change logs txt files +* `manual/` in Markdown format for MkDocs +* `licenses/` for library +* `schema-doc/` folder with scripts to generate extra docs * [copyright.txt](copyright.txt) Copyright template -* `licenses` for library -* Utility folder with scripts to generate extra docs -* `translate` python module for help with sphinx-build to mkdocs diff --git a/docs/changes2.1.x.txt b/docs/changes/changes2.1.x.txt similarity index 100% rename from docs/changes2.1.x.txt rename to docs/changes/changes2.1.x.txt diff --git a/docs/changes2.10.x.txt b/docs/changes/changes2.10.x.txt similarity index 100% rename from docs/changes2.10.x.txt rename to docs/changes/changes2.10.x.txt diff --git a/docs/changes2.2.x.txt b/docs/changes/changes2.2.x.txt similarity index 100% rename from docs/changes2.2.x.txt rename to docs/changes/changes2.2.x.txt diff --git a/docs/changes2.4.x.txt b/docs/changes/changes2.4.x.txt similarity index 100% rename from docs/changes2.4.x.txt rename to docs/changes/changes2.4.x.txt diff --git a/docs/changes2.6.x.txt b/docs/changes/changes2.6.x.txt similarity index 100% rename from docs/changes2.6.x.txt rename to docs/changes/changes2.6.x.txt diff --git a/docs/changes2.8.x.txt b/docs/changes/changes2.8.x.txt similarity index 100% rename from docs/changes2.8.x.txt rename to docs/changes/changes2.8.x.txt diff --git a/docs/changes3.0.0-0.txt b/docs/changes/changes3.0.0-0.txt similarity index 100% rename from docs/changes3.0.0-0.txt rename to docs/changes/changes3.0.0-0.txt diff --git a/docs/changes3.0.1-0.txt b/docs/changes/changes3.0.1-0.txt similarity index 100% rename from docs/changes3.0.1-0.txt rename to docs/changes/changes3.0.1-0.txt diff --git a/docs/changes3.0.2-0.txt b/docs/changes/changes3.0.2-0.txt similarity index 100% rename from docs/changes3.0.2-0.txt rename to docs/changes/changes3.0.2-0.txt diff --git a/docs/changes3.0.3-0.txt b/docs/changes/changes3.0.3-0.txt similarity index 100% rename from docs/changes3.0.3-0.txt rename to docs/changes/changes3.0.3-0.txt diff --git a/docs/changes3.0.4-0.txt b/docs/changes/changes3.0.4-0.txt similarity index 100% rename from docs/changes3.0.4-0.txt rename to docs/changes/changes3.0.4-0.txt diff --git a/docs/changes3.0.5-0.txt b/docs/changes/changes3.0.5-0.txt similarity index 100% rename from docs/changes3.0.5-0.txt rename to docs/changes/changes3.0.5-0.txt diff --git a/docs/changes3.10.0-0.txt b/docs/changes/changes3.10.0-0.txt similarity index 100% rename from docs/changes3.10.0-0.txt rename to docs/changes/changes3.10.0-0.txt diff --git a/docs/changes3.10.1-0.txt b/docs/changes/changes3.10.1-0.txt similarity index 100% rename from docs/changes3.10.1-0.txt rename to docs/changes/changes3.10.1-0.txt diff --git a/docs/changes3.10.2-0.txt b/docs/changes/changes3.10.2-0.txt similarity index 100% rename from docs/changes3.10.2-0.txt rename to docs/changes/changes3.10.2-0.txt diff --git a/docs/changes3.10.3-0.txt b/docs/changes/changes3.10.3-0.txt similarity index 100% rename from docs/changes3.10.3-0.txt rename to docs/changes/changes3.10.3-0.txt diff --git a/docs/changes3.10.4-0.txt b/docs/changes/changes3.10.4-0.txt similarity index 100% rename from docs/changes3.10.4-0.txt rename to docs/changes/changes3.10.4-0.txt diff --git a/docs/changes3.10.5-0.txt b/docs/changes/changes3.10.5-0.txt similarity index 100% rename from docs/changes3.10.5-0.txt rename to docs/changes/changes3.10.5-0.txt diff --git a/docs/changes3.10.6-0.txt b/docs/changes/changes3.10.6-0.txt similarity index 100% rename from docs/changes3.10.6-0.txt rename to docs/changes/changes3.10.6-0.txt diff --git a/docs/changes3.12.0-0.txt b/docs/changes/changes3.12.0-0.txt similarity index 100% rename from docs/changes3.12.0-0.txt rename to docs/changes/changes3.12.0-0.txt diff --git a/docs/changes/changes3.12.12-0.txt b/docs/changes/changes3.12.12-0.txt new file mode 100644 index 00000000000..43a4fce6aa0 --- /dev/null +++ b/docs/changes/changes3.12.12-0.txt @@ -0,0 +1,64 @@ +================================================================================ +=== +=== GeoNetwork 3.12.12: List of changes +=== +================================================================================ +- Documentation / GeoNetwork 3.12 doing a release fixes (#7852) +- [Backport 3.12.x] GeoNetwork harvester / Check if a resource exists to save it, instead of trying to retrieve the file details, to avoid confusing NoSuchFileException exception (#7846) +- Harvesters / Reset harvester history pagination when selecting a harvester (#7836) +- Follow up of #7279 to unify the button links in the metadata detail page as done for the analog change in main branch (#7391) +- Remove invalid empty migration added accidentally in https://github.com/geonetwork/core-geonetwork/commit/93377dd1866a5ee3f5b0098bcd1dd6188c009771 (#7821) +- Doc / Editor configuration improvements (#7826) +- [Backport 3.12.x] Bump actions/setup-java from 4.0.0 to 4.1.0 (#7816) +- [Backport 3.12.x] Fix alignment of user enabled checkbox (#7772) +- Remove handlebars.js v2.0.0 (#7762) +- update 3.12.x branch to recent sphinx-build and crank up warnings +- Addressing docs glitch #7666 creating-group and authentication-mode +- Addressing docs glitch #7666 tutorials/deployment/index +- addressing docs-glitch in install-guide/configuring-database +- addressing docs-glitch in search-ui/enrichview and search-ui/loadview +- addressing docs-glitch in install-guide/map-print-setup +- addressing docs-glitch in publishing/managing-privileges +- corrected minor typo in install-guide/map-print-setup +- Fix conversion errors after switching to MkDocs +- manual review of mkdocs glitches +- [Backport 3.12.x] Create a metadata / Add dynamic and download privileges to the users in the same group (#7748) +- Metadata detail page - don't display the resources description duplicated (#6798) +- Use the generated metadata UUID for resource links when importing metadata with the option 'Generate UUID' (#7734) +- Remove unused jslint-maven-plugin +- [Backport 3.12.x] Bump org.json:json from 20140107 to 20240205 (#7723) +- [Backport 3.12.x] Github Actions / Bump stCarolas/setup-maven from 4 to 5 (#7719) +- [Backport 3.12.x] Bump commons-fileupload from 1.3.3 to 1.5 (#7698) +- Fix pdf link issue +- Fix mimetypes on attachments as some were incorrect. (#7676) +- Docs / Update copyright year +- Fix the grid on the homepage of the documentation (#7559) +- troubleshoot release module order of execution and profile acivation and handling of jetty folder +- Remember to include -Drelease flag so that all modules (even optional modules) are compiled and tested during QA +- [BP] Bump actions/setup-java from 3.12.0 to 4.0.0 (#7522) +- [BP] Service context null pointer (#7593) +- [BP] Overview not shown in PDF export when the overview image is stored in GeoNetwork and requires authentication to access it. Fixes #7540 (#7556) +- [BP] Update iso19139 csw-full.xsl (#7558) +- [BP] Add ownerId to geonet:info (#7547) +- [BP] Don't display header menu and footer in single metadata PDF export (#7532) +- [BP] Bump actions/setup-python from 4 to 5 (#7543) +- [BP] Remote INSPIRE Atom Feeds harvester - Remove duplicates by dataset identifier (#7491) +- [BP] When getting locale message, default locale to LocaleContextHolder when locale is null (#7516) +- [BP] Fix some cases that were not considering both message and description when displaying errors. (#7517) +- [BP] Docs / Fix the mike version to 2.0.0 and change the parameter --no-redirect to --alias-type=copy (changed in mike 2.0.0) (#7507) +- [BP] Check http links in documentation (#7496) +- [BP] Update manual links to use https://docs.geonetwork-opensource.org/ (#7487) +- [BP] Change the structure of the MkDocs assets. Stylesheets and logos are moved to the `overrides` directory (#7429) +- Fix publish link (#7479) +- Fix url link in full view. bracket ") " could be included in link (#7483) +- [BP] Batch edit access level for editor role (#7464) +- [BP] Add node identifier parameter for xlst processing. Required in skin.xsl (#7454) +- Fix other exceptions in tests related to (#6977) +- [BP] Remove exception class name from the error message (#6977) +- [BP] Fix cookies path when deployed on root "/" context (#7446) +- [BP] Fix exception handling from schematron validation so that it flags the metadata as invalid if there is an exception (#6978) +- [BP] Remove old password field for admins (#7417) +- [backport 3.12.x] Add documentation to GitHub workflows (#7414) +- [BP] Change the url the icon in the homepage is linking to (#7422) +- Remove changelog for latest and stable +- Correct canonical_version to stable diff --git a/docs/changes3.2.0-0.txt b/docs/changes/changes3.2.0-0.txt similarity index 100% rename from docs/changes3.2.0-0.txt rename to docs/changes/changes3.2.0-0.txt diff --git a/docs/changes3.2.1-0.txt b/docs/changes/changes3.2.1-0.txt similarity index 100% rename from docs/changes3.2.1-0.txt rename to docs/changes/changes3.2.1-0.txt diff --git a/docs/changes3.2.2-0.txt b/docs/changes/changes3.2.2-0.txt similarity index 100% rename from docs/changes3.2.2-0.txt rename to docs/changes/changes3.2.2-0.txt diff --git a/docs/changes3.4.0-0.txt b/docs/changes/changes3.4.0-0.txt similarity index 100% rename from docs/changes3.4.0-0.txt rename to docs/changes/changes3.4.0-0.txt diff --git a/docs/changes3.4.1-0.txt b/docs/changes/changes3.4.1-0.txt similarity index 100% rename from docs/changes3.4.1-0.txt rename to docs/changes/changes3.4.1-0.txt diff --git a/docs/changes3.4.2-0.txt b/docs/changes/changes3.4.2-0.txt similarity index 100% rename from docs/changes3.4.2-0.txt rename to docs/changes/changes3.4.2-0.txt diff --git a/docs/changes3.6.0-0.txt b/docs/changes/changes3.6.0-0.txt similarity index 100% rename from docs/changes3.6.0-0.txt rename to docs/changes/changes3.6.0-0.txt diff --git a/docs/changes3.8.0-0.txt b/docs/changes/changes3.8.0-0.txt similarity index 100% rename from docs/changes3.8.0-0.txt rename to docs/changes/changes3.8.0-0.txt diff --git a/docs/changes3.8.1-0.txt b/docs/changes/changes3.8.1-0.txt similarity index 100% rename from docs/changes3.8.1-0.txt rename to docs/changes/changes3.8.1-0.txt diff --git a/docs/changes3.8.2-0.txt b/docs/changes/changes3.8.2-0.txt similarity index 100% rename from docs/changes3.8.2-0.txt rename to docs/changes/changes3.8.2-0.txt diff --git a/docs/changes3.8.3-0.txt b/docs/changes/changes3.8.3-0.txt similarity index 100% rename from docs/changes3.8.3-0.txt rename to docs/changes/changes3.8.3-0.txt diff --git a/docs/changes4.0.0-0.txt b/docs/changes/changes4.0.0-0.txt similarity index 100% rename from docs/changes4.0.0-0.txt rename to docs/changes/changes4.0.0-0.txt diff --git a/docs/changes4.0.0-alpha.1.txt b/docs/changes/changes4.0.0-alpha.1.txt similarity index 100% rename from docs/changes4.0.0-alpha.1.txt rename to docs/changes/changes4.0.0-alpha.1.txt diff --git a/docs/changes4.0.0-alpha.2.txt b/docs/changes/changes4.0.0-alpha.2.txt similarity index 100% rename from docs/changes4.0.0-alpha.2.txt rename to docs/changes/changes4.0.0-alpha.2.txt diff --git a/docs/changes4.0.1-0.txt b/docs/changes/changes4.0.1-0.txt similarity index 100% rename from docs/changes4.0.1-0.txt rename to docs/changes/changes4.0.1-0.txt diff --git a/docs/changes4.0.2-0.txt b/docs/changes/changes4.0.2-0.txt similarity index 100% rename from docs/changes4.0.2-0.txt rename to docs/changes/changes4.0.2-0.txt diff --git a/docs/changes4.0.3-0.txt b/docs/changes/changes4.0.3-0.txt similarity index 100% rename from docs/changes4.0.3-0.txt rename to docs/changes/changes4.0.3-0.txt diff --git a/docs/changes4.0.4-0.txt b/docs/changes/changes4.0.4-0.txt similarity index 100% rename from docs/changes4.0.4-0.txt rename to docs/changes/changes4.0.4-0.txt diff --git a/docs/changes4.0.5-0.txt b/docs/changes/changes4.0.5-0.txt similarity index 100% rename from docs/changes4.0.5-0.txt rename to docs/changes/changes4.0.5-0.txt diff --git a/docs/changes4.0.6-0.txt b/docs/changes/changes4.0.6-0.txt similarity index 100% rename from docs/changes4.0.6-0.txt rename to docs/changes/changes4.0.6-0.txt diff --git a/docs/changes4.2.0-0.txt b/docs/changes/changes4.2.0-0.txt similarity index 100% rename from docs/changes4.2.0-0.txt rename to docs/changes/changes4.2.0-0.txt diff --git a/docs/changes4.2.1-0.txt b/docs/changes/changes4.2.1-0.txt similarity index 100% rename from docs/changes4.2.1-0.txt rename to docs/changes/changes4.2.1-0.txt diff --git a/docs/changes4.2.2-0.txt b/docs/changes/changes4.2.2-0.txt similarity index 100% rename from docs/changes4.2.2-0.txt rename to docs/changes/changes4.2.2-0.txt diff --git a/docs/changes4.2.3-0.txt b/docs/changes/changes4.2.3-0.txt similarity index 100% rename from docs/changes4.2.3-0.txt rename to docs/changes/changes4.2.3-0.txt diff --git a/docs/changes4.2.4-0.txt b/docs/changes/changes4.2.4-0.txt similarity index 100% rename from docs/changes4.2.4-0.txt rename to docs/changes/changes4.2.4-0.txt diff --git a/docs/changes4.2.5-0.txt b/docs/changes/changes4.2.5-0.txt similarity index 100% rename from docs/changes4.2.5-0.txt rename to docs/changes/changes4.2.5-0.txt diff --git a/docs/changes/changes4.2.8-0.txt b/docs/changes/changes4.2.8-0.txt new file mode 100644 index 00000000000..4775ab02b1e --- /dev/null +++ b/docs/changes/changes4.2.8-0.txt @@ -0,0 +1,84 @@ +================================================================================ +=== +=== GeoNetwork 4.2.8: List of changes +=== +================================================================================ +- [BP] Transifex updates. (#7651) +- [BP] Upgrade guidance for geonetwork 3 users (#7644) +- Remove unused Jeeves classes (#7643) +- Miscellaneous code cleanup with MetadataStatus and RelatedResponse Fix some incorrect comments Remove class prefix as it was not required. Added missing license. +- Move Direction as an reference object in the open api specification. +- Bump actions/cache from 3 to 4 +- [BP] build release procedure updates to include docs in release tag (#7412) +- [BP] unpack schema sample data into folders, produce mef files at build time (#7457) +- document `backport 4.2.x` label change required for bot +- Fix the grid on the homepage of the documentation (#7559) +- [BP] Fix error on edit page elements with non-unique id #gnRemoteRecordUrl (#7621) +- [BP] Record view / Display WFS downloads for WFS online resources without a name defined (#7626) +- Do not clean api docs out of src/main/webapp +- build fix: do not remove api docs during clean:clean@reset +- build: fix jetty-download activiation required for release +- [BP] Bump actions/setup-java from 3.12.0 to 4.0.0 (#7522) +- [BP] Build / Use Java 21 for Sonarcloud plugin (#7622) +- [BP] Build / release module build workflow improvements / fixes (#7619) +- [BP] Remove default response from open api specification. (#7609) +- [BP] Cleanup consumes from metadata insert api. (#7616) +- [BP] Add missing @ApiResponse status for successful api search response. (#7594) +- [BP] Fix SpringDoc duplicate OperationId (#7580) +- [BP] CSW GetRecords doesn't escape query values when creating the Elasticsearch query (#7529) +- [BP] Add missing import from commit 63cd59fb38df389694566d69459bcfbedb7ca89e (Service context null pointer (#7593)) +- [BP] Changed http to https for urls that appear in open api specification. (#7601) +- [BP] Fix duplicate API endpoint /feeds (#7581) +- [BP] Service context null pointer (#7593) +- [BP] Fix linux specific file separator used for harvester transform option list +- [BP] Fix issue with @ResponseStatus and @ApiResponse being out of sync. (#7588) +- [BP] Spring doc - Set API enum as ref (#7595) +- [BP] Add missing swagger icons that were referenced in the index.html file. Icons taken from https://github.com/swagger-api/swagger-ui/tree/master/dist +- Fix duplicate operation id caused by use of 2 methods GET/POST method from getKeywordById api (#7586) +- [BP] Fix add element attribute in the metadata editor, causing the element section is removed from the user interface until the metadata is saved +- [BP] Record view / Invalid timezone shift for years outside moment's 10 years range. +- [BP] Home page / sort topic categories and INSPIRE themes facets alphabetically (#7569) +- [BP] Metadata extents API / Make configurable to display the metadata bboxes using geodesic extents for local projections. (#7560) +- [BP] Spring doc API for link api should accept a structured object instead of JSONObject (#7585) +- [BP] Update SpringDoc json/yaml generator to make the results more deterministic. (#7574) +- [BP] Add missing apiResponse for group and map api (#7590) +- GeoNetwork 4.2.x minor versions library updates (#7405) +- [BP] API should return a structured object instead of JSONObject (#7584) +- [BP] Fix initialization of SpringDoc so that it does not use null servername and null version (#7575) +- [BP] Fix duplicate spring doc tags. Ensure that all tags have the same description. +- [BP] Fix case of wrong use for HttpStatus.CREATED and HttpStatus.OK In some cases HttpStatus.CREATED was used when it should have been HttpStatus.OK and other cases it is the opposite. +- [BP] Overview not shown in PDF export when the overview image is stored in GeoNetwork and requires authentication to access it. Fixes #7540 (#7556) +- [BP] fix for empty language leading to spurious comma +- [BP] Display metadata user and group owner in the transfer ownership dialog +- [BP] Add German codelist translations for scope code (#7566) +- [BP] Remove ?debug from link to admin dashboard. (#7564) +- [BP] Visual fix for icon text circles (firefox) +- [BP] cryptic parse exception +- [BP] Update iso19139 csw-full.xsl (#7558) +- [BP] Documentation / Update Elasticsearch installation page typos and remove the old elastic search documentation. Fixes #7551 (#7555) +- [BP] Metadata indexing - create an organisation name field that tracks the organisations of the different types of contacts +- [BP] Elasticsearch index creation - log the exception when a parsing error of the index configuration file occurs +- [BP] Add ownerId to geonet:info (#7547) +- [BP] Don't display header menu and footer in single metadata PDF export (#7532) +- [BP] Use Apache Commons Text library to escape JSON content in xslt processing (#7525) +- Edited small typos and text. Some link fixes. +- [BP] OpenId / Use the user profile configured locally if the configuration option OPENIDCONNECT_USERPROFILEUPDATEENABLED is disabled (#7445) +- [BP] Bump actions/setup-python from 4 to 5 (#7543) +- [BP] INSPIRE Validator - exception handling improvements (#7519) +- [BP] Fix check to verify if a group has enabled the workflow, checking if the workflow is also enabled (#7535) +- [BP] Reset user password dialog - don't display the field to request the old password for administrators - unify UI check with backend check (#7510) +- [BP] consistent styling of recordgroup label +- [BP] Metadata workflow / Record view / reload the page with the approved version when cancelling a working copy (#7503) +- [BP] Search / Add option to show less facet values (#7497) +- [BP] Metadata indexing / Escape website and logo information for contacts +- [BP] Validation of INSPIRE ATOM services to return API exceptions instead of error 400 (#7490) +- [BP] INSPIRE Atom Search - add missing filter by feed dataset identifiers (#7492) +- [BP] Remote INSPIRE Atom Feeds harvester - Remove duplicates by dataset identifier (#7491) +- [BP] Fix language for region picker directives (#7495) +- [BP] When getting locale message, default locale to LocaleContextHolder when locale is null (#7516) +- [BP] Fix some cases that were not considering both message and description when displaying errors. (#7517) +- [BP] Elasticsearch / Update to 7.17.15 (#7368) +- [BP] Fix indexing of iso19110 metadata with cardinalities composed of multiple ranges (#7486) +- [BP] Harvesting / WFS / Cleaning comment (#7504) +- [BP] CSW Harvester / Don't set a default search filter field (#7494) +- [BP] Docs / Fix the mike version to 2.0.0 and change the parameter --no-redirect to --alias-type=copy (changed in mike 2.0.0) (#7507) diff --git a/docs/changes/changes4.2.9-0.txt b/docs/changes/changes4.2.9-0.txt new file mode 100644 index 00000000000..28b376d7ba9 --- /dev/null +++ b/docs/changes/changes4.2.9-0.txt @@ -0,0 +1,70 @@ +================================================================================ +=== +=== GeoNetwork 4.2.9: List of changes +=== +================================================================================ +- Fix startup error. Follow up #7456 (#7859) +- Documentation / GeoNetwork 4.2 doing a release fixes (#7647) +- [Backport 4.2.x] Extend proxy to manage duplicated parameters (#7854) +- [Backport 4.2.x] Configuration to restrict the hosts and ports accessible by the http proxy servlet (#7326) +- [Backport 4.2.x] GeoNetwork harvester / Check if a resource exists to save it, instead of trying to retrieve the file details, to avoid confusing NoSuchFileException exception (#7845) +- Standards / Formatter / Citation / Pick latest date (#7835) +- [Backport 4.2.x] INSPIRE / Add testsuite for IACS (#7834) +- [Backport 4.2.x] Harvester / Localfilesystem / Log properly to harvester log file. (#7833) +- [Backport 4.2.x] Record view / ISO19139 / ISO19115-3.2008 display the unit part in @uom attribute, not the full url (#7832) +- Harvesters / Reset harvester history pagination when selecting a harvester (#7831) +- Trigger metadata unpublish event when removing the privileges to the ALL group in the privileges dialog (#7828) +- Doc / Editor configuration improvements (#7827) +- Update lodash to version 4.17.21 (#7825) +- [Backport 4.2.x] Bump actions/setup-java from 4.0.0 to 4.1.0 (#7814) +- Record view / Don't add the associated resources in the metadata static page, this page doesn't include JS libs (#7797) +- [Backport 4.2.x] Decouple metadata user feedback from metadata rating feature (#7796) +- [Backport 4.2.x] Fix wrong manual links (#7793) +- [Backport 4.2.x] Additional ISO19139 German translations (#7788) +- [Backport 4.2.x] Replace the 'unlock' icon with the 'lock open' icon (#7787) +- [Backport 4.2.x] Removed @RequestHeader for "Accept" headers as it is not supported by openAPI specification (#7785) +- Fix missing MetadataStatusResponse and MetadataWorkflowStatusResponse in open api spec (#7783) +- Fix SpringDoc duplicate Schema name (#7781) +- [Backport 4.2.x] Fix duplicate GET operation on /{portal}/api/sources and missing /subportal endpoint (#7780) +- Spit getRecordAs @RequestMapping into getRecordAsJson and getRecordAsXML in order to fix duplicate operation Id - By having multiple @RequestMapping was causing to create operation id as getRecordAs and getRecordAs_1 +- [Backport 4.2.x] Fix alignment of user enabled checkbox (#7773) +- [Backport 4.2.x] Fix ISO19139 German labels (#7763) +- Remove handlebars.js v2.0.0 +- Reports / Fix extract user groups for non-admin users (#7746) +- Addressing docs glitch #7666 creating-group and authentication-mode +- Addressing docs glitch #7666 in installing-from-war-file, version-4.0.2 and tutorials/deployment/index +- addressing docs-glitch in install-guide/configuring-database +- addressing docs-glitch in search-ui/enrichview and search-ui/loadview +- addressing docs-glitch in install-guide/map-print-setup +- addressing docs-glitch in publishing/managing-privileges +- corrected minor typo in install-guide/map-print-setup +- Fix conversion errors after switching to MkDocs +- manual review of mkdocs glitches +- [Backport 4.2.x] Create a metadata / Add dynamic and download privileges to the users in the same group (#7744) +- Index / Add danish language. (#7736) +- [Backport 4.2.x] Documentation / Elasticsearch query endpoint - query samples (#7732) +- [Backport 4.2.x] Separate docs for _search and _msearch (#7731) +- Map viewer / Remove Stamen background layers - no longer available (#7730) +- Use the generated metadata UUID for resource links when importing metadata with the option 'Generate UUID' (#7729) +- Remove unused jslint-maven-plugin (#7727) +- [Backport 4.2.x] Bump org.json:json from 20140107 to 20240205 (#7724) +- [Backport 4.2.x] Github Actions / Bump stCarolas/setup-maven from 4 to 5 (#7720) +- Enable preemptive for csw requests with credentials (#5497) (#7716) +- [Backport 4.2.x] Add a role and feature matrix to the GeoNetwork documentation (#7709) +- Addressing docs translation glitch #7687- fixes creating-custom-editor +- Addressing docs translation glitch #7687- fixes adding-static-pages and configuring-search-fields +- [Backport 4.2.x] Bump commons-fileupload from 1.3.3 to 1.5 (#7699) +- [Backport 4.2.x] Remove empty class SourcesLib and deprecated/unused methods in ResourceLib / Sonarlint improvements (#7694) +- Update Springdoc so that it supports Map objects in the request parameters. By default injectable parameters are excluded from request parameters. Map is one of those object however it does occur where map objects are supplied as parameters and they should be added to open api spec. There are currently no cases where a map is injected on purpose into the request parameters. This will fix issues with missing request parameters documentation which are based on Map objects. +- Fix spring doc for attachment and keyword to better identify files resources being returned. Update attachment api "Get a metadata resource" should indicate that gets a file resource Also "Create a new resource" should identify that it consumes any resources Update keywords api "Download a thesaurus by name" should indicate that gets a file resource +- Fix springdoc so that enums names are used instead of toString This fixes bug where some apis will not execute correctly from the swagger pager due to the wrong enum value being supplied. i.e. visibility should be using enum values PUBLIC/PRIVATE instead of public/private in formatters/zip api, the format should be SIMPLE/PARTIAL/FULL instead of simple/partial/full +- Editor / Fix add element attribute (#7685) +- [Backport 4.2.x] Metadata editor / Fix javascript error in the add thumbnail option when the metadata has 1 WMS layer (#7684) +- [BP] Search results / Configure related records type depending on template. (#7376) +- Metadata editor / Fix javascript error when editing a metadata, due to undefined property in gnLinkToMetadata directive (#7682) +- Fix pdf link issue (#7681) +- Fix mimetypes on attachments as some were incorrect. (#7675) +- accidental localhost link in docs +- Docs / Update copyright year +- Bump github/codeql-action from 2 to 3 (#7662) +- Bump advanced-security/maven-dependency-submission-action from 3 to 4 (#7661) diff --git a/docs/changes4.4.0-0.txt b/docs/changes/changes4.4.0-0.txt similarity index 100% rename from docs/changes4.4.0-0.txt rename to docs/changes/changes4.4.0-0.txt diff --git a/docs/changes4.4.1-0.txt b/docs/changes/changes4.4.1-0.txt similarity index 100% rename from docs/changes4.4.1-0.txt rename to docs/changes/changes4.4.1-0.txt diff --git a/docs/changes/changes4.4.2-0.txt b/docs/changes/changes4.4.2-0.txt new file mode 100644 index 00000000000..737215d8ac0 --- /dev/null +++ b/docs/changes/changes4.4.2-0.txt @@ -0,0 +1,120 @@ +================================================================================ +=== +=== GeoNetwork 4.4.2: List of changes +=== +================================================================================ +- Transifex updates. (#7651) +- Upgrade guidance for geonetwork 3 users (#7644) +- Documentation / Unify version numbers in doing a release page +- Remove unused Jeeves classes (#7612) +- Miscellaneous code cleanup with MetadataStatus and RelatedResponse Fix some incorrect comments Remove class prefix as it was not required. Added missing license. +- Move Direction as an reference object in the open api specification. +- Remove problematic migration to add a column +- Update GITHUB.md +- Bump actions/cache from 3 to 4 +- build release procedure updates to include docs in release tag (#7412) +- unpack schema sample data into folders, produce mef files at build time (#7457) +- Update CONTRIBUTING.md +- document `backport 4.2.x` label change required for bot +- Fix the grid on the homepage of the documentation (#7559) +- backport bot to respond to PR tags +- Fix error on edit page elements with non-unique id #gnRemoteRecordUrl (#7621) +- Record view / Display WFS downloads for WFS online resources without a name defined (#7626) +- Do not clean api docs out of src/main/webapp +- build fix: do not remove api docs during clean:clean@reset +- Bump actions/setup-java from 3.12.0 to 4.0.0 (#7522) +- Use Java 21 for Sonarcloud plugin (#7622) +- Build / release module build workflow improvements / fixes (#7619) +- Remove default response from open api specification. (#7609) +- Cleanup consumes from metadata insert api. (#7616) +- Add missing @ApiResponse status for successful api search response. (#7594) +- Fix SpringDoc duplicate OperationId (#7580) +- CSW GetRecords doesn't escape query values when creating the Elasticsearch query (#7529) +- Add missing import from commit 63cd59fb38df389694566d69459bcfbedb7ca89e (Service context null pointer (#7593)) +- Changed http to https for urls that appear in open api specification. (#7601) +- Fix duplicate API endpoint /feeds (#7581) +- Service context null pointer (#7593) +- Update files with Prettier formatting +- Preventing indexing error by limiting selection amount +- Fix linux specific file separator used for harvester transform option list +- Fix issue with @ResponseStatus and @ApiResponse being out of sync. (#7588) +- Spring doc - Set API enum as ref (#7595) +- Add missing swagger icons that were referenced in the index.html file. (#7597) +- using icon for call/phone +- feat: avoid race condition, resize map once target size ok (#7545) +- Configuration to restrict the hosts and ports accessible by the http proxy servlet (#7326) +- Search results / Configure related records type depending on template. (#7376) +- Fix duplicate operation id caused by use of 2 methods GET/POST method from getKeywordById api (#7586) +- Build info improvements. (#7400) +- Harvesting / WFS feature / Add WFS2 support for MapServer +- Github / PR template / Add funded by (#7554) +- INSPIRE Atom feeds / Fix link to atom search endpoint +- Fix add element attribute in the metadata editor, causing the element section is removed from the user interface until the metadata is saved +- fix duplicate check on uuid+initiative+association (#7567) +- Record view / Invalid timezone shift for years outside moment's 10 years range. +- Home page / sort topic categories and INSPIRE themes facets alphabetically (#7569) +- Metadata extents API / Make configurable to display the metadata bboxes using geodesic extents for local projections. (#7560) +- Spring doc API for link api should accept a structured object instead of JSONObject (#7585) +- Update SpringDoc json/yaml generator to make the results more deterministic. (#7574) +- Add missing apiResponse for group and map api (#7590) +- API should return a structured object instead of JSONObject (#7584) +- Fix initialization of SpringDoc so that it does not use null servername and null version (#7575) +- Fix duplicate spring doc tags. Ensure that all tags have the same description. +- Fix case of wrong use for HttpStatus.CREATED and HttpStatus.OK In some cases HttpStatus.CREATED was used when it should have been HttpStatus.OK and other cases it is the opposite. +- Overview not shown in PDF export when the overview image is stored in GeoNetwork and requires authentication to access it. Fixes #7540 (#7556) +- fix for empty language leading to spurious comma +- Add German codelist translations for scope code (#7566) +- Fix PULL_REQUEST_TEMPLATE.md link to contribution guidelines +- Remove ?debug from link to admin dashboard. (#7564) +- Update iso19139 csw-full.xsl (#7558) +- add icon to static pages, configurable +- cryptic parse exception +- Visual fix for icon text circles (firefox) +- Update authors.md +- Update authors.md +- Documentation / Update Elasticsearch installation page typos and remove the old elastic search documentation. Fixes #7551 (#7555) +- Restructure the 'composed of' block on the detail page (#7480) +- Metadata indexing - create an organisation name field that tracks the organisations of the different types of contacts +- Elasticsearch index creation - log the exception when a parsing error of the index configuration file occurs +- Add ownerId to geonet:info (#7547) +- Update PROCESS_FOR_DEPRECATION.md +- Create PROCESS_FOR_DEPRECATION.md +- Update CONTRIBUTING.md +- Don't display header menu and footer in single metadata PDF export (#7532) +- Edited small typos and text. Some link fixes. +- Use Apache Commons Text library to escape JSON content in xslt processing (#7525) +- Edited small typos and text. Some link fixes. +- OpenId / Use the user profile configured locally if the configuration option OPENIDCONNECT_USERPROFILEUPDATEENABLED is disabled (#7445) +- Bump actions/setup-python from 4 to 5 +- INSPIRE Validator - exception handling improvements (#7519) +- Automatic HTML formatting +- Fix check to verify if a group has enabled the workflow, checking if the workflow is also enabled (#7535) +- Admin console / Improve form checks for group, category and source (#7449) +- Administration form validation improvements (#7533) +- Reset user password dialog - don't display the field to request the old password for administrators - unify UI check with backend check (#7510) +- New property to define an alternate logo for pdf export (#7481) +- Metadata workflow / Record view / reload the page with the approved version when cancelling a working copy (#7503) +- consistent styling of recordgroup label +- Metadata indexing / Escape website and logo information for contacts +- Search / Add option to show less facet values (#7497) +- GeoNetwork 4.4.x minor versions library updates: +- Update CONTRIBUTING.md +- Update README.md +- Validation of INSPIRE ATOM services to return API exceptions instead of error 400 (#7490) +- INSPIRE Atom Search - add missing filter by feed dataset identifiers (#7492) +- Remote INSPIRE Atom Feeds harvester - Remove duplicates by dataset identifier (#7491) +- Fix language for region picker directives (#7495) +- When getting locale message, default locale to LocaleContextHolder when locale is null (#7516) +- Fix some cases that were not considering both message and description when displaying errors. (#7517) +- Elasticsearch / Update to 7.17.15 (#7368) +- Record view / DQ / Add measure date information +- Ensure only dataset are returned when linking dataset to service +- Record view / Add distributor contact. (#7473) +- Record view / Topic category / Translations +- Record view / Improve language list. +- Fix indexing of iso19110 metadata with cardinalities composed of multiple ranges (#7486) +- Harvesting / WFS / Cleaning comment (#7504) +- CSW Harvester / Don't set a default search filter field (#7494) +- Docs / Fix mike parameter with extra dash. Follow up of #7507 +- Docs / Fix the mike version to 2.0.0 and change the parameter --no-redirect to --alias-type=copy (changed in mike 2.0.0) (#7507) +- Update version 4.4.2-SNAPSHOT \ No newline at end of file diff --git a/docs/changes/changes4.4.3-0.txt b/docs/changes/changes4.4.3-0.txt new file mode 100644 index 00000000000..ce5661888d1 --- /dev/null +++ b/docs/changes/changes4.4.3-0.txt @@ -0,0 +1,117 @@ +================================================================================ +=== +=== GeoNetwork 4.4.3: List of changes +=== +================================================================================ +- Update linux workflow to maven 3.8.3 +- require maven 3.8.3 minimum for MNG-7214 fix +- Move version 3.12.x changelog to archive +- Release notes for GeoNetwork 4.4.3. +- Release notes for GeoNetwork 4.29 / 3.12.12 versions +- add necessary welsh language files for translating the application (#7851) +- Standard / ISO19115-3 / Batch edit may trigger error on creation date (#7712) +- Fix startup error. Follow up #7456 (#7858) +- i18n / Transifex update. (#7855) +- ISO19139 / Index online resources application profile element encoded as anchor (#7798) +- Extend proxy to manage duplicated parameters (#7456) +- Documentation / GeoNetwork 4.4 doing a release fixes (#7648) +- Indexing / ISO / Properly index all keywords even if in different thesaurus block +- Editor / Distribution / Properly refresh list link on last one (#7844) +- GeoNetwork harvester / Check if a resource exists to save it, instead of trying to retrieve the file details, to avoid confusing NoSuchFileException exception (#7577) +- Update README.md +- Update README.md +- Bump actions/upload-artifact from 3.1.0 to 4.3.1 +- Bump ossf/scorecard-action from 2.1.2 to 2.3.1 +- Harvesting / WFS Features / Do not skip attributes even if geom is invalid. +- Editor / Associated resource / Add button icon configuration. +- Editor / Distribution improvements - Update configuration for ISO19139 distributions as protocols are not categorized as ISO19115-3 protocols. (#7838) +- Standards / Formatter / Citation / Pick latest date +- INSPIRE / Add testsuite for IACS (#7756) +- Harvester / Localfilesystem / Log properly to harvester log file. (#7660) +- Harvester / WFS / No need to manually managed commit interval (#7737) +- Record view / ISO19139 / ISO19115-3.2008 display the unit part in @uom attribute, not the full url (#7791) +- Harvesters / Reset harvester history pagination when selecting a harvester +- Thesaurus / Improve support of EU publication office SKOS format (#7673) +- Create scorecard.yml +- Doc / Editor configuration improvements (#7776) +- Update lodash to version 4.17.21 +- Improve Elasticsearch manual installation to disable security for development +- Docker / Update docker compose in es module to Elasticsearch 8 (#7817) +- Trigger metadata unpublish event when removing the privileges to the ALL group in the privileges dialog +- Bump actions/setup-java from 4.0.0 to 4.1.0 (#7808) +- Translated the index warnings / errors. (#7531) +- minor typo fixes +- System setting for documentation url (#7782) +- Record view / Don't add the associated resources in the metadata static page, this page doesn't include JS libs +- Decouple metadata user feedback from metadata rating feature (#7770) +- Thesaurus / Add support for codelist described using SDMX +- Fix wrong manual links +- Standard / ISO19115-3 / Quality report / Index descriptive results +- Additional ISO19139 German translations (#7778) +- Update the `set privileges` popup with the new icon +- Replace the 'unlock' icon with the 'lock open' icon which is clearer. This new icon has more visual differences with the lock icon and it's therefore easier for a user to see the difference. +- Removed @RequestHeader for "Accept" headers as it is not supported by openAPI specification (#7572) +- Fix missing MetadataStatusResponse and MetadataWorkflowStatusResponse in open api spec (#7627) +- Fix SpringDoc duplicate Schema name Without this fix springdoc would randomly pick between 2 conflicting schemas and this could produce incorrect results in the open api spec. +- Merge getSubPortals into getSources so that there is only one GET api for the operation. This fixes the bug with 2 GET operation on /{portal}/api/sources +- Update services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java +- Fix duplicate GET operation on /{portal}/api/sources Add missing /subportal endpoint. +- Spit getRecordAs @RequestMapping into getRecordAsJson and getRecordAsXML in order to fix duplicate operation Id - By having multiple @RequestMapping was causing to create operation id as getRecordAs and getRecordAs_1 +- Fix alignment of user enabled checkbox (#7764) +- Fix ISO19139 German labels (#7761) +- Remove handlebars.js v2.0.0 +- Fix query field name in OverviewIndexFieldUpdater to update the metadata overview in the index +- API / Category / Fix update fields +- Addressing docs glitch #7666 creating-group and authentication-mode +- Addressing docs glitch #7666 in installing-from-war-file, version-4.0.2 and tutorials/deployment/index +- addressing docs-glitch in install-guide/configuring-database +- addressing docs-glitch in search-ui/enrichview and search-ui/loadview +- addressing docs-glitch in install-guide/map-print-setup +- addressing docs-glitch in publishing/managing-privileges +- corrected minor typo in install-guide/map-print-setup +- Fix conversion errors after switching to MkDocs +- manual review of mkdocs glitches +- Reports / Fix extract user groups for non-admin users (#7742) +- Create a metadata / Add dynamic and download privileges to the users in the same group (#7679) +- Thesaurus / Add support for thesaurus described using OWL format +- Editor / Add view name class to facilitate custom styling +- Update SECURITY.md +- Index / Add danish language. (#7697) +- Standard / ISO19115-3 / Improve french translation for temporal extent (#7700) +- Standard / ISO19115-3 / Editor configuration / Improve date field configuration (#7702) +- Documentation / Elasticsearch query endpoint - query samples (#7722) +- remove mention of q query parameter +- Separate docs for _search and _msearch +- Map viewer / Remove Stamen background layers - no longer available +- Use the generated metadata UUID for resource links when importing metadata with the option 'Generate UUID' +- Elasticssearch 8 upgrade (#7599) +- Vertical extent label modification (#7604) +- Remove unused jslint-maven-plugin (#7725) +- Bump org.json:json from 20140107 to 20240205 (#7701) +- using gn- icon definitions for all types now +- Github Actions / Bump stCarolas/setup-maven from 4 to 5 (#7718) +- Enable preemptive for csw requests with credentials (#5497) (#7706) +- Add a role and feature matrix to the GeoNetwork documentation (#7686) +- Addressing docs translation glitch #7687- fixes adding-static-pages and configuring-search-fields (#7696) +- Automatic formatting +- Bump commons-fileupload from 1.3.3 to 1.5 (#6851) +- Remove empty class SourcesLib and deprecated/unused methods in ResourceLib / Sonarlint improvements (#7692) +- Fix spring doc for attachment and keyword to better identify files resources being returned. Update attachment api "Get a metadata resource" should indicate that gets a file resource Also "Create a new resource" should identify that it consumes any resources Update keywords api "Download a thesaurus by name" should indicate that gets a file resource +- Fix springdoc so that enums names are used instead of toString This fixes bug where some apis will not execute correctly from the swagger pager due to the wrong enum value being supplied. i.e. visibility should be using enum values PUBLIC/PRIVATE instead of public/private in formatters/zip api, the format should be SIMPLE/PARTIAL/FULL instead of simple/partial/full +- Update Springdoc so that it supports Map objects in the request parameters. By default injectable parameters are excluded from request parameters. Map is one of those object however it does occur where map objects are supplied as parameters and they should be added to open api spec. There are currently no cases where a map is injected on purpose into the request parameters. This will fix issues with missing request parameters documentation which are based on Map objects. +- Editor / Fix add element attribute (#7683) +- Metadata editor / Fix javascript error in the add thumbnail option when the metadata has 1 WMS layer (#7646) +- Add configuration to filter out Elasticsearch fields when download or dynamic privileges are not set +- Fix javascript error accessing the metadata detail page in gnMetadataSocialLink directive +- Metadata editor / Fix javascript error when editing a metadata, due to undefined property in gnLinkToMetadata directive +- Fix pdf link issue (#7667) +- Editor / Distribution improvements (#7468) +- Fix mimetypes on attachments as some were incorrect. (#7671) +- accidental localhost link in docs +- Docs / Update copyright year +- Bump github/codeql-action from 2 to 3 (#7552) +- Bump advanced-security/maven-dependency-submission-action from 3 to 4 (#7655) +- Formatter / Withheld element not always hidden. +- Change log for version 4.4.2 (#7654) +- Change log for version 4.2.8 +- Update version to 4.4.3-SNAPSHOT \ No newline at end of file diff --git a/docs/manual/LICENSE b/docs/manual/LICENSE new file mode 100644 index 00000000000..1d658d6d376 --- /dev/null +++ b/docs/manual/LICENSE @@ -0,0 +1,319 @@ +Creative Commons Legal Code + +Attribution 3.0 Unported + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR + DAMAGES RESULTING FROM ITS USE. + +License + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(f) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + f. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + h. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(b), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at http://creativecommons.org/. diff --git a/docs/manual/README.md b/docs/manual/README.md index 67b9ce2ffc7..6a32791b9e9 100644 --- a/docs/manual/README.md +++ b/docs/manual/README.md @@ -1,12 +1,12 @@ -# Geonetwork Manual and Help +# GeoNetwork Manual and Help -Documentation for GeoNetwork opensource is available via https://geonetwork-opensource.org. +Documentation for GeoNetwork opensource is available via https://docs.geonetwork-opensource.org. -This documentation is written under the creative commons license [Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)](LICENSE.md). +This documentation is written under the Creative Commons license [Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)](LICENSE). Reference: -* [Documentation Writing Guide](docs/devel/docs/docs.md) +* [Documentation Writing Guide](docs/devel/docs/index.md) ## Communication @@ -14,9 +14,9 @@ The [project issue tracker](https://github.com/geonetwork/core-geonetwork/issues ## Material for MkDocs -Documentation is [mkdocs-material](https://squidfunk.github.io/mkdocs-material/) which is a Markdown documentation framework written on top of [MkDocs](https://www.mkdocs.org/). +Documentation is [MkDocs-material](https://squidfunk.github.io/mkdocs-material/) which is a Markdown documentation framework written on top of [MkDocs](https://www.mkdocs.org/). -If you are using python3: +If you are using Python3: 1. Install using ``pip3`` and build: @@ -45,7 +45,7 @@ If you are using python3: ### VirtualEnv -If you use a python virtual environment: +If you use a Python virtual environment: 1. Activate virtual environment: @@ -65,9 +65,9 @@ If you use a python virtual environment: ### Docker -If you are not familiar with python the mkdocs-material website has instructions for docker: +If you are not familiar with Python the MkDocs-material website has instructions for Docker: -1. Run mkdocs in Docker environment: +1. Run MkDocs in Docker environment: ``` docker pull squidfunk/mkdocs-material @@ -90,7 +90,7 @@ If you are not familiar with python the mkdocs-material website has instructions 3. Both ``install`` and ``deploy`` are skipped (so ``mvn clean install`` is fine). -4. Use default profile to only build the default english docs: +4. Use default profile to only build the default English docs: ``` mvn install -Pdefault diff --git a/docs/manual/docs/administrator-guide/configuring-the-catalog/csw-configuration.md b/docs/manual/docs/administrator-guide/configuring-the-catalog/csw-configuration.md index 902c451848d..d352dc7fcd5 100644 --- a/docs/manual/docs/administrator-guide/configuring-the-catalog/csw-configuration.md +++ b/docs/manual/docs/administrator-guide/configuring-the-catalog/csw-configuration.md @@ -4,7 +4,7 @@ To get to the CSW server configuration, you must be logged on as administrator f ![](img/csw.png) -The CSW service provides a description of itself, the human who administers it, and other information through a `es` request (eg. ). This form allows you to configure the CSW server and fill out some of the properties returned in response to a GetCapabilities request. +The CSW service provides a description of itself, the human who administers it, and other information through a `GetCapabilities` request (eg. ). This form allows you to configure the CSW server and fill out some of the properties returned in response to a GetCapabilities request. Configuration options: diff --git a/docs/manual/docs/administrator-guide/configuring-the-catalog/inspire-configuration.md b/docs/manual/docs/administrator-guide/configuring-the-catalog/inspire-configuration.md index 0dea7a3ce98..b5d9afb4266 100644 --- a/docs/manual/docs/administrator-guide/configuring-the-catalog/inspire-configuration.md +++ b/docs/manual/docs/administrator-guide/configuring-the-catalog/inspire-configuration.md @@ -50,11 +50,11 @@ The type of encoding of keywords can be defined using the gear icon (See validat ![image](img/inspire-keyword-encoding-type.png) -Via the schema plugin form configuration it is an option to configure a thesaurus to be used for a specific `or` element. The thesaures concepts are used to populate an auto complete text field for that element. +Via the schema plugin form configuration it is an option to configure a thesaurus to be used for a specific `Anchor` element. The thesaures concepts are used to populate an auto complete text field for that element. ## INSPIRE validation -INSPIRE validation of metadata records is available at [the INSPIRE Validator](https://inspire.ec.europa.eu/validator/about/). It is using [ETF which is an open source testing framework for spatial data and services](https://github.com/etf-validator/etf-webapp). GeoNetwork is able to `te` any record using a service provided by an instance of ETF. To configure remote validation, go to `Admin console` --> `Settings` and set the URL of the validator. The url of the main INSPIRE validator is `https://inspire.ec.europa.eu/validator/`. +INSPIRE validation of metadata records is available at [the INSPIRE Validator](https://inspire.ec.europa.eu/validator/about/). It is using [ETF which is an open source testing framework for spatial data and services](https://github.com/etf-validator/etf-webapp). GeoNetwork is able to `remote validate` any record using a service provided by an instance of ETF. To configure remote validation, go to `Admin console` --> `Settings` and set the URL of the validator. The url of the main INSPIRE validator is `https://inspire.ec.europa.eu/validator/`. ![image](img/inspire-configuration.png) diff --git a/docs/manual/docs/administrator-guide/configuring-the-catalog/user-interface-configuration.md b/docs/manual/docs/administrator-guide/configuring-the-catalog/user-interface-configuration.md index cc87db286e5..1734724599f 100644 --- a/docs/manual/docs/administrator-guide/configuring-the-catalog/user-interface-configuration.md +++ b/docs/manual/docs/administrator-guide/configuring-the-catalog/user-interface-configuration.md @@ -177,7 +177,6 @@ This section is for configuring the map shown when viewing a record. - **wmts**: generic WMTS layer, required properties: `name, url`. - **tms**: generic TMS layer, required property: `url`. - **osm**: OpenStreetMap default layer, no other property required. - - **stamen**: Stamen layers, required property: `name`. - **bing_aerial**: Bing Aerial background, required property: `key` containing the license key. ![](img/ui-settings-mapviewerlayers.png) @@ -189,11 +188,6 @@ All layers can also have some optional extra properties: Examples of layers: -This layer will use OpenStreetMap Stamen style, but only when the map is in `EPSG:3857`: - -``` json -{"type":"stamen","projectionList":["EPSG:3857"]} -``` This WMS layer will be shown but only when the map is on `EPSG:4326`: diff --git a/docs/manual/docs/administrator-guide/managing-users-and-groups/authentication-mode.md b/docs/manual/docs/administrator-guide/managing-users-and-groups/authentication-mode.md index 61b53c68e47..efc095df787 100644 --- a/docs/manual/docs/administrator-guide/managing-users-and-groups/authentication-mode.md +++ b/docs/manual/docs/administrator-guide/managing-users-and-groups/authentication-mode.md @@ -143,7 +143,7 @@ An attribute could define both the profile and the group for a user. To extract ldap.privilege.pattern.idx.profil=2 ``` - Enable the bean `er` for `LDAPUserDetailsContextMapperWithPattern` ( in `WEB-INF/config-security/config-security-ldap.xml`). + Enable the bean `ldapUserContextMapper` for `LDAPUserDetailsContextMapperWithPattern` ( in `WEB-INF/config-security/config-security-ldap.xml`). ``` xml Catalogue admin tools** page: + + * http://localhost:8080/geonetwork/doc/api/index.html + + ![](img/admin-console-api.png) + **Catalogue admin tools** + +2. The OpenAPI document is browsable as an html page: + + ![](img/geonetwork-api-html.png) + **GeoNetwork API OpenAPI document** + +3. The page documents each service end-point, including parameters and output results. + + ![](img/geonetwork-api-document.png) + **GeoNetwork API Service Endpoint Description** + +4. Selecting **Server variables** at the top of the page allows API testing: + + ![](img/geonetwork-api-test.png) + **GeoNetwork API Service Endpoint Test** + +5. The OpenAPI document is also available as `text/json` and `text/xml` for script access: + + * https://localhost:8080/geonetwork/srv/api/doc + + ```json + { + "openapi": "3.0.1", + "info": { + "title": "GeoNetwork 4.0.1 OpenAPI Documentation", + "description": "This is the description of the GeoNetwork OpenAPI. Use this API to manage your catalog.", + "contact": { + "name": "GeoNetwork user mailing list", + "url": "https://sourceforge.net/p/geonetwork/mailman/geonetwork-users/", + "email": "geonetwork-users@lists.sourceforge.net" + }, + "license": { + "name": "GPL 2.0", + "url": "http://www.gnu.org/licenses/old-licenses/gpl-2.0.html" + }, + "version": "4.0.1" + }, + { + "description": "Learn how to access the catalog using the GeoNetwork REST API.", + "url": "http://localhost:8080/geonetwork/doc/api" + }, + ... + ``` + +## Upgrading from GeoNetwork 3 Guidance + +In version `4.0.1` onward the API description is provided using OpenAPI specification: + +- The version of the API correspond to the version of the GeoNetwork instance when the API changed. + +- The GeoNetwork API version number is indicated in the `html` page title available + +- The GeoNetwork API version number is available to scripts at in the `json` or `xml` info description. + +- Previously in GeoNetwork 3 the GeoNetwork API version was included in the path, ``/srv/api/0.1/\...`` + +!!! warning + + Lucene is no longer available, replaced by Elasticsearch. + +!!! warning + + The GeoNetwork API support for Report Uploads has not been migrated. + + Interested parties may contact the project team for guidance and to express their intent. + +!!! warning + + XLink / Remove directory entry used in other record has not been migrated. + + Interested parties may contact the project team for guidance and to express their intent. + + ## Using the API to apply an XSL process This is an example to trigger an XSL process on a set of records. It illustrates how to make a set of actions using the API: @@ -151,7 +236,7 @@ Then use the function in formula. Here we search for records matching particular ## Building client for the API using codegen -The API is described using the open API specification. [Codegen](https://swagger.io/swagger-codegen/) is a tool to build an API client based on the specification. To build a Java client use the following procedure. +The API is described using the Open API specification. [Codegen](https://swagger.io/swagger-codegen/) is a tool to build an API client based on the specification. To build a Java client use the following procedure. First, create a configuration file apiconfig.json for the API: @@ -392,6 +477,5 @@ auth = (username, password), headers=headers, json=json_data, ) - print(response.text) ``` diff --git a/docs/manual/docs/api/z39-50.md b/docs/manual/docs/api/z39-50.md index 7c3260cb46a..299c6c35063 100644 --- a/docs/manual/docs/api/z39-50.md +++ b/docs/manual/docs/api/z39-50.md @@ -2,5 +2,19 @@ !!! warning - Deprecated since version 4.0.0. + Unavailable since version 4.0.0. + + There is no known sponsor or interested party for implementing Z39.50. + Interested parties may contact the project team for guidance and to express their intent. +Z39.50 is the name of an older communication protocol used for distributed searching across metadata catalogs. + +Reference: + +* [ISO 23950:1998 Information and documentation Information retrieval (Z39.50)](https://www.iso.org/standard/27446.html) + +## Upgrading from GeoNetwork 3.0 Guidance + +Z39-50 API is no longer available. + +Recommend migrating to use of [Catalog Service for the Web (CSW)](csw.md) API. diff --git a/docs/manual/docs/contributing/doing-a-release.md b/docs/manual/docs/contributing/doing-a-release.md index d738e52e157..c69cddb6178 100644 --- a/docs/manual/docs/contributing/doing-a-release.md +++ b/docs/manual/docs/contributing/doing-a-release.md @@ -5,22 +5,25 @@ This section documents the steps followed by the development team to do a new re Once the release branch has been thoroughly tested and is stable a release can be made. The following script can be used on Linux and Mac. For this a running build environment is needed -with the following utilities: sed, xmlstarlet and sftp. +with the following utilities: ***sed***, ***xmlstarlet*** and ***sftp***. -1. Prepare the release +1. Prepare the release (examples prepairs version 4.4.1 as latest release): ``` shell # Setup properties + from=origin frombranch=origin/main - versionbranch=4.2.x - version=4.2.3 + series=4.4 + versionbranch=$series.x + version=4.4.1 minorversion=0 + release=latest newversion=$version-$minorversion - currentversion=4.2.3-SNAPSHOT - previousversion=4.2.2 - nextversion=4.2.4-SNAPSHOT - nextMajorVersion=4.4.0-SNAPSHOT + currentversion=4.4.1-SNAPSHOT + previousversion=4.4.0 + nextversion=4.4.2-SNAPSHOT + nextMajorVersion=4.6.0-SNAPSHOT # Get the branch @@ -41,17 +44,101 @@ with the following utilities: sed, xmlstarlet and sftp. ./update-version.sh $currentversion $newversion # Generate list of changes - cat < docs/changes$newversion.txt + cat < docs/changes/changes$newversion.txt ================================================================================ === === GeoNetwork $version: List of changes === ================================================================================ EOF - git log --pretty='format:- %s' $previousversion... >> docs/changes$newversion.txt + git log --pretty='format:- %s' $previousversion... >> docs/changes/changes$newversion.txt ``` -2. Commit & tag the new version +2. Prepare change-log notes. + + Git notes are managed similar to push and pulling tags. Start by pulling the latest notes: + ``` + git pull origin refs/notes/commits + ``` + + Review changes along with any notes: + ``` + git log --pretty='format:%h: %s %n note: %N' $previousversion... + ``` + + Use `git note append` to document commits adding major features. + + ``` + git notes append -m "" + ``` + + Use `git note remove` if you need to clear a note and start again: + ``` + git notes remove + ``` + + Preview changes using: + + ``` + git log --pretty='format:* %N' $previousversion... | grep -v "^* $" + ``` + + Save your notes: + ``` + git push origin refs/notes/commits + ``` + +3. Create change log page: `docs/manual/docs/overview/change-log/` + + ``` shell + cat < docs/manual/docs/overview/change-log/version-$version.md + # Version $version + + GeoNetwork $version is a minor release. + + ## Migration notes + + ### API changes + + ### Installation changes + + ### Index changes + + ## List of changes + + Major changes: + + EOF + + git log --pretty='format:* %N' $previousversion.. | grep -v "^* $" >> docs/manual/docs/overview/change-log/version-$version.md + + cat < docs/manual/docs/overview/change-log/version-$version.md + + and more \... see [$version issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A$version+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A$version+is%3Aclosed) for full details. + EOF + ``` + + Fill in the above markdown file, removing any unused headings. + +4. Update links and navigation: + + * ``docs/manual/mkdocs.yml`` + * ``docs/manual/docs/overview/change-log/index.md`` + * ``docs/manual/docs/overview/change-log/latest.md`` + * ``docs/manual/docs/overview/change-log/stable.md`` + * ``docs/manual/docs/overview/change-log/archive.md`` + + Test documentation locally: + ``` + cd docs/manual + mkdocs serve + ``` + Once running check the new page: + ``` + open http://localhost:8000/ocverview/change-log/$newversion + ``` + +5. Commit & tag the new version ``` shell # Then commit the new version @@ -62,24 +149,29 @@ with the following utilities: sed, xmlstarlet and sftp. git tag -a $version -m "Tag for $version release" ``` -3. Build +6. Build ``` shell + # deep clean + mvn clean:clean@reset + # Build the new release + mvn install -Drelease + + # Create a minimal war (with only the default datasources) + cd web mvn clean install -DskipTests -Pwar -Pwro4j-prebuild-cache - # Download Jetty and create the installer - cd release - mvn clean install -Djetty-download - ant - + cd ../release + mvn clean install -Pjetty-download,bundle # Deploy to osgeo repository (requires credentials in ~/.m2/settings.xml) - mvn deploy + cd .. + mvn deploy -Drelease ``` -4. Test +7. Test ``` shell cd target/GeoNetwork-$newversion @@ -88,18 +180,39 @@ with the following utilities: sed, xmlstarlet and sftp. ./startup.sh -f ``` -5. Set the next version +8. Set the next version ``` shell # Set version number to SNAPSHOT ./update-version.sh $newversion $nextversion + nextversionnosnapshot=${nextversion//[-SNAPSHOT]/} + + # Add SQL migration step for the next version + mkdir web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v${nextversionnosnapshot//[.]/} + cat < web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v${nextversionnosnapshot//[.]/}/migrate-default.sql + UPDATE Settings SET value='${nextversionnosnapshot}' WHERE name='system/platform/version'; + UPDATE Settings SET value='SNAPSHOT' WHERE name='system/platform/subVersion'; + EOF + vi web/src/main/webResources/WEB-INF/config-db/database_migration.xml + ``` + + In `WEB-INF/config-db/database_migration.xml` add an entry for the new version in the 2 steps: + + ``` xml + + + WEB-INF/classes/setup/sql/migrate/v442/migrate- + + + ``` + ``` shell git add . git commit -m "Update version to $nextversion" ``` -6. Publishing +9. Publishing ``` shell # Push the branch and tag @@ -107,7 +220,7 @@ with the following utilities: sed, xmlstarlet and sftp. git push origin $version ``` -7. Generate checksum files +10. Generate checksum files - If using Linux: @@ -120,7 +233,7 @@ with the following utilities: sed, xmlstarlet and sftp. ``` shell md5 -r web/target/geonetwork.war > web/target/geonetwork.war.md5 - md5 -r release/target/GeoNetwork-$newversion/geonetwork-bundle-$newversion.zip > release/target/GeoNetwork-$newversion/geonetwork-bundle-$newversion.zip.md5 + md5 -r release/target/GeoNetwork-$version/geonetwork-bundle-$newversion.zip > release/target/GeoNetwork-$version/geonetwork-bundle-$newversion.zip.md5 ``` On sourceforge first: @@ -131,17 +244,15 @@ with the following utilities: sed, xmlstarlet and sftp. cd /home/frs/project/g/ge/geonetwork/GeoNetwork_opensource # or for RC release cd /home/frs/project/g/ge/geonetwork/GeoNetwork_unstable_development_versions/ - mkdir v3.12.1 - cd v3.12.1 - put docs/changes3.12.1-0.txt + mkdir v4.4.1 + cd v4.4.1 + put docs/changes/changes4.4.1-0.txt put release/target/GeoNetwork*/geonetwork-bundle*.zip* put web/target/geonetwork.war* bye ``` -8. Update or add the changelog in the documentation . - -9. Close the milestone on github with link to sourceforge download. +11. Close the milestone on github with link to sourceforge download. Publish the release on github . @@ -164,6 +275,14 @@ with the following utilities: sed, xmlstarlet and sftp. git checkout master ./update-version.sh $currentversion $nextMajorVersion ``` + + Update documentation to reflect series change of `latest`, `stable`, `maintenance` and `archive`: + + * ``docs/manual/mkdocs.yml`` navigation changes as branches change role + * ``docs/manual/docs/overview/change-log/index.md`` + * ``docs/manual/docs/overview/change-log/latest.md`` + * ``docs/manual/docs/overview/change-log/stable.md`` + * ``docs/manual/docs/overview/change-log/archive.md`` Commit the new version diff --git a/docs/manual/docs/customizing-application/adding-static-pages.md b/docs/manual/docs/customizing-application/adding-static-pages.md index d1c31a506f7..0d0fd982b9a 100644 --- a/docs/manual/docs/customizing-application/adding-static-pages.md +++ b/docs/manual/docs/customizing-application/adding-static-pages.md @@ -8,13 +8,13 @@ This feature allows to store the HTML content for static pages and show the link - Each page can be in 3 states: - - `EN`: visible to administrator. - - `TE`: visible to logged users. - - `IC`: visible to everyone. + - `HIDDEN`: visible to administrator. + - `PRIVATE`: visible to logged users. + - `PUBLIC`: visible to everyone. -- Pages can be added to different page sections. Currently the sections implemented are `OP` (top menu of the main page) and `ER` (footer of the main page). +- Pages can be added to different page sections. Currently the sections implemented are `TOP` (top menu of the main page) and `FOOTER` (footer of the main page). -- Only the administrator can edit the pages and see the pages in `EN` status. +- Only the administrator can edit the pages and see the pages in `HIDDEN` status. - The creation and the management of the content is done via the API. @@ -80,10 +80,10 @@ curl -X DELETE "http://localhost:8080/geonetwork/srv/api/pages/eng/contactus?for The status of the page can be changed with the method PUT `/api/pages/{language}/{pageId}/{status}` where status could assume these values: -- `IC` - Visible to every user -- `LY` - Visible to not logged users -- `TE` - Visible to logged users -- `EN` - Hidden to anyone +- `PUBLIC` - Visible to every user +- `PUBLIC_ONLY` - Visible to not logged users +- `PRIVATE` - Visible to logged users +- `HIDDEN` - Hidden to anyone Other methods in the API are to change/delete a page and to GET the list of the pages or the info of a specific one. diff --git a/docs/manual/docs/customizing-application/configuring-search-fields.md b/docs/manual/docs/customizing-application/configuring-search-fields.md index c03b2184ba8..a67a102493f 100644 --- a/docs/manual/docs/customizing-application/configuring-search-fields.md +++ b/docs/manual/docs/customizing-application/configuring-search-fields.md @@ -195,7 +195,7 @@ curl -X POST "localhost:8080/geonetwork/srv/api/search/records/_search" \ To customize how the field is indexed see `web/src/main/webResources/WEB-INF/data/config/index/records.json`. -To return it in the search response, use the `ce` parameter of the query. See . +To return it in the search response, use the `_source` parameter of the query. See . ## Boosting at search time @@ -248,4 +248,45 @@ By default, the search score is defined as (see `web-ui/src/main/resources/catal ## Language analyzer -By default a `rd` analyzer is used. If the catalog content is english, it may make sense to change the analyzer to `sh`. To customize the analyzer see `web/src/main/webResources/WEB-INF/data/config/index/records.json` + +By default a `standard` analyzer is used. If the catalog content is english, it may make sense to change the analyzer to `english`. To customize the analyzer see `web/src/main/webResources/WEB-INF/data/config/index/records.json` + +To add a new language, check first if Elasticsearch provides a specific analyzer for that language (see https://www.elastic.co/guide/en/elasticsearch/reference/current/analysis-lang-analyzer.html). Then configure fields that are multilingual +in `records.json` (eg. adding Danish): + +* If the field is used for full text search, use the language analyzer: + +```json +{ + "textField": { + "match": "*Object", + "mapping": { + "type": "object", + "properties": { + "default": {}, + ... + "langdan": { + "type": "text", + "analyzer": "danish" + }, +``` + +* If the field is a keyword like organisation name or tag field use type `keyword` (which is required for computing aggregations) + +```json +{ + "tag": { + "match": "th_*", + "mapping": { + "type": "object", + "copy_to": ["tag"], + "properties": { + "default": {}, + ... + "langdan": { + "type": "keyword", + "copy_to": [ + "any.langdan" + ] + }, +``` \ No newline at end of file diff --git a/docs/manual/docs/customizing-application/editor-ui/creating-custom-editor.md b/docs/manual/docs/customizing-application/editor-ui/creating-custom-editor.md index db72ca078f2..dad54a2adc6 100644 --- a/docs/manual/docs/customizing-application/editor-ui/creating-custom-editor.md +++ b/docs/manual/docs/customizing-application/editor-ui/creating-custom-editor.md @@ -59,7 +59,7 @@ The other option to define a more advanced field type is to catch the element us ## Grouping element from the standards {#creating-custom-editor-fieldsWithFieldset} -List of elements to be displayed in a fieldset (i.e. boxed element). Those elements usually contain children elements and define major sections in the standard. For example, in ISO19139, `on` and `on` are major sections and should usually be displayed as a group of `on`. +List of elements to be displayed in a fieldset (i.e. boxed element). Those elements usually contain children elements and define major sections in the standard. For example, in ISO19139, `identification` and `distribution` are major sections and should usually be displayed as a group of `information`. ``` xml @@ -127,7 +127,7 @@ A view has a label and defines a specific rendering of the metadata records. A v ``` -The view could be displayed or not according to the metadata record content or the current user session using the `rd` and `fo` attributes. +The view could be displayed or not according to the metadata record content or the current user session using the `displayIfRecord` and `displayIfServiceInfo` attributes. Attributes: @@ -334,7 +334,7 @@ e.g. only 2 INSPIRE themes: ## Adding a section to a tab {#creating-custom-editor-section} -A section is a group of fields. If a `me` attribute is provided, then it will create an HTML fieldset which is collapsible. If no `me` attribute is provided, then it will just render the inner elements. For example, if you need a tab without a root fieldset, just create the mandatory section with no name and then the inner elements. +A section is a group of fields. If a `name` attribute is provided, then it will create an HTML fieldset which is collapsible. If no `name` attribute is provided, then it will just render the inner elements. For example, if you need a tab without a root fieldset, just create the mandatory section with no name and then the inner elements. Attributes: @@ -433,13 +433,13 @@ displayIfRecord and displayIfServiceInfo could be combined. An AND operator is u ## Adding a field {#creating-custom-editor-field} -To display a simple element use the `th` attribute to point to the element to display: +To display a simple element use the `xpath` attribute to point to the element to display: ``` xml ``` -To override a field label use the `me` attribute and define that new label in `{schema}/loc/{lang}/strings.xml`: +To override a field label use the `name` attribute and define that new label in `{schema}/loc/{lang}/strings.xml`: ``` xml ``` -`el` attribute can be used in template mode or not. Example to remove `on` while only editing `or` or `ce`. `or` or `ce` are mandatory, but as the `el` element points to the `on` ancestor, there is no mandatory flag displayed and the remove control removes the `on` element. +`del` attribute can be used in template mode or not. Example to remove `spatialResolution` while only editing `denominator` or `distance`. `denominator` or `distance` are mandatory, but as the `del` element points to the `spatialResolution` ancestor, there is no mandatory flag displayed and the remove control removes the `spatialResolution` element. ``` xml ``` -In the case of the search page (``catalog.search``), the `$angularApp` variable equals `gn_search`. And for the `lt` view the `$angularModule` equals `gn_search_default`. +In the case of the search page (``catalog.search``), the `$angularApp` variable equals `gn_search`. And for the `default` view the `$angularModule` equals `gn_search_default`. So, by default, the main AngularJS module is loaded here from `gn_search_default` module declared here (See `web-ui/src/main/resources/catalog/views/default/module.js`. diff --git a/docs/manual/docs/help/index.md b/docs/manual/docs/help/index.md index 418cf1b02fa..8193a33d53e 100644 --- a/docs/manual/docs/help/index.md +++ b/docs/manual/docs/help/index.md @@ -6,7 +6,7 @@ This catalogue lists records of available datasets. The catalogue uses ***GeoNetwork*** technology for listing, searching and reviewing records. -GeoNetwork is a catalog application to manage spatially referenced resources. It provides powerful metadata editing and search functions as well as an interactive web map viewer. GeoNetwork is widely used as the foundation of Spatial Data Infrastructures world wide. +GeoNetwork is a catalog application to manage spatially referenced resources. It provides powerful metadata editing and search functions as well as an interactive web map viewer. GeoNetwork is widely used as the foundation of Spatial Data Infrastructures worldwide. !!! info "Reference" diff --git a/docs/manual/docs/help/map/index.md b/docs/manual/docs/help/map/index.md index f996b73cc55..d1a3fc38442 100644 --- a/docs/manual/docs/help/map/index.md +++ b/docs/manual/docs/help/map/index.md @@ -31,7 +31,7 @@ The map viewer has a number of features to explore the data: * Location search including searching for your current position. ![Map viewer](img/map.png) - *Mapviewer* + *Map viewer* ## Add layers @@ -104,7 +104,7 @@ Use web mapping service: ![WMS Layers Style](img/add_wms_style.png) *Add WMS Layer style* -6. Locate the ``mapcolor9` layer style, and press **Add to map**. +6. Locate the ``mapcolor9`` layer style, and press **Add to map**. ### Add files @@ -126,14 +126,6 @@ Use **`KML`** files on the map. 3. Use these buttons to mange the layer order, and remove layers. -### Background map - -1. From the **Map** viewer, press **Manage layers** control - -2. Change the **Background map** from `OpenStreetMap` to `Stamen Watercolor`. - - ![Basemap](img/manage_basemap.png) - *Manage basemap* ## Legend diff --git a/docs/manual/docs/help/search/index.md b/docs/manual/docs/help/search/index.md index e5f6e10c9d5..288d951baec 100644 --- a/docs/manual/docs/help/search/index.md +++ b/docs/manual/docs/help/search/index.md @@ -231,3 +231,4 @@ To select records from multiple search results: ![](img/export_index_csv.png) *Export CSV* + diff --git a/docs/manual/docs/index.fr.md b/docs/manual/docs/index.fr.md index a491841fe52..13e3b55195b 100644 --- a/docs/manual/docs/index.fr.md +++ b/docs/manual/docs/index.fr.md @@ -9,36 +9,52 @@ Bienvenue à GeoNetwork. Cette documentation est organisée en guides spécifiqu
-:fontawesome-solid-signs-post: [Vue d'ensemble](overview/index.md) - -: Historique de GeoNetwork, communauté, détails de la licence et derniers changements. - -:fontawesome-solid-circle-info: [Aide en ligne](help/index.md) - -: Aide en ligne pour les visiteurs du catalogue (aucune connexion n'est requise). - -:fontawesome-solid-person-circle-question: [Guide de l'utilisateur](user-guide/index.md) - -: Guide de l'utilisateur opérationnel décrivant l'édition, la révision et la gestion des enregistrements (nécessite une connexion). - -:fontawesome-solid-screwdriver-wrench: [Guide du mainteneur](maintainer-guide/index.md) - -: Instructions d'installation, de configuration et de mise à jour - -:fontawesome-solid-user-graduate: [Tutoriels](tutorials/index.md) - -: Explorer des sujets à l'aide de tutoriels étape par étape - -:fontawesome-solid-plug: [Référence API](api/index.md) - -: Référence API pour les développeurs accédant aux services du catalogue. - -:fontawesome-regular-file-code: [Développement](devel/index.md) - -: Informations sur le développement, la personnalisation de GeoNetwork et la participation au projet GeoNetwork. - -:fontawesome-regular-bookmark: [Annexes](annexes/index.md) - -: Informations de référence +- :fontawesome-solid-signs-post: [Vue d'ensemble](overview/index.md) + + --- + + Historique de GeoNetwork, communauté, détails de la licence et derniers changements. + +- :fontawesome-solid-circle-info: [Aide en ligne](help/index.md) + + --- + + Aide en ligne pour les visiteurs du catalogue (aucune connexion n'est requise). + +- :fontawesome-solid-person-circle-question: [Guide de l'utilisateur](user-guide/index.md) + + --- + + Guide de l'utilisateur opérationnel décrivant l'édition, la révision et la gestion des enregistrements (nécessite une connexion). + +- :fontawesome-solid-screwdriver-wrench: [Guide du mainteneur](maintainer-guide/index.md) + + --- + + Instructions d'installation, de configuration et de mise à jour + +- :fontawesome-solid-user-graduate: [Tutoriels](tutorials/index.md) + + --- + + Explorer des sujets à l'aide de tutoriels étape par étape + +- :fontawesome-solid-plug: [Référence API](api/index.md) + + --- + + Référence API pour les développeurs accédant aux services du catalogue. + +- :fontawesome-regular-file-code: [Développement](devel/index.md) + + --- + + Informations sur le développement, la personnalisation de GeoNetwork et la participation au projet GeoNetwork. + +- :fontawesome-regular-bookmark: [Annexes](annexes/index.md) + + --- + + Informations de référence
diff --git a/docs/manual/docs/index.md b/docs/manual/docs/index.md index 5780bdfdb5e..d8e34f4b6b0 100644 --- a/docs/manual/docs/index.md +++ b/docs/manual/docs/index.md @@ -9,36 +9,52 @@ Welcome to GeoNetwork. This documentation is organized into specific guides targ
-:fontawesome-solid-signs-post: [Overview](overview/index.md) - -: GeoNetwork background, community, license details, and the latest changes. - -:fontawesome-solid-circle-info: [Online Help](help/index.md) - -: Online help for visitors to the catalogue (no login required). - -:fontawesome-solid-person-circle-question: [User Guide](user-guide/index.md) - -: Operational user-guide describing the editing, review and management of records (requires-login). - -:fontawesome-solid-screwdriver-wrench: [Maintainer Guide](maintainer-guide/index.md) - -: Installation, setup and update instructions - -:fontawesome-solid-user-graduate: [Tutorials](tutorials/index.md) - -: Explore topics using step-by-step tutorials - -:fontawesome-solid-plug: [API Reference](api/index.md) - -: API Reference for developers accecssing catalogue services. - -:fontawesome-regular-file-code: [Development](devel/index.md) - -: Development information on customizing GeoNetwork and taking part in the GeoNetwork project. - -:fontawesome-regular-bookmark: [Annexes](annexes/index.md) - -: Reference information +- :fontawesome-solid-signs-post: [Overview](overview/index.md) + + --- + + GeoNetwork background, community, license details, and the latest changes. + +- :fontawesome-solid-circle-info: [Online Help](help/index.md) + + --- + + Online help for visitors to the catalogue (no login required). + +- :fontawesome-solid-person-circle-question: [User Guide](user-guide/index.md) + + --- + + Operational user-guide describing the editing, review and management of records (requires-login). + +- :fontawesome-solid-screwdriver-wrench: [Maintainer Guide](maintainer-guide/index.md) + + --- + + Installation, setup and update instructions + + - :fontawesome-solid-user-graduate: [Tutorials](tutorials/index.md) + + --- + + Explore topics using step-by-step tutorials + +- :fontawesome-solid-plug: [API Reference](api/index.md) + + --- + + API Reference for developers accecssing catalogue services. + +- :fontawesome-regular-file-code: [Development](devel/index.md) + + --- + + Development information on customizing GeoNetwork and taking part in the GeoNetwork project. + +- :fontawesome-regular-bookmark: [Annexes](annexes/index.md) + + --- + + Reference information
diff --git a/docs/manual/docs/install-guide/configuring-database.md b/docs/manual/docs/install-guide/configuring-database.md index 213aedc9372..86ed099dda7 100644 --- a/docs/manual/docs/install-guide/configuring-database.md +++ b/docs/manual/docs/install-guide/configuring-database.md @@ -72,7 +72,7 @@ Setting configuration properties via environment variables is common in containe -e jdbc.port=5432 geonetwork:latest ``` -Within PostgreSQL it is possible to configure `es` or `is`. In the latter case GeoNetwork will use spatial capabilities of PostGIS to filter metadata. In the first case (and for other database dialects) a Shapefile is created for storage of metadata coverage. +Within PostgreSQL it is possible to configure `postgres` or `postgis`. In the latter case GeoNetwork will use spatial capabilities of PostGIS to filter metadata. In the first case (and for other database dialects) a Shapefile is created for storage of metadata coverage. ## Logging diff --git a/docs/manual/docs/install-guide/index.md b/docs/manual/docs/install-guide/index.md index fbe408ff852..86f88f8a864 100644 --- a/docs/manual/docs/install-guide/index.md +++ b/docs/manual/docs/install-guide/index.md @@ -6,23 +6,28 @@ Prior versions of GeoNetwork require a Java 8 environment (JRE) to be installed on your system. -Before you can use GeoNetwork on your own computer, you need to install it. There are several different ways to install GeoNetwork: +Before you can use GeoNetwork on your own computer, you need to install it: -- [Installing using a ZIP file](installing-from-zip.md) -- [Installing from WAR file](installing-from-war-file.md) -- [Installing with docker](installing-with-docker.md) -- [Building from Source Code](installing-from-source-code.md) -- [Installing search platform](installing-index.md) +1. GeoNetwork uses the Elasticsearch engine which must be installed first: -!!! note + * [Installing search platform](installing-index.md) - A windows installer is also available from the GeoNetwork download page, [Installing a third-party distribution](https://geonetwork-opensource.org/downloads.html#third-party-distributions) . +2. There are several different ways to install GeoNetwork: + * [Installing using a ZIP file](installing-from-zip.md) + * [Installing from WAR file](installing-from-war-file.md) + * [Installing with docker](installing-with-docker.md) + * [Building from Source Code](installing-from-source-code.md) -After installing the application, you can configure the following: + !!! note + + A windows installer is also available from the GeoNetwork download page, [Installing a third-party distribution](https://geonetwork-opensource.org/downloads.html#third-party-distributions) . -- [Configuring the database](configuring-database.md) -- [Customizing the data directory](customizing-data-directory.md) -- [Logging](logging.md) -- [Configuring printing of the map](map-print-setup.md) -- [Loading templates and sample data](loading-samples.md) + +3. After installing the application, you can configure the following: + + - [Configuring the database](configuring-database.md) + - [Customizing the data directory](customizing-data-directory.md) + - [Logging](logging.md) + - [Configuring printing of the map](map-print-setup.md) + - [Loading templates and sample data](loading-samples.md) diff --git a/docs/manual/docs/install-guide/installing-from-source-code.md b/docs/manual/docs/install-guide/installing-from-source-code.md index 4725640a805..8541a05ad9d 100644 --- a/docs/manual/docs/install-guide/installing-from-source-code.md +++ b/docs/manual/docs/install-guide/installing-from-source-code.md @@ -173,7 +173,7 @@ mvn jetty:run -Penv-dev After some moments of startup and initialization, GeoNetwork is available at: -For changes related to the user interface in the ``web-ui`` module or the metadata schemas in the `schemeas` module, these can be deployed in Jetty executing the following Maven command in the **web** module: +For changes related to the user interface in the ``web-ui`` module or the metadata schemas in the `schemas` module, these can be deployed in Jetty executing the following Maven command in the **web** module: ``` shell mvn process-resources -DschemasCopy=true diff --git a/docs/manual/docs/install-guide/installing-from-war-file.md b/docs/manual/docs/install-guide/installing-from-war-file.md index 391d343dda3..1619e8df79f 100644 --- a/docs/manual/docs/install-guide/installing-from-war-file.md +++ b/docs/manual/docs/install-guide/installing-from-war-file.md @@ -57,15 +57,15 @@ For [Jetty](https://www.eclipse.org/jetty/) we the following versions: 9.4.x. Ne Download Elasticsearch for your platform ``` shell - wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.6.2-linux-x86_64.tar.gz - tar xvfz elasticsearch-7.6.2-linux-x86_64.tar.gz - cd elasticsearch-7.6.2-linux-x86_64/bin + wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.3-linux-x86_64.tar.gz + tar xvfz elasticsearch-8.11.3-linux-x86_64.tar.gz + cd elasticsearch-8.11.3-linux-x86_64/bin ./elasticsearch ``` Open in your web browser to check that Elasticsearch is running. - To use an existing instance check `on` in [Installing search platform](installing-index.md). + To use an existing instance check Configure connection in [Installing search platform](installing-index.md). 5. Up and running? diff --git a/docs/manual/docs/install-guide/installing-index.md b/docs/manual/docs/install-guide/installing-index.md index 86154d779b2..e3ea8950d68 100644 --- a/docs/manual/docs/install-guide/installing-index.md +++ b/docs/manual/docs/install-guide/installing-index.md @@ -1,74 +1,117 @@ -# Installing search platform {#installing-index} +# Installing search platform -The GeoNetwork search engine is built on top of Elasticsearch. The platform is used to index records and also to analyze WFS data (See [Analyze and visualize data](../user-guide/analyzing/data.md) ), an [Elasticsearch](https://www.elastic.co/products/elasticsearch) instance must be installed next to the catalog. +The GeoNetwork search engine is built on top of Elasticsearch. The platform is used to index records and also to analyze WFS data (See [Analyze and visualize data](../user-guide/analyzing/data.md) ). -## Manual installation +GeoNetwork requires an [Elasticsearch](https://www.elastic.co/products/elasticsearch) instance to be installed next to the catalog. -Download Elasticsearch from and unzip the file. +## Elasticsearch compatibility -``` shell -wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-7.9.2.tar.gz -tar xvfz elasticsearch-7.9.2.tar.gz -``` +| Elasticsearch Version | Compatibility | +|-----------------------| ------------- | +| Elasticsearch 7.15.x | minimum | +| Elasticsearch 8.11.3 | tested | -Manually start and stop Elasticsearch using: -``` shell -elasticsearch-7.9.2/bin/elasticsearch -``` +## Installation -Stop Elasticsearch using +=== "Manual installation" + + 1. **Download:** Elasticsearch 8.x (`8.11.3` tested, minimum `7.15.x`) from and unzip the file. -``` shell -elasticsearch-7.6.2/bin/elasticsearch stop -``` + ``` shell + wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.11.3.tar.gz + tar xvfz elasticsearch-8.11.3.tar.gz + ``` -GeoNetwork will start even if Elasticsearch index is down. A warning will be displayed. Once the Elasticsearch index is up, indices are created if they do not exist. + 2. **Start**: Manually start Elasticsearch using: -(Optional) Then create the default index (the application will create them automatically once the index is up and running and if no indices are found): + ``` shell + elasticsearch-8.11.3/bin/elasticsearch + ``` -## Customizing index - -User may want to customize the index settings for example to change language configuration (see `es/README.md`). Check the configuration file in `` `$GN_DATA_DIRECTORY/config/index ```. To manually remove and recreate the index, use the following: - -``` shell -cd $GN_DATA_DIRECTORY/config/index -curl -X DELETE http://localhost:9200/features -curl -X DELETE http://localhost:9200/records -curl -X DELETE http://localhost:9200/searchlogs + 3. **Stop**: Manually stop Elasticsearch using: -curl -X PUT http://localhost:9200/features -H 'Content-Type: application/json' -d @features.json -curl -X PUT http://localhost:9200/records -H 'Content-Type: application/json' -d @records.json -curl -X PUT http://localhost:9200/searchlogs -H 'Content-Type: application/json' -d @searchlogs.json -``` + ``` shell + elasticsearch-8.11.3/bin/elasticsearch stop + ``` + +=== "Install using Maven" -## Install using Maven + 1. Developers are encouraged to run Elasticseach using maven in order to test against the version of Elasticsearch intended for the next release. + + !!! note + + When running from source code the Elasticsearch version is obtained from [pom.xml](https://github.com/geonetwork/core-geonetwork/blob/main/pom.xml) property `es.version`. + + 2. **Download**: Run maven from the **`es`** directory. -Running from the source code, use Maven to download. + ``` shell + cd es + mvn install -Pes-download + ``` + + 3. **Start**: Use maven ``exec`` plugin to run Elasticsearch: + + ``` shell + mvn exec:exec -Des-start + ``` + + Elasticsearch will run in foreground mode sending output to the shell. -``` shell -cd es -mvn install -Pes-download -mvn exec:exec -Des-start -``` + 4. **Stop**: To stop Elasticsearch use ++ctrl+c++ to stop the maven process. -To stop Elasticsearch when using Maven, simply stop the process as Elasticsearch is started in foreground mode. +## Index creation -## Check installation +1. GeoNetwork will connect to Elasticsearch on startup, and indices will be created if they do not exist. + + * GeoNetwork will start even if Elasticsearch index is down (or not yet running). + + * When GeoNetwork cannot contact Elasticsearch a warning will be displayed. + + * When Elasticsearch index is available, indices are created if they do not exist. -Access Elasticsearch admin page from . +2. Optional: To manually create indices use the **Admin Console** to create the default index. + + The application will create indices automatically once the Elasticsearch is up and running and if no indices are found. -## Configure connection - -Update Elasticsearch connection details in `` `WEB-INF/config.properties ```` and restart the application (see :code:\`web/src/main/webResources/WEB-INF/config.properties``): - -``` shell -es.protocol=http -es.port=9200 -es.host=localhost -es.url=${es.protocol}://${es.host}:${es.port} -es.username= -es.password= -``` +## Customizing index -It is not needed nor recommended to open port 9200 to the outside. GeoNetwork is protecting the Elasticsearch instance exposing only the search API and taking care of user privileges. +1. User may want to customize the index settings for example to change language configuration (see [es/README.md](https://github.com/geonetwork/core-geonetwork/tree/main/es#readme)). + +2. Check the configuration file in **`$GN_DATA_DIRECTORY/config/index`**. + +3. To manually remove and recreate the index, use the following: + + ``` shell + cd $GN_DATA_DIRECTORY/config/index + curl -X DELETE http://localhost:9200/features + curl -X DELETE http://localhost:9200/records + curl -X DELETE http://localhost:9200/searchlogs + + curl -X PUT http://localhost:9200/features -H 'Content-Type: application/json' -d @features.json + curl -X PUT http://localhost:9200/records -H 'Content-Type: application/json' -d @records.json + curl -X PUT http://localhost:9200/searchlogs -H 'Content-Type: application/json' -d @searchlogs.json + ``` + +## Check Elasticsearch installation + +1. Access Elasticsearch admin page from . + + !!! note + + It is not needed nor recommended to open port `9200` to the outside. GeoNetwork is protecting the Elasticsearch instance exposing only the search API and taking care of user privileges. + +## Configure GeoNetwork connection to Elasticsearch + +1. Update Elasticsearch connection details in ```WEB-INF/config.properties```: + + ``` properties + es.protocol=http + es.port=9200 + es.host=localhost + es.url=${es.protocol}://${es.host}:${es.port} + es.username= + es.password= + ``` + +2. Restart the application: diff --git a/docs/manual/docs/install-guide/map-print-setup.md b/docs/manual/docs/install-guide/map-print-setup.md index f8f07db07b3..d4d17d7a8f1 100644 --- a/docs/manual/docs/install-guide/map-print-setup.md +++ b/docs/manual/docs/install-guide/map-print-setup.md @@ -2,7 +2,7 @@ This section describes how to configure the options to print maps. Printing a map generates a pdf file on the server which is downloaded by the client to be send to a printer. During pdf creation map data is downloaded from various sources to be included in the pdf. -GeoNetwork needs to be able to access the external resource. Set up a webproxy in `gs` if your network requires a webproxy to be set up to access the internet. +GeoNetwork needs to be able to access the external resource. Set up a webproxy in `system settings` if your network requires a webproxy to be set up to access the internet. Locate the file ``WEB-INF/config-print/print-config.yaml``, this configuration file has a lot of options to customise the print options. Read more about the various parameters at @@ -11,4 +11,4 @@ The folder contains 3 template files: - ``template.pdf`` and ``template-landscape.pdf`` which are used to generate the map viewer pdf - ``template-thumbnail.pdf`` which is used to build a thumbnail in the metadata editor (see [Generating a thumbnail using WMS layers](../user-guide/associating-resources/linking-thumbnail.md#linking-thumbnail-from-wms)). -These templates are created by exporting pdf from the included ``template.odf`` file in the folder. +These templates are created by exporting pdf from the included ``template.pdf`` file in the folder. diff --git a/docs/manual/docs/maintainer-guide/index.md b/docs/manual/docs/maintainer-guide/index.md index 9dfe5bcdec8..3b829b3d8a2 100644 --- a/docs/manual/docs/maintainer-guide/index.md +++ b/docs/manual/docs/maintainer-guide/index.md @@ -4,6 +4,5 @@ For installation instructions, please refer to the [Installation guide](../insta - [Installation](../install-guide/index.md) - [Setup](../administrator-guide/index.md) -- [Setting up search/content statistics](statistics/index.md) - [Production Use](production-use/index.md) - [Updating the application](updating/index.md) diff --git a/docs/manual/docs/maintainer-guide/statistics/img/content-indexing.png b/docs/manual/docs/maintainer-guide/statistics/img/content-indexing.png deleted file mode 100644 index da425d56212..00000000000 Binary files a/docs/manual/docs/maintainer-guide/statistics/img/content-indexing.png and /dev/null differ diff --git a/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-1.png b/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-1.png deleted file mode 100644 index ea5a99b3f43..00000000000 Binary files a/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-1.png and /dev/null differ diff --git a/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-2.png b/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-2.png deleted file mode 100644 index 3a313fe5d2b..00000000000 Binary files a/docs/manual/docs/maintainer-guide/statistics/img/kibana-index-2.png and /dev/null differ diff --git a/docs/manual/docs/maintainer-guide/statistics/img/search-statistics-dashboards.png b/docs/manual/docs/maintainer-guide/statistics/img/search-statistics-dashboards.png deleted file mode 100644 index b45f3bd40b1..00000000000 Binary files a/docs/manual/docs/maintainer-guide/statistics/img/search-statistics-dashboards.png and /dev/null differ diff --git a/docs/manual/docs/maintainer-guide/statistics/img/search-statistics.png b/docs/manual/docs/maintainer-guide/statistics/img/search-statistics.png deleted file mode 100644 index e853154aba7..00000000000 Binary files a/docs/manual/docs/maintainer-guide/statistics/img/search-statistics.png and /dev/null differ diff --git a/docs/manual/docs/maintainer-guide/statistics/index.md b/docs/manual/docs/maintainer-guide/statistics/index.md deleted file mode 100644 index fa8a5070973..00000000000 --- a/docs/manual/docs/maintainer-guide/statistics/index.md +++ /dev/null @@ -1,16 +0,0 @@ -# Setting up search/content statistics {#statistics} - -Since GeoNetwork 3.4 search and content statistics are stored in [Elasticsearch](https://www.elastic.co/products/elasticsearch/) using [Kibana](https://www.elastic.co/products/kibana) dashboards to visualize them in the GeoNetwork administration application. - -This guide describes the configuration required to integrate Elasticsearch/Kibana in GeoNetwork to store and visualize the search and content statistics. - -GeoNetwork 3.8.x supports Elasticsearch/Kibana 7.2, other versions may not work properly. - -!!! note - - This guide doesn't provide a production level setup for Elasticsearch/Kibana. Please refer to the Elasticsearch/Kibana documentation to do a proper setup/configuration for a production environment. - - -- [Setup Elasticsearch](setup-elasticsearch.md) -- [Setup Kibana](setup-kibana.md) -- [Setup GeoNetwork](setup-geonetwork.md) diff --git a/docs/manual/docs/maintainer-guide/statistics/setup-elasticsearch.md b/docs/manual/docs/maintainer-guide/statistics/setup-elasticsearch.md deleted file mode 100644 index cdc8074bb97..00000000000 --- a/docs/manual/docs/maintainer-guide/statistics/setup-elasticsearch.md +++ /dev/null @@ -1,39 +0,0 @@ -# Setup Elasticsearch {#statistics_es} - -This section describes how to setup Elasticsearch to be used in GeoNetwork to store the search/content statistics. Note that Geonetwork must have been built with the `es` profile for Elasticsearch to be used. See for details. - -## Installation - -Elasticsearch can be installed manually, or for some operating systems packages are available. - -!!! note - - If installed manually, Elasticsearch must be configured as a service to ensure it starts automatically when the server is started. This is beyond the scope of this guide. - - -To install manually: - -- Download Elasticsearch from . For Geonetwork 3.8.x version 7.2.x is recommended. - -- Unzip the file and copy it, for example, to ``/opt/elasticsearch`` - -- Execute Elastic Search: - - ``` shell - $ cd /opt/elasticsearch/bin - $ ./elasticsearch & - ``` - -- Verify in a browser that Elasticsearch is running: - -## Load indexes - -``` shell -$ cd /tmp -$ curl -O https://raw.githubusercontent.com/geonetwork/core-geonetwork/3.4.x/es/config/features.json -$ curl -X PUT http://localhost:9200/gn-features -d @features.json -$ curl -O https://raw.githubusercontent.com/geonetwork/core-geonetwork/3.4.x/es/config/records.json -$ curl -X PUT http://localhost:9200/gn-records -d @records.json -$ curl -O https://raw.githubusercontent.com/geonetwork/core-geonetwork/3.4.x/es/config/searchlogs.json -$ curl -X PUT http://localhost:9200/gn-searchlogs -d @searchlogs.json -``` diff --git a/docs/manual/docs/maintainer-guide/statistics/setup-geonetwork.md b/docs/manual/docs/maintainer-guide/statistics/setup-geonetwork.md deleted file mode 100644 index c23f21c8a3a..00000000000 --- a/docs/manual/docs/maintainer-guide/statistics/setup-geonetwork.md +++ /dev/null @@ -1,56 +0,0 @@ -# Setup GeoNetwork {#statistics_geonetwork} - -!!! note - - GeoNetwork package should be build using the Maven `es` profile in order to be able to configure the search/content statistics to use Elasticsearch/Kibana: - - ``` shell - $ cd GN_SOURCES/ - $ mvn clean install -Penv-prod,es - ``` - - If you're using a development environment, can use the following command from the Geonetwork sources folder: - - ``` shell - $ cd GN_SOURCES/web - $ mvn jetty:run -Penv-dev,es - ``` - - -To setup the search/content statistics in GeoNetwork, do the following steps: - -- Start GeoNetwork. - -- Login as an `or` user and load the templates (). - -- Create some metadata records (optionally you can load the samples). - -- Enable search statistics in the Settings page (). - - ![](img/search-statistics.png) - -- Do some searches in GeoNetwork. - -- Go to `us` in and select Search Statistics`. - - The first time you're prompted to select the Kibana default index. Select `.dashboards`: - - ![](img/kibana-index-1.png) - - and set as default to get it working. - - ![](img/kibana-index-2.png) - -- Reload the page and check the dashboards. - - ![](img/search-statistics-dashboards.png) - -## Content statistics/validation - -Content and validation status statistics are available also from to `us` in , but require to index manually the content in Elasticsearch. This will be improved in future versions. - -- To index the content and validation status in Elasticsearch, go to and select the following option: - - ![](img/content-indexing.png) - -- Access the `cs` and `on` in `us` () diff --git a/docs/manual/docs/maintainer-guide/statistics/setup-kibana.md b/docs/manual/docs/maintainer-guide/statistics/setup-kibana.md deleted file mode 100644 index 77026847255..00000000000 --- a/docs/manual/docs/maintainer-guide/statistics/setup-kibana.md +++ /dev/null @@ -1,38 +0,0 @@ -# Setup Kibana {#statistics_kibana} - -This section describes how to setup Kibana to be used in GeoNetwork to visualize the search/content statistics: - -- Download Kibana from . For Geonetwork 3.8.x version 7.2.x is recommended. - -- Unzip the file, for example to ``/opt/kibana`` - -- Configure Kibana to use it in GeoNetwork: - - ``` shell - $ cd opt/kibana - $ vi config/kibana.yml - server.basePath: "/geonetwork/dashboards" - kibana.index: “.dashboards" - ``` - -- Execute Kibana: - - ``` shell - $ cd /opt/kibana/bin - $ ./kibana & - ``` - - !!! note - - Usually you'll want to configure Kibana to start automatically when the server is startup, this is not covered in this guide. - - -- Verify in a browser that Kibana is running: - -- Kibana should also be visible in Geonetwork at - -## Load Kibana data - -Visit Kibana in a browser using one of the above links and go to 'Saved Objects'. - -Import export.json from diff --git a/docs/manual/docs/maintainer-guide/updating/index.md b/docs/manual/docs/maintainer-guide/updating/index.md index a036fe0d636..48cb0ac2b92 100644 --- a/docs/manual/docs/maintainer-guide/updating/index.md +++ b/docs/manual/docs/maintainer-guide/updating/index.md @@ -1,7 +1,165 @@ -# Updating the application {#updating} +# Updating the application -Since GeoNetwork 4.0.4, passwords stored in the database for the mail server, harvesters, etc. are encrypted with [Jasypt](http://www.jasypt.org/). +## Before you start -By default, a random encryption password is generated when GeoNetwork is started, if it is not already defined, and it is stored in the file **`/geonetwork/WEB-INF/data/config/encryptor/encryptor.properties`**. If you have set the location of the data directory outside of the application, the file will be stored in this external location. Read more at [Customizing the data directory](../../install-guide/customizing-data-directory.md). +### Jasypt encryption settings -The file with the encryption settings **must be copied** to the new installation when upgrading the application; otherwise, it will not be possible to decrypt the existing passwords stored in the database. +Since GeoNetwork 4.0.4, passwords stored in the database for the mail server, harvesters, etc. are encrypted with [Jasypt](https://www.jasypt.org/). + +By default, a random encryption password is generated when GeoNetwork is started, if it is not already defined, and it is stored in the data directory **`data/config/encryptor/encryptor.properties`**. + +=== "Application data directory" + + If you are using the application data direcotry, the encryption password is stored in the file **`/geonetwork/WEB-INF/data/config/encryptor/encryptor.properties`**. + + !!! warning + + This **`encryptor.propertie`** file **must be copied** to the new installation when upgrading the application; otherwise, it will not be possible to decrypt the existing passwords stored in the database. + +=== "External data directory" + + If you have set the location of the data directory outside of the application, the file will be stored in this external location at **`data/config/encryptor/encryptor.properties`** + + Read more at [Customizing the data directory](../../install-guide/customizing-data-directory.md). + +## Updating from GeoNetwork 4.2 to GeoNetwork 4.4 + +Updating from GeoNetwork 4.2 to GeoNetwork 4.4 is a minor upgrade and may be performed in place. + +Before you start: + +=== "Application data directory" + + 1. Stop the services: + + * Application server running GeoNetwork + + 2. When using application data directory be sure to backup **`/geonetwork/WEB-INF/data/config/encryptor/encryptor.properties`** + +=== "External data directory" + + Before you start: + + 1. Stop the services: + + * Application server running GeoNetwork +Update: + +=== "Application data directory" + + 1. GeoNetwork 4.4 requires a Java 11 environment (JRE) to be installed on your system. + + Later versions of Java will not work at present. This must be done prior to installation. + + 2. This release of GeoNetwork 4.4 is developed and tested with Elasticsearch `7.17.15`. + + If required update Elasticsearch, see [Installing search platform](../../install-guide/installing-index.md). + + 3. Minor update may be performed in place, update **`geonetwork`** according to [installation approach](../../install-guide/index.md) used. + + !!! note + + Configurations changes made to application **`/WEB-INF/`** files should be backed up, and the same configuration changes reapplied to the new application. + + With the service stopped you may find it easier to manually deploy (by unzipping the **`geonetwork.war`** + into the correct **`webapps/geonetwork/`** location) in order to apply configuration changes to **`/WEB-INF/**`. + + Read more at [Configuration](../../install-guide/configuring-database.md). + + 4. Restore the backup of **`encryptor.properties`** to the application data directory. + + 5. Restart services: + + * Application server for GeoNetwork + +=== "External data directory" + + 1. GeoNetwork 4.4 requires a Java 11 environment (JRE) to be installed on your system. + + Later versions of Java will not work at present. This must be done prior to installation. + + 2. This release of GeoNetwork 4.4 is developed and tested with Elasticsearch `7.17.15`. + + If required update Elasticsearch, see [Installing search platform](../../install-guide/installing-index.md). + + 3. Minor update may be performed in place, update **`geonetwork`** according to [installation approach](../../install-guide/index.md) used. + + !!! note + + Configurations changes made to application **`/WEB-INF/`** files should be backed up, and the same configuration changes reapplied to the new application. + + With the service stopped you may find it easier to manually deploy (by unzipping the **`geonetwork.war`** + into the correct **`webapps/geonetwork/`** location) in order to apply configuration changes to **`/WEB-INF/**`. + + Read more at [Configuration](../../install-guide/configuring-database.md). + + 4. Restart services: + + * Application server for GeoNetwork + +Guidance: + +* [Changelog 4.4.x](../../overview/change-log/latest/index.md) +* No additional guidance provided at this time. + +## Upgrade from GeoNetwork 3.0 to GeoNetwork 4.4 + +Upgrading from GeoNetwork 3.0 to GeoNetwork 4.4 is a major upgrade. + +We do not recommend updating a production system. Instead treat this as a migration, setting up a new GeoNetwork 4.0 installation and transfer data and settings. + +Before you start: + +* Backup your database + +* Backup your data directory + +GeoNetwork for Migration: + +1. GeoNetwork 4.4 requires a Java 11 environment (JRE) to be installed on your system. + + Later versions of Java will not work at present. This must be done prior to installation. + +2. GeoNetwork 4 changes the search engine to Elasticsearch. + + For installation instructions see [Installing search platform](../../install-guide/installing-index.md). + +2. Perform a new install of GeoNetwork 4.0, according to [installation approach](../../install-guide/index.md) used. + +3. Configure GeoNetwork use to your database and data directory. + +4. Start services: + + * Database + * Elasticsearch + * Application server for GeoNetwork + +4. GeoNetwork 4 encrypts stored passwords using Jasypt as described at the top of this page. + + During startup a random encryption password will be generated and stored in data directory **`data/config/encryptor/encryptor.properties`**. + +5. During startup the database schema will be updated to match the table structure required for GeoNetwork 4. + +6. GeoNetwork will create indices automatically when connecting to Elasticsearch. + +Guidance: + +* Be advised of the following changes in application functionality: + + * [Permalink to search page from GeoNetwork 3.x will need to be updated to work with 4.x. ](../../user-guide/quick-start/index.md#upgrading-from-geonetwork-3-guidance) + +* Please be advised of the following changes to application services: + * [Search / q service is replaced by Elasticsearch](../../api/q-search.md#upgrading-from-geonetwork-30-guidance) + * [Virtual CSW end-points are replaced by sub-portals](../../api/csw.md#upgrading-from-geonetwork-30-guidance) + * [RDF DCAT API no longer available](../../api/rdf-dcat.md#upgrading-from-geonetwork-30-guidance) + * [Z39-50 API is no longer available](../../api/z39-50.md#upgrading-from-geonetwork-30-guidance) + * [OpenSearch no longer available](../../api/opensearch.md#upgrading-from-geonetwork-30-guidance) + * [Open Archives Initiative Protocol for Metadata Harvesting (OAI-PMH) is not migrated](../../api/oai-pmh.md#upgrading-from-geonetwork-30-guidance) + * [GeoNetwork API support for Report Uploads not migrated](../../api/the-geonetwork-api.md#upgrading-from-geonetwork-3-guidance) + + The above links provided recommendations and mitigation measures where appropriate. + +* For information on new features and functionality: + + * [Changelog 4.4.x](../../overview/change-log/latest/index.md) + * [Changelog 4.0.x](../../overview/change-log/archive/index.md#40x) diff --git a/docs/manual/docs/overview/about.md b/docs/manual/docs/overview/about.md index 3737502a229..967fa95ba83 100644 --- a/docs/manual/docs/overview/about.md +++ b/docs/manual/docs/overview/about.md @@ -1,6 +1,6 @@ # About -GeoNetwork-opensource is a catalog allowing visitors to search and discover spatial information. Visitors are can the included map viewer to preview datasets interactively. +GeoNetwork opensource is a catalog allowing visitors to search and discover spatial information. Visitors are can the included map viewer to preview datasets interactively. GeoNetwork provides the tools to managing spatially referenced resources including record editing. diff --git a/docs/manual/docs/overview/authors.md b/docs/manual/docs/overview/authors.md index 8cba5d77de6..60099cdf93a 100644 --- a/docs/manual/docs/overview/authors.md +++ b/docs/manual/docs/overview/authors.md @@ -8,21 +8,21 @@ In brief the committee votes on proposals on the geonetwork-dev mailinglist. Pro ### Members of the Project Steering Committee -- Emanuele Tajariol -- Florent Gravin -- Francois Prunayre -- Jeroen Ticheler - Chair -- Jose Garcia -- Paul van Genuchten -- Simon Pigot -- Jo Cook +- Jeroen Ticheler (jeroen ticheler * geocat net) [GeoCat](https://www.geocat.net) - Chair +- Francois Prunayre [Titellus](https://titellus.net) +- Simon Pigot [CSIRO](https://www.csiro.au) +- Florent Gravin [CamptoCamp](https://camptocamp.com) +- Jose Garcia [GeoCat](https://www.geocat.net) +- Jo Cook [Astun Technology](https://www.astuntechnology.com) +- Paul van Genuchten [ISRIC](https://www.isric.org) ### Former members of the PSC -- Andrea Carboni -- Archie Warnock -- Patrizia Monteduro +- Patrizia Monteduro (Patrizia Monteduro * fao org) [FAO-UN](https://www.fao.org) +- Emanuele Tajariol (e tajariol * mclink it - GeoSolutions) - Jesse Eichar +- Andrea Carboni (acarboni * crisalis-tech com - Independent consultant) +- Archie Warnock (warnock * awcubed com) [A/WWW Enterprises](https://www.awcubed.com) ## Committers @@ -42,6 +42,8 @@ Committers list is available [here for the core team](https://github.com/orgs/ge - Paul van Genuchten - Antonio Cerciello - Michel Gabriel +- Jody Garnett +- David Blasby ## Contributors diff --git a/docs/manual/docs/overview/change-log/archive/index.md b/docs/manual/docs/overview/change-log/archive/index.md index 3c9dd91e5f5..824ca283152 100644 --- a/docs/manual/docs/overview/change-log/archive/index.md +++ b/docs/manual/docs/overview/change-log/archive/index.md @@ -18,6 +18,22 @@ Volunteers wishing to backport security fixes to older versions of GeoNetwork ar - [Version 4.0.0 Alpha.2](../version-4.0.0-alpha.2.md) - [Version 4.0.0 Alpha.1](../version-4.0.0-alpha.1.md) +## 3.12.x + +- [Version 3.12.12](../version-3.12.12.md) +- [Version 3.12.11](../version-3.12.11.md) +- [Version 3.12.10](../version-3.12.10.md) +- [Version 3.12.9](../version-3.12.9.md) +- [Version 3.12.8](../version-3.12.8.md) +- [Version 3.12.7](../version-3.12.7.md) +- [Version 3.12.6](../version-3.12.6.md) +- [Version 3.12.5](../version-3.12.5.md) +- [Version 3.12.4](../version-3.12.4.md) +- [Version 3.12.3](../version-3.12.3.md) +- [Version 3.12.3](../version-3.12.2.md) +- [Version 3.12.1](../version-3.12.1.md) +- [Version 3.12.0](../version-3.12.0.md) + ## 3.10.x - [Version 3.10.10](../version-3.10.10.md) diff --git a/docs/manual/docs/overview/change-log/index.md b/docs/manual/docs/overview/change-log/index.md index 152bd21cb85..83a627c6074 100644 --- a/docs/manual/docs/overview/change-log/index.md +++ b/docs/manual/docs/overview/change-log/index.md @@ -1,6 +1,6 @@ # Change log -Notable changes made to a GeoNetwork-open source including new features, migration instructions, and bug fixes. +Notable changes made to GeoNetwork opensource including new features, migration instructions, and bug fixes. - [Latest](latest/index.md) - [Stable](stable/index.md) diff --git a/docs/manual/docs/overview/change-log/latest/index.md b/docs/manual/docs/overview/change-log/latest/index.md index 6e78c807893..8560067a361 100644 --- a/docs/manual/docs/overview/change-log/latest/index.md +++ b/docs/manual/docs/overview/change-log/latest/index.md @@ -6,8 +6,10 @@ This series is under active development by our community, with new features, imp ## Latest -- [Version 4.4.1](../version-4.4.1.md) +- [Version 4.4.3](../version-4.4.3.md) ## History +- [Version 4.4.2](../version-4.4.2.md) +- [Version 4.4.1](../version-4.4.1.md) - [Version 4.4.0](../version-4.4.0.md) diff --git a/docs/manual/docs/overview/change-log/maintenance/index.md b/docs/manual/docs/overview/change-log/maintenance/index.md index a7939f78428..bced56a1b6a 100644 --- a/docs/manual/docs/overview/change-log/maintenance/index.md +++ b/docs/manual/docs/overview/change-log/maintenance/index.md @@ -5,18 +5,4 @@ This series is under active use by our community, with regular improvements, doc ## Latest -* [Version 3.12.11](../version-3.12.11.md) - ## History - -* [Version 3.12.10](../version-3.12.10.md) -* [Version 3.12.9](../version-3.12.9.md) -* [Version 3.12.8](../version-3.12.8.md) -* [Version 3.12.7](../version-3.12.7.md) -* [Version 3.12.6](../version-3.12.6.md) -* [Version 3.12.5](../version-3.12.5.md) -* [Version 3.12.4](../version-3.12.4.md) -* [Version 3.12.3](../version-3.12.3.md) -* [Version 3.12.3](../version-3.12.2.md) -* [Version 3.12.1](../version-3.12.1.md) -* [Version 3.12.0](../version-3.12.0.md) diff --git a/docs/manual/docs/overview/change-log/stable/index.md b/docs/manual/docs/overview/change-log/stable/index.md index 7dd249e7131..ad5d4b53c6f 100644 --- a/docs/manual/docs/overview/change-log/stable/index.md +++ b/docs/manual/docs/overview/change-log/stable/index.md @@ -5,11 +5,13 @@ This series is under active use by our community, with regular improvements, doc ## Latest -- [Version 4.2.7](../version-4.2.7.md) +- [Version 4.2.9](../version-4.2.9.md) ## History +- [Version 4.2.8](../version-4.2.8.md) +- [Version 4.2.7](../version-4.2.7.md) - [Version 4.2.6](../version-4.2.6.md) - [Version 4.2.5](../version-4.2.5.md) - [Version 4.2.4](../version-4.2.4.md) diff --git a/docs/manual/docs/overview/change-log/version-3.12.12.md b/docs/manual/docs/overview/change-log/version-3.12.12.md new file mode 100644 index 00000000000..79c6d07c10f --- /dev/null +++ b/docs/manual/docs/overview/change-log/version-3.12.12.md @@ -0,0 +1,24 @@ +# Version 3.12.12 + +GeoNetwork 3.12.12 is a minor release. + +## List of changes + +- Metadata + - [BP] [Fix exception handling from schematron validation so that it flags the metadata as invalid if there is an exception](https://github.com/geonetwork/core-geonetwork/pull/6978) + - [BP] [Overview not shown in PDF export when the overview image is stored in GeoNetwork and requires authentication to access it](https://github.com/geonetwork/core-geonetwork/pull/7556) + +- Administration + - [BP] [Harvesters / Reset harvester history pagination when selecting a harvester](https://github.com/geonetwork/core-geonetwork/pull/7836) + - [BP] [GeoNetwork harvester / Check if a resource exists to save it, instead of trying to retrieve the file details, to avoid confusing NoSuchFileException exception](https://github.com/geonetwork/core-geonetwork/pull/7846) + +- Other + - [BP] [Fix cookies path when deployed on root "/" context](https://github.com/geonetwork/core-geonetwork/pull/7446) + - [BP] [Remove exception class name from the error message](https://github.com/geonetwork/core-geonetwork/pull/6977) + - Update `org.json:json` from version 20140107 to 20240205 + - Update `commons-fileupload` from version 1.3.3 to 1.5 + - Documentation / Manual improvements + +and more \... see [3.12.12 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A3.12.12+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A3.12.12+is%3Aclosed) for full details. + +**Full Changelog**: [here](https://github.com/geonetwork/core-geonetwork/compare/3.12.11...3.12.12) diff --git a/docs/manual/docs/overview/change-log/version-4.0.0.md b/docs/manual/docs/overview/change-log/version-4.0.0.md index 35fa9eba20a..35cfa79493f 100644 --- a/docs/manual/docs/overview/change-log/version-4.0.0.md +++ b/docs/manual/docs/overview/change-log/version-4.0.0.md @@ -12,12 +12,6 @@ By moving from Lucene to Elasticsearch GeoNetwork 4.0.0 offers: The Elasticsearch engine supports GeoNetwork core functionality: user can search, edit records and manage the catalogue as usual. Only the searches (and all related protocols) are affected by the change to Elasticsearch. The editor and admin application remains the same. -There remains opportunities to help: - -- **A number of features that you enjoy in GeoNetwork 3 may not yet be available**. -- Multilingual editing is the same, but multilingual search is not yet fully operational. -- Read carefully the [list of missing features](https://github.com/geonetwork/core-geonetwork/issues/4727) before migrating. - Before you start: - Installing the application require to install an Elasticsearch instance (see [Installing search platform](../../install-guide/installing-index.md)). @@ -49,3 +43,44 @@ Release highlights: - [Restrict access to Elasticsearch API](https://github.com/geonetwork/core-geonetwork/pull/5023) See [4.0.0 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A4.0.0+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?q=is%3Apr+milestone%3A4.0.0+is%3Aclosed) for full details. + +## Removed features + +There remains opportunities to help: + +- **A number of features that you enjoy in GeoNetwork 3 may not yet be available**. +- Multilingual editing is the same, but multilingual search is not yet fully operational. + +Read carefully the [list of missing features](https://github.com/geonetwork/core-geonetwork/issues/4727) before migrating. + +* Search API - RDF (replaced with use CSW with DCAT output format) +* Search API - Z39.50 +* Search API - SRU +* Search API - OpenSearch +* Search API - OAI-PMH +* GeoNetwork API features: + + - ReportUploads + - XLink: Remove directory entry used in other record + - Lucene: Lucene is no longer available, migrated scripts to Elastisearch API + +* Index + + - Search on bounding polygons, currently only the geometry is indexed but not used in search + +* Admin + + - Information about the index - not needed, can be replaced by Kibana admin? + - Schematron rules (See SchematronCriteriaGroupServiceIntegrationTest) + - Search / Statistics and search stat dashboards + +* Security + + - ECAS not tested + +* Harvester + + - GeoNetwork 4 can not be harvested using GeoNetwork protocol by GeoNetwork 3 version. + + Mitigation: Use CSW Harvester instead. + diff --git a/docs/manual/docs/overview/change-log/version-4.0.2.md b/docs/manual/docs/overview/change-log/version-4.0.2.md index e6609dd481e..f886103c3e6 100644 --- a/docs/manual/docs/overview/change-log/version-4.0.2.md +++ b/docs/manual/docs/overview/change-log/version-4.0.2.md @@ -4,7 +4,7 @@ GeoNetwork 4.0.2 release is a minor release but adds a better multilingual suppo ## Database migration -With the possibility to [restore deleted record](https://github.com/geonetwork/core-geonetwork/pull/4817), catalog maintainer has to update the database with the [following migration SQL script](https://github.com/geonetwork/core-geonetwork/blob/master/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v3110/migrate-default.sql#L10-L27). +With the possibility to [restore deleted records](https://github.com/geonetwork/core-geonetwork/pull/4817), the catalog maintainer has to update the database with the [following migration SQL script](https://github.com/geonetwork/core-geonetwork/blob/master/web/src/main/webapp/WEB-INF/classes/setup/sql/migrate/v3110/migrate-default.sql#L10-L27). ## Index migration @@ -12,9 +12,9 @@ Index migration is only required if you created custom facets configuration in t ### Codelist -`t_` is now `l_` and is an object composed of: +``codelist`` is now ``cl_`` and is an object composed of: -So `et` was: +characterSet was: ``` js codelist_characterSet: [ @@ -39,13 +39,13 @@ cl_characterSet: [{ }] ``` -So if using `xt` in a facet, use ``cl_characterSet.default`` with this new version. +So if using ``codelist_characterSet_text`` in a facet, use ``cl_characterSet.default`` with this new version. ### Thesaurus -The `ds` field is now only used for rendering (not for query) to limit the total number of field and avoid some errors on large catalogues. +The ``allKeywords`` field is now only used for rendering (not for query) to limit the total number of field and avoid some errors on large catalogues. -Use the per thesaurus fields which were named ``thesaurus_geonetwork+external/local+type+thesaurusid`` and are now `id`. The field is also an object composed of: +Use the per thesaurus fields, which were named ``thesaurus_geonetwork+external/local+type+thesaurusid`` and are now ``th_thesaurusid``. The field is also an object composed of: ``` js thesaurus_geonetworkthesaurusexternalthemeeeatopics: [ @@ -73,11 +73,11 @@ th_eea-topics: [{ ### Topic category -`ic` is renamed to `ic` and has the same structure as a codelist. +``topic`` is renamed to ``cl_topic`` and has the same structure as a codelist. ### GeoTag -`ag` is now stored in the ``template field for keyword types](https://github.com/geonetwork/core-geonetwork/pull/5243) ie. [keywordType-place`` +``geotag`` is now stored in the [template field for keyword types](https://github.com/geonetwork/core-geonetwork/pull/5243) ie. ``keywordType-place`` For more details check [Configuring search fields](../../customizing-application/configuring-search-fields.md) and [Configuring faceted search](../../customizing-application/configuring-faceted-search.md). diff --git a/docs/manual/docs/overview/change-log/version-4.2.8.md b/docs/manual/docs/overview/change-log/version-4.2.8.md new file mode 100644 index 00000000000..99c8f867363 --- /dev/null +++ b/docs/manual/docs/overview/change-log/version-4.2.8.md @@ -0,0 +1,17 @@ +# Version 4.2.8 {#version-428} + +GeoNetwork 4.2.8 release is a minor release. + +## List of changes + +Release highlights: + +- [Elasticsearch / Update to 7.17.15](https://github.com/geonetwork/core-geonetwork/pull/7368) +- [Fix CSW GetRecords query parameters processing](https://github.com/geonetwork/core-geonetwork/pull/7529) +- [INSPIRE Validator - exception handling improvements](https://github.com/geonetwork/core-geonetwork/pull/7519) +- [Minor versions library updates](https://github.com/geonetwork/core-geonetwork/pull/7405) +- [Record view / Invalid timezone shift for years outside moment's 10 years range](https://github.com/geonetwork/core-geonetwork/pull/7553) +- [Record view / Overview not shown in PDF export when the overview image requires authentication to access it](https://github.com/geonetwork/core-geonetwork/pull/7556) +- SpringDoc fixes + +and more \... see [4.2.8 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A4.2.8+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A4.2.8+is%3Aclosed) for full details. diff --git a/docs/manual/docs/overview/change-log/version-4.2.9.md b/docs/manual/docs/overview/change-log/version-4.2.9.md new file mode 100644 index 00000000000..875c88c9d5e --- /dev/null +++ b/docs/manual/docs/overview/change-log/version-4.2.9.md @@ -0,0 +1,18 @@ +# Version 4.2.9 {#version-429} + +GeoNetwork 4.2.9 release is a minor release. + +## List of changes + +Major changes: + +- [Create a metadata / Add dynamic and download privileges to the users in the same group](https://github.com/geonetwork/core-geonetwork/pull/7744) +- [Decouple metadata user feedback from metadata rating feature](https://github.com/geonetwork/core-geonetwork/pull/7796) +- [Extend http proxy to manage duplicated parameters](https://github.com/geonetwork/core-geonetwork/pull/7854) +- [Fix MIME-types on attachments](https://github.com/geonetwork/core-geonetwork/pull/7675) +- [Fix pdf link to the application website](https://github.com/geonetwork/core-geonetwork/pull/7681) +- Update `org.json:json` from version 20140107 to 20240205 +- Documentation / Manual improvements +- Documentation / API SpringDoc fixes + +and more \... see [4.2.9 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A4.2.9+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A4.2.9+is%3Aclosed) for full details. diff --git a/docs/manual/docs/overview/change-log/version-4.4.2.md b/docs/manual/docs/overview/change-log/version-4.4.2.md new file mode 100644 index 00000000000..807950b6642 --- /dev/null +++ b/docs/manual/docs/overview/change-log/version-4.4.2.md @@ -0,0 +1,29 @@ +# Version 4.4.2 {#version-442} + +GeoNetwork 4.4.2 release is a minor release. + +## Migration notes + +### Java + +**Version 4.4 only works on Java 11.** + +### Index changes + +After update, don't forget to go to admin console --> tools --> Delete index and reindex. + +## List of changes + +Release highlights: + +- [Security / Configuration to restrict the hosts and ports accessible by the http proxy servlet](https://github.com/geonetwork/core-geonetwork/pull/7326) +- [Elasticsearch / Update to 7.17.15](https://github.com/geonetwork/core-geonetwork/pull/7368) +- [Fix CSW GetRecords query parameters processing](https://github.com/geonetwork/core-geonetwork/pull/7529) +- [INSPIRE Validator - exception handling improvements](https://github.com/geonetwork/core-geonetwork/pull/7519) +- [Minor versions library updates](https://github.com/geonetwork/core-geonetwork/pull/7405) +- [Record view / Invalid timezone shift for years outside moment's 10 years range](https://github.com/geonetwork/core-geonetwork/pull/7553) +- [Record view / Overview not shown in PDF export when the overview image requires authentication to access it](https://github.com/geonetwork/core-geonetwork/pull/7556) +- SpringDoc fixes + + +See [4.4.2 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A4.4.2+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A4.4.2+is%3Aclosed) for full details. diff --git a/docs/manual/docs/overview/change-log/version-4.4.3.md b/docs/manual/docs/overview/change-log/version-4.4.3.md new file mode 100644 index 00000000000..25985a774f8 --- /dev/null +++ b/docs/manual/docs/overview/change-log/version-4.4.3.md @@ -0,0 +1,46 @@ +# Version 4.4.3 {#version-423} + +GeoNetwork 4.4.3 release is a minor release. + +## Migration notes + +### Java + +**Version 4.4 only works on Java 11.** + +### Index changes + +This version use Elasticsearch version 8 Java client, it is recommended to use an Elasticsearch version 8 server. +However version 7.15+ and 8+ have been tested. + +After update, don't forget to go to admin console --> tools --> Delete index and reindex. + +### Map + +[Stamen background layers are not available, update your maps](https://github.com/geonetwork/core-geonetwork/pull/7715). + + +## List of changes + +Major changes: + +- [Elasticssearch 8 upgrade](https://github.com/geonetwork/core-geonetwork/pull/7599) +- [Editor / Distribution panel improvements](https://github.com/geonetwork/core-geonetwork/pull/7468) +- [Thesaurus / Add support for codelist described using SDMX](https://github.com/geonetwork/core-geonetwork/pull/7790) +- [Thesaurus / Add support for thesaurus described using OWL format](https://github.com/geonetwork/core-geonetwork/pull/7674) +- [Thesaurus / Improve support of EU publication office SKOS format](https://github.com/geonetwork/core-geonetwork/pull/7673) +- [INSPIRE / Add testsuite for IACS](https://github.com/geonetwork/core-geonetwork/pull/7756) +- [Map viewer / Remove Stamen background layers - no longer available](https://github.com/geonetwork/core-geonetwork/pull/7715) +- [i18n / Add welsh language for user interface](https://github.com/geonetwork/core-geonetwork/pull/7851) +- [Index / Add danish language configuration](https://github.com/geonetwork/core-geonetwork/pull/7697) +- [Index / Translated the index warnings and errors](https://github.com/geonetwork/core-geonetwork/pull/7531) +- [Create a metadata / Add dynamic and download privileges to the users in the same group](https://github.com/geonetwork/core-geonetwork/pull/7744) +- [Decouple metadata user feedback from metadata rating feature](https://github.com/geonetwork/core-geonetwork/pull/7796) +- [Extend http proxy to manage duplicated parameters](https://github.com/geonetwork/core-geonetwork/pull/7854) +- [Fix MIME-types on attachments](https://github.com/geonetwork/core-geonetwork/pull/7675) +- [Fix pdf link to the application website](https://github.com/geonetwork/core-geonetwork/pull/7681) +- Update `org.json:json` from version 20140107 to 20240205 +- Documentation / Manual improvements +- Documentation / API SpringDoc fixes + +and more \... see [4.4.3 issues](https://github.com/geonetwork/core-geonetwork/issues?q=is%3Aissue+milestone%3A4.4.3+is%3Aclosed) and [pull requests](https://github.com/geonetwork/core-geonetwork/pulls?page=3&q=is%3Apr+milestone%3A4.4.3+is%3Aclosed) for full details. diff --git a/docs/manual/docs/tutorials/customui/search/defaultview.md b/docs/manual/docs/tutorials/customui/search/defaultview.md index 6d9de9e456c..9a25b024f1d 100644 --- a/docs/manual/docs/tutorials/customui/search/defaultview.md +++ b/docs/manual/docs/tutorials/customui/search/defaultview.md @@ -13,17 +13,13 @@ Read instructions about [default view customization](../../../customizing-applic - Propose to sort only by change date and title. -- Use Stamen layer source for search map background. - - Recenter search map on Seoul -- - - Change full map configuration +- Change full map configuration - : - play with initial extent - - change brackground layer list - - add WMS layers to the default map + - play with initial extent + - change brackground layer list + - add WMS layers to the default map - Change default location for map search diff --git a/docs/manual/docs/tutorials/introduction/deployment/index.md b/docs/manual/docs/tutorials/introduction/deployment/index.md index e26395184b7..f1a27168d1d 100644 --- a/docs/manual/docs/tutorials/introduction/deployment/index.md +++ b/docs/manual/docs/tutorials/introduction/deployment/index.md @@ -2,7 +2,7 @@ On this chapter, we will learn how to make GeoNetwork run on your machine or in the cloud. -Up to version 3 GeoNetwork could easily be deployed locally, there is even a [Windows installer](https://my.geocat.net/download/category/6/GeoNetwork.html). But with the arrival of version 4, GeoNetwork has a number of dependencies that need to be deployed separately, which makes the deployment locally more challenging. Instead the GeoNetwork community provides `es` for automated deployment using Docker. [Docker](https://docker.com) is a virtualisation technology which facilitates deployment of a `on` of prepackaged `es` locally or in the cloud. +Up to version 3 GeoNetwork could easily be deployed locally, there is even a [Windows installer](https://my.geocat.net/download/category/6/GeoNetwork.html). But with the arrival of version 4, GeoNetwork has a number of dependencies that need to be deployed separately, which makes the deployment locally more challenging. Instead the GeoNetwork community provides recipes for automated deployment using Docker. [Docker](https://docker.com) is a virtualisation technology which facilitates deployment of a composition of prepackaged images locally or in the cloud. For this workshop we will focus on the use of docker, but we also provide some guidance on how to set up the environment without docker, or even build the application from sources. diff --git a/docs/manual/docs/user-guide/describing-information/deleting-metadata.md b/docs/manual/docs/user-guide/describing-information/deleting-metadata.md index b3f5ee3a16e..0839fdd02b8 100644 --- a/docs/manual/docs/user-guide/describing-information/deleting-metadata.md +++ b/docs/manual/docs/user-guide/describing-information/deleting-metadata.md @@ -1,6 +1,6 @@ # Deleting a record -User must have editing privileges to be able to remove a record. This topic describes the various ways to delete a record. +The user must have editing privileges to be able to remove a record. This topic describes the various ways to delete a record. There are different ways to remove a record: diff --git a/docs/manual/docs/user-guide/describing-information/editing-metadata.md b/docs/manual/docs/user-guide/describing-information/editing-metadata.md index e9d92133c92..c5c89b52766 100644 --- a/docs/manual/docs/user-guide/describing-information/editing-metadata.md +++ b/docs/manual/docs/user-guide/describing-information/editing-metadata.md @@ -4,7 +4,7 @@ This topic describes how to use the metadata editor. ## Using the editor toolbar -The top tool bar provides the main editor actions: +The top toolbar provides the most important functionality available to the editor: ![](img/editor-toolbar.png) @@ -12,26 +12,26 @@ From left to right: - Set metadata categories - Set metadata group -- Advanced features (eg. versioning) +- Advanced features (e.g. versioning) - `Validate` to run metadata validation - `Cancel` to cancel all changes made since the beginning of the editing session - `Save & Close` to save and close the editor -- `Save` to save the metadata or the template. The drowdown also allows to set if the edits are minor or not (ie. the change date of the metadata will not be updated if case of minor edits). +- `Save` to save the metadata or the template. The dropdown also allows to set if the edits are minor or not (i.e. the change date of the metadata will not be updated if case of minor edits). - Change view mode for the editor form, enable tooltips or switch to advanced editor mode ## Navigating the form -To describe the resource, an editor can use the form, which provides different views. By default the editor provides: +To describe the resource, an editor can use the form, which provides different views. By default, the editor provides: -- a `simple view` displaying all fields from the metadata -- a `full view` based on the metadata standard and providing access to all descriptors from the standards -- an `XML view` displaying the record as an XML document +- a `Default` view displaying all fields from the metadata +- an `Advanced` view based on the metadata standard and providing access to all descriptors from the metadata standard in use +- an `XML` view displaying the record as an XML document To switch from one view to another, use the following menu: ![](img/view-mode.png) -Once the view is selected, the editor can browse the list of fields to populate. He can also use the scroll-spy on the lower right corner to quickly access a section. +Once the view is selected, the editor can browse the list of fields to populate. He can also use the scroll-spy in the lower right corner to quickly access a section. ![](img/scoll-spy.png) diff --git a/docs/manual/docs/user-guide/describing-information/index.md b/docs/manual/docs/user-guide/describing-information/index.md index 42a2d6a620e..8808d7a1ec3 100644 --- a/docs/manual/docs/user-guide/describing-information/index.md +++ b/docs/manual/docs/user-guide/describing-information/index.md @@ -2,11 +2,11 @@ This section describes how to catalog resources. First, user needs to choose a standard to use. In general, the following rules apply: -- [Dublin core](https://dublincore.org/) usually used for opendata portal, referencing publication, report, \... +- [Dublin core](https://dublincore.org/) usually used for open data portal, referencing publication, report, \... - [ISO 19115/119/139](https://www.iso.org/iso/en/home/store/catalogue_tc/catalogue_detail.htm?csnumber=32557) used for spatial resources (eg. datasets, services, maps) - [ISO 19110](https://www.iso.org/iso/en/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=39965) used for feature attribute table -The editor form provides uploading of data, graphics, documents, pdf files and any other content type. It supports among other: +The editor form provides uploading of data, graphics, documents, pdf files and any other content type. It supports among others: - full standard support - multilingual metadata editing, @@ -27,4 +27,4 @@ The editor form provides uploading of data, graphics, documents, pdf files and a - [Multilingual editing](multilingual-editing.md) - [Describing resources for the INSPIRE directive](inspire-editing.md) -Based on user profiles (see [Creating user](../../administrator-guide/managing-users-and-groups/creating-user.md)), a dashboard provides easy access to their informations and tasks. +Based on user profiles (see [Creating user](../../administrator-guide/managing-users-and-groups/creating-user.md)), a dashboard provides easy access to their information and tasks. diff --git a/docs/manual/docs/user-guide/describing-information/managing-templates.md b/docs/manual/docs/user-guide/describing-information/managing-templates.md index e3c14f7c1ea..76a42857514 100644 --- a/docs/manual/docs/user-guide/describing-information/managing-templates.md +++ b/docs/manual/docs/user-guide/describing-information/managing-templates.md @@ -1,6 +1,6 @@ # Managing templates {#creating-templates} -Templates are metadata records that you can use for describing new resources. This topic describes how to load and manage templates. +Templates are metadata records that you can use as the starting point to describe new resources. This topic describes how to load and manage templates. ## Creating and managing templates diff --git a/docs/manual/docs/user-guide/publishing/img/set-selection-privileges.png b/docs/manual/docs/user-guide/publishing/img/set-selection-privileges.png index 3a543a29c9f..612bbd6f300 100644 Binary files a/docs/manual/docs/user-guide/publishing/img/set-selection-privileges.png and b/docs/manual/docs/user-guide/publishing/img/set-selection-privileges.png differ diff --git a/docs/manual/docs/user-guide/publishing/managing-privileges.md b/docs/manual/docs/user-guide/publishing/managing-privileges.md index 44d37d54a57..7670dbff69e 100644 --- a/docs/manual/docs/user-guide/publishing/managing-privileges.md +++ b/docs/manual/docs/user-guide/publishing/managing-privileges.md @@ -20,7 +20,7 @@ Below is a brief description for each privilege to help you identify which ones **Interactive Map**: Users in the specified group/s are able to get an interactive map. The interactive map has to be created separately using a Web Map Server such as GeoServer, which is distributed with GeoNetwork. -**Featured**: When randomly selected by GeoNetwork, the metadata record can appear in the `ed` section of the GeoNetwork home page. +**Featured**: When randomly selected by GeoNetwork, the metadata record can appear in the `Featured` section of the GeoNetwork home page. **Notify**: Users in the specified group receive notification if data attached to the metadata record is downloaded. diff --git a/docs/manual/docs/user-guide/quick-start/index.md b/docs/manual/docs/user-guide/quick-start/index.md index 28cc0c5594e..2dc7839aa5a 100644 --- a/docs/manual/docs/user-guide/quick-start/index.md +++ b/docs/manual/docs/user-guide/quick-start/index.md @@ -111,3 +111,26 @@ From the results or the record view, you can add WMS layers referenced in a meta ![](img/map-africa-basin.png) Read more about use of the [Maps and dataset visualisation](../map/index.md) + +## Upgrading from GeoNetwork 3 Guidance + +!!! warning + + Permalink to search page from GeoNetwork 3.x will need to be updated to work with 4.x. + +If you have linked to search results please be advised that some of the URL parameters have changed and you should test and recreate these URLs. + +* The catalog services does not provide **permalink** to search results. +* URLs created with ***GeoNetwork 3.x*** search results are not intended to work with ***GeoNetwork 4.x***. + +* The use of a new search engine has resulted in changes to the URL parameter names and facets: + + Very simple text query: + + * GeoNetwork 3: ``http://localhost:8080/geonetwork/srv/eng/catalog.search#/search?any=road`` + * GeoNetwork 4: ``http://localhost:8080/geonetwork/srv/eng/catalog.search#/search?any=road`` + + The use of a new search engine has resulted in changes to the URL parameter names and facets: + + * GeoNetwork 3: ``http://localhost:8080/geonetwork/srv/eng/catalog.search#/searchsrv/eng/catalog.search#/search?resultType=details&sortBy=changeDate&any=road&fast=index&_content_type=json&from=1&to=20`` + * GeoNetwork 4: ``http://localhost:8080/geonetwork/srv/eng/catalog.search#/search?isTemplate=n&resourceTemporalDateRange=%7B%22range%22:%7B%22resourceTemporalDateRange%22:%7B%22gte%22:null,%22lte%22:null,%22relation%22:%22intersects%22%7D%7D%7D&sortBy=dateStamp&sortOrder=desc&any=road&from=1&to=20`` diff --git a/docs/manual/docs/user-guide/workflow/life-cycle.md b/docs/manual/docs/user-guide/workflow/life-cycle.md index 5adb41f1dde..c1bf876157f 100644 --- a/docs/manual/docs/user-guide/workflow/life-cycle.md +++ b/docs/manual/docs/user-guide/workflow/life-cycle.md @@ -31,7 +31,7 @@ In the last case, to enable workflow and change the status from `Unknown` to `Dr To enable workflow for the full catalogue or certain groups, check Administration --> Settings --> Metadata Workflow. In workflow mode, in case approved records are modified, you're working on a copy of the approved record. Changes on the record will not be visible to users outside your group until the modified record is approved again. -When done editing you can submit a record for review by a content reviewer. The submit button is available on the `rd` menu in the metadata view. A popup will open in which you can leave a message for the content reviewer. +When done editing you can submit a record for review by a content reviewer. The submit button is available on the `manage record` menu in the metadata view. A popup will open in which you can leave a message for the content reviewer. ![](img/submit-for-review.png) diff --git a/docs/manual/help.yml b/docs/manual/help.yml index f0d5ed46422..54ce701b640 100644 --- a/docs/manual/help.yml +++ b/docs/manual/help.yml @@ -61,14 +61,24 @@ plugins: Record: Record Map: Map +validation: + nav: + omitted_files: warn + not_found: warn + absolute_links: warn + links: + not_found: warn + absolute_links: warn + unrecognized_links: warn + markdown_extensions: - admonition - attr_list - def_list - pymdownx.details - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg options: custom_icons: - overrides/.icons diff --git a/docs/manual/mkdocs.yml b/docs/manual/mkdocs.yml index a1ce6a1cdc3..9c58d1c236b 100644 --- a/docs/manual/mkdocs.yml +++ b/docs/manual/mkdocs.yml @@ -10,7 +10,7 @@ repo_url: https://github.com/geonetwork/core-geonetwork edit_uri: edit/main/docs/manual/docs # Copyright -copyright: Copyright © 2001 - 2023 FAO-UN and others +copyright: Copyright © 2001 - 2024 FAO-UN and others extra_css: - assets/stylesheets/extra.css @@ -54,7 +54,9 @@ theme: # Plugins - install using: pip3 install -r requirements.txt plugins: - - search + - exclude: + glob: + - annexes/gallery/bin/README.md - i18n: docs_structure: suffix reconfigure_material: true @@ -63,20 +65,18 @@ plugins: name: English build: true default: true - site_name: 'GeoNetwork Opensource (EN)' + site_name: 'GeoNetwork opensource (EN)' - locale: fr name: Français build: !ENV [FRENCH,true] - site_name: 'GeoNetwork Opensource (FR)' + site_name: 'GeoNetwork opensource (FR)' site_description: Catalogue GeoNetwork pour répertorier, rechercher et examiner les enregistrements. nav_translations: Home: Home Search: Search Record: Record Map: Map - - exclude: - glob: - - annexes/gallery/bin/README.md + - search # Customizations extra: @@ -93,6 +93,17 @@ extra: link: https://geonetwork-opensource.org/ name: GeoNetwork Website +# For use with --strict to produce failures on build warnings +validation: + nav: + omitted_files: warn + not_found: warn + absolute_links: warn + links: + not_found: warn + absolute_links: warn + unrecognized_links: warn + # Extensions # - These are carefully chosen to work with pandoc markdown support for whole document translation markdown_extensions: @@ -101,8 +112,8 @@ markdown_extensions: - def_list - pymdownx.details - pymdownx.emoji: - emoji_index: !!python/name:materialx.emoji.twemoji - emoji_generator: !!python/name:materialx.emoji.to_svg + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg options: custom_icons: - overrides/.icons @@ -138,10 +149,14 @@ nav: - overview/change-log/index.md - 'Latest': - overview/change-log/latest/index.md + - overview/change-log/version-4.4.3.md + - overview/change-log/version-4.4.2.md - overview/change-log/version-4.4.1.md - overview/change-log/version-4.4.0.md - 'Stable': - overview/change-log/stable/index.md + - overview/change-log/version-4.2.9.md + - overview/change-log/version-4.2.8.md - overview/change-log/version-4.2.7.md - overview/change-log/version-4.2.6.md - overview/change-log/version-4.2.5.md @@ -152,6 +167,7 @@ nav: - overview/change-log/version-4.2.0.md - 'Maintenance': - overview/change-log/maintenance/index.md + - overview/change-log/version-3.12.12.md - overview/change-log/version-3.12.11.md - overview/change-log/version-3.12.10.md - overview/change-log/version-3.12.9.md @@ -315,22 +331,17 @@ nav: - 'Installation': - 'Installing': - install-guide/index.md + - install-guide/installing-index.md - install-guide/installing-from-zip.md - install-guide/installing-from-war-file.md - install-guide/installing-with-docker.md - install-guide/installing-from-source-code.md - - install-guide/installing-index.md - 'Configuration': - install-guide/configuring-database.md - install-guide/customizing-data-directory.md - install-guide/logging.md - install-guide/map-print-setup.md - install-guide/loading-samples.md - - 'Setting up search/content statistics': - - maintainer-guide/statistics/index.md - - maintainer-guide/statistics/setup-elasticsearch.md - - maintainer-guide/statistics/setup-kibana.md - - maintainer-guide/statistics/setup-geonetwork.md - maintainer-guide/production-use/index.md - 'Updating the application': maintainer-guide/updating/index.md - 'API': @@ -338,9 +349,11 @@ nav: - api/the-geonetwork-api.md - api/search.md - api/csw.md + - api/inspire_atom.md + - 'Open Archive Initiative': api/oai-pmh.md - api/opensearch.md - - api/rdf-dcat.md - - api/oai-pmh.md + - api/q-search.md + - 'RDF DCAT': api/rdf-dcat.md - api/z39-50.md - 'Development': - devel/index.md diff --git a/docs/manual/overrides/assets/stylesheets/extra.css b/docs/manual/overrides/assets/stylesheets/extra.css index 8155e94365e..07b39e34520 100644 --- a/docs/manual/overrides/assets/stylesheets/extra.css +++ b/docs/manual/overrides/assets/stylesheets/extra.css @@ -9,48 +9,6 @@ img + em, .browser-border + em, .browser-mockup + em { font-size: 0.75rem; } -/* grid */ -.md-typeset .grid { - column-count: 2; - column-gap: 2em; - margin-bottom: 20px; -} -.md-typeset .grid dl { - display: grid; - grid-template-columns: repeat(auto-fit,minmax(16rem,1fr)); - margin: 0; -} -.md-typeset .grid.cards dt, .md-typeset .grid.cards dd { - border: 0.05rem solid var(--md-default-fg-color--lightest); - border-radius: 0.1rem; - display: block; - margin: 0; - padding: 0.8rem; - transition: border .25s,box-shadow .25s; -} -.md-typeset .grid.cards dt { - font-weight: bold; -} -.md-typeset .grid.cards dt .twemoji { - margin-right: 5px; -} -.md-typeset .grid.cards dd { - margin-bottom: 0.8rem; - margin-top: -1px; -} -.md-typeset .grid.cards dd p { - margin: 0; -} -@media (max-width: 768px) { - .md-typeset .grid dl { - display: inline-block; - margin-bottom: 20px; - } - .md-typeset .grid.cards dt, .md-typeset .grid.cards dd { - width: calc(100vw - 1.2rem - 1.2rem); - } -} - /* definition list used to display general inputs */ .md-typeset dl dd { margin: 10px 0; diff --git a/docs/manual/pom.xml b/docs/manual/pom.xml index 5c5ba26f734..ec2012561cc 100644 --- a/docs/manual/pom.xml +++ b/docs/manual/pom.xml @@ -27,7 +27,7 @@ gn-docs org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 gn-guide @@ -60,7 +60,7 @@ - + @@ -77,7 +77,7 @@ - + @@ -104,13 +104,6 @@
- - - maven-install-plugin - - true - - maven-deploy-plugin diff --git a/docs/manual/requirements.txt b/docs/manual/requirements.txt index a3fae0623c6..8a3fcb93da6 100644 --- a/docs/manual/requirements.txt +++ b/docs/manual/requirements.txt @@ -1,4 +1,4 @@ -mkdocs-material +mkdocs-material>=9.5.3 mkdocs-static-i18n>=1.0.5 mkdocs-include-markdown-plugin mkdocs-exclude diff --git a/docs/pom.xml b/docs/pom.xml index 492f3233def..41fadf135f2 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 gn-docs diff --git a/doi/pom.xml b/doi/pom.xml index 89ae28cbac5..b18b6e06c3f 100644 --- a/doi/pom.xml +++ b/doi/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/domain/pom.xml b/domain/pom.xml index 235c6478416..f715db3010e 100644 --- a/domain/pom.xml +++ b/domain/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -293,6 +293,7 @@ test 9300 9200 + -Xmx2g diff --git a/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java b/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java index d1a599629ed..6421552e0a4 100644 --- a/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java +++ b/domain/src/main/java/org/fao/geonet/domain/MetadataStatus.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2020 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -168,7 +168,6 @@ public int getMetadataId() { * Set the id of the metadata the status is related to. * * @param metadataId the id of the metadata the status is related to. - * @return this id object */ public void setMetadataId(int metadataId) { this.metadataId = metadataId; @@ -188,7 +187,6 @@ public int getUserId() { * Set the user who changed the status. * * @param userId the user who changed the status. - * @return this id object */ public void setUserId(int userId) { this.userId = userId; @@ -220,12 +218,11 @@ public void setChangeMessage(String changeMessage) { *

* * This may be null for {@link StatusValueType#workflow} type as usually a list - * of user is notified when the status change. - * + * of user is notified when the status changes. + *

* This is defined when a task is delegated to someone in particular and the * type is {@link StatusValueType#task}. - * - * + *

* Note the author of the status is set in {@link#setId(Id)}. * * @return the user responsible for this task. @@ -320,7 +317,7 @@ public void setRelatedMetadataStatus(MetadataStatus relatedMetadataStatus) { } /** - * Get the uuid of the metadata.. + * Get the uuid of the metadata. * * @return the uuid of the metadata. */ @@ -339,7 +336,7 @@ public void setUuid(@Nonnull String uuid) { } /** - * Get the multilingual titles of the metadata as a json string.. + * Get the multilingual titles of the metadata as a json string. * * @return the multilingual titles of the metadata. */ diff --git a/domain/src/main/java/org/fao/geonet/domain/page/Page.java b/domain/src/main/java/org/fao/geonet/domain/page/Page.java index 12c8edadc5a..fb3247a4308 100644 --- a/domain/src/main/java/org/fao/geonet/domain/page/Page.java +++ b/domain/src/main/java/org/fao/geonet/domain/page/Page.java @@ -58,12 +58,13 @@ public class Page extends GeonetEntity implements Serializable { private PageStatus status; private String label; + private String icon; public Page() { } - public Page(PageIdentity pageIdentity, byte[] data, String link, PageFormat format, List sections, PageStatus status, String label) { + public Page(PageIdentity pageIdentity, byte[] data, String link, PageFormat format, List sections, PageStatus status, String label, String icon) { super(); this.pageIdentity = pageIdentity; this.data = data; @@ -72,6 +73,7 @@ public Page(PageIdentity pageIdentity, byte[] data, String link, PageFormat form this.sections = sections; this.status = status; this.label = label; + this.icon = icon; } public enum PageStatus { @@ -139,6 +141,11 @@ public String getLabel() { return label; } + @Column + public String getIcon() { + return icon; + } + public void setPageIdentity(PageIdentity pageIdentity) { this.pageIdentity = pageIdentity; } @@ -167,6 +174,10 @@ public void setLabel(String label) { this.label = label; } + public void setIcon(String icon) { + this.icon = icon; + } + @Override public String toString() { return String.format("Entity of type %s with id: %s", this.getClass().getName(), getPageIdentity().getLinkText()); diff --git a/es/README.md b/es/README.md index bd0bdaefefd..c27cee0b4b0 100644 --- a/es/README.md +++ b/es/README.md @@ -1,17 +1,31 @@ # Install, configure and start Elasticsearch +This section describes several methods for configuring Elasticsearch for development. + +These configurations should not be used for a production deployment. + ## Manual installation -1. Download Elasticsearch (at least 7.6.2 for Geonetwork 4.0.x) from https://www.elastic.co/downloads/elasticsearch -and copy to the ES module, e.g., es/elasticsearch-7.6.2 - -2. Start ES using: +1. Download Elasticsearch 8.x (tested with 8.11.3 for Geonetwork 4.4.x) from https://www.elastic.co/downloads/elasticsearch +and copy to the ES module, e.g., es/elasticsearch-8.11.3 + +2. Disable the security + +Elasticsearch 8 has security enabled by default. To disable this configuration for development, update the file `config/elasticsearch.yml` adding at the end: + +``` +xpack.security.enabled: false +xpack.security.enrollment.enabled: false +``` + + +3. Start ES using: ```shell script ./bin/elasticsearch ``` -3. Check that elasticsearch is running by visiting http://localhost:9200 in a browser +4. Check that elasticsearch is running by visiting http://localhost:9200 in a browser ## Maven installation @@ -35,13 +49,17 @@ and copy to the ES module, e.g., es/elasticsearch-7.6.2 1. Use docker pull to download the image (you can check version in the :file:`pom.xml` file): ``` - docker pull docker.elastic.co/elasticsearch/elasticsearch:7.6.2 + docker pull docker.elastic.co/elasticsearch/elasticsearch:8.11.3 ``` 2. Use docker run, leaving 9200 available: ``` - docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.6.2 + docker run -p 9200:9200 -p 9300:9300 \ + -e "discovery.type=single-node" \ + -e "xpack.security.enabled=false" \ + -e "xpack.security.enrollment.enabled=false" \ + docker.elastic.co/elasticsearch/elasticsearch:8.11.3 ``` 3. Check that elasticsearch is running by visiting http://localhost:9200 in a browser diff --git a/es/docker-compose.yml b/es/docker-compose.yml index 4c8f574bea2..cd255c6f531 100644 --- a/es/docker-compose.yml +++ b/es/docker-compose.yml @@ -2,23 +2,25 @@ version: '3' services: elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2 - container_name: elasticsearch + image: docker.elastic.co/elasticsearch/elasticsearch:8.11.3 + container_name: elasticsearch8 environment: - cluster.name=docker-cluster - bootstrap.memory_lock=true - discovery.type=single-node - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - xpack.security.enabled=false + - xpack.security.enrollment.enabled=false ulimits: memlock: soft: -1 hard: -1 volumes: - - ./es-dashboards/data:/usr/share/elasticsearch/data + - ./es-dashboards/data/index:/usr/share/elasticsearch/data ports: - "9200:9200" kibana: - image: docker.elastic.co/kibana/kibana:7.6.2 - container_name: kibana + image: docker.elastic.co/kibana/kibana:8.11.3 + container_name: kibana8 ports: - "5601:5601" diff --git a/es/es-dashboards/README.md b/es/es-dashboards/README.md index 592afa898a8..b95aa299cb4 100644 --- a/es/es-dashboards/README.md +++ b/es/es-dashboards/README.md @@ -9,8 +9,6 @@ Set Kibana base path and index name in config/kibana.yml: ``` server.basePath: "/geonetwork/dashboards" server.rewriteBasePath: false -kibana.index: ".dashboards" - ``` Adapt if needed ```elasticsearch.url``` and ```server.host```. diff --git a/es/es-dashboards/data/export.ndjson b/es/es-dashboards/data/export.ndjson index 307650e09e5..746884b3d50 100644 --- a/es/es-dashboards/data/export.ndjson +++ b/es/es-dashboards/data/export.ndjson @@ -1,10 +1,25 @@ -{"attributes":{"fieldFormatMap":"{\"uuid\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"},\"urlTemplate\":\"http://localhost:8080/geonetwork/srv/api/records/{{value}}\",\"labelTemplate\":\"{{value}}\"}},\"overview.url\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"},\"type\":\"img\",\"urlTemplate\":\"{{rawValue}}\",\"labelTemplate\":\"\",\"width\":\"100\"}},\"metadataLink\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://localhost:5601\",\"pathname\":\"/app/visualize\",\"basePath\":\"\"},\"urlTemplate\":\"https://www.geocat.ch/geonetwork/srv/fre/catalog.search#/metadata/{{rawValue}}\",\"labelTemplate\":\"More info\"}}}","fields":"[{\"name\":\"MD_ConstraintsUseLimitationObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.lang\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.langeng\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.langfra\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.langfre\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.langger\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_ConstraintsUseLimitationObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_ConstraintsUseLimitationObject.langita\"}}},{\"name\":\"MD_ConstraintsUseLimitationObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.default\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.default\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.default\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.lang\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langeng\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langfra\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langfre\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langger\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langita\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsOtherConstraintsObject.langroh\"}}},{\"name\":\"MD_LegalConstraintsOtherConstraintsObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"MD_LegalConstraintsUseLimitationObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.langeng\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.langfra\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.langfre\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.langger\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_LegalConstraintsUseLimitationObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_LegalConstraintsUseLimitationObject.langita\"}}},{\"name\":\"MD_LegalConstraintsUseLimitationObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_SecurityConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_SecurityConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"MD_SecurityConstraintsUseLimitationObject.default\"}}},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_SecurityConstraintsUseLimitationObject.langfre\"}}},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"MD_SecurityConstraintsUseLimitationObject.langger\"}}},{\"name\":\"MD_SecurityConstraintsUseLimitationObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"Org\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"OrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"_id\",\"type\":\"string\",\"esTypes\":[\"_id\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_index\",\"type\":\"string\",\"esTypes\":[\"_index\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"_score\",\"type\":\"number\",\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_source\",\"type\":\"_source\",\"esTypes\":[\"_source\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"_type\",\"type\":\"string\",\"esTypes\":[\"_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"accessConstraints\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"accessConstraints.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"accessConstraints\"}}},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.AtlasofSwitzerlandCategories.id\"}}},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.AtlasofSwitzerlandCategories.keywords.value\"}}},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.AtlasofSwitzerlandCategories.link\"}}},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.AtlasofSwitzerlandCategories.theme\"}}},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.AtlasofSwitzerlandCategories.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.AtlasofSwitzerlandCategories.title\"}}},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.id\"}}},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.keywords.value\"}}},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.link\"}}},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.theme\"}}},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET.title\"}}},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEMETINSPIREthemesversion10.id\"}}},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEMETINSPIREthemesversion10.keywords.value\"}}},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEMETINSPIREthemesversion10.link\"}}},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEMETINSPIREthemesversion10.theme\"}}},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEMETINSPIREthemesversion10.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEMETINSPIREthemesversion10.title\"}}},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.id\"}}},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.keywords.value\"}}},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.link\"}}},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.theme\"}}},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.GEneralMultilingualEnvironmentalThesaurus.title\"}}},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.id\"}}},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.keywords.value\"}}},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.link\"}}},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.theme\"}}},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung.title\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalnonegemet.id\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalnonegemet.keywords.value\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalnonegemet.link\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalnonegemet.theme\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalnonegemet.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalnonegemet.title\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalplaceregions.id\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalplaceregions.link\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalplaceregions.theme\"}}},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesaurusexternalplaceregions.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesaurusexternalplaceregions.title\"}}},{\"name\":\"allKeywords.gemet.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemet.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemet.id\"}}},{\"name\":\"allKeywords.gemet.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemet.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemet.keywords.value\"}}},{\"name\":\"allKeywords.gemet.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemet.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemet.link\"}}},{\"name\":\"allKeywords.gemet.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemet.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemet.theme\"}}},{\"name\":\"allKeywords.gemet.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemet.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemet.title\"}}},{\"name\":\"allKeywords.gemettheme.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemettheme.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemettheme.id\"}}},{\"name\":\"allKeywords.gemettheme.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemettheme.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemettheme.keywords.value\"}}},{\"name\":\"allKeywords.gemettheme.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemettheme.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemettheme.link\"}}},{\"name\":\"allKeywords.gemettheme.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemettheme.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemettheme.theme\"}}},{\"name\":\"allKeywords.gemettheme.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.gemettheme.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.gemettheme.title\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.id\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.link\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.theme\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDataset.title\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.id\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.keywords.value\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.link\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.theme\"}}},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope.title\"}}},{\"name\":\"allKeywords.inspireservicetaxonomy.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspireservicetaxonomy.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspireservicetaxonomy.id\"}}},{\"name\":\"allKeywords.inspireservicetaxonomy.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspireservicetaxonomy.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspireservicetaxonomy.keywords.value\"}}},{\"name\":\"allKeywords.inspireservicetaxonomy.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspireservicetaxonomy.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspireservicetaxonomy.link\"}}},{\"name\":\"allKeywords.inspireservicetaxonomy.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspireservicetaxonomy.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspireservicetaxonomy.theme\"}}},{\"name\":\"allKeywords.inspireservicetaxonomy.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspireservicetaxonomy.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspireservicetaxonomy.title\"}}},{\"name\":\"allKeywords.inspiretheme.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspiretheme.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspiretheme.id\"}}},{\"name\":\"allKeywords.inspiretheme.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspiretheme.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspiretheme.keywords.value\"}}},{\"name\":\"allKeywords.inspiretheme.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspiretheme.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspiretheme.link\"}}},{\"name\":\"allKeywords.inspiretheme.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspiretheme.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspiretheme.theme\"}}},{\"name\":\"allKeywords.inspiretheme.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.inspiretheme.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.inspiretheme.title\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.id\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.keywords.value\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.link\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.theme\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalnonegeocatch.title\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.id\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.id.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.id\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.keywords.value\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.link.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.link\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.theme\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.theme.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.theme\"}}},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.title\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.title.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.geonetworkthesauruslocalthemegeocatch.title\"}}},{\"name\":\"allKeywords.otherKeywords-.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.otherKeywords-.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.otherKeywords-.keywords.value\"}}},{\"name\":\"allKeywords.otherKeywords-theme.keywords.value\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"allKeywords.otherKeywords-theme.keywords.value.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"allKeywords.otherKeywords-theme.keywords.value\"}}},{\"name\":\"any\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"anytext\",\"type\":\"unknown\",\"esTypes\":[\"search_as_you_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"anytext._2gram\",\"type\":\"unknown\",\"esTypes\":[\"search_as_you_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"anytext\"}}},{\"name\":\"anytext._3gram\",\"type\":\"unknown\",\"esTypes\":[\"search_as_you_type\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"anytext\"}}},{\"name\":\"anytext._index_prefix\",\"type\":\"unknown\",\"esTypes\":[\"prefix\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"anytext\"}}},{\"name\":\"authorOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"authorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bbox_xmax\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bbox_xmin\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bbox_ymax\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"bbox_ymin\",\"type\":\"number\",\"esTypes\":[\"double\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"changeDate\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_DCP_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_accessConstraints_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_associationType_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_cellGeometry_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterEncoding_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_characterSet_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_classification_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_couplingType_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_dimensionName_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_function_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_geometricObjectType_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_hierarchyLevel_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_level_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_maintenanceAndUpdateFrequency_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_name_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_presentationForm_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_spatialRepresentationType_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langfra\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_status_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_topologyLevel_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_type_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_updateScope_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text_langeng\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text_langfre\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text_langger\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text_langita\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"cl_useConstraints_text_langroh\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"contact.address\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.address.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.address\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.email\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.email.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.email\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.individual\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.individual.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.individual\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.logo\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.logo.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.logo\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.org\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.organisation\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.organisation.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.organisation\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.phone\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.phone.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.phone\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.position\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.position.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.position\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.role\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.website\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contact.website.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contact.website\"},\"nested\":{\"path\":\"contact\"}}},{\"name\":\"contactForResource.address\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.address.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.address\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.email\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.email.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.email\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.individual\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.individual.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.individual\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.logo\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.logo.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.logo\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.org\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.organisation\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.organisation.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.organisation\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.phone\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.phone.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.phone\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.position\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.position.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.position\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.role\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.website\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"contactForResource.website.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"contactForResource.website\"},\"nested\":{\"path\":\"contactForResource\"}}},{\"name\":\"coordinateSystem\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"coordinateSystem.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"coordinateSystem\"}}},{\"name\":\"createDate\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"creationDateForResource\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"creationMonthForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"creationYearForResource\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"crsDetails.code\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"crsDetails.code.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"crsDetails.code\"}}},{\"name\":\"crsDetails.codeSpace\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"crsDetails.codeSpace.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"crsDetails.codeSpace\"}}},{\"name\":\"crsDetails.name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"crsDetails.name.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"crsDetails.name\"}}},{\"name\":\"crsDetails.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"crsDetails.url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"crsDetails.url\"}}},{\"name\":\"custodianOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"custodianOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"dateStamp\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"displayOrder\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"displayOrder.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"displayOrder\"}}},{\"name\":\"distributorOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"distributorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"docType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"document\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"documentStandard\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"draft\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"editorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extentDescriptionObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.default\"}}},{\"name\":\"extentDescriptionObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.default\"}}},{\"name\":\"extentDescriptionObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.default\"}}},{\"name\":\"extentDescriptionObject.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.lang\"}}},{\"name\":\"extentDescriptionObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langeng\"}}},{\"name\":\"extentDescriptionObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langfra\"}}},{\"name\":\"extentDescriptionObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langfre\"}}},{\"name\":\"extentDescriptionObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langger\"}}},{\"name\":\"extentDescriptionObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langita\"}}},{\"name\":\"extentDescriptionObject.langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extentDescriptionObject.langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extentDescriptionObject.langroh\"}}},{\"name\":\"extentDescriptionObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"extra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"extra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"extra\"}}},{\"name\":\"featureOfRecord\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"featureOfRecord.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"featureOfRecord\"}}},{\"name\":\"feedbackCount\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"feedbackCount.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"feedbackCount\"}}},{\"name\":\"format\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geoTag\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"geom\",\"type\":\"geo_shape\",\"esTypes\":[\"geo_shape\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"groupOwner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"groupPublished\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"harvesterId\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"harvesterUuid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hasBoundingPolygon\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hasInspireTheme\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hasOverview\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"hassource\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"hassource.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"hassource\"}}},{\"name\":\"hasxlinks\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"id\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"indexingDate\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"indexingError\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"indexingError.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"indexingError\"}}},{\"name\":\"indexingErrorMsg\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"indexingErrorMsg.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"indexingErrorMsg\"}}},{\"name\":\"inspireConformResource\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"inspireConformResource.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"inspireConformResource\"}}},{\"name\":\"isAboveThreshold\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isHarvested\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isOpenData\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isPublishedToAll\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isSchemaValid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isTemplate\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isValid\",\"type\":\"boolean\",\"esTypes\":[\"boolean\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"isValidInspire\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"lastRevisionDateForResource\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"lastRevisionMonthForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"lastRevisionYearForResource\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"lineage\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"link.description\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"link\"}}},{\"name\":\"link.link\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"link\"}}},{\"name\":\"link.name\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"link\"}}},{\"name\":\"link.protocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"link\"}}},{\"name\":\"link.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"link\"}}},{\"name\":\"link.url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"link.url\"},\"nested\":{\"path\":\"link\"}}},{\"name\":\"linkProtocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrl\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocol\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolAndere\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolCHTOPOspecialisedgeoportal\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolOGCWFShttpgetcapabilities\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolOGCWMS111httpgetmap\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolOGCWMShttpgetcapabilities\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolOGCWMShttpgetmap\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolOGCWMTShttpgetcapabilities\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWDOWNLOAD10ftpdownload\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWDOWNLOAD10httpdownload\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWDOWNLOADURL\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWLINK10httplink\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWLINK10httppartners\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWLINK10httprelated\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWLINK10httprss\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolWWWLINK10httpsamples\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocolnull\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"linkUrlProtocoltexthtml\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"location\",\"type\":\"geo_point\",\"esTypes\":[\"geo_point\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"logo\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"logo.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"logo\"}}},{\"name\":\"mainLanguage\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"mdStatus\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"metadataIdentifier\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"op0\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"op1\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"op3\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"op5\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"originatorOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"originatorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"otherConstraints\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"otherConstraints.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"otherConstraints\"}}},{\"name\":\"otherLanguage\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"otherLanguage.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"otherLanguage\"}}},{\"name\":\"otherLanguageId\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"otherLanguageId.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"otherLanguageId\"}}},{\"name\":\"overview.text.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.default\"}}},{\"name\":\"overview.text.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.lang\"}}},{\"name\":\"overview.text.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.langeng\"}}},{\"name\":\"overview.text.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.langfre\"}}},{\"name\":\"overview.text.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.langger\"}}},{\"name\":\"overview.text.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.langita\"}}},{\"name\":\"overview.text.langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.text.langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.text.langroh\"}}},{\"name\":\"overview.url\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overview.url.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overview.url\"}}},{\"name\":\"overviewUrl\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"overviewUrl.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"overviewUrl\"}}},{\"name\":\"owner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ownerOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"ownerOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"parentUuid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"partnerOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pointOfContactOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pointOfContactOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"pointOfTruthURL\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"popularity\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"principalInvestigatorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"processorOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"processorOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"publicationDateForResource\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"publicationMonthForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"publicationYearForResource\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"publisherOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"publisherOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"rating\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"record\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"record.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"record\"}}},{\"name\":\"recordGroup\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"recordJoin\",\"type\":\"unknown\",\"esTypes\":[\"join\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"recordJoin#record\",\"type\":\"unknown\",\"esTypes\":[\"parent\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"recordLink.associationType\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.associationType.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"recordLink.associationType\"},\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.created\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.initiativeType\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.initiativeType.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"recordLink.initiativeType\"},\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.origin\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.subtype\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.title\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.to\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.type\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordLink.url\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"nested\":{\"path\":\"recordLink\"}}},{\"name\":\"recordOperateOn\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"recordOwner\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resolutionDistance\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resolutionScaleDenominator\",\"type\":\"number\",\"esTypes\":[\"integer\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceAbstractObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.default\"}}},{\"name\":\"resourceAbstractObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.default\"}}},{\"name\":\"resourceAbstractObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.default\"}}},{\"name\":\"resourceAbstractObject.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.lang\"}}},{\"name\":\"resourceAbstractObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langeng\"}}},{\"name\":\"resourceAbstractObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langfra\"}}},{\"name\":\"resourceAbstractObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langfre\"}}},{\"name\":\"resourceAbstractObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langger\"}}},{\"name\":\"resourceAbstractObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langita\"}}},{\"name\":\"resourceAbstractObject.langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceAbstractObject.langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceAbstractObject.langroh\"}}},{\"name\":\"resourceAbstractObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceCreditObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceCreditObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceCreditObject.default\"}}},{\"name\":\"resourceCreditObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceCreditObject.default\"}}},{\"name\":\"resourceCreditObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceCreditObject.default\"}}},{\"name\":\"resourceCreditObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceCreditObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceCreditObject.langfre\"}}},{\"name\":\"resourceCreditObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceCreditObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceCreditObject.langger\"}}},{\"name\":\"resourceCreditObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceIdentifier\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceLanguage\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceProviderOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceProviderOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceTemporalDateRange\",\"type\":\"unknown\",\"esTypes\":[\"date_range\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceTemporalExtentDateRange\",\"type\":\"unknown\",\"esTypes\":[\"date_range\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceTitleObject.default\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.default.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.default\"}}},{\"name\":\"resourceTitleObject.default.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.default\"}}},{\"name\":\"resourceTitleObject.default.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.default\"}}},{\"name\":\"resourceTitleObject.lang\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.lang.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.lang\"}}},{\"name\":\"resourceTitleObject.langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langeng\"}}},{\"name\":\"resourceTitleObject.langfra\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langfra.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langfra\"}}},{\"name\":\"resourceTitleObject.langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langfre\"}}},{\"name\":\"resourceTitleObject.langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langger\"}}},{\"name\":\"resourceTitleObject.langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langita\"}}},{\"name\":\"resourceTitleObject.langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"resourceTitleObject.langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"resourceTitleObject.langroh\"}}},{\"name\":\"resourceTitleObject.link\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"resourceType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"revisionDateForResource\",\"type\":\"date\",\"esTypes\":[\"date\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"revisionMonthForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"revisionYearForResource\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"root\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"schema\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"scope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"serviceType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"shape\",\"type\":\"geo_shape\",\"esTypes\":[\"geo_shape\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false},{\"name\":\"shapeParsingError\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"shapeParsingError.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"shapeParsingError\"}}},{\"name\":\"sourceCatalogue\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"spatialRepresentationType\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"specialistAuthorityOrg\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"specialistAuthorityOrgForResource\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"standardName\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tag\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"tag.completion\",\"type\":\"unknown\",\"esTypes\":[\"completion\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"tag\"}}},{\"name\":\"tag.reverse\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"tag\"}}},{\"name\":\"tag.trigram\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false,\"subType\":{\"multi\":{\"parent\":\"tag\"}}},{\"name\":\"tagNumber\",\"type\":\"number\",\"esTypes\":[\"short\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"text_\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"text_.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"text_\"}}},{\"name\":\"th_AtlasofSwitzerlandCategories\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_AtlasofSwitzerlandCategoriesNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMET\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GDIBWSchlsselwortlistenachdemmehrsprachigenUmweltThesaurusGEMETNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GEMETINSPIREthemesversion10\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GEMETINSPIREthemesversion10Number\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GEneralMultilingualEnvironmentalThesaurus\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_GEneralMultilingualEnvironmentalThesaurusNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnung\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_SchlsselwortlistezurEinteilungderGeodatendienstenachAnhangTeilDNr5derINSPIREMetadatenverordnungNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesaurusexternalnonegemet\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesaurusexternalnonegemetNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesaurusexternalplaceregionsNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_gemet\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_gemetNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_gemettheme\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_gemetthemeNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_httpinspireeceuropaeumetadatacodelistPriorityDatasetPriorityDatasetNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScope\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_httpinspireeceuropaeumetadatacodelistSpatialScopeSpatialScopeNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_inspireservicetaxonomy\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_inspireservicetaxonomyNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_inspiretheme\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_inspirethemeNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesauruslocalnonegeocatch\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesauruslocalnonegeocatchNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesauruslocalthemegeocatch\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"th_geonetworkthesauruslocalthemegeocatchNumber\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"topic\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"topic_text\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"topic_text_langeng\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"topic_text_langeng.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"topic_text_langeng\"}}},{\"name\":\"topic_text_langfre\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"topic_text_langfre.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"topic_text_langfre\"}}},{\"name\":\"topic_text_langger\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"topic_text_langger.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"topic_text_langger\"}}},{\"name\":\"topic_text_langita\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"topic_text_langita.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"topic_text_langita\"}}},{\"name\":\"topic_text_langroh\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"topic_text_langroh.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"topic_text_langroh\"}}},{\"name\":\"useLimitation\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"useLimitation.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"useLimitation\"}}},{\"name\":\"userinfo\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"userinfo.keyword\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true,\"subType\":{\"multi\":{\"parent\":\"userinfo\"}}},{\"name\":\"uuid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"valid\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"validReport\",\"type\":\"string\",\"esTypes\":[\"text\"],\"count\":0,\"scripted\":false,\"searchable\":false,\"aggregatable\":false,\"readFromDocValues\":false},{\"name\":\"xlink\",\"type\":\"string\",\"esTypes\":[\"keyword\"],\"count\":0,\"scripted\":false,\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":true},{\"name\":\"metadataLink\",\"type\":\"string\",\"count\":1,\"scripted\":true,\"script\":\"doc['uuid'].value\",\"lang\":\"painless\",\"searchable\":true,\"aggregatable\":true,\"readFromDocValues\":false}]","sourceFilters":"[{\"value\":\"document\"},{\"value\":\"allKeyword*\"},{\"value\":\"th_*\"},{\"value\":\"shape\"}]","timeFieldName":"dateStamp","title":"gn-records*"},"coreMigrationVersion":"7.15.1","id":"gn-records","migrationVersion":{"index-pattern":"7.11.0"},"references":[],"type":"index-pattern","updated_at":"2021-11-08T09:41:21.432Z","version":"WzI1LDFd"} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Type of datasets","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Type of datasets\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":15,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Spatial representation type\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":true,\"valueAxis\":\"ValueAxis-1\"},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":true},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"radiusRatio\":50,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"truncateLegend\":true,\"maxLegendLines\":1}}"},"coreMigrationVersion":"7.15.1","id":"3bd8fda0-edc8-11ea-8507-4172ebbe444b","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:18:30.787Z","version":"WzE0MSwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Main data types and formats","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Main data types and formats\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":3,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Spatial type\"},\"schema\":\"split\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"format\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Format\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":true,\"valueAxis\":\"ValueAxis-1\"},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":true},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"row\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"radiusRatio\":0,\"truncateLegend\":true,\"maxLegendLines\":1}}"},"coreMigrationVersion":"7.15.1","id":"2701af60-edca-11ea-8507-4172ebbe444b","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:19:16.925Z","version":"WzE1MCwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Licenses","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Licenses\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"MD_LegalConstraintsUseLimitationObject.default.keyword\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Licenses\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"dimensions\":{\"metrics\":[{\"accessor\":1,\"format\":{\"id\":\"number\"},\"params\":{},\"label\":\"Count\",\"aggType\":\"count\"}],\"buckets\":[{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"otherBucketLabel\":\"Other\",\"missingBucketLabel\":\"Missing\",\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"}}},\"params\":{},\"label\":\"Licenses\",\"aggType\":\"terms\"}]},\"showToolbar\":true}}"},"coreMigrationVersion":"7.15.1","id":"1dd1b5a0-8dc4-11e9-9bb7-5db216293bad","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:20:07.559Z","version":"WzE4NSwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Access constraints","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Access constraints\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_accessConstraints.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Access constraints\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"dimensions\":{\"metrics\":[{\"accessor\":1,\"format\":{\"id\":\"number\"},\"params\":{},\"label\":\"Count\",\"aggType\":\"count\"}],\"buckets\":[{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"otherBucketLabel\":\"Other\",\"missingBucketLabel\":\"Missing\",\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"}}},\"params\":{},\"label\":\"Access constraints\",\"aggType\":\"terms\"}]},\"showToolbar\":true}}"},"coreMigrationVersion":"7.15.1","id":"a5d404c0-8dc5-11e9-9bb7-5db216293bad","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:20:24.001Z","version":"WzE5MSwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Maintenance and update frequency info","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Maintenance and update frequency info\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_maintenanceAndUpdateFrequency.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Maintenance and update frequency\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":20,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"Number of records\",\"showToolbar\":true}}"},"coreMigrationVersion":"7.15.1","id":"5bc57fd0-edcd-11ea-8507-4172ebbe444b","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:20:37.795Z","version":"WzE5OCwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Main keywords cloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Main keywords cloud\",\"type\":\"tagcloud\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"tag.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":45,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Main keywords\"},\"schema\":\"segment\"}],\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}}}"},"coreMigrationVersion":"7.15.1","id":"f9825c40-8dc3-11e9-9bb7-5db216293bad","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:21:09.963Z","version":"WzIyMSwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Resource creation date by topics","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Resource creation date by topics\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"creationDateForResource\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"y\",\"used_interval\":\"1y\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Creation\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_topic.default\",\"orderBy\":\"_key\",\"order\":\"asc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"group\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"log\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"radiusRatio\":0,\"truncateLegend\":true,\"maxLegendLines\":1}}"},"coreMigrationVersion":"7.15.1","id":"05d4bbf0-eddb-11ea-8507-4172ebbe444b","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:22:04.958Z","version":"WzI1OSwxXQ=="} -{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Metadata other languages","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Metadata other languages\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"otherLanguageId.keyword\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Record other language code\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"Count\",\"showToolbar\":true}}"},"coreMigrationVersion":"7.15.1","id":"b6b35080-ede5-11ea-8507-4172ebbe444b","migrationVersion":{"visualization":"7.14.0"},"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"type":"visualization","updated_at":"2021-11-08T10:23:19.746Z","version":"WzMwOSwxXQ=="} -{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":9,"missingRefCount":0,"missingReferences":[]} +{"attributes":{"fieldAttrs":"{\"mainLanguage\":{\"count\":1},\"resourceLanguage\":{\"count\":1},\"cl_resourceScope.default\":{\"count\":2},\"isAboveThreshold\":{\"count\":1},\"isSchemaValid\":{\"count\":1},\"isValid\":{\"count\":1},\"resourceTitle\":{\"count\":1},\"resourceTitleObject.default\":{\"customLabel\":\"Title\",\"count\":5},\"resourceType\":{\"count\":1},\"scope\":{\"count\":1},\"valid\":{\"count\":1},\"valid_xsd\":{\"count\":1},\"uuid\":{\"count\":4},\"overview.url\":{\"count\":1}}","fieldFormatMap":"{\"isTemplate\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"\",\"labelTemplate\":\"\"}},\"hasOverview\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"hasInspireTheme\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"isSchemaValid\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"isAboveThreshold\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"hasxlinks\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"isValid\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"isHarvested\":{\"id\":\"url\",\"params\":{\"type\":\"img\",\"urlTemplate\":\"../../assets/img/{{value}}.png\"}},\"overviewUrl\":{\"id\":\"url\",\"params\":{\"urlTemplate\":\"{{rawValue}}\",\"type\":\"img\"}},\"linkUrl\":{\"id\":\"url\",\"params\":{\"urlTemplate\":\"{{rawValue}}\"}},\"recordLink\":{\"id\":\"url\",\"params\":{\"urlTemplate\":\"{{rawValue}}\",\"labelTemplate\":\"Link to record\"}},\"uuid\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/geonetwork/dashboards/app/management/kibana/spaces/\",\"basePath\":\"/geonetwork/dashboards\"},\"urlTemplate\":\"http://localhost:8080/geonetwork/srv/eng/catalog.search?debug#/metadata/{{value}}\",\"labelTemplate\":\"{{value}}\",\"type\":\"a\",\"width\":null,\"height\":null}},\"overview.url\":{\"id\":\"url\",\"params\":{\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/geonetwork/dashboards/app/management/kibana/dataViews/dataView/gn-records\",\"basePath\":\"/geonetwork/dashboards\"},\"type\":\"img\",\"urlTemplate\":\"{{rawValue}}\",\"width\":\"100\",\"height\":\"100\",\"labelTemplate\":null}}}","fields":"[]","name":"gn-records","runtimeFieldMap":"{}","sourceFilters":"[{\"value\":\"document\"}]","title":"gn-records","typeMeta":"{}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"gn-records","managed":false,"references":[],"sort":[1704790395604,8589934670],"type":"index-pattern","typeMigrationVersion":"7.11.0","updated_at":"2024-01-09T08:53:15.604Z","version":"WzgwLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Resource creation date by topics","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Resource creation date by topics\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"date_histogram\",\"params\":{\"field\":\"creationDateForResource\",\"timeRange\":{\"from\":\"now-15y\",\"to\":\"now\"},\"useNormalizedEsInterval\":true,\"scaleMetricValues\":false,\"interval\":\"y\",\"used_interval\":\"1y\",\"drop_partials\":false,\"min_doc_count\":1,\"extended_bounds\":{},\"customLabel\":\"Creation\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_topic.default\",\"orderBy\":\"_key\",\"order\":\"asc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"group\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"log\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"radiusRatio\":0,\"truncateLegend\":true,\"maxLegendLines\":1,\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"05d4bbf0-eddb-11ea-8507-4172ebbe444b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934647],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzEyLDFd"} +{"attributes":{"columns":["resourceTitleObject.default","valid_xsd","valid","resourceType"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"highlightAll\":true,\"query\":{\"query\":\"-draft: \\\"y\\\" +isTemplate: \\\"n\\\"\",\"language\":\"lucene\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[["_score","desc"]],"title":"Records / All (Validity)","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704789510919,8589934597],"type":"search","typeMigrationVersion":"7.9.3","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQwLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"draft\",\"negate\":true,\"params\":{\"query\":\"y\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"isTemplate\",\"negate\":false,\"params\":{\"query\":\"n\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}}}]}"},"savedSearchRefName":"search_0","title":"Records projection by spatial type","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Records projection by spatial type\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"coordinateSystem.keyword\",\"orderBy\":\"_key\",\"order\":\"asc\",\"size\":100,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Projection\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Representation type\"},\"schema\":\"group\"}],\"params\":{\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"scale\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"type\":\"histogram\",\"grid\":{\"categoryLines\":false},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"filter\":true,\"truncate\":100},\"title\":{},\"style\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"},\"style\":{}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"stacked\",\"data\":{\"label\":\"Records\",\"id\":\"1\"},\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true,\"circlesRadius\":3}],\"radiusRatio\":0,\"detailedTooltip\":true,\"palette\":{\"type\":\"palette\",\"name\":\"default\"},\"truncateLegend\":true,\"maxLegendLines\":1,\"labels\":{\"show\":false},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"1233d430-2c28-11e7-bda0-15897525b97b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934611],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQ0LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Licenses","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Licenses\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"MD_LegalConstraintsUseLimitationObject.default.keyword\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Licenses\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"dimensions\":{\"metrics\":[{\"accessor\":1,\"format\":{\"id\":\"number\"},\"params\":{},\"label\":\"Count\",\"aggType\":\"count\"}],\"buckets\":[{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"otherBucketLabel\":\"Other\",\"missingBucketLabel\":\"Missing\",\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"}}},\"params\":{},\"label\":\"Licenses\",\"aggType\":\"terms\"}]},\"showToolbar\":true}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"1dd1b5a0-8dc4-11e9-9bb7-5db216293bad","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934639],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzgsMV0="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Main data types and formats","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Main data types and formats\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":3,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Spatial type\"},\"schema\":\"split\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"format\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Format\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":true,\"valueAxis\":\"ValueAxis-1\"},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":true},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"row\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"radiusRatio\":0,\"truncateLegend\":true,\"maxLegendLines\":1,\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"2701af60-edca-11ea-8507-4172ebbe444b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934637],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzcsMV0="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[]}"},"savedSearchRefName":"search_0","title":"Records number","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Records number\",\"type\":\"metric\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Records\"},\"schema\":\"metric\"}],\"params\":{\"handleNoResults\":true,\"fontSize\":60,\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"31c88fe0-2c21-11e7-a86a-2d31307889bf","managed":false,"references":[{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934599],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQxLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Type of datasets","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Type of datasets\",\"type\":\"histogram\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":15,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Spatial representation type\"},\"schema\":\"segment\"}],\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":true,\"valueAxis\":\"ValueAxis-1\"},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Number of records\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Number of records\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"lineWidth\":2,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"top\",\"times\":[],\"addTimeMarker\":false,\"labels\":{\"show\":true},\"thresholdLine\":{\"show\":false,\"value\":10,\"width\":1,\"style\":\"full\",\"color\":\"#E7664C\"},\"radiusRatio\":50,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"isVislibVis\":true,\"detailedTooltip\":true,\"truncateLegend\":true,\"maxLegendLines\":1,\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"3bd8fda0-edc8-11ea-8507-4172ebbe444b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934635],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzYsMV0="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Maintenance and update frequency info","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Maintenance and update frequency info\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{\"customLabel\":\"Number of records\"},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_maintenanceAndUpdateFrequency.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":25,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Maintenance and update frequency\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":20,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"Number of records\",\"showToolbar\":true}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"5bc57fd0-edcd-11ea-8507-4172ebbe444b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934643],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzEwLDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"formBased":{"layers":{"f1b15e44-b346-4b48-8d1b-0092c8af975e":{"columnOrder":["9e336358-89cb-4cd6-b57f-6e3af23841d1","3d936a54-1e5d-4073-8d82-76aceb0e558c"],"columns":{"3d936a54-1e5d-4073-8d82-76aceb0e558c":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"___records___"},"9e336358-89cb-4cd6-b57f-6e3af23841d1":{"dataType":"string","isBucketed":true,"label":"Filters","operationType":"filters","params":{"filters":[{"input":{"language":"kuery","query":"valid :\"1\" "},"label":"Valid"},{"input":{"language":"kuery","query":"valid:\"0\" "},"label":"Invalid"},{"input":{"language":"kuery","query":"valid: \"-1\" "},"label":"Not validated"}]},"scale":"ordinal"}},"incompleteColumns":{}}}}},"filters":[{"$state":{"store":"appState"},"meta":{"alias":null,"disabled":false,"index":"filter-index-pattern-0","key":"draft","negate":true,"params":{"query":"y"},"type":"phrase"},"query":{"match_phrase":{"draft":"y"}}},{"$state":{"store":"appState"},"meta":{"alias":null,"disabled":false,"index":"filter-index-pattern-1","key":"isTemplate","negate":false,"params":{"query":"n"},"type":"phrase"},"query":{"match_phrase":{"isTemplate":"n"}}}],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","layerId":"f1b15e44-b346-4b48-8d1b-0092c8af975e","layerType":"data","legendDisplay":"default","legendSize":"auto","metrics":["3d936a54-1e5d-4073-8d82-76aceb0e558c"],"nestedLegend":false,"numberDisplay":"percent","primaryGroups":["9e336358-89cb-4cd6-b57f-6e3af23841d1"]}],"shape":"pie"}},"title":"Records (validation)","visualizationType":"lnsPie"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"6f118a50-4100-11ee-9f42-3704c671ff67","managed":false,"references":[{"id":"gn-records","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"gn-records","name":"indexpattern-datasource-layer-f1b15e44-b346-4b48-8d1b-0092c8af975e","type":"index-pattern"},{"id":"gn-records","name":"filter-index-pattern-0","type":"index-pattern"},{"id":"gn-records","name":"filter-index-pattern-1","type":"index-pattern"}],"sort":[1704789510919,8589934633],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzUwLDFd"} +{"attributes":{"columns":["uuid","resourceTitleObject.default"],"description":"","hits":0,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"draft\",\"params\":{\"query\":\"y\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"isTemplate\",\"params\":{\"query\":\"n\"},\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}},\"$state\":{\"store\":\"appState\"}}],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[],"title":"Records (working copy)","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"7264b880-40f6-11ee-9f42-3704c671ff67","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"}],"sort":[1704789510919,8589934619],"type":"search","typeMigrationVersion":"7.9.3","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQ2LDFd"} +{"attributes":{"columns":["resourceTitleObject.default","uuid","overview.url"],"description":"","grid":{},"hideChart":false,"isTextBasedQuery":false,"kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"sort":[],"timeRestore":false,"title":"Records","usesAdHocDataView":false},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:45:33.426Z","id":"737d7520-aecb-11ee-8d56-55a140a7c65f","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704790474646,8589934669],"type":"search","typeMigrationVersion":"8.0.0","updated_at":"2024-01-09T08:54:34.646Z","version":"Wzg1LDFd"} +{"attributes":{"buildNum":68312,"defaultIndex":"gn-records","isDefaultIndexMigrated":true},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:26:11.510Z","id":"8.11.3","managed":false,"references":[],"sort":[1704788873866,16],"type":"config","typeMigrationVersion":"8.9.0","updated_at":"2024-01-09T08:27:53.866Z","version":"WzU1LDFd"} +{"attributes":{"buildNum":68312,"isDefaultIndexMigrated":true},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:26:11.511Z","id":"8.11.3","managed":false,"references":[],"sort":[1704788771511,20],"type":"config-global","updated_at":"2024-01-09T08:26:11.511Z","version":"WzM1LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"draft\",\"negate\":true,\"params\":{\"query\":\"y\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"isTemplate\",\"negate\":false,\"params\":{\"query\":\"n\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}}}]}"},"savedSearchRefName":"search_0","title":"Records by scope","uiStateJSON":"{\"vis\":{\"legendOpen\":true}}","version":1,"visState":"{\"title\":\"Records by scope\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"resourceType\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":55,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\"},\"schema\":\"segment\"}],\"params\":{\"addTooltip\":true,\"legendPosition\":\"right\",\"isDonut\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true,\"type\":\"pie\",\"nestedLegend\":false,\"truncateLegend\":true,\"maxLegendLines\":1,\"labels\":{\"show\":true,\"last_level\":false,\"values\":true,\"valuesFormat\":\"percent\",\"percentDecimals\":2,\"truncate\":100,\"position\":\"default\"},\"legendDisplay\":\"show\",\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"ab466d90-2c1e-11e7-a86a-2d31307889bf","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934628],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQ5LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"*\",\"language\":\"lucene\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":12,\"i\":\"4\"},\"panelIndex\":\"4\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4\"},{\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":0,\"w\":24,\"h\":12,\"i\":\"13406337-9365-4178-b96e-c2820ce8a779\"},\"panelIndex\":\"13406337-9365-4178-b96e-c2820ce8a779\",\"embeddableConfig\":{\"attributes\":{\"title\":\"\",\"visualizationType\":\"lnsPie\",\"type\":\"lens\",\"references\":[{\"id\":\"6f118a50-4100-11ee-9f42-3704c671ff67\",\"name\":\"panel_13406337-9365-4178-b96e-c2820ce8a779\",\"type\":\"lens\"},{\"id\":\"gn-records\",\"name\":\"indexpattern-datasource-current-indexpattern\",\"type\":\"index-pattern\"},{\"id\":\"gn-records\",\"name\":\"indexpattern-datasource-layer-f1b15e44-b346-4b48-8d1b-0092c8af975e\",\"type\":\"index-pattern\"},{\"id\":\"gn-records\",\"name\":\"filter-index-pattern-0\",\"type\":\"index-pattern\"},{\"id\":\"gn-records\",\"name\":\"filter-index-pattern-1\",\"type\":\"index-pattern\"}],\"state\":{\"visualization\":{\"shape\":\"pie\",\"layers\":[{\"layerId\":\"f1b15e44-b346-4b48-8d1b-0092c8af975e\",\"numberDisplay\":\"percent\",\"categoryDisplay\":\"default\",\"legendDisplay\":\"default\",\"nestedLegend\":false,\"layerType\":\"data\",\"legendSize\":\"auto\",\"primaryGroups\":[\"9e336358-89cb-4cd6-b57f-6e3af23841d1\"],\"metrics\":[\"3d936a54-1e5d-4073-8d82-76aceb0e558c\"]}]},\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filters\":[{\"meta\":{\"alias\":null,\"negate\":true,\"disabled\":false,\"type\":\"phrase\",\"key\":\"draft\",\"params\":{\"query\":\"y\"},\"index\":\"filter-index-pattern-0\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}},\"$state\":{\"store\":\"appState\"}},{\"meta\":{\"alias\":null,\"negate\":false,\"disabled\":false,\"type\":\"phrase\",\"key\":\"isTemplate\",\"params\":{\"query\":\"n\"},\"index\":\"filter-index-pattern-1\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}},\"$state\":{\"store\":\"appState\"}}],\"datasourceStates\":{\"formBased\":{\"layers\":{\"f1b15e44-b346-4b48-8d1b-0092c8af975e\":{\"columns\":{\"9e336358-89cb-4cd6-b57f-6e3af23841d1\":{\"label\":\"Filters\",\"dataType\":\"string\",\"operationType\":\"filters\",\"scale\":\"ordinal\",\"isBucketed\":true,\"params\":{\"filters\":[{\"input\":{\"query\":\"isValid :true \",\"language\":\"kuery\"},\"label\":\"Valid\"},{\"input\":{\"query\":\"isValid :false \",\"language\":\"kuery\"},\"label\":\"Invalid\"}]}},\"3d936a54-1e5d-4073-8d82-76aceb0e558c\":{\"label\":\"Count of records\",\"dataType\":\"number\",\"operationType\":\"count\",\"isBucketed\":false,\"scale\":\"ratio\",\"sourceField\":\"___records___\"}},\"columnOrder\":[\"9e336358-89cb-4cd6-b57f-6e3af23841d1\",\"3d936a54-1e5d-4073-8d82-76aceb0e558c\"],\"incompleteColumns\":{}}}}}}},\"enhancements\":{}},\"panelRefName\":\"panel_13406337-9365-4178-b96e-c2820ce8a779\"},{\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":12,\"w\":48,\"h\":14,\"i\":\"45d4ac28-b22e-400b-bf83-07cf7d9b2670\"},\"panelIndex\":\"45d4ac28-b22e-400b-bf83-07cf7d9b2670\",\"embeddableConfig\":{\"enhancements\":{},\"sort\":[]},\"panelRefName\":\"panel_45d4ac28-b22e-400b-bf83-07cf7d9b2670\"}]","timeRestore":false,"title":"Catalogue validation reports","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:55:43.500Z","id":"915983d0-2c2e-11e7-a889-7bfa00c573d3","managed":false,"references":[{"id":"ab466d90-2c1e-11e7-a86a-2d31307889bf","name":"4:panel_4","type":"visualization"},{"id":"6f118a50-4100-11ee-9f42-3704c671ff67","name":"13406337-9365-4178-b96e-c2820ce8a779:panel_13406337-9365-4178-b96e-c2820ce8a779","type":"lens"},{"id":"6f118a50-4100-11ee-9f42-3704c671ff67","name":"13406337-9365-4178-b96e-c2820ce8a779:panel_13406337-9365-4178-b96e-c2820ce8a779","type":"lens"},{"id":"gn-records","name":"13406337-9365-4178-b96e-c2820ce8a779:indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"gn-records","name":"13406337-9365-4178-b96e-c2820ce8a779:indexpattern-datasource-layer-f1b15e44-b346-4b48-8d1b-0092c8af975e","type":"index-pattern"},{"id":"gn-records","name":"13406337-9365-4178-b96e-c2820ce8a779:filter-index-pattern-0","type":"index-pattern"},{"id":"gn-records","name":"13406337-9365-4178-b96e-c2820ce8a779:filter-index-pattern-1","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"45d4ac28-b22e-400b-bf83-07cf7d9b2670:panel_45d4ac28-b22e-400b-bf83-07cf7d9b2670","type":"search"}],"sort":[1704790543500,8589934658],"type":"dashboard","typeMigrationVersion":"8.9.0","updated_at":"2024-01-09T08:55:43.500Z","version":"WzkxLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Access constraints","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Access constraints\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_accessConstraints.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Access constraints\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"\",\"dimensions\":{\"metrics\":[{\"accessor\":1,\"format\":{\"id\":\"number\"},\"params\":{},\"label\":\"Count\",\"aggType\":\"count\"}],\"buckets\":[{\"accessor\":0,\"format\":{\"id\":\"terms\",\"params\":{\"id\":\"string\",\"otherBucketLabel\":\"Other\",\"missingBucketLabel\":\"Missing\",\"parsedUrl\":{\"origin\":\"http://localhost:8080\",\"pathname\":\"/kibana/s/catalog/app/kibana\",\"basePath\":\"/kibana/s/catalog\"}}},\"params\":{},\"label\":\"Access constraints\",\"aggType\":\"terms\"}]},\"showToolbar\":true}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"a5d404c0-8dc5-11e9-9bb7-5db216293bad","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934641],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzksMV0="} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Metadata other languages","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Metadata other languages\",\"type\":\"table\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"otherLanguageId.keyword\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":10,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Record other language code\"},\"schema\":\"bucket\"}],\"params\":{\"perPage\":10,\"showPartialRows\":false,\"showMetricsAtAllLevels\":false,\"sort\":{\"columnIndex\":null,\"direction\":null},\"showTotal\":false,\"totalFunc\":\"sum\",\"percentageCol\":\"Count\",\"showToolbar\":true}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"b6b35080-ede5-11ea-8507-4172ebbe444b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934649],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzEzLDFd"} +{"attributes":{"description":"","state":{"datasourceStates":{"formBased":{"layers":{"9e052f6c-5b16-43d5-84f8-c9c06b6d46cd":{"columnOrder":["259b8c09-0e27-46d9-bd6b-e7cb428f6f5f","cab056c2-d6c9-4b4e-9da2-775fbb7a79a2"],"columns":{"259b8c09-0e27-46d9-bd6b-e7cb428f6f5f":{"dataType":"string","isBucketed":true,"label":"Filters","operationType":"filters","params":{"filters":[{"input":{"language":"kuery","query":"isPublishedToAll :\"true\" "},"label":"Public"},{"input":{"language":"kuery","query":"isPublishedToAll : \"false\" "},"label":"Non-public"}]},"scale":"ordinal"},"cab056c2-d6c9-4b4e-9da2-775fbb7a79a2":{"dataType":"number","isBucketed":false,"label":"Count of records","operationType":"count","scale":"ratio","sourceField":"___records___"}},"incompleteColumns":{}}}}},"filters":[{"$state":{"store":"appState"},"meta":{"alias":null,"disabled":false,"index":"filter-index-pattern-0","key":"draft","negate":true,"params":{"query":"y"},"type":"phrase"},"query":{"match_phrase":{"draft":"y"}}},{"$state":{"store":"appState"},"meta":{"alias":null,"disabled":false,"index":"filter-index-pattern-1","key":"isTemplate","negate":false,"params":{"query":"n"},"type":"phrase"},"query":{"match_phrase":{"isTemplate":"n"}}}],"query":{"language":"kuery","query":""},"visualization":{"layers":[{"categoryDisplay":"default","layerId":"9e052f6c-5b16-43d5-84f8-c9c06b6d46cd","layerType":"data","legendDisplay":"default","legendSize":"auto","metrics":["cab056c2-d6c9-4b4e-9da2-775fbb7a79a2"],"nestedLegend":false,"numberDisplay":"percent","primaryGroups":["259b8c09-0e27-46d9-bd6b-e7cb428f6f5f"]}],"shape":"donut"}},"title":"Records publication status","visualizationType":"lnsPie"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"c352ec50-40f9-11ee-9f42-3704c671ff67","managed":false,"references":[{"id":"gn-records","name":"indexpattern-datasource-current-indexpattern","type":"index-pattern"},{"id":"gn-records","name":"indexpattern-datasource-layer-9e052f6c-5b16-43d5-84f8-c9c06b6d46cd","type":"index-pattern"},{"id":"gn-records","name":"filter-index-pattern-0","type":"index-pattern"},{"id":"gn-records","name":"filter-index-pattern-1","type":"index-pattern"}],"sort":[1704789510919,8589934624],"type":"lens","typeMigrationVersion":"8.9.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQ3LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"draft\",\"negate\":true,\"params\":{\"query\":\"y\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"isTemplate\",\"negate\":false,\"params\":{\"query\":\"n\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}}}]}"},"savedSearchRefName":"search_0","title":"Records resource types","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Records resource types\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"resourceType\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":15,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Resource type\"},\"schema\":\"segment\"}],\"params\":{\"addTooltip\":true,\"legendPosition\":\"right\",\"isDonut\":false,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true,\"type\":\"pie\",\"nestedLegend\":false,\"truncateLegend\":true,\"maxLegendLines\":1,\"labels\":{\"show\":true,\"last_level\":false,\"values\":true,\"valuesFormat\":\"percent\",\"percentDecimals\":2,\"truncate\":100,\"position\":\"default\"},\"legendDisplay\":\"show\",\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"ecb17d40-2c20-11e7-a86a-2d31307889bf","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934603],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQyLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"draft\",\"negate\":true,\"params\":{\"query\":\"y\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"isTemplate\",\"negate\":false,\"params\":{\"query\":\"n\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}}}]}"},"savedSearchRefName":"search_0","title":"Records by types and spatial types","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Records by types and spatial types\",\"type\":\"pie\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_hierarchyLevel.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Resource type\"},\"schema\":\"segment\"},{\"id\":\"3\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"cl_spatialRepresentationType.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":5,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Spatial type\"},\"schema\":\"segment\"}],\"params\":{\"addTooltip\":true,\"legendPosition\":\"right\",\"isDonut\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"},\"distinctColors\":true,\"type\":\"pie\",\"nestedLegend\":false,\"truncateLegend\":true,\"maxLegendLines\":1,\"labels\":{\"show\":true,\"last_level\":false,\"values\":true,\"valuesFormat\":\"percent\",\"percentDecimals\":2,\"truncate\":100,\"position\":\"default\"},\"legendDisplay\":\"show\",\"legendSize\":\"auto\"}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"d4771f20-2c28-11e7-bda0-15897525b97b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934607],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQzLDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"language\":\"kuery\",\"query\":\"\"},\"filter\":[{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"draft\",\"negate\":true,\"params\":{\"query\":\"y\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\"},\"query\":{\"match_phrase\":{\"draft\":\"y\"}}},{\"$state\":{\"store\":\"appState\"},\"meta\":{\"alias\":null,\"disabled\":false,\"key\":\"isTemplate\",\"negate\":false,\"params\":{\"query\":\"n\"},\"type\":\"phrase\",\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index\"},\"query\":{\"match_phrase\":{\"isTemplate\":\"n\"}}}]}"},"savedSearchRefName":"search_0","title":"Records by service types","uiStateJSON":"{}","version":1,"visState":"{\"aggs\":[{\"enabled\":true,\"id\":\"1\",\"params\":{},\"schema\":\"metric\",\"type\":\"count\"},{\"enabled\":true,\"id\":\"2\",\"params\":{\"customLabel\":\"Service type\",\"field\":\"serviceType\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"order\":\"desc\",\"orderBy\":\"1\",\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"size\":15},\"schema\":\"segment\",\"type\":\"terms\"}],\"params\":{\"addTooltip\":true,\"distinctColors\":true,\"isDonut\":true,\"labels\":{\"last_level\":false,\"percentDecimals\":2,\"position\":\"default\",\"show\":true,\"truncate\":100,\"values\":true,\"valuesFormat\":\"percent\"},\"legendPosition\":\"right\",\"maxLegendLines\":1,\"nestedLegend\":false,\"palette\":{\"name\":\"kibana_palette\",\"type\":\"palette\"},\"truncateLegend\":true,\"type\":\"pie\",\"legendDisplay\":\"show\",\"legendSize\":\"auto\"},\"title\":\"Records by service types\",\"type\":\"pie\"}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:38:30.919Z","id":"efb9e1a0-2c28-11e7-bda0-15897525b97b","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index","type":"index-pattern"},{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.filter[1].meta.index","type":"index-pattern"},{"id":"8c335410-2c1c-11e7-98e3-1f6e0031eefe","name":"search_0","type":"search"}],"sort":[1704789510919,8589934615],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:38:30.919Z","version":"WzQ1LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"*\",\"language\":\"lucene\"},\"filter\":[]}"},"optionsJSON":"{\"useMargins\":true,\"syncColors\":false,\"syncCursor\":true,\"syncTooltips\":false,\"hidePanelTitles\":false}","panelsJSON":"[{\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":0,\"w\":12,\"h\":12,\"i\":\"2\"},\"panelIndex\":\"2\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_2\"},{\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":0,\"w\":24,\"h\":24,\"i\":\"5\"},\"panelIndex\":\"5\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":{\"legendOpen\":true}},\"panelRefName\":\"panel_5\"},{\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":24,\"w\":24,\"h\":12,\"i\":\"8\"},\"panelIndex\":\"8\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":{\"legendOpen\":true}},\"panelRefName\":\"panel_8\"},{\"type\":\"visualization\",\"gridData\":{\"x\":0,\"y\":36,\"w\":48,\"h\":24,\"i\":\"9\"},\"panelIndex\":\"9\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_9\"},{\"type\":\"visualization\",\"gridData\":{\"x\":24,\"y\":24,\"w\":24,\"h\":12,\"i\":\"10\"},\"panelIndex\":\"10\",\"embeddableConfig\":{\"enhancements\":{},\"vis\":{\"legendOpen\":true}},\"panelRefName\":\"panel_10\"},{\"type\":\"visualization\",\"gridData\":{\"x\":36,\"y\":0,\"w\":12,\"h\":12,\"i\":\"121afbff-3745-4fff-b8d4-2a75c1a6d08c\"},\"panelIndex\":\"121afbff-3745-4fff-b8d4-2a75c1a6d08c\",\"embeddableConfig\":{\"savedVis\":{\"title\":\"\",\"description\":\"\",\"type\":\"metric\",\"params\":{\"addTooltip\":true,\"addLegend\":false,\"type\":\"metric\",\"metric\":{\"percentageMode\":false,\"useRanges\":false,\"colorSchema\":\"Green to Red\",\"metricColorMode\":\"None\",\"colorsRange\":[{\"from\":0,\"to\":10000}],\"labels\":{\"show\":true},\"invertColors\":false,\"style\":{\"bgFill\":\"#000\",\"bgColor\":false,\"labelColor\":false,\"subText\":\"\",\"fontSize\":60}}},\"uiState\":{},\"data\":{\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"}],\"searchSource\":{}}},\"enhancements\":{}},\"title\":\"Records number (working copies)\"},{\"type\":\"lens\",\"gridData\":{\"x\":24,\"y\":12,\"w\":24,\"h\":12,\"i\":\"4bedb9ac-9543-493e-8beb-696aeba6d002\"},\"panelIndex\":\"4bedb9ac-9543-493e-8beb-696aeba6d002\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_4bedb9ac-9543-493e-8beb-696aeba6d002\"},{\"type\":\"search\",\"gridData\":{\"x\":0,\"y\":60,\"w\":48,\"h\":16,\"i\":\"cc0baba5-40b7-44ea-8f96-eadb5ffe5180\"},\"panelIndex\":\"cc0baba5-40b7-44ea-8f96-eadb5ffe5180\",\"embeddableConfig\":{\"enhancements\":{}},\"panelRefName\":\"panel_cc0baba5-40b7-44ea-8f96-eadb5ffe5180\"}]","timeRestore":false,"title":"Catalogue statistics","version":1},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:54:28.278Z","id":"cf5d74b0-2c25-11e7-8cd9-338183f2da0f","managed":false,"references":[{"id":"31c88fe0-2c21-11e7-a86a-2d31307889bf","name":"2:panel_2","type":"visualization"},{"id":"ecb17d40-2c20-11e7-a86a-2d31307889bf","name":"5:panel_5","type":"visualization"},{"id":"d4771f20-2c28-11e7-bda0-15897525b97b","name":"8:panel_8","type":"visualization"},{"id":"1233d430-2c28-11e7-bda0-15897525b97b","name":"9:panel_9","type":"visualization"},{"id":"efb9e1a0-2c28-11e7-bda0-15897525b97b","name":"10:panel_10","type":"visualization"},{"id":"7264b880-40f6-11ee-9f42-3704c671ff67","name":"121afbff-3745-4fff-b8d4-2a75c1a6d08c:search_0","type":"search"},{"id":"c352ec50-40f9-11ee-9f42-3704c671ff67","name":"4bedb9ac-9543-493e-8beb-696aeba6d002:panel_4bedb9ac-9543-493e-8beb-696aeba6d002","type":"lens"},{"id":"737d7520-aecb-11ee-8d56-55a140a7c65f","name":"cc0baba5-40b7-44ea-8f96-eadb5ffe5180:panel_cc0baba5-40b7-44ea-8f96-eadb5ffe5180","type":"search"}],"sort":[1704790468278,8589934667],"type":"dashboard","typeMigrationVersion":"8.9.0","updated_at":"2024-01-09T08:54:28.278Z","version":"Wzg0LDFd"} +{"attributes":{"description":"","kibanaSavedObjectMeta":{"searchSourceJSON":"{\"query\":{\"query\":\"\",\"language\":\"kuery\"},\"filter\":[],\"indexRefName\":\"kibanaSavedObjectMeta.searchSourceJSON.index\"}"},"title":"Main keywords cloud","uiStateJSON":"{}","version":1,"visState":"{\"title\":\"Main keywords cloud\",\"type\":\"tagcloud\",\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"params\":{},\"schema\":\"metric\"},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"params\":{\"field\":\"tag.default\",\"orderBy\":\"1\",\"order\":\"desc\",\"size\":45,\"otherBucket\":false,\"otherBucketLabel\":\"Other\",\"missingBucket\":false,\"missingBucketLabel\":\"Missing\",\"customLabel\":\"Main keywords\"},\"schema\":\"segment\"}],\"params\":{\"scale\":\"linear\",\"orientation\":\"single\",\"minFontSize\":18,\"maxFontSize\":72,\"showLabel\":true,\"palette\":{\"type\":\"palette\",\"name\":\"kibana_palette\"}}}"},"coreMigrationVersion":"8.8.0","created_at":"2024-01-09T08:27:20.247Z","id":"f9825c40-8dc3-11e9-9bb7-5db216293bad","managed":false,"references":[{"id":"gn-records","name":"kibanaSavedObjectMeta.searchSourceJSON.index","type":"index-pattern"}],"sort":[1704788840247,8589934645],"type":"visualization","typeMigrationVersion":"8.5.0","updated_at":"2024-01-09T08:27:20.247Z","version":"WzExLDFd"} +{"excludedObjects":[],"excludedObjectsCount":0,"exportedCount":24,"missingRefCount":0,"missingReferences":[]} diff --git a/es/es-dashboards/data/index/.gitkeep b/es/es-dashboards/data/index/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/es/es-dashboards/pom.xml b/es/es-dashboards/pom.xml index ccc7789e60f..fd86d42981d 100644 --- a/es/es-dashboards/pom.xml +++ b/es/es-dashboards/pom.xml @@ -28,7 +28,7 @@ gn-es org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT diff --git a/es/pom.xml b/es/pom.xml index 8810c45ef97..d9338a9fc8f 100644 --- a/es/pom.xml +++ b/es/pom.xml @@ -5,7 +5,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 gn-es diff --git a/estest/pom.xml b/estest/pom.xml index eb142036d6b..588ce309374 100644 --- a/estest/pom.xml +++ b/estest/pom.xml @@ -5,7 +5,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/events/pom.xml b/events/pom.xml index e531d17ca58..329dd0c6ad5 100644 --- a/events/pom.xml +++ b/events/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT GeoNetwork Events diff --git a/harvesters/pom.xml b/harvesters/pom.xml index c4a2e977855..07259922463 100644 --- a/harvesters/pom.xml +++ b/harvesters/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java index 09502913bfb..ed65e8b52a1 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/BaseAligner.java @@ -119,9 +119,9 @@ public void addPrivileges(String id, Iterable privilegesIterable, Gr String name = localGroups.getName(priv.getGroupId()); if (name == null) { - LOGGER.debug(" - Skipping removed group with id:{}", priv.getGroupId()); + LOGGER.debug(" - Skipping removed group with id: {}", priv.getGroupId()); } else { - LOGGER.debug(" - Setting privileges for group : {}", name); + LOGGER.debug(" - Setting privileges for group: {}", name); for (int opId : priv.getOperations()) { name = dataManager.getAccessManager().getPrivilegeName(opId); //--- all existing operation diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/AbstractHarvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/AbstractHarvester.java index bec3d2cda1a..8a3f270a826 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/AbstractHarvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/AbstractHarvester.java @@ -140,8 +140,12 @@ public abstract class AbstractHarvester retrieveMetadataUuidsFromIdentifier(EsSearchManager searchMa FIELDLIST_UUID, 0, 1000); - for (SearchHit hit : queryResult.getHits()) { - String uuid = hit.getSourceAsMap().get(Geonet.IndexFieldNames.UUID).toString(); + for (Hit hit : (List) queryResult.hits().hits()) { + String uuid = objectMapper.convertValue(hit.source(), Map.class).get(Geonet.IndexFieldNames.UUID).toString(); metadataUuids.add(uuid); } diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java index 23600da91b1..0a3c5b7bd9d 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geoPREST/Aligner.java @@ -291,7 +291,7 @@ private Element retrieveMetadata(String uuid) { try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, response, groupIdVal); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java index b7f4ac58be3..e2c4065c367 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet/Aligner.java @@ -30,6 +30,7 @@ import org.fao.geonet.GeonetContext; import org.fao.geonet.Logger; import org.fao.geonet.MetadataResourceDatabaseMigration; +import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.api.records.attachments.Store; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.*; @@ -77,9 +78,9 @@ public class Aligner extends BaseAligner { private UUIDMapper localUuids; private String processName; private String preferredSchema; - private Map processParams = new HashMap(); + private Map processParams = new HashMap<>(); private MetadataRepository metadataRepository; - private Map> hmRemoteGroups = new HashMap>(); + private Map> hmRemoteGroups = new HashMap<>(); private SettingManager settingManager; public Aligner(AtomicBoolean cancelMonitor, Logger log, ServiceContext context, XmlRequest req, @@ -119,7 +120,7 @@ private void setupLocEntity(List list, Map> for (Element entity : list) { String name = entity.getChildText("name"); - Map hm = new HashMap(); + Map hm = new HashMap<>(); hmEntity.put(name, hm); @SuppressWarnings("unchecked") @@ -163,7 +164,7 @@ public HarvestResult align(SortedSet records, List err result.locallyRemoved++; } - } catch (Throwable t) { + } catch (Exception t) { log.error("Couldn't remove metadata with uuid " + uuid); log.error(t); result.unchangedMetadata++; @@ -197,7 +198,6 @@ public HarvestResult align(SortedSet records, List err String id = dataMan.getMetadataId(ri.uuid); // look up value of localrating/enable - SettingManager settingManager = context.getBean(SettingManager.class); String localRating = settingManager.getValue(Settings.SYSTEM_LOCALRATING_ENABLE); if (id == null) { @@ -230,6 +230,7 @@ public HarvestResult align(SortedSet records, List err case SKIP: log.debug("Skipping record with uuid " + ri.uuid); result.uuidSkipped++; + break; default: break; } @@ -248,7 +249,7 @@ public HarvestResult align(SortedSet records, List err } } - } catch (Throwable t) { + } catch (Exception t) { log.error("Couldn't insert or update metadata with uuid " + ri.uuid); log.error(t); result.unchangedMetadata++; @@ -282,7 +283,7 @@ private Element extractValidMetadataForImport(DirectoryStream files, Eleme Log.debug(Geonet.MEF, "Multiple metadata files"); Map> mdFiles = - new HashMap>(); + new HashMap<>(); for (Path file : files) { if (Files.isRegularFile(file)) { Element metadata = Xml.loadFile(file); @@ -353,8 +354,8 @@ private Element extractValidMetadataForImport(DirectoryStream files, Eleme } private void addMetadata(final RecordInfo ri, final boolean localRating, String uuid) throws Exception { - final String id[] = {null}; - final Element md[] = {null}; + final String[] id = {null}; + final Element[] md = {null}; //--- import metadata from MEF file @@ -465,7 +466,7 @@ private String addMetadata(RecordInfo ri, Element md, Element info, boolean loca try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, md, groupIdVal); @@ -595,13 +596,13 @@ private void addPrivilegesFromGroupPolicy(String id, Element privil) throws Exce } private Map> buildPrivileges(Element privil) { - Map> map = new HashMap>(); + Map> map = new HashMap<>(); for (Object o : privil.getChildren("group")) { Element group = (Element) o; String name = group.getAttributeValue("name"); - Set set = new HashSet(); + Set set = new HashSet<>(); map.put(name, set); for (Object op : group.getChildren("operation")) { @@ -662,9 +663,9 @@ private String createGroup(String name) throws Exception { */ private void updateMetadata(final RecordInfo ri, final String id, final boolean localRating, final boolean useChangeDate, String localChangeDate, Boolean force) throws Exception { - final Element md[] = {null}; - final Element publicFiles[] = {null}; - final Element privateFiles[] = {null}; + final Element[] md = {null}; + final Element[] publicFiles = {null}; + final Element[] privateFiles = {null}; if (localUuids.getID(ri.uuid) == null && !force) { if (log.isDebugEnabled()) @@ -746,7 +747,7 @@ private void updateMetadata(RecordInfo ri, String id, Element md, try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, md, groupIdVal); @@ -756,7 +757,6 @@ private void updateMetadata(RecordInfo ri, String id, Element md, return; } - final IMetadataManager metadataManager = context.getBean(IMetadataManager.class); Metadata metadata; if (!force && !ri.isMoreRecentThan(date)) { if (log.isDebugEnabled()) @@ -883,12 +883,18 @@ private void saveFile(final Store store, String metadataUuid, String file, ISODate remIsoDate = new ISODate(changeDate); boolean saveFile; - final MetadataResource description = store.getResourceDescription(context, metadataUuid, visibility, file, true); - if (description == null) { - saveFile = true; - } else { - ISODate locIsoDate = new ISODate(description.getLastModification().getTime(), false); + Store.ResourceHolder resourceHolder; + try { + resourceHolder = store.getResource(context, metadataUuid, visibility, file, true); + } catch (ResourceNotFoundException ex) { + resourceHolder = null; + } + + if ((resourceHolder != null) && (resourceHolder.getMetadata() != null)) { + ISODate locIsoDate = new ISODate(resourceHolder.getMetadata().getLastModification().getTime(), false); saveFile = (remIsoDate.timeDifferenceInSeconds(locIsoDate) > 0); + } else { + saveFile = true; } if (saveFile) { diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java index 94cf7c6fa38..93f48ff9a48 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/geonet20/Aligner.java @@ -369,7 +369,7 @@ private Element getRemoteMetadata(XmlRequest req, String id) throws Exception { try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, md, groupIdVal); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java index 312a0285b5f..c99d57ff4d5 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFilesystemHarvester.java @@ -45,7 +45,6 @@ import org.fao.geonet.kernel.harvest.harvester.HarvestResult; import org.fao.geonet.kernel.search.IndexingMode; import org.fao.geonet.repository.MetadataRepository; -import org.fao.geonet.repository.OperationAllowedRepository; import org.fao.geonet.repository.specification.MetadataSpecs; import org.fao.geonet.utils.IO; import org.jdom.Element; @@ -158,8 +157,6 @@ void updateMetadata(Element xml, final String id, GroupMapper localGroups, final metadataManager.save(metadata); } - OperationAllowedRepository repository = context.getBean(OperationAllowedRepository.class); - repository.deleteAllByMetadataId(Integer.parseInt(id)); aligner.addPrivileges(id, params.getPrivileges(), localGroups, context); metadata.getCategories().clear(); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFsHarvesterFileVisitor.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFsHarvesterFileVisitor.java index c188611e549..791f9a17cda 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFsHarvesterFileVisitor.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/localfilesystem/LocalFsHarvesterFileVisitor.java @@ -54,8 +54,6 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.XML; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.annotation.Nonnull; import java.io.IOException; @@ -75,7 +73,6 @@ * @author Jesse on 11/6/2014. */ class LocalFsHarvesterFileVisitor extends SimpleFileVisitor { - private Logger LOGGER = LoggerFactory.getLogger(Geonet.HARVESTER); private final LocalFilesystemParams params; private final DataManager dataMan; @@ -110,9 +107,7 @@ public LocalFsHarvesterFileVisitor(AtomicBoolean cancelMonitor, ServiceContext c this.repo = context.getBean(IMetadataUtils.class); this.startTime = System.currentTimeMillis(); - String harvesterName = params.getName().replaceAll("\\W+", "_"); - LOGGER = LoggerFactory.getLogger(harvesterName); - LOGGER.debug("Start visiting files at {}.", this.startTime); + harvester.getLogger().debug(String.format("Start visiting files at %s.", this.startTime)); } @Override @@ -136,9 +131,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO try { result.totalMetadata++; - if (LOGGER.isDebugEnabled() && result.totalMetadata % 1000 == 0) { + if (harvester.getLogger().isDebugEnabled() && result.totalMetadata % 1000 == 0) { long elapsedTime = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime); - LOGGER.debug("{} records inserted in {} s ({} records/s).", new Object[] { + harvester.getLogger().debug("{} records inserted in {} s ({} records/s).", new Object[] { result.totalMetadata, elapsedTime, result.totalMetadata / elapsedTime}); @@ -152,7 +147,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO processXml(file); } } catch (Exception e) { - LOGGER.error("An error occurred while harvesting file {}. Error is: {}.", + harvester.getLogger().error("An error occurred while harvesting file {}. Error is: {}.", file.toAbsolutePath().normalize(), e.getMessage()); } return FileVisitResult.CONTINUE; @@ -168,7 +163,7 @@ private void processJson(Path file) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); Element recordAsElement; try { - LOGGER.debug("reading file: {}", filePath); + harvester.getLogger().debug("reading file: {}", filePath); String uuid = com.google.common.io.Files.getNameWithoutExtension(file.getFileName().toString()); String recordAsJson = objectMapper.readTree(filePath.toFile()).toString(); JSONObject sanitizedJson = sanitize(new JSONObject(recordAsJson)); @@ -180,18 +175,18 @@ private void processJson(Path file) throws Exception { recordAsElement = Xml.loadString(recordAsXml, false); recordAsElement.addContent(new Element("uuid").setText(uuid)); } catch (JsonProcessingException e) { - LOGGER.error("Error processing JSON from file {}, ignoring", filePath); - LOGGER.error("full stack", e); + harvester.getLogger().error("Error processing JSON from file {}, ignoring", filePath); + harvester.getLogger().error("full stack", e); result.badFormat++; return; } catch (JDOMException e) { - LOGGER.error("Error transforming JSON into XML from file {}, ignoring", filePath); - LOGGER.error("full stack", e); + harvester.getLogger().error("Error transforming JSON into XML from file {}, ignoring", filePath); + harvester.getLogger().error("full stack", e); result.badFormat++; return; } catch (Exception e) { - LOGGER.error("Error retrieving JSON from file {}, ignoring", filePath); - LOGGER.error("full stack", e); + harvester.getLogger().error("Error retrieving JSON from file {}, ignoring", filePath); + harvester.getLogger().error("full stack", e); result.unretrievable++; return; } @@ -241,16 +236,16 @@ private void processXml(Path file) throws Exception { Element xml; try { - LOGGER.debug("reading file: {}", filePath); + harvester.getLogger().debug(String.format("reading file: %s", filePath)); xml = Xml.loadFile(file); } catch (JDOMException e) { - LOGGER.error("Error loading XML from file {}, ignoring", filePath); - LOGGER.error("full stack", e); + harvester.getLogger().error("Error loading XML from file {}, ignoring", filePath); + harvester.getLogger().error("full stack", e); result.badFormat++; return; } catch (Exception e) { - LOGGER.error("Error retrieving XML from file {}, ignoring", filePath); - LOGGER.error("full stack", e); + harvester.getLogger().error("Error retrieving XML from file {}, ignoring", filePath); + harvester.getLogger().error("full stack", e); result.unretrievable++; return; } @@ -266,7 +261,7 @@ private void processXmlData(Path file, Element rawXml) throws Exception { try { xml = Xml.transform(xml, thisXslt); } catch (Exception e) { - LOGGER.error("Cannot transform XML from file {}, ignoring. Error was: {}", filePath, e.getMessage()); + harvester.getLogger().error("Cannot transform XML from file {}, ignoring. Error was: {}", filePath, e.getMessage()); result.badFormat++; return; } @@ -288,7 +283,7 @@ private void processXmlData(Path file, Element rawXml) throws Exception { params.getValidate().validate(dataMan, context, xml, groupIdVal); } catch (Exception e) { - LOGGER.error("Cannot validate XML from file {}, ignoring. Error was: {}", filePath, e.getMessage()); + harvester.getLogger().error("Cannot validate XML from file {}, ignoring. Error was: {}", filePath, e.getMessage()); result.doesNotValidate++; return; } @@ -315,14 +310,14 @@ private void processXmlData(Path file, Element rawXml) throws Exception { updateMetadata(file, filePath, xml, schema, id, metadata, true); break; case RANDOM: - LOGGER.debug("Generating random uuid for remote record with uuid " + metadata.getUuid()); + harvester.getLogger().debug("Generating random uuid for remote record with uuid " + metadata.getUuid()); String createDate = getCreateDate(file, xml, schema, uuid); String newUuid = UUID.randomUUID().toString(); id = addMetadata(xml, schema, newUuid, createDate); break; case SKIP: - LOGGER.debug("Skipping record with uuid " + metadata.getUuid()); + harvester.getLogger().debug("Skipping record with uuid " + metadata.getUuid()); result.uuidSkipped++; result.unchangedMetadata++; @@ -351,7 +346,7 @@ private String getCreateDate(Path file, Element xml, String schema, String uuid) try { createDate = dataMan.extractDateModified(schema, xml); } catch (Exception ex) { - LOGGER.error("LocalFilesystemHarvester - addMetadata - can't get metadata modified date for metadata uuid= {} " + + harvester.getLogger().error("LocalFilesystemHarvester - addMetadata - can't get metadata modified date for metadata uuid= {} " + "using current date for modified date", uuid); createDate = new ISODate().toString(); } @@ -376,25 +371,25 @@ private void updateMetadata(Path file, Path filePath, Element xml, String schema String changeDate = new ISODate(fileDate.getTime(), false).getDateAndTime(); - LOGGER.debug(" File date is: {} / record date is: {}", filePath, modified); + harvester.getLogger().debug(" File date is: {} / record date is: {}", filePath, modified); if (DateUtils.truncate(recordDate, Calendar.SECOND) .before(DateUtils.truncate(fileDate, Calendar.SECOND))) { - LOGGER.debug(" Db record is older than file. Updating record with id: {}", id); + harvester.getLogger().debug(String.format(" Db record is older than file. Updating record with id: %s", id)); updateMedata(xml, id, changeDate, force); } else { - LOGGER.debug(" Db record is not older than last modified date of file. No need for update."); + harvester.getLogger().debug(" Db record is not older than last modified date of file. No need for update."); result.unchangedMetadata++; } } else { - LOGGER.debug(" updating existing metadata, id is: " + id); + harvester.getLogger().debug(" updating existing metadata, id is: " + id); String changeDate; try { changeDate = dataMan.extractDateModified(schema, xml); } catch (Exception ex) { - LOGGER.error("LocalFilesystemHarvester - updateMetadata - can't get metadata modified date for " + + harvester.getLogger().error("LocalFilesystemHarvester - updateMetadata - can't get metadata modified date for " + "metadata id= {}, using current date for modified date", id); changeDate = new ISODate().toString(); } @@ -406,7 +401,7 @@ private void updateMetadata(Path file, Path filePath, Element xml, String schema private void processMef(Path file) { Path filePath = file.toAbsolutePath().normalize(); - LOGGER.debug("reading file: {}", filePath); + harvester.getLogger().debug(String.format("reading file: %s", filePath)); try { String xsl = params.getImportXslt(); MEFLib.Version version = MEFLib.getMEFVersion(file); @@ -439,7 +434,7 @@ private void processMef(Path file) { params.getValidate() != NOVALIDATION, false, context, file); for (String id : ids) { - LOGGER.debug("Metadata imported from MEF: {}", id); + harvester.getLogger().debug(String.format("Metadata imported from MEF: %s", id)); context.getBean(MetadataRepository.class).update(Integer.valueOf(id), new Updater() { @Override public void apply(@Nonnull final Metadata metadata) { @@ -454,8 +449,8 @@ public void apply(@Nonnull final Metadata metadata) { result.addedMetadata++; } } catch (Exception e) { - LOGGER.error("Error retrieving MEF from file {}, ignoring", filePath); - LOGGER.error("Error: ", e); + harvester.getLogger().error("Error retrieving MEF from file {}, ignoring", filePath); + harvester.getLogger().error("Error: ", e); result.unretrievable++; } } @@ -465,26 +460,26 @@ private String getUuidFromFile(Element xml, Path filePath, String schema) { try { uuid = dataMan.extractUUID(schema, xml); } catch (Exception e) { - LOGGER.debug("Failed to extract metadata UUID for file {}" + + harvester.getLogger().debug("Failed to extract metadata UUID for file {}" + " using XSL extract-uuid. The record is probably " + "a subtemplate. Will check uuid attribute on root element.", filePath); // Extract UUID from uuid attribute in subtemplates String uuidAttribute = xml.getAttributeValue("uuid"); if (uuidAttribute != null) { - LOGGER.debug("Found uuid attribute {} for file {}.", uuidAttribute, filePath); + harvester.getLogger().debug("Found uuid attribute {} for file {}.", uuidAttribute, filePath); uuid = uuidAttribute; } else { // Assigning a new UUID uuid = UUID.randomUUID().toString(); - LOGGER.debug("No UUID found, the record will be assigned a random uuid {} for file {}.", uuid, filePath); + harvester.getLogger().debug("No UUID found, the record will be assigned a random uuid {} for file {}.", uuid, filePath); } } return uuid; } private String addMetadata(Element xml, String schema, String uuid, String createDate) throws Exception { - LOGGER.debug("adding new metadata"); + harvester.getLogger().debug("adding new metadata"); String id = harvester.addMetadata(xml, uuid, schema, localGroups, localCateg, createDate, aligner, false); listOfRecordsToIndex.add(Integer.valueOf(id)); result.addedMetadata++; diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java index f9d015b0446..e22b3dc96be 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/oaipmh/Harvester.java @@ -472,7 +472,7 @@ private Element retrieveMetadata(XmlRequest transport, RecordInfo ri) { try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, md, groupIdVal); diff --git a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java index 2a0ec92c883..789cbc44ba2 100644 --- a/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java +++ b/harvesters/src/main/java/org/fao/geonet/kernel/harvest/harvester/webdav/Harvester.java @@ -351,7 +351,7 @@ private Element retrieveMetadata(RemoteFile rf) { try { Integer groupIdVal = null; if (StringUtils.isNotEmpty(params.getOwnerIdGroup())) { - groupIdVal = Integer.parseInt(params.getOwnerIdGroup()); + groupIdVal = getGroupOwner(); } params.getValidate().validate(dataMan, context, md, groupIdVal); diff --git a/healthmonitor/pom.xml b/healthmonitor/pom.xml index b97df05cf6b..fd58de76ef6 100644 --- a/healthmonitor/pom.xml +++ b/healthmonitor/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/DashboardAppHealthCheck.java b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/DashboardAppHealthCheck.java index c42a4a543a6..ca400b7edb8 100644 --- a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/DashboardAppHealthCheck.java +++ b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/DashboardAppHealthCheck.java @@ -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) * @@ -29,7 +29,6 @@ import org.apache.commons.lang.StringUtils; import org.apache.http.client.methods.HttpGet; import org.fao.geonet.kernel.search.EsSearchManager; -import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.utils.GeonetHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; @@ -43,7 +42,6 @@ public HealthCheck create(final ServiceContext context) { @Override protected Result check() throws Exception { final GeonetHttpRequestFactory httpRequestFactory = context.getBean(GeonetHttpRequestFactory.class); - final SettingManager settingManager = context.getBean(SettingManager.class); final EsSearchManager searchMan = context.getBean(EsSearchManager.class); final String dashboardAppUrl = searchMan.getClient().getDashboardAppUrl(); @@ -56,9 +54,9 @@ protected Result check() throws Exception { if (httpResponse.getRawStatusCode() == 200 // Kibana default config || httpResponse.getRawStatusCode() == 404 // Kibana alive but probably using a custom basePath ) { - return Result.healthy(String.format( + return Result.healthy( "Dashboard application is running." - )); + ); // Could make sense to do some more checks. // Kibana status may be red/yellow ? // String url = settingManager.getBaseURL() + "dashboards/api/status"; @@ -68,7 +66,7 @@ protected Result check() throws Exception { "Dashboard application is not available currently. " + "This component is only required if you use dashboards."); } - } catch (Throwable e) { + } catch (Exception e) { return Result.unhealthy(e); } finally { if (httpResponse != null) { diff --git a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexHealthCheck.java b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexHealthCheck.java index 14b3f44db41..d141374e717 100644 --- a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexHealthCheck.java +++ b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexHealthCheck.java @@ -23,14 +23,12 @@ package org.fao.geonet.monitor.health; +import co.elastic.clients.elasticsearch.core.SearchResponse; import com.yammer.metrics.core.HealthCheck; import jeeves.monitor.HealthCheckFactory; import jeeves.server.context.ServiceContext; -import org.elasticsearch.action.search.SearchResponse; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.kernel.search.EsSearchManager; -import org.fao.geonet.kernel.setting.SettingInfo; -import org.openrdf.sesame.sail.query.In; import org.springframework.context.ApplicationContext; /** @@ -45,17 +43,12 @@ protected Result check() { ApplicationContext applicationContext = ApplicationContextHolder.get(); EsSearchManager searchMan = applicationContext.getBean(EsSearchManager.class); final SearchResponse result = searchMan.query("*", null, 0, 0); - if (result.status().getStatus() == 200) { - return Result.healthy(String.format( - "%s records indexed in remote index currently.", - result.getHits().getTotalHits().value - )); - } else { - return Result.unhealthy( - "Index storing records is not available currently. " + - "This component is required. Check your installation."); - } - } catch (Throwable e) { + + return Result.healthy(String.format( + "%s records indexed in remote index currently.", + result.hits().total().value() + )); + } catch (Exception e) { return Result.unhealthy(e); } } diff --git a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexReadOnlyHealthCheck.java b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexReadOnlyHealthCheck.java index cc0d104f485..a45518ae5d9 100644 --- a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexReadOnlyHealthCheck.java +++ b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/IndexReadOnlyHealthCheck.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 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) * @@ -23,17 +23,16 @@ package org.fao.geonet.monitor.health; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; import com.yammer.metrics.core.HealthCheck; import jeeves.monitor.HealthCheckFactory; import jeeves.server.context.ServiceContext; -import org.elasticsearch.action.admin.indices.settings.get.GetSettingsRequest; -import org.elasticsearch.action.admin.indices.settings.get.GetSettingsResponse; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.client.RequestOptions; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.kernel.search.EsSearchManager; import org.springframework.context.ApplicationContext; +import java.io.IOException; + /** * Checks to ensure that the Elasticsearch index is not in readonly mode. * @@ -52,25 +51,21 @@ public HealthCheck create(final ServiceContext context) { protected Result check() { try { ApplicationContext applicationContext = ApplicationContextHolder.get(); - EsSearchManager searchMan = applicationContext.getBean(EsSearchManager.class); - String indexBlockRead = "index.blocks.read_only_allow_delete"; - GetSettingsRequest request = new GetSettingsRequest(); - request.names(indexBlockRead); - GetSettingsResponse settings = searchMan.getClient().getClient() - .indices().getSettings(request, RequestOptions.DEFAULT); + EsSearchManager esSearchManager = applicationContext.getBean(EsSearchManager.class); - Boolean isReadOnly = "true".equals(settings.getSetting(searchMan.getDefaultIndex(), indexBlockRead)); + String indexName = esSearchManager.getDefaultIndex(); + boolean isReadOnly = esSearchManager.isIndexWritable(indexName); if (!isReadOnly) { return Result.healthy(String.format( - "Index is writable.", - searchMan.getDefaultIndex() + "Index '%s' is writable.", + indexName )); } else { return Result.unhealthy( "Index is in Readonly mode. Check disk usage and/or indexing server logs."); } - } catch (Throwable e) { + } catch (IOException | ElasticsearchException e) { return Result.unhealthy(e); } } diff --git a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/NoIndexErrorsHealthCheck.java b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/NoIndexErrorsHealthCheck.java index c641634ef94..d3afc90e4f1 100644 --- a/healthmonitor/src/main/java/org/fao/geonet/monitor/health/NoIndexErrorsHealthCheck.java +++ b/healthmonitor/src/main/java/org/fao/geonet/monitor/health/NoIndexErrorsHealthCheck.java @@ -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) * @@ -47,7 +47,7 @@ protected Result check() throws Exception { long numDocs = searchMan.getNumDocs("+" + IndexFields.INDEXING_ERROR_FIELD + ":true"); if (numDocs > 0) { - return Result.unhealthy("Found " + numDocs + " metadata that had errors during indexing"); + return Result.unhealthy(String.format("Found %d metadata that had errors during indexing", numDocs)); } else { return Result.healthy(); } diff --git a/index/pom.xml b/index/pom.xml index 4ab4b55f4d5..e4801d49c46 100644 --- a/index/pom.xml +++ b/index/pom.xml @@ -5,7 +5,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 gn-index @@ -33,8 +33,8 @@ transport --> - org.elasticsearch.client - elasticsearch-rest-high-level-client + co.elastic.clients + elasticsearch-java org.springframework diff --git a/index/src/main/java/org/fao/geonet/index/es/EsRestClient.java b/index/src/main/java/org/fao/geonet/index/es/EsRestClient.java index f4486d6dc1b..2f031f726b2 100644 --- a/index/src/main/java/org/fao/geonet/index/es/EsRestClient.java +++ b/index/src/main/java/org/fao/geonet/index/es/EsRestClient.java @@ -23,7 +23,27 @@ package org.fao.geonet.index.es; +import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient; +import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.*; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; +import co.elastic.clients.elasticsearch._types.query_dsl.WrapperQuery; +import co.elastic.clients.elasticsearch.cluster.HealthResponse; +import co.elastic.clients.elasticsearch.core.*; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.indices.AnalyzeRequest; +import co.elastic.clients.elasticsearch.indices.AnalyzeResponse; +import co.elastic.clients.elasticsearch.indices.analyze.AnalyzeToken; +import co.elastic.clients.json.JsonData; +import co.elastic.clients.json.JsonpMapper; +import co.elastic.clients.json.jackson.JacksonJsonpMapper; +import co.elastic.clients.transport.ElasticsearchTransport; +import co.elastic.clients.transport.rest_client.RestClientTransport; import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import jakarta.json.spi.JsonProvider; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpHost; @@ -37,33 +57,8 @@ import org.apache.http.nio.conn.SchemeIOSessionStrategy; import org.apache.http.nio.conn.ssl.SSLIOSessionStrategy; import org.apache.http.ssl.SSLContextBuilder; -import org.apache.lucene.search.TotalHits; -import org.elasticsearch.ElasticsearchException; -import org.elasticsearch.ElasticsearchStatusException; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest; -import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; -import org.elasticsearch.action.bulk.BulkRequest; -import org.elasticsearch.action.bulk.BulkResponse; -import org.elasticsearch.action.get.GetRequest; -import org.elasticsearch.action.index.IndexRequest; -import org.elasticsearch.action.search.SearchRequest; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.action.support.WriteRequest; -import org.elasticsearch.client.*; -import org.elasticsearch.client.core.MainResponse; -import org.elasticsearch.client.indices.AnalyzeRequest; -import org.elasticsearch.client.indices.AnalyzeResponse; -import org.elasticsearch.xcontent.XContentType; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; -import org.elasticsearch.index.query.QueryStringQueryBuilder; -import org.elasticsearch.index.reindex.BulkByScrollResponse; -import org.elasticsearch.index.reindex.DeleteByQueryRequest; -import org.elasticsearch.script.Script; -import org.elasticsearch.search.SearchHit; -import org.elasticsearch.search.builder.SearchSourceBuilder; -import org.elasticsearch.search.sort.FieldSortBuilder; -import org.elasticsearch.search.sort.SortBuilder; +import org.elasticsearch.client.RestClient; +import org.elasticsearch.client.RestClientBuilder; import org.fao.geonet.utils.Log; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; @@ -71,6 +66,7 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; import java.io.IOException; +import java.io.StringReader; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.*; @@ -82,7 +78,9 @@ public class EsRestClient implements InitializingBean { private static EsRestClient instance; - private RestHighLevelClient client; + private ElasticsearchClient client; + + private ElasticsearchAsyncClient asyncClient; @Value("${es.url}") private String serverUrl; @@ -108,10 +106,14 @@ public static EsRestClient get() { return instance; } - public RestHighLevelClient getClient() { + public ElasticsearchClient getClient() { return client; } + public ElasticsearchAsyncClient getAsynchClient() { + return asyncClient; + } + public String getDashboardAppUrl() { return dashboardAppUrl; } @@ -159,7 +161,14 @@ public boolean isTrusted(X509Certificate[] arg0, String arg1) throws Certificate builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)); } } - client = new RestHighLevelClient(builder); + + RestClient restClient = builder.build(); + + ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper()); + + client = new ElasticsearchClient(transport); + + asyncClient = new ElasticsearchAsyncClient(transport); synchronized (EsRestClient.class) { instance = this; @@ -206,20 +215,29 @@ public BulkResponse bulkRequest(String index, Map docs) throws I throw new IOException("Index not yet activated."); } - BulkRequest request = new BulkRequest(); - request.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE); + BulkRequest.Builder requestBuilder = new BulkRequest.Builder() + .index(index) + .refresh(Refresh.True); + + JsonpMapper jsonpMapper = client._transport().jsonpMapper(); + JsonProvider jsonProvider = jsonpMapper.jsonProvider(); + Iterator> iterator = docs.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); - request.add(new IndexRequest(index).id(entry.getKey()) - .source(entry.getValue(), XContentType.JSON)); - // https://www.elastic.co/fr/blog/customizing-your-document-routing - // For features & record search we need to route the record - // document to the same place as the features in order to make join -// .routing(ROUTING_KEY)); + + JsonData jd = JsonData.from(jsonProvider.createParser(new StringReader(entry.getValue())), jsonpMapper); + + requestBuilder + .operations(op -> op.index(idx -> idx.index(index) + .id(entry.getKey()) + .document(jd))); } + + BulkRequest request = requestBuilder.build(); + try { - return client.bulk(request, RequestOptions.DEFAULT); + return client.bulk(request); } catch (IOException e) { e.printStackTrace(); throw e; @@ -254,86 +272,88 @@ public SearchResponse query(String index, String luceneQuery, String filterQuery public SearchResponse query(String index, String luceneQuery, String filterQuery, Set includedFields, Map scriptedFields, - int from, int size, List> sort) throws Exception { - final QueryBuilder query = QueryBuilders.queryStringQuery(luceneQuery); - QueryBuilder filter = null; + int from, int size, List sort) throws Exception { + + Query.Builder queryBuilder = new Query.Builder(); + queryBuilder.queryString(new QueryStringQuery.Builder().query(luceneQuery).build()); + + Query.Builder filterBuilder = null; if (StringUtils.isNotEmpty(filterQuery)) { - filter = QueryBuilders.queryStringQuery(filterQuery); + filterBuilder = new Query.Builder(); + filterBuilder.queryString(new QueryStringQuery.Builder().query(filterQuery).build()); } - return query(index, query, filter, includedFields, scriptedFields, from, size, sort); + return query(index, queryBuilder, filterBuilder, includedFields, scriptedFields, from, size, sort); } /** * Query using JSON elastic query */ - public SearchResponse query(String index, JsonNode jsonQuery, QueryBuilder postFilterBuilder, + public SearchResponse query(String index, JsonNode jsonQuery, Query.Builder postFilterBuilder, Set includedFields, Map scriptedFields, int from, int size) throws Exception { return query(index, jsonQuery, postFilterBuilder, includedFields, scriptedFields, from, size, null); } - public SearchResponse query(String index, JsonNode jsonQuery, QueryBuilder postFilterBuilder, Set includedFields, + public SearchResponse query(String index, JsonNode jsonQuery, Query.Builder postFilterBuilder, Set includedFields, int from, int size) throws Exception { return query(index, jsonQuery, postFilterBuilder, includedFields, new HashMap<>(), from, size, null); } - public SearchResponse query(String index, JsonNode jsonQuery, QueryBuilder postFilterBuilder, + public SearchResponse query(String index, JsonNode jsonQuery, Query.Builder postFilterBuilder, Set includedFields, Map scriptedFields, - int from, int size, List> sort) throws Exception { - final QueryBuilder query = QueryBuilders.wrapperQuery(String.valueOf(jsonQuery)); + int from, int size, List sort) throws Exception { + final Query.Builder query = new Query.Builder(); + + WrapperQuery.Builder wrapperQueryBuilder = new WrapperQuery.Builder(); + wrapperQueryBuilder.query(Base64.getEncoder().encodeToString(String.valueOf(jsonQuery).getBytes())); + query.wrapper(wrapperQueryBuilder.build()); + return query(index, query, postFilterBuilder, includedFields, scriptedFields, from, size, sort); } - public SearchResponse query(String index, QueryBuilder queryBuilder, QueryBuilder postFilterBuilder, + public SearchResponse query(String index, Query.Builder queryBuilder, Query.Builder postFilterBuilder, Set includedFields, Map scriptedFields, - int from, int size, List> sort) throws Exception { + int from, int size, List sort) throws Exception { if (!activated) { return null; } // TODOES: Add permission if index is gn-records // See EsHTTPProxy#addUserInfo - SearchRequest searchRequest = new SearchRequest(); - searchRequest.indices(index); - SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); - searchSourceBuilder.query(queryBuilder); + SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder() + .index(index) + .from(from) + .size(size) + .query(queryBuilder.build()) + .trackTotalHits(th -> th.enabled(true)) + .source(sc -> sc.filter(f -> f.includes(new ArrayList<>(includedFields)))); + + if (postFilterBuilder != null) { + searchRequestBuilder.postFilter(postFilterBuilder.build()); + } if (MapUtils.isNotEmpty(scriptedFields)) { for (Map.Entry scriptedField: scriptedFields.entrySet()) { - searchSourceBuilder.scriptField(scriptedField.getKey(), new Script(scriptedField.getValue())); - } - } + ScriptField scriptField = ScriptField.of( + b -> b.script(sb -> sb.inline(is -> is.source(scriptedField.getValue()))) + ); - searchSourceBuilder.fetchSource(includedFields.toArray(new String[includedFields.size()]), null); - searchSourceBuilder.from(from); - searchSourceBuilder.size(size); - searchSourceBuilder.trackTotalHits(true); - if (postFilterBuilder != null) { - searchSourceBuilder.postFilter(postFilterBuilder); + searchRequestBuilder.scriptFields(scriptedField.getKey(), scriptField); + } } - if ((sort != null) && (!sort.isEmpty())) { - sort.forEach(searchSourceBuilder::sort); + if (sort != null) { + searchRequestBuilder.sort(sort); } - searchRequest.source(searchSourceBuilder); + SearchRequest searchRequest = searchRequestBuilder.build(); try { - SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); - if (searchResponse.status().getStatus() == 200) { - return searchResponse; - } else { - throw new IOException(String.format( - "Error during querying index. Errors is '%s'.", searchResponse.status().toString() - )); - } - } catch (ElasticsearchStatusException esException) { - Throwable[] suppressed = esException.getSuppressed(); - if (suppressed.length > 0 && suppressed[0] instanceof ResponseException) { - ResponseException re = (ResponseException) suppressed[0]; - Log.error("geonetwork.index", String.format( - "Error during querying index. %s", re.getMessage())); - } + return client.search(searchRequest, ObjectNode.class); + + } catch (ElasticsearchException esException) { + Log.error("geonetwork.index", String.format( + "Error during querying index. %s", esException.error().toString())); throw esException; } } @@ -344,19 +364,24 @@ public String deleteByQuery(String index, String query) throws Exception { return ""; } - DeleteByQueryRequest request = new DeleteByQueryRequest(); - request.setRefresh(true); - request.indices(index); - request.setQuery(new QueryStringQueryBuilder(query)); + DeleteByQueryRequest request = DeleteByQueryRequest.of( + b -> b.index(new ArrayList<>(Arrays.asList(index))) + .q(query) + .refresh(true)); - final BulkByScrollResponse deleteByQueryResponse = - client.deleteByQuery(request, RequestOptions.DEFAULT); + final DeleteByQueryResponse deleteByQueryResponse = + client.deleteByQuery(request); - if (deleteByQueryResponse.getStatus().getDeleted() >= 0) { - return String.format("Record removed. %s.", deleteByQueryResponse.getStatus().getDeleted()); + + if (deleteByQueryResponse.deleted() >= 0) { + return String.format("Record removed. %s.", deleteByQueryResponse.deleted()); } else { + StringBuilder stringBuilder = new StringBuilder(); + + deleteByQueryResponse.failures().forEach(f -> stringBuilder.append(f.toString())); + throw new IOException(String.format( - "Error during removal. Errors is '%s'.", deleteByQueryResponse.getStatus().getReasonCancelled() + "Error during removal. Errors are '%s'.", stringBuilder.toString() )); } } @@ -370,9 +395,19 @@ public Map getDocument(String index, String id) throws Exception if (!activated) { return Collections.emptyMap(); } - GetRequest request = new GetRequest().index(index).id(id); - return client.get(request, RequestOptions.DEFAULT).getSourceAsMap(); + GetRequest request = GetRequest.of( + b -> b.index(index).id(id) + ); + + GetResponse response = client.get(request, ObjectNode.class); + + if (response.found()) { + ObjectMapper objectMapper = new ObjectMapper(); + return objectMapper.convertValue(response.source(), Map.class); + } else { + throw new Exception(String.format("Document with id %s not found", id)); + } } /** @@ -388,35 +423,33 @@ public Map getFieldsValues(String index, String id, Set String query = String.format("_id:\"%s\"", id); // TODO: Check maxRecords // TODO: Use _doc API? + + final SearchResponse searchResponse = this.query(index, query, null, fields, new HashMap<>(), 0, 1, null); - if (searchResponse.status().getStatus() == 200) { - TotalHits totalHits = searchResponse.getHits().getTotalHits(); - long matches = totalHits == null ? -1 : totalHits.value; - if (matches == 0) { - return fieldValues; - } else if (matches == 1) { - final SearchHit[] hits = searchResponse.getHits().getHits(); - - fields.forEach(f -> { - final Object o = hits[0].getSourceAsMap().get(f); - if (o instanceof String) { - fieldValues.put(f, (String) o); - } else if (o instanceof HashMap && f.endsWith("Object")) { - fieldValues.put(f, (String) ((HashMap) o).get("default")); - } - }); - } else { - throw new IOException(String.format( - "Your query '%s' returned more than one record, %d in fact. Can't retrieve field values for more than one record.", - query, - matches - )); - } + + List totalHits = searchResponse.hits().hits(); + long matches = totalHits.size(); + if (matches == 0) { + return fieldValues; + } else if (matches == 1) { + final Hit hit = totalHits.get(0); + + fields.forEach(f -> { + final Object o = hit.fields().get(f); + if (o instanceof String) { + fieldValues.put(f, (String) o); + } else if (o instanceof HashMap && f.endsWith("Object")) { + fieldValues.put(f, (String) ((HashMap) o).get("default")); + } + }); } else { throw new IOException(String.format( - "Error during fields value retrieval. Status is '%s'.", searchResponse.status().getStatus() + "Your query '%s' returned more than one record, %d in fact. Can't retrieve field values for more than one record.", + query, + matches )); } + } catch (Exception e) { throw new IOException(String.format( "Error during fields value retrieval. Errors is '%s'.", e.getMessage() @@ -464,19 +497,21 @@ public static String analyzeField(String collection, String analyzer, String fieldValue) { - AnalyzeRequest request = AnalyzeRequest.withIndexAnalyzer(collection, - analyzer, - // Replace , as it is meaningful in synonym map format - fieldValue.replace(",", "")); + AnalyzeRequest analyzeRequest = AnalyzeRequest.of( + b -> b.index(collection) + .analyzer(analyzer) + // Replace , as it is meaningful in synonym map format + .text(fieldValue.replace(",", "")) + ); try { - AnalyzeResponse response = EsRestClient.get().client.indices().analyze(request, RequestOptions.DEFAULT); + AnalyzeResponse response = EsRestClient.get().client.indices().analyze(analyzeRequest); - final List tokens = response.getTokens(); + final List tokens = response.tokens(); if (tokens.size() == 1) { - final String type = tokens.get(0).getType(); + final String type = tokens.get(0).type(); if ("SYNONYM".equals(type) || "word".equals(type)) { - return tokens.get(0).getTerm(); + return tokens.get(0).token(); } return ""; } else { @@ -487,27 +522,16 @@ public static String analyzeField(String collection, } } - - protected void finalize() { - try { - client.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - // TODO: check index exist too public String getServerStatus() throws IOException { - ClusterHealthRequest request = new ClusterHealthRequest(); - ClusterHealthResponse response = client.cluster().health(request, RequestOptions.DEFAULT); - return response.getStatus().toString(); -// return getClient().ping(RequestOptions.DEFAULT); + HealthResponse response = client.cluster().health(); + return response.status().toString(); } public String getServerVersion() throws IOException, ElasticsearchException { - MainResponse.Version version = client.info(RequestOptions.DEFAULT).getVersion(); + ElasticsearchVersionInfo version = client.info().version(); - return version.getNumber(); + return version.number(); } } diff --git a/inspire-atom/pom.xml b/inspire-atom/pom.xml index 31e74646e0e..09424fda0c8 100644 --- a/inspire-atom/pom.xml +++ b/inspire-atom/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -93,6 +93,11 @@ h2 test + + + co.elastic.clients + elasticsearch-java + diff --git a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/util/InspireAtomUtil.java b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/util/InspireAtomUtil.java index 6c6980b6f8c..a452d0733d0 100644 --- a/inspire-atom/src/main/java/org/fao/geonet/inspireatom/util/InspireAtomUtil.java +++ b/inspire-atom/src/main/java/org/fao/geonet/inspireatom/util/InspireAtomUtil.java @@ -22,13 +22,13 @@ //============================================================================== package org.fao.geonet.inspireatom.util; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.elasticsearch.core.search.TotalHits; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; -import org.apache.lucene.search.TotalHits; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.search.SearchHit; import org.fao.geonet.GeonetContext; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.constants.Geonet; @@ -372,8 +372,8 @@ public static List searchMetadataByTypeAndProtocol(ServiceCont 0, 10000); IMetadataUtils dataManager = context.getBean(IMetadataUtils.class); - for (SearchHit hit : result.getHits()) { - String id = hit.getSourceAsMap().get(Geonet.IndexFieldNames.ID).toString(); + for (Hit hit : (List) result.hits().hits()) { + String id = objectMapper.convertValue(hit.source(), Map.class).get(Geonet.IndexFieldNames.ID).toString(); allMdInfo.add(dataManager.findOne(id)); } } catch (Exception ex) { @@ -409,10 +409,10 @@ public static String retrieveDatasetUuidFromIdentifier(EsSearchManager searchMan FIELDLIST_CORE, 0, 1); - TotalHits totalHits = result.getHits().getTotalHits(); + TotalHits totalHits = result.hits().total(); - if ((totalHits != null) && (totalHits.value > 0)) { - id = result.getHits().getAt(0).getId(); + if ((totalHits != null) && (totalHits.value() > 0)) { + id = ((Hit) result.hits().hits().get(0)).id(); } } catch (Exception ex) { Log.error(Geonet.ATOM, ex.getMessage(), ex); @@ -552,8 +552,8 @@ public static Element getMetadataFeedByResourceIdentifier(final ServiceContext c esJsonQuery, FIELDLIST_CORE, 0, 1); - for (SearchHit hit : result.getHits()) { - datasetMd = repo.findOneByUuid(hit.getId()); + for (Hit hit : (List) result.hits().hits()) { + datasetMd = repo.findOneByUuid(hit.id()); } } catch (Exception e) { @@ -600,8 +600,8 @@ public static Element getMetadataFeedByResourceIdentifier(final ServiceContext c esJsonQuery, FIELDLIST_CORE, 0, 1); - for (SearchHit hit : result.getHits()) { - serviceMetadata = repo.findOneByUuid(hit.getId()); + for (Hit hit : (List) result.hits().hits()) { + serviceMetadata = repo.findOneByUuid(hit.id()); } } catch (Exception e) { 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 cd1e247b53c..0e27e9c8763 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 @@ -22,6 +22,8 @@ //============================================================================== package org.fao.geonet.services.inspireatom; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import io.swagger.v3.oas.annotations.Parameter; @@ -31,8 +33,6 @@ 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; @@ -56,12 +56,16 @@ import org.jdom.Content; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; + +import java.util.Arrays; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.stream.Collectors; import static org.fao.geonet.kernel.search.EsFilterBuilder.buildPermissionsFilter; @@ -103,14 +107,17 @@ public class AtomSearch { description = "") @GetMapping( value = "/feeds", - produces = MediaType.APPLICATION_XML_VALUE + produces = { + MediaType.APPLICATION_XML_VALUE, + MediaType.TEXT_HTML_VALUE + } ) @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "Get a list of feeds."), @ApiResponse(responseCode = "204", description = "Not authenticated.") }) @ResponseStatus(OK) - public Element feeds( + public Object feeds( @Parameter( description = "fileIdentifier", required = false) @@ -118,6 +125,20 @@ public Element feeds( String fileIdentifier, @Parameter(hidden = true) HttpServletRequest request) throws Exception { + + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT))?MediaType.APPLICATION_XML_VALUE:request.getHeader(HttpHeaders.ACCEPT); + List accept = Arrays.asList(acceptHeader.split(",")); + + if (accept.contains(MediaType.TEXT_HTML_VALUE)) { + return feedsAsHtml(fileIdentifier, request); + } else{ + return feedsAsXml(fileIdentifier, request); + } + } + private Element feedsAsXml( + String fileIdentifier, + HttpServletRequest request) throws Exception { + ServiceContext context = ApiUtils.createServiceContext(request); boolean inspireEnable = sm.getValueAsBool(Settings.SYSTEM_INSPIRE_ENABLE); @@ -182,41 +203,24 @@ public Element feeds( // Loop over the results and retrieve feeds to add in results // First element in results (pos=0) is the summary, ignore it - for (SearchHit hit : result.getHits().getHits()) { - String id = hit.getSourceAsMap().get(Geonet.IndexFieldNames.ID).toString(); + for (Hit hit : (List) result.hits().hits()) { + String id = objectMapper.convertValue(hit.source(), Map.class).get(Geonet.IndexFieldNames.ID).toString(); InspireAtomFeed feed = service.findByMetadataId(Integer.parseInt(id)); if (feed != null) { Element feedEl = Xml.loadString(feed.getAtom(), false); feeds.addContent((Content) feedEl.clone()); } else { - Log.debug(Geonet.ATOM, String.format("No feed available for %s", hit.getId())); + Log.debug(Geonet.ATOM, String.format("No feed available for %s", hit.id())); } } return feeds; } - @io.swagger.v3.oas.annotations.Operation( - summary = "Get ATOM feeds", - description = "") - @GetMapping( - value = "/feeds", - produces = MediaType.TEXT_HTML_VALUE - ) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Get a list of feeds."), - @ApiResponse(responseCode = "204", description = "Not authenticated.") - }) - @ResponseStatus(OK) - public String feedsAsHtml( - @Parameter( - description = "fileIdentifier", - required = false) - @RequestParam(defaultValue = "") + private String feedsAsHtml( String fileIdentifier, - @Parameter(hidden = true) HttpServletRequest request) throws Exception { - Element feeds = feeds(fileIdentifier, request); + Element feeds = feedsAsXml(fileIdentifier, request); Locale locale = languageUtils.parseAcceptLanguage(request.getLocales()); String language = IsoLanguagesMapper.iso639_2T_to_iso639_2B(locale.getISO3Language()); diff --git a/jmeter/pom.xml b/jmeter/pom.xml index 04a4f1d32de..8e4d6b03860 100644 --- a/jmeter/pom.xml +++ b/jmeter/pom.xml @@ -29,7 +29,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT diff --git a/listeners/pom.xml b/listeners/pom.xml index 87171af6849..ed51f29c582 100644 --- a/listeners/pom.xml +++ b/listeners/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT GeoNetwork Events diff --git a/messaging/pom.xml b/messaging/pom.xml index 24030b2daab..72f32b8bdba 100644 --- a/messaging/pom.xml +++ b/messaging/pom.xml @@ -5,7 +5,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/oaipmh/pom.xml b/oaipmh/pom.xml index 3caa519ddb6..0e0f1055cf3 100644 --- a/oaipmh/pom.xml +++ b/oaipmh/pom.xml @@ -30,7 +30,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT diff --git a/pom.xml b/pom.xml index b09002fd182..a193462e79c 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ org.geonetwork-opensource geonetwork pom - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT GeoNetwork opensource GeoNetwork opensource is a standards based, Free and Open Source catalog application to manage spatially referenced @@ -111,11 +111,6 @@ maven-jar-plugin 3.1.2 - - org.codehaus.mojo - jslint-maven-plugin - 1.0.1 - org.bsc.maven maven-processor-plugin @@ -133,6 +128,14 @@ maven-clean-plugin 3.1.0 + + maven-install-plugin + 3.1.1 + + + maven-deploy-plugin + 3.1.1 + net.alchim31.maven yuicompressor-maven-plugin @@ -242,12 +245,36 @@ maven-toolchains-plugin 3.0.0 + + org.apache.maven.plugins + maven-enforcer-plugin + 3.4.1 + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-maven + + enforce + + + + + 3.8.3 + + + + + + + maven-compiler-plugin 11 @@ -266,7 +293,6 @@ - org.apache.maven.plugins maven-resources-plugin UTF-8 @@ -292,7 +318,6 @@ - org.apache.maven.plugins maven-surefire-plugin org.fao.geonet.repository.AbstractSpringDataTest @@ -517,7 +542,7 @@ commons-fileupload commons-fileupload - 1.3.3 + 1.5 commons-collections @@ -1197,9 +1222,10 @@ jaxb-impl 2.3.1 + - org.elasticsearch.client - elasticsearch-rest-high-level-client + co.elastic.clients + elasticsearch-java ${es.version} @@ -1268,6 +1294,12 @@ 1.12.261 + + org.json + json + 20240205 + + @@ -1454,16 +1486,6 @@ - - WEB-INF/config-db/jdbc.properties @@ -1521,12 +1543,18 @@ * allow only host registered in metadata link table --> DB_LINK_CHECK + + ^(localhost|127\..*|0\..*|255\.255\.255\.255|.*\.local|.*\.localhost|0:0:0:0:0:0:1|::1)$ + + + + 8080 8090 - 7.17.15 + 8.11.3 linux-x86_64 tar.gz http @@ -1539,7 +1567,7 @@ records gn-searchlogs searchlogs - 6.19 + 6.28 standard diff --git a/release/README.md b/release/README.md index 899ac2f3251..656b84986a1 100644 --- a/release/README.md +++ b/release/README.md @@ -14,19 +14,33 @@ cd release mvn clean install -Drelease ``` -## Manual +This module is designed to be used as part of a full build. It copies files from web/target so gn-web-app must be built first. + +## Manual release Open a terminal window and execute the following steps from within the ``release`` folder. -* Once GeoNetwork has been built (run Maven in the repository root), download Jetty: +1. Once GeoNetwork has been built (run Maven in the repository root), download Jetty: + + ```bash + mvn clean install -Pjetty-download + ``` + + This will download the version of jetty indicated in dependency management, and rename to ``jetty`` folder + (adjusting ``jetty-deploy.xml`` configuration to use `web` rather than default ``webapps``). + +2. Next, create the ZIP distributions and copy the WAR: - ` - mvn clean install -Pjetty-download - ` + ``` + ant + ``` + + The build.xml file will check everything is available and assemble into a zip. -* Next, create the ZIP distributions and copy the WAR: +## Jetty download - ` - ant - ` +To clean up the ``jetty`` download, when switching between branches: +```bash +mvn clean:clean@reset +``` diff --git a/release/build.properties b/release/build.properties index 9c8572d1963..bf6bc74a749 100644 --- a/release/build.properties +++ b/release/build.properties @@ -5,11 +5,11 @@ homepage=https://geonetwork-opensource.org supportEmail=geonetwork-users@lists.sourceforge.net # Application version properties -version=4.4.2 +version=4.4.4 subVersion=SNAPSHOT # Java runtime properties javaVersion=11 javaDisplayVersion=11 -jreUrl=https://adoptium.net/en-GB/temurin/releases/?version=4.4.2 +jreUrl=https://adoptium.net/en-GB/temurin/releases/?version=4.4.4 jreName=AdoptOpenJDK diff --git a/release/build.xml b/release/build.xml index bc2aa3a8743..1b89d8c48e5 100644 --- a/release/build.xml +++ b/release/build.xml @@ -106,7 +106,7 @@ - + @@ -118,6 +118,7 @@ + diff --git a/release/pom.xml b/release/pom.xml index 2b6937d65a4..44514cb837d 100644 --- a/release/pom.xml +++ b/release/pom.xml @@ -7,13 +7,13 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT gn-release pom - Release module - Use to create distribution packages. + GeoNetwork Release module + Use to create zip distribution packages (copies from files from web which must be built first). @@ -23,6 +23,35 @@ + + + ${project.groupId} + gn-web-app + ${project.version} + war + + + + + + maven-clean-plugin + + + reset + clean + + + + jetty + + + + + + + + + @@ -40,8 +69,9 @@ download-maven-plugin - download-jetty + jetty-download pre-integration-test + wget @@ -57,7 +87,9 @@ maven-antrun-plugin - install + jetty-rename + pre-integration-test + run @@ -91,6 +123,7 @@ post-integration-test + run diff --git a/schemas-test/pom.xml b/schemas-test/pom.xml index e138b2883ab..4a5030a5f39 100644 --- a/schemas-test/pom.xml +++ b/schemas-test/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 jar diff --git a/schemas/config-editor.xsd b/schemas/config-editor.xsd index 0c8511f7ab1..a6ecd0ed338 100644 --- a/schemas/config-editor.xsd +++ b/schemas/config-editor.xsd @@ -903,6 +903,7 @@ Define if this tab is the default one for the view. Only one tab should be the d diff --git a/schemas/csw-record/pom.xml b/schemas/csw-record/pom.xml index 8fbe3b13e00..c687b740dab 100644 --- a/schemas/csw-record/pom.xml +++ b/schemas/csw-record/pom.xml @@ -5,7 +5,7 @@ gn-schemas org.geonetwork-opensource.schemas - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 gn-schema-csw-record diff --git a/schemas/dublin-core/pom.xml b/schemas/dublin-core/pom.xml index 3d7c16fee4b..2ef0f9b6309 100644 --- a/schemas/dublin-core/pom.xml +++ b/schemas/dublin-core/pom.xml @@ -5,7 +5,7 @@ gn-schemas org.geonetwork-opensource.schemas - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -46,6 +46,28 @@ + + maven-antrun-plugin + + + sample-data + compile + + + + + + + + + run + + + + maven-assembly-plugin diff --git a/schemas/dublin-core/src/assembly/schema-plugin.xml b/schemas/dublin-core/src/assembly/schema-plugin.xml index 60537d198c0..081cffedf15 100644 --- a/schemas/dublin-core/src/assembly/schema-plugin.xml +++ b/schemas/dublin-core/src/assembly/schema-plugin.xml @@ -11,6 +11,17 @@ src/main/plugin/ true + + dublin-core/sample-data/** + + + + target/sample-data + dublin-core/sample-data + true + + *.mef + diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json b/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json index d9b986c378c..abfae192cdc 100644 --- a/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json +++ b/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json @@ -12,6 +12,20 @@ "fields": { "url": {} } + }], + "associatedResourcesTypes": [{ + "type": "parent", + "label": "linkToParent", + "config": { + "sources": { + "metadataStore": { + "params": { + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": true} + } + } }] } } diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/index-fields/index.xsl b/schemas/dublin-core/src/main/plugin/dublin-core/index-fields/index.xsl index 2f87019176e..ad96831b557 100644 --- a/schemas/dublin-core/src/main/plugin/dublin-core/index-fields/index.xsl +++ b/schemas/dublin-core/src/main/plugin/dublin-core/index-fields/index.xsl @@ -71,7 +71,7 @@ - + - + - + - + - + - + @@ -187,7 +187,7 @@ - "default": + "default": @@ -206,7 +206,7 @@ - "default": + "default": @@ -227,8 +227,8 @@ { "protocol":"", - "urlObject":{"default": ""}, - "nameObject":{"default": ""}, + "urlObject":{"default": ""}, + "nameObject":{"default": ""}, "descriptionObject":{"default": ""}, "function": "" } diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml b/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml index 9a234c424ad..ff7aa5b8ef2 100644 --- a/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml +++ b/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml @@ -54,8 +54,20 @@ - + + + + + +

@@ -68,8 +80,20 @@ - + + + + + +
@@ -79,8 +103,21 @@ - + + + + + + + diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/process/thumbnail-add.xsl b/schemas/dublin-core/src/main/plugin/dublin-core/process/thumbnail-add.xsl new file mode 100644 index 00000000000..b430b19e703 --- /dev/null +++ b/schemas/dublin-core/src/main/plugin/dublin-core/process/thumbnail-add.xsl @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/dublin-core.mef b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/dublin-core.mef deleted file mode 100644 index 3024aaccda4..00000000000 Binary files a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/dublin-core.mef and /dev/null differ diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/info.xml b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/info.xml new file mode 100644 index 00000000000..e4f944dccd7 --- /dev/null +++ b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/info.xml @@ -0,0 +1,20 @@ + + + + 2005-03-31T19:13:30 + 2005-03-31T19:13:30 + dublin-core + false + full + + + + + + + + + + + + diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/metadata.xml b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/metadata.xml new file mode 100644 index 00000000000..0b53761a20a --- /dev/null +++ b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/metadata.xml @@ -0,0 +1,21 @@ + + + + Natural polar ecosystems + GeoNetwork test user + Antarctic ecosystem + Arctic ecosystem + polar ecosystem + A harmonised database of natural ecosystems in the Circumpolar Arctic, based on published vegetation maps. + GeoNetwork test centre + Unknown + 2000 + Maps and graphics + Web page + 01f8da31-10d7-11da-b569-000f1f1a7b03 + Compiled from published vegetation maps of the 8 circumpolar countries. + en + Unknown + North 90, South -90, East 180, West -180. (Global) + Access constraints: None. Usage constraints: No restrictions + \ No newline at end of file diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/private/.keepme b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/private/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/public/.keepme b/schemas/dublin-core/src/main/plugin/dublin-core/sample-data/natural_polar_ecosystems/public/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19110/pom.xml b/schemas/iso19110/pom.xml index 123311d9b92..f529f2f67dc 100644 --- a/schemas/iso19110/pom.xml +++ b/schemas/iso19110/pom.xml @@ -28,7 +28,7 @@ gn-schemas org.geonetwork-opensource.schemas - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json b/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json index c4e7c09595a..53bfe374f1f 100644 --- a/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json +++ b/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json @@ -1,3 +1,8 @@ { - "config": {} + "config": { + "associatedResourcesTypes": [ { + "type": "hasfeaturecats", + "allowToAddRelation": false + }] + } } diff --git a/schemas/iso19110/src/main/plugin/iso19110/index-fields/index.xsl b/schemas/iso19110/src/main/plugin/iso19110/index-fields/index.xsl index 1706f2d20bc..c2d2e3913d3 100644 --- a/schemas/iso19110/src/main/plugin/iso19110/index-fields/index.xsl +++ b/schemas/iso19110/src/main/plugin/iso19110/index-fields/index.xsl @@ -50,7 +50,7 @@ [ { - "typeName" : "", - "definition" :"", - "code" :"", + "typeName" : "", + "definition" :"", + "code" :"", "isAbstract" :"", - "aliases" : "" + "aliases" : "" + + + + + + + run + + + + + maven-assembly-plugin diff --git a/schemas/iso19115-3.2018/src/assembly/schema-plugin.xml b/schemas/iso19115-3.2018/src/assembly/schema-plugin.xml index 60537d198c0..5783f8780f9 100644 --- a/schemas/iso19115-3.2018/src/assembly/schema-plugin.xml +++ b/schemas/iso19115-3.2018/src/assembly/schema-plugin.xml @@ -11,6 +11,17 @@ src/main/plugin/ true + + iso19115-3.2018/sample-data/** + + + + target/sample-data + iso19115-3.2018/sample-data + true + + *.mef + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json index c15c53fc2b5..b087ecb8ee4 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json @@ -3,7 +3,7 @@ "display": "select", "types": [ { - "group": "onlineDiscover", + "group": "overview", "label": "onlineDiscoverThumbnail", "sources": { "filestore": true, @@ -15,38 +15,44 @@ "fields": { "url": { "isMultilingual": false, - "param": "thumbnail_url" + "param": "thumbnail_url", + "label": "overviewUrl" }, "name": { + "label": "overviewDescription", "param": "thumbnail_desc" } } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverInApp", - "copyLabel": "name", + "group": "API", + "label": "onlineDiscoverWMS", + "copyLabel": "desc", + "icon": "fa gn-icon-onlinesrc", "sources": { "metadataStore": { - "label": "searchAnApplication", + "label": "searchAservice", "params": { - "resourceType": "application", + "serviceType": ["OGC:WMS", "WMS", "view"], "isTemplate": "n" } } }, - "icon": "fa gn-icon-map", "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { - "value": "WWW:LINK", + "value": "OGC:WMS", "hidden": true, "isMultilingual": false }, - "name": {}, + "name": { + "label": "layerName", + "tooltip": "layerNameTooltip" + }, "desc": {}, "function": { "value": "browsing", @@ -56,15 +62,15 @@ } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverWMS", + "group": "API", + "label": "onlineDiscoverINSPIREView", "copyLabel": "desc", "icon": "fa gn-icon-onlinesrc", "sources": { "metadataStore": { "label": "searchAservice", "params": { - "serviceType": ["OGC:WMS", "WMS", "view"], + "serviceType": "view", "isTemplate": "n" } } @@ -72,7 +78,8 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { "value": "OGC:WMS", @@ -85,19 +92,24 @@ "value": "browsing", "hidden": true, "isMultilingual": false + }, + "applicationProfile": { + "value": "inspire-view", + "hidden": true, + "isMultilingual": false } } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverINSPIREView", + "group": "API", + "label": "onlineDiscoverWMTS", "copyLabel": "desc", "icon": "fa gn-icon-onlinesrc", "sources": { "metadataStore": { "label": "searchAservice", "params": { - "serviceType": "view", + "serviceType": ["OGC:WMTS", "WMTS"], "isTemplate": "n" } } @@ -105,10 +117,11 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { - "value": "OGC:WMS", + "value": "OGC:WMTS", "hidden": true, "isMultilingual": false }, @@ -118,24 +131,19 @@ "value": "browsing", "hidden": true, "isMultilingual": false - }, - "applicationProfile": { - "value": "inspire-view", - "hidden": true, - "isMultilingual": false } } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverWMTS", + "group": "API", + "label": "onlineDiscoverArcGIS", "copyLabel": "desc", "icon": "fa gn-icon-onlinesrc", "sources": { "metadataStore": { "label": "searchAservice", "params": { - "serviceType": ["OGC:WMTS", "WMTS"], + "serviceType": "ESRI:REST", "isTemplate": "n" } } @@ -146,7 +154,7 @@ "isMultilingual": false }, "protocol": { - "value": "OGC:WMTS", + "value": "ESRI:REST", "hidden": true, "isMultilingual": false }, @@ -160,8 +168,8 @@ } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverArcGIS", + "group": "API", + "label": "onlineDiscoverArcGISTiled", "copyLabel": "desc", "icon": "fa gn-icon-onlinesrc", "sources": { @@ -179,7 +187,7 @@ "isMultilingual": false }, "protocol": { - "value": "ESRI:REST", + "value": "ESRI:REST-TILED", "hidden": true, "isMultilingual": false }, @@ -193,15 +201,15 @@ } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverArcGISTiled", + "group": "API", + "label": "OGC API Maps", "copyLabel": "desc", "icon": "fa gn-icon-onlinesrc", "sources": { "metadataStore": { "label": "searchAservice", "params": { - "serviceType": "ESRI:REST", + "serviceType": ["OGC API Maps"], "isTemplate": "n" } } @@ -209,10 +217,11 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { - "value": "ESRI:REST-TILED", + "value": "OGC API Maps", "hidden": true, "isMultilingual": false }, @@ -226,20 +235,52 @@ } }, { - "group": "onlineDiscover", - "label": "onlineDiscoverMap", - "copyLabel": "name", + "group": "API", + "label": "OGC API Records", + "copyLabel": "desc", + "icon": "fa gn-icon-onlinesrc", "sources": { - "filestore": true + "metadataStore": { + "label": "searchAservice", + "params": { + "serviceType": ["OGC API Records"], + "isTemplate": "n" + } + } }, - "icon": "fa gn-icon-map", "process": "onlinesrc-add", "fields": { "url": { + "isMultilingual": false, + "label": "ogcServiceUrl" + }, + "protocol": { + "value": "OGC API Records", + "hidden": true, "isMultilingual": false }, + "name": {}, + "desc": {}, + "function": { + "value": "search", + "hidden": true, + "isMultilingual": false + } + } + }, + { + "group": "API", + "label": "REST API", + "copyLabel": "name", + "icon": "fa gn-icon-onlinesrc", + "process": "onlinesrc-add", + "fields": { + "url": { + "isMultilingual": false, + "label": "restApiUrl" + }, "protocol": { - "value": "WWW:LINK", + "value": "REST", "hidden": true, "isMultilingual": false }, @@ -294,7 +335,7 @@ "isMultilingual": false }, "protocol": { - "value": "WWW:LINK", + "value": "WWW:DOWNLOAD", "hidden": true, "isMultilingual": false }, @@ -324,7 +365,8 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { "value": "OGC:WFS", @@ -357,7 +399,8 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { "value": "OGC:WCS", @@ -390,7 +433,8 @@ "process": "onlinesrc-add", "fields": { "url": { - "isMultilingual": false + "isMultilingual": false, + "label": "ogcServiceUrl" }, "protocol": { "value": "OGC:WFS", @@ -449,6 +493,74 @@ } } }, + { + "group": "onlineDownload", + "label": "OGC API Features", + "copyLabel": "desc", + "icon": "fa gn-icon-onlinesrc", + "sources": { + "metadataStore": { + "label": "searchAservice", + "params": { + "serviceType": ["OGC API Features"], + "isTemplate": "n" + } + } + }, + "process": "onlinesrc-add", + "fields": { + "url": { + "isMultilingual": false, + "label": "ogcServiceUrl" + }, + "protocol": { + "value": "OGC API Features", + "hidden": true, + "isMultilingual": false + }, + "name": {}, + "desc": {}, + "function": { + "value": "download", + "hidden": true, + "isMultilingual": false + } + } + }, + { + "group": "onlineDownload", + "label": "OGC API Coverages", + "copyLabel": "desc", + "icon": "fa gn-icon-onlinesrc", + "sources": { + "metadataStore": { + "label": "searchAservice", + "params": { + "serviceType": ["OGC API Coverages"], + "isTemplate": "n" + } + } + }, + "process": "onlinesrc-add", + "fields": { + "url": { + "isMultilingual": false, + "label": "ogcServiceUrl" + }, + "protocol": { + "value": "OGC API Coverages", + "hidden": true, + "isMultilingual": false + }, + "name": {}, + "desc": {}, + "function": { + "value": "download", + "hidden": true, + "isMultilingual": false + } + } + }, { "group": "localNetwork", "label": "localNetworkVectorFile", @@ -562,12 +674,13 @@ } }, { - "group": "onlineUse", + "group": "onlineUseFcats", "label": "onlineUseFcats", "copyLabel": "name", "sources": { "filestore": true }, + "fileStoreFilter": "*.{pdf,PDF,xls,XLS,ods,ODS,csv,CSV,txt,TXT}", "icon": "fa fa-table", "process": "fcats-file-add", "fields": { @@ -588,12 +701,13 @@ } }, { - "group": "onlineUse", + "group": "quality", "label": "onlineUseDQReport", "copyLabel": "name", "sources": { "filestore": true }, + "fileStoreFilter": "*.{pdf,PDF}", "icon": "fa fa-table", "process": "dq-report-add", "fields": { @@ -620,12 +734,13 @@ } }, { - "group": "onlineUse", + "group": "quality", "label": "onlineUseDQTOR", "copyLabel": "name", "sources": { "filestore": true }, + "fileStoreFilter": "*.{pdf,PDF}", "icon": "fa fa-table", "process": "dq-report-add", "fields": { @@ -652,12 +767,13 @@ } }, { - "group": "onlineUse", + "group": "quality", "label": "onlineUseDQProdReport", "copyLabel": "name", "sources": { "filestore": true }, + "fileStoreFilter": "*.{pdf,PDF}", "icon": "fa fa-table", "process": "dq-report-add", "fields": { @@ -684,7 +800,7 @@ } }, { - "group": "onlineUse", + "group": "onlineUseLegend", "label": "onlineUseLegendLYR", "copyLabel": "name", "sources": { @@ -711,7 +827,7 @@ } }, { - "group": "onlineUse", + "group": "onlineUseLegend", "label": "onlineUseStyleSLD", "copyLabel": "name", "sources": { @@ -738,7 +854,7 @@ } }, { - "group": "onlineUse", + "group": "onlineUseLegend", "label": "onlineUseStyleQML", "copyLabel": "name", "sources": { @@ -765,7 +881,67 @@ } }, { - "group": "onlineMore", + "group": "links", + "label": "onlineDiscoverInApp", + "copyLabel": "name", + "sources": { + "metadataStore": { + "label": "searchAnApplication", + "params": { + "resourceType": "application", + "isTemplate": "n" + } + } + }, + "icon": "fa gn-icon-map", + "process": "onlinesrc-add", + "fields": { + "url": { + "isMultilingual": false + }, + "protocol": { + "value": "WWW:LINK", + "hidden": true, + "isMultilingual": false + }, + "name": {}, + "desc": {}, + "function": { + "value": "browsing", + "hidden": true, + "isMultilingual": false + } + } + }, + { + "group": "links", + "label": "onlineDiscoverMap", + "copyLabel": "name", + "sources": { + "filestore": true + }, + "icon": "fa gn-icon-map", + "process": "onlinesrc-add", + "fields": { + "url": { + "isMultilingual": false + }, + "protocol": { + "value": "WWW:LINK", + "hidden": true, + "isMultilingual": false + }, + "name": {}, + "desc": {}, + "function": { + "value": "browsing", + "hidden": true, + "isMultilingual": false + } + } + }, + { + "group": "links", "label": "onlineMoreWWW", "copyLabel": "name", "icon": "fa gn-icon-onlinesrc", @@ -789,7 +965,7 @@ } }, { - "group": "onlineMore", + "group": "links", "label": "onlineMoreFile", "copyLabel": "name", "sources": { @@ -815,6 +991,81 @@ } } } - ] + ], + "associatedResourcesTypes": [{ + "type": "siblings", + "label": "linkToSibling", + "config": { + "sources": { + "metadataStore": { + "params": { + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": true} + } + } + }, { + "type": "service", + "label": "linkToService", + "condition": "!gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "label": "searchAservice", + "params": { + "resourceType": ["service"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "dataset", + "label": "linkToDataset", + "condition": "gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "source", + "label": "linkToSource", + "config": { + "sources": { + "metadataStore": { + "label": "linkToSource", + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "fcats", + "label": "linkToFeatureCatalog", + "config": { + "sources": { + "metadataStore": { + "label": "linkToFeatureCatalog", + "params": { + "resourceType": ["featureCatalog"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }] } } diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/citation/base.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/citation/base.xsl index eac4257ad87..927d7c6e20b 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/citation/base.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/citation/base.xsl @@ -92,7 +92,7 @@ ('publication', 'revision')]/ cit:date/gco:*[. != '']"/> - + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/jsonld/iso19115-3.2018-to-jsonld.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/jsonld/iso19115-3.2018-to-jsonld.xsl index ab9ac906295..47ae498adc4 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/jsonld/iso19115-3.2018-to-jsonld.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/jsonld/iso19115-3.2018-to-jsonld.xsl @@ -305,9 +305,9 @@ { "@type":"DataDownload", - "contentUrl": "" + "contentUrl": "" , - "encodingFormat": "" + "encodingFormat": "" , "name": @@ -426,7 +426,7 @@ select="$metadata/gmd:locale/*[concat('#', @id) = $languageId]/gmd:languageCode/*/@codeListValue"/> { , diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/xsl-view/view.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/xsl-view/view.xsl index abb49d93770..62a7f60fd03 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/xsl-view/view.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/formatter/xsl-view/view.xsl @@ -1180,7 +1180,10 @@ -   + +   diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index-subtemplate.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index-subtemplate.xsl index f27d590d5bb..15ccf364926 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index-subtemplate.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/index-fields/index-subtemplate.xsl @@ -113,13 +113,13 @@ then concat(' (', $contactInfo, ')') else ''"/> { - "default": "" ,"lang": "": "" } @@ -128,7 +128,7 @@ {"common": ""} + select="util:escapeForJson(normalize-space(.))"/>"} { - "default": "" + "default": "" ,"lang": "" + ]/@value"/>": "" } @@ -168,7 +168,7 @@ { - "default": "" } @@ -191,9 +191,9 @@ { "default": "" } @@ -217,7 +217,7 @@ select="string-join(mco:useLimitations/*/text(), ', ')"/> { - "default": " { - "default": "" @@ -250,7 +250,7 @@ {"common": ""} + select="util:escapeForJson(normalize-space(.))"/>"} 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 0cc9d491cb8..1ef4f780a95 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 @@ -338,7 +338,16 @@ - Warning / Date with value '' was not a valid date format. + + { + "string": "indexingErrorMsg-invalidDateFormat", + "type": "warning", + "values": { + "dateType": "", + "date": "" + } + } + @@ -557,7 +566,7 @@ select="gn-fn-index:build-thesaurus-index-field-name($thesaurusId, $thesaurusTitle)"/> + select="current-group()/mri:keyword[*/normalize-space() != '']"/> - Warning / Keyword not found in . + + { + "string": "indexingErrorMsg-keywordNotFoundInThesaurus", + "type": "warning", + "values": { + "keyword": "", + "thesaurus": "" + } + } + @@ -674,7 +692,9 @@ - + @@ -840,14 +860,29 @@ } - Warning / Field resourceTemporalDateRange / Lower and upper bounds empty. Date range not indexed. + + { + "string": "indexingErrorMsg-invalidBounds", + "type": "warning", + "values": { } + } + - Warning / Field resourceTemporalDateRange / Lower range bound '' can not be greater than upper bound ''. + + { + "string": "indexingErrorMsg-temporalRangeLowerGreaterThanUpper", + "type": "warning", + "values": { + "lowerBound": "", + "upperBound": "" + } + } + @@ -869,6 +904,7 @@ ,"lte": + ,"unit": "m" } @@ -920,10 +956,10 @@ { - "code": "", - "codeSpace": "", - "name": "", - "url": "" + "code": "", + "codeSpace": "", + "name": "", + "url": "" } @@ -954,7 +990,7 @@ { - "title": "", + "title": "", "date": "", @@ -962,7 +998,7 @@ "link": "", - "explanation": "", + "explanation": "", "pass": "" } @@ -1002,11 +1038,11 @@ [ { - "typeName" : "", - "definition" :"", - "code" :"", + "typeName" : "", + "definition" :"", + "code" :"", "isAbstract" :"", - "aliases" : "" + "aliases" : "" - {"name": "", - "definition": "", - "code": "", + {"name": "", + "definition": "", + "code": "", "link": "", "type": "" - ,"cardinality": "" + ,"cardinality": "" ,"values": [ { - "label": "", - "code": "", - "definition": ""} + "label": "", + "code": "", + "definition": ""} , ] @@ -1069,9 +1105,9 @@ { - "protocol": "", - "function": "", "urlObject": "nilReason": "", - "applicationProfile": "" } @@ -1147,7 +1183,7 @@ 'description', .//cit:CI_Organisation/cit:name, $allLanguages, true())"/> - ,"individual":"" + ,"individual":"" } , @@ -1172,12 +1208,12 @@ + ]/mdq:result/(mdq:DQ_QuantitativeResult|mdq:DQ_DescriptiveResult)"> + select="mdq:value/gco:Record[. != '']|mdq:statement/gco:CharacterString[. != '']"/> { - "name": "", + "name": "", - "description": "", + "description": "", - "date": "", + "date": "", - "value": "", + "value": "", - "unit": "", + "unit": "", "type": "" } - + @@ -1249,11 +1285,11 @@ { - "protocol":"", + "protocol":"", "mimeType":"", "urlObject": ", "function":"", - "applicationProfile":"", + "applicationProfile":"", "group": } @@ -1304,7 +1340,7 @@ + {"name": "children", "parent": ""}--> @@ -1351,18 +1387,6 @@ - - - - - - - @@ -1430,13 +1454,13 @@ 'organisation', $organisationName, $languages, true())"/>, "role":"", - "email":"", - "website":"", - "logo":"", - "individual":"", - "position":"", - "phone":"", - "address":"" + "email":"", + "website":"", + "logo":"", + "individual":"", + "position":"", + "phone":"", + "address":"" ,"nilReason": "" @@ -1444,7 +1468,7 @@ ,"identifiers":[ { - "code": "", + "code": "", "codeSpace": "", "link": "" } @@ -1549,7 +1573,7 @@ - + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml index 2fd90f1191c..8025779fe40 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml @@ -621,11 +621,37 @@ - + - + - - + + + + + + + + + @@ -800,13 +861,48 @@ - - + + + + + + + + + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/layout-custom-fields-date.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/layout-custom-fields-date.xsl index fe766297a92..a261761b187 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/layout-custom-fields-date.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/layout-custom-fields-date.xsl @@ -46,10 +46,12 @@ + + @@ -64,9 +66,20 @@ match="cit:CI_Date/cit:date[../cit:dateType]"> - - + + + + + + + + + + + + + + @@ -104,7 +117,8 @@
+ data-element-ref="{concat('_X', gn:element/@ref)}" + data-hide-time="{if ($viewConfig/@hideTimeInCalendar = 'true') then 'true' else 'false'}">
@@ -156,7 +170,8 @@
+ data-element-ref="{concat('_X', gn:element/@ref)}" + data-hide-time="{if ($viewConfig/@hideTimeInCalendar = 'true') then 'true' else 'false'}">
diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/utility-tpl.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/utility-tpl.xsl index 59a5bfde479..2022184aca4 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/utility-tpl.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/utility-tpl.xsl @@ -19,6 +19,7 @@ xmlns:gco="http://standards.iso.org/iso/19115/-3/gco/1.0" xmlns:gfc="http://standards.iso.org/iso/19110/gfc/1.1" xmlns:gn-fn-index="http://geonetwork-opensource.org/xsl/functions/index" + xmlns:util="java:org.fao.geonet.util.XslUtil" xmlns:gn="http://www.fao.org/geonetwork" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all"> @@ -40,8 +41,8 @@ [ { - "value": "WWW:DOWNLOAD:", - "label": ""} + "value": "WWW:DOWNLOAD:", + "label": ""} , ] diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/loc/fre/codelists.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/loc/fre/codelists.xml index bcedeee9a80..ee71f0eab4f 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/loc/fre/codelists.xml +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/loc/fre/codelists.xml @@ -2963,7 +2963,7 @@ now - + unknown diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/add-info-from-wms.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/add-info-from-wms.xsl index 5a585fba608..81b658f1007 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/add-info-from-wms.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/add-info-from-wms.xsl @@ -62,14 +62,10 @@ WMS service - is described in online resource section. Run to update extent, CRS or - graphic overview - for this WMS service for the layer named: + is described in online resource section. Run to update extent, CRS or graphic overview for this WMS service for the layer named: Le service de visualisation - est décrit dans la section resource en ligne. Exécuter cette action - pour mettre à jour l'étendue, les systèmes de projection - ou les aperçus pour ce service et la couche nommée : + est décrit dans la section resource en ligne. Exécuter cette action pour mettre à jour l'étendue, les systèmes de projection ou les aperçus pour ce service et la couche nommée : Er is een verwijzing gevonden naar de WMS service . Gebruik deze functie om de dekking, de projectie of thumbnail af te leiden of bij te werken vanuit deze WMS-service voor de laag met de naam: @@ -87,6 +83,7 @@ + @@ -100,8 +97,7 @@ - + select="//cit:CI_OnlineResource[contains(cit:protocol/gco:CharacterString, 'OGC:WMS') and normalize-space(cit:linkage/*[1]/text()) = $wmsServiceUrl]"/> @@ -132,13 +128,15 @@ + + target="link#{cit:protocol/gco:CharacterString}#{$url}#{$layerName}"> . + select="$layerName"/>. true { @@ -157,7 +155,9 @@ "defaultValue":""}, "wmsServiceUrl":{"type":"string", "defaultValue":""} + select="$url"/>"}, + "wmsLayerName":{"type":"string", "defaultValue":""} } @@ -211,42 +211,43 @@ + - - - - + + + + + + - - - + + + + + + + + - - - - - - - - - @@ -254,16 +255,16 @@ + and $wmsLayerName != ''"> + select="$capabilitiesDoc//Layer[Name=$wmsLayerName]/LatLonBoundingBox"/> @@ -412,8 +413,6 @@ - - @@ -426,8 +425,20 @@ - + + + + + + @@ -436,8 +447,6 @@ - - diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/create-featurecatalogue-from-wfs.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/create-featurecatalogue-from-wfs.xsl index 8c4f3dfa9a2..bc2f6cfd34b 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/create-featurecatalogue-from-wfs.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/process/create-featurecatalogue-from-wfs.xsl @@ -64,21 +64,23 @@ + select="$root[count(.//srv:SV_ServiceIdentification) = 0]//mdb:distributionInfo//mrd:onLine/*[contains(cit:protocol/*, 'WFS') or contains(cit:protocol/*, 'ESRI:REST')]"/> + + + - + target="link#{$protocol}#{$url[1]}#{$featureType[1]}"> + true - {"featureCatWfsUrl":{"type":"text", "defaultValue":""}, - "featureCatWfsFeatureType":{"type":"text", "defaultValue":""}, + {"featureCatWfsUrl":{"type":"text", "defaultValue":""}, + "featureCatWfsFeatureType":{"type":"text", "defaultValue":""}, "featureCatReplace":{"type":"boolean", "defaultValue":""}} @@ -109,7 +111,6 @@ mdb:defaultLocale| mdb:parentMetadata| mdb:metadataScope| - mdb:contact| mdb:dateInfo| mdb:metadataStandard| mdb:metadataProfile| diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/0dbac056-cd5a-4c9c-bbec-564e47c4507c.zip b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/0dbac056-cd5a-4c9c-bbec-564e47c4507c.zip deleted file mode 100644 index e2fbde2094f..00000000000 Binary files a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/0dbac056-cd5a-4c9c-bbec-564e47c4507c.zip and /dev/null differ diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/info.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/info.xml new file mode 100644 index 00000000000..76d3d431b08 --- /dev/null +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/info.xml @@ -0,0 +1,28 @@ + + + + 2015-05-04T16:34:08 + 2019-12-09T16:45:53 + iso19115-3.2018 + n + 1772 + full + 0 + 0 + 0dbac056-cd5a-4c9c-bbec-564e47c4507c + + + + + + + + + + + + + + + + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/metadata.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/metadata.xml new file mode 100644 index 00000000000..e636ba33c17 --- /dev/null +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/metadata.xml @@ -0,0 +1,471 @@ + + + + + + 0dbac056-cd5a-4c9c-bbec-564e47c4507c + + + urn:uuid + + + + + + + + + + + + + + + + + + + + Service + + + + + + + + + + + + Metadata point of contact + + + + + + + helpdesk@my.org + + + + + + + + + + + + + 2019-12-09T16:45:52 + + + + + + + + + + 2019-04-02T12:31:35 + + + + + + + + + + ISO 19115-3 + + + + + + + + + Discovery service for my catalogue (Sample record, please remove!) + + + + + 2015-01-30 + + + + + + + + + + 2015-05-05 + + + + + + + + + + +-- Customize this sample record to describe the Catalogue Service Web (CSW) service of your catalogue --. + +See https://docs.geonetwork-opensource.org/4.4/administrator-guide/configuring-the-catalog/csw-configuration/ + +Access datasets, maps and services from my organisation using the OGC CSW standards. + +This catalogue contains descriptions of GIS resources on the ... region produced and/or maintained by .... + + + + + + + + + + Geo Helpdesk + + + + + + + helpdesk@my.org + + + + + + + + + + + + + ... + + + + + 2.75 + + + 6.50 + + + 49.45 + + + 50.85 + + + + + + + + + infoCatalogueService + + + + + + + + INSPIRE Service taxonomy + + + + + 2010-04-22 + + + + + + + + + + geonetwork.thesaurus.external.theme.inspire-service-taxonomy + + + + + + + + + + + Human health and safety + + + Protected sites + + + + + + + + GEMET - INSPIRE themes, version 1.0 + + + + + 2008-06-01 + + + + + + + + + + geonetwork.thesaurus.external.theme.httpinspireeceuropaeutheme-theme + + + + + + + + + + + Regional + + + + + + + + Spatial scope + + + + + 2019-07-02 + + + + + + + + + + geonetwork.thesaurus.external.theme.httpinspireeceuropaeumetadatacodelistSpatialScope-SpatialScope + + + + + + + + + + + + + + No limitations to public access + + + + + + + Specific use limitation + + + + + + Describe here any use limitation. + + + + + discovery + + + + + + + + GetCapabilities + + + + + + + + http://localhost:8080/geonetwork/srv/eng/csw + + + OGC:CSW + + + + + + + + + + + + + + + + + + + + My organisation + + + + + + + helpdesk@my.org + + + + + + + + + + + + + No restriction. + + + + + + + + + + + http://localhost:8080/geonetwork/srv/eng/csw?SERVICE=CSW&VERSION=2.0.2&REQUEST=GetCapabilities + + + OGC:CSW + + + CSW service URL + + + + + + + + + + + + + + + + + + + + + + + + Service + + + + + + + + + + + + + Commission Regulation (EC) No 976/2009 of 19 October 2009 implementing Directive 2007/2/EC of the European Parliament and of the Council as regards the Network Services + + + + + 2009-10-19 + + + + + + + + + + See the referenced specification + + + + + + + + + + + + + + COMMISSION REGULATION (EU) No 1089/2010 of 23 November 2010 implementing Directive 2007/2/EC of the European Parliament and of the Council as regards interoperability of spatial data sets and services + + + + + 2010-12-08 + + + + + + + + + + See the referenced specification + + + true + + + + + + + + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/metadata/.keepme b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/metadata/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/private/.keepme b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/private/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/public/.keepme b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/sample-data/service_record/public/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml index a012a32fbf8..a0a2c3cc1a4 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/schema-ident.xml @@ -149,6 +149,7 @@ Ce schéma est également composé des normes : */cit:protocol/gco:CharacterString = 'FILE' or */cit:protocol/gco:CharacterString = 'DB' or */cit:protocol/gco:CharacterString = 'COPYFILE']" + jsonpath="$['link'][?(@.protocol =~ /(DOWNLOAD|FILE|DB|COPYFILE).*?/i)]" ifNotOperation="download"/> diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/update-fixed-info.xsl b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/update-fixed-info.xsl index e5aa0478dab..cdefac96038 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/update-fixed-info.xsl +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/update-fixed-info.xsl @@ -124,14 +124,13 @@ as="xs:boolean"/> - + - + diff --git a/schemas/iso19139/pom.xml b/schemas/iso19139/pom.xml index cfbe8185c0a..4d63ef8ffce 100644 --- a/schemas/iso19139/pom.xml +++ b/schemas/iso19139/pom.xml @@ -5,7 +5,7 @@ gn-schemas org.geonetwork-opensource.schemas - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -57,6 +57,45 @@ + + + maven-antrun-plugin + + + sample-data + compile + + + + + + + + + + + + + run + + + + maven-assembly-plugin diff --git a/schemas/iso19139/src/assembly/schema-plugin.xml b/schemas/iso19139/src/assembly/schema-plugin.xml index 60537d198c0..80aecf42159 100644 --- a/schemas/iso19139/src/assembly/schema-plugin.xml +++ b/schemas/iso19139/src/assembly/schema-plugin.xml @@ -11,6 +11,17 @@ src/main/plugin/ true + + iso19139/sample-data/** + + + + target/sample-data + iso19139/sample-data + true + + *.mef + diff --git a/schemas/iso19139/src/main/plugin/iso19139/config/associated-panel/default.json b/schemas/iso19139/src/main/plugin/iso19139/config/associated-panel/default.json index 461ee01d5aa..c07bf8ead62 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/config/associated-panel/default.json +++ b/schemas/iso19139/src/main/plugin/iso19139/config/associated-panel/default.json @@ -52,6 +52,97 @@ "multilingualFields": ["name", "desc"], "wmsResources": { "addLayerNamesMode": "resourcename" + }, + "associatedResourcesTypes": [{ + "type": "parent", + "label": "linkToParent", + "config": { + "sources": { + "metadataStore": { + "label": "linkToParent", + "params": { + "resourceType": ["series"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "service", + "label": "linkToService", + "condition": "!gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "label": "searchAservice", + "params": { + "resourceType": ["service"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "dataset", + "label": "linkToDataset", + "condition": "gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "source", + "label": "linkToSource", + "config": { + "sources": { + "metadataStore": { + "label": "linkToSource", + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "fcats", + "label": "linkToFeatureCatalog", + "config": { + "sources": { + "metadataStore": { + "label": "linkToFeatureCatalog", + "params": { + "resourceType": ["featureCatalog"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "siblings", + "label": "linkToSibling", + "config": { + "sources": { + "metadataStore": { + "params": { + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": true} + } + } } + ] } } diff --git a/schemas/iso19139/src/main/plugin/iso19139/formatter/citation/base.xsl b/schemas/iso19139/src/main/plugin/iso19139/formatter/citation/base.xsl index 923ef672057..9901fc1028e 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/formatter/citation/base.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/formatter/citation/base.xsl @@ -91,7 +91,7 @@ ('publication', 'revision')]/ gmd:date/gco:*[. != '']"/> - + diff --git a/schemas/iso19139/src/main/plugin/iso19139/formatter/jsonld/iso19139-to-jsonld.xsl b/schemas/iso19139/src/main/plugin/iso19139/formatter/jsonld/iso19139-to-jsonld.xsl index 310d01c6bbf..20e654c1f72 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/formatter/jsonld/iso19139-to-jsonld.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/formatter/jsonld/iso19139-to-jsonld.xsl @@ -248,9 +248,9 @@ { "@type":"DataDownload", - "contentUrl":"" + "contentUrl":"" , - "encodingFormat":"" + "encodingFormat":"" , "name": { , diff --git a/schemas/iso19139/src/main/plugin/iso19139/formatter/xsl-view/view.xsl b/schemas/iso19139/src/main/plugin/iso19139/formatter/xsl-view/view.xsl index 3f13daa000f..fdb57470c2f 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/formatter/xsl-view/view.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/formatter/xsl-view/view.xsl @@ -1151,7 +1151,10 @@ -   + +   diff --git a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index-subtemplate.xsl b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index-subtemplate.xsl index d63dd1ce1a2..7c27ca271ed 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index-subtemplate.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index-subtemplate.xsl @@ -90,12 +90,12 @@ then concat(' (', $contactInfo, ')') else ''"/> { - "default": "" ,"lang": "": "" } @@ -122,11 +122,11 @@ |gmd:description/gmd:PT_FreeText/*/gmd:LocalisedCharacterString[. != ''] )[1])"/> { - "default": "" + "default": "" ,"lang": "" + ]/@value"/>": "" } @@ -136,7 +136,7 @@ select="concat('S:', .//gmd:southBoundLatitude/*/text(), ', W:', .//gmd:westBoundLongitude/*/text(), ', N:', .//gmd:northBoundLatitude/*/text(), ', E:',.//gmd:eastBoundLongitude/*/text())"/> { - "default": "" + "default": "" } @@ -153,7 +153,7 @@ then gmd:name/gco:CharacterString else concat(gmd:name/gco:CharacterString, ' ', gmd:version/gco:CharacterString)"/> { - "default": "" + "default": "" } @@ -168,7 +168,7 @@ string-join(gmd:MD_LegalConstraints/gmd:otherConstraints/*/text(), ', '))"/> { - "default": "" + "default": "" } @@ -181,7 +181,7 @@ { - "default": "" + "default": "" } 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 c7d7c857fcc..4e10fb7d56f 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/index-fields/index.xsl @@ -297,7 +297,16 @@ - Warning / Date with value '' was not a valid date format. + + { + "string": "indexingErrorMsg-invalidDateFormat", + "type": "warning", + "values": { + "dateType": "", + "date": "" + } + } + @@ -335,7 +344,7 @@ { - "code": "", + "code": "", "codeSpace": "", "link": "" } @@ -507,7 +516,7 @@ select="gn-fn-index:build-thesaurus-index-field-name($thesaurusId, $thesaurusTitle)"/> + select="current-group()/gmd:keyword[*/normalize-space() != '']"/> - Warning / Keyword not found in . + + { + "string": "indexingErrorMsg-keywordNotFoundInThesaurus", + "type": "warning", + "values": { + "keyword": "", + "thesaurus": "" + } + } + @@ -623,7 +641,9 @@ - + @@ -787,14 +807,29 @@ } - Warning / Field resourceTemporalDateRange / Lower and upper bounds empty or not valid dates. Date range not indexed. + + { + "string": "indexingErrorMsg-invalidBounds", + "type": "warning", + "values": { } + } + - Warning / Field resourceTemporalDateRange / Lower range bound '' can not be greater than upper bound ''. + + { + "string": "indexingErrorMsg-temporalRangeLowerGreaterThanUpper", + "type": "warning", + "values": { + "lowerBound": "", + "upperBound": "" + } + } + @@ -866,10 +901,10 @@ { - "code": "", - "codeSpace": "", - "name": "", - "url": "" + "code": "", + "codeSpace": "", + "name": "", + "url": "" } @@ -899,7 +934,7 @@ { - "title": "", + "title": "", "date": "", @@ -907,7 +942,7 @@ "link": "", - "explanation": "", + "explanation": "", "pass": "" } @@ -934,7 +969,7 @@ then 'remote' else 'catalog'"/>", "to": "", - "title": "", + "title": "", "url": "" } @@ -957,7 +992,7 @@ then 'remote' else 'catalog'"/>", "to": "", - "title": "", + "title": "", "url": "" } @@ -977,19 +1012,19 @@ + select="(../../gmd:dateTime/gco:DateTime)[1]"/> { - "name": "", + "name": "", - "description": "", + "description": "", - "date": "", + "date": "", - "value": "", + "value": "", - "unit": "", + "unit": "", "type": "" } @@ -1035,7 +1070,7 @@ select="gn-fn-index:add-multilingual-field( 'description', gmd:organisationName, $allLanguages, true())"/> - ,"individual":"" + ,"individual":"" } , @@ -1088,7 +1123,7 @@ + select="util:escapeForJson((gmd:name/*/text())[1])"/> @@ -1105,13 +1140,15 @@ { - "protocol":"", + "protocol":"", "mimeType":"", - "urlObject":{"default": ""}, + "urlObject":{ + "default": "", + "lang": ""}, "nameObject": , @@ -1124,7 +1161,7 @@ "nilReason": "", "function":"", - "applicationProfile":"", + "applicationProfile":"", "group": } - + @@ -1207,17 +1244,6 @@ - - - - - - - @@ -1278,13 +1304,13 @@ 'organisation', $organisationName, $languages, true())"/>, "role":"", - "email":"", - "website":"", - "logo":"", - "individual":"", - "position":"", - "phone":"", - "address":"" + "email":"", + "website":"", + "logo":"", + "individual":"", + "position":"", + "phone":"", + "address":"" ,"nilReason": "" @@ -1306,7 +1332,7 @@ + regex=".*[i|I][d|D]=([a-zA-Z0-9\-\.\{{\}}]*).*"> @@ -1384,7 +1410,7 @@ - + diff --git a/schemas/iso19139/src/main/plugin/iso19139/layout/config-editor.xml b/schemas/iso19139/src/main/plugin/iso19139/layout/config-editor.xml index 36f1b0be42b..f37d3ba1d75 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/layout/config-editor.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/layout/config-editor.xml @@ -518,12 +518,36 @@ + - + + + + + + + + @@ -3183,24 +3207,36 @@ --> + + - - - + + + - - - @@ -3305,12 +3341,36 @@ class="gn-label-above-input gn-indent-bluescale"> + - + + + + + + + + @@ -3381,12 +3441,36 @@ + - + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/layout/utility-tpl.xsl b/schemas/iso19139/src/main/plugin/iso19139/layout/utility-tpl.xsl index 02b3303886d..6eac79b2bf8 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/layout/utility-tpl.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/layout/utility-tpl.xsl @@ -29,6 +29,7 @@ xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:gn-fn-index="http://geonetwork-opensource.org/xsl/functions/index" xmlns:gn="http://www.fao.org/geonetwork" + xmlns:util="java:org.fao.geonet.util.XslUtil" version="2.0" exclude-result-prefixes="#all"> @@ -47,8 +48,8 @@ [ { - "value": "WWW:DOWNLOAD:", - "label": ""} + "value": "WWW:DOWNLOAD:", + "label": ""} , ] diff --git a/schemas/iso19139/src/main/plugin/iso19139/loc/fre/codelists.xml b/schemas/iso19139/src/main/plugin/iso19139/loc/fre/codelists.xml index 47cc153dd8f..b6536b5fc71 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/loc/fre/codelists.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/loc/fre/codelists.xml @@ -2073,7 +2073,7 @@ now - + unknown diff --git a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/codelists.xml b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/codelists.xml index a715df4a283..fdf6efea660 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/codelists.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/codelists.xml @@ -1578,26 +1578,25 @@ Publication --> - map - + staticMap - + interactiveMap - + featureCatalog - + diff --git a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/labels.xml b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/labels.xml index 0d9a861ad56..9992dceb00e 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/labels.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/labels.xml @@ -240,15 +240,13 @@ - Räumlichen - Angaben, wie räumliche Ausprägungen definiert werden. - Unterschieden werden Vektordaten und - Rasterdaten. + Datenstruktur + Angabe zur technischen Beschaffenheit der Daten. Unterschieden werden z. B. Vektordaten und Rasterdaten. + Vektordaten können aus unterschiedlichen Geometrietypen bestehen und werden durch attributive Informationen + (alphanumerischen Daten) in ihrer Aussage unterstützt. - Digitale Darstellung der räumlichen Informationen im - Datenbestand - + Typ der digitalen Daten (Raster / Vektor / Alphanumerisch) @@ -913,11 +911,11 @@ - Darstellungs + Darst.katalog z.B. Signaturenkatalog Information über das Regelwerk für die Darstellung einer - Ressource + Ressource in einer Karte @@ -1005,15 +1003,11 @@ - Anwendung - Mit diesem Element werden Informationen zu einem - Anwendungsschema erfasst. Ein - Anwendungsschema bietet eine formale Beschreibung der Datenstruktur und des Inhaltes - und somit - die Voraussetzung für den Austausch von Daten zwischen verschiedenen - Systemen. Die ISO - konforme Erstellung von Anwendungsschemata erfolgt nach dem ISO Standard - 19109. + Schema + Mit diesem Element werden Informationen zu einem Anwendungsschema / Datenmodell erfasst. Ein Anwendungsschema + bietet eine formale Beschreibung der Datenstruktur und des Inhaltes der Daten und ist somit die Voraussetzung für + den Austausch von Daten zwischen verschiedenen Systemen. Die ISO-konforme Erstellung der + Anwendungsschemata erfolgt nach dem ISO Standard 19109. Information zum Datenmodell des Datenbestands @@ -1094,7 +1088,7 @@ Information zum Inhalt einer Rasterdatenzelle - Ressourcen können z.B. Datensätze, digitale Karten, Messreihen, @@ -1544,6 +1538,10 @@ Obligatorisch Version des Datenformats + + + + Nummern von Änderungen, Revisionen oder Zusätzen @@ -1939,10 +1937,11 @@ - + - Schlüsselwörter, ihr Typ und Quellenangabe + Schlagwörter, ihr Typ (thematische Zugehörigkeit) und Quellenangabe + (häufig ein Thesaurus mit Publikationsdatum) @@ -2114,7 +2113,7 @@ Einschränkungen bezüglich der Ressource - @@ -2286,7 +2285,7 @@ Ressource gehört - @@ -2388,7 +2387,7 @@ - z.B. Massstab, Bodenauflösung, Gitterweite, Rasterauflösung + z.B. Maßstab, Bodenauflösung, Gitterweite, Rasterauflösung Angaben über die räumliche Auflösung der geografischen Informationen @@ -2401,10 +2400,10 @@ Adressangaben zur verantwortlichen Stelle - + - Angabe der Strasse und Hausnummer (ggf. auch als + Angabe der Straße und Hausnummer (ggf. auch als Postfach) gemäss ISO 11180, Anhang A @@ -2734,6 +2733,11 @@ gehört + + + + + @@ -2776,7 +2780,7 @@ - + Obligatorisch wenn Hierarchieebene = Datenbestand, bzw. auf alle Fälle obligatorisch nach INSPIRE @@ -2908,18 +2912,18 @@ Quelle von Schlüsselwörtern - + - Hergeleitet vom ISO 19103; Massstab, wobei MD_RepresentativFraction.denominator = 1 + Hergeleitet vom ISO 19103; Maßstab, wobei MD_RepresentativFraction.denominator = 1 / Scale.measure Und Scale.targetUnits = Scale.sourceUnits - - Empfehlung: Falls ein Massstabsbereich angegeben werden soll, ist die Klasse MD_Resolution - zweifach zu nutzen und jeweils die obere und untere Massstabszahl einzutragen. + + Empfehlung: Falls ein Maßstabsbereich angegeben werden soll, ist die Klasse MD_Resolution + zweifach zu nutzen und jeweils die obere und untere Maßstabszahl einzutragen. Soll ausgefüllt werden, falls keine Distanz (Bodenauflösung) angegeben wird - Angabe der Massstabszahl (mz) eines Massstabs 1 : mz + Angabe der Maßstabszahl (mz) eines Maßstabs 1 : mz @@ -2943,7 +2947,7 @@ - Detailliertheitsgrad, angegeben durch eine Massstabszahl oder eine + Detailliertheitsgrad, angegeben durch eine Maßstabszahl oder eine Bodenauflösung @@ -2954,16 +2958,16 @@ Hierarchieebenen sind dem ISO 19115 - Anhang H zu entnehmen) - + auszufüllen, wenn keine Distanz (Bodenauflösung) angegeben wird - Detailliertheitsgrad, angegeben durch den Massstab einer vergleichbaren gedruckten + Detailliertheitsgrad, angegeben durch den Maßstab einer vergleichbaren gedruckten Karte Abstand der Rastermittelpunkte bzw. Gitterstützpunkte - auszufüllen, wenn kein Vergleichsmassstab angegeben wird + auszufüllen, wenn kein Vergleichsmaßstab angegeben wird Bodenauflösung @@ -3257,10 +3261,10 @@ Detaillierte Beschreibung der Ebene der Quelldaten - + - Angabe der Massstabszahl (mz) eines Massstabs 1 : mz der Ausgangskarte + Angabe der Maßstabszahl (mz) eines Maßstab 1 : mz der Ausgangskarte diff --git a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/strings.xml b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/strings.xml index 423d80f9210..9814b4a0626 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/loc/ger/strings.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/loc/ger/strings.xml @@ -49,17 +49,17 @@ Resource XML - Metadata - Identification - Distribution - Quality - Spatial rep. - Ref. system - Content - Portrayal - Md. constraints - Md. maintenance - Schema info + Metadaten + Basisinfo + Vertrieb + Qualität + Datenstruktur + Referenzsystem + Dateninhalt + Signaturenkatalog + Metadateneinschränkungen + Metadatenpflege + Datenmodell Keywords Geographic coverage diff --git a/schemas/iso19139/src/main/plugin/iso19139/present/csw/csw-full.xsl b/schemas/iso19139/src/main/plugin/iso19139/present/csw/csw-full.xsl index c52ab55abec..90ff2e96b56 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/present/csw/csw-full.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/present/csw/csw-full.xsl @@ -28,6 +28,7 @@ xmlns:dct="http://purl.org/dc/terms/" xmlns:gco="http://www.isotc211.org/2005/gco" xmlns:gmd="http://www.isotc211.org/2005/gmd" + xmlns:gmx="http://www.isotc211.org/2005/gmx" xmlns:srv="http://www.isotc211.org/2005/srv" xmlns:geonet="http://www.fao.org/geonetwork" xmlns:ows="http://www.opengis.net/ows" @@ -278,10 +279,9 @@ - + - diff --git a/schemas/iso19139/src/main/plugin/iso19139/process/add-info-from-wms.xsl b/schemas/iso19139/src/main/plugin/iso19139/process/add-info-from-wms.xsl index 5ebfc4bc509..f70c6d75919 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/process/add-info-from-wms.xsl +++ b/schemas/iso19139/src/main/plugin/iso19139/process/add-info-from-wms.xsl @@ -35,14 +35,10 @@ WMS service - is described in online resource section. Run to update extent, CRS or - graphic overview - for this WMS service for the layer named: + is described in online resource section. Run to update extent, CRS or graphic overview for this WMS service for the layer named: Le service de visualisation - est décrit dans la section resource en ligne. Exécuter cette action - pour mettre à jour l'étendue, les systèmes de projection - ou les aperçus pour ce service et la couche nommée : + est décrit dans la section resource en ligne. Exécuter cette action pour mettre à jour l'étendue, les systèmes de projection ou les aperçus pour ce service et la couche nommée : Er is een verwijzing gevonden naar de WMS service . Gebruik deze functie om de dekking, de projectie of thumbnail af te leiden of bij te werken vanuit deze WMS-service voor de laag met de naam: @@ -60,6 +56,7 @@ + @@ -74,7 +71,7 @@ --> - + @@ -105,13 +102,15 @@ + + target="link#{gmd:protocol/gco:CharacterString}#{$url}#{$layerName}"> . + select="$layerName"/>. true {"setExtent":{"type":"boolean", "defaultValue":""}, @@ -128,7 +127,9 @@ "defaultValue":""}, "wmsServiceUrl":{"type":"string", "defaultValue":""} + select="$url"/>"}, + "wmsLayerName":{"type":"string", "defaultValue":""} } @@ -173,26 +174,21 @@ gmd:graphicOverview "/> - + + select="$capabilitiesDoc//Layer[Name=$wmsLayerName]/LatLonBoundingBox"/> + - - - - - - @@ -415,8 +411,6 @@ - - @@ -429,7 +423,7 @@ - diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/info.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/info.xml new file mode 100644 index 00000000000..be1aef30997 --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/info.xml @@ -0,0 +1,46 @@ + + + + da165110-88fd-11da-a88f-000d939bc5d8 + 2007-07-19T14:45:07 + 2007-11-06T12:13:00 + iso19139 + false + full + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/metadata.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/metadata.xml new file mode 100644 index 00000000000..6f6639e92c7 --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/metadata.xml @@ -0,0 +1,337 @@ + + + + da165110-88fd-11da-a88f-000d939bc5d8 + + + eng + + + + + + + + Jippe Hoogeveen + + + FAO - NRCW + + + Technical Officer + + + + + + + + + + + + + + + + + Viale delle Terme di Caracalla + + + Rome + + + + + + 00153 + + + Italy + + + jippe.hoogeveen@fao.org + + + + + + + + + + + + 2007-11-06T12:13:00 + + + ISO 19115:2003/19139 + + + 1.0 + + + + + + + + WGS 1984 + + + + + + + + + + + Hydrological Basins in Africa (Sample record, please remove!) + + + + + 2000-07-19T14:45:00 + + + + + + + + First + + + + + + + + Major hydrological basins and their sub-basins. This dataset divides the African continent according to its hydrological characteristics. +The dataset consists of the following information:- numerical code and name of the major basin (MAJ_BAS and MAJ_NAME); - area of the major basin in square km (MAJ_AREA); - numerical code and name of the sub-basin (SUB_BAS and SUB_NAME); - area of the sub-basin in square km (SUB_AREA); - numerical code of the sub-basin towards which the sub-basin flows (TO_SUBBAS) (the codes -888 and -999 have been assigned respectively to internal sub-basins and to sub-basins draining into the sea) + + + This dataset is developed as part of a GIS-based information system on water resources for the African continent. It has been published in the framework of the AQUASTAT - programme of the Land and Water Division of the Food and Agriculture Organization of the United Nations, as part of FAO Land and Water Digital Media Series #13: "Atlas of Water Resources and Irrigation in Africa". + +For a wider distribution and to support other projects at FAO this map is also distributed in a DVD as part of a publication entitled: "Jenness, J.; Dooley, J.; Aguilar-Manjarrez, J.; Riva, C. African Water Resource Database. GIS-based tools for inland aquatic resource management. 2. Technical manual and workbook. CIFA Technical Paper. No. 33, Part 2. Rome, FAO. 2007. 308 p." + + + + + + + + + + + + + + + http://localhost:8080/geonetwork/srv/api/records/da165110-88fd-11da-a88f-000d939bc5d8/attachments/thumbnail_s.gif + + + Hydrological Basins in Africa (small preview) + + + + + + + http://localhost:8080/geonetwork/srv/api/records/da165110-88fd-11da-a88f-000d939bc5d8/attachments/thumbnail.gif + + + Hydrological Basins in Africa + + + + + + + watersheds + + + river basins + + + water resources + + + hydrology + + + AQUASTAT + + + AWRD + + + + + + + + + + Africa + + + + + + + + + + + + + + + + + + + + + + 5000000 + + + + + + + eng + + + + + + inlandWaters + + + + + + + + 2006-01-01T04:29:00 + 2008-01-08T04:29:00 + + + + + + + + + + + + -17.3 + + + 51.1 + + + -34.6 + + + 38.2 + + + + + + + You can customize the template to suit your needs. You can add and remove fields and fill out default information (e.g. contact details). Fields you can not change in the default view may be accessible in the more comprehensive (and more complex) advanced view. You can even use the XML editor to create custom structures, but they have to be validated by the system, so know what you do :-) + + + + + + + + + ShapeFile + + + Grass Version 6.1 + + + + + + + + + http://www.fao.org/ag/AGL/aglw/aquastat/watresafrica/index.stm + + + WWW:LINK-1.0-http--link + + + + + + Online link to the 'Water Resources and Irrigation in Africa'- website + + + + + + + http://localhost:8080/geonetwork/srv/api/records/da165110-88fd-11da-a88f-000d939bc5d8/attachments/basins.zip + + + WWW:DOWNLOAD-1.0-http--download + + + basins.zip + + + Hydrological basins in Africa (Shapefile Format) + + + + + + + https://data.apps.fao.org/map/gsrv/gsrv1/geonetwork/wms + + + OGC:WMS + + + geonetwork:basins_296 + + + Hydrological basins in Africa + + + + + + + + + + + + + + + + + + + + The linework of the map is obtained by delineating drainage basin boundaries from an hydrologically corrected digital elevation model with a resolution of 1 * 1 km. + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/private/.keepme b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/private/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/private/basins.zip b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/private/basins.zip new file mode 100644 index 00000000000..e56056000bf Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/private/basins.zip differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/.keepme b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/.keepme new file mode 100644 index 00000000000..e69de29bb2d diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail.gif b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail.gif new file mode 100644 index 00000000000..77efd60a935 Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail.gif differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail_s.gif b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail_s.gif new file mode 100644 index 00000000000..4c7876fc7ea Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/basins_in_africa/public/thumbnail_s.gif differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-basins-in-africa.mef b/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-basins-in-africa.mef deleted file mode 100644 index 156be44779b..00000000000 Binary files a/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-basins-in-africa.mef and /dev/null differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-physiographic.mef b/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-physiographic.mef deleted file mode 100755 index 9520091935b..00000000000 Binary files a/schemas/iso19139/src/main/plugin/iso19139/sample-data/iso-19139-physiographic.mef and /dev/null differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive.mef b/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive.mef deleted file mode 100644 index fa154ad032b..00000000000 Binary files a/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive.mef and /dev/null differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/info.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/info.xml new file mode 100644 index 00000000000..64a95f09e6e --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/info.xml @@ -0,0 +1,35 @@ + + + + 2007-07-19T14:45:07 + 2007-11-06T12:13:00 + iso19139 + false + full + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/metadata.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/metadata.xml new file mode 100644 index 00000000000..77f44d9e734 --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/locality_polygons_comprehensive/metadata.xml @@ -0,0 +1 @@ + 388fab80-4f71-11db-8a85-000f207026fd eng utf8 dataset dataset - Localities - Victoria Falzon Katie Ms Department of Sustainability and Environment (DSE) Project Officer +61 3 8636 2319 +61 3 8636 2813 PO Box 500 East Melbourne Victoria 3002 Australia katie.falzon@dse.vic.gov.au http://www.land.vic.gov.au/vicmap custodian 2007-08-13 ANZLIC Metadata Profile 1.1 EPSG Geodetic Parameter Dataset 20070716 revision Version 6.13 4283 Localities in Victoria (VMADMIN.LOCALITY_POLYGON) - Comprehensive Elements 2006-09-11 publication Falzon Katie Ms Department of Sustainability and Environment (DSE) Project Officer +61 3 8636 2319 +61 3 8636 2813 PO Box 500 East Melbourne Victoria 3002 Australia katie.falzon@dse.vic.gov.au http://www.land.vic.gov.au/vicmap custodian mapDigital This dataset is the definitive set of locality boundaries for the state of Victoria as defined by Local Government and registered by the Registrar of Geographic Names. The boundaries are aligned to Vicmap Property. This dataset is part of the Vicmap Admin dataset series. The Vicmap Admin product, which includes this dataset, provides an accurate representation of various administrative boundaries across Victoria, at a capture scale of 1:25,000. It is used in a variety of applications, particularly in emergency services, natural resource management, planning and development, and digital map publications. onGoing Department of Sustainability and Environment (DSE) Marketting manager +61 3 8636 2683 +61 3 8636 2813 PO Box 500 East Melbourne Victoria 3002 Australia vicmap@dse.vic.gov.au http://www.land.vic.gov.au/vicmapdsp Please consult the above link for a list of Data Suppliers from whom this dataset may be purchased pointOfContact fortnightly dataset BOUNDARIES-Administrative LAND-Ownership theme ANZLIC search words 2006-10-10 revision 2 ANZLIC GPO Box 337 CANBERRA Australian Capital Territory 2601 AUSTRALIA info@ANZLIC.org.au custodian documentDigital intellectualPropertyRights otherRestrictions The data is available under licence from Spatial Information Infrastructure in a variety of formats and media. Licence fees and distribution charges apply vector 25 eng boundaries This dataset is maintained by a Spatial Information Infrastructure (SII) contractor. It is supplied to SII in SII's proprietary Incremental Update Format (IUF) and loaded into ESRI's ArcSDE (Oracle with geometry stored as sde binary) Victoria 1 141 150 -39 -34 1 ANZLIC Geographic Extent Name Register - States and Territories 2006-10-10 revision Version 2 http://www.ga.gov.au/anzmeta/gen/anzlic-algens.xml#anzlic-state_territory documentDigital VIC Data represents localities mapped since mid 2005 2005-05-15 Most popular formats including ESRI shape, MapInfo Tab and Oracle Spatial Various http://www.land.vic.gov.au/vicmapdsp Web page listing Data Service Providers (DSP)s dataset Localities in Victoria (VMADMIN.LOCALITY_POLYGON) Valid code Test This measure validates codes/values against the authoritative list published as Vicmap Reference Table LOCALITY. directInternal 2006-11-14T00:00:00 Valid code Test 2005-05-15 revision All LOCALITY_name and GAZETTED_LOCALITY_NAMES are valid. 1 Codelist omission This measure ensures all gazetted localities registered in the authoritative list published as Vicmap Reference Table LOCALITY are present in the dataset directInternal 2006-11-13T00:00:00 Omitted code Test 2005-05-15 revision No gazetted localities are missing. 1 Overlapping polygon This measure identifies overlapping polygons directInternal 2006-11-14T00:00:00 Overlapping polygon Test 2005-05-15 revision Five (5) overlapping polygons exist 1 Feature represented as a single object Each locality must be represented as a single object. Where the locality is represetned by 2 or more parts, the locality feature will be represented as a single object as a multi-part polygon. directInternal 2006-11-10T00:00:00 Single feature Test 2005-05-15 revision All localities are represented as single object. 1 Absolute external positional accuracy directInternal 2006-11-10T00:00:00 Positional accuracy Test 2005-05-15 revision Varies as per scale of capture of Vicmap Property. Vicmap Property is classified as 'BB' accuracy, ie. 90% of well-defined features are within 1mm, at plot scale, of their true position, eg. 1:500 equates to +/- 0.5metre and 1:25,000 equates to +/- 25 metres. Anecdotal evidence suggests that the spatial accuracy of the major part of the data set, at all scales is frequently better than BB. 1 Local Government defines suburb, town and rural district boundaries after seeking appropriate consultation with all interested parties. Proposed boundaries/names are submitted to the Registrar of Geographic Names for approval and formal gazettal. As boundary and name information changes are processed through the formal definition and gazettal process at the Registrar of Geographic Names, the amendments will be flagged and the dataset will be updated by the maintainer with all new boundaries aligned to Vicmap Property. Vicmap Property is the principal source of the digital data. Its accuracy varies across Victoria with metropolitan Melbourne source data collected at 1:500 scale and rural Victoria data collected at 1:25,000. For futher information see the Vicmap Property product specification at http://www.land.vic.gov.au/vicmap. diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session.mef b/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session.mef deleted file mode 100644 index b207304a121..00000000000 Binary files a/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session.mef and /dev/null differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/info.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/info.xml new file mode 100644 index 00000000000..64a95f09e6e --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/info.xml @@ -0,0 +1,35 @@ + + + + 2007-07-19T14:45:07 + 2007-11-06T12:13:00 + iso19139 + false + full + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/metadata.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/metadata.xml new file mode 100644 index 00000000000..5e44f5ad424 --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/photographic_session/metadata.xml @@ -0,0 +1,111 @@ + + + + 46E7F9B1-99F6-3241-9039-EAE7201534F4 + + + eng + + + + + + collectionSession + + + + 2007-08-27 + + + ANZLIC Metadata Profile + + + 1.1 + + + http://www.ga.gov.au/news/index.jsp#openday + + + + + + + Geoscience Australia's Open Day Photographs 26th August 2007 + + + + + 2007-08-26 + + + + + + + + + + Chris Fitzgerald + + + Geoscience Australia + + + Geoscience Australia's Open Day Photographer + + + + + + + + + + + During National Science Week on Sunday 26th August 2007, Geoscience Australia opened its + doors to the community to showcase a diverse range of work activities. Members of the public had the + opportunity to discover how earthquakes are detected, pan for gold, tour the building, view Australia in 3D, + become a seafloor detective and talk to the people who work for Australia's national geoscience research + organisation. The photographs of that open day have been converted into thumbmail images and are available on + the GA web site. + + + + + + The images are only available in thumbnail size for privacy reasons. + + + + + + + + + + + + + + + eng + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/info.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/info.xml new file mode 100644 index 00000000000..72eafde8c7d --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/info.xml @@ -0,0 +1,44 @@ + + + + 78f93047-74f8-4419-ac3d-fc62e4b0477b + 2007-07-25T12:05:45 + 2007-11-06T12:10:47 + iso19139 + false + full + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/metadata.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/metadata.xml new file mode 100644 index 00000000000..9984b85ac7d --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/metadata.xml @@ -0,0 +1,351 @@ + + + + + 78f93047-74f8-4419-ac3d-fc62e4b0477b + + + + + + + + + + + Marina Zanetti + + + FAO - Land and Water Development Division + + + GIS specialist + + + + + + + + 2007-11-06T12:10:47 + + + ISO 19115:2003/19139 + + + 1.0 + + + + + + + + + + + + + + + + + + + + + Lambert Azimuthal Projection + + + + + + + + + + + Physiographic Map of North and Central Eurasia (Sample record, please remove!) + + + + + 1999-10-01 + + + + + + + + First + + + + + + + + Physiographic maps for the CIS and Baltic States (CIS_BS), Mongolia, China and Taiwan Province of China. Between the three regions (China, Mongolia, and CIS_BS countries) DCW boundaries were introduced. There are no DCW boundaries between Russian Federation and the rest of the new countries of the CIS_BS. The original physiographic map of China includes the Chinese border between India and China, which extends beyond the Indian border line, and the South China Sea islands (no physiographic information is present for islands in the South China Sea). The use of these country boundaries does not imply the expression of any opinion whatsoever on the part of FAO concerning the legal or constitutional states of any country, territory, or sea area, or concerning delimitation of frontiers. The Maps visualize the items LANDF, HYPSO, SLOPE that correspond to Landform, Hypsometry and Slope. + + + + + + + + + + + Nachtergaele Freddy + + + FAO - Land and Water Development Division + + + Technical officer + + + + + + + Viale delle Terme di Caracalla + + + Rome + + + RM + + + 00153 + + + Italy + + + Freddy.Nachtergaele@fao.org + + + + + + + + + + + + + + + + + + + + + http://localhost:8080/geonetwork/srv/api/records/78f93047-74f8-4419-ac3d-fc62e4b0477b/attachments/phy_s.gif + + + Physiographic Map (small preview) + + + + + + + http://localhost:8080/geonetwork/srv/api/records/78f93047-74f8-4419-ac3d-fc62e4b0477b/attachments/phy.gif + + + Physiographic Map + + + + + + + physiography, soil + + + + + + + + + + Eurasia + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5000000 + + + + + + + + + + + + + geoscientificInformation + + + + + + + + 2000-01-01T04:29:00 + 2008-01-08T04:29:00 + + + + + + + + + + + + 37 + + + 156 + + + -3 + + + 83 + + + + + + + The maps are included in the SOIL and TERRAIN Database for Northern and Central EURASIA CD-ROM , which contains also the Soil map of North and Central Eurasia, reports and databases. Copies of this CD-Rom can be ordered from: Sales and Marketing Group FAO, Viale delle Terme di Caracalla, 00153 Rome, or by email to Publications-sales@fao.org. The terms and definitions used in the Physiographic database are based on the procedures manual for ?Global and National Soils and Terrain Digital Databases (SOTER)?, prepared by UNEP, ISSS, ISRIC and FAO and published by FAO as World Soil Resources Report #74 Rev1 (1995). Refinements were made in China as part of the preparation of a physiographic map for Asia, work carried out by G. van Lynden for FAO as part of the ASSOD project. + + + + + + + + + + + http://localhost:8080/geonetwork/srv/api/records/78f93047-74f8-4419-ac3d-fc62e4b0477b/attachments/phy.zip + + + WWW:DOWNLOAD-1.0-http--download + + + phy.zip + + + Physiography of North and Central Eurasia Landform (Gif Format) + + + + + + + https://data.apps.fao.org/map/gsrv/gsrv1/geonetwork/wms + + + OGC:WMS + + + geonetwork:phy_landf_7386 + + + Physiography of North and Central Eurasia Landform + + + + + + + https://data.apps.fao.org/map/gsrv/gsrv1/geonetwork/wms + + + OGC:WMS + + + geonetwork:phy_slope_7386 + + + Physiography of North and Central Eurasia Slope + + + + + + + https://data.apps.fao.org/map/gsrv/gsrv1/geonetwork/wms + + + OGC:WMS + + + geonetwork:phy_hypso_7386 + + + Physiography of North and Central Eurasia Hypsography + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/private/phy.zip b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/private/phy.zip new file mode 100644 index 00000000000..e71cb203995 Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/private/phy.zip differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy.gif b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy.gif new file mode 100644 index 00000000000..4558bdb76d5 Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy.gif differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy_s.gif b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy_s.gif new file mode 100644 index 00000000000..2b5fab74ac1 Binary files /dev/null and b/schemas/iso19139/src/main/plugin/iso19139/sample-data/physiographic/public/phy_s.gif differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950-server-sample.mef b/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950-server-sample.mef deleted file mode 100644 index 2056a06da25..00000000000 Binary files a/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950-server-sample.mef and /dev/null differ diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/info.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/info.xml new file mode 100644 index 00000000000..f89211d9bfb --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/info.xml @@ -0,0 +1,36 @@ + + + + 2010-02-02T00:00:00 + 2010-02-02T00:00:00 + iso19139 + false + 30 + full + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/metadata.xml b/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/metadata.xml new file mode 100644 index 00000000000..f27c5d1350a --- /dev/null +++ b/schemas/iso19139/src/main/plugin/iso19139/sample-data/z3950_server/metadata.xml @@ -0,0 +1,147 @@ + + + + z3950-server-test-record-aabb12345bbaa + + + eng + + + + + + service - distributed Z23950 zservice + + + + + Geoffrey's Tube Australia + + + + + + + + 2009-10-15 + + + ISO 19115:2003/19139 + + + 1.0 + + + + + + + The Geoffrey's Tube Z3950 Server (Sample Record - Please Delete!) + + + + + 2010-03-24 + + + + + + + + + + + + + Geoffrey's Tube Craftsmen + + + + + + + + + + + + + + + + + + + + + http://www.google.com + + + Home URL + + + + + + + + + + + + + + This catalog is for registering all metadata records held by the Geofffrey's Tube Palace Hotel Ballroom. + + + ISO 23950 gateway + + + + + + + 112 + + + 154 + + + -44 + + + -9 + + + + + + + + + + ASDD nodes zserver + + + + + + z3950://www.google.com:6668/geonetwork + + + z3950 + + + ASDD Z3950 zserver + + + + + + + + + + + diff --git a/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml b/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml index b3bd3bd0bf7..14ec15c73b6 100644 --- a/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml +++ b/schemas/iso19139/src/main/plugin/iso19139/schema-ident.xml @@ -89,8 +89,10 @@ of data.]]> diff --git a/schemas/iso19139/src/main/plugin/iso19139/schematron/schematron-rules-iso.sch b/schemas/iso19139/src/main/plugin/iso19139/schematron/schematron-rules-iso.sch index 87bb01c4cb9..fd1cb718fae 100755 --- a/schemas/iso19139/src/main/plugin/iso19139/schematron/schematron-rules-iso.sch +++ b/schemas/iso19139/src/main/plugin/iso19139/schematron/schematron-rules-iso.sch @@ -64,6 +64,7 @@ USA. + @@ -348,21 +349,19 @@ USA. $loc/strings/report.M23 - - - + + $loc/strings/report.M23-dup - diff --git a/schemas/pom.xml b/schemas/pom.xml index d60972a18ad..9067bb95ee9 100644 --- a/schemas/pom.xml +++ b/schemas/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/schemas/schema-core/pom.xml b/schemas/schema-core/pom.xml index 02191811511..560c937ac02 100644 --- a/schemas/schema-core/pom.xml +++ b/schemas/schema-core/pom.xml @@ -28,7 +28,7 @@ gn-schemas org.geonetwork-opensource.schemas - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 diff --git a/sde/pom.xml b/sde/pom.xml index 5b34f8da536..cfe299ba538 100644 --- a/sde/pom.xml +++ b/sde/pom.xml @@ -30,7 +30,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT diff --git a/services/pom.xml b/services/pom.xml index 44755e8356f..6f50044d4d1 100644 --- a/services/pom.xml +++ b/services/pom.xml @@ -27,7 +27,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT 4.0.0 @@ -230,6 +230,14 @@ commons-codec commons-codec + + co.elastic.clients + elasticsearch-java + + + commons-fileupload + commons-fileupload + diff --git a/services/src/main/java/org/fao/geonet/api/ApiParams.java b/services/src/main/java/org/fao/geonet/api/ApiParams.java index d3c5825c26c..55720ac7579 100644 --- a/services/src/main/java/org/fao/geonet/api/ApiParams.java +++ b/services/src/main/java/org/fao/geonet/api/ApiParams.java @@ -33,6 +33,10 @@ public class ApiParams { public static final String API_CLASS_CATALOG_TAG = "site"; public static final String API_CLASS_REGISTRIES_OPS = "Registries related operations"; public static final String API_CLASS_REGISTRIES_TAG = "registries"; + public static final String API_CLASS_TOOLS_TAG = "tools"; + public static final String API_CLASS_TOOLS_OPS = "Utility operations"; + public static final String API_CLASS_FORMATTERS_TAG = "formatters"; + public static final String API_CLASS_FORMATTERS_OPS = "Formatter operations"; public static final String API_PARAM_RECORD_UUID = "Record UUID."; diff --git a/services/src/main/java/org/fao/geonet/api/GlobalExceptionController.java b/services/src/main/java/org/fao/geonet/api/GlobalExceptionController.java index fc2f4efc25c..e7a89ad1ec9 100644 --- a/services/src/main/java/org/fao/geonet/api/GlobalExceptionController.java +++ b/services/src/main/java/org/fao/geonet/api/GlobalExceptionController.java @@ -34,6 +34,7 @@ import org.fao.geonet.exceptions.ServiceNotAllowedEx; import org.fao.geonet.exceptions.UserNotFoundEx; import org.fao.geonet.exceptions.XSDValidationErrorEx; +import org.fao.geonet.inspire.validator.InspireValidatorException; import org.fao.geonet.utils.Log; import org.json.JSONException; import org.springframework.beans.factory.annotation.Autowired; @@ -164,6 +165,7 @@ public ApiError maxFileExceededHandler(final Exception exception) { @ExceptionHandler({ HttpMessageNotReadableException.class, Exception.class, + InspireValidatorException.class, RuntimeException.class }) public ApiError runtimeExceptionHandler(final Exception exception, final HttpServletRequest request) { diff --git a/services/src/main/java/org/fao/geonet/api/OpenApiConfig.java b/services/src/main/java/org/fao/geonet/api/OpenApiConfig.java index b2ab44c2914..feed405074f 100644 --- a/services/src/main/java/org/fao/geonet/api/OpenApiConfig.java +++ b/services/src/main/java/org/fao/geonet/api/OpenApiConfig.java @@ -1,6 +1,6 @@ /* * ============================================================================= - * === 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) * === @@ -36,7 +36,7 @@ import io.swagger.v3.oas.models.servers.ServerVariables; import org.fao.geonet.NodeInfo; import org.fao.geonet.kernel.setting.SettingManager; -import org.springframework.beans.factory.annotation.Autowired; +import org.springdoc.core.SpringDocUtils; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.concurrent.ConcurrentMapCacheManager; @@ -45,13 +45,28 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import static org.fao.geonet.kernel.setting.Settings.SYSTEM_PLATFORM_VERSION; @Configuration @EnableCaching @OpenAPIDefinition -public class OpenApiConfig { +public class OpenApiConfig { + + private static OpenAPI openAPI = null; + + private static SettingManager settingManager = null; + + static { + // By default, Spring Doc ignores injectable parameters supported by Spring MVC + // https://springdoc.org/faq.html#_what_are_the_ignored_types_in_the_documentation + // This includes the following list. + // https://docs.spring.io/spring-framework/docs/5.1.x/spring-framework-reference/web.html#mvc-ann-arguments + // As java.util.Map's are uses for JSON input and output values among other types, it is best to remove java.util.Map from this list. + // If we want to use java.util.Map as an injectable then we will need to ensure we hide the parameter. + SpringDocUtils.getConfig().removeRequestWrapperToIgnore(Map.class); + } @Bean(name = "cacheManager") public CacheManager cacheManager() { @@ -59,11 +74,77 @@ public CacheManager cacheManager() { } @Bean - @Autowired - public OpenAPI OpenApiConfig(SettingManager settingManager) { - List servers = new ArrayList<>(); + public OpenAPI openApi(final SettingManager settingManager) { + return OpenApiConfig.setupOpenApiConfig(settingManager); + } + + /** + * Setup OpenAPI configuration. + * + * Using static function so that should the bean be called twice (which it does), it will reuse the same static objects. + * During first call, the settingManager may not be properly setup (i.e. initial install) and will return null values for version and host information. + * Subsequent calls will update the related OpenAPI information with the settingManager values. + * + * It is also static so that when we update the object on second call, it will also update the object returned from the first call as they will be + * pointing to the same object. + * + * @param settingManager containing host and version information required. + * @return OpenAPI object. + */ + private static OpenAPI setupOpenApiConfig(final SettingManager settingManager) { + OpenApiConfig.settingManager = settingManager; + if (openAPI == null) { + openAPI = new OpenAPI().info(new Info() + .description("This is the description of the GeoNetwork OpenAPI. Use this API to manage your catalog.") + .contact(new Contact() + .email("geonetwork-users@lists.sourceforge.net") + .name("GeoNetwork user mailing list") + .url("https://sourceforge.net/p/geonetwork/mailman/geonetwork-users/") + ) + .license(new License() + .name("GPL 2.0") + .url("https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"))) + .externalDocs(new ExternalDocumentation() + .description("Learn how to access the catalog using the GeoNetwork REST API.")); + + setVersionRelatedInfo(); + setHostRelatedInfo(); + + } else if (openAPI.getInfo() != null && openAPI.getInfo().getVersion() == null) { + // During initial install, the version will not be set when using the JeevesApplicationContext + // But it will be set afterward when creating WebApplicationContext. So if the version is null but our new version is not null + // then lets update data based on the version. + setVersionRelatedInfo(); + // If the version was not set then the hostUrl was also not set correctly so update that as well. + setHostRelatedInfo(); + } + + return openAPI; + } + + /** + * Update openAPI object with version related information. + */ + private static void setVersionRelatedInfo() { String version = settingManager.getValue(SYSTEM_PLATFORM_VERSION); + + openAPI.getInfo().setVersion(version); + openAPI.getInfo().setTitle(String.format( + "GeoNetwork %s OpenAPI Documentation", + version)); + } + + /** + * Update openAPI object with host related information. + */ + public static void setHostRelatedInfo() { + if (settingManager == null || openAPI == null) { + return; + } + + List servers = new ArrayList<>(); + String hostUrl = settingManager.getBaseURL().replaceAll("/+$", ""); ServerVariable catalogVariable = new ServerVariable() @@ -85,24 +166,7 @@ public OpenAPI OpenApiConfig(SettingManager settingManager) { .addServerVariable("portal", portalVariable) ) ); - - return new OpenAPI().info(new Info() - .title(String.format( - "GeoNetwork %s OpenAPI Documentation", - version)) - .description("This is the description of the GeoNetwork OpenAPI. Use this API to manage your catalog.") - .contact(new Contact() - .email("geonetwork-users@lists.sourceforge.net") - .name("GeoNetwork user mailing list") - .url("https://sourceforge.net/p/geonetwork/mailman/geonetwork-users/") - ) - .version(version) - .license(new License() - .name("GPL 2.0") - .url("http://www.gnu.org/licenses/old-licenses/gpl-2.0.html"))) - .externalDocs(new ExternalDocumentation() - .description("Learn how to access the catalog using the GeoNetwork REST API.") - .url(String.format("%s/doc/api", hostUrl))) - .servers(servers); + openAPI.setServers(servers); + openAPI.getExternalDocs().setUrl(String.format("%s/doc/api", hostUrl)); } } diff --git a/services/src/main/java/org/fao/geonet/api/OpenApiController.java b/services/src/main/java/org/fao/geonet/api/OpenApiController.java index b2a93bbf367..7a03f1ed844 100644 --- a/services/src/main/java/org/fao/geonet/api/OpenApiController.java +++ b/services/src/main/java/org/fao/geonet/api/OpenApiController.java @@ -21,6 +21,8 @@ package org.fao.geonet.api; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializationFeature; + import io.swagger.v3.core.util.Json; import io.swagger.v3.core.util.PathUtils; import io.swagger.v3.core.util.Yaml; @@ -33,12 +35,9 @@ import org.springdoc.webmvc.core.RouterFunctionProvider; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; @@ -47,6 +46,7 @@ import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import javax.servlet.http.HttpServletRequest; + import java.util.*; import static org.springdoc.core.Constants.*; @@ -64,6 +64,8 @@ * springdoc.api-docs.enabled=true * springdoc.api-docs.path=/api/doc * springdoc.cache.disabled=true + * springdoc.writer-with-order-by-keys=true + * springdoc.writer-with-default-pretty-printer=true */ @RestController public class OpenApiController extends AbstractOpenApiResource { @@ -95,28 +97,40 @@ public OpenApiController(ObjectFactory openAPIBuilderObjectFacto "org.fao.geonet.api", "org.fao.geonet.services.inspireatom", "org.fao.geonet.monitor.service"})); + + // Ensure open api document is consistently orders to make it easier to compare changes later. + springDocConfigProperties.setWriterWithOrderByKeys(true); + springDocConfigProperties.setWriterWithDefaultPrettyPrinter(true); + + // remove default response + springDocConfigProperties.setOverrideWithGenericResponse(false); + this.requestMappingHandlerMapping = requestMappingHandlerMapping; this.servletContextProvider = servletContextProvider; this.springSecurityOAuth2Provider = springSecurityOAuth2Provider; this.routerFunctionProvider = routerFunctionProvider; + + // Ensure all enums are written based on the enum name. + Json.mapper().configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, false); + Yaml.mapper().configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, false); } @Operation(hidden = true) @GetMapping(value = "/{portal}/api/doc", produces = MediaType.APPLICATION_JSON_VALUE) - public String openapiJson(HttpServletRequest request, @Value(API_DOCS_URL) String apiDocsUrl) + public String openapiJson(HttpServletRequest request) throws JsonProcessingException { - calculateServerUrl(request, apiDocsUrl); + setServerBaseUrl(request); OpenAPI openAPI = this.getOpenApi(request.getLocale()); - return Json.mapper().writeValueAsString(openAPI); + return writeJsonValue(openAPI); } @Operation(hidden = true) @GetMapping(value = "/{portal}/api/doc.yml", produces = APPLICATION_OPENAPI_YAML) - public String openapiYaml(HttpServletRequest request, @Value(DEFAULT_API_DOCS_URL_YAML) String apiDocsUrl) + public String openapiYaml(HttpServletRequest request) throws JsonProcessingException { - calculateServerUrl(request, apiDocsUrl); + setServerBaseUrl(request); OpenAPI openAPI = this.getOpenApi(request.getLocale()); - return Yaml.mapper().writeValueAsString(openAPI); + return writeYamlValue(openAPI); } @Override @@ -180,9 +194,14 @@ protected boolean isRestController(Map restControllers, || !ModelAndView.class.isAssignableFrom(handlerMethod.getMethod().getReturnType())); } - protected void calculateServerUrl(HttpServletRequest request, String apiDocsUrl) { - String requestUrl = decode(request.getRequestURL().toString()); - String calculatedUrl = requestUrl.substring(0, requestUrl.length() - apiDocsUrl.length()); - this.openAPIService.setServerBaseUrl(calculatedUrl); + private String getServerBaseUrl(HttpServletRequest request) { + String contextPath = request.getContextPath(); + StringBuffer requestURL = request.getRequestURL(); + String serverBaseUrl = requestURL.substring(0, requestURL.indexOf(contextPath) + contextPath.length()); + return serverBaseUrl; + } + + protected void setServerBaseUrl(HttpServletRequest request) { + this.openAPIService.setServerBaseUrl(getServerBaseUrl(request)); } } diff --git a/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java b/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java index b63d2524aad..fddaf9eff9d 100644 --- a/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java +++ b/services/src/main/java/org/fao/geonet/api/es/EsHTTPProxy.java @@ -35,9 +35,14 @@ import com.google.common.collect.Sets; import com.jayway.jsonpath.DocumentContext; import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; import io.swagger.v3.oas.annotations.Hidden; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; @@ -65,8 +70,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; @@ -270,12 +275,16 @@ private static boolean hasOperation(ObjectNode doc, ReservedGroup group, Reserve @io.swagger.v3.oas.annotations.Operation( - summary = "Search endpoint", - description = "See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html for search parameters details.") + summary = "Execute a search query and get back search hits that match the query.", + description = "The search API execute a search query with a JSON request body. For more information see https://www.elastic.co/guide/en/elasticsearch/reference/current/search-search.html for search parameters, and https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html JSON Query DSL.") @RequestMapping(value = "/search/records/_search", - method = { - RequestMethod.POST - }) + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Search results.", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(type = "string"))) + }) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public void search( @@ -292,22 +301,28 @@ public void search( HttpServletRequest request, @Parameter(hidden = true) HttpServletResponse response, - @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, - @Parameter(hidden = true) - HttpEntity httpEntity) throws Exception { + @RequestBody + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON request based on Elasticsearch API.", + content = @Content(examples = { + @ExampleObject(value = "{\"query\":{\"match\":{\"_id\":\"catalogue_uuid\"}}}") + })) + String body) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - call(context, httpSession, request, response, SEARCH_ENDPOINT, httpEntity.getBody(), bucket, relatedTypes); + call(context, httpSession, request, response, SEARCH_ENDPOINT, body, bucket, relatedTypes); } @io.swagger.v3.oas.annotations.Operation( - summary = "Search endpoint", - description = "See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html for search parameters details.") + summary = "Executes several searches with a Elasticsearch API request.", + description = "The multi search API executes several searches from a single API request. See https://www.elastic.co/guide/en/elasticsearch/reference/current/search-multi-search.html for search parameters, and https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html Query DSL.") @RequestMapping(value = "/search/records/_msearch", - method = { - RequestMethod.POST - }) + method = RequestMethod.POST, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Search results.", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(type = "string"))) + }) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public void msearch( @@ -324,12 +339,14 @@ public void msearch( HttpServletRequest request, @Parameter(hidden = true) HttpServletResponse response, - @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, - @Parameter(hidden = true) - HttpEntity httpEntity) throws Exception { + @RequestBody + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON request based on Elasticsearch API.", + content = @Content(examples = { + @ExampleObject(value = "{\"query\":{\"match\":{\"_id\":\"catalogue_uuid\"}}}") + })) + String body) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - call(context, httpSession, request, response, MULTISEARCH_ENDPOINT, httpEntity.getBody(), bucket, relatedTypes); + call(context, httpSession, request, response, MULTISEARCH_ENDPOINT, body, bucket, relatedTypes); } @@ -342,7 +359,13 @@ public void msearch( @RequestMapping(value = "/search/records/{endPoint}", method = { RequestMethod.POST, RequestMethod.GET - }) + }, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_JSON_VALUE) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Search results.", + content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(type = "string"))) + }) @ResponseStatus(value = HttpStatus.OK) @PreAuthorize("hasAuthority('Administrator')") @ResponseBody @@ -357,16 +380,18 @@ public void call( HttpServletRequest request, @Parameter(hidden = true) HttpServletResponse response, - @RequestBody(description = "JSON request based on Elasticsearch API.") - String body, - @Parameter(hidden = true) - HttpEntity httpEntity) throws Exception { + @RequestBody + @io.swagger.v3.oas.annotations.parameters.RequestBody(description = "JSON request based on Elasticsearch API.", + content = @Content(examples = { + @ExampleObject(value = "{\"query\":{\"match\":{\"_id\":\"catalogue_uuid\"}}}") + })) + String body) throws Exception { ServiceContext context = ApiUtils.createServiceContext(request); - call(context, httpSession, request, response, endPoint, httpEntity.getBody(), bucket, null); + call(context, httpSession, request, response, endPoint, body, bucket, null); } - public void call(ServiceContext context, HttpSession httpSession, HttpServletRequest request, + private void call(ServiceContext context, HttpSession httpSession, HttpServletRequest request, HttpServletResponse response, String endPoint, String body, String selectionBucket, @@ -815,7 +840,7 @@ protected boolean isContentTypeValid(final String contentType) { * * { * ... - * "address":"" + * "address":"" * * ,"nilReason": "withheld" * @@ -878,7 +903,11 @@ private JsonNode filterResponseElements(ObjectMapper mapper, ObjectNode sourceNo for(String jsonPath : jsonPathFilters) { if (StringUtils.isNotBlank(jsonPath)) { - jsonContext = jsonContext.delete(jsonPath); + try { + jsonContext = jsonContext.delete(jsonPath); + } catch (PathNotFoundException ex) { + // The node to remove is not returned in the response, ignore the error + } } } diff --git a/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java b/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java index 138208a134f..0b0fb4980d2 100644 --- a/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java +++ b/services/src/main/java/org/fao/geonet/api/groups/GroupsApi.java @@ -296,7 +296,7 @@ public List getGroups( produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT ) - @ResponseStatus(value = HttpStatus.OK) + @ResponseStatus(value = HttpStatus.CREATED) @PreAuthorize("hasAuthority('UserAdmin')") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "Group created."), @@ -357,6 +357,7 @@ public ResponseEntity addGroup( method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Group information for the group id supplied."), @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND) }) @ResponseBody diff --git a/services/src/main/java/org/fao/geonet/api/links/LinksApi.java b/services/src/main/java/org/fao/geonet/api/links/LinksApi.java index aac2aa1342d..46f17469977 100644 --- a/services/src/main/java/org/fao/geonet/api/links/LinksApi.java +++ b/services/src/main/java/org/fao/geonet/api/links/LinksApi.java @@ -25,6 +25,8 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; +import com.google.gson.Gson; + import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -52,7 +54,6 @@ import org.fao.geonet.repository.specification.LinkSpecs; import org.jdom.JDOMException; import org.json.JSONException; -import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.data.domain.Page; @@ -64,6 +65,7 @@ import org.springframework.jmx.export.naming.SelfNaming; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; +import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -72,6 +74,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; + +import java.beans.PropertyEditorSupport; import java.io.IOException; import java.sql.SQLException; import java.util.ArrayDeque; @@ -146,9 +150,9 @@ public void iniMBeansSlidingWindowWithEmptySlot() { @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public Page getRecordLinks( - @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421', groupId: 12}\", lastState being 'ok'/'ko'/'unknown'", required = false) + @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421'}\", lastState being 'ok'/'ko'/'unknown'", required = false) @RequestParam(required = false) - JSONObject filter, + LinkFilter filter, @Parameter(description = "Optional, filter links to records published in that group.", required = false) @RequestParam(required = false) Integer[] groupIdFilter, @@ -187,9 +191,9 @@ public Page getRecordLinks( produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") public Page getRecordLinksPost( - @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421', groupId: 12}\", lastState being 'ok'/'ko'/'unknown'", required = false) + @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421'}\", lastState being 'ok'/'ko'/'unknown'", required = false) @RequestParam(required = false) - JSONObject filter, + LinkFilter filter, @Parameter(description = "Optional, filter links to records published in that group.", required = false) @RequestParam(required = false) Integer[] groupIdFilter, @@ -207,7 +211,7 @@ public Page getRecordLinksPost( return getLinks(filter, groupIdFilter, groupOwnerIdFilter, pageRequest, userSession); } private Page getLinks( - JSONObject filter, + LinkFilter filter, Integer[] groupIdFilter, Integer[] groupOwnerIdFilter, Pageable pageRequest, @@ -228,22 +232,22 @@ private Page getLinks( Integer stateToMatch = null; String url = null; List associatedRecords = null; - if (filter.has("lastState")) { + if (filter.getLastState() != null) { stateToMatch = 0; - if (filter.getString("lastState").equalsIgnoreCase("ok")) { + if (filter.getLastState().equalsIgnoreCase("ok")) { stateToMatch = 1; - } else if (filter.getString("lastState").equalsIgnoreCase("ko")) { + } else if (filter.getLastState().equalsIgnoreCase("ko")) { stateToMatch = -1; } } - if (filter.has("url")) { - url = filter.getString("url"); + if (filter.getUrl() != null) { + url = filter.getUrl(); } - if (filter.has("records")) { + if (filter.getRecords() != null) { associatedRecords = Arrays.stream( - filter.getString("records").split(" ") + filter.getRecords().split(" ") ).collect(Collectors.toList()); } @@ -277,9 +281,9 @@ private Page getLinks( @PreAuthorize("isAuthenticated()") @ResponseBody public void getRecordLinksAsCsv( - @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421', groupId: 12}\", lastState being 'ok'/'ko'/'unknown'", required = false) + @Parameter(description = "Filter, e.g. \"{url: 'png', lastState: 'ko', records: 'e421'}\", lastState being 'ok'/'ko'/'unknown'", required = false) @RequestParam(required = false) - JSONObject filter, + LinkFilter filter, @Parameter(description = "Optional, filter links to records published in that group.", required = false) @RequestParam(required = false) Integer[] groupIdFilter, @@ -445,4 +449,50 @@ private MAnalyseProcess getRegistredMAnalyseProcess() { mAnalyseProcesses.addFirst(mAnalyseProcess); return mAnalyseProcess; } + + @InitBinder + public void initBinder(WebDataBinder dataBinder) { + dataBinder.registerCustomEditor(LinkFilter.class, new PropertyEditorSupport() { + Object value; + @Override + public Object getValue() { + return value; + } + + @Override + public void setAsText(String text) throws IllegalArgumentException { + value = new Gson().fromJson(text, LinkFilter.class); + } + }); + } + + private static class LinkFilter { + private String url; + private String lastState; + private String records; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getLastState() { + return lastState; + } + + public void setLastState(String lastState) { + this.lastState = lastState; + } + + public String getRecords() { + return records; + } + + public void setRecords(String records) { + this.records = records; + } + } } diff --git a/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java b/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java index f09a537da72..f6a86262247 100644 --- a/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java +++ b/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java @@ -109,6 +109,7 @@ public class MapServersApi { @ResponseStatus(HttpStatus.OK) @PreAuthorize("hasAuthority('Editor')") @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "List of all mapservers."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_EDITOR) }) List getMapservers() throws Exception { @@ -257,7 +258,7 @@ public void updateMapserver( @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_REVIEWER) }) @ResponseStatus(HttpStatus.NO_CONTENT) - public void updateMapserver( + public void updateMapserverAuth( @Parameter( description = API_PARAM_MAPSERVER_IDENTIFIER, required = true, diff --git a/services/src/main/java/org/fao/geonet/api/pages/PageProperties.java b/services/src/main/java/org/fao/geonet/api/pages/PageProperties.java index 14ce1da943c..227908a4082 100644 --- a/services/src/main/java/org/fao/geonet/api/pages/PageProperties.java +++ b/services/src/main/java/org/fao/geonet/api/pages/PageProperties.java @@ -19,6 +19,7 @@ public class PageProperties implements Serializable { private String link; private String content; private String label; + private String icon; private Page.PageFormat format; private Page page; @@ -34,6 +35,7 @@ public PageProperties(Page p) { sections = p.getSections(); status = p.getStatus(); label = p.getLabel(); + icon = p.getIcon(); } @Override @@ -104,4 +106,12 @@ public String getLabel() { public void setLabel(String label) { this.label = label; } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } } diff --git a/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java b/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java index 678310239ab..491c04878e2 100644 --- a/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java +++ b/services/src/main/java/org/fao/geonet/api/pages/PagesAPI.java @@ -172,8 +172,8 @@ private ResponseEntity createPage(PageProperties pageProperties, Optional page = pageRepository.findById(new PageIdentity(language, pageId)); - if (!page.isPresent()) { - Page newPage = getEmptyHiddenDraftPage(pageProperties.getLanguage(), pageProperties.getPageId(), pageProperties.getLabel(), format); + if (page.isEmpty()) { + Page newPage = getEmptyHiddenDraftPage(pageProperties.getLanguage(), pageProperties.getPageId(), pageProperties.getLabel(), pageProperties.getIcon(), format); fillContent(data, link, content, newPage); if (section != null) { @@ -201,6 +201,7 @@ private ResponseEntity updatePageInternal(@NotNull String language, String newPageId = pageProperties.getPageId(); Page.PageFormat format = pageProperties.getFormat(); String newLabel = pageProperties.getLabel(); + String newIcon = pageProperties.getIcon(); checkValidLanguage(language); @@ -214,7 +215,7 @@ private ResponseEntity updatePageInternal(@NotNull String language, Optional page = pageRepository.findById(new PageIdentity(language, pageId)); - if (!page.isPresent()) { + if (page.isEmpty()) { throw new ResourceNotFoundException("Can't update non existing page " + pageId + "."); } Page pageToUpdate = page.get(); @@ -238,7 +239,8 @@ private ResponseEntity updatePageInternal(@NotNull String language, format != null ? format : pageToUpdate.getFormat(), pageProperties.getSections() != null ? pageProperties.getSections() : pageToUpdate.getSections(), pageProperties.getStatus() != null ? pageProperties.getStatus() : pageToUpdate.getStatus(), - newLabel != null ? newLabel : pageToUpdate.getLabel()); + newLabel != null ? newLabel : pageToUpdate.getLabel(), + newIcon != null ? newIcon : pageToUpdate.getIcon()); pageRepository.save(pageCopy); pageRepository.delete(pageToUpdate); @@ -248,6 +250,7 @@ private ResponseEntity updatePageInternal(@NotNull String language, pageToUpdate.setSections(pageProperties.getSections() != null ? pageProperties.getSections() : pageToUpdate.getSections()); pageToUpdate.setStatus(pageProperties.getStatus() != null ? pageProperties.getStatus() : pageToUpdate.getStatus()); pageToUpdate.setLabel(newLabel); + pageToUpdate.setIcon(newIcon); pageRepository.save(pageToUpdate); } @@ -339,7 +342,7 @@ public ResponseEntity getPageContent( final Optional page = pageRepository.findById(new PageIdentity(language, pageId)); - if (!page.isPresent()) { + if (page.isEmpty()) { return ResponseEntity.notFound().build(); } @@ -522,7 +525,7 @@ private Page searchPage(final String language, final String pageId, final PageRe throws ResourceNotFoundException { final Optional page = pageRepository.findById(new PageIdentity(language, pageId)); - if (!page.isPresent()) { + if (page.isEmpty()) { throw new ResourceNotFoundException("Page " + pageId + " not found"); } return page.get(); @@ -531,9 +534,9 @@ private Page searchPage(final String language, final String pageId, final PageRe /** * @return An empty hidden draft Page */ - protected Page getEmptyHiddenDraftPage(final String language, final String pageId, final String label, final Page.PageFormat format) { + protected Page getEmptyHiddenDraftPage(final String language, final String pageId, final String label, final String icon, final Page.PageFormat format) { final List sections = new ArrayList<>(); - return new Page(new PageIdentity(language, pageId), null, null, format, sections, Page.PageStatus.HIDDEN, label); + return new Page(new PageIdentity(language, pageId), null, null, format, sections, Page.PageStatus.HIDDEN, label, icon); } /** diff --git a/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java b/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java index 4feae357817..56734ce5db1 100644 --- a/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java +++ b/services/src/main/java/org/fao/geonet/api/processing/ValidateApi.java @@ -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) * @@ -37,12 +37,13 @@ import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.processing.report.SimpleMetadataProcessingReport; import org.fao.geonet.api.processing.report.registry.IProcessingReportRegistry; -import org.fao.geonet.api.records.editing.InspireValidatorUtils; +import org.fao.geonet.inspire.validator.InspireValidatorUtils; import org.fao.geonet.domain.AbstractMetadata; import org.fao.geonet.domain.MetadataValidation; import org.fao.geonet.domain.Pair; import org.fao.geonet.domain.SchematronRequirement; import org.fao.geonet.events.history.RecordValidationTriggeredEvent; +import org.fao.geonet.inspire.validator.MInspireEtfValidateProcess; import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.SchemaManager; @@ -55,7 +56,6 @@ import org.fao.geonet.utils.Xml; import org.jdom.Element; import org.jdom.Namespace; -import org.jdom.Text; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.http.HttpStatus; diff --git a/services/src/main/java/org/fao/geonet/api/records/CatalogApi.java b/services/src/main/java/org/fao/geonet/api/records/CatalogApi.java index 77f6b2833ea..e7547678245 100644 --- a/services/src/main/java/org/fao/geonet/api/records/CatalogApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/CatalogApi.java @@ -23,6 +23,9 @@ package org.fao.geonet.api.records; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableSet; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameters; @@ -42,7 +45,6 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.text.StrSubstitutor; -import org.elasticsearch.action.search.SearchResponse; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.API; import org.fao.geonet.api.ApiParams; @@ -175,7 +177,7 @@ private static String paramsAsString(Map requestParams) { summary = "Get a set of metadata records as ZIP", description = "Metadata Exchange Format (MEF) is returned. MEF is a ZIP file containing " + "the metadata as XML and some others files depending on the version requested. " + - "See http://geonetwork-opensource.org/manuals/trunk/eng/users/annexes/mef-format.html.") + "See https://docs.geonetwork-opensource.org/latest/annexes/mef-format/.") @GetMapping(value = "/zip", consumes = { MediaType.ALL_VALUE @@ -239,11 +241,6 @@ public void exportAsMef( required = false) @RequestParam(required = false, defaultValue = "true") boolean approved, - @RequestHeader( - value = HttpHeaders.ACCEPT, - defaultValue = "application/x-gn-mef-2-zip" - ) - String acceptHeader, @Parameter(hidden = true) HttpSession httpSession, @Parameter(hidden = true) @@ -265,6 +262,7 @@ public void exportAsMef( Log.info(Geonet.MEF, "Current record(s) in selection: " + uuidList.size()); ServiceContext context = ApiUtils.createServiceContext(request); + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? "application/x-gn-mef-2-zip" : request.getHeader(HttpHeaders.ACCEPT); MEFLib.Version version = MEFLib.Version.find(acceptHeader); if (version == MEFLib.Version.V1) { throw new IllegalArgumentException("MEF version 1 only support one record. Use the /records/{uuid}/formatters/zip to retrieve that format"); @@ -401,9 +399,11 @@ public void exportAsPdf( }); Element response = new Element("response"); - Arrays.asList(searchResponse.getHits().getHits()).forEach(h -> { + ObjectMapper objectMapper = new ObjectMapper(); + searchResponse.hits().hits().forEach(h1 -> { + Hit h = (Hit) h1; Element r = new Element("metadata"); - final Map source = h.getSourceAsMap(); + final Map source = objectMapper.convertValue(h.source(), Map.class); source.entrySet().forEach(e -> { Object v = e.getValue(); if (v instanceof String) { @@ -548,8 +548,9 @@ public void exportAsCsv( EsFilterBuilder.buildPermissionsFilter(ApiUtils.createServiceContext(httpRequest)), FIELDLIST_CORE, 0, maxhits); - List idsToExport = Arrays.stream(searchResponse.getHits().getHits()) - .map(h -> (String) h.getSourceAsMap().get("id")) + ObjectMapper objectMapper = new ObjectMapper(); + List idsToExport = (List) searchResponse.hits().hits().stream() + .map(h -> (String) objectMapper.convertValue(((Hit) h).source(), Map.class).get("id")) .collect(Collectors.toList()); // Determine filename to use diff --git a/services/src/main/java/org/fao/geonet/api/records/DoiApi.java b/services/src/main/java/org/fao/geonet/api/records/DoiApi.java index 69fa10aa7ad..ce59aa1d8e4 100644 --- a/services/src/main/java/org/fao/geonet/api/records/DoiApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/DoiApi.java @@ -48,6 +48,7 @@ import javax.servlet.http.HttpSession; import java.util.Map; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; @@ -57,7 +58,8 @@ @RequestMapping(value = { "/{portal}/api/records" }) -@Tag(name = API_CLASS_RECORD_TAG) +@Tag(name = API_CLASS_RECORD_TAG, + description = API_CLASS_RECORD_OPS) @Controller("doi") @PreAuthorize("hasAuthority('Editor')") @ReadWriteController diff --git a/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java b/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java index cf54416b94a..86fda4b1f6c 100644 --- a/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java @@ -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) * @@ -33,11 +33,11 @@ import org.apache.http.HttpStatus; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.NodeInfo; -import org.fao.geonet.api.API; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.exception.ResourceNotFoundException; -import org.fao.geonet.api.records.editing.InspireValidatorUtils; +import org.fao.geonet.inspire.validator.InspireValidationRunnable; +import org.fao.geonet.inspire.validator.InspireValidatorUtils; import org.fao.geonet.api.records.formatters.FormatType; import org.fao.geonet.api.records.formatters.FormatterApi; import org.fao.geonet.api.records.formatters.FormatterWidth; @@ -52,7 +52,6 @@ import org.fao.geonet.kernel.SchemaManager; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.kernel.setting.Settings; -import org.fao.geonet.repository.MetadataValidationRepository; import org.fao.geonet.repository.SourceRepository; import org.fao.geonet.schema.iso19139.ISO19139Namespaces; import org.fao.geonet.util.ThreadPool; @@ -83,6 +82,7 @@ import java.util.HashMap; import java.util.Map; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; @@ -90,7 +90,8 @@ @RequestMapping(value = { "/{portal}/api/records" }) -@Tag(name = API_CLASS_RECORD_TAG) +@Tag(name = API_CLASS_RECORD_TAG, + description = API_CLASS_RECORD_OPS) @Controller("inspire") @PreAuthorize("hasAuthority('Editor')") @ReadWriteController @@ -126,8 +127,7 @@ public class InspireValidationApi { @io.swagger.v3.oas.annotations.Operation( summary = "Get test suites available.", description = "TG13, TG2, ...") - @RequestMapping(value = "/{metadataUuid}/validate/inspire/testsuites", - method = RequestMethod.GET, + @GetMapping(value = "/{metadataUuid}/validate/inspire/testsuites", produces = { MediaType.APPLICATION_JSON_VALUE }) @@ -154,8 +154,7 @@ Map getTestSuites( + "An INSPIRE endpoint must be configured in Settings. " + "This activates an asyncronous process, this method does not return any report. " + "This method returns an id to be used to get the report.") - @RequestMapping(value = "/{metadataUuid}/validate/inspire", - method = RequestMethod.PUT, + @PutMapping(value = "/{metadataUuid}/validate/inspire", produces = { MediaType.TEXT_PLAIN_VALUE }) @@ -205,16 +204,16 @@ String validateRecordForInspire( String schema = metadata.getDataInfo().getSchemaId(); if (!schema.matches(supportedSchemaRegex)) { response.setStatus(HttpStatus.SC_NOT_ACCEPTABLE); - return String.format("INSPIRE validator does not support records in schema '%'. Schema must match expression '%' and have an ISO19139 formatter.", + return String.format("INSPIRE validator does not support records in schema '%s'. Schema must match expression '%s' and have an ISO19139 formatter.", schema, supportedSchemaRegex); } String id = String.valueOf(metadata.getId()); - String URL = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL); - String URL_QUERY = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL_QUERY); - if (StringUtils.isEmpty(URL_QUERY)) { - URL_QUERY = URL; + String inspireValidatorUrl = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL); + String inspireValidatorQueryUrl = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL_QUERY); + if (StringUtils.isEmpty(inspireValidatorQueryUrl)) { + inspireValidatorQueryUrl = inspireValidatorUrl; } ServiceContext context = ApiUtils.createServiceContext(request); @@ -268,7 +267,7 @@ String validateRecordForInspire( InputStream metadataToTest = convertElement2InputStream(md); - testId = inspireValidatorUtils.submitFile(context, URL, URL_QUERY, metadataToTest, testsuite, metadata.getUuid()); + testId = inspireValidatorUtils.submitFile(context, inspireValidatorUrl, inspireValidatorQueryUrl, metadataToTest, testsuite, metadata.getUuid()); } else { String portal = NodeInfo.DEFAULT_NODE; if (!NodeInfo.DEFAULT_NODE.equals(mode)) { @@ -288,10 +287,10 @@ String validateRecordForInspire( portal, ISO19139Namespaces.GMD.getURI(), metadataUuid); - testId = inspireValidatorUtils.submitUrl(context, URL, URL_QUERY, getRecordByIdUrl, testsuite, metadata.getUuid()); + testId = inspireValidatorUtils.submitUrl(context, inspireValidatorUrl, inspireValidatorQueryUrl, getRecordByIdUrl, testsuite, metadata.getUuid()); } - threadPool.runTask(new InspireValidationRunnable(context, URL, testId, metadata.getId())); + threadPool.runTask(new InspireValidationRunnable(context, inspireValidatorUrl, testId, metadata.getId())); return testId; } @@ -313,8 +312,7 @@ private InputStream convertElement2InputStream(Element md) description = "User MUST be able to edit the record to validate it. " + "An INSPIRE endpoint must be configured in Settings. " + "If the process is complete an object with status is returned. ") - @RequestMapping(value = "/{testId}/validate/inspire", - method = RequestMethod.GET, + @GetMapping(value = "/{testId}/validate/inspire", produces = { MediaType.APPLICATION_JSON_VALUE } @@ -341,15 +339,15 @@ Map checkValidation( HttpSession session ) throws Exception { - String URL = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL); + String inspireValidatorUrl = settingManager.getValue(Settings.SYSTEM_INSPIRE_REMOTE_VALIDATION_URL); ServiceContext context = ApiUtils.createServiceContext(request); try { - if (inspireValidatorUtils.isReady(context, URL, testId)) { + if (inspireValidatorUtils.isReady(context, inspireValidatorUrl, testId)) { Map values = new HashMap<>(); - values.put("status", inspireValidatorUtils.isPassed(context, URL, testId)); - values.put("report", inspireValidatorUtils.getReportUrl(URL, testId)); + values.put("status", inspireValidatorUtils.isPassed(context, inspireValidatorUrl, testId)); + values.put("report", inspireValidatorUtils.getReportUrl(inspireValidatorUrl, testId)); response.setStatus(HttpStatus.SC_OK); return values; diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java index 2323f1439ab..64bb64c4200 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java @@ -24,6 +24,8 @@ package org.fao.geonet.api.records; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -106,7 +108,7 @@ public class MetadataApi { private ApplicationContext context; - public static RelatedResponse getAssociatedResources( + public static RelatedResponse getRelatedResources( String language, ServiceContext context, AbstractMetadata md, RelatedItemType[] type, int start, int rows) throws Exception { // TODO PERF: ByPass XSL processing and create response directly @@ -132,14 +134,14 @@ public synchronized void setApplicationContext(ApplicationContext context) { } @io.swagger.v3.oas.annotations.Operation(summary = "Get a metadata record", - description = "Depending on the accept header the appropriate formatter is used. " + + description = "Accept header should indicate which is the appropriate format " + + "to return. It could be text/html, application/xml, application/zip, ..." + + "If no appropriate Accept header found, the XML format is returned." + + "Depending on the accept header the appropriate formatter is used. " + "When requesting a ZIP, a MEF version 2 file is returned. " + "When requesting HTML, the default formatter is used.") @RequestMapping(value = "/{metadataUuid:.+}", method = RequestMethod.GET, - consumes = { - MediaType.ALL_VALUE - }, produces = { MediaType.TEXT_HTML_VALUE, MediaType.APPLICATION_XML_VALUE, @@ -152,25 +154,26 @@ public synchronized void setApplicationContext(ApplicationContext context) { MediaType.ALL_VALUE }) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Return the record."), - @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW), - @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND) + @ApiResponse(responseCode = "200", description = "Return the record.", + content = @Content(schema = @Schema(type = "string", format = "binary"))), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW, + content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(type = "string")), + @Content(mediaType = MediaType.APPLICATION_XML_VALUE, schema = @Schema(type = "string")), + @Content(mediaType = MediaType.APPLICATION_XHTML_XML_VALUE, schema = @Schema(type = "string")) + }), + @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND, + content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = @Schema(type = "string")), + @Content(mediaType = MediaType.APPLICATION_XML_VALUE, schema = @Schema(type = "string")), + @Content(mediaType = MediaType.APPLICATION_XHTML_XML_VALUE, schema = @Schema(type = "string")) + }) }) public String getRecord( @Parameter(description = API_PARAM_RECORD_UUID, required = true) @PathVariable String metadataUuid, - @Parameter(description = "Accept header should indicate which is the appropriate format " + - "to return. It could be text/html, application/xml, application/zip, ..." + - "If no appropriate Accept header found, the XML format is returned.", - required = true) - @RequestHeader( - value = HttpHeaders.ACCEPT, - defaultValue = MediaType.APPLICATION_XML_VALUE, - required = false - ) - String acceptHeader, HttpServletResponse response, HttpServletRequest request ) @@ -181,6 +184,8 @@ public String getRecord( Log.debug(API.LOG_MODULE_NAME, e.getMessage(), e); throw new NotAllowedException(ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW); } + + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? MediaType.APPLICATION_XML_VALUE : request.getHeader(HttpHeaders.ACCEPT); List accept = Arrays.asList(acceptHeader.split(",")); String defaultFormatter = "xsl-view"; @@ -188,9 +193,10 @@ public String getRecord( || accept.contains(MediaType.APPLICATION_XHTML_XML_VALUE) || accept.contains("application/pdf")) { return "forward:" + (metadataUuid + "/formatters/" + defaultFormatter); - } else if (accept.contains(MediaType.APPLICATION_XML_VALUE) - || accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + } else if (accept.contains(MediaType.APPLICATION_XML_VALUE)) { return "forward:" + (metadataUuid + "/formatters/xml"); + } else if (accept.contains(MediaType.APPLICATION_JSON_VALUE)) { + return "forward:" + (metadataUuid + "/formatters/json"); } else if (accept.contains("application/zip") || accept.contains(MEF_V1_ACCEPT_TYPE) || accept.contains(MEF_V2_ACCEPT_TYPE)) { @@ -236,25 +242,63 @@ public ResponseEntity getRecordPermalink( } - @io.swagger.v3.oas.annotations.Operation(summary = "Get a metadata record as XML or JSON", - description = "") - @RequestMapping(value = - { - "/{metadataUuid}/formatters/xml", - "/{metadataUuid}/formatters/json" - }, + @io.swagger.v3.oas.annotations.Operation(summary = "Get a metadata record as JSON") + @RequestMapping(value = "/{metadataUuid}/formatters/json", method = RequestMethod.GET, - produces = { - MediaType.APPLICATION_XML_VALUE, - MediaType.APPLICATION_JSON_VALUE - }) + produces = MediaType.APPLICATION_JSON_VALUE + ) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Return the record."), + @ApiResponse(responseCode = "200", description = "Return the record.", + content = @Content(schema = @Schema(type = "string", format = "binary"))), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW) }) - public @ResponseBody - Object getRecordAs( + public Object getRecordAsJson( + @Parameter( + description = API_PARAM_RECORD_UUID, + required = true) + @PathVariable + String metadataUuid, + @Parameter(description = "Add XSD schema location based on standard configuration " + + "(see schema-ident.xml).", + required = false) + @RequestParam(required = false, defaultValue = "true") + boolean addSchemaLocation, + @Parameter(description = "Increase record popularity", + required = false) + @RequestParam(required = false, defaultValue = "true") + boolean increasePopularity, + @Parameter(description = "Add geonet:info details", + required = false) + @RequestParam(required = false, defaultValue = "false") + boolean withInfo, + @Parameter(description = "Download as a file", + required = false) + @RequestParam(required = false, defaultValue = "false") + boolean attachment, + @Parameter(description = "Download the approved version", + required = false) + @RequestParam(required = false, defaultValue = "true") + boolean approved, + HttpServletResponse response, + HttpServletRequest request + ) + throws Exception { + return getRecordAs(metadataUuid, addSchemaLocation, increasePopularity, withInfo, attachment, approved, response, request, MediaType.APPLICATION_JSON); + } + + @io.swagger.v3.oas.annotations.Operation(summary = "Get a metadata record as XML") + @RequestMapping(value = "/{metadataUuid}/formatters/xml", + method = RequestMethod.GET, + produces = MediaType.APPLICATION_XML_VALUE + ) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the record.", + content = @Content(schema = @Schema(type = "string", format = "binary"))), + @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW) + }) + @ResponseBody + public Object getRecordAsXml( @Parameter( description = API_PARAM_RECORD_UUID, required = true) @@ -281,13 +325,43 @@ Object getRecordAs( required = false) @RequestParam(required = false, defaultValue = "true") boolean approved, - @RequestHeader( - value = HttpHeaders.ACCEPT, - defaultValue = MediaType.APPLICATION_XML_VALUE - ) - String acceptHeader, HttpServletResponse response, HttpServletRequest request + ) + throws Exception { + return getRecordAs(metadataUuid, addSchemaLocation, increasePopularity, withInfo, attachment, approved, response, request, MediaType.APPLICATION_XML); + } + + /** + * Get records based on media type. Supports xml or json + * @return It will default to xml if the media type is not known. + */ + + /** + * Get records based on media type. + * + * @param metadataUuid Record UUID. + * @param addSchemaLocation Add XSD schema location based on standard configuration (see schema-ident.xml). + * @param increasePopularity Increase record popularity + * @param withInfo Add geonet:info details + * @param attachment Download as a file + * @param approved Download the approved version + * @param response object + * @param request object + * @param mediaType Supports xml or json - default to xml if the media type is not known. + * @return It will the object + * @throws Exception if record not found of permissions denied. + */ + private Object getRecordAs( + String metadataUuid, + boolean addSchemaLocation, + boolean increasePopularity, + boolean withInfo, + boolean attachment, + boolean approved, + HttpServletResponse response, + HttpServletRequest request, + MediaType mediaType ) throws Exception { AbstractMetadata metadata; @@ -352,7 +426,7 @@ Object getRecordAs( } } - boolean isJson = acceptHeader.contains(MediaType.APPLICATION_JSON_VALUE); + boolean isJson = MediaType.APPLICATION_JSON.equals(mediaType); String mode = (attachment) ? "attachment" : "inline"; response.setHeader("Content-Disposition", String.format( @@ -367,7 +441,7 @@ Object getRecordAs( summary = "Get a metadata record as ZIP", description = "Metadata Exchange Format (MEF) is returned. MEF is a ZIP file containing " + "the metadata as XML and some others files depending on the version requested. " + - "See http://geonetwork-opensource.org/manuals/trunk/eng/users/annexes/mef-format.html.") + "See https://docs.geonetwork-opensource.org/latest/annexes/mef-format/.") @RequestMapping(value = "/{metadataUuid}/formatters/zip", method = RequestMethod.GET, consumes = { @@ -426,11 +500,6 @@ void getRecordAsZip( required = false) @RequestParam(required = false, defaultValue = "true") boolean approved, - @RequestHeader( - value = HttpHeaders.ACCEPT, - defaultValue = "application/x-gn-mef-2-zip" - ) - String acceptHeader, HttpServletResponse response, HttpServletRequest request ) @@ -445,6 +514,7 @@ void getRecordAsZip( Path stylePath = dataDirectory.getWebappDir().resolve(Geonet.Path.SCHEMAS); Path file = null; ServiceContext serviceContext = ApiUtils.createServiceContext(request); + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? "application/x-gn-mef-2-zip" : request.getHeader(HttpHeaders.ACCEPT); MEFLib.Version version = MEFLib.Version.find(acceptHeader); try { if (version == MEFLib.Version.V1) { @@ -471,7 +541,7 @@ void getRecordAsZip( if (withRelated) { // Adding children in MEF file - RelatedResponse related = getAssociatedResources( + RelatedResponse related = getRelatedResources( metadataUuid, null, approved, 0, 100, request); uuidsToExport.addAll(getUuidsOfAssociatedRecords(related.getParent())); uuidsToExport.addAll(getUuidsOfAssociatedRecords(related.getChildren())); @@ -521,7 +591,7 @@ private List getUuidsOfAssociatedRecords(IListOnlyClassToArray associate @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW), @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND) }) - @ResponseStatus(HttpStatus.CREATED) + @ResponseStatus(HttpStatus.OK) public ResponseEntity getRecordPopularity( @Parameter(description = API_PARAM_RECORD_UUID, required = true) @@ -596,7 +666,7 @@ public ResponseEntity increaseRecordPopularity( summary = "Get record related resources", description = "Retrieve related services, datasets, onlines, thumbnails, sources, ... " + "to this records.
" + - "More info") + "More info") @RequestMapping(value = "/{metadataUuid:.+}/related", method = RequestMethod.GET, produces = { @@ -609,7 +679,7 @@ public ResponseEntity increaseRecordPopularity( @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_VIEW) }) @ResponseBody - public RelatedResponse getAssociatedResources( + public RelatedResponse getRelatedResources( @Parameter( description = API_PARAM_RECORD_UUID, required = true) @@ -644,7 +714,7 @@ public RelatedResponse getAssociatedResources( String language = languageUtils.getIso3langCode(request.getLocales()); final ServiceContext serviceContext = ApiUtils.createServiceContext(request); - return getAssociatedResources(language, serviceContext, md, type, start, rows); + return getRelatedResources(language, serviceContext, md, type, start, rows); } @io.swagger.v3.oas.annotations.Operation( @@ -680,7 +750,7 @@ public FeatureResponse getFeatureCatalog( Map decodeMap = new HashMap<>(); try { - RelatedResponse related = getAssociatedResources( + RelatedResponse related = getRelatedResources( metadataUuid, type, approved, 0, 100, request); if (isIncludedAttributeTable(related.getFcats())) { diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataAssociatedApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataAssociatedApi.java index 5859385b99c..5a62a945f15 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataAssociatedApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataAssociatedApi.java @@ -93,7 +93,7 @@ public synchronized void setApplicationContext(ApplicationContext context) { summary = "Get record associated resources", description = "Retrieve related services, datasets, sources, ... " + "to this records.
" + - "More info") + "More info") @RequestMapping(value = "/{metadataUuid:.+}/associated", method = RequestMethod.GET, produces = { diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataIndexApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataIndexApi.java index 57303c9be00..17314e31b87 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataIndexApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataIndexApi.java @@ -29,13 +29,11 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.server.UserSession; -import jeeves.server.context.ServiceContext; import jeeves.services.ReadWriteController; -import net.sf.json.JSONObject; +import jeeves.xlink.Processor; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; import org.fao.geonet.kernel.DataManager; -import org.fao.geonet.kernel.SelectionManager; import org.fao.geonet.kernel.datamanager.IMetadataUtils; import org.fao.geonet.kernel.search.index.BatchOpsMetadataReindexer; import org.fao.geonet.kernel.setting.SettingManager; @@ -46,7 +44,6 @@ import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.Set; @@ -86,7 +83,7 @@ public class MetadataIndexApi { }) public @ResponseBody - JSONObject index( + IndexResponse index( @Parameter(description = API_PARAM_RECORD_UUIDS_OR_SELECTION, required = false, example = "") @@ -122,14 +119,39 @@ JSONObject index( } } index = ids.size(); - new BatchOpsMetadataReindexer(dataManager, ids) - .process(settingManager.getSiteId(), false); - JSONObject res = new JSONObject(); - res.put("success", true); - res.put("count", index); + if (index > 0) { + // clean XLink Cache so that cache and index remain in sync + Processor.clearCache(); - return res; + new BatchOpsMetadataReindexer(dataManager, ids) + .process(settingManager.getSiteId(), false); + } + + IndexResponse indexResponse = new IndexResponse(); + indexResponse.setSuccess(true); + indexResponse.setCount(index); + return indexResponse; } + private static class IndexResponse { + private boolean success; + private int count; + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + } } diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java index 1c50daf45e3..0dfb298d2c3 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java @@ -253,7 +253,7 @@ public void deleteRecord( MediaType.APPLICATION_JSON_VALUE } ) - @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Report about deleted records."), + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Report about deleted records."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_EDITOR)}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -315,9 +315,9 @@ public SimpleMetadataProcessingReport deleteRecords( + "URL or file in a folder on the catalog server. When loading" + "from the catalog server folder, it might be faster to use a " + "local filesystem harvester.") - @RequestMapping(method = {RequestMethod.PUT}, produces = {MediaType.APPLICATION_JSON_VALUE}, consumes = { - MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE, - MediaType.APPLICATION_FORM_URLENCODED_VALUE}) + @RequestMapping(method = RequestMethod.PUT, + produces = MediaType.APPLICATION_JSON_VALUE, + consumes = MediaType.APPLICATION_XML_VALUE) @ApiResponses(value = {@ApiResponse(responseCode = "201", description = API_PARAM_REPORT_ABOUT_IMPORTED_RECORDS), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_EDITOR)}) @PreAuthorize("hasAuthority('Editor')") diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java index f40d2e293b0..2392c7fc2ae 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java @@ -84,7 +84,7 @@ public class MetadataProcessApi { SettingManager sm; @io.swagger.v3.oas.annotations.Operation(summary = "Get suggestions", description ="Analyze the record an suggest processes to improve the quality of the record.
" - + "More info") + + "More info") @RequestMapping(value = "/{metadataUuid}/processes", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -162,7 +162,7 @@ ResponseEntity processRecordPreview( RequestMethod.POST,}, produces = MediaType.APPLICATION_XML_VALUE) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) - @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Record processed and saved."), + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Record processed and saved."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_EDIT)}) public @ResponseBody ResponseEntity processRecord( diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java index 70f0872e85d..9ff4d556c57 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2023 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -48,6 +48,7 @@ import org.fao.geonet.events.history.RecordGroupOwnerChangeEvent; import org.fao.geonet.events.history.RecordOwnerChangeEvent; import org.fao.geonet.events.history.RecordPrivilegesChangeEvent; +import org.fao.geonet.events.md.MetadataUnpublished; import org.fao.geonet.kernel.AccessManager; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.datamanager.*; @@ -63,6 +64,8 @@ import org.fao.geonet.util.WorkflowUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.http.HttpStatus; @@ -91,7 +94,8 @@ @PreAuthorize("hasAuthority('Editor')") @Controller("recordSharing") @ReadWriteController -public class MetadataSharingApi { +public class MetadataSharingApi implements ApplicationEventPublisherAware +{ private static final String DEFAULT_PUBLICATION_TYPE_NAME = "default"; @Autowired @@ -157,6 +161,14 @@ public class MetadataSharingApi { @Autowired private IPublicationConfig publicationConfig; + private ApplicationEventPublisher eventPublisher; + + @Override + public void setApplicationEventPublisher( + ApplicationEventPublisher applicationEventPublisher) { + this.eventPublisher = applicationEventPublisher; + } + public static Vector retrievePrivileges(ServiceContext context, String id, Integer userId, Integer groupId) { OperationAllowedRepository opAllowRepo = context.getBean(OperationAllowedRepository.class); @@ -280,7 +292,7 @@ public void unpublish( "Clear first allows to unset all operations first before setting the new ones." + "Clear option does not remove reserved groups operation if user is not an " + "administrator, a reviewer or the owner of the record.
" + - "More info") + "More info") @RequestMapping( value = "/{metadataUuid}/sharing", method = RequestMethod.PUT @@ -357,7 +369,7 @@ public void share( @ResponseStatus(HttpStatus.CREATED) public @ResponseBody - MetadataProcessingReport publish( + MetadataProcessingReport publishMultipleRecords( @Parameter(description = ApiParams.API_PARAM_RECORD_UUIDS_OR_SELECTION, required = false) @RequestParam(required = false) String[] uuids, @@ -393,7 +405,7 @@ MetadataProcessingReport publish( @ResponseStatus(HttpStatus.CREATED) public @ResponseBody - MetadataProcessingReport unpublish( + MetadataProcessingReport unpublishMultipleRecords( @Parameter(description = ApiParams.API_PARAM_RECORD_UUIDS_OR_SELECTION, required = false) @RequestParam(required = false) String[] uuids, @@ -429,7 +441,7 @@ MetadataProcessingReport unpublish( @ResponseStatus(HttpStatus.CREATED) public @ResponseBody - MetadataProcessingReport share( + MetadataProcessingReport shareMultipleRecords( @Parameter(description = ApiParams.API_PARAM_RECORD_UUIDS_OR_SELECTION, required = false) @RequestParam(required = false) String[] uuids, @@ -549,12 +561,17 @@ private void setOperations( } } - if (notifyByMail) { - java.util.Optional allGroupPrivsBefore = - sharingBefore.getPrivileges().stream().filter(p -> p.getGroup() == ReservedGroup.all.getId()).findFirst(); + java.util.Optional allGroupPrivsBefore = + sharingBefore.getPrivileges().stream().filter(p -> p.getGroup() == ReservedGroup.all.getId()).findFirst(); + boolean publishedBefore = allGroupPrivsBefore.get().getOperations().get(ReservedOperation.view.name()); - boolean publishedBefore = allGroupPrivsBefore.get().getOperations().get(ReservedOperation.view.name()); + if (sharing.isClear() && publishedBefore && !metadataUtils.isMetadataPublished(metadata.getId())) { + // Throw the metadata unpublish event, when removing privileges, are not part of the parameters, + // not processed in setOperation / unsetOperation that triggers the metadata publish/unpublish events + eventPublisher.publishEvent(new MetadataUnpublished(metadata)); + } + if (notifyByMail) { java.util.Optional allGroupOpsAfter = privileges.stream().filter(p -> p.getGroup() == ReservedGroup.all.getId()).findFirst(); diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java index 11a2cd603d4..0e326143429 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java @@ -95,7 +95,7 @@ public class MetadataTagApi { @io.swagger.v3.oas.annotations.Operation( summary = "Get record tags", description = "Tags are used to classify information.
" + - "More info") + "More info") @GetMapping( value = "/{metadataUuid}/tags", produces = { @@ -389,9 +389,9 @@ public MetadataProcessingReport tagRecords( produces = { MediaType.APPLICATION_JSON_VALUE }) - @ResponseStatus(value = HttpStatus.NO_CONTENT) + @ResponseStatus(value = HttpStatus.OK) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Report about removed records."), + @ApiResponse(responseCode = "200", description = "Report about removed records."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_EDITOR) }) @PreAuthorize("hasAuthority('Editor')") diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java index 89ec9b7ab0f..76bb7f594f3 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataUtils.java @@ -23,6 +23,9 @@ package org.fao.geonet.api.records; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; +import co.elastic.clients.json.JsonData; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -31,9 +34,6 @@ import jeeves.server.context.ServiceContext; import org.apache.commons.lang.StringUtils; import org.apache.commons.text.StringEscapeUtils; -import org.elasticsearch.action.search.SearchResponse; -import org.elasticsearch.common.document.DocumentField; -import org.elasticsearch.search.SearchHit; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.GeonetContext; import org.fao.geonet.NodeInfo; @@ -294,29 +294,29 @@ public static Map> getAssociated( Set remoteRecords = relatedTypeDetails.getRemoteRecords(); List records = new ArrayList<>(); - if (result.getHits().getTotalHits().value > 0) { - for (SearchHit e : Arrays.asList(result.getHits().getHits())) { - allCatalogueUuids.add(e.getId()); + if (!result.hits().hits().isEmpty()) { + for (Hit e : (List) result.hits().hits()) { + allCatalogueUuids.add(e.id()); AssociatedRecord associatedRecord = new AssociatedRecord(); - associatedRecord.setUuid(e.getId()); + associatedRecord.setUuid(e.id()); // Set properties eg. remote, associationType, ... - associatedRecord.setProperties(relatedTypeDetails.recordsProperties.get(e.getId())); + associatedRecord.setProperties(relatedTypeDetails.recordsProperties.get(e.id())); // Add scripted field values to the properties of the record - if (!e.getFields().isEmpty()) { + if (!e.fields().isEmpty()) { FIELDLIST_RELATED_SCRIPTED.keySet().forEach(f -> { - DocumentField dc = e.getFields().get(f); - + JsonData dc = (JsonData) e.fields().get(f); + if (dc != null) { if (associatedRecord.getProperties() == null) { associatedRecord.setProperties(new HashMap<>()); } - associatedRecord.getProperties().put(dc.getName(), dc.getValue()); + associatedRecord.getProperties().put(f, dc.toJson().asJsonArray().get(0).toString().replaceAll("^\"|\"$", "")); } }); } - JsonNode source = mapper.readTree(e.getSourceAsString()); + JsonNode source = mapper.convertValue(e.source(), JsonNode.class); ObjectNode doc = mapper.createObjectNode(); doc.set("_source", source); EsHTTPProxy.addUserInfo(doc, context); @@ -330,12 +330,12 @@ public static Map> getAssociated( associatedRecord.setRecord(source); associatedRecord.setOrigin(RelatedItemOrigin.catalog.name()); records.add(associatedRecord); - if (expectedUuids.contains(e.getId())) { - expectedUuids.remove(e.getId()); + if (expectedUuids.contains(e.id())) { + expectedUuids.remove(e.id()); } // Remote records may be found in current catalogue (eg. if harvested) - if (remoteRecords.contains(e.getId())) { - remoteRecords.remove(e.getId()); + if (remoteRecords.contains(e.id())) { + remoteRecords.remove(e.id()); } } } @@ -382,9 +382,9 @@ private static void assignPortalOrigin(int start, int size, EsSearchManager sear start, size); Set allPortalUuids = new HashSet<>(); - if (recordsInPortal.getHits().getTotalHits().value > 0) { - for (SearchHit e : Arrays.asList(recordsInPortal.getHits().getHits())) { - allPortalUuids.add(e.getId()); + if (!recordsInPortal.hits().hits().isEmpty()) { + for (Hit e : (List) recordsInPortal.hits().hits()) { + allPortalUuids.add(e.id()); } } @@ -662,12 +662,17 @@ private static Element search(String uuidQueryValue, String type, String from, S fromValue, (toValue - fromValue)); Element typeResponse = new Element(type.equals("brothersAndSisters") ? "siblings" : type); - if (result.getHits().getTotalHits().value > 0) { + if (!result.hits().hits().isEmpty()) { // Build the old search service response format Element response = new Element("response"); - Arrays.asList(result.getHits().getHits()).forEach(e -> { + + ObjectMapper objectMapper = new ObjectMapper(); + + result.hits().hits().forEach(e1 -> { + Hit e = (Hit) e1; + Element recordMetadata = new Element("metadata"); - final Map source = e.getSourceAsMap(); + final Map source = objectMapper.convertValue(e.source(), Map.class); recordMetadata.addContent(new Element("id").setText((String) source.get(Geonet.IndexFieldNames.ID))); recordMetadata.addContent(new Element("uuid").setText((String) source.get(Geonet.IndexFieldNames.UUID))); if (type.equals("brothersAndSisters")) { @@ -718,9 +723,10 @@ public static Set getUuidsToExport(String query) throws Exception { int size = Integer.parseInt(si.getSelectionMaxRecords()); final SearchResponse result = searchMan.query(query, null, from, size); - if (result.getHits().getTotalHits().value > 0) { - final SearchHit[] elements = result.getHits().getHits(); - Arrays.asList(elements).forEach(e -> uuids.add((String) e.getSourceAsMap().get(Geonet.IndexFieldNames.UUID))); + if (!result.hits().hits().isEmpty()) { + final List elements = result.hits().hits(); + ObjectMapper objectMapper = new ObjectMapper(); + elements.forEach(e -> uuids.add((String) objectMapper.convertValue(e.source(), Map.class).get(Geonet.IndexFieldNames.UUID))); } Log.info(Geonet.MEF, " Found " + uuids.size() + " record(s)."); return uuids; diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java index 3c2bef091c9..be90091b318 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java @@ -50,13 +50,15 @@ import javax.servlet.http.HttpServletRequest; import java.nio.file.Path; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; import static org.fao.geonet.api.ApiParams.API_PARAM_RECORD_UUID; @EnableWebMvc @Controller @Service -@Tag(name = "records", - description = "Metadata record operations") +@Tag(name = API_CLASS_RECORD_TAG, + description = API_CLASS_RECORD_OPS) public class AttachmentsActionsApi { private final ApplicationContext appContext = ApplicationContextHolder.get(); @Autowired @@ -91,7 +93,7 @@ public void init() { @io.swagger.v3.oas.annotations.Operation( summary = "Create an overview using the map print module", - description = "More info" + description = "More info" //response = MetadataResource.class) ) @RequestMapping( diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java index 01830efc5f6..b1b27ecfa30 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java @@ -1,6 +1,6 @@ /* * ============================================================================= - * === 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) * === @@ -25,7 +25,12 @@ package org.fao.geonet.api.records.attachments; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_RECORD_TAG; + import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -41,13 +46,10 @@ import org.fao.geonet.events.history.AttachmentDeletedEvent; import org.fao.geonet.util.ImageUtil; import org.springframework.context.ApplicationContext; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; -import org.springframework.util.MultiValueMap; import org.springframework.util.StreamUtils; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; @@ -65,7 +67,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.awt.image.BufferedImage; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URL; import java.nio.file.Files; @@ -80,7 +81,8 @@ @EnableWebMvc @Service @RequestMapping(value = {"/{portal}/api/records/{metadataUuid}/attachments"}) -@Tag(name = "records", description = "Metadata record operations") +@Tag(name = API_CLASS_RECORD_TAG, + description = API_CLASS_RECORD_OPS) public class AttachmentsApi { public static final Integer MIN_IMAGE_SIZE = 1; public static final Integer MAX_IMAGE_SIZE = 2048; @@ -107,15 +109,22 @@ public static String getFileContentType(Path file) throws IOException { case "png": case "gif": case "bmp": + contentType = "image/" + ext; + break; case "tif": case "tiff": + contentType = "image/tiff"; + break; case "jpg": case "jpeg": - contentType = "image/" + ext; + contentType = "image/jpeg"; break; case "txt": + contentType = "text/plain"; + break; + case "htm": case "html": - contentType = "text/" + ext; + contentType = "text/html"; break; default: contentType = "application/" + ext; @@ -149,7 +158,7 @@ public List getResources() { return null; } - @io.swagger.v3.oas.annotations.Operation(summary = "List all metadata attachments", description = "More info") + @io.swagger.v3.oas.annotations.Operation(summary = "List all metadata attachments", description = "More info") @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.OK) @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Return the record attachments."), @@ -190,7 +199,9 @@ public void delResources( @io.swagger.v3.oas.annotations.Operation(summary = "Create a new resource for a given metadata") @PreAuthorize("hasAuthority('Editor')") - @RequestMapping(method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(method = RequestMethod.POST, + consumes = MediaType.ALL_VALUE, + produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Attachment uploaded."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_EDIT)}) @@ -246,7 +257,8 @@ public MetadataResource putResourceFromURL( // @PreAuthorize("permitAll") @RequestMapping(value = "/{resourceId:.+}", method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) - @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Record attachment."), + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Record attachment.", + content = @Content(schema = @Schema(type = "string", format = "binary"))), @ApiResponse(responseCode = "403", description = "Operation not allowed. " + "User needs to be able to download the resource.")}) public void getResource( diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java index e38140c0809..d77eea18e68 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java @@ -596,7 +596,7 @@ public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required @ApiResponses(value = {@ApiResponse(responseCode = "201", description = "Element reordered."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_EDIT)}) @ResponseBody - public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required = true) @PathVariable String metadataUuid, + public void reorderElement(@Parameter(description = API_PARAM_RECORD_UUID, required = true) @PathVariable String metadataUuid, @Parameter(description = "Reference of the element to move.", required = true) @RequestParam String ref, @Parameter(description = "Direction", required = true) @PathVariable Direction direction, @Parameter(description = "Should attributes be shown on the editor snippet?", required = false) @RequestParam(defaultValue = "false") boolean displayAttributes, diff --git a/services/src/main/java/org/fao/geonet/api/records/extent/MapRenderer.java b/services/src/main/java/org/fao/geonet/api/records/extent/MapRenderer.java index 5fe0922a251..21673b2aea0 100644 --- a/services/src/main/java/org/fao/geonet/api/records/extent/MapRenderer.java +++ b/services/src/main/java/org/fao/geonet/api/records/extent/MapRenderer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2022 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) * @@ -24,7 +24,6 @@ package org.fao.geonet.api.records.extent; import jeeves.server.context.ServiceContext; -import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.fao.geonet.api.regions.GeomFormat; import org.fao.geonet.exceptions.BadParameterEx; @@ -33,15 +32,12 @@ import org.fao.geonet.kernel.region.RegionsDAO; import org.fao.geonet.kernel.setting.SettingManager; import org.fao.geonet.kernel.setting.Settings; -import org.fao.geonet.lib.Lib; import org.geotools.geometry.jts.JTS; -import org.geotools.geometry.jts.JTSFactoryFinder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.referencing.CRS; import org.locationtech.jts.awt.ShapeWriter; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryFactory; import org.geotools.api.metadata.extent.Extent; import org.geotools.api.metadata.extent.GeographicBoundingBox; import org.geotools.api.metadata.extent.GeographicExtent; @@ -49,15 +45,9 @@ import org.geotools.api.referencing.operation.MathTransform; import org.springframework.context.ApplicationContext; -import javax.imageio.ImageIO; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; import java.util.Collection; import java.util.Map; import java.util.SortedSet; @@ -84,35 +74,48 @@ public static AffineTransform worldToScreenTransform(Envelope mapExtent, Dimensi return new AffineTransform(scaleX, 0.0d, 0.0d, -scaleY, tx, ty); } + /** + * Returns a bounding box geometry. + * + * @param geom Bounding box geometry. + * @param srs Bounding box geometry srs. + * @param useGeodesicExtents false: returns the bounding box geometry as a rectangle (using min / max bounds). + * true: returns the bounding box geometry. + * @return bounding box geometry. + */ + public static Geometry getGeometryExtent(Geometry geom, String srs, boolean useGeodesicExtents) { + boolean isGlobalSrs = srs.equals("EPSG:4326") || srs.equals("EPSG:3857"); + + return (!isGlobalSrs && !useGeodesicExtents ? geom.getEnvelope() : geom); + } + /** * Check if a geometry is in the domain of validity of a projection and if not return the * intersection of the geometry with the coordinate system domain of validity. */ public static Geometry computeGeomInDomainOfValidity(Geometry geom, CoordinateReferenceSystem mapCRS) { - final GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null); final Extent domainOfValidity = mapCRS.getDomainOfValidity(); Geometry adjustedGeom = geom; if (domainOfValidity != null) { for (final GeographicExtent extent : domainOfValidity.getGeographicElements()) { - if (Boolean.FALSE.equals(extent.getInclusion())) { + if ((extent == null) || (Boolean.FALSE.equals(extent.getInclusion()))) { continue; } + if (extent instanceof GeographicBoundingBox) { - if (extent != null) { - GeographicBoundingBox box = (GeographicBoundingBox) extent; - - Envelope env = new Envelope( - box.getWestBoundLongitude(), - box.getEastBoundLongitude(), - box.getSouthBoundLatitude(), - box.getNorthBoundLatitude()); - if (env.contains(geom.getEnvelopeInternal())) { - return geom; - } else { - Geometry extentPolygon = JTS.toGeometry(env); - adjustedGeom = geom.intersection(extentPolygon); - } + GeographicBoundingBox box = (GeographicBoundingBox) extent; + + Envelope env = new Envelope( + box.getWestBoundLongitude(), + box.getEastBoundLongitude(), + box.getSouthBoundLatitude(), + box.getNorthBoundLatitude()); + if (env.contains(geom.getEnvelopeInternal())) { + return geom; + } else { + Geometry extentPolygon = JTS.toGeometry(env); + adjustedGeom = geom.intersection(extentPolygon); } } } @@ -123,12 +126,15 @@ public static Geometry computeGeomInDomainOfValidity(Geometry geom, CoordinateRe } public BufferedImage render(String id, String srs, Integer width, Integer height, - String background, String geomParam, String geomType, String geomSrs, String fillColor, String strokeColor) throws Exception { + String background, String geomParam, String geomType, + String geomSrs, String fillColor, String strokeColor) throws Exception { ApplicationContext appContext = context.getApplicationContext(); Map regionGetMapBackgroundLayers = appContext.getBean("regionGetMapBackgroundLayers", Map.class); SortedSet regionGetMapExpandFactors = appContext.getBean("regionGetMapExpandFactors", SortedSet.class); SettingManager settingManager = appContext.getBean(SettingManager.class); + boolean useGeodesicExtents = settingManager.getValueAsBool(Settings.REGION_GETMAP_GEODESIC_EXTENTS, false); + Geometry geom = null; if (id != null) { Collection daos = context.getApplicationContext().getBeansOfType(RegionsDAO.class).values(); @@ -192,7 +198,6 @@ public BufferedImage render(String id, String srs, Integer width, Integer height ; BufferedImage baseMapImage = baseMapRenderer.render(); - // ImageIO.write(baseMapImage, "png", new File("delme.png")); image = baseMapImage; } else { @@ -209,8 +214,9 @@ public BufferedImage render(String id, String srs, Integer width, Integer height Color geomStrokeColor = getColor(strokeColor, new Color(0, 0, 0, 255)); AffineTransform worldToScreenTransform = worldToScreenTransform(bboxOfImage, imageDimensions); for (int i = 0; i < geom.getNumGeometries(); i++) { + Geometry geomExtent = MapRenderer.getGeometryExtent(geom.getGeometryN(i), srs, useGeodesicExtents); // draw each included geometry separately to ensure they are filled correctly - Shape shape = worldToScreenTransform.createTransformedShape(shapeWriter.toShape(geom.getGeometryN(i))); + Shape shape = worldToScreenTransform.createTransformedShape(shapeWriter.toShape(geomExtent)); graphics.setColor(geomFillColor); graphics.fill(shape); diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/CacheApi.java b/services/src/main/java/org/fao/geonet/api/records/formatters/CacheApi.java index fa0ce65a394..d90ea166d70 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/CacheApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/CacheApi.java @@ -23,6 +23,9 @@ package org.fao.geonet.api.records.formatters; +import static org.fao.geonet.api.ApiParams.API_CLASS_FORMATTERS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_FORMATTERS_TAG; + import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -40,8 +43,8 @@ @RequestMapping(value = { "/{portal}/api/formatters" }) -@Tag(name = "formatters", - description = "Formatter operations") +@Tag(name = API_CLASS_FORMATTERS_TAG, + description = API_CLASS_FORMATTERS_OPS) @Controller("formatters") @ReadWriteController public class CacheApi { diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterAdminApi.java b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterAdminApi.java index 2541576729a..db2b6fb6eac 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterAdminApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterAdminApi.java @@ -73,6 +73,8 @@ import java.util.List; import java.util.Set; +import static org.fao.geonet.api.ApiParams.API_CLASS_FORMATTERS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_FORMATTERS_TAG; import static org.fao.geonet.api.records.formatters.FormatterConstants.SCHEMA_PLUGIN_FORMATTER_DIR; import static org.fao.geonet.api.records.formatters.FormatterConstants.VIEW_XSL_FILENAME; import static org.springframework.web.bind.annotation.RequestMethod.GET; @@ -82,8 +84,8 @@ * * @author jeichar */ -@Tag(name = "formatters", - description = "Formatter admin operations") +@Tag(name = API_CLASS_FORMATTERS_TAG, + description = API_CLASS_FORMATTERS_OPS) @Controller("formattersList") public class FormatterAdminApi extends AbstractFormatService { 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 a548af626bd..1a9830c3ef6 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 @@ -186,11 +186,6 @@ public void getRecordFormattedBy( @Parameter( description = "Formatter type to use." ) - @RequestHeader( - value = HttpHeaders.ACCEPT, - defaultValue = MediaType.TEXT_HTML_VALUE - ) - String acceptHeader, @PathVariable( value = "formatterId" ) final String formatterId, @@ -225,11 +220,13 @@ public void getRecordFormattedBy( Locale locale = languageUtils.parseAcceptLanguage(servletRequest.getLocales()); + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? MediaType.TEXT_HTML_VALUE : request.getHeader(HttpHeaders.ACCEPT); + // TODO : // if text/html > xsl_view // if application/pdf > xsl_view and PDF output // if application/x-gn-+(xml|html|pdf|text) - // Force PDF ouutput when URL parameter is set. + // Force PDF output when URL parameter is set. // This is useful when making GET link to PDF which // can not use headers. if (MediaType.ALL_VALUE.equals(acceptHeader)) { @@ -262,9 +259,12 @@ public void getRecordFormattedBy( } - Boolean hideWithheld = true; -// final boolean hideWithheld = Boolean.TRUE.equals(hide_withheld) || -// !context.getBean(AccessManager.class).canEdit(context, resolvedId); + final ServiceContext context = createServiceContext( + language, + formatType, + request.getNativeRequest(HttpServletRequest.class)); + + Boolean hideWithheld = !context.getBean(AccessManager.class).canEdit(context, String.valueOf(metadata.getId())); Key key = new Key(metadata.getId(), language, formatType, formatterId, hideWithheld, width); final boolean skipPopularityBool = false; @@ -272,11 +272,6 @@ public void getRecordFormattedBy( Validator validator; - final ServiceContext context = createServiceContext( - language, - formatType, - request.getNativeRequest(HttpServletRequest.class)); - if (changeDate != null) { final long changeDateAsTime = changeDate.toDate().getTime(); long roundedChangeDate = changeDateAsTime / 1000 * 1000; @@ -414,8 +409,6 @@ public HttpEntity getCachedPublicMetadata( * @param id the id, uuid or fileIdentifier of the metadata * @param xslid the id of the formatter * @param skipPopularity if true then don't increment popularity - * @param hide_withheld if true hideWithheld (private) elements even if the current user would - * normally have access to them. * @param width the approximate size of the element that the formatter output will be * embedded in compared to the full device width. Allowed options are the * enum values: {@link org.fao.geonet.api.records.formatters.FormatterWidth} @@ -431,7 +424,6 @@ public void exec( @RequestParam(value = "uuid", required = false) final String uuid, @RequestParam(value = "xsl", required = false) final String xslid, @RequestParam(defaultValue = "n") final String skipPopularity, - @RequestParam(value = "hide_withheld", required = false) final Boolean hide_withheld, @RequestParam(value = "width", defaultValue = "_100") final FormatterWidth width, final NativeWebRequest request) throws Exception { final FormatType formatType = FormatType.valueOf(type.toLowerCase()); @@ -440,8 +432,7 @@ public void exec( ServiceContext context = createServiceContext(lang, formatType, request.getNativeRequest(HttpServletRequest.class)); Lib.resource.checkPrivilege(context, resolvedId, ReservedOperation.view); - final boolean hideWithheld = Boolean.TRUE.equals(hide_withheld) || - !context.getBean(AccessManager.class).canEdit(context, resolvedId); + final boolean hideWithheld = !context.getBean(AccessManager.class).canEdit(context, resolvedId); Key key = new Key(Integer.parseInt(resolvedId), lang, formatType, xslid, hideWithheld, width); final boolean skipPopularityBool = skipPopularity.equals("y"); @@ -610,11 +601,6 @@ public Pair getMetadata(ServiceContext context, int i Element metadata = serializer.removeHiddenElements(false, md, true); if (doXLinks) Processor.processXLink(metadata, context); - boolean withholdWithheldElements = hide_withheld != null && hide_withheld; - if (XmlSerializer.getThreadLocal(false) != null || withholdWithheldElements) { - XmlSerializer.getThreadLocal(true).setForceFilterEditOperation(withholdWithheldElements); - } - return Pair.read(metadata, md); } 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 b078c626117..d16513f6c92 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 @@ -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) * @@ -28,11 +28,14 @@ import com.lowagie.text.Image; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; +import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.records.attachments.Store; import org.fao.geonet.api.records.extent.MapRenderer; import org.fao.geonet.api.records.extent.MetadataExtentApi; import org.fao.geonet.constants.Geonet; import org.fao.geonet.constants.Params; +import org.fao.geonet.domain.MetadataResourceVisibility; import org.fao.geonet.utils.Log; import org.xhtmlrenderer.extend.FSImage; import org.xhtmlrenderer.extend.ReplacedElement; @@ -82,10 +85,11 @@ private static Set getSupportedExts() { if (imgFormatExts == null) { synchronized (ImageReplacedElementFactory.class) { if (imgFormatExts == null) { - imgFormatExts = Sets.newHashSet(); + Set tmpImgFormatExts = Sets.newHashSet(); for (String ext : ImageIO.getReaderFileSuffixes()) { - imgFormatExts.add(ext.toLowerCase()); + tmpImgFormatExts.add(ext.toLowerCase()); } + imgFormatExts = tmpImgFormatExts; } } } @@ -93,9 +97,11 @@ private static Set getSupportedExts() { return imgFormatExts; } - static private Pattern ONE_EXTENT_API_REGEX = Pattern.compile(".*/(.*)/extents/([0-9]+)\\.png.*"); - static private Pattern ALL_EXTENT_API_REGEX = Pattern.compile(".*/(.*)/extents\\.png.*"); - static private final String EXTENT_XPATH = ".//*[local-name() ='extent']/*/*[local-name() = 'geographicElement']/*"; + private static final Pattern ONE_EXTENT_API_REGEX = Pattern.compile(".*/(.*)/extents/(\\d+)\\.png.*"); + private static final Pattern ALL_EXTENT_API_REGEX = Pattern.compile(".*/(.*)/extents\\.png.*"); + private static final String EXTENT_XPATH = ".//*[local-name() ='extent']/*/*[local-name() = 'geographicElement']/*"; + + private static final String DEFAULT_SRS = "EPSG:4326"; @Override public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockBox box, @@ -109,15 +115,15 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockB if (!"img".equals(nodeName)) { try { return superFactory.createReplacedElement(layoutContext, box, userAgentCallback, cssWidth, cssHeight); - } catch (Throwable e) { + } catch (Exception e) { return new EmptyReplacedElement(cssWidth, cssHeight); } } - String src = element.getAttribute("src"); + String baseUrlNoLang = baseURL.substring(0, baseURL.length() - 4); - boolean useExtentApi = src.startsWith(baseURL.substring(0, baseURL.length() - 4)) + boolean useExtentApi = src.startsWith(baseUrlNoLang) && mapRenderer != null && (ALL_EXTENT_API_REGEX.matcher(src).matches() || ONE_EXTENT_API_REGEX.matcher(src).matches()); @@ -135,7 +141,7 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockB regionId = String.format("metadata:@id%s:@xpath(%s)[%s]", ApiUtils.getInternalId(oneMatcher.group(1), true), EXTENT_XPATH, oneMatcher.group(2)); } Map parameters = getParams(src); - String srs = parameters.get(MetadataExtentApi.MAP_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.MAP_SRS_PARAM) : "EPSG:4326"; + String srs = parameters.get(MetadataExtentApi.MAP_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.MAP_SRS_PARAM) : DEFAULT_SRS; Integer width = parameters.get(MetadataExtentApi.WIDTH_PARAM) != null ? Integer.parseInt(parameters.get(MetadataExtentApi.WIDTH_PARAM)) : null; Integer height = parameters.get(MetadataExtentApi.HEIGHT_PARAM) != null ? Integer.parseInt(parameters.get(MetadataExtentApi.HEIGHT_PARAM)) : null; String background = parameters.get(MetadataExtentApi.BACKGROUND_PARAM); @@ -145,20 +151,25 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockB } float factor = layoutContext.getDotsPerPixel(); return loadImage(layoutContext, box, userAgentCallback, cssWidth, cssHeight, new BufferedImageLoader(image), factor); - } else if (src.startsWith(baseURL + "region.getmap.png") | src.endsWith("/geom.png") && mapRenderer != null) { + } else if (src.startsWith(baseURL + "region.getmap.png") || src.endsWith("/geom.png") && mapRenderer != null) { BufferedImage image = null; try { Map parameters = getParams(src); String id = parameters.get(Params.ID); - String srs = parameters.get(MetadataExtentApi.MAP_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.MAP_SRS_PARAM) : "EPSG:4326"; + String srs = parameters.get(MetadataExtentApi.MAP_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.MAP_SRS_PARAM) : DEFAULT_SRS; Integer width = parameters.get(MetadataExtentApi.WIDTH_PARAM) != null ? Integer.parseInt(parameters.get(MetadataExtentApi.WIDTH_PARAM)) : null; Integer height = parameters.get(MetadataExtentApi.HEIGHT_PARAM) != null ? Integer.parseInt(parameters.get(MetadataExtentApi.HEIGHT_PARAM)) : null; String background = parameters.get(MetadataExtentApi.BACKGROUND_PARAM); String geomParam = parameters.get(MetadataExtentApi.GEOM_PARAM); String geomType = parameters.get(MetadataExtentApi.GEOM_TYPE_PARAM) != null ? parameters.get(MetadataExtentApi.GEOM_TYPE_PARAM) : "WKT"; - String geomSrs = parameters.get(MetadataExtentApi.GEOM_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.GEOM_SRS_PARAM) : "EPSG:4326"; + String geomSrs = parameters.get(MetadataExtentApi.GEOM_SRS_PARAM) != null ? parameters.get(MetadataExtentApi.GEOM_SRS_PARAM) : DEFAULT_SRS; + if ((width == null) && (height == null)) { + // Width or height are required. If not set the default width with the same value + // as defined in MetadataExtentApi.getOneRecordExtentAsImage + width = 300; + } image = mapRenderer.render( id, srs, width, height, background, geomParam, geomType, geomSrs, null, null); } catch (Exception e) { @@ -189,6 +200,21 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockB } float factor = layoutContext.getDotsPerPixel(); return loadImage(layoutContext, box, userAgentCallback, cssWidth, cssHeight, new UrlImageLoader(builder.toString()), factor); + + } else if (src.startsWith(baseUrlNoLang) && src.contains("/attachments/")) { + // Process attachments urls to load the images from the data directory + Matcher m = Pattern.compile(baseUrlNoLang + "api/records/(.*)/attachments/(.*)$").matcher(src); + if (m.find()) { + String uuid = m.group(1); + String file = m.group(2); + + float factor = layoutContext.getDotsPerPixel(); + return loadImage(layoutContext, box, userAgentCallback, cssWidth, cssHeight, new DataDirectoryImageLoader(uuid, file), factor); + } else if (isSupportedImageFormat(src)) { + float factor = layoutContext.getDotsPerPixel(); + return loadImage(layoutContext, box, userAgentCallback, cssWidth, cssHeight, new UrlImageLoader(src), factor); + } + } else if (isSupportedImageFormat(src)) { float factor = layoutContext.getDotsPerPixel(); return loadImage(layoutContext, box, userAgentCallback, cssWidth, cssHeight, new UrlImageLoader(src), factor); @@ -196,7 +222,7 @@ public ReplacedElement createReplacedElement(LayoutContext layoutContext, BlockB try { return superFactory.createReplacedElement(layoutContext, box, userAgentCallback, cssWidth, cssHeight); - } catch (Throwable e) { + } catch (Exception e) { return new EmptyReplacedElement(cssWidth, cssHeight); } } @@ -247,7 +273,7 @@ private ReplacedElement loadImage(LayoutContext layoutContext, BlockBox box, Use try { return superFactory.createReplacedElement(layoutContext, box, userAgentCallback, cssWidth, cssHeight); - } catch (Throwable e2) { + } catch (Exception e2) { return new EmptyReplacedElement(cssWidth, cssHeight); } } @@ -296,6 +322,35 @@ public Image loadImage() throws Exception { } } + + /** + * Class to load images from the metadata data directory. + */ + private class DataDirectoryImageLoader implements ImageLoader { + private final String uuid; + private final String file; + public DataDirectoryImageLoader(String uuid, String file) { + this.uuid = uuid; + this.file = file; + } + + @Override + public Image loadImage() throws Exception { + Store store = ApplicationContextHolder.get().getBean("filesystemStore", Store.class); + BufferedImage bufferedImage; + try (Store.ResourceHolder imageFile = store.getResourceInternal( + this.uuid, + MetadataResourceVisibility.PUBLIC, + this.file, true)) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + bufferedImage = ImageIO.read(imageFile.getPath().toFile()); + ImageIO.write(bufferedImage, "png", baos); + return Image.getInstance(baos.toByteArray()); + } + } + } + + /* Define an AWT BufferedImage image loader */ private class BufferedImageLoader implements ImageLoader { diff --git a/services/src/main/java/org/fao/geonet/api/records/model/Direction.java b/services/src/main/java/org/fao/geonet/api/records/model/Direction.java index 5434beeb525..5fe3746c9e0 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/Direction.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/Direction.java @@ -22,6 +22,9 @@ */ package org.fao.geonet.api.records.model; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum Direction { up, down } diff --git a/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusDto.java b/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusDto.java new file mode 100644 index 00000000000..be930fb79b1 --- /dev/null +++ b/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusDto.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2001-2024 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ + +package org.fao.geonet.api.records.model; + +import java.util.LinkedHashMap; + +import org.fao.geonet.domain.ISODate; +import org.fao.geonet.domain.MetadataStatus; +import org.fao.geonet.domain.StatusValue; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.JsonIdentityReference; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; + +/* Contains all the important fields from + org.fao.geonet.domain.MetadataStatus + and used as a model object for api mapping */ + +public class MetadataStatusDto { + + private int id; + private ISODate _changedate; + private int metadataId; + private int userId; + private String changeMessage; + private StatusValue statusValue; + private int _owner; + private ISODate _duedate; + private ISODate _closedate; + private String previousState; + private String currentState; + + private MetadataStatus relatedMetadataStatus; + private String uuid; + private LinkedHashMap titles; + + public MetadataStatusDto(MetadataStatus metadataStatus) { + setUuid(metadataStatus.getUuid()); + setId(metadataStatus.getId()); + setUserId(metadataStatus.getUserId()); + setChangeDate(metadataStatus.getChangeDate()); + setChangeMessage(metadataStatus.getChangeMessage()); + setOwner(metadataStatus.getOwner()); + setDueDate(metadataStatus.getDueDate()); + setCloseDate(metadataStatus.getCloseDate()); + setStatusValue(metadataStatus.getStatusValue()); + setTitles(metadataStatus.getTitles()); + setRelatedMetadataStatus(metadataStatus.getRelatedMetadataStatus()); + setMetadataId(metadataStatus.getMetadataId()); + setCurrentState(metadataStatus.getCurrentState()); + setPreviousState(metadataStatus.getPreviousState()); + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public ISODate getChangeDate() { + return _changedate; + } + + public void setChangeDate(ISODate _changedate) { + this._changedate = _changedate; + } + + public int getMetadataId() { + return metadataId; + } + + public void setMetadataId(int metadataId) { + this.metadataId = metadataId; + } + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getChangeMessage() { + return changeMessage; + } + + public void setChangeMessage(String changeMessage) { + this.changeMessage = changeMessage; + } + + public StatusValue getStatusValue() { + return statusValue; + } + + public void setStatusValue(StatusValue statusValue) { + this.statusValue = statusValue; + } + + public Integer getOwner() { + return _owner; + } + + public void setOwner(int _owner) { + this._owner = _owner; + } + + public ISODate getDueDate() { + return _duedate; + } + + public void setDueDate(ISODate _duedate) { + this._duedate = _duedate; + } + + public ISODate getCloseDate() { + return _closedate; + } + + public void setCloseDate(ISODate _closedate) { + this._closedate = _closedate; + } + + public void setPreviousState(String previousState) { + this.previousState = previousState; + } + + public String getPreviousState() { + return previousState; + } + + public void setCurrentState(String currentState) { + this.currentState = currentState; + } + + public String getCurrentState() { + return currentState; + } + + + @JsonProperty(value = "relatedMetadataStatusId") + @JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id") + @JsonIdentityReference(alwaysAsId = true) + public MetadataStatus getRelatedMetadataStatus() { + return relatedMetadataStatus; + } + public void setRelatedMetadataStatus(MetadataStatus relatedMetadataStatus) { + this.relatedMetadataStatus = relatedMetadataStatus; + } + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public LinkedHashMap getTitles() { + return titles; + } + + public void setTitles(LinkedHashMap titles) { + this.titles = titles; + } +} diff --git a/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusResponse.java b/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusResponse.java index 57bf55084b1..81ad06801b9 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusResponse.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/MetadataStatusResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -23,11 +23,9 @@ package org.fao.geonet.api.records.model; -import org.fao.geonet.domain.ISODate; import org.fao.geonet.domain.MetadataStatus; -public class MetadataStatusResponse extends MetadataStatus { - +public class MetadataStatusResponse extends MetadataStatusDto { private String authorName; private String authorEmail; @@ -48,20 +46,7 @@ public class MetadataStatusResponse extends MetadataStatus { private boolean loadFull = false; public MetadataStatusResponse(MetadataStatus metadataStatus) { - setUuid(metadataStatus.getUuid()); - setId(metadataStatus.getId()); - setUserId(metadataStatus.getUserId()); - setChangeDate(metadataStatus.getChangeDate()); - setChangeMessage(metadataStatus.getChangeMessage()); - setOwner(metadataStatus.getOwner()); - setDueDate(metadataStatus.getDueDate()); - setCloseDate(metadataStatus.getCloseDate()); - setStatusValue(metadataStatus.getStatusValue()); - setTitles(metadataStatus.getTitles()); - setRelatedMetadataStatus(metadataStatus.getRelatedMetadataStatus()); - setMetadataId(metadataStatus.getMetadataId()); - setCurrentState(metadataStatus.getCurrentState()); - setPreviousState(metadataStatus.getPreviousState()); + super(metadataStatus); } public MetadataStatusResponse(MetadataStatus metadataStatus, boolean loadFull) { @@ -73,54 +58,48 @@ public String getAuthorEmail() { return authorEmail; } - public MetadataStatusResponse setAuthorEmail(String authorEmail) { + public void setAuthorEmail(String authorEmail) { this.authorEmail = authorEmail; - return this; } public String getOwnerEmail() { return ownerEmail; } - public MetadataStatusResponse setOwnerEmail(String ownerEmail) { + public void setOwnerEmail(String ownerEmail) { this.ownerEmail = ownerEmail; - return this; } public String getOwnerName() { return ownerName; } - public MetadataStatusResponse setOwnerName(String ownerName) { + public void setOwnerName(String ownerName) { this.ownerName = ownerName; - return this; } public String getOwnerProfile() { return ownerProfile; } - public MetadataStatusResponse setOwnerProfile(String ownerProfile) { + public void setOwnerProfile(String ownerProfile) { this.ownerProfile = ownerProfile; - return this; } public String getAuthorName() { return authorName; } - public MetadataStatusResponse setAuthorName(String authorName) { + public void setAuthorName(String authorName) { this.authorName = authorName; - return this; } public String getAuthorProfile() { return authorProfile; } - public MetadataStatusResponse setAuthorProfile(String authorProfile) { + public void setAuthorProfile(String authorProfile) { this.authorProfile = authorProfile; - return this; } public String getCurrentStatus() { @@ -128,19 +107,11 @@ public String getCurrentStatus() { } public boolean isCurrentStateEmpty() { - if (super.getCurrentState() == null || super.getCurrentState().length() == 0) { - return true; - } else { - return false; - } + return super.getCurrentState() == null || super.getCurrentState().isEmpty(); } public boolean isPreviousStateEmpty() { - if (super.getPreviousState() == null || super.getPreviousState().length() == 0) { - return true; - } else { - return false; - } + return super.getPreviousState() == null || super.getPreviousState().isEmpty(); } @Override @@ -165,22 +136,19 @@ public String getPreviousState() { } } - public MetadataStatusResponse setCurrentStatus(String currentStatus) { + public void setCurrentStatus(String currentStatus) { this.currentStatus = currentStatus; - return this; } - public MetadataStatusResponse setPreviousStatus(String previousStatus) { + public void setPreviousStatus(String previousStatus) { this.previousStatus = previousStatus; - return this; } public String getTitle() { return title; } - public MetadataStatusResponse setTitle(String title) { + public void setTitle(String title) { this.title = title; - return this; } public int getStatusId() { diff --git a/services/src/main/java/org/fao/geonet/api/records/model/MetadataWorkflowStatusResponse.java b/services/src/main/java/org/fao/geonet/api/records/model/MetadataWorkflowStatusResponse.java index 7dd773036e8..7ff62475519 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/MetadataWorkflowStatusResponse.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/MetadataWorkflowStatusResponse.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -32,7 +32,7 @@ @XmlRootElement public class MetadataWorkflowStatusResponse { - private MetadataStatus currentStatus; + private MetadataStatusDto currentStatus; private List reviewers; private boolean hasEditPermission; private List status; @@ -44,18 +44,22 @@ public MetadataWorkflowStatusResponse(MetadataStatus currentStatus, List reviewers, boolean hasEditPermission, List status) { - this.currentStatus = currentStatus; + setCurrentStatus(currentStatus); this.reviewers = reviewers; this.hasEditPermission = hasEditPermission; this.status = status; } - public MetadataStatus getCurrentStatus() { + public MetadataStatusDto getCurrentStatus() { return currentStatus; } public void setCurrentStatus(MetadataStatus currentStatus) { - this.currentStatus = currentStatus; + if (currentStatus != null) { + this.currentStatus = new MetadataStatusDto(currentStatus); + } else { + this.currentStatus = null; + } } public List getReviewers() { diff --git a/services/src/main/java/org/fao/geonet/api/records/model/related/FCRelatedMetadataItem.java b/services/src/main/java/org/fao/geonet/api/records/model/related/FCRelatedMetadataItem.java index 8f33aa51cbc..cd6d68072f7 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/related/FCRelatedMetadataItem.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/related/FCRelatedMetadataItem.java @@ -27,6 +27,8 @@ import java.util.ArrayList; import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + /** *

Java class for anonymous complex type. * @@ -378,6 +380,8 @@ public List getElement() { "type", "values" }) + // Use different schema name for source to resolve conflict with org.fao.geonet.kernel.schema.labels.Element + @Schema(name = "FCRelatedElement") public static class Element { protected String name; diff --git a/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedItemType.java b/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedItemType.java index a2fc5e8df43..396f26d6513 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedItemType.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedItemType.java @@ -1,6 +1,9 @@ package org.fao.geonet.api.records.model.related; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(enumAsRef = true) public enum RelatedItemType { /** diff --git a/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedResponse.java b/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedResponse.java index d1d63750efa..f43c657554e 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedResponse.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/related/RelatedResponse.java @@ -1,3 +1,26 @@ +/* + * Copyright (C) 2001-2024 Food and Agriculture Organization of the + * United Nations (FAO-UN), United Nations World Food Programme (WFP) + * and United Nations Environment Programme (UNEP) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ + package org.fao.geonet.api.records.model.related; @@ -8,6 +31,7 @@ import java.util.ArrayList; import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; /** *

Java class for relatedResponse complex type. @@ -174,150 +198,150 @@ @XmlRootElement(name = "related") public class RelatedResponse { - protected RelatedResponse.Children children; - protected RelatedResponse.Parent parent; - protected RelatedResponse.Siblings siblings; - protected RelatedResponse.Associated associated; - protected RelatedResponse.Service services; - protected RelatedResponse.Dataset datasets; - protected RelatedResponse.Fcat fcats; - protected RelatedResponse.Hasfeaturecats hasfeaturecats; - protected RelatedResponse.Source sources; - protected RelatedResponse.Hassource hassources; - protected RelatedResponse.Related related; - protected RelatedResponse.Online onlines; - protected RelatedResponse.Thumbnail thumbnails; + protected Children children; + protected Parent parent; + protected Siblings siblings; + protected Associated associated; + protected Service services; + protected Dataset datasets; + protected Fcat fcats; + protected Hasfeaturecats hasfeaturecats; + protected Source sources; + protected Hassource hassources; + protected Related related; + protected Online onlines; + protected Thumbnail thumbnails; /** * Gets the value of the children property. * - * @return possible object is {@link RelatedResponse.Children } + * @return possible object is {@link Children } */ - public RelatedResponse.Children getChildren() { + public Children getChildren() { return children; } /** * Sets the value of the children property. * - * @param value allowed object is {@link RelatedResponse.Children } + * @param value allowed object is {@link Children } */ - public void setChildren(RelatedResponse.Children value) { + public void setChildren(Children value) { this.children = value; } /** * Gets the value of the parent property. * - * @return possible object is {@link RelatedResponse.Parent } + * @return possible object is {@link Parent } */ - public RelatedResponse.Parent getParent() { + public Parent getParent() { return parent; } /** * Sets the value of the parent property. * - * @param value allowed object is {@link RelatedResponse.Parent } + * @param value allowed object is {@link Parent } */ - public void setParent(RelatedResponse.Parent value) { + public void setParent(Parent value) { this.parent = value; } /** * Gets the value of the siblings property. * - * @return possible object is {@link RelatedResponse.Siblings } + * @return possible object is {@link Siblings } */ - public RelatedResponse.Siblings getSiblings() { + public Siblings getSiblings() { return siblings; } /** * Sets the value of the siblings property. * - * @param value allowed object is {@link RelatedResponse.Siblings } + * @param value allowed object is {@link Siblings } */ - public void setSiblings(RelatedResponse.Siblings value) { + public void setSiblings(Siblings value) { this.siblings = value; } /** * Gets the value of the associated property. * - * @return possible object is {@link RelatedResponse.Associated } + * @return possible object is {@link Associated } */ - public RelatedResponse.Associated getAssociated() { + public Associated getAssociated() { return associated; } /** * Sets the value of the associated property. * - * @param value allowed object is {@link RelatedResponse.Associated } + * @param value allowed object is {@link Associated } */ - public void setAssociated(RelatedResponse.Associated value) { + public void setAssociated(Associated value) { this.associated = value; } /** * Gets the value of the service property. * - * @return possible object is {@link RelatedResponse.Service } + * @return possible object is {@link Service } */ - public RelatedResponse.Service getServices() { + public Service getServices() { return services; } /** * Sets the value of the service property. * - * @param value allowed object is {@link RelatedResponse.Service } + * @param value allowed object is {@link Service } */ - public void setServices(RelatedResponse.Service value) { + public void setServices(Service value) { this.services = value; } /** * Gets the value of the dataset property. * - * @return possible object is {@link RelatedResponse.Dataset } + * @return possible object is {@link Dataset } */ - public RelatedResponse.Dataset getDatasets() { + public Dataset getDatasets() { return datasets; } /** * Sets the value of the dataset property. * - * @param value allowed object is {@link RelatedResponse.Dataset } + * @param value allowed object is {@link Dataset } */ - public void setDatasets(RelatedResponse.Dataset value) { + public void setDatasets(Dataset value) { this.datasets = value; } /** * Gets the value of the fcat property. * - * @return possible object is {@link RelatedResponse.Fcat } + * @return possible object is {@link Fcat } */ - public RelatedResponse.Fcat getFcats() { + public Fcat getFcats() { return fcats; } /** * Sets the value of the fcat property. * - * @param value allowed object is {@link RelatedResponse.Fcat } + * @param value allowed object is {@link Fcat } */ - public void setFcats(RelatedResponse.Fcat value) { + public void setFcats(Fcat value) { this.fcats = value; } /** * Gets the value of the hasfeaturecats property. * - * @return possible object is {@link RelatedResponse.Hasfeaturecats } + * @return possible object is {@link Hasfeaturecats } */ public Hasfeaturecats getHasfeaturecats() { return hasfeaturecats; @@ -326,99 +350,99 @@ public Hasfeaturecats getHasfeaturecats() { /** * Sets the value of the hasfeaturecats property. * - * @param value allowed object is {@link RelatedResponse.Hasfeaturecats } + * @param value allowed object is {@link Hasfeaturecats } */ - public void setHasfeaturecats(RelatedResponse.Hasfeaturecats value) { + public void setHasfeaturecats(Hasfeaturecats value) { this.hasfeaturecats = value; } /** * Gets the value of the source property. * - * @return possible object is {@link RelatedResponse.Source } + * @return possible object is {@link Source } */ - public RelatedResponse.Source getSources() { + public Source getSources() { return sources; } /** * Sets the value of the source property. * - * @param value allowed object is {@link RelatedResponse.Source } + * @param value allowed object is {@link Source } */ - public void setSources(RelatedResponse.Source value) { + public void setSources(Source value) { this.sources = value; } /** * Gets the value of the hassource property. * - * @return possible object is {@link RelatedResponse.Hassource } + * @return possible object is {@link Hassource } */ - public RelatedResponse.Hassource getHassources() { + public Hassource getHassources() { return hassources; } /** * Sets the value of the hassource property. * - * @param value allowed object is {@link RelatedResponse.Hassource } + * @param value allowed object is {@link Hassource } */ - public void setHassources(RelatedResponse.Hassource value) { + public void setHassources(Hassource value) { this.hassources = value; } /** * Gets the value of the related property. * - * @return possible object is {@link RelatedResponse.Related } + * @return possible object is {@link Related } */ - public RelatedResponse.Related getRelated() { + public Related getRelated() { return related; } /** * Sets the value of the related property. * - * @param value allowed object is {@link RelatedResponse.Related } + * @param value allowed object is {@link Related } */ - public void setRelated(RelatedResponse.Related value) { + public void setRelated(Related value) { this.related = value; } /** * Gets the value of the online property. * - * @return possible object is {@link RelatedResponse.Online } + * @return possible object is {@link Online } */ - public RelatedResponse.Online getOnlines() { + public Online getOnlines() { return onlines; } /** * Sets the value of the online property. * - * @param value allowed object is {@link RelatedResponse.Online } + * @param value allowed object is {@link Online } */ - public void setOnlines(RelatedResponse.Online value) { + public void setOnlines(Online value) { this.onlines = value; } /** * Gets the value of the thumbnail property. * - * @return possible object is {@link RelatedResponse.Thumbnail } + * @return possible object is {@link Thumbnail } */ - public RelatedResponse.Thumbnail getThumbnails() { + public Thumbnail getThumbnails() { return thumbnails; } /** * Sets the value of the thumbnail property. * - * @param value allowed object is {@link RelatedResponse.Thumbnail } + * @param value allowed object is {@link Thumbnail } */ - public void setThumbnails(RelatedResponse.Thumbnail value) { + public void setThumbnails(Thumbnail value) { this.thumbnails = value; } @@ -452,7 +476,7 @@ public static class Associated implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -467,7 +491,7 @@ public static class Associated implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -504,7 +528,7 @@ public static class Children implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -519,7 +543,7 @@ public static class Children implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -556,7 +580,7 @@ public static class Dataset implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -571,7 +595,7 @@ public static class Dataset implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -608,7 +632,7 @@ public static class Fcat implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -623,7 +647,7 @@ public static class Fcat implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -660,7 +684,7 @@ public static class Hasfeaturecats implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -675,7 +699,7 @@ public static class Hasfeaturecats implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -710,7 +734,7 @@ public static class Hassource implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -725,7 +749,7 @@ public static class Hassource implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -762,7 +786,7 @@ public static class Online implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -776,7 +800,7 @@ public static class Online implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -813,7 +837,7 @@ public static class Parent implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -828,7 +852,7 @@ public static class Parent implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -865,7 +889,7 @@ public static class Related implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -880,7 +904,7 @@ public static class Related implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -917,7 +941,7 @@ public static class Service implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -932,7 +956,7 @@ public static class Service implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -969,7 +993,7 @@ public static class Siblings implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -984,7 +1008,7 @@ public static class Siblings implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -1014,6 +1038,8 @@ public List getItem() { @XmlType(name = "", propOrder = { "item" }) + // Use different schema name for source to resolve conflict with org.fao.geonet.domain.Source + @Schema(name = "RelatedSource") public static class Source implements IListOnlyClassToArray { protected List item; @@ -1021,7 +1047,7 @@ public static class Source implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -1036,7 +1062,7 @@ public static class Source implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } @@ -1073,7 +1099,7 @@ public static class Thumbnail implements IListOnlyClassToArray { /** * Gets the value of the item property. * - *

This accessor method returns a reference to the live list, not a snapshot. Therefore + *

This accessor method returns a reference to the live list, not a snapshot. Therefor * any modification you make to the returned list will be present inside the JAXB object. * This is why there is not a set method for the item property. * @@ -1088,7 +1114,7 @@ public static class Thumbnail implements IListOnlyClassToArray { */ public List getItem() { if (item == null) { - item = new ArrayList(); + item = new ArrayList<>(); } return this.item; } diff --git a/services/src/main/java/org/fao/geonet/api/records/model/validation/Report.java b/services/src/main/java/org/fao/geonet/api/records/model/validation/Report.java index d2c6fcda3e8..046b07e1607 100644 --- a/services/src/main/java/org/fao/geonet/api/records/model/validation/Report.java +++ b/services/src/main/java/org/fao/geonet/api/records/model/validation/Report.java @@ -7,6 +7,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import java.math.BigInteger; +import io.swagger.v3.oas.annotations.media.Schema; /** *

Java class for anonymous complex type. @@ -46,6 +47,8 @@ "schematronVerificationError" }) @XmlRootElement(name = "report") +// Use different schema name for report to resolve conflict with org.fao.geonet.api.processing.report.Report +@Schema(name = "ValidationReport") public class Report { @XmlElement(required = true) diff --git a/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java b/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java index c4983b33782..a011b58b68d 100644 --- a/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java +++ b/services/src/main/java/org/fao/geonet/api/registries/DirectoryUtils.java @@ -1,11 +1,12 @@ package org.fao.geonet.api.registries; +import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Hit; import com.google.common.collect.Table; import jeeves.server.ServiceConfig; import jeeves.server.context.ServiceContext; import jeeves.xlink.XLink; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.action.search.SearchResponse; import org.fao.geonet.domain.*; import org.fao.geonet.kernel.DataManager; import org.fao.geonet.kernel.UpdateDatestamp; @@ -397,8 +398,6 @@ private static String getPropertiesAsParameters(Element source, * @return The record identifier */ private static String search(ServiceContext context, Map searchParameters) { - ServiceConfig _config = new ServiceConfig(); - EsSearchManager searchMan = context.getBean(EsSearchManager.class); StringBuilder query = new StringBuilder("+isTemplate:s"); @@ -408,8 +407,8 @@ private static String search(ServiceContext context, Map searchP try { SearchResponse results = searchMan.query(query.toString(), null, 0, 1); - if (results.getHits().getTotalHits().value > 0) { - return results.getHits().getHits()[0].getId(); + if (results.hits().hits().size() > 0) { + return ((Hit) results.hits().hits().get(0)).id(); } } catch (Exception e) { e.printStackTrace(); diff --git a/services/src/main/java/org/fao/geonet/api/registries/vocabularies/KeywordsApi.java b/services/src/main/java/org/fao/geonet/api/registries/vocabularies/KeywordsApi.java index 93ad30ec0c2..4486174f74e 100644 --- a/services/src/main/java/org/fao/geonet/api/registries/vocabularies/KeywordsApi.java +++ b/services/src/main/java/org/fao/geonet/api/registries/vocabularies/KeywordsApi.java @@ -24,6 +24,7 @@ package org.fao.geonet.api.registries.vocabularies; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; @@ -48,6 +49,7 @@ import org.fao.geonet.api.tools.i18n.LanguageUtils; import org.fao.geonet.constants.Geonet; import org.fao.geonet.domain.ISODate; +import org.fao.geonet.exceptions.BadParameterEx; import org.fao.geonet.kernel.*; import org.fao.geonet.kernel.search.KeywordsSearcher; import org.fao.geonet.kernel.search.keyword.*; @@ -61,6 +63,7 @@ import org.jdom.output.XMLOutputter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; @@ -138,6 +141,8 @@ public class KeywordsApi { @Autowired ThesaurusManager thesaurusManager; + List allowedExtensions = Arrays.asList("rdf", "owl", "xml", "sdmx"); + /** * Search keywords. * @@ -327,10 +332,7 @@ public Object searchKeywords( ) @RequestMapping( path = "/keyword", - method = { - RequestMethod.GET, - RequestMethod.POST - }, + method = RequestMethod.GET, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE @@ -374,17 +376,113 @@ public Object getKeywordById( @Parameter(hidden = true) @RequestParam Map allRequestParams, - @RequestHeader( - value = "Accept", - defaultValue = MediaType.APPLICATION_XML_VALUE - ) - String accept, @Parameter(hidden = true) HttpServletRequest request ) throws Exception { + return getKeyword(uri,sThesaurusName,langs, keywordOnly, transformation,langMapJson,allRequestParams, request); + } + + /** + * Gets the keyword by id. + * + * @param uri the uri + * @param sThesaurusName the s thesaurus name + * @param langs the langs + * @param keywordOnly the keyword only + * @param transformation the transformation + * @param allRequestParams the all request params + * @param request the request + * @return the keyword by id + * @throws Exception the exception + */ + @io.swagger.v3.oas.annotations.Operation( + summary = "Get keyword by ids", + description = "Retrieve XML representation of keyword(s) from same thesaurus" + + "using different transformations. " + + "'to-iso19139-keyword' is the default and return an ISO19139 snippet." + + "'to-iso19139-keyword-as-xlink' return an XLinked element. Custom transformation " + + "can be create on a per schema basis." + + "This can be used instead of the GET method for cases where you need to submit large parameters list" + ) + @RequestMapping( + path = "/keyword", + method = RequestMethod.POST, + produces = { + MediaType.APPLICATION_XML_VALUE, + MediaType.APPLICATION_JSON_VALUE + }) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "XML snippet with requested keywords."), + }) + @ResponseBody + @ResponseStatus(HttpStatus.OK) + public Object getKeywordByIds( + @Parameter( + description = "Keyword identifier or list of keyword identifiers comma separated.", + required = true) + @RequestParam(name = "id") + String uri, + @Parameter( + description = "Thesaurus to look info for the keyword(s).", + required = true) + @RequestParam(name = "thesaurus") + String sThesaurusName, + @Parameter( + description = "Languages.", + required = false) + @RequestParam(name = "lang", required = false) + String[] langs, + @Parameter( + description = "Only print the keyword, no thesaurus information.", + required = false) + @RequestParam(required = false, defaultValue = "false") + boolean keywordOnly, + @Parameter( + description = "XSL template to use (ISO19139 keyword by default, see convert.xsl).", + required = false) + @RequestParam(required = false) + String transformation, + @Parameter( + description = "langMap, that converts the values in the 'lang' parameter to how they will be actually represented in the record. {'fre':'fra'} or {'fre':'fr'}. Missing/empty means to convert to iso 2 letter.", + required = false) + @RequestParam (name = "langMap", required = false) + String langMapJson, + @Parameter(hidden = true) + @RequestParam + Map allRequestParams, + @Parameter(hidden = true) + HttpServletRequest request + ) throws Exception { + return getKeyword(uri,sThesaurusName,langs, keywordOnly, transformation,langMapJson,allRequestParams, request); + } + + /** + * Gets the keyword by id. + * + * @param uri the uri + * @param sThesaurusName the s thesaurus name + * @param langs the langs + * @param keywordOnly the keyword only + * @param transformation the transformation + * @param allRequestParams the all request params + * @param request the request + * @return the keyword by id + * @throws Exception the exception + */ + private Object getKeyword( + String uri, + String sThesaurusName, + String[] langs, + boolean keywordOnly, + String transformation, + String langMapJson, + Map allRequestParams, + HttpServletRequest request + ) throws Exception { final String SEPARATOR = ","; ServiceContext context = ApiUtils.createServiceContext(request); - boolean isJson = MediaType.APPLICATION_JSON_VALUE.equals(accept); + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT)) ? MediaType.APPLICATION_XML_VALUE : request.getHeader(HttpHeaders.ACCEPT); + boolean isJson = MediaType.APPLICATION_JSON_VALUE.equals(acceptHeader); // Search thesaurus by name (as facet key only contains the name of the thesaurus) Thesaurus thesaurus = thesaurusManager.getThesaurusByName(sThesaurusName); @@ -458,7 +556,7 @@ public Object getKeywordById( } } - Element langConversion = null; + Element langConversion = null; if ( (langMapJson != null) && (!langMapJson.isEmpty()) ){ JSONObject obj = JSONObject.fromObject(langMapJson); langConversion = new Element("languageConversions"); @@ -466,8 +564,8 @@ public Object getKeywordById( String key = ((Map.Entry) entry).getKey().toString(); String value = ((Map.Entry) entry).getValue().toString(); Element conv = new Element("conversion"); - conv.setAttribute("from",key.toString()); - conv.setAttribute("to",value.toString().replace("#","")); + conv.setAttribute("from", key); + conv.setAttribute("to", value.replace("#","")); langConversion.addContent(conv); } @@ -506,13 +604,10 @@ public Object getKeywordById( root.addContent(gui); root.addContent(nodeUrl); root.addContent(nodeId); - final Element transform = Xml.transform(root, convertXsl); - - return transform; + return Xml.transform(root, convertXsl); } } - /** * Gets the thesaurus. * @@ -529,10 +624,12 @@ public Object getKeywordById( value = "/{thesaurus:.+}", method = RequestMethod.GET, produces = { - MediaType.TEXT_XML_VALUE + MediaType.TEXT_XML_VALUE, + MediaType.APPLICATION_XML_VALUE }) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Thesaurus in SKOS format."), + @ApiResponse(responseCode = "200", description = "Thesaurus in SKOS format.", + content = @Content(schema = @io.swagger.v3.oas.annotations.media.Schema(type = "string", format = "binary"))), @ApiResponse(responseCode = "404", description = ApiParams.API_RESPONSE_RESOURCE_NOT_FOUND) }) @ResponseBody @@ -623,7 +720,7 @@ public void deleteThesaurus( */ @io.swagger.v3.oas.annotations.Operation( summary = "Uploads a new thesaurus from a file", - description = "Uploads a new thesaurus." + description = "Supported thesaurus are RDF/XML files using SKOS specification, OWL file describing NamedIndividual elements or SDMX file describing Codelist element. For RDF, extension must be .rdf or .xml, for OWL, .owl and for SDMX, .sdmx." ) @RequestMapping( method = RequestMethod.POST, @@ -704,13 +801,14 @@ public String uploadThesaurus( String extension = FilenameUtils.getExtension(fname); - if (extension.equalsIgnoreCase("rdf") || - extension.equalsIgnoreCase("xml")) { + if (allowedExtensions.contains(extension.toLowerCase())) { Log.debug(Geonet.THESAURUS, "Uploading thesaurus: " + fname); // Rename .xml to .rdf for all thesaurus fname = fname.replace(extension, "rdf"); - uploadThesaurus(rdfFile, stylesheet, context, fname, type.toString(), dir); + uploadThesaurus(rdfFile, + getStylesheetForExtension(stylesheet, extension), + context, fname, type.toString(), dir); } else { Log.debug(Geonet.THESAURUS, "Incorrect extension for thesaurus named: " + fname); throw new Exception("Incorrect extension for thesaurus named: " @@ -723,13 +821,19 @@ public String uploadThesaurus( return String.format("Thesaurus '%s' loaded in %d sec.", fname, duration); } finally { - if (tempDir != null) { - FileUtils.deleteQuietly(tempDir); - } + FileUtils.deleteQuietly(tempDir); } } - + private static String getStylesheetForExtension(String stylesheet, String extension) { + if (extension.equals("owl")) { + return "owl-to-skos"; + } else if (extension.equals("sdmx")) { + return "sdmx-to-skos"; + } else { + return stylesheet; + } + } /** @@ -831,7 +935,7 @@ public void importCsvAsThesaurus( } long fsize; - if (csvFile != null && Files.exists(csvFile)) { + if (Files.exists(csvFile)) { fsize = Files.size(csvFile); } else { throw new MissingServletRequestParameterException("CSV file doesn't exist", "file"); @@ -874,9 +978,7 @@ public void importCsvAsThesaurus( response.getOutputStream().write(Xml.getString(element).getBytes()); } } finally { - if (tempDir != null) { - FileUtils.deleteQuietly(tempDir); - } + FileUtils.deleteQuietly(tempDir); } } @@ -945,7 +1047,7 @@ public Element convertCsvToSkos(Path csvFile, extractRelated(key, thesaurusNamespaceUrl, csvParser, csvRecord, conceptLinkSeparator, conceptBroaderIdColumn, broaderLinks); - if (broaderLinks.get(key) == null || broaderLinks.get(key).size() == 0) { + if (broaderLinks.get(key) == null || broaderLinks.get(key).isEmpty()) { topConcepts.add(key); } extractRelated(key, thesaurusNamespaceUrl, csvParser, csvRecord, @@ -966,7 +1068,7 @@ public Element convertCsvToSkos(Path csvFile, } Element scheme = buildConceptScheme(csvFile, thesaurusTitle, thesaurusNamespaceUrl); - if(broaderLinks.size() > 0 && topConcepts.size() > 0) { + if(broaderLinks.size() > 0 && !topConcepts.isEmpty()) { topConcepts.forEach(t -> { Element topConcept = new Element("hasTopConcept", SKOS_NAMESPACE); topConcept.setAttribute("resource", t, RDF_NAMESPACE); @@ -1177,13 +1279,14 @@ public String uploadThesaurusFromUrl( String extension = FilenameUtils.getExtension(fname); - if (extension.equalsIgnoreCase("rdf") || - extension.equalsIgnoreCase("xml")) { + if (allowedExtensions.contains(extension.toLowerCase())) { Log.debug(Geonet.THESAURUS, "Uploading thesaurus: " + fname); // Rename .xml to .rdf for all thesaurus fname = fname.replace(extension, "rdf"); - uploadThesaurus(rdfFile, stylesheet, context, fname, type.toString(), dir); + uploadThesaurus(rdfFile, + getStylesheetForExtension(stylesheet, extension), + context, fname, type.toString(), dir); } else { Log.debug(Geonet.THESAURUS, "Incorrect extension for thesaurus named: " + fname); throw new MissingServletRequestParameterException("Incorrect extension for thesaurus", fname); @@ -1319,7 +1422,7 @@ private Path extractSKOSFromRegistry(String registryUrl, REGISTRY_TYPE registryT * @throws IOException Signals that an I/O exception has occurred. * @throws MalformedURLException the malformed URL exception */ - private Path getXMLContentFromUrl(String url, ServiceContext context) throws URISyntaxException, IOException, MalformedURLException { + private Path getXMLContentFromUrl(String url, ServiceContext context) throws URISyntaxException, IOException { Path rdfFile; URI uri = new URI(url); rdfFile = Files.createTempFile("thesaurus", ".rdf"); @@ -1347,17 +1450,21 @@ private void uploadThesaurus(Path rdfFile, String style, ServiceContext context, String fname, String type, String dir) throws Exception { - Path stylePath = context.getAppPath().resolve(Geonet.Path.STYLESHEETS); - Element tsXml; Element xml = Xml.loadFile(rdfFile); xml.detach(); if (!"_none_".equals(style)) { FilePathChecker.verify(style); - - tsXml = Xml.transform(xml, stylePath.resolve(style)); - tsXml.detach(); + Path xsltPath = dataDirectory.getWebappDir().resolve(String.format( + "xslt/services/thesaurus/%s.xsl", style)); + if (Files.exists(xsltPath)) { + tsXml = Xml.transform(xml, xsltPath); + tsXml.detach(); + } else { + throw new BadParameterEx(String.format( + "XSL transformation '%s' not found. Only conversion provided in xslt/services/thesaurus can be used.", style)); + } } else { tsXml = xml; } diff --git a/services/src/main/java/org/fao/geonet/api/related/Related.java b/services/src/main/java/org/fao/geonet/api/related/Related.java index ae800982a45..460ab60b6aa 100644 --- a/services/src/main/java/org/fao/geonet/api/related/Related.java +++ b/services/src/main/java/org/fao/geonet/api/related/Related.java @@ -81,7 +81,7 @@ public synchronized void setApplicationContext(ApplicationContext context) { summary = "Get record related resources for all requested metadatas", description = "Retrieve related services, datasets, onlines, thumbnails, sources, ... " + "to all requested records.
" + - "More info") + "More info") @RequestMapping(value = "", method = RequestMethod.GET, produces = { diff --git a/services/src/main/java/org/fao/geonet/api/site/SiteApi.java b/services/src/main/java/org/fao/geonet/api/site/SiteApi.java index 00f478966be..d39d42f5134 100644 --- a/services/src/main/java/org/fao/geonet/api/site/SiteApi.java +++ b/services/src/main/java/org/fao/geonet/api/site/SiteApi.java @@ -23,6 +23,8 @@ package org.fao.geonet.api.site; +import co.elastic.clients.elasticsearch.core.CountRequest; +import co.elastic.clients.elasticsearch.core.CountResponse; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; @@ -32,16 +34,15 @@ import jeeves.server.JeevesProxyInfo; import jeeves.server.UserSession; import jeeves.server.context.ServiceContext; +import jeeves.xlink.Processor; import org.apache.commons.lang3.StringUtils; -import org.elasticsearch.client.RequestOptions; -import org.elasticsearch.client.core.CountRequest; -import org.elasticsearch.client.core.CountResponse; import org.fao.geonet.ApplicationContextHolder; import org.fao.geonet.GeonetContext; import org.fao.geonet.NodeInfo; import org.fao.geonet.SystemInfo; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; +import org.fao.geonet.api.OpenApiConfig; import org.fao.geonet.api.exception.NotAllowedException; import org.fao.geonet.api.site.model.SettingSet; import org.fao.geonet.api.site.model.SettingsListResponse; @@ -88,10 +89,10 @@ import org.springframework.web.bind.annotation.ResponseStatus; import javax.imageio.ImageIO; -import javax.servlet.ServletRegistration; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.awt.image.BufferedImage; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -420,6 +421,7 @@ public void saveSettings( ApplicationContext applicationContext = ApplicationContextHolder.get(); String currentUuid = settingManager.getSiteId(); String oldSiteName = settingManager.getSiteName(); + String oldBaseUrl = settingManager.getBaseURL(); if (!settingManager.setValues(allRequestParams)) { throw new OperationAbortedEx("Cannot set all values"); @@ -439,6 +441,11 @@ public void saveSettings( sourceRepository.save(siteSource); } } + String newBaseUrl = settingManager.getBaseURL(); + // Update SpringDoc host information if the base url is changed. + if (!oldBaseUrl.equals(newBaseUrl)) { + OpenApiConfig.setHostRelatedInfo(); + } // Update the system default timezone. If the setting is blank use the timezone user.timezone property from command line or // TZ environment variable @@ -571,7 +578,7 @@ public boolean isIndexing( method = RequestMethod.PUT) @PreAuthorize("hasAuthority('Editor')") @ResponseBody - public HttpEntity index( + public HttpEntity indexSite( @Parameter(description = "Drop and recreate index", required = false) @RequestParam(required = false, defaultValue = "true") @@ -580,10 +587,6 @@ public HttpEntity index( required = false) @RequestParam(required = false, defaultValue = "false") boolean asynchronous, - @Parameter(description = "Records having only XLinks", - required = false) - @RequestParam(required = false, defaultValue = "false") - boolean havingXlinkOnly, @Parameter(description = "Index. By default only remove record index.", required = false) @RequestParam(required = false, defaultValue = "records") @@ -611,11 +614,14 @@ public HttpEntity index( searchMan.init(true, Optional.of(Arrays.asList(indices))); } + // clean XLink Cache so that cache and index remain in sync + Processor.clearCache(); + if (StringUtils.isEmpty(bucket)) { BaseMetadataManager metadataManager = ApplicationContextHolder.get().getBean(BaseMetadataManager.class); metadataManager.synchronizeDbWithIndex(context, false, asynchronous); } else { - searchMan.rebuildIndex(context, havingXlinkOnly, false, bucket); + searchMan.rebuildIndex(context, false, bucket); } return new HttpEntity<>(HttpStatus.CREATED); @@ -676,9 +682,9 @@ public Map indexAndDbSynchronizationStatus( EsSearchManager searchMan = ApplicationContextHolder.get().getBean(EsSearchManager.class); CountResponse countResponse = esRestClient.getClient().count( - new CountRequest(searchMan.getDefaultIndex()), - RequestOptions.DEFAULT); - infoIndexDbSynch.put("index.count", countResponse.getCount()); + CountRequest.of(b -> b.index(searchMan.getDefaultIndex())) + ); + infoIndexDbSynch.put("index.count", countResponse.count()); return infoIndexDbSynch; } @@ -869,7 +875,7 @@ public List getXslTransformations( )) { for (Path sheet : sheets) { String id = sheet.toString(); - if (id != null && id.contains("convert/from") && id.endsWith(".xsl")) { + if (id != null && id.contains("convert" + File.separator + "from") && id.endsWith(".xsl")) { String name = com.google.common.io.Files.getNameWithoutExtension( sheet.getFileName().toString()); list.add(IMPORT_STYLESHEETS_SCHEMA_PREFIX + schema + ":convert/" + name); diff --git a/services/src/main/java/org/fao/geonet/api/site/SiteInformation.java b/services/src/main/java/org/fao/geonet/api/site/SiteInformation.java index 7041658c6dc..466944ecc22 100644 --- a/services/src/main/java/org/fao/geonet/api/site/SiteInformation.java +++ b/services/src/main/java/org/fao/geonet/api/site/SiteInformation.java @@ -23,15 +23,18 @@ package org.fao.geonet.api.site; +import co.elastic.clients.elasticsearch._types.ElasticsearchException; import com.fasterxml.jackson.annotation.JsonProperty; import jeeves.server.ServiceConfig; import jeeves.server.context.ServiceContext; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.lang.StringUtils; -import org.elasticsearch.ElasticsearchException; import org.fao.geonet.GeonetContext; import org.fao.geonet.constants.Geonet; import org.fao.geonet.kernel.search.EsSearchManager; +import org.fao.geonet.kernel.setting.SettingInfo; +import org.fao.geonet.kernel.setting.SettingManager; +import org.fao.geonet.kernel.setting.Settings; import org.fao.geonet.utils.Env; import org.fao.geonet.utils.Log; import org.fao.geonet.utils.TransformerFactoryFactory; @@ -70,7 +73,7 @@ public SiteInformation(final ServiceContext context, final GeonetContext gc) { Log.error(Geonet.GEONETWORK, e.getMessage(), e); } loadEnvInfo(); - loadVersionInfo(); + loadVersionInfo(context); loadSystemInfo(); } } @@ -266,12 +269,17 @@ private void loadDatabaseInfo(ServiceContext context) throws SQLException { } /** - * Compute information about git commit. + * Compute information about the application and git commit. */ - private void loadVersionInfo() { + private void loadVersionInfo(ServiceContext context) { Properties prop = new Properties(); try (InputStream input = getClass().getResourceAsStream("/git.properties")) { + SettingManager settingManager = context.getBean(SettingManager.class); + + versionProperties.put("app.version", settingManager.getValue(Settings.SYSTEM_PLATFORM_VERSION)); + versionProperties.put("app.subVersion", settingManager.getValue(Settings.SYSTEM_PLATFORM_SUBVERSION)); + prop.load(input); Enumeration e = prop.propertyNames(); diff --git a/services/src/main/java/org/fao/geonet/api/sld/SldApi.java b/services/src/main/java/org/fao/geonet/api/sld/SldApi.java index 27b766679cf..fc8cd2dfc47 100644 --- a/services/src/main/java/org/fao/geonet/api/sld/SldApi.java +++ b/services/src/main/java/org/fao/geonet/api/sld/SldApi.java @@ -1,5 +1,8 @@ package org.fao.geonet.api.sld; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_TAG; + import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.transaction.TransactionManager; @@ -47,8 +50,8 @@ @RequestMapping(value = { "/{portal}/api/tools/ogc" }) -@Tag(name = "tools", - description = "Utility operations") +@Tag(name = API_CLASS_TOOLS_TAG, + description = API_CLASS_TOOLS_OPS) public class SldApi { public static final String LOGGER = Geonet.GEONETWORK + ".api.sld"; diff --git a/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java b/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java index 2c268bab668..5b50c543e5c 100644 --- a/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java +++ b/services/src/main/java/org/fao/geonet/api/sources/SourcesApi.java @@ -24,11 +24,15 @@ package org.fao.geonet.api.sources; import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; import jeeves.server.context.ServiceContext; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; import org.fao.geonet.api.exception.ResourceNotFoundException; @@ -41,6 +45,7 @@ import org.fao.geonet.resources.Resources; import org.jdom.Element; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -81,14 +86,21 @@ public class SourcesApi { summary = "Get all sources", description = "Sources are the local catalogue, subportal, external catalogue (when importing MEF files) or harvesters.") @RequestMapping( - produces = MediaType.APPLICATION_JSON_VALUE, + produces = { + MediaType.APPLICATION_JSON_VALUE, + MediaType.TEXT_HTML_VALUE + }, method = RequestMethod.GET) @ResponseStatus(HttpStatus.OK) @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "List of source catalogues.") + @ApiResponse(responseCode = "200", description = "List of source catalogues.", + content = { + @Content(mediaType = MediaType.APPLICATION_JSON_VALUE, array = @ArraySchema(schema = @Schema(implementation = Source.class))), + @Content(mediaType = MediaType.TEXT_HTML_VALUE, schema = @Schema(type = "string")) + }) }) @ResponseBody - public List getSources( + public ResponseEntity getSources( @Parameter( description = "Group owner of the source (only applies to subportal)." ) @@ -97,47 +109,41 @@ public List getSources( required = false) Integer group, @Parameter(hidden = true) - HttpServletResponse response + HttpServletResponse response, + @Parameter(hidden = true) + HttpServletRequest request ) throws Exception { setHeaderVaryOnAccept(response); + List sources; if (group != null) { - return sourceRepository.findByGroupOwner(group); + sources = sourceRepository.findByGroupOwner(group); + } else { + sources = sourceRepository.findAll(SortUtils.createSort(Source_.name)); + } + + if (sources == null) { + return ResponseEntity.notFound().build(); + } + String acceptHeader = StringUtils.isBlank(request.getHeader(HttpHeaders.ACCEPT))?MediaType.APPLICATION_JSON_VALUE:request.getHeader(HttpHeaders.ACCEPT); + if (acceptHeader.contains(MediaType.TEXT_HTML_VALUE)) { + return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(getSourcesAsHtml(sources)); } else { - return sourceRepository.findAll(SortUtils.createSort(Source_.name)); + return ResponseEntity.ok().contentType(MediaType.APPLICATION_JSON).body(sources); } } - @io.swagger.v3.oas.annotations.Operation( - summary = "Get portal list", - description = "List all subportal available.") - @RequestMapping( - produces = MediaType.TEXT_HTML_VALUE, - method = RequestMethod.GET) - @ResponseStatus(HttpStatus.OK) - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "List of portals.") - }) - @ResponseBody - public void getSubPortals( - @Parameter(hidden = true) - HttpServletResponse response - ) throws Exception { - final List sources = sourceRepository.findAll(SortUtils.createSort(Source_.name)); + private String getSourcesAsHtml(List sources) throws Exception { Element sourcesList = new Element("sources"); sources.stream().map(GeonetEntity::asXml).forEach(sourcesList::addContent); - response.setContentType(MediaType.TEXT_HTML_VALUE); - setHeaderVaryOnAccept(response); - response.getWriter().write( - new XsltResponseWriter(null, "portal") + return new XsltResponseWriter(null, "portal") .withJson("catalog/locales/en-core.json") .withJson("catalog/locales/en-search.json") .withXml(sourcesList) .withParam("cssClass", "gn-portal") .withXsl("xslt/ui-search/portal-list.xsl") - .asHtml()); + .asHtml(); } - @io.swagger.v3.oas.annotations.Operation( summary = "Get all sources by type", description = "Sources are the local catalogue, subportal, external catalogue (when importing MEF files) or harvesters.") @@ -272,7 +278,7 @@ public ResponseEntity updateSource( @PreAuthorize("hasAuthority('Administrator')") @ResponseStatus(HttpStatus.NO_CONTENT) @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Source deleted."), + @ApiResponse(responseCode = "204", description = "Source deleted."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_ONLY_ADMIN) }) @ResponseBody diff --git a/services/src/main/java/org/fao/geonet/api/tools/i18n/TranslationApi.java b/services/src/main/java/org/fao/geonet/api/tools/i18n/TranslationApi.java index 0ed2fce83f4..1d667c47d1f 100644 --- a/services/src/main/java/org/fao/geonet/api/tools/i18n/TranslationApi.java +++ b/services/src/main/java/org/fao/geonet/api/tools/i18n/TranslationApi.java @@ -52,6 +52,8 @@ import java.util.stream.Collectors; import static java.util.stream.Collectors.groupingBy; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_TAG; import static org.springframework.http.HttpStatus.CREATED; import static org.springframework.http.HttpStatus.OK; @@ -61,7 +63,8 @@ @RequestMapping(value = { "/{portal}/api/i18n" }) -@Tag(name = "tools") +@Tag(name = API_CLASS_TOOLS_TAG, + description = API_CLASS_TOOLS_OPS) @RestController public class TranslationApi { @@ -313,7 +316,7 @@ public Map getDbTranslations( MediaType.APPLICATION_JSON_VALUE }) @ResponseBody - public Map> getTranslationsPackage() { + public Map> getTranslationsPackages() { return translationPackBuilder.getPackages(); } diff --git a/services/src/main/java/org/fao/geonet/api/tools/mail/MailApi.java b/services/src/main/java/org/fao/geonet/api/tools/mail/MailApi.java index a5f2bf6519f..2ff5f7f9e9b 100644 --- a/services/src/main/java/org/fao/geonet/api/tools/mail/MailApi.java +++ b/services/src/main/java/org/fao/geonet/api/tools/mail/MailApi.java @@ -45,6 +45,9 @@ //=== Rome - Italy. email: geonetwork@osgeo.org //============================================================================== +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_TAG; + import io.swagger.v3.oas.annotations.tags.Tag; import org.fao.geonet.api.API; import org.fao.geonet.api.tools.i18n.LanguageUtils; @@ -73,8 +76,8 @@ @RequestMapping(value = { "/{portal}/api/tools/mail" }) -@Tag(name = "tools", - description = "Utility operations") +@Tag(name = API_CLASS_TOOLS_TAG, + description = API_CLASS_TOOLS_OPS) @Controller("mail") public class MailApi { diff --git a/services/src/main/java/org/fao/geonet/api/tools/migration/MigrationApi.java b/services/src/main/java/org/fao/geonet/api/tools/migration/MigrationApi.java index c5a5a8f8568..63c72bb3ae5 100644 --- a/services/src/main/java/org/fao/geonet/api/tools/migration/MigrationApi.java +++ b/services/src/main/java/org/fao/geonet/api/tools/migration/MigrationApi.java @@ -23,6 +23,9 @@ package org.fao.geonet.api.tools.migration; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_OPS; +import static org.fao.geonet.api.ApiParams.API_CLASS_TOOLS_TAG; + import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import org.fao.geonet.ApplicationContextHolder; @@ -43,7 +46,8 @@ @RequestMapping(value = { "/{portal}/api/tools/migration" }) -@Tag(name = "tools") +@Tag(name = API_CLASS_TOOLS_TAG, + description = API_CLASS_TOOLS_OPS) @RestController public class MigrationApi { diff --git a/services/src/main/java/org/fao/geonet/api/usersearches/UserSearchesApi.java b/services/src/main/java/org/fao/geonet/api/usersearches/UserSearchesApi.java index 8a59adf9cb7..517744f9ce7 100644 --- a/services/src/main/java/org/fao/geonet/api/usersearches/UserSearchesApi.java +++ b/services/src/main/java/org/fao/geonet/api/usersearches/UserSearchesApi.java @@ -290,7 +290,7 @@ public UserSearchDto getUserCustomSearch( @RequestMapping( produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.PUT) - @ResponseStatus(value = HttpStatus.OK) + @ResponseStatus(value = HttpStatus.CREATED) @PreAuthorize("isAuthenticated()") @ApiResponses(value = { @ApiResponse(responseCode = "201", description = "User search created.") diff --git a/services/src/main/java/org/fao/geonet/config/PublicationOption.java b/services/src/main/java/org/fao/geonet/config/PublicationOption.java index 6be7da114b3..5c2e53783c5 100644 --- a/services/src/main/java/org/fao/geonet/config/PublicationOption.java +++ b/services/src/main/java/org/fao/geonet/config/PublicationOption.java @@ -30,6 +30,8 @@ import java.util.List; import java.util.Map; +import io.swagger.v3.oas.annotations.media.Schema; + /** * Defines a publication configuration. * @@ -42,12 +44,15 @@ public class PublicationOption { private String name; // Group to publish + @Schema(enumAsRef = true) private ReservedGroup publicationGroup; // List of operations to activate in the group to publish/unpublish. - List publicationOperations; + @Schema(enumAsRef = true) + private List publicationOperations; // Additional group(s)/operations(s) to publish/unpublish when the publication is selected. + @Schema(enumAsRef = true) private EnumMap> additionalPublications = new EnumMap<>(ReservedGroup.class); PublicationOption(String name, ReservedGroup publicationGroup, List publicationOperations) { diff --git a/services/src/main/java/org/fao/geonet/guiservices/util/Env.java b/services/src/main/java/org/fao/geonet/guiservices/util/Env.java index 05c14986b57..b6971709704 100644 --- a/services/src/main/java/org/fao/geonet/guiservices/util/Env.java +++ b/services/src/main/java/org/fao/geonet/guiservices/util/Env.java @@ -52,9 +52,6 @@ public void init(Path appPath, ServiceConfig params) throws Exception { //-------------------------------------------------------------------------- public Element exec(Element params, ServiceContext context) throws Exception { - // reset the thread local - XmlSerializer.clearThreadLocal(); - GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME); Element response = gc.getBean(SettingManager.class).getAllAsXML(true); diff --git a/services/src/main/java/org/fao/geonet/api/records/InspireValidationRunnable.java b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidationRunnable.java similarity index 75% rename from services/src/main/java/org/fao/geonet/api/records/InspireValidationRunnable.java rename to services/src/main/java/org/fao/geonet/inspire/validator/InspireValidationRunnable.java index 7413f2f115c..ab8d64557b6 100644 --- a/services/src/main/java/org/fao/geonet/api/records/InspireValidationRunnable.java +++ b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidationRunnable.java @@ -1,10 +1,32 @@ -package org.fao.geonet.api.records; +/* + * 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) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ +package org.fao.geonet.inspire.validator; import jeeves.server.context.ServiceContext; import jeeves.transaction.TransactionManager; import jeeves.transaction.TransactionTask; import org.fao.geonet.ApplicationContextHolder; -import org.fao.geonet.api.records.editing.InspireValidatorUtils; +import org.fao.geonet.inspire.validator.InspireValidatorUtils; import org.fao.geonet.domain.MetadataValidation; import org.fao.geonet.domain.MetadataValidationId; import org.fao.geonet.domain.MetadataValidationStatus; diff --git a/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorException.java b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorException.java new file mode 100644 index 00000000000..7d42bd91f44 --- /dev/null +++ b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorException.java @@ -0,0 +1,80 @@ +/* + * 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) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ + +package org.fao.geonet.inspire.validator; + +import org.fao.geonet.exceptions.LocalizedException; + +import java.util.Locale; + +public class InspireValidatorException extends LocalizedException { + public InspireValidatorException() { + super(); + } + + public InspireValidatorException(String message) { + super(message); + } + + public InspireValidatorException(String message, Throwable cause) { + super(message, cause); + } + + public InspireValidatorException(Throwable cause) { + super(cause); + } + + protected String getResourceBundleBeanQualifier() { + return "apiMessages"; + } + + @Override + public InspireValidatorException withMessageKey(String messageKey) { + super.withMessageKey(messageKey); + return this; + } + + @Override + public InspireValidatorException withMessageKey(String messageKey, Object[] messageKeyArgs) { + super.withMessageKey(messageKey, messageKeyArgs); + return this; + } + + @Override + public InspireValidatorException withDescriptionKey(String descriptionKey) { + super.withDescriptionKey(descriptionKey); + return this; + } + + @Override + public InspireValidatorException withDescriptionKey(String descriptionKey, Object[] descriptionKeyArgs) { + super.withDescriptionKey(descriptionKey, descriptionKeyArgs); + return this; + } + + @Override + public InspireValidatorException withLocale(Locale locale) { + super.withLocale(locale); + return this; + } +} diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/InspireValidatorUtils.java b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorUtils.java similarity index 78% rename from services/src/main/java/org/fao/geonet/api/records/editing/InspireValidatorUtils.java rename to services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorUtils.java index 8eab1aa74cb..c996c49f208 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/InspireValidatorUtils.java +++ b/services/src/main/java/org/fao/geonet/inspire/validator/InspireValidatorUtils.java @@ -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) * @@ -21,7 +21,7 @@ * Rome - Italy. email: geonetwork@osgeo.org */ -package org.fao.geonet.api.records.editing; +package org.fao.geonet.inspire.validator; import com.google.common.base.Function; import com.google.common.io.CharStreams; @@ -29,6 +29,7 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; import org.apache.http.client.methods.*; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; @@ -59,6 +60,7 @@ import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -84,62 +86,62 @@ public class InspireValidatorUtils { /** * The Constant USER_AGENT. */ - private final static String USER_AGENT = "Mozilla/5.0"; + private static final String USER_AGENT = "Mozilla/5.0"; /** * The Constant ACCEPT. */ - private final static String ACCEPT = "application/json"; + private static final String ACCEPT = "application/json"; /** * The Constant CheckStatus_URL. */ - private final static String CheckStatus_URL = "/v2/status"; + private static final String CHECKSTATUS_URL = "/v2/status"; /** * The Constant ExecutableTestSuites_URL. */ - private final static String ExecutableTestSuites_URL = "/v2/ExecutableTestSuites"; + private static final String EXECUTABLETESTSUITES_URL = "/v2/ExecutableTestSuites"; /** * The Constant TestObjects_URL. */ - private final static String TestObjects_URL = "/v2/TestObjects"; + private static final String TESTOBJECTS_URL = "/v2/TestObjects"; /** * The Constant TestRuns_URL. */ - private final static String TestRuns_URL = "/v2/TestRuns"; + private static final String TESTRUNS_URL = "/v2/TestRuns"; /** * Test status PASSED. */ - public final static String TEST_STATUS_PASSED = "PASSED"; + public static final String TEST_STATUS_PASSED = "PASSED"; /** * Test status FAILED. */ - public final static String TEST_STATUS_FAILED = "FAILED"; + public static final String TEST_STATUS_FAILED = "FAILED"; /** * Test status PASSED_MANUAL. */ - public final static String TEST_STATUS_PASSED_MANUAL = "PASSED_MANUAL"; + public static final String TEST_STATUS_PASSED_MANUAL = "PASSED_MANUAL"; /** * Test status UNDEFINED. */ - public final static String TEST_STATUS_UNDEFINED = "UNDEFINED"; + public static final String TEST_STATUS_UNDEFINED = "UNDEFINED"; /** * Test status NOT_APPLICABLE. */ - public final static String TEST_STATUS_NOT_APPLICABLE = "NOT_APPLICABLE"; + public static final String TEST_STATUS_NOT_APPLICABLE = "NOT_APPLICABLE"; /** * Test status INTERNAL_ERROR. */ - public final static String TEST_STATUS_INTERNAL_ERROR = "INTERNAL_ERROR"; + public static final String TEST_STATUS_INTERNAL_ERROR = "INTERNAL_ERROR"; @Value("#{validatorAdditionalConfig['defaultTestSuite']}") public String defaultTestSuite; @@ -170,7 +172,7 @@ public void setTestsuites(Map testsuites) { this.testsuites = testsuites; } - public Map getTestsuites() { + public Map getTestsuites() { return testsuites; } @@ -178,7 +180,7 @@ public void setTestsuitesConditions(Map testsuitesConditions) { this.testsuitesConditions = testsuitesConditions; } - public Map getTestsuitesConditions() { + public Map getTestsuitesConditions() { return testsuitesConditions; } @@ -208,18 +210,18 @@ public InspireValidatorUtils() { * @return true, if successful * @throws IOException */ - public boolean checkServiceStatus(ServiceContext context, String endPoint) throws IOException { - HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + CheckStatus_URL); + public boolean checkServiceStatus(ServiceContext context, String endPoint) { + HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + CHECKSTATUS_URL); // add request header request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); try (ClientHttpResponse response = this.execute(context, request)) { - if (response.getStatusCode().value() == 200) { + if (response.getStatusCode().value() == HttpStatus.SC_OK) { return true; } else { - Log.warning(Log.SERVICE, "INSPIRE service not available: " + endPoint + CheckStatus_URL); + Log.warning(Log.SERVICE, "INSPIRE service not available: " + endPoint + CHECKSTATUS_URL); return false; } } catch (Exception e) { @@ -235,9 +237,9 @@ public boolean checkServiceStatus(ServiceContext context, String endPoint) throw * @param xml the xml * @return the string */ - private String uploadMetadataFile(ServiceContext context, String endPoint, InputStream xml) { + private String uploadMetadataFile(ServiceContext context, String endPoint, InputStream xml) throws InspireValidatorException { - HttpPost request = new HttpPost(StringUtils.removeEnd(endPoint, "/") + TestObjects_URL + "?action=upload"); + HttpPost request = new HttpPost(StringUtils.removeEnd(endPoint, "/") + TESTOBJECTS_URL + "?action=upload"); request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); @@ -254,20 +256,22 @@ private String uploadMetadataFile(ServiceContext context, String endPoint, Input response = this.execute(context, request); - if (response.getStatusCode().value() == 200) { + if (response.getStatusCode().value() == HttpStatus.SC_OK) { new BasicResponseHandler(); String body = CharStreams.toString(new InputStreamReader(response.getBody())); JSONObject jsonRoot = new JSONObject(body); return jsonRoot.getJSONObject("testObject").getString("id"); } else { - Log.warning(Log.SERVICE, - "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TestObjects_URL); - return null; + Log.error(Log.SERVICE, + "INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TESTOBJECTS_URL); + throw new InspireValidatorException(String.format("INSPIRE service - error uploading file: %s, error code: %d", response.getStatusText(), response.getStatusCode().value())); } + } catch (InspireValidatorException e) { + throw e; } catch (Exception e) { Log.error(Log.SERVICE, "Error calling INSPIRE service: " + endPoint, e); - return null; + throw new InspireValidatorException(String.format("Error calling INSPIRE service: %s, %s", endPoint, e.getMessage())); } finally { IOUtils.closeQuietly(response); } @@ -285,18 +289,15 @@ private List getTests(ServiceContext context, String endPoint, String te testsuite = getDefaultTestSuite(); } - HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + ExecutableTestSuites_URL); + HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + EXECUTABLETESTSUITES_URL); request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); - ClientHttpResponse response = null; - try { + try(ClientHttpResponse response = this.execute(context, request)) { String[] tests = testsuites.get(testsuite); - response = this.execute(context, request); - - if (response.getStatusCode().value() == 200) { + if (response.getStatusCode().value() == HttpStatus.SC_OK) { List testList = new ArrayList<>(); @@ -326,14 +327,12 @@ private List getTests(ServiceContext context, String endPoint, String te return testList; } else { Log.warning(Log.SERVICE, - "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + ExecutableTestSuites_URL); - return null; + "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + EXECUTABLETESTSUITES_URL); + return Collections.emptyList(); } } catch (Exception e) { Log.error(Log.SERVICE, "Exception in INSPIRE service: " + endPoint, e); - return null; - } finally { - IOUtils.closeQuietly(response); + return Collections.emptyList(); } } @@ -348,9 +347,9 @@ private List getTests(ServiceContext context, String endPoint, String te * @throws IOException Signals that an I/O exception has occurred. * @throws JSONException the JSON exception */ - private String testRun(ServiceContext context, String endPoint, String fileId, List testList, String testTitle) throws JSONException, IOException { + private String testRun(ServiceContext context, String endPoint, String fileId, List testList, String testTitle) throws InspireValidatorException { - HttpPost request = new HttpPost(StringUtils.removeEnd(endPoint, "/") + TestRuns_URL); + HttpPost request = new HttpPost(StringUtils.removeEnd(endPoint, "/") + TESTRUNS_URL); request.setHeader("Content-type", ACCEPT); request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); @@ -387,9 +386,10 @@ private String testRun(ServiceContext context, String endPoint, String fileId, L StringEntity entity = new StringEntity(json.toString()); request.setEntity(entity); + response = this.execute(context, request); - if (response.getStatusCode().value() == 201) { + if (response.getStatusCode().value() == HttpStatus.SC_CREATED) { String body = CharStreams.toString(new InputStreamReader(response.getBody())); @@ -400,8 +400,8 @@ private String testRun(ServiceContext context, String endPoint, String fileId, L return testId; } else { Log.warning(Log.SERVICE, - "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TestRuns_URL); - throw new IOException(String.format("Error while creating test on validator side. Status is: %d (%s). Error: %s", + "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TESTRUNS_URL); + throw new InspireValidatorException(String.format("Error while creating test on validator side. Status is: %d (%s). Error: %s", response.getStatusCode().value(), response.getStatusText(), response.getBody() != null @@ -409,9 +409,11 @@ private String testRun(ServiceContext context, String endPoint, String fileId, L )); } + } catch (InspireValidatorException e) { + throw e; } catch (Exception e) { Log.error(Log.SERVICE, "Exception in INSPIRE service: " + endPoint, e); - throw e; + throw new InspireValidatorException(String.format("Error calling INSPIRE service: %s, %s", endPoint, e.getMessage())); } finally { IOUtils.closeQuietly(response); } @@ -436,19 +438,19 @@ private void addApiKey(HttpRequestBase request) { * @return true, if is ready * @throws Exception */ - public boolean isReady(ServiceContext context, String endPoint, String testId) throws Exception { + public boolean isReady(ServiceContext context, String endPoint, String testId) throws ResourceNotFoundException, InspireValidatorException { if (testId == null) { return false; } - HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + TestRuns_URL + "/" + testId + "/progress"); + HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + TESTRUNS_URL + "/" + testId + "/progress"); request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); addApiKey(request); try (ClientHttpResponse response = this.execute(context, request)) { - if (response.getStatusCode().value() == 200) { + if (response.getStatusCode().value() == HttpStatus.SC_OK) { String body = CharStreams.toString(new InputStreamReader(response.getBody())); JSONObject jsonRoot = new JSONObject(body); @@ -456,22 +458,19 @@ public boolean isReady(ServiceContext context, String endPoint, String testId) t // Completed when estimated number of Test Steps is equal to completed Test Steps // Somehow this condition is necessary but not sufficient // so another check on real value of test is evaluated - return jsonRoot.getInt("val") == jsonRoot.getInt("max") & isPassed(context, endPoint, testId) != null; - - } else if (response.getStatusCode().value() == 404) { - + return jsonRoot.getInt("val") == jsonRoot.getInt("max") && isPassed(context, endPoint, testId) != null; + } else if (response.getStatusCode().value() == HttpStatus.SC_NOT_FOUND) { throw new ResourceNotFoundException("Test not found"); - } else { Log.warning(Log.SERVICE, - "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TestRuns_URL + "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TESTRUNS_URL + "?view=progress"); } } catch (ResourceNotFoundException e) { throw e; } catch (Exception e) { Log.error(Log.SERVICE, "Exception in INSPIRE service: " + endPoint, e); - throw e; + throw new InspireValidatorException(String.format("Exception in INSPIRE service: %s, %s", endPoint, e)); } return false; @@ -485,22 +484,20 @@ public boolean isReady(ServiceContext context, String endPoint, String testId) t * @return the string * @throws Exception */ - public String isPassed(ServiceContext context, String endPoint, String testId) throws Exception { + public String isPassed(ServiceContext context, String endPoint, String testId) throws InspireValidatorException { if (testId == null) { - throw new Exception(""); + throw new InspireValidatorException(String.format("Exception in INSPIRE service: %s, test not provided", endPoint)); } - HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + TestRuns_URL + "/" + testId); + HttpGet request = new HttpGet(StringUtils.removeEnd(endPoint, "/") + TESTRUNS_URL + "/" + testId); request.addHeader("User-Agent", USER_AGENT); request.addHeader("Accept", ACCEPT); addApiKey(request); try (ClientHttpResponse response = this.execute(context, request)) { - - if (response.getStatusCode().value() == 200) { - + if (response.getStatusCode().value() == HttpStatus.SC_OK) { String body = CharStreams.toString(new InputStreamReader(response.getBody())); JSONObject jsonRoot = new JSONObject(body); @@ -511,19 +508,16 @@ public String isPassed(ServiceContext context, String endPoint, String testId) t } catch (JSONException e) { return null; } - - } else if (response.getStatusCode().value() == 404) { - + } else if (response.getStatusCode().value() == HttpStatus.SC_NOT_FOUND) { throw new ResourceNotFoundException("Test not found"); - } else { Log.warning(Log.SERVICE, - "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TestRuns_URL + "WARNING: INSPIRE service HTTP response: " + response.getStatusCode().value() + " for " + TESTRUNS_URL + "?view=progress"); } } catch (Exception e) { Log.error(Log.SERVICE, "Exception in INSPIRE service: " + endPoint, e); - throw e; + throw new InspireValidatorException(String.format("Exception in INSPIRE service: %s, %s", endPoint, e)); } return null; @@ -538,7 +532,7 @@ public String isPassed(ServiceContext context, String endPoint, String testId) t */ public String getReportUrl(String endPoint, String testId) { - return endPoint + TestRuns_URL + "/" + testId + ".html"; + return endPoint + TESTRUNS_URL + "/" + testId + ".html"; } /** @@ -550,7 +544,7 @@ public String getReportUrl(String endPoint, String testId) { */ public static String getReportUrlJSON(String endPoint, String testId) { - return endPoint + TestRuns_URL + "/" + testId + ".json"; + return endPoint + TESTRUNS_URL + "/" + testId + ".json"; } /** @@ -562,35 +556,35 @@ public static String getReportUrlJSON(String endPoint, String testId) { */ public static String getReportUrlXML(String endPoint, String testId) { - return endPoint + TestRuns_URL + "/" + testId + ".xml"; + return endPoint + TESTRUNS_URL + "/" + testId + ".xml"; } /** * Submit file to the external ETF validator. * - * @param record the record + * @param metadataRecord the metadata record * @param testsuite * @return the string * @throws IOException Signals that an I/O exception has occurred. * @throws JSONException the JSON exception */ - public String submitFile(ServiceContext context, String serviceEndpoint, String serviceQueryEndpoint, InputStream record, String testsuite, String testTitle) - throws IOException, JSONException { + public String submitFile(ServiceContext context, String serviceEndpoint, String serviceQueryEndpoint, InputStream metadataRecord, String testsuite, String testTitle) + throws InspireValidatorException { if (checkServiceStatus(context, serviceQueryEndpoint)) { // Get the tests to execute List tests = getTests(context, serviceQueryEndpoint, testsuite); // Upload file to test - String testFileId = uploadMetadataFile(context, serviceQueryEndpoint, record); + String testFileId = uploadMetadataFile(context, serviceQueryEndpoint, metadataRecord); if (testFileId == null) { Log.error(Log.SERVICE, "File not valid.", new IllegalArgumentException()); return null; } - if (tests == null || tests.size() == 0) { + if (tests.isEmpty()) { Log.error(Log.SERVICE, - "Default test sequence not supported. Check org.fao.geonet.api.records.editing.InspireValidatorUtils.TESTS_TO_RUN_TG13.", + "Default test sequence not supported. Check org.fao.geonet.inspire.validator.InspireValidatorUtils.TESTS_TO_RUN_TG13.", new Exception()); return null; } @@ -598,9 +592,8 @@ public String submitFile(ServiceContext context, String serviceEndpoint, String return testRun(context, serviceEndpoint, testFileId, tests, testTitle); } else { - ServiceNotFoundEx ex = new ServiceNotFoundEx(serviceEndpoint); - Log.error(Log.SERVICE, "Service unavailable.", ex); - throw ex; + Log.error(Log.SERVICE, String.format("INSPIRE service end-point unavailable: %s", serviceEndpoint), new InspireValidatorException()); + throw new InspireValidatorException(String.format("INSPIRE service end-point unavailable: %s", serviceEndpoint)); } } @@ -613,32 +606,28 @@ public String submitFile(ServiceContext context, String serviceEndpoint, String * @throws JSONException the JSON exception */ public String submitUrl(ServiceContext context, String serviceEndpoint, String serviceEndpointQuery, String getRecordById, String testsuite, String testTitle) - throws IOException, JSONException { + throws InspireValidatorException { - try { - if (checkServiceStatus(context, serviceEndpointQuery)) { - // Get the tests to execute - List tests = getTests(context, serviceEndpoint, testsuite); - if (tests == null || tests.size() == 0) { - Log.error(Log.SERVICE, - "Default test sequence not supported. Check org.fao.geonet.api.records.editing.InspireValidatorUtils.TESTS_TO_RUN_TG13.", - new Exception()); - return null; - } - // Return test id from Inspire service - return testRun(context, serviceEndpoint, getRecordById, tests, testTitle); - - } else { - ServiceNotFoundEx ex = new ServiceNotFoundEx(serviceEndpoint); - Log.error(Log.SERVICE, "Service unavailable.", ex); - throw ex; + if (checkServiceStatus(context, serviceEndpointQuery)) { + // Get the tests to execute + List tests = getTests(context, serviceEndpoint, testsuite); + if (tests.isEmpty()) { + Log.error(Log.SERVICE, + "Default test sequence not supported. Check org.fao.geonet.inspire.validator.InspireValidatorUtils.TESTS_TO_RUN_TG13.", + new Exception()); + return null; } - } finally { - // client.close(); + // Return test id from Inspire service + return testRun(context, serviceEndpoint, getRecordById, tests, testTitle); + + } else { + ServiceNotFoundEx ex = new ServiceNotFoundEx(serviceEndpoint); + Log.error(Log.SERVICE, "Service unavailable.", ex); + throw ex; } } - public String retrieveReport(ServiceContext context, String endPoint) throws Exception { + public String retrieveReport(ServiceContext context, String endPoint) throws InspireValidatorException { HttpGet request = new HttpGet(endPoint); @@ -650,11 +639,12 @@ public String retrieveReport(ServiceContext context, String endPoint) throws Exc return IOUtils.toString(response.getBody(), StandardCharsets.UTF_8.name()); } catch (Exception e) { Log.warning(Log.SERVICE, "Error calling INSPIRE service to retrieve the result report: " + endPoint, e); - throw e; + throw new InspireValidatorException(String.format("Error calling INSPIRE service to retrieve the result report: %s, %s", endPoint, e.getMessage())); } } - public void waitUntilReady(ServiceContext context, String endPoint, String testId) throws Exception { + public void waitUntilReady(ServiceContext context, String endPoint, String testId) throws + ResourceNotFoundException, InterruptedException, InspireValidatorException { int checkCounter = 1; while (checkCounter++ <= maxNumberOfEtfChecks) { @@ -665,7 +655,7 @@ public void waitUntilReady(ServiceContext context, String endPoint, String testI Thread.sleep(intervalBetweenEtfChecks); } - throw new Exception("ETF validation task hasn't finish after " + maxNumberOfEtfChecks + " checks."); + throw new InspireValidatorException(String.format("ETF validation task hasn't finish after %d checks.", maxNumberOfEtfChecks)); } /** @@ -710,12 +700,12 @@ public MetadataValidationStatus calculateValidationStatus(String validationStatu * @return */ public Map calculateTestsuitesToApply(String schemaid, IMetadataSchemaUtils metadataSchemaUtils) { - Map testsuitesConditions = getTestsuitesConditions(); + Map allTestsuitesConditions = getTestsuitesConditions(); // Check for rules for the schema - Map testsuitesConditionsForSchema = testsuitesConditions.entrySet().stream() + Map testsuitesConditionsForSchema = allTestsuitesConditions.entrySet().stream() .filter(x -> x.getKey().split("::")[0].equalsIgnoreCase(schemaid)) - .collect(Collectors.toMap(map -> map.getKey().split("::")[1], map -> map.getValue())); + .collect(Collectors.toMap(map -> map.getKey().split("::")[1], Map.Entry::getValue)); // If no rules found, check the rules in the dependencies of the schema if (testsuitesConditionsForSchema.isEmpty()) { @@ -732,9 +722,9 @@ public Map calculateTestsuitesToApply(String schemaid, IMetadata schemasProcessed.add(schemaDependsOn); String schemaDependsOnFilter = schemaDependsOn; - testsuitesConditionsForSchema = testsuitesConditions.entrySet().stream() + testsuitesConditionsForSchema = allTestsuitesConditions.entrySet().stream() .filter(x -> x.getKey().split("::")[0].equalsIgnoreCase(schemaDependsOnFilter)) - .collect(Collectors.toMap(map -> map.getKey().split("::")[1], map -> map.getValue())); + .collect(Collectors.toMap(map -> map.getKey().split("::")[1], Map.Entry::getValue)); conditionsFound = !testsuitesConditionsForSchema.isEmpty(); diff --git a/services/src/main/java/org/fao/geonet/api/processing/MInspireEtfValidateProcess.java b/services/src/main/java/org/fao/geonet/inspire/validator/MInspireEtfValidateProcess.java similarity index 87% rename from services/src/main/java/org/fao/geonet/api/processing/MInspireEtfValidateProcess.java rename to services/src/main/java/org/fao/geonet/inspire/validator/MInspireEtfValidateProcess.java index 9f640f7e80a..07c1faf1c71 100644 --- a/services/src/main/java/org/fao/geonet/api/processing/MInspireEtfValidateProcess.java +++ b/services/src/main/java/org/fao/geonet/inspire/validator/MInspireEtfValidateProcess.java @@ -1,13 +1,11 @@ -package org.fao.geonet.api.processing; +package org.fao.geonet.inspire.validator; import jeeves.server.context.ServiceContext; import jeeves.transaction.TransactionManager; import jeeves.transaction.TransactionTask; import org.apache.commons.lang.StringUtils; -import org.apache.http.HttpStatus; import org.fao.geonet.NodeInfo; import org.fao.geonet.api.API; -import org.fao.geonet.api.records.editing.InspireValidatorUtils; import org.fao.geonet.api.records.formatters.FormatType; import org.fao.geonet.api.records.formatters.FormatterApi; import org.fao.geonet.api.records.formatters.FormatterWidth; @@ -48,8 +46,8 @@ public class MInspireEtfValidateProcess implements SelfNaming { private final ApplicationContext appContext; private final ServiceContext serviceContext; - private final String URL; - private final String URL_QUERY; + private final String url; + private final String urlQuery; private ObjectName probeName; private int metadataToAnalyseCount = -1; @@ -62,11 +60,11 @@ public class MInspireEtfValidateProcess implements SelfNaming { public MInspireEtfValidateProcess(String catalogueId, - String URL, String URL_QUERY, + String url, String urlQuery, ServiceContext serviceContext, ApplicationContext appContext) { - this.URL = URL; - this.URL_QUERY = URL_QUERY; + this.url = url; + this.urlQuery = urlQuery; this.serviceContext = serviceContext; this.appContext = appContext; @@ -151,9 +149,9 @@ public void processMetadata(Set uuids, String mode) throws Exception { continue; } - for (AbstractMetadata record : metadataRepository.findAllByUuid(uuid)) { + for (AbstractMetadata metadataRecord : metadataRepository.findAllByUuid(uuid)) { try { - if (!accessManager.canEdit(serviceContext, String.valueOf(record.getId()))) { + if (!accessManager.canEdit(serviceContext, String.valueOf(metadataRecord.getId()))) { metadataAnalysed++; metadataNotAllowed++; } else { @@ -162,11 +160,11 @@ public void processMetadata(Set uuids, String mode) throws Exception { public Object doInTransaction(TransactionStatus transaction) throws Throwable { // Evaluate test conditions for INSPIRE test suites to apply to the metadata Map testsuiteConditions = - inspireValidatorUtils.calculateTestsuitesToApply(record.getDataInfo().getSchemaId(), metadataSchemaUtils); + inspireValidatorUtils.calculateTestsuitesToApply(metadataRecord.getDataInfo().getSchemaId(), metadataSchemaUtils); boolean reindexMetadata = false; - String mdToValidate = retrieveMetadataToValidate(context, record); + String mdToValidate = retrieveMetadataToValidate(context, metadataRecord); try { boolean inspireMetadata = false; @@ -176,12 +174,12 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { boolean applyCondition = false; try { // Checks the condition in the original record - applyCondition = Xml.selectBoolean(record.getXmlData(false), + applyCondition = Xml.selectBoolean(metadataRecord.getXmlData(false), entry.getValue(), - schemaManager.getSchema(record.getDataInfo().getSchemaId()).getNamespaces()); + schemaManager.getSchema(metadataRecord.getDataInfo().getSchemaId()).getNamespaces()); } catch (Exception ex) { Log.error(API.LOG_MODULE_NAME, String.format("Error checking INSPIRE rule %s to apply to metadata: %s", - entry.getKey(), record.getUuid()), ex); + entry.getKey(), metadataRecord.getUuid()), ex); } if (applyCondition) { @@ -189,8 +187,8 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { String testId = null; String getRecordByIdUrl = null; if (StringUtils.isEmpty(mode)) { - testId = inspireValidatorUtils.submitFile(serviceContext, URL, URL_QUERY, - new ByteArrayInputStream(mdToValidate.getBytes()), entry.getKey(), record.getUuid()); + testId = inspireValidatorUtils.submitFile(serviceContext, url, urlQuery, + new ByteArrayInputStream(mdToValidate.getBytes()), entry.getKey(), metadataRecord.getUuid()); } else { String portal = null; if (!NodeInfo.DEFAULT_NODE.equals(mode)) { @@ -213,25 +211,25 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { appContext.getBean(SettingManager.class).getBaseURL(), portal, ISO19139Namespaces.GMD.getURI(), - record.getUuid()); - testId = inspireValidatorUtils.submitUrl(serviceContext, URL, URL_QUERY, getRecordByIdUrl, entry.getKey(), record.getUuid()); + metadataRecord.getUuid()); + testId = inspireValidatorUtils.submitUrl(serviceContext, url, urlQuery, getRecordByIdUrl, entry.getKey(), metadataRecord.getUuid()); } } if (testId != null) { - inspireValidatorUtils.waitUntilReady(serviceContext, URL, testId); + inspireValidatorUtils.waitUntilReady(serviceContext, url, testId); - String reportUrl = inspireValidatorUtils.getReportUrl(URL, testId); - String reportXmlUrl = InspireValidatorUtils.getReportUrlXML(URL, testId); + String reportUrl = inspireValidatorUtils.getReportUrl(url, testId); + String reportXmlUrl = InspireValidatorUtils.getReportUrlXML(url, testId); String reportXml = inspireValidatorUtils.retrieveReport(serviceContext, reportXmlUrl); - String validationStatus = inspireValidatorUtils.isPassed(serviceContext, URL, testId); + String validationStatus = inspireValidatorUtils.isPassed(serviceContext, url, testId); MetadataValidationStatus metadataValidationStatus = inspireValidatorUtils.calculateValidationStatus(validationStatus); MetadataValidation metadataValidation = new MetadataValidation() - .setId(new MetadataValidationId(record.getId(), "inspire")) + .setId(new MetadataValidationId(metadataRecord.getId(), "inspire")) .setStatus(metadataValidationStatus).setRequired(false) .setReportUrl(reportUrl).setReportContent(reportXml); @@ -252,7 +250,7 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { metadataNotInspire++; MetadataValidation metadataValidation = new MetadataValidation() - .setId(new MetadataValidationId(record.getId(), "inspire")) + .setId(new MetadataValidationId(metadataRecord.getId(), "inspire")) .setStatus(MetadataValidationStatus.DOES_NOT_APPLY).setRequired(false); metadataValidationRepository.save(metadataValidation); @@ -265,14 +263,14 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { } if (reindexMetadata) { - dataManager.indexMetadata(new ArrayList<>(Arrays.asList(record.getId() + ""))); + dataManager.indexMetadata(new ArrayList<>(Arrays.asList(metadataRecord.getId() + ""))); } } catch (Exception ex) { metadataAnalysedInError++; Log.error(API.LOG_MODULE_NAME, String.format("Error validating metadata %s in INSPIRE validator: %s", - record.getUuid(), ex.getMessage()), ex); + metadataRecord.getUuid(), ex.getMessage()), ex); } metadataAnalysed++; @@ -286,7 +284,7 @@ public Object doInTransaction(TransactionStatus transaction) throws Throwable { metadataAnalysedInError++; Log.error(API.LOG_MODULE_NAME, String.format("Error validating metadata %s in INSPIRE validator: %s", - record.getUuid(), ex.getMessage()), ex); + metadataRecord.getUuid(), ex.getMessage()), ex); } @@ -306,15 +304,15 @@ private final void runInNewTransaction(String name, TransactionTask tran * otherwise if not available an iso19139 formatter returns null. * * @param context - * @param record + * @param metadataRecord * @return Metadata to validate or null if can't be converted to iso19139 format. */ - private String retrieveMetadataToValidate(ServiceContext context, AbstractMetadata record) { + private String retrieveMetadataToValidate(ServiceContext context, AbstractMetadata metadataRecord) { String mdToValidate = null; - if (!record.getDataInfo().getSchemaId().equals(ISO19139SchemaPlugin.IDENTIFIER)) { + if (!metadataRecord.getDataInfo().getSchemaId().equals(ISO19139SchemaPlugin.IDENTIFIER)) { try { - Key key = new Key(record.getId(), "eng", FormatType.xml, "iso19139", true, FormatterWidth._100); + Key key = new Key(metadataRecord.getId(), "eng", FormatType.xml, "iso19139", true, FormatterWidth._100); final FormatterApi.FormatMetadata formatMetadata = new FormatterApi().new FormatMetadata(context, key, null); @@ -323,10 +321,10 @@ private String retrieveMetadataToValidate(ServiceContext context, AbstractMetada } catch (Exception ex) { Log.error(API.LOG_MODULE_NAME, String.format("Error converting metadata %s to ISO19139 for INSPIRE validator: %s", - record.getUuid(), ex.getMessage()), ex); + metadataRecord.getUuid(), ex.getMessage()), ex); } } else { - mdToValidate = record.getData(); + mdToValidate = metadataRecord.getData(); } return mdToValidate; diff --git a/services/src/main/resources/config-spring-geonetwork.xml b/services/src/main/resources/config-spring-geonetwork.xml index e5baa797a34..33f965c8d9f 100644 --- a/services/src/main/resources/config-spring-geonetwork.xml +++ b/services/src/main/resources/config-spring-geonetwork.xml @@ -64,7 +64,7 @@ diff --git a/services/src/test/java/org/fao/geonet/api/pages/PagesApiTest.java b/services/src/test/java/org/fao/geonet/api/pages/PagesApiTest.java index a77f0f1f3c1..8a3709e7223 100644 --- a/services/src/test/java/org/fao/geonet/api/pages/PagesApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/pages/PagesApiTest.java @@ -109,6 +109,7 @@ public void putPage() throws Exception { newPage.setPageId(pageId); newPage.setLink(link + "updated"); newPage.setLabel(pageId + "updated"); + newPage.setIcon("dummy-icon"); newPage.getSections().add(Page.PageSection.FOOTER); MockHttpServletRequestBuilder updatePageBuilder = put("/srv/api/pages/eng/license") .content(gson.toJson(newPage)) @@ -123,6 +124,7 @@ public void putPage() throws Exception { Assert.assertTrue(page.isPresent()); Assert.assertEquals(link + "updated", page.get().getLink()); Assert.assertEquals(pageId + "updated", page.get().getLabel()); + Assert.assertEquals("dummy-icon", page.get().getIcon()); Assert.assertTrue(page.get().getSections().contains(Page.PageSection.TOP)); Assert.assertTrue(page.get().getSections().contains(Page.PageSection.FOOTER)); 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 0eb0bcc1607..dd9f3880916 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 @@ -180,7 +180,7 @@ public void getRecord() throws Exception { contentTypes.put(MediaType.APPLICATION_XHTML_XML_VALUE, this.uuid + "/formatters/xsl-view"); contentTypes.put("application/pdf", this.uuid + "/formatters/xsl-view"); contentTypes.put(MediaType.APPLICATION_XML_VALUE, this.uuid + "/formatters/xml"); - contentTypes.put(MediaType.APPLICATION_JSON_VALUE, this.uuid + "/formatters/xml"); + contentTypes.put(MediaType.APPLICATION_JSON_VALUE, this.uuid + "/formatters/json"); contentTypes.put("application/zip", this.uuid + "/formatters/zip"); contentTypes.put(MEF_V1_ACCEPT_TYPE, this.uuid + "/formatters/zip"); contentTypes.put(MEF_V2_ACCEPT_TYPE, this.uuid + "/formatters/zip"); diff --git a/services/src/test/java/org/fao/geonet/api/records/editing/InspireValidatorUtilsTest.java b/services/src/test/java/org/fao/geonet/api/records/editing/InspireValidatorUtilsTest.java index 77805a1a416..07d95187e81 100644 --- a/services/src/test/java/org/fao/geonet/api/records/editing/InspireValidatorUtilsTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/editing/InspireValidatorUtilsTest.java @@ -3,6 +3,7 @@ import jeeves.server.context.ServiceContext; import org.fao.geonet.api.exception.ResourceNotFoundException; import org.fao.geonet.domain.MetadataValidationStatus; +import org.fao.geonet.inspire.validator.InspireValidatorUtils; import org.fao.geonet.services.AbstractServiceIntegrationTest; import org.junit.Before; import org.junit.Ignore; diff --git a/services/src/test/java/org/fao/geonet/api/records/extent/MapRendererTest.java b/services/src/test/java/org/fao/geonet/api/records/extent/MapRendererTest.java index 47ca774eea0..4a176232520 100644 --- a/services/src/test/java/org/fao/geonet/api/records/extent/MapRendererTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/extent/MapRendererTest.java @@ -1,3 +1,26 @@ +/* + * 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) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * + * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2, + * Rome - Italy. email: geonetwork@osgeo.org + */ + package org.fao.geonet.api.records.extent; import org.fao.geonet.kernel.region.Region; @@ -48,4 +71,55 @@ public void domainIntersectsBoundingPolygon() throws Exception { String result = wktWriter.write(MapRenderer.computeGeomInDomainOfValidity(bbox, Region.decodeCRS("EPSG:3857"))); assertEquals("POLYGON ((159.70909090909092 -85.06, 135 -76, 161 -76, 153 -81, 161.12 -85.06, 159.70909090909092 -85.06))", result); } + + @Test + public void extentGeodesicLocalProjection() throws Exception { + String test = "POLYGON ((646.3563610491983 308975.2885578188, 10545.528150276805 637111.0281460616, 276050.8102636032 636456.3084312443, 284347.2430806639 308289.5622343737, 646.3563610491983 308975.2885578188))"; + String localSrs = "EPSG:28992"; + Geometry geometry = wktReader.read(test); + geometry.setSRID(28992); + + Geometry geometryExtent = MapRenderer.getGeometryExtent(geometry, localSrs, true); + assertEquals(geometry, geometryExtent); + } + + @Test + public void extentNonGeodesicLocalProjection() throws Exception { + String test = "POLYGON ((646.3563610491983 308975.2885578188, 10545.528150276805 637111.0281460616, 276050.8102636032 636456.3084312443, 284347.2430806639 308289.5622343737, 646.3563610491983 308975.2885578188))"; + String localSrs = "EPSG:28992"; + Geometry geometry = wktReader.read(test); + geometry.setSRID(28992); + + Geometry geometryExtent = MapRenderer.getGeometryExtent(geometry, localSrs, false); + assertEquals(geometry.getEnvelope(), geometryExtent); + } + + @Test + public void extentGlobal3857Projection() throws Exception { + String test = "POLYGON ((159.70909090909092 -85.06, 135 -76, 161 -76, 153 -81, 161.12 -85.06, 159.70909090909092 -85.06))"; + String globalSrs = "EPSG:3857"; + Geometry geometry = wktReader.read(test); + geometry.setSRID(3857); + + // For global projections + Geometry geometryExtent = MapRenderer.getGeometryExtent(geometry, globalSrs, true); + assertEquals(geometry, geometryExtent); + + geometryExtent = MapRenderer.getGeometryExtent(geometry, globalSrs, false); + assertEquals(geometry, geometryExtent); + } + + @Test + public void extentGlobal4326Projection() throws Exception { + String test = "POLYGON ((165 -87, 135 -76, 161 -76, 153 -81, 165 -87))"; + String globalSrs = "EPSG:4326"; + Geometry geometry = wktReader.read(test); + geometry.setSRID(4326); + + Geometry geometryExtent = MapRenderer.getGeometryExtent(geometry, globalSrs, true); + assertEquals(geometry, geometryExtent); + + geometryExtent = MapRenderer.getGeometryExtent(geometry, globalSrs, false); + assertEquals(geometry, geometryExtent); + } } diff --git a/services/src/test/java/org/fao/geonet/api/records/formatters/AbstractFormatterTest.java b/services/src/test/java/org/fao/geonet/api/records/formatters/AbstractFormatterTest.java index 7c201ebd10d..285b4e29f05 100644 --- a/services/src/test/java/org/fao/geonet/api/records/formatters/AbstractFormatterTest.java +++ b/services/src/test/java/org/fao/geonet/api/records/formatters/AbstractFormatterTest.java @@ -118,7 +118,7 @@ protected void measureFormatterPerformance(final MockHttpServletRequest request, TestFunction testFunction = new TestFunction() { @Override public void exec() throws Exception { - formatService.exec(getUILang(), getOutputType().name(), "" + id, null, formatterId, "true", false, FormatterWidth._100, + formatService.exec(getUILang(), getOutputType().name(), "" + id, null, formatterId, "true", FormatterWidth._100, webRequest); } }; 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 b019a489d36..60bf0c6a49f 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 @@ -94,7 +94,7 @@ public void testExec() throws Exception { JeevesDelegatingFilterProxy.setApplicationContextAttributeKey(srvAppContext); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); - formatService.exec("eng", "html", "" + id, null, formatter.getId(), "true", false, _100, new ServletWebRequest(request, response)); + formatService.exec("eng", "html", "" + id, null, formatter.getId(), "true", _100, new ServletWebRequest(request, response)); final String view = response.getContentAsString(); try { @@ -105,7 +105,7 @@ public void testExec() throws Exception { } try { response = new MockHttpServletResponse(); - formatService.exec("eng", "testpdf", "" + id, null, formatter.getId(), "true", false, _100, + formatService.exec("eng", "testpdf", "" + id, null, formatter.getId(), "true", _100, new ServletWebRequest(request, response)); // Files.write(Paths.get("e:/tmp/view.pdf"), response.getContentAsByteArray()); // System.exit(0); @@ -138,7 +138,7 @@ public void testExecXslt() throws Exception { JeevesDelegatingFilterProxy.setApplicationContextAttributeKey(applicationContextAttributeKey); final MockHttpServletResponse response = new MockHttpServletResponse(); - formatService.exec("eng", "html", "" + id, null, formatterName, "true", false, _100, new ServletWebRequest(request, response)); + formatService.exec("eng", "html", "" + id, null, formatterName, "true", _100, new ServletWebRequest(request, response)); final String viewXml = response.getContentAsString(); final Element view = Xml.loadString(viewXml, false); assertEqualsText("fromFunction", view, "*//p"); diff --git a/services/src/test/java/org/fao/geonet/api/registries/vocabularies/KeywordsApiTest.java b/services/src/test/java/org/fao/geonet/api/registries/vocabularies/KeywordsApiTest.java index 68cc20c7e6e..01871cb85a0 100644 --- a/services/src/test/java/org/fao/geonet/api/registries/vocabularies/KeywordsApiTest.java +++ b/services/src/test/java/org/fao/geonet/api/registries/vocabularies/KeywordsApiTest.java @@ -32,16 +32,24 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockMultipartHttpServletRequest; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.WebApplicationContext; import javax.servlet.http.HttpSession; import java.util.List; import static org.fao.geonet.csw.common.Csw.NAMESPACE_DC; +import static org.fao.geonet.csw.common.Csw.NAMESPACE_DCT; import static org.fao.geonet.kernel.rdf.Selectors.RDF_NAMESPACE; import static org.fao.geonet.kernel.rdf.Selectors.SKOS_NAMESPACE; import static org.junit.Assert.assertEquals; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** * export CATALOG=http://localhost:8080/geonetwork @@ -61,6 +69,8 @@ public class KeywordsApiTest extends AbstractServiceIntegrationTest { @Autowired private SpringLocalServiceInvoker invoker; + @Autowired + private WebApplicationContext wac; @Test public void testConvertCsvToSkos() throws Exception { @@ -199,4 +209,46 @@ public void testConvertCsvToSkosDefaultTitleAndNamespace() throws Exception { assertEquals( "taxref.csv", scheme.getChildText("title", NAMESPACE_DC)); } + + + @Test + public void testImportOntologyToSkos() throws Exception { + createServiceContext(); + User user = new User().setId(USER_ID); + HttpSession session = loginAs(user); + MockHttpSession mockHttpSession = loginAsAdmin(); + + MockMultipartHttpServletRequest request = new MockMultipartHttpServletRequest(session.getServletContext()); + request.setRequestURI("/srv/api/registries/vocabularies"); + MockMultipartFile file = new MockMultipartFile( + "file", + "mobility-theme.owl", + null, + getClass().getClassLoader().getResourceAsStream("mobility-theme.owl")); + request.addFile(file); + request.setSession(session); + request.setParameter("type", "external"); + request.setParameter("dir", "theme"); + MockHttpServletResponse response = new MockHttpServletResponse(); + invoker.invoke(request, response); + assertEquals(200, response.getStatus()); + + + MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build(); + MvcResult result = mockMvc.perform(get("/srv/api/registries/vocabularies/external.theme.mobility-theme") + .accept("application/xml") + .session(mockHttpSession)) + .andExpect(status().isOk()) + .andReturn(); + + Element thesaurus = Xml.loadString(result.getResponse().getContentAsString(), false); + Element scheme = (Element) thesaurus.getChildren("ConceptScheme", SKOS_NAMESPACE).get(0); + assertEquals( + "https://w3id.org/mobilitydcat-ap/mobility-theme", scheme.getAttributeValue("about", RDF_NAMESPACE)); + assertEquals( + "Mobility Theme", scheme.getChildText("title", NAMESPACE_DCT)); + + List concepts = thesaurus.getChildren("Concept", SKOS_NAMESPACE); + assertEquals(123, concepts.size()); + } } diff --git a/services/src/test/resources/mobility-theme.owl b/services/src/test/resources/mobility-theme.owl new file mode 100644 index 00000000000..2c661d26f2e --- /dev/null +++ b/services/src/test/resources/mobility-theme.owl @@ -0,0 +1,1979 @@ + + + + + Mario Scrocca (Cefriel) + Peter Lubrich (BASt) + Controlled vocabulary for the classification of the theme for a mobility dataset. + + NAPCORE SubWG4.4 + Mobility Theme + Published Controlled Vocabulary + 1.0.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Accesibility information for vehicles + + + + + + + + + + + + + Accidents and incidents + + + + + + + + + + + + + Address identifiers + + + + + + + + + + + + Air and space travel + + + + + + + + + + + + + Applicable road user charges and payment methods + + + + + + + + + + + + + Availability of charging points for electric vehicles + + + + + + + + + + + + + Availability of delivery areas + + + + + + + + + + + + + Availability of filling stations + + + + + + + + + + + + + Basic commercial conditions + + + + + + + + + + + + + Basic common standard fares + + + + + + + + + + + + + Bike-hiring Availability + + + + + + + + + + + + + Bike-hiring Stations + + + + + + + + + + + + + Bike-parking locations + + + + + + + + + + + + + Bike sharing Availability + + + + + + + + + + + + + Bike-sharing Locations and stations + + + + + + + + + + + + + Bridge access conditions + + + + + + + + + + + + + Bridge closures and access conditions + + + + + + + + + + + + + Car-hiring Availability + + + + + + + + + + + + + Car parking availability + + + + + + + + + + + + + Car parking locations and conditions + + + + + + + + + + + + + Car-sharing Availability + + + + + + + + + + + + + Car-sharing Locations and stations + + + + + + + + + + + + + Car-sharing Stations + + + + + + + + + + + + + Common fare products + + + + + + + + + + + + + Connection links + + + + + + + + + + + + + Current travel times + + + + + + + + + + + + + + + Cycle network data + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data content category + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Data content sub-category + + + + + + + + + + + + + + Direction of travel on reversible lanes + + + + + + + + + + + + + Disruptions, delays, cancellations + + + + + + + + + + + + + Dynamic overtaking bans on heavy goods vehicles + + + + + + + + + + + + + Dynamic speed limits + + + + + + + + + + + + + + + + + + + + + Dynamic traffic signs and regulations + + + + + + + + + + + + + E-scooter-sharing Availability + + + + + + + + + + + + + E-scooter-sharing Locations and stations + + + + + + + + + + + + + + + Environmental standards for vehicles + + + + + + + + + + + + + Expected delays + + + + + + + + + + + + + Fares + + + + + + + + + + + + + + + + Filling and charging stations + + + + + + + + + + + + + + + Freight and logistics + + + + + + + + + + + + + Freight delivery regulations + + + + + + + + + + + + + + + + + General information for trip-planning + + + + + + + + + + + + + Geometry + + + + + + + + + + + + + Gradients + + + + + + + + + + + + + Hours of operation + + + + + + + + + + + + + Identification of tolled roads + + + + + + + + + + + + + Junctions + + + + + + + + + + + + + Lane closures and access conditions + + + + + + + + + + + + + Location and conditions of charging points + + + + + + + + + + + + + Location and conditions of filling stations + + + + + + + + + + + + + Location and length of queues + + + + + + + + + + + + + Location of delivery areas + + + + + + + + + + + + + Location of tolling stations + + + + + + + + + + + + + Locations and stations + + + + + + + + + + + + + Long-term road works + + + + + + + + + + + + + Network closures/diversions + + + + + + + + + + + + + Network detailed attributes + + + + + + + + + + + + + Network geometry and lane character + + + + + + + + + + + + + Network topology and routes/lines + + + + + + + + + + + + + Number of lanes + + + + + + + + + + + + + Operational Calendar + + + + + + + + + + + + Other + + + + + + + + + + + + + Other access restrictions and traffic regulations + + + + + + + + + + + + + Other static traffic signs + + + + + + + + + + + + + Other temporary traffic management measures or plans + + + + + + + + + + + + + Other traffic regulations + + + + + + + + + + + + + Parameters needed to calculate costs + + + + + + + + + + + + + Parameters needed to calculate environmental factors + + + + + + + + + + + + + Park and Ride stops + + + + + + + + + + + + + + + + + + + + Parking, service and rest area information + + + + + + + + + + + + + Passenger classes + + + + + + + + + + + + + Payment methods + + + + + + + + + + + + + Payment methods for tolls + + + + + + + + + + + + + Pedestrian accessibility facilities + + + + + + + + + + + + + + Pedestrian network data + + + + + + + + + + + + + Pedestrian network geometry + + + + + + + + + + + + + Permanent access restrictions + + + + + + + + + + + + + Planned interchanges between scheduled services + + + + + + + + + + + + + Points of interest + + + + + + + + + + + + + Poor road conditions + + + + + + + + + + + + + Predicted travel times + + + + + + + + + + + + + Provider data + + + + + + + + + + + + + + + + + + + Public transport non-scheduled transport + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Public transport scheduled transport + + + + + + + + + + + + + Purchase information + + + + + + + + + + + + + Real-time estimated departure and arrival times + + + + + + + + + + + + + + + + + + + + Real-time traffic data + + + + + + + + + + + + + Reservation and purchase options + + + + + + + + + + + + + Road classification + + + + + + + + + + + + + Road closures and access conditions + + + + + + + + + + + + + + + Road events and conditions + + + + + + + + + + + + + Road weather conditions + + + + + + + + + + + + + Road width + + + + + + + + + + + + + + Road work information + + + + + + + + + + + + + Service and rest area availability + + + + + + + + + + + + + Service and rest area locations and conditions + + + + + + + + + + + + + Service areas and service times + + + + + + + + + + + + + + + + + + + + + + + + Sharing and Hiring Services + + + + + + + + + + + + + Short-term road works + + + + + + + + + + + + + Special Fare Products + + + + + + + + + + + + + Speed + + + + + + + + + + + + + Speed limits + + + + + + + + + + + + + + + + + + Static road network data + + + + + + + + + + + + + + + + + + + Static traffic signs and regulations + + + + + + + + + + + + + Stop facilities accessibility and paths within facility + + + + + + + + + + + + + Stop facilities geometry and map layout + + + + + + + + + + + + + Stop facilities location and features + + + + + + + + + + + + + Stop facilities status of features + + + + + + + + + + + + + Timetables static + + + + + + + + + + + + + + + + Toll information + + + + + + + + + + + + + Topographic places + + + + + + + + + + + + + Traffic circulation plans + + + + + + + + + + + + + Traffic data at border crossings to third countries + + + + + + + + + + + + + Traffic volume + + + + + + + + + + + + + Transport operators + + + + + + + + + + + + + Truck parking availability + + + + + + + + + + + + + Truck parking locations and conditions + + + + + + + + + + + + + Tunnel access conditions + + + + + + + + + + + + + Tunnel closures and access conditions + + + + + + + + + + + + + Vehicle details + + + + + + + + + + + + + Waiting time at border crossings to non-EU Member States + + + + + + + + + + + + Waterways and water bodies + + + + + + + + diff --git a/slave/pom.xml b/slave/pom.xml index c7541870efd..fcffaafd4fd 100644 --- a/slave/pom.xml +++ b/slave/pom.xml @@ -28,7 +28,7 @@ geonetwork org.geonetwork-opensource - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT GeoNetwork Slave diff --git a/software_development/GITHUB.md b/software_development/GITHUB.md index defd00d6e85..7db12a215fb 100644 --- a/software_development/GITHUB.md +++ b/software_development/GITHUB.md @@ -34,3 +34,23 @@ GeoNetwork uses feature branches for development, and a pull-request workflow fo * [Contributing](../CONTRIBUTING.md). * [Making a pull request](https://docs.geonetwork-opensource.org/latest/contributing/making-a-pull-request/). + +## Automation + +### Quality Assurance + +A number of [workflows](../.github/workflows/) are setup to ensure each PR compiles, passes tests and so forth. + +* [linux.yml](../.github/workflows/linux.yml): build and QA, including -Drelease check +* [docs.yml](../.github/workflows/docs.yml): publish docs to gh-pages branch +* [backport.yml](../.github/workflows/backport.yml): backport tagged pull requests + +### Tags + +Use backport tags to take advantage of the [backport.yml](https://github.com/m-kuhn/backport) automation: + +> Backport is a JavaScript GitHub Action to backport a pull request by simply adding a label to it. +> +> It can backport rebased and merged pull requests with a single commit and squashed and merged pull requests. I + +This agrees with our CONTRIBUTING policy of using rebase and squash and merge. diff --git a/web-ui/download-from-transifex.sh b/web-ui/download-from-transifex.sh index 6aadf404ce4..f3e72045e98 100755 --- a/web-ui/download-from-transifex.sh +++ b/web-ui/download-from-transifex.sh @@ -59,6 +59,7 @@ l=( 'nl::nl' 'cs::cs_CZ' 'ca::ca' + 'cy::cy' 'pt::pt_BR' 'fi::fi' 'is::is' diff --git a/web-ui/pom.xml b/web-ui/pom.xml index a1aba6095a1..c360922c8ac 100644 --- a/web-ui/pom.xml +++ b/web-ui/pom.xml @@ -30,7 +30,7 @@ org.geonetwork-opensource geonetwork - 4.4.2-SNAPSHOT + 4.4.4-SNAPSHOT org.geonetwork-opensource @@ -47,14 +47,6 @@ - - - org.codehaus.mojo - jslint-maven-plugin - 1.0.1 - - - +
+ +
+

{{alertMsg}}

diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html index 3fbc27150ff..9d1cd9e388f 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html @@ -1,9 +1,16 @@
+
-
-
+
+ +
-
+ + +
+
+ +
diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html index 8e108777894..ec3b600611a 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html @@ -13,6 +13,7 @@ data-allow-blank="false" data-selected-info="config.associationType" data-gn-schema-info="associationType" + data-ng-disabled="config.associationTypeForced" lang="lang" >
@@ -29,12 +30,19 @@ data-gn-schema-info="initiativeType" lang="lang" data-allow-blank="true" + data-ng-disabled="config.initiativeTypeForced" >
-
+
@@ -59,21 +67,48 @@
- - {{md.resourceTitle}} - - +
+ + +
+ {{md.resourceTitle}} +
+ +
+
+
-
+ +
+ +
+