list = body.getChildren();
- if (list.size() == 0)
+ if (list.isEmpty())
throw new BadSoapResponseEx(envelope);
return list.get(0);
diff --git a/common/src/main/java/org/fao/geonet/utils/GeonetHttpRequestFactory.java b/common/src/main/java/org/fao/geonet/utils/GeonetHttpRequestFactory.java
index 7ba666fd2a..bd548561c0 100644
--- a/common/src/main/java/org/fao/geonet/utils/GeonetHttpRequestFactory.java
+++ b/common/src/main/java/org/fao/geonet/utils/GeonetHttpRequestFactory.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)
*
@@ -200,6 +200,7 @@ public HttpClientBuilder getDefaultHttpClientBuilder() {
final HttpClientBuilder builder = HttpClientBuilder.create();
builder.setRedirectStrategy(new LaxRedirectStrategy());
builder.disableContentCompression();
+ builder.useSystemProperties();
synchronized (this) {
if (connectionManager == null) {
@@ -249,40 +250,40 @@ public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
private static class AdaptingResponse extends AbstractClientHttpResponse {
- private final CloseableHttpResponse _response;
- private final CloseableHttpClient _client;
+ private final CloseableHttpResponse response;
+ private final CloseableHttpClient client;
public AdaptingResponse(CloseableHttpClient client, CloseableHttpResponse response) {
- this._response = response;
- this._client = client;
+ this.response = response;
+ this.client = client;
}
@Override
public int getRawStatusCode() throws IOException {
- return _response.getStatusLine().getStatusCode();
+ return response.getStatusLine().getStatusCode();
}
@Override
public String getStatusText() throws IOException {
- return _response.getStatusLine().getReasonPhrase();
+ return response.getStatusLine().getReasonPhrase();
}
@Override
public void close() {
- IOUtils.closeQuietly(_response);
- IOUtils.closeQuietly(_client);
+ IOUtils.closeQuietly(response);
+ IOUtils.closeQuietly(client);
}
@Override
public InputStream getBody() throws IOException {
- return _response.getEntity().getContent();
+ return response.getEntity().getContent();
}
@Override
public HttpHeaders getHeaders() {
final HttpHeaders httpHeaders = new HttpHeaders();
- final Header[] headers = _response.getAllHeaders();
+ final Header[] headers = response.getAllHeaders();
for (Header header : headers) {
final HeaderElement[] elements = header.getElements();
diff --git a/common/src/main/java/org/fao/geonet/utils/IO.java b/common/src/main/java/org/fao/geonet/utils/IO.java
index f3eaeeb034..bfdcb57dd2 100644
--- a/common/src/main/java/org/fao/geonet/utils/IO.java
+++ b/common/src/main/java/org/fao/geonet/utils/IO.java
@@ -1,5 +1,5 @@
//=============================================================================
-//=== Copyright (C) 2001-2005 Food and Agriculture Organization of the
+//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
@@ -30,6 +30,8 @@
import org.fao.geonet.utils.debug.DebuggingInputStream;
import org.fao.geonet.utils.debug.DebuggingReader;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -37,31 +39,15 @@
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
-import java.nio.file.DirectoryStream;
-import java.nio.file.FileSystem;
-import java.nio.file.FileSystemNotFoundException;
-import java.nio.file.FileSystems;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.StandardCopyOption;
+import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.sql.ResultSet;
import java.sql.Statement;
-import java.util.Comparator;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.Map;
import java.util.Objects;
-import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
//=============================================================================
/**
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 5521672ab0..094dfb4942 100644
--- a/common/src/main/java/org/fao/geonet/utils/Log.java
+++ b/common/src/main/java/org/fao/geonet/utils/Log.java
@@ -105,6 +105,16 @@ public final class Log {
public static final String TRANSFORMER_FACTORY = JEEVES
+ ".transformerFactory";
+ /**
+ * This is the name of the RollingFileAppender in your log4j2.xml configuration file.
+ *
+ * LogConfig uses this name to lookup RollingFileAppender to check configuration in
+ * case a custom log file location has been used.
+ */
+ private static final String FILE_APPENDER_NAME = "File";
+
+ public static final String GEONETWORK_MODULE = "geonetwork";
+
/**
* Default constructor. Builds a Log.
*/
@@ -331,4 +341,62 @@ public static File toLogFile(Appender appender) {
return null;
}
+ public static File getLogfile() {
+ // Appender is supplied by LogUtils based on parsing log4j2.xml file indicated
+ // by database settings
+
+ // First, try the fileappender from the logger named "geonetwork"
+ org.apache.log4j.Appender appender = org.apache.log4j.Logger.getLogger(GEONETWORK_MODULE).getAppender(FILE_APPENDER_NAME);
+ // If still not found, try the one from the logger named "jeeves"
+ if (appender == null) {
+ appender = org.apache.log4j.Logger.getLogger(Log.JEEVES).getAppender(FILE_APPENDER_NAME);
+ }
+ if (appender != null) {
+ if (appender instanceof AppenderWrapper) {
+ AppenderWrapper wrapper = (AppenderWrapper) appender;
+ org.apache.logging.log4j.core.Appender appender2 = wrapper.getAppender();
+
+ if (appender2 instanceof FileAppender) {
+ FileAppender fileAppender = (FileAppender) appender2;
+ String logFileName = fileAppender.getFileName();
+ if (logFileName != null) {
+ File logFile = new File(logFileName);
+ if (logFile.exists()) {
+ return logFile;
+ }
+ }
+ }
+ if (appender2 instanceof RollingFileAppender) {
+ RollingFileAppender fileAppender = (RollingFileAppender) appender2;
+ String logFileName = fileAppender.getFileName();
+ if (logFileName != null) {
+ File logFile = new File(logFileName);
+ if (logFile.exists()) {
+ return logFile;
+ }
+ }
+ }
+ }
+ }
+ Log.warning(GEONETWORK_MODULE, "Error when getting logger file for the " + "appender named '" + FILE_APPENDER_NAME + "'. "
+ + "Check your log configuration file. "
+ + "A FileAppender or RollingFileAppender is required to return last activity to the user interface."
+ + "Appender file not found.");
+
+ if (System.getProperties().containsKey("log_dir")) {
+ File logDir = new File(System.getProperty("log_dir"));
+ if (logDir.exists() && logDir.isDirectory()) {
+ File logFile = new File(logDir, "logs/geonetwork.log");
+ if (logFile.exists()) {
+ return logFile;
+ }
+ }
+ } else {
+ File logFile = new File("logs/geonetwork.log");
+ if (logFile.exists()) {
+ return logFile;
+ }
+ }
+ return null; // unavailable
+ }
}
diff --git a/common/src/main/java/org/fao/geonet/utils/Xml.java b/common/src/main/java/org/fao/geonet/utils/Xml.java
index 59636fc9c9..c796fbfe4e 100644
--- a/common/src/main/java/org/fao/geonet/utils/Xml.java
+++ b/common/src/main/java/org/fao/geonet/utils/Xml.java
@@ -31,14 +31,9 @@
import net.sf.saxon.Configuration;
import net.sf.saxon.Controller;
import net.sf.saxon.FeatureKeys;
-import org.apache.commons.io.IOUtils;
import org.apache.fop.apps.Fop;
import org.apache.fop.apps.FopFactory;
import org.apache.fop.apps.MimeConstants;
-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.xml.resolver.tools.CatalogResolver;
import org.fao.geonet.exceptions.XSDValidationErrorEx;
import org.fao.geonet.utils.nio.NioPathAwareEntityResolver;
@@ -100,7 +95,6 @@
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
-import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystemNotFoundException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
@@ -135,6 +129,7 @@ public final class Xml {
+ "\uE000-\uFFFD"
+ "\ud800\udc00-\udbff\udfff"
+ "]";
+ public static final String XML_VERSION_HEADER = "<\\?xml version=['\"]1.0['\"] encoding=['\"].*['\"]\\?>\\s*";
public static SAXBuilder getSAXBuilder(boolean validate) {
SAXBuilder builder = getSAXBuilderWithPathXMLResolver(validate, null);
@@ -398,6 +393,9 @@ public static Object unmarshall(Element xml, Class clazz) throws Exception {
public static Element transform(Element xml, Path styleSheetPath, Map params) throws Exception {
JDOMResult resXml = new JDOMResult();
transform(xml, styleSheetPath, resXml, params);
+ if (resXml.getDocument() == null) {
+ throw new NullPointerException("Failed to create a Document for " + resXml.getResult());
+ }
return (Element) resXml.getDocument().getRootElement().detach();
}
//--------------------------------------------------------------------------
@@ -1118,6 +1116,10 @@ private static void doCreateAttributesXpathExpr(StringBuilder builder, List"));
+ assertEquals(true,
+ Xml.isXMLLike(" "));
+ assertEquals(true,
+ Xml.isXMLLike("\n "));
+ assertEquals(true,
+ Xml.isRDFLike("\n"));
+ assertEquals(true,
+ Xml.isRDFLike("\n"));
+ }
}
diff --git a/core/README.md b/core/README.md
index f9d23d22e6..cc99498e81 100644
--- a/core/README.md
+++ b/core/README.md
@@ -1,9 +1,9 @@
-The core module contains the core Geonetwork classes. For example SearchManager for searching the metadata index, DataManager for saving
+The core module contains the core Geonetwork classes. For example, EsSearchManager for searching the metadata index, DataManager for saving
and loading Metadata.
-Services, Harvesters, etc... that are are plugins will usually depend on core and will make use of these core classes to implement their
+Services, Harvesters, etc... that are plugins will usually depend on core and will make use of these core classes to implement their
services.
-Geonetwork is wired together via Spring-Dependency-Injection. The critical classes (DataManager, SchemaManager, SearchManager, etc...) are
+Geonetwork is wired together via Spring-Dependency-Injection. The critical classes (DataManager, SchemaManager, EsSearchManager, etc...) are
all singleton beans in the application context and can be either injected into other beans or obtained via the ServiceContext.getBean
-method (or ServiceContext.getApplicationContext()).
\ No newline at end of file
+method (or ServiceContext.getApplicationContext()).
diff --git a/core/pom.xml b/core/pom.xml
index 0ba137fc72..c961a0fb62 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -27,13 +27,13 @@
geonetwork
org.geonetwork-opensource
- 4.2.2-georchestra
+ 4.2.7-georchestra
4.0.0
gn-core
GeoNetwork core
- 4.2.2-georchestra
+ 4.2.7-georchestra
@@ -386,26 +386,20 @@
${project.groupId}
gn-domain
- ${project.parent.version}
+ ${project.version}
tests
test
${project.groupId}
gn-events
- ${project.parent.version}
+ ${project.version}
org.apache.jclouds
jclouds-all
- 2.2.1
-
-
- javax.annotation
- jsr250-api
-
-
+ 2.3.0
@@ -501,27 +495,27 @@
${project.groupId}
gn-oaipmh
- ${project.parent.version}
+ ${project.version}
${project.groupId}
gn-domain
- ${project.parent.version}
+ ${project.version}
${project.groupId}
gn-cachingxslt
- ${project.parent.version}
+ ${project.version}
org.geonetwork-opensource.schemas
gn-schema-iso19139
- ${project.parent.version}
+ ${project.version}
${project.groupId}
gn-dummy-api
- ${project.parent.version}
+ ${project.version}
@@ -593,7 +587,7 @@
${project.groupId}
gn-index
- ${project.parent.version}
+ ${project.version}
org.springframework
@@ -607,7 +601,7 @@
com.amazonaws
aws-java-sdk-s3
- 1.11.618
+ 1.12.261
org.jasypt
diff --git a/core/src/main/java/jeeves/config/springutil/JeevesNodeAwareLogoutSuccessHandler.java b/core/src/main/java/jeeves/config/springutil/JeevesNodeAwareLogoutSuccessHandler.java
index e1c0aec969..8442c90959 100644
--- a/core/src/main/java/jeeves/config/springutil/JeevesNodeAwareLogoutSuccessHandler.java
+++ b/core/src/main/java/jeeves/config/springutil/JeevesNodeAwareLogoutSuccessHandler.java
@@ -26,11 +26,15 @@
import org.fao.geonet.NodeInfo;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
+import org.fao.geonet.constants.Geonet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AbstractAuthenticationTargetUrlRequestHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
+import org.apache.commons.lang3.StringUtils;
+import org.fao.geonet.kernel.setting.SettingInfo;
+
import java.io.IOException;
import java.net.MalformedURLException;
@@ -84,8 +88,10 @@ protected String determineTargetUrl(HttpServletRequest request, HttpServletRespo
String siteHost = settingManager.getValue(Settings.SYSTEM_SERVER_HOST);
String siteProtocol = settingManager.getValue(Settings.SYSTEM_SERVER_PROTOCOL);
- int sitePort = settingManager.getValueAsInt(Settings.SYSTEM_SERVER_PORT);
-
+
+ // some conditional logic to handle the case where there's no port in the settings
+ SettingInfo si = new SettingInfo();
+ int sitePort = si.getSitePort();
if (!hostName.equalsIgnoreCase(siteHost) ||
!protocol.equalsIgnoreCase(siteProtocol) ||
diff --git a/core/src/main/java/jeeves/server/dispatchers/ServiceManager.java b/core/src/main/java/jeeves/server/dispatchers/ServiceManager.java
index b742d7f00d..c99f6b92b4 100644
--- a/core/src/main/java/jeeves/server/dispatchers/ServiceManager.java
+++ b/core/src/main/java/jeeves/server/dispatchers/ServiceManager.java
@@ -356,17 +356,26 @@ public ServiceContext createServiceContext(String name, ConfigurableApplicationC
return context;
}
+ /**
+ * If we do have the optional x-forwarded-for request header then
+ * use whatever is in it to record ip address of client
+ */
+ public static String getRequestIpAddress(HttpServletRequest request) {
+ String xForwardedForHeader = request.getHeader("x-forwarded-for");
+ return StringUtils.isNotEmpty(xForwardedForHeader)
+ ? xForwardedForHeader : request.getRemoteAddr();
+ }
+
+
public ServiceContext createServiceContext(String name, String lang, HttpServletRequest request) {
ServiceContext context = new ServiceContext(name, ApplicationContextHolder.get(), htContexts, entityManager);
context.setBaseUrl(baseUrl);
context.setLanguage(lang);
- context.setIpAddress(request.getRemoteAddr());
+ context.setIpAddress(getRequestIpAddress(request));
context.setMaxUploadSize(maxUploadSize);
context.setServlet(servlet);
- String ip = request.getRemoteAddr();
-
// Session is created by ApiInterceptor when needed
// Save the session here in the ServiceContext (not used in the API package).
final HttpSession httpSession = request.getSession(false);
diff --git a/core/src/main/java/jeeves/server/sources/ServiceRequestFactory.java b/core/src/main/java/jeeves/server/sources/ServiceRequestFactory.java
index 8310e69764..5e89a7d534 100644
--- a/core/src/main/java/jeeves/server/sources/ServiceRequestFactory.java
+++ b/core/src/main/java/jeeves/server/sources/ServiceRequestFactory.java
@@ -24,6 +24,7 @@
package jeeves.server.sources;
import jeeves.constants.Jeeves;
+import jeeves.server.dispatchers.ServiceManager;
import jeeves.server.sources.ServiceRequest.InputMethod;
import jeeves.server.sources.ServiceRequest.OutputMethod;
import jeeves.server.sources.http.HttpServiceRequest;
@@ -102,9 +103,7 @@ public static ServiceRequest create(HttpServletRequest req,
srvReq.setLanguage(lang);
srvReq.setService(service);
srvReq.setJSONOutput(extractJSONFlag(req.getQueryString()));
- String ip = req.getRemoteAddr();
- String forwardedFor = req.getHeader("x-forwarded-for");
- if (forwardedFor != null) ip = forwardedFor;
+ String ip = ServiceManager.getRequestIpAddress(req);
srvReq.setAddress(ip);
srvReq.setOutputStream(res.getOutputStream());
diff --git a/core/src/main/java/jeeves/server/sources/http/JeevesServlet.java b/core/src/main/java/jeeves/server/sources/http/JeevesServlet.java
index 154a268212..17ef9e745b 100644
--- a/core/src/main/java/jeeves/server/sources/http/JeevesServlet.java
+++ b/core/src/main/java/jeeves/server/sources/http/JeevesServlet.java
@@ -29,6 +29,7 @@
import jeeves.constants.Jeeves;
import jeeves.server.JeevesEngine;
import jeeves.server.UserSession;
+import jeeves.server.dispatchers.ServiceManager;
import jeeves.server.sources.ServiceRequest;
import jeeves.server.sources.ServiceRequestFactory;
@@ -113,13 +114,7 @@ public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOExc
//---------------------------------------------------------------------------
private void execute(HttpServletRequest req, HttpServletResponse res) throws IOException {
- String ip = req.getRemoteAddr();
- // if we do have the optional x-forwarded-for request header then
- // use whatever is in it to record ip address of client
- String forwardedFor = req.getHeader("x-forwarded-for");
- if (forwardedFor != null) {
- ip = forwardedFor;
- }
+ String ip = ServiceManager.getRequestIpAddress(req);
Log.info(Log.REQUEST, "==========================================================");
Log.info(Log.REQUEST, "HTML Request (from " + ip + ") : " + req.getRequestURI());
diff --git a/core/src/main/java/jeeves/services/RegExpReplace.java b/core/src/main/java/jeeves/services/RegExpReplace.java
index 9a6dc18149..a172f82c4e 100644
--- a/core/src/main/java/jeeves/services/RegExpReplace.java
+++ b/core/src/main/java/jeeves/services/RegExpReplace.java
@@ -42,7 +42,7 @@
//=============================================================================
/**
- * This service reads a configuration xml file containint containing pattern-replacement pairs and
+ * 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:
*/
diff --git a/core/src/main/java/org/fao/geonet/MetadataResourceDatabaseMigration.java b/core/src/main/java/org/fao/geonet/MetadataResourceDatabaseMigration.java
index 7cf4be53b1..ff9e62cc6b 100644
--- a/core/src/main/java/org/fao/geonet/MetadataResourceDatabaseMigration.java
+++ b/core/src/main/java/org/fao/geonet/MetadataResourceDatabaseMigration.java
@@ -78,7 +78,7 @@ public class MetadataResourceDatabaseMigration extends DatabaseMigrationTask {
"gmd:fileDescription/gco:CharacterString = 'large_thumbnail']/gmd:fileName/" +
"gco:CharacterString[not(starts-with(normalize-space(text()), 'http'))]";
private static final String XPATH_THUMBNAIL_WITH_URL =
- "*//gmd:graphicOverview/gmd:MD_BrowseGraphic[gmd:fileDescription/gco:CharacterString]/gmd:fileName/gco:CharacterString[starts-with(normalize-space(text()), 'http')]";
+ "*//gmd:graphicOverview/gmd:MD_BrowseGraphic/gmd:fileName/gco:CharacterString[starts-with(normalize-space(text()), 'http')]";
private static final String XPATH_ATTACHMENTS_WITH_URL =
"*//gmd:CI_OnlineResource/gmd:linkage/gmd:URL";
diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java
index 3aea91d393..87f74ab998 100644
--- a/core/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java
+++ b/core/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java
@@ -65,6 +65,8 @@ public class CMISStore extends AbstractStore {
private Path baseMetadataDir = null;
+ private static final String CMIS_PROPERTY_PREFIX = "cmis:";
+
@Autowired
CMISConfiguration cmisConfiguration;
@@ -214,75 +216,64 @@ protected MetadataResource putResource(final ServiceContext context, final Strin
// Reset Filter from the default operationalContext to include all fields because we may need secondary properties.
oc.setFilter(null);
- CmisObject cmisObject;
+ Map properties = new HashMap();
+ Document doc;
try {
- cmisObject = cmisConfiguration.getClient().getObjectByPath(key, oc);
- } catch (Exception e) {
- cmisObject = null;
- }
+ doc = (Document) cmisConfiguration.getClient().getObjectByPath(key, oc);
- Map properties = new HashMap();
- if (!StringUtils.isEmpty(cmisConfiguration.getCmisMetadataUUIDPropertyName()) && !cmisConfiguration.existMetadataUUIDSecondaryProperty()) {
- setCmisMetadataUUIDPrimary(properties, metadataUuid);
+ // Update existing document
+ setCmisProperties(metadataUuid, properties, doc, additionalProperties);
+ doc = cmisUtils.saveDocument(key, doc, properties, is, changeDate);
+ } catch (CmisObjectNotFoundException e) {
+ // add new document
+ setCmisProperties(metadataUuid, properties, null, additionalProperties);
+ doc = cmisUtils.saveDocument(key, null, properties, is, changeDate);
}
- // If cmisObject is empty then it is a new record. With new records, we need to set the default value for the status.
- MetadataResourceExternalManagementProperties.ValidationStatus defaultStatus = null;
- // We only need to set the default if there is a status property supplied.
- if (!StringUtils.isEmpty(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName())) {
- if (cmisConfiguration.getExternalResourceManagementValidationStatusDefaultValue() != null) {
- // If a default property name does exist then use it
- defaultStatus = MetadataResourceExternalManagementProperties.ValidationStatus.valueOf(cmisConfiguration.getExternalResourceManagementValidationStatusDefaultValue());
- } else {
- // Otherwise lets default to incomplete.
- // Reason - as the administrator decided to use the status, it most likely means that there are extra properties that need to be set after a file is uploaded so defaulting it to
- // incomplete seems reasonable.
- defaultStatus = MetadataResourceExternalManagementProperties.ValidationStatus.INCOMPLETE;
- }
- if (cmisObject==null &&
- !cmisConfiguration.existExternalResourceManagementValidationStatusSecondaryProperty() &&
- !properties.containsKey(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName())
- ) {
- setCmisExternalManagementResourceStatusPrimary(properties, defaultStatus);
- }
+ return createResourceDescription(context, metadataUuid, visibility, filename,
+ doc, metadataId, approved);
+ }
+
+ protected void setCmisProperties(String metadataUuid, Map properties, Document doc, Map additionalProperties) {
+
+ // Add additional properties if exists.
+ if (MapUtils.isNotEmpty(additionalProperties)) {
+ properties.putAll(additionalProperties);
}
- Document doc = cmisUtils.saveDocument(key, cmisObject, properties, is, changeDate);
+ // now update metadata uuid and status within primary cmis fields if needed.
- // The optional metadata UUID is assigned to a user-defined property in the content management system.
- // In some content management systems, custom properties appear as CMIS Secondary properties.
- // CMIS Secondary properties cannot be set in the same call that creates the document and sets it's properties,
- // so this is done in the following call after the document is created
+ // Don't allow users metadata uuid to be supplied as a property so let's overwrite any value that may exist.
+ if (!StringUtils.isEmpty(cmisConfiguration.getCmisMetadataUUIDPropertyName())) {
+ setCmisMetadataUUIDPrimary(properties, metadataUuid);
+ }
+ // If document is empty it is a new record so set the default status value property if it does not already exist as an additional property.
+ if (doc == null &&
+ !StringUtils.isEmpty(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName()) &&
+ !properties.containsKey(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName())) {
+ setCmisExternalManagementResourceStatusPrimary(properties, cmisConfiguration.getValidationStatusDefaultValue());
+ }
+
+ // If we have secondary properties then lets apply those changes as well.
if (cmisConfiguration.existSecondaryProperty()) {
- Map secondaryProperties = new HashMap<>();
- if (MapUtils.isNotEmpty(additionalProperties)) {
- secondaryProperties.putAll(additionalProperties);
+ Property secondaryProperties = null;
+ if (doc != null) {
+ secondaryProperties = doc.getProperty(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
}
+
+ // Don't allow users metadata uuid to be supplied as a property so let's overwrite any value that may exist.
if (cmisConfiguration.existMetadataUUIDSecondaryProperty()) {
- setCmisMetadataUUIDSecondary(doc, secondaryProperties, metadataUuid);
+ setCmisMetadataUUIDSecondary(secondaryProperties, properties, metadataUuid);
}
-
- // If cmisObject is empty and the property does not already exist as an additional secondary property then it is a new record.
- // With new records, we need to set the default value for the status.
- if (cmisObject==null &&
+ // If document is empty it is a new record so set the default status value property if it does not already exist as an additional secondary property.
+ if (doc == null &&
cmisConfiguration.existExternalResourceManagementValidationStatusSecondaryProperty() &&
- !secondaryProperties.containsKey(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName().split(CMISConfiguration.CMIS_SECONDARY_PROPERTY_SEPARATOR)[1])) {
- setCmisExternalManagementResourceStatusSecondary(doc, secondaryProperties, defaultStatus);
- }
-
- try {
- doc.updateProperties(secondaryProperties);
- } catch (Exception e) {
- Log.error(Geonet.RESOURCES,
- String.format("Unable to update CMIS secondary property on metadata resource '%s' for metadata '%s'.", key, metadataUuid), e);
- throw e;
+ !properties.containsKey(cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName().split(CMISConfiguration.CMIS_SECONDARY_PROPERTY_SEPARATOR)[1])) {
+ setCmisExternalManagementResourceStatusSecondary(secondaryProperties, properties, cmisConfiguration.getValidationStatusDefaultValue());
}
}
- return createResourceDescription(context, metadataUuid, visibility, filename,
- doc, metadataId, approved);
}
-
protected void setCmisMetadataUUIDPrimary(Map properties, String metadataUuid) {
setCmisPrimaryProperty(properties, cmisConfiguration.getCmisMetadataUUIDPropertyName(), metadataUuid);
}
@@ -298,22 +289,21 @@ protected void setCmisPrimaryProperty(Map properties, String pro
}
}
- protected void setCmisExternalManagementResourceStatusSecondary(Document doc, Map properties, MetadataResourceExternalManagementProperties.ValidationStatus status) {
- setCmisSecondaryProperty(doc, properties, cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName(), status.getValue());
+ protected void setCmisExternalManagementResourceStatusSecondary(Property secondaryProperty, Map properties, MetadataResourceExternalManagementProperties.ValidationStatus status) {
+ setCmisSecondaryProperty(secondaryProperty, properties, cmisConfiguration.getExternalResourceManagementValidationStatusPropertyName(), status.getValue());
}
- protected void setCmisMetadataUUIDSecondary(Document doc, Map properties, String metadataUuid) {
- setCmisSecondaryProperty(doc, properties, cmisConfiguration.getCmisMetadataUUIDPropertyName(), metadataUuid);
+ protected void setCmisMetadataUUIDSecondary(Property secondaryProperty, Map properties, String metadataUuid) {
+ setCmisSecondaryProperty(secondaryProperty, properties, cmisConfiguration.getCmisMetadataUUIDPropertyName(), metadataUuid);
}
- protected void setCmisSecondaryProperty(Document doc, Map properties, String propertyName, Object value) {
+ protected void setCmisSecondaryProperty(Property secondaryProperty, Map properties, String propertyName, Object value) {
if (!StringUtils.isEmpty(propertyName) &&
propertyName.contains(cmisConfiguration.getSecondaryPropertySeparator())) {
String[] splitPropertyNames = propertyName.split(Pattern.quote(cmisConfiguration.getSecondaryPropertySeparator()));
String aspectName = splitPropertyNames[0];
String secondaryPropertyName = splitPropertyNames[1];
List aspects = null;
- Property secondaryProperty = doc.getProperty(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
if (secondaryProperty != null) {
// It may return an unmodifiable list and we need to potentially modify the list so lets make a copy of the list.
aspects = new ArrayList<>(secondaryProperty.getValues());
@@ -501,21 +491,19 @@ public MetadataResourceContainer getResourceContainerDescription(final ServiceCo
final String key = getMetadataDir(context, metadataId);
- try {
- String folderRoot = cmisConfiguration.getExternalResourceManagementFolderRoot();
- if (folderRoot == null) {
- folderRoot = "";
- }
- Folder parentFolder = cmisUtils.getFolderCache(key + folderRoot);
- MetadataResourceExternalManagementProperties metadataResourceExternalManagementProperties =
- getMetadataResourceExternalManagementProperties(context, metadataId, metadataUuid, null, String.valueOf(metadataId), null, null, parentFolder.getId(), parentFolder.getType(), MetadataResourceExternalManagementProperties.ValidationStatus.UNKNOWN);
- return new FilesystemStoreResourceContainer(metadataUuid, metadataId, metadataUuid,
- settingManager.getNodeURL() + "api/records/", metadataResourceExternalManagementProperties, approved);
-
- } catch (CmisObjectNotFoundException e) {
- return null;
+ String folderRoot = cmisConfiguration.getExternalResourceManagementFolderRoot();
+ if (folderRoot == null) {
+ folderRoot = "";
}
+ Folder parentFolder = cmisUtils.getFolderCache(key + folderRoot, false, true);
+ MetadataResourceExternalManagementProperties metadataResourceExternalManagementProperties =
+ getMetadataResourceExternalManagementProperties(context, metadataId, metadataUuid, null, String.valueOf(metadataId), null, null, parentFolder.getId(), parentFolder.getType(), MetadataResourceExternalManagementProperties.ValidationStatus.UNKNOWN);
+
+ return new FilesystemStoreResourceContainer(metadataUuid, metadataId, metadataUuid,
+ settingManager.getNodeURL() + "api/records/", metadataResourceExternalManagementProperties, approved);
+
+
}
@Override
@@ -536,28 +524,33 @@ public void copyResources(ServiceContext context, String sourceUuid, String targ
Document sourceDocument = sourceEntry.getValue();
// Get cmis properties from the source document
- Map sourceProperties = getSecondaryProperties(sourceDocument);
+ Map sourceProperties = getProperties(sourceDocument);
putResource(context, targetUuid, sourceDocument.getName(), sourceDocument.getContentStream().getStream(), null, metadataResourceVisibility, targetApproved, sourceProperties);
}
- } catch (CmisObjectNotFoundException e) {
- Log.warning("Cannot find folder object from CMIS ... Abort copping resources from "+sourceResourceTypeDir, e);
+ } catch (CmisObjectNotFoundException | ResourceNotFoundException e) {
+ Log.warning(Geonet.RESOURCES, "Cannot find folder object from CMIS ... Abort copping resources from " + sourceResourceTypeDir);
}
}
- protected Map getSecondaryProperties(Document document) {
- String aspectId=null;
+ protected Map getProperties(Document document) {
+ Map properties = new HashMap<>();
+
+ // Get secondary properties aspect if it exists.
+ String aspectId = null;
Property aspectProperty = document.getProperty(PropertyIds.SECONDARY_OBJECT_TYPE_IDS);
- if (aspectProperty != null) {
+ if (aspectProperty != null && !StringUtils.isEmpty(aspectProperty.getValueAsString())) {
aspectId = aspectProperty.getValueAsString();
}
- Map properties = new HashMap<>();
- if (!StringUtils.isEmpty(aspectId)) {
- for (Property> property:document.getProperties()) {
- if (property.getId().startsWith(aspectId) && property.getValue()!=null) {
- properties.put(property.getId(), property.getValue());
- }
+ for (Property> property : document.getProperties()) {
+ // Add secondary properties if exists.
+ if (aspectId != null && property.getId().startsWith(aspectId) && property.getValue() != null) {
+ properties.put(property.getId(), property.getValue());
+ }
+ // Add other common cmis properties.
+ if (property.getId().startsWith(CMIS_PROPERTY_PREFIX) && property.getValue() != null) {
+ properties.put(property.getId(), property.getValue());
}
}
diff --git a/core/src/main/java/org/fao/geonet/api/tools/i18n/TranslationPackBuilder.java b/core/src/main/java/org/fao/geonet/api/tools/i18n/TranslationPackBuilder.java
index 3d301ab938..f290068098 100644
--- a/core/src/main/java/org/fao/geonet/api/tools/i18n/TranslationPackBuilder.java
+++ b/core/src/main/java/org/fao/geonet/api/tools/i18n/TranslationPackBuilder.java
@@ -201,72 +201,59 @@ public Map getDbTranslation(String language, List type)
if (type == null || type.contains("StatusValue")) {
List valueList = statusValueRepository.findAll();
- Iterator valueIterator = valueList.iterator();
- while (valueIterator.hasNext()) {
- StatusValue entity = valueIterator.next();
- translations.put("status-" + entity.getId() + "",
- getLabelOrKey(entity, language, entity.getId() + ""));
+ for (StatusValue entity : valueList) {
+ String label = getLabelOrKey(entity, language, entity.getId() + "");
+ translations.put("status-" + entity.getId(), label);
+ translations.put("status-" + entity.getName(), label);
}
}
if (type == null || type.contains("MetadataCategory")) {
List metadataCategoryList = categoryRepository.findAll();
- Iterator metadataCategoryIterator = metadataCategoryList.iterator();
- while (metadataCategoryIterator.hasNext()) {
- MetadataCategory entity = metadataCategoryIterator.next();
- translations.put("cat-" + entity.getName() + "",
+ for (MetadataCategory entity : metadataCategoryList) {
+ translations.put("cat-" + entity.getName(),
getLabelOrKey(entity, language, entity.getName()));
}
}
if (type == null || type.contains("Group")) {
List groupList = groupRepository.findAll();
- Iterator groupIterator = groupList.iterator();
- while (groupIterator.hasNext()) {
- Group entity = groupIterator.next();
- translations.put("group-" + entity.getId() + "",
+ for (Group entity : groupList) {
+ translations.put("group-" + entity.getId(),
getLabelOrKey(entity, language, entity.getName()));
}
}
if (type == null || type.contains("Operation")) {
List operationList = operationRepository.findAll();
- Iterator operationIterator = operationList.iterator();
- while (operationIterator.hasNext()) {
- Operation entity = operationIterator.next();
- translations.put("op-" + entity.getId() + "",
- getLabelOrKey(entity, language, entity.getId() + ""));
- translations.put("op-" + entity.getName() + "",
+ for (Operation entity : operationList) {
+ translations.put("op-" + entity.getId(),
+ getLabelOrKey(entity, language, String.valueOf(entity.getId())));
+ translations.put("op-" + entity.getName(),
getLabelOrKey(entity, language, entity.getName()));
}
}
if (type == null || type.contains("Source")) {
List sourceList = sourceRepository.findAll();
- Iterator sourceIterator = sourceList.iterator();
- while (sourceIterator.hasNext()) {
- Source entity = sourceIterator.next();
- translations.put("source-" + entity.getUuid() + "",
+ for (Source entity : sourceList) {
+ translations.put("source-" + entity.getUuid(),
getLabelOrKey(entity, language, entity.getUuid()));
}
}
if (type == null || type.contains("Schematron")) {
List schematronList = schematronRepository.findAll();
- Iterator schematronIterator = schematronList.iterator();
- while (schematronIterator.hasNext()) {
- Schematron entity = schematronIterator.next();
- translations.put("sch-" + entity.getRuleName() + "",
+ for (Schematron entity : schematronList) {
+ translations.put("sch-" + entity.getRuleName(),
getLabelOrKey(entity, language, entity.getRuleName()));
}
}
if (type == null || type.contains("IsoLanguage")) {
List isoLanguageList = isoLanguageRepository.findAll();
- Iterator isoLanguageIterator = isoLanguageList.iterator();
- while (isoLanguageIterator.hasNext()) {
- IsoLanguage entity = isoLanguageIterator.next();
- translations.put("lang-" + entity.getCode() + "",
+ for (IsoLanguage entity : isoLanguageList) {
+ translations.put("lang-" + entity.getCode(),
getLabelOrKey(entity, language, entity.getCode()));
}
}
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 c24900cffb..ac51983fc4 100644
--- a/core/src/main/java/org/fao/geonet/constants/Geonet.java
+++ b/core/src/main/java/org/fao/geonet/constants/Geonet.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)
//===
@@ -183,6 +183,7 @@ public static final class Path {
public static final String XSLT_FOLDER = "xslt";
public static final String CONV_STYLESHEETS = STYLESHEETS + "/conversion";
public static final String IMPORT_STYLESHEETS = CONV_STYLESHEETS + "/import";
+ public static final String IMPORT_STYLESHEETS_SCHEMA_PREFIX = "schema:";
public static final String WFS_STYLESHEETS = "convert/WFSToFragments";
public static final String TDS_STYLESHEETS = "convert/ThreddsToFragments";
public static final String TDS_19119_19139_STYLESHEETS = "convert/ThreddsCatalogto19119";
@@ -659,6 +660,8 @@ public static class IndexFieldNames {
public static final String ANY = "any";
public static final String LOCALE = "locale";
public static final String IS_PUBLISHED_TO_ALL = "isPublishedToAll";
+ public static final String IS_PUBLISHED_TO_INTRANET = "isPublishedToIntranet";
+ public static final String IS_PUBLISHED_TO_GUEST = "isPublishedToGuest";
public static final String FEEDBACKCOUNT = "feedbackCount";
public static final String DRAFT = "draft";
public static final String DRAFT_ID = "draftId";
@@ -680,6 +683,7 @@ public static class RecordLink {
public static final String INSPIRE_VALIDATION_DATE = "_inspireValidationDate";
public static final String STATUS_WORKFLOW = "statusWorkflow";
public static final String USER_SAVED_COUNT = "userSavedCount";
+ public static final String INDEXING_DATE = "indexingDate";
}
public static class SearchConfig {
diff --git a/core/src/main/java/org/fao/geonet/kernel/AbstractSchematronValidator.java b/core/src/main/java/org/fao/geonet/kernel/AbstractSchematronValidator.java
index 324895f4f4..d8ad4b2df5 100644
--- a/core/src/main/java/org/fao/geonet/kernel/AbstractSchematronValidator.java
+++ b/core/src/main/java/org/fao/geonet/kernel/AbstractSchematronValidator.java
@@ -47,7 +47,7 @@
public class AbstractSchematronValidator {
protected void runSchematron(String lang, Path schemaDir, List validations, Element schemaTronXmlOut,
- int metadataId, Element md, ApplicableSchematron applicable) {
+ int metadataId, Element md, ApplicableSchematron applicable) {
final ConfigurableApplicationContext applicationContext = ApplicationContextHolder.get();
ThesaurusManager thesaurusManager = applicationContext.getBean(ThesaurusManager.class);
@@ -61,6 +61,9 @@ protected void runSchematron(String lang, Path schemaDir, List params = new HashMap();
params.put("lang", lang);
@@ -74,28 +77,33 @@ protected void runSchematron(String lang, Path schemaDir, List i = xmlReport.getDescendants(new ElementFilter("fired-rule", Geonet.Namespaces.SVRL));
- int firedRules = Iterators.size(i);
+ firedRules = Iterators.size(i);
i = xmlReport.getDescendants(new ElementFilter("failed-assert", Geonet.Namespaces.SVRL));
- int invalidRules = Iterators.size(i);
+ invalidRules = Iterators.size(i);
- if (validations != null) {
- validations.add(new MetadataValidation().
- setId(new MetadataValidationId(metadataId, ruleId)).
- setStatus(invalidRules != 0 ? MetadataValidationStatus.INVALID : MetadataValidationStatus.VALID).
- setRequired(requirement == SchematronRequirement.REQUIRED).
- setNumTests(firedRules).
- setNumFailures(invalidRules));
-
- }
+ metadataValidationStatus = invalidRules != 0 ? MetadataValidationStatus.INVALID : MetadataValidationStatus.VALID;
}
} catch (Exception e) {
Log.error(Geonet.DATA_MANAGER, "WARNING: schematron xslt " + ruleId + " failed", e);
// If an error occurs that prevents to verify schematron rules, add to show in report
Element errorReport = new Element("schematronVerificationError", Edit.NAMESPACE);
- errorReport.addContent("Schematron error ocurred, rules could not be verified: " + e.getMessage());
+ errorReport.addContent("Schematron error occurred, rules could not be verified: " + e.getMessage());
report.addContent(errorReport);
+
+ // As the validation failed due to an exception lets identify the metadata as never validated.
+ metadataValidationStatus = MetadataValidationStatus.NEVER_CALCULATED;
+ } finally {
+ if (metadataValidationStatus != null && validations != null) {
+ validations.add(new MetadataValidation().
+ setId(new MetadataValidationId(metadataId, ruleId)).
+ setStatus(metadataValidationStatus).
+ setRequired(requirement == SchematronRequirement.REQUIRED).
+ setNumTests(firedRules).
+ setNumFailures(invalidRules));
+
+ }
}
// -- append report to main XML report.
diff --git a/core/src/main/java/org/fao/geonet/kernel/AccessManager.java b/core/src/main/java/org/fao/geonet/kernel/AccessManager.java
index 1b19757027..4c8820b248 100644
--- a/core/src/main/java/org/fao/geonet/kernel/AccessManager.java
+++ b/core/src/main/java/org/fao/geonet/kernel/AccessManager.java
@@ -23,41 +23,16 @@
package org.fao.geonet.kernel;
-import static org.fao.geonet.kernel.setting.Settings.SYSTEM_METADATAPRIVS_PUBLICATIONBYGROUPOWNERONLY;
-import static org.fao.geonet.repository.specification.OperationAllowedSpecs.hasMetadataId;
-import static org.fao.geonet.repository.specification.OperationAllowedSpecs.hasOperation;
-import static org.springframework.data.jpa.domain.Specification.where;
-
-import java.sql.SQLException;
-import java.util.*;
-
+import jeeves.server.UserSession;
+import jeeves.server.context.ServiceContext;
import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.constants.Geonet;
-import org.fao.geonet.domain.AbstractMetadata;
-import org.fao.geonet.domain.Group;
-import org.fao.geonet.domain.MetadataSourceInfo;
-import org.fao.geonet.domain.Operation;
-import org.fao.geonet.domain.OperationAllowed;
-import org.fao.geonet.domain.Pair;
-import org.fao.geonet.domain.Profile;
-import org.fao.geonet.domain.ReservedGroup;
-import org.fao.geonet.domain.ReservedOperation;
-import org.fao.geonet.domain.Setting;
-import org.fao.geonet.domain.User;
-import org.fao.geonet.domain.UserGroup;
-import org.fao.geonet.domain.User_;
+import org.fao.geonet.domain.*;
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
-import org.fao.geonet.repository.GroupRepository;
-import org.fao.geonet.repository.GroupRepositoryCustom;
-import org.fao.geonet.repository.OperationAllowedRepository;
-import org.fao.geonet.repository.OperationRepository;
-import org.fao.geonet.repository.SettingRepository;
-import org.fao.geonet.repository.SortUtils;
-import org.fao.geonet.repository.UserGroupRepository;
-import org.fao.geonet.repository.UserRepository;
+import org.fao.geonet.repository.*;
import org.fao.geonet.repository.specification.UserGroupSpecs;
import org.fao.geonet.utils.Log;
import org.jdom.Element;
@@ -65,10 +40,15 @@
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.data.jpa.domain.Specification;
-import jeeves.server.UserSession;
-import jeeves.server.context.ServiceContext;
+import java.sql.SQLException;
+import java.util.*;
+
+import static org.fao.geonet.kernel.setting.Settings.SYSTEM_INTRANET_IP_SEPARATOR;
+import static org.fao.geonet.kernel.setting.Settings.SYSTEM_METADATAPRIVS_PUBLICATIONBYGROUPOWNERONLY;
+import static org.fao.geonet.repository.specification.OperationAllowedSpecs.hasMetadataId;
+import static org.fao.geonet.repository.specification.OperationAllowedSpecs.hasOperation;
+import static org.springframework.data.jpa.domain.Specification.where;
/**
* Handles the access to a metadata depending on the metadata/group.
@@ -543,11 +523,29 @@ public String getPrivilegeName(int id) {
return operation.isPresent()?operation.get().getName():"";
}
+ public boolean isLocalhost(String ip) {
+ return ip.startsWith("0:0:0:0:0:0:0:1") || ip.equals("127.0.0.1");
+ }
+
+ private boolean isValidIntranetSettings(
+ String[] networkArray,
+ String[] netmaskArray
+ ) {
+ if (networkArray.length != netmaskArray.length) {
+ Log.error(Geonet.ACCESS_MANAGER,
+ String.format(
+ "Invalid intranet configuration. Define as many network mask (currently %d) as network ip (currently %d). Check Settings > Intranet.",
+ netmaskArray.length, networkArray.length));
+ return false;
+ } else {
+ return true;
+ }
+ }
+
public boolean isIntranet(String ip) {
//--- consider IPv4 & IPv6 loopback
//--- we use 'startsWith' because some addresses can be 0:0:0:0:0:0:0:1%0
-
- if (ip.startsWith("0:0:0:0:0:0:0:1") || ip.equals("127.0.0.1")) return true;
+ if (isLocalhost(ip)) return true;
// IPv6 link-local
String ipv6LinkLocalPrefix = "fe80:";
@@ -564,12 +562,26 @@ else if (ip.indexOf(':') >= 0) {
Optional netmask = settingRepository.findById(Settings.SYSTEM_INTRANET_NETMASK);
try {
- if (network.isPresent() && netmask.isPresent() &&
- StringUtils.isNotEmpty(network.get().getValue()) && StringUtils.isNotEmpty(netmask.get().getValue())) {
- long lIntranetNet = getAddress(network.get().getValue());
- long lIntranetMask = getAddress(netmask.get().getValue());
+ if (network.isPresent()
+ && netmask.isPresent()
+ && StringUtils.isNotEmpty(network.get().getValue())
+ && StringUtils.isNotEmpty(netmask.get().getValue())) {
long lAddress = getAddress(ip.split(",")[0]);
- return (lAddress & lIntranetMask) == (lIntranetNet & lIntranetMask);
+ String[] networkArray = network
+ .get().getValue().split(SYSTEM_INTRANET_IP_SEPARATOR);
+ String[] netmaskArray = netmask
+ .get().getValue().split(SYSTEM_INTRANET_IP_SEPARATOR);
+
+ if (isValidIntranetSettings(networkArray, netmaskArray)) {
+ for (int i = 0; i < networkArray.length; i++) {
+ long lIntranetNet = getAddress(networkArray[i]);
+ long lIntranetMask = getAddress(netmaskArray[i]);
+ if ((lAddress & lIntranetMask) == (lIntranetNet & lIntranetMask)) {
+ return true;
+ }
+ }
+ }
+ return false;
}
} catch (Exception nfe) {
Log.error(Geonet.ACCESS_MANAGER,"isIntranet error: " + nfe.getMessage(), nfe);
@@ -612,10 +624,6 @@ private long getAddress(String ip) {
* @return True if there's an uthenticated session, False otherwise.
*/
private boolean isUserAuthenticated(UserSession us) {
- if (us == null || !us.isAuthenticated()) {
- return false;
- } else {
- return true;
- }
+ return us != null && us.isAuthenticated();
}
}
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 5de03269a5..971e1c072a 100644
--- a/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.java
+++ b/core/src/main/java/org/fao/geonet/kernel/AllThesaurus.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)
*
@@ -166,7 +166,7 @@ public String getKeywordUrl() {
}
@Override
- public void retrieveThesaurusTitle() {
+ public void retrieveThesaurusInformation() {
// nothing to do
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/GeonetworkDataDirectory.java b/core/src/main/java/org/fao/geonet/kernel/GeonetworkDataDirectory.java
index 2f511299d8..ea4444d061 100644
--- a/core/src/main/java/org/fao/geonet/kernel/GeonetworkDataDirectory.java
+++ b/core/src/main/java/org/fao/geonet/kernel/GeonetworkDataDirectory.java
@@ -25,11 +25,6 @@
import jeeves.server.ServiceConfig;
import jeeves.server.sources.http.JeevesServlet;
-import org.apache.log4j.Appender;
-import org.apache.log4j.Logger;
-import org.apache.log4j.bridge.AppenderWrapper;
-import org.apache.logging.log4j.core.appender.FileAppender;
-import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.utils.IO;
@@ -45,6 +40,8 @@
import java.nio.file.Path;
import java.util.Iterator;
+import static org.fao.geonet.constants.Geonet.Path.IMPORT_STYLESHEETS_SCHEMA_PREFIX;
+
/**
* The GeoNetwork data directory is the location on the file system where GeoNetwork stores all of
* its custom configuration. This configuration defines such things as: What thesaurus is used by
@@ -122,15 +119,6 @@ public void init(final String webappName, final Path webappDir, Path systemDataD
this.init(webappName, webappDir, handlerConfig, jeevesServlet);
}
-
- /**
- * This is the name of the RollingFileAppender in your log4j2.xml configuration file.
- *
- * LogConfig uses this name to lookup RollingFileAppender to check configuration in
- * case a custom log file location has been used.
- */
- private static final String FILE_APPENDER_NAME = "File";
-
/**
* Logfile location as determined from appender, or system property, or default.
*
@@ -139,62 +127,7 @@ public void init(final String webappName, final Path webappDir, Path systemDataD
* @return logfile location, or {@code null} if unable to determine
*/
public static File getLogfile() {
- // Appender is supplied by LogUtils based on parsing log4j2.xml file indicated
- // by database settings
-
- // First, try the fileappender from the logger named "geonetwork"
- Appender appender = Logger.getLogger(Geonet.GEONETWORK).getAppender(FILE_APPENDER_NAME);
- // If still not found, try the one from the logger named "jeeves"
- if (appender == null) {
- appender = Logger.getLogger(Log.JEEVES).getAppender(FILE_APPENDER_NAME);
- }
- if (appender != null) {
- if (appender instanceof AppenderWrapper) {
- AppenderWrapper wrapper = (AppenderWrapper) appender;
- org.apache.logging.log4j.core.Appender appender2 = wrapper.getAppender();
-
- if (appender2 instanceof FileAppender) {
- FileAppender fileAppender = (FileAppender) appender2;
- String logFileName = fileAppender.getFileName();
- if (logFileName != null) {
- File logFile = new File(logFileName);
- if (logFile.exists()) {
- return logFile;
- }
- }
- }
- if (appender2 instanceof RollingFileAppender) {
- RollingFileAppender fileAppender = (RollingFileAppender) appender2;
- String logFileName = fileAppender.getFileName();
- if (logFileName != null) {
- File logFile = new File(logFileName);
- if (logFile.exists()) {
- return logFile;
- }
- }
- }
- }
- }
- Log.warning(Geonet.GEONETWORK, "Error when getting logger file for the " + "appender named '" + FILE_APPENDER_NAME + "'. "
- + "Check your log configuration file. "
- + "A FileAppender or RollingFileAppender is required to return last activity to the user interface."
- + "Appender file not found.");
-
- if (System.getProperties().containsKey("log_dir")) {
- File logDir = new File(System.getProperty("log_dir"));
- if (logDir.exists() && logDir.isDirectory()) {
- File logFile = new File(logDir, "logs/geonetwork.log");
- if (logFile.exists()) {
- return logFile;
- }
- }
- } else {
- File logFile = new File("logs/geonetwork.log");
- if (logFile.exists()) {
- return logFile;
- }
- }
- return null; // unavailable
+ return Log.getLogfile();
}
/**
@@ -456,6 +389,29 @@ private void initDataDirectory() throws IOException {
}
}
+ Path resourcesConfigDir = this.resourcesDir.resolve("config");
+ if (!Files.exists(resourcesConfigDir) || IO.isEmptyDir(resourcesConfigDir)) {
+ Log.info(Geonet.DATA_DIRECTORY, " - Copying config ...");
+ try {
+ Files.createDirectories(resourcesConfigDir);
+ final Path fromDir = getDefaultDataDir(webappDir).resolve("data").resolve("resources").resolve("config");
+
+ if (Files.exists(fromDir)) {
+ try (DirectoryStream paths = Files.newDirectoryStream(fromDir)) {
+ for (Path path : paths) {
+ final Path relativePath = fromDir.relativize(path);
+ final Path dest = resourcesConfigDir.resolve(relativePath.toString());
+ if (!Files.exists(dest)) {
+ IO.copyDirectoryOrFile(path, dest, false);
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ Log.error(Geonet.DATA_DIRECTORY, " - Config copy failed: " + e.getMessage(), e);
+ }
+ }
+
logoDir = this.resourcesDir.resolve("images").resolve("harvesting");
if (!Files.exists(logoDir) || IO.isEmptyDir(logoDir)) {
Log.info(Geonet.DATA_DIRECTORY, " - Copying logos ...");
@@ -816,6 +772,22 @@ public void setBackupDir(Path backupDir) {
this.backupDir = backupDir;
}
+
+ public Path getXsltConversion(String conversionId) {
+ if (conversionId.startsWith(IMPORT_STYLESHEETS_SCHEMA_PREFIX)) {
+ String[] pathToken = conversionId.split(":");
+ if (pathToken.length == 3) {
+ return this.getSchemaPluginsDir()
+ .resolve(pathToken[1])
+ .resolve(pathToken[2] + ".xsl");
+ }
+ } else {
+ return this.getWebappDir().resolve(Geonet.Path.IMPORT_STYLESHEETS).
+ resolve(conversionId + ".xsl");
+ }
+ return null;
+ }
+
/**
* Event is raised when GeonetworkDataDirectory has finished being initialized.
*/
diff --git a/core/src/main/java/org/fao/geonet/kernel/SpringLocalServiceInvoker.java b/core/src/main/java/org/fao/geonet/kernel/SpringLocalServiceInvoker.java
index 87da11f488..fa3711cb85 100644
--- a/core/src/main/java/org/fao/geonet/kernel/SpringLocalServiceInvoker.java
+++ b/core/src/main/java/org/fao/geonet/kernel/SpringLocalServiceInvoker.java
@@ -22,8 +22,6 @@
*/
package org.fao.geonet.kernel;
-import org.fao.geonet.NodeInfo;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
@@ -43,16 +41,19 @@
public class SpringLocalServiceInvoker {
- @Autowired
- public RequestMappingHandlerMapping requestMappingHandlerMapping;
+ public final RequestMappingHandlerMapping requestMappingHandlerMapping;
- @Autowired
- public RequestMappingHandlerAdapter requestMappingHandlerAdapter;
+ public final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
private HandlerMethodArgumentResolverComposite argumentResolvers;
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
private DefaultDataBinderFactory webDataBinderFactory;
+ public SpringLocalServiceInvoker(RequestMappingHandlerMapping requestMappingHandlerMapping, RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
+ this.requestMappingHandlerMapping = requestMappingHandlerMapping;
+ this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
+ }
+
public void init() {
argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(requestMappingHandlerAdapter.getArgumentResolvers());
returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(requestMappingHandlerAdapter.getReturnValueHandlers());
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 e4a2073596..d9937ed895 100644
--- a/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java
+++ b/core/src/main/java/org/fao/geonet/kernel/Thesaurus.java
@@ -1,4 +1,4 @@
-//=== Copyright (C) 2001-2005 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)
//===
@@ -75,6 +75,14 @@ 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 SKOS_NAMESPACE = "http://www.w3.org/2004/02/skos/core#";
+
+ private static final String DCTERMS_NAMESPACE = "http://purl.org/dc/terms/";
+
+ private static final String DC_NAMESPACE = "http://purl.org/dc/elements/1.1/";
+
private String fname;
private String type;
@@ -91,6 +99,12 @@ public class Thesaurus {
private String date;
+ private String createdDate;
+
+ private String issuedDate;
+
+ private String modifiedDate;
+
private String defaultNamespace;
private String downloadUrl;
@@ -99,12 +113,14 @@ public class Thesaurus {
private IsoLanguagesMapper isoLanguageMapper;
- private Map multilingualTitles = new Hashtable();
+ private Map multilingualTitles = new Hashtable<>();
+
+ private Map multilingualDescriptions = new Hashtable<>();
// map of lang -> dictionary of values
// key is a dublinCore element (i.e. https://guides.library.ucsc.edu/c.php?g=618773&p=4306386)
// see #retrieveDublinCore() for example
- private Map> dublinCoreMultilingual = new Hashtable>();
+ private Map> dublinCoreMultilingual = new Hashtable<>();
private Cache THESAURUS_SEARCH_CACHE;
@@ -129,7 +145,8 @@ public Thesaurus(IsoLanguagesMapper isoLanguageMapper, String fname, String tnam
}
public Thesaurus(IsoLanguagesMapper isoLanguageMapper, String fname,
- String tname, String description, String tnamespace, String type, String dname, Path thesaurusFile, String siteUrl,
+ Map multilingualTitles, Map multilingualDescriptions,
+ String tnamespace, String type, String dname, Path thesaurusFile, String siteUrl,
boolean ignoreMissingError, int thesaurusCacheMaxSize) {
super();
@@ -151,14 +168,26 @@ public Thesaurus(IsoLanguagesMapper isoLanguageMapper, String fname,
this.defaultNamespace = (tnamespace == null ? DEFAULT_THESAURUS_NAMESPACE : tnamespace);
- if (tname != null) {
- this.title = tname;
- } else {
- retrieveThesaurusTitle(thesaurusFile, dname + "." + fname, ignoreMissingError);
+ retrieveThesaurusInformation(thesaurusFile, dname + "." + fname, ignoreMissingError);
+
+ if (multilingualTitles != null) {
+ this.multilingualTitles = multilingualTitles;
+
+ if (StringUtils.isBlank(this.title)) {
+ this.title = this.multilingualTitles.get(toiso639_1_Lang(Geonet.DEFAULT_LANGUAGE));
+ }
+
+ if (StringUtils.isBlank(this.title)) {
+ this.title = dname + "." + fname;
+ }
}
- if (description != null) {
- this.description = description;
+ if (multilingualDescriptions != null) {
+ this.multilingualDescriptions = multilingualDescriptions;
+
+ if (StringUtils.isBlank(this.description)) {
+ this.description = this.multilingualDescriptions.get(toiso639_1_Lang(Geonet.DEFAULT_LANGUAGE));
+ }
}
}
@@ -185,6 +214,18 @@ public Map> getDublinCoreMultilingual() {
return Collections.unmodifiableMap(this.dublinCoreMultilingual);
}
+ public Map getMultilingualDescriptions() {
+ return Collections.unmodifiableMap(this.multilingualDescriptions);
+ }
+
+ public void setMultilingualTitles(Map multilingualTitles) {
+ this.multilingualTitles = multilingualTitles;
+ }
+
+ public void setMultilingualDescriptions(Map multilingualDescriptions) {
+ this.multilingualDescriptions = multilingualDescriptions;
+ }
+
/**
* @return Thesaurus identifier
*/
@@ -223,6 +264,18 @@ public String getDate() {
return date;
}
+ public String getCreatedDate() {
+ return createdDate;
+ }
+
+ public String getIssuedDate() {
+ return issuedDate;
+ }
+
+ public String getModifiedDate() {
+ return modifiedDate;
+ }
+
@Nonnull
public FileTime getLastModifiedTime() {
FileTime lastModified;
@@ -247,8 +300,8 @@ public String getKeywordUrl() {
return keywordUrl;
}
- public void retrieveThesaurusTitle() {
- retrieveThesaurusTitle(thesaurusFile, dname + "." + fname, false);
+ public void retrieveThesaurusInformation() {
+ retrieveThesaurusInformation(thesaurusFile, dname + "." + fname, false);
}
protected String buildDownloadUrl(String fname, String type, String dname, String siteUrl) {
@@ -289,15 +342,11 @@ public synchronized Thesaurus initRepository() throws ConfigurationException, IO
return this;
}
- /**
- * TODO javadoc.
- */
public synchronized QueryResultsTable performRequest(String query) throws IOException, MalformedQueryException,
QueryEvaluationException, AccessDeniedException {
if (Log.isDebugEnabled(Geonet.THESAURUS))
Log.debug(Geonet.THESAURUS, "Query : " + query);
- //printResultsTable(resultsTable);
return repository.performTableQuery(QueryLanguage.SERQL, query);
}
@@ -332,30 +381,7 @@ public List getConceptSchemes() {
return ret;
} catch (Exception e) {
Log.error(Geonet.THESAURUS_MAN, "Error retrieving concept schemes for " + thesaurusFile + ". Error is: " + e.getMessage());
- return Collections.EMPTY_LIST;
- }
- }
-
- /**
- *
- * @param resultsTable
- */
- @SuppressWarnings("unused")
- private void printResultsTable(QueryResultsTable resultsTable) {
- int rowCount = resultsTable.getRowCount();
- int columnCount = resultsTable.getColumnCount();
-
- for (int row = 0; row < rowCount; row++) {
- for (int column = 0; column < columnCount; column++) {
- Value value = resultsTable.getValue(row, column);
-
- if (value != null) {
- System.out.print(value.toString());
- } else {
- System.out.print("null");
- }
- System.out.print("\t");
- }
+ return Collections.emptyList();
}
}
@@ -371,18 +397,17 @@ public synchronized URI addElement(KeywordBean keyword) throws IOException, Acce
ValueFactory myFactory = myGraph.getValueFactory();
// Define namespace
- String namespaceSkos = "http://www.w3.org/2004/02/skos/core#";
String namespaceGml = "http://www.opengis.net/gml#";
// Create subject
URI mySubject = myFactory.createURI(keyword.getUriCode());
- URI skosClass = myFactory.createURI(namespaceSkos, "Concept");
+ URI skosClass = myFactory.createURI(SKOS_NAMESPACE, "Concept");
URI rdfType = myFactory.createURI(org.openrdf.vocabulary.RDF.TYPE);
URI predicatePrefLabel = myFactory
- .createURI(namespaceSkos, "prefLabel");
+ .createURI(SKOS_NAMESPACE, "prefLabel");
URI predicateScopeNote = myFactory
- .createURI(namespaceSkos, "scopeNote");
+ .createURI(SKOS_NAMESPACE, "scopeNote");
URI predicateBoundedBy = myFactory.createURI(namespaceGml, "BoundedBy");
URI predicateEnvelope = myFactory.createURI(namespaceGml, "Envelope");
@@ -502,9 +527,8 @@ public synchronized URI updateElement(KeywordBean keyword, boolean replace) thro
// Set namespace skos and predicates
ValueFactory myFactory = myGraph.getValueFactory();
- String namespaceSkos = "http://www.w3.org/2004/02/skos/core#";
- URI predicatePrefLabel = myFactory.createURI(namespaceSkos, "prefLabel");
- URI predicateScopeNote = myFactory.createURI(namespaceSkos, "scopeNote");
+ URI predicatePrefLabel = myFactory.createURI(SKOS_NAMESPACE, "prefLabel");
+ URI predicateScopeNote = myFactory.createURI(SKOS_NAMESPACE, "scopeNote");
// Get subject (URI)
URI subject = myFactory.createURI(keyword.getUriCode());
@@ -544,7 +568,7 @@ public synchronized URI updateElement(KeywordBean keyword, boolean replace) thro
BNode subjectGml = null;
iter = myGraph.getStatements(subject, predicateBoundedBy, null);
while (iter.hasNext()) {
- AtomicReference st = new AtomicReference(iter.next());
+ AtomicReference st = new AtomicReference<>(iter.next());
if (st.get().getObject() instanceof BNode) {
subjectGml = (BNode) st.get().getObject();
}
@@ -556,7 +580,7 @@ public synchronized URI updateElement(KeywordBean keyword, boolean replace) thro
if (!(iter.hasNext())) {
break;
}
- AtomicReference st = new AtomicReference(iter.next());
+ AtomicReference st = new AtomicReference<>(iter.next());
myGraph.remove(st.get());
break;
}
@@ -566,7 +590,7 @@ public synchronized URI updateElement(KeywordBean keyword, boolean replace) thro
if (!(iter.hasNext())) {
break;
}
- AtomicReference st = new AtomicReference(iter.next());
+ AtomicReference st = new AtomicReference<>(iter.next());
myGraph.remove(st.get());
break;
}
@@ -586,7 +610,7 @@ public synchronized URI updateElement(KeywordBean keyword, boolean replace) thro
private void removeMatchingLiterals(boolean replace, Graph myGraph, StatementIterator iter, Set valueLanguages) {
try {
- ArrayList toRemove = new ArrayList();
+ ArrayList toRemove = new ArrayList<>();
while (iter.hasNext()) {
Statement st = iter.next();
if (st.getObject() instanceof Literal) {
@@ -619,11 +643,11 @@ public synchronized boolean isFreeCode(String namespace, String code) throws Acc
ValueFactory myFactory = myGraph.getValueFactory();
URI obj = namespace == null ? myFactory.createURI(code) : myFactory.createURI(namespace, code);
Collection> statementsCollection = myGraph.getStatementCollection(obj, null, null);
- if (statementsCollection != null && statementsCollection.size() > 0) {
+ if (statementsCollection != null && !statementsCollection.isEmpty()) {
res = false;
}
statementsCollection = myGraph.getStatementCollection(null, null, obj);
- if (statementsCollection != null && statementsCollection.size() > 0) {
+ if (statementsCollection != null && !statementsCollection.isEmpty()) {
res = false;
}
return res;
@@ -669,7 +693,7 @@ public synchronized Thesaurus updateCodeByURI(String olduri, String newuri) thro
private Thesaurus updateElementCode(Graph myGraph, URI oldobj, URI newobj) {
StatementIterator iterStSubject = myGraph.getStatements(oldobj, null, null);
while (iterStSubject.hasNext()) {
- AtomicReference st = new AtomicReference(iterStSubject.next());
+ AtomicReference st = new AtomicReference<>(iterStSubject.next());
myGraph.add(newobj, st.get().getPredicate(), st.get().getObject());
}
@@ -685,42 +709,125 @@ private Thesaurus updateElementCode(Graph myGraph, URI oldobj, URI newobj) {
public void writeConceptScheme(String thesaurusTitle, String namespace) throws IOException, AccessDeniedException, GraphException {
- writeConceptScheme(thesaurusTitle, null, null, null, namespace);
+ Graph myGraph = new org.openrdf.model.impl.GraphImpl();
+ writeConceptScheme(myGraph, thesaurusTitle, null, null, null, null, null, namespace);
+ repository.addGraph(myGraph);
}
/**
- * Set the title of the thesaurus and save the graph to the repository.
+ * Set the information about a new thesaurus and save the graph to the repository.
*/
- public void writeConceptScheme(String thesaurusTitle,
- String description,
- String identifier,
- String type,
- String namespace) throws IOException, AccessDeniedException, GraphException {
+ public void createConceptScheme(String thesaurusTitle,
+ Map multilingualTitles,
+ String thesaurusDescription,
+ Map multilingualDescriptions,
+ String identifier,
+ String type,
+ String namespace) throws IOException, AccessDeniedException, GraphException {
Graph myGraph = new org.openrdf.model.impl.GraphImpl();
- ValueFactory myFactory = myGraph.getValueFactory();
+ writeConceptScheme(myGraph,
+ thesaurusTitle,
+ multilingualTitles,
+ thesaurusDescription,
+ multilingualDescriptions,
+ identifier,
+ type,
+ namespace);
+
+ repository.addGraph(myGraph);
+ }
+
+
+ /**
+ * Set the information about an existing thesaurus.
+ */
+ public void updateConceptScheme(String thesaurusTitle,
+ Map multilingualTitles,
+ String thesaurusDescription,
+ Map multilingualDescriptions,
+ String identifier,
+ String type,
+ String namespace) throws AccessDeniedException, GraphException {
+ Graph myGraph = repository.getGraph();
+ removeElement(getConceptSchemes().get(0));
+
+ writeConceptScheme(myGraph,
+ thesaurusTitle,
+ multilingualTitles,
+ thesaurusDescription,
+ multilingualDescriptions,
+ identifier,
+ type,
+ namespace);
+ }
+
+ public void writeConceptScheme(Graph myGraph, String thesaurusTitle,
+ Map multilingualTitles,
+ String thesaurusDescription,
+ Map multilingualDescriptions,
+ String identifier,
+ String type,
+ String namespace) throws GraphException {
- String namespaceSkos = "http://www.w3.org/2004/02/skos/core#";
- String namespaceDC = "http://purl.org/dc/elements/1.1/";
+ ValueFactory myFactory = myGraph.getValueFactory();
URI mySubject = myFactory.createURI(namespace);
- URI skosClass = myFactory.createURI(namespaceSkos, "ConceptScheme");
+ URI skosClass = myFactory.createURI(SKOS_NAMESPACE, "ConceptScheme");
URI rdfType = myFactory.createURI(org.openrdf.vocabulary.RDF.TYPE);
mySubject.addProperty(rdfType, skosClass);
- addElement("title", thesaurusTitle, myGraph, myFactory, mySubject);
- addElement("description", description, myGraph, myFactory, mySubject);
+ URI titleURI = myFactory.createURI(DC_NAMESPACE, "title");
+
+ boolean addTitleElement = true;
+ if (multilingualTitles != null) {
+ for (Entry entrySet : multilingualTitles.entrySet()) {
+ if (StringUtils.isNotEmpty(entrySet.getValue())) {
+ String language = toiso639_1_Lang(entrySet.getKey());
+ Value valueObj = myFactory.createLiteral(entrySet.getValue(), language);
+ myGraph.add(mySubject, titleURI, valueObj);
+
+ addTitleElement = false;
+ }
+ }
+ }
+
+ if (addTitleElement) {
+ addElement("title", thesaurusTitle, myGraph, myFactory, mySubject);
+ }
+
+
+ boolean addDescriptionElement = true;
+ URI descriptionURI = myFactory.createURI(DC_NAMESPACE, "description");
+
+ if (multilingualDescriptions != null) {
+ for (Entry entrySet : multilingualDescriptions.entrySet()) {
+ if (StringUtils.isNotEmpty(entrySet.getValue())) {
+ String language = toiso639_1_Lang(entrySet.getKey());
+ Value valueObj = myFactory.createLiteral(entrySet.getValue(), language);
+ myGraph.add(mySubject, descriptionURI, valueObj);
+
+ addDescriptionElement = false;
+ }
+ }
+ }
+
+ if (addDescriptionElement) {
+ addElement("description", thesaurusDescription, myGraph, myFactory, mySubject);
+ }
+
addElement("identifier", identifier, myGraph, myFactory, mySubject);
addElement("type", type, myGraph, myFactory, mySubject);
-
- repository.addGraph(myGraph);
}
+
+
+
+
private void addElement(String name, String value, Graph myGraph, ValueFactory myFactory, URI mySubject) {
- String namespaceDC = "http://purl.org/dc/elements/1.1/";
if (StringUtils.isNotEmpty(value)) {
- URI uri = myFactory.createURI(namespaceDC, name);
+ URI uri = myFactory.createURI(DC_NAMESPACE, name);
Value object = myFactory.createLiteral(value);
myGraph.add(mySubject, uri, object);
}
@@ -751,11 +858,7 @@ private void addElement(String name, String value, Graph myGraph, ValueFactory m
// note - only looks at language-specified elements
//
private void retrieveDublinCore(Element thesaurusEl) {
- List theNSs = new ArrayList();
- theNSs.add(Namespace.getNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
- theNSs.add(Namespace.getNamespace("skos", "http://www.w3.org/2004/02/skos/core#"));
- theNSs.add(Namespace.getNamespace("dc", "http://purl.org/dc/elements/1.1/"));
- theNSs.add(Namespace.getNamespace("dcterms", "http://purl.org/dc/terms/"));
+ List theNSs = getThesaurusNamespaces();
Namespace xmlNS = Namespace.getNamespace("xml","http://www.w3.org/XML/1998/namespace");
try {
@@ -767,11 +870,9 @@ private void retrieveDublinCore(Element thesaurusEl) {
String value = el.getTextTrim();
String name = el.getName();
if (!dublinCoreMultilingual.containsKey(lang)) {
- dublinCoreMultilingual.put(lang,new HashMap());
+ dublinCoreMultilingual.put(lang,new HashMap<>());
}
dublinCoreMultilingual.get(lang).put(name,value);
-
- int t=0;
}
} catch (Exception e) {
Log.warning(Geonet.THESAURUS,"error extracting multilingual dublin core items from thesaurus",e);
@@ -792,34 +893,50 @@ private void retrieveDublinCore(Element thesaurusEl) {
// "fr": "French Version (fr)"
// }
private void retrieveMultiLingualTitles(Element thesaurusEl) {
- List theNSs = new ArrayList();
- theNSs.add(Namespace.getNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"));
- theNSs.add(Namespace.getNamespace("skos", "http://www.w3.org/2004/02/skos/core#"));
- theNSs.add(Namespace.getNamespace("dc", "http://purl.org/dc/elements/1.1/"));
- theNSs.add(Namespace.getNamespace("dcterms", "http://purl.org/dc/terms/"));
-
- Namespace xmlNS = Namespace.getNamespace("xml","http://www.w3.org/XML/1998/namespace");
-
try {
- List multiLingualTitles = (List) Xml.selectNodes(thesaurusEl,
- "skos:ConceptScheme/dc:title[@xml:lang]|skos:ConceptScheme/dcterms:title[@xml:lang]", theNSs);
+ 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]";
multilingualTitles.clear();
- for (Element el: multiLingualTitles) {
- String lang = isoLanguageMapper.iso639_2_to_iso639_1(el.getAttribute("lang", xmlNS).getValue());
- String title = el.getTextTrim();
- multilingualTitles.put(lang,title);
- }
+ multilingualTitles.putAll(retrieveMultilingualField(thesaurusEl, xpathTitles));
} catch (Exception e) {
Log.warning(Geonet.THESAURUS,"error extracting multilingual titles from thesaurus",e);
}
}
+ private void retrieveMultiLingualDescriptions(Element thesaurusEl) {
+ try {
+ String xpathDescriptions = "skos:ConceptScheme/dc:description[@xml:lang]|skos:ConceptScheme/dcterms:description[@xml:lang]|rdf:Description[rdf:type/@rdf:resource = 'http://www.w3.org/2004/02/skos/core#ConceptScheme']/dc:description[@xml:lang]";
+ multilingualDescriptions.clear();
+ multilingualDescriptions.putAll(retrieveMultilingualField(thesaurusEl, xpathDescriptions));
+ } catch (Exception e) {
+ Log.warning(Geonet.THESAURUS,"error extracting multilingual descriptions from thesaurus",e);
+ }
+ }
+
+ private Map retrieveMultilingualField(Element thesaurusEl, String xpath) throws JDOMException {
+ List theNSs = getThesaurusNamespaces();
+
+ Namespace xmlNS = Namespace.getNamespace("xml","http://www.w3.org/XML/1998/namespace");
+
+ Map multilingualValues = new HashMap<>();
+ List multilingualValuesEl = (List) Xml.selectNodes(thesaurusEl,
+ xpath, theNSs);
+ for (Element el: multilingualValuesEl) {
+ String lang = isoLanguageMapper.iso639_2_to_iso639_1(el.getAttribute("lang", xmlNS).getValue());
+ String titleValue = el.getTextTrim();
+ multilingualValues.put(lang, titleValue);
+ }
+
+ return multilingualValues;
+ }
+
/**
- * Retrieves the thesaurus title from rdf file.
+ * Retrieves the thesaurus information from rdf file.
*
* Used to set the thesaurusName and thesaurusDate for keywords.
*/
- private void retrieveThesaurusTitle(Path thesaurusFile, String defaultTitle, boolean ignoreMissingError) {
+ private void retrieveThesaurusInformation(Path thesaurusFile, String defaultTitle, boolean ignoreMissingError) {
+ if (!Files.exists(thesaurusFile)) return;
+
// set defaults as in the case of a local thesaurus file, this info
// may not be present yet
this.title = defaultTitle;
@@ -827,36 +944,38 @@ private void retrieveThesaurusTitle(Path thesaurusFile, String defaultTitle, boo
try {
Element thesaurusEl = Xml.loadFile(thesaurusFile);
- List theNSs = new ArrayList();
- Namespace rdfNamespace = Namespace.getNamespace("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
+ List theNSs = new ArrayList<>();
+ Namespace rdfNamespace = Namespace.getNamespace("rdf", RDF_NAMESPACE);
theNSs.add(rdfNamespace);
- theNSs.add(Namespace.getNamespace("skos", "http://www.w3.org/2004/02/skos/core#"));
- theNSs.add(Namespace.getNamespace("dc", "http://purl.org/dc/elements/1.1/"));
- theNSs.add(Namespace.getNamespace("dcterms", "http://purl.org/dc/terms/"));
+ theNSs.add(Namespace.getNamespace("skos", SKOS_NAMESPACE));
+ theNSs.add(Namespace.getNamespace("dc", DC_NAMESPACE));
+ theNSs.add(Namespace.getNamespace("dcterms", DCTERMS_NAMESPACE));
this.defaultNamespace = null;
retrieveMultiLingualTitles(thesaurusEl);
retrieveDublinCore(thesaurusEl);
- Element title = Xml.selectElement(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);
- if (title != null) {
- this.title = title.getValue();
- this.defaultNamespace = title.getParentElement().getAttributeValue("about", rdfNamespace);
+ if (titleEl != null) {
+ this.title = titleEl.getValue();
+ this.defaultNamespace = titleEl.getParentElement().getAttributeValue("about", rdfNamespace);
} else {
this.title = defaultTitle;
this.defaultNamespace = DEFAULT_THESAURUS_NAMESPACE;
}
- Element description = Xml.selectElement(thesaurusEl,
+ Element descriptionEl = Xml.selectElement(thesaurusEl,
"skos:ConceptScheme/dc:description|skos:ConceptScheme/dcterms:description|" +
"skos:Collection/dc:description|skos:Collection/dcterms:description|" +
"rdf:Description/dc:description|rdf:Description/dcterms:description", theNSs);
- this.description = description != null ? description.getValue() : "";
+ this.description = descriptionEl != null ? descriptionEl.getValue() : "";
+
+ retrieveMultiLingualDescriptions(thesaurusEl);
try {
new java.net.URI(this.defaultNamespace);
@@ -864,8 +983,17 @@ private void retrieveThesaurusTitle(Path thesaurusFile, String defaultTitle, boo
this.defaultNamespace = DEFAULT_THESAURUS_NAMESPACE;
}
- Element dateEl = Xml.selectElement(thesaurusEl, "skos:ConceptScheme/dcterms:issued|skos:Collection/dc:date", theNSs);
+ Element issuedDateEl = Xml.selectElement(thesaurusEl, "skos:ConceptScheme/dcterms:issued", theNSs);
+ this.issuedDate = issuedDateEl==null? "": issuedDateEl.getText();
+
+ Element modifiedDateEl = Xml.selectElement(thesaurusEl, "skos:ConceptScheme/dcterms:modified", theNSs);
+ this.modifiedDate = modifiedDateEl==null? "": modifiedDateEl.getText();
+ Element createdDateEl = Xml.selectElement(thesaurusEl, "skos:ConceptScheme/dcterms:created", theNSs);
+ this.createdDate = createdDateEl==null? "": createdDateEl.getText();
+
+ // Default date
+ Element dateEl = Xml.selectElement(thesaurusEl, "skos:ConceptScheme/dcterms:issued|skos:Collection/dc:date", theNSs);
Date thesaususDate = parseThesaurusDate(dateEl);
if (thesaususDate == null) {
@@ -921,11 +1049,13 @@ private Date parseThesaurusDate(Element dateEl) {
String dateVal = dateEl.getText();
// Try several date formats (date format seem not unified)
- List dfList = new ArrayList();
+ List dfList = new ArrayList<>();
dfList.add(new SimpleDateFormat("EEE MMM d HH:mm:ss z yyyy"));
dfList.add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
dfList.add(new SimpleDateFormat("yyyy-MM-dd"));
+ dfList.add(new SimpleDateFormat("yyyy-MM"));
+ dfList.add(new SimpleDateFormat("yyyy"));
StringBuffer errorMsg = new StringBuffer("Error parsing the thesaurus date value: ");
errorMsg.append(dateVal);
@@ -980,9 +1110,8 @@ public synchronized void addRelation(String subject, KeywordRelation related, St
// Set namespace skos and predicates
ValueFactory myFactory = myGraph.getValueFactory();
- String namespaceSkos = "http://www.w3.org/2004/02/skos/core#";
- URI relationURI = myFactory.createURI(namespaceSkos, related.name);
- URI opposteRelationURI = myFactory.createURI(namespaceSkos, related.opposite().name);
+ URI relationURI = myFactory.createURI(SKOS_NAMESPACE, related.name);
+ URI opposteRelationURI = myFactory.createURI(SKOS_NAMESPACE, related.opposite().name);
URI subjectURI = myFactory.createURI(subject);
URI relatedSubjectURI = myFactory.createURI(relatedSubject);
@@ -1094,7 +1223,7 @@ public List getNarrower(String uri, String... languages) {
*/
public boolean hasBroader(String uri) {
- return getRelated(uri, KeywordRelation.NARROWER).size() > 0;
+ return !getRelated(uri, KeywordRelation.NARROWER).isEmpty();
}
/**
@@ -1154,79 +1283,13 @@ public KeywordBean getKeywordWithLabel(String label, String langCode) {
throw new RuntimeException(e);
}
- if (matchingKeywords.size() == 0) {
+ if (matchingKeywords.isEmpty()) {
throw new TermNotFoundException(label);
}
return matchingKeywords.get(0);
}
- // ------------------------------- Deprecated methods -----------------------------
-
- /**
- * @deprecated since 2.9.0. Use {@link #addElement(KeywordBean)}
- */
- URI addElement(String code, String prefLab, String note, String lang) throws GraphException, IOException,
- AccessDeniedException {
-
- KeywordBean bean = new KeywordBean(getIsoLanguageMapper())
- .setUriCode(code)
- .setValue(prefLab, lang)
- .setDefinition(note, lang);
-
- return addElement(bean);
- }
-
- /**
- * @deprecated since 2.9.0 use {@link #addElement(KeywordBean)}
- */
- URI addElement(String code, String prefLab, String note, String east, String west, String south,
- String north, String lang) throws IOException, AccessDeniedException, GraphException {
-
- return addElement(new KeywordBean(getIsoLanguageMapper())
- .setUriCode(code)
- .setValue(prefLab, lang)
- .setDefinition(note, lang)
- .setCoordEast(east)
- .setCoordNorth(north)
- .setCoordSouth(south)
- .setCoordWest(west));
- }
-
-
- /**
- * @deprecated since 2.9.0 use {@link #updateElement(KeywordBean, boolean)}
- */
- URI updateElement(String namespace, String id, String prefLab, String note, String lang) throws IOException,
- MalformedQueryException, QueryEvaluationException, AccessDeniedException, GraphException {
- KeywordBean keyword = new KeywordBean(getIsoLanguageMapper())
- .setNamespaceCode(namespace)
- .setRelativeCode(id)
- .setValue(prefLab, lang)
- .setDefinition(note, lang);
- return updateElement(keyword, false);
- }
-
- /**
- * @deprecated Since 2.9.0 use {@link #updateElement(KeywordBean, boolean)}
- */
- URI updateElement(String namespace, String id, String prefLab, String note, String east, String west,
- String south, String north, String lang) throws AccessDeniedException, IOException,
- MalformedQueryException, QueryEvaluationException, GraphException {
-
- KeywordBean bean = new KeywordBean(getIsoLanguageMapper())
- .setNamespaceCode(namespace)
- .setRelativeCode(id)
- .setValue(prefLab, lang)
- .setDefinition(note, lang)
- .setCoordEast(east)
- .setCoordNorth(north)
- .setCoordSouth(south)
- .setCoordWest(west);
-
- return updateElement(bean, true);
- }
-
public synchronized void clear() throws IOException, AccessDeniedException {
AdminListener listener = new DummyAdminListener();
repository.clear(listener);
@@ -1298,8 +1361,19 @@ private List> classifyBroaderTerms(KeywordBean term, Str
}
private ArrayList classifyTermWithNoBroaderTerms(KeywordBean term) {
- ArrayList list = new ArrayList ();
+ ArrayList list = new ArrayList <>();
list.add(term);
return list;
}
+
+
+ private List getThesaurusNamespaces() {
+ List theNSs = new ArrayList<>();
+ theNSs.add(Namespace.getNamespace("rdf", RDF_NAMESPACE));
+ theNSs.add(Namespace.getNamespace("skos", SKOS_NAMESPACE));
+ theNSs.add(Namespace.getNamespace("dc", DC_NAMESPACE));
+ theNSs.add(Namespace.getNamespace("dcterms", DCTERMS_NAMESPACE));
+
+ return theNSs;
+ }
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/ThesaurusManager.java b/core/src/main/java/org/fao/geonet/kernel/ThesaurusManager.java
index 9dbb6165cf..a48563dc3d 100644
--- a/core/src/main/java/org/fao/geonet/kernel/ThesaurusManager.java
+++ b/core/src/main/java/org/fao/geonet/kernel/ThesaurusManager.java
@@ -1,4 +1,4 @@
-//=== Copyright (C) 2001-2005 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)
//===
@@ -34,18 +34,16 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
-import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
import org.fao.geonet.Util;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.domain.AbstractMetadata;
-import org.fao.geonet.domain.Constants;
-import org.fao.geonet.domain.ThesaurusActivation;
import org.fao.geonet.kernel.datamanager.IMetadataManager;
import org.fao.geonet.kernel.datamanager.IMetadataSchemaUtils;
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
@@ -53,7 +51,6 @@
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
import org.fao.geonet.languages.IsoLanguagesMapper;
-import org.fao.geonet.repository.ThesaurusActivationRepository;
import org.fao.geonet.utils.IO;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
@@ -90,16 +87,13 @@ public class ThesaurusManager implements ThesaurusFinder {
@Autowired
private IsoLanguagesMapper isoLanguagesMapper;
- @Autowired
- private ThesaurusActivationRepository thesaurusActivationRepository;
-
@Autowired
private IMetadataSchemaUtils metadataSchemaUtils;
@Autowired
private AllThesaurus allThesaurus;
- private ConcurrentHashMap thesauriMap = new ConcurrentHashMap();
+ private ConcurrentHashMap thesauriMap = new ConcurrentHashMap<>();
private LocalService service = null;
private Path thesauriDirectory = null;
private boolean initialized = false;
@@ -300,9 +294,11 @@ public void addThesaurus(Thesaurus gst, boolean writeConceptScheme) throws Excep
thesauriMap.put(thesaurusName, gst);
if (writeConceptScheme) {
- gst.writeConceptScheme(
+ gst.createConceptScheme(
gst.getTitle(),
+ gst.getMultilingualTitles(),
gst.getDescription(),
+ gst.getMultilingualDescriptions(),
gst.getFname(),
gst.getDname(),
gst.getDefaultNamespace());
@@ -440,7 +436,7 @@ public String createUpdateThesaurusFromRegister(String uuid, String type, Servic
}
String theKey = gst.getKey();
- gst.retrieveThesaurusTitle();
+ gst.retrieveThesaurusInformation();
addOrReloadThesaurus(gst);
@@ -487,15 +483,17 @@ public Element buildResultfromThTable(ServiceContext context) throws SQLExceptio
// ],
Element elMultilingualTitles = new Element("multilingualTitles");
for (Map.Entry entry : currentTh.getMultilingualTitles().entrySet()) {
- Element elMultilingualTitle = new Element("multilingualTitle");
- Element elMultilingualTitl_lang = new Element("lang");
- elMultilingualTitl_lang.setText(entry.getKey());
- Element elMultilingualTitle_title = new Element("title");
- elMultilingualTitle_title.setText(entry.getValue());
- elMultilingualTitle.addContent(elMultilingualTitl_lang);
- elMultilingualTitle.addContent(elMultilingualTitle_title);
-
- elMultilingualTitles.addContent(elMultilingualTitle);
+ if (StringUtils.isNotBlank(entry.getValue())) {
+ Element elMultilingualTitle = new Element("multilingualTitle");
+ Element elMultilingualTitleLang = new Element("lang");
+ elMultilingualTitleLang.setText(entry.getKey());
+ Element elMultilingualTitleTitle = new Element("title");
+ elMultilingualTitleTitle.setText(entry.getValue());
+ elMultilingualTitle.addContent(elMultilingualTitleLang);
+ elMultilingualTitle.addContent(elMultilingualTitleTitle);
+
+ elMultilingualTitles.addContent(elMultilingualTitle);
+ }
}
//add dublin core items to the response
@@ -507,20 +505,41 @@ public Element buildResultfromThTable(ServiceContext context) throws SQLExceptio
for (Map.Entry> entryLang : currentTh.getDublinCoreMultilingual().entrySet()) {
String lang = entryLang.getKey();
for (Map.Entry entryItem : entryLang.getValue().entrySet()) {
- Element elItem = new Element("dublinCoreMultilingual");
- Element elLang = new Element("lang");
- elLang.setText(lang);
- Element elTag = new Element("tag");
- elTag.setText(entryItem.getKey());
- Element elValue = new Element("value");
- elValue.setText(entryItem.getValue());
-
- elItem.addContent(elLang);
- elItem.addContent(elTag);
- elItem.addContent(elValue);
-
+ if (StringUtils.isNotBlank(entryItem.getValue())) {
+ Element elItem = new Element("dublinCoreMultilingual");
+ Element elLang = new Element("lang");
+ elLang.setText(lang);
+ Element elTag = new Element("tag");
+ elTag.setText(entryItem.getKey());
+ Element elValue = new Element("value");
+ elValue.setText(entryItem.getValue());
+
+ elItem.addContent(elLang);
+ elItem.addContent(elTag);
+ elItem.addContent(elValue);
+
+ elDublinCoreMultilingual.addContent(elItem);
+ }
+ }
+ }
- elDublinCoreMultilingual.addContent(elItem);
+ //add multilingual descriptions in to response
+ // "multilingualDescriptions": [
+ // { "lang": "fr","title": "Data Usage Scope FR"},
+ // {"lang": "en","title": "Data Usage Scope EN"}
+ // ],
+ Element elMultilingualDescriptions = new Element("multilingualDescriptions");
+ for (Map.Entry entry : currentTh.getMultilingualDescriptions().entrySet()) {
+ if (StringUtils.isNotBlank(entry.getValue())) {
+ Element elMultilingualDescription = new Element("multilingualDescription");
+ Element elMultilingualDescLang = new Element("lang");
+ elMultilingualDescLang.setText(entry.getKey());
+ Element elMultilingualDescDesc = new Element("description");
+ elMultilingualDescDesc.setText(entry.getValue());
+ elMultilingualDescription.addContent(elMultilingualDescLang);
+ elMultilingualDescription.addContent(elMultilingualDescDesc);
+
+ elMultilingualDescriptions.addContent(elMultilingualDescription);
}
}
@@ -532,6 +551,18 @@ public Element buildResultfromThTable(ServiceContext context) throws SQLExceptio
String date = currentTh.getDate();
elDate.addContent(date);
+ Element elCreatedDate = new Element("createdDate");
+ String createdDate = currentTh.getCreatedDate();
+ elCreatedDate.addContent(createdDate);
+
+ Element elIssuedDate = new Element("issuedDate");
+ String issuedDate = currentTh.getIssuedDate();
+ elIssuedDate.addContent(issuedDate);
+
+ Element elModifiedDate = new Element("modifiedDate");
+ String modifiedDate = currentTh.getModifiedDate();
+ elModifiedDate.addContent(modifiedDate);
+
Element elUrl = new Element("url");
String url = currentTh.getDownloadUrl();
elUrl.addContent(url);
@@ -540,17 +571,6 @@ public Element buildResultfromThTable(ServiceContext context) throws SQLExceptio
String defaultURI = currentTh.getDefaultNamespace();
elDefaultURI.addContent(defaultURI);
-
- Element elActivated = new Element("activated");
-
- // By default thesaurus are enabled (if nothing defined in db)
- char activated = Constants.YN_TRUE;
- final Optional activation = thesaurusActivationRepository.findById(currentTh.getKey());
- if (activation.isPresent() && !activation.get().isActivated()) {
- activated = Constants.YN_FALSE;
- }
- elActivated.setText("" + activated);
-
elLoop.addContent(elKey);
elLoop.addContent(elDname);
elLoop.addContent(description);
@@ -558,11 +578,14 @@ public Element buildResultfromThTable(ServiceContext context) throws SQLExceptio
elLoop.addContent(elTitle);
elLoop.addContent(elMultilingualTitles);
elLoop.addContent(elDublinCoreMultilingual);
+ elLoop.addContent(elMultilingualDescriptions);
elLoop.addContent(elDate);
+ elLoop.addContent(elCreatedDate);
+ elLoop.addContent(elIssuedDate);
+ elLoop.addContent(elModifiedDate);
elLoop.addContent(elUrl);
elLoop.addContent(elDefaultURI);
elLoop.addContent(elType);
- elLoop.addContent(elActivated);
elRoot.addContent(elLoop);
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java b/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java
index a0edb9b06e..8e7453c5a8 100644
--- a/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.java
+++ b/core/src/main/java/org/fao/geonet/kernel/backup/ArchiveAllMetadataJob.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,20 +23,14 @@
package org.fao.geonet.kernel.backup;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import javax.annotation.Nullable;
-
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import jeeves.server.UserSession;
+import jeeves.server.context.ServiceContext;
+import jeeves.server.dispatchers.ServiceManager;
+import org.apache.commons.io.FileUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.constants.Geonet;
-import org.fao.geonet.domain.AbstractMetadata;
import org.fao.geonet.domain.Metadata;
import org.fao.geonet.domain.MetadataType;
import org.fao.geonet.domain.Profile;
@@ -51,25 +45,26 @@
import org.fao.geonet.repository.specification.UserSpecs;
import org.fao.geonet.utils.IO;
import org.fao.geonet.utils.Log;
+import org.locationtech.jts.util.Assert;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
-import org.springframework.context.annotation.Bean;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
-import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Service;
-import com.google.common.base.Function;
-import com.google.common.collect.Lists;
-import org.locationtech.jts.util.Assert;
-
-import jeeves.server.UserSession;
-import jeeves.server.context.ServiceContext;
-import jeeves.server.dispatchers.ServiceManager;
+import javax.annotation.Nullable;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
@Service
public class ArchiveAllMetadataJob extends QuartzJobBean {
@@ -122,6 +117,7 @@ public void createBackup(ServiceContext serviceContext) throws Exception {
return;
}
long startTime = System.currentTimeMillis();
+ Path srcFile = null;
try {
Log.info(BACKUP_LOG, "Starting backup of all metadata");
@@ -145,7 +141,7 @@ public String apply(@Nullable Metadata input) {
boolean resolveXlink = true;
boolean removeXlinkAttribute = false;
boolean skipOnError = true;
- Path srcFile = MEFLib.doMEF2Export(serviceContext, new HashSet<>(uuids), format, false, stylePath,
+ srcFile = MEFLib.doMEF2Export(serviceContext, new HashSet<>(uuids), format, false, stylePath,
resolveXlink, removeXlinkAttribute, skipOnError, true, true);
Path backupDir = dataDirectory.getBackupDir().resolve(BACKUP_DIR);
@@ -155,7 +151,7 @@ public String apply(@Nullable Metadata input) {
Files.createDirectories(destFile.getParent());
Files.move(srcFile, destFile);
if (!Files.exists(destFile)) {
- throw new Exception("Moving backup file failed!");
+ throw new Exception("Target file already exists. Moving backup file failed!");
}
long timeMinutes = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - startTime);
Log.info(BACKUP_LOG, "Backup finished. Backup time: " + timeMinutes + " Backup file: " + destFile);
@@ -163,6 +159,9 @@ public String apply(@Nullable Metadata input) {
Log.error(BACKUP_LOG, "Failed to create a back up of metadata", t);
} finally {
backupIsRunning.set(false);
+ if (srcFile != null) {
+ FileUtils.deleteQuietly(srcFile.toFile());
+ }
}
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java
index 512d881061..83c4b8d05d 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataManager.java
@@ -62,6 +62,9 @@ public interface IMetadataManager {
/**
* Removes the record with the id metadataId
+ * from the database and index without sending events.
+ *
+ * This is useful for harvesting tasks.
*
* @param context
* @param metadataId
@@ -69,6 +72,17 @@ public interface IMetadataManager {
*/
void deleteMetadata(ServiceContext context, String metadataId) throws Exception;
+ /**
+ * Delete the record with the id metadataId
+ * from the database and index
+ * and additionally take care of cleaning up resources, send events, ...
+ *
+ * @param context
+ * @param metadataId
+ * @throws Exception
+ */
+ void purgeMetadata(ServiceContext context, String metadataId, boolean withBackup) throws Exception;
+
/**
* Removes a record without notifying.
*
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java
index 89fa4efd6d..96b0aa34ee 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataStatus.java
@@ -100,6 +100,15 @@ public interface IMetadataStatus {
*/
MetadataStatus getStatus(int metadataId) throws Exception;
+ /**
+ * Given a metadata id, return the previous status of the metadata
+ *
+ * @param metadataId
+ * @return
+ * @throws Exception
+ */
+ MetadataStatus getPreviousStatus(int metadataId) throws Exception;
+
/**
* Given a metadata id, return the status of the metadata
*
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java
index 4dc52beb5b..63c3dadb55 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataUtils.java
@@ -23,6 +23,7 @@
package org.fao.geonet.kernel.datamanager;
+import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -31,6 +32,7 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import org.fao.geonet.api.exception.ResourceNotFoundException;
import org.fao.geonet.domain.AbstractMetadata;
import org.fao.geonet.domain.ISODate;
import org.fao.geonet.domain.MetadataSourceInfo;
@@ -39,6 +41,7 @@
import org.fao.geonet.repository.SimpleMetadata;
import org.fao.geonet.repository.reports.MetadataReportsQueries;
import org.jdom.Element;
+import org.jdom.JDOMException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
@@ -118,6 +121,14 @@ public interface IMetadataUtils {
LinkedHashMap extractTitles(@Nonnull String id) throws Exception;
+ String getPermalink(String uuid, String language);
+
+ String getDefaultUrl(String uuid, String language);
+
+ String getDoi(String uuid) throws ResourceNotFoundException, IOException, JDOMException;
+
+ String getResourceIdentifier(String uuid) throws ResourceNotFoundException, JDOMException, IOException;
+
/**
* Extract the last editing date from the record
*
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java
index d0fe5b4e37..70d1532250 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/IMetadataValidator.java
@@ -28,7 +28,6 @@
import org.fao.geonet.domain.AbstractMetadata;
import org.fao.geonet.domain.MetadataValidation;
import org.fao.geonet.domain.Pair;
-import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
@@ -37,7 +36,7 @@
/**
* Interface to handle all operations related to validations of records
- *
+ *
* @author delawen
*
*/
@@ -51,9 +50,9 @@ public interface IMetadataValidator {
void validateExternalMetadata(String schema, Element xml, ServiceContext context, String fileName, Integer groupOwner) throws Exception;
/**
- *
+ *
* if the metadata has no namespace or already has a namespace then we must skip this phase
- *
+ *
* @param md
*/
void setNamespacePrefix(Element md);
@@ -72,9 +71,9 @@ public interface IMetadataValidator {
* Used by harvesters that need to validate metadata.
*
* @param metadata metadata
- * @param lang Language from context
+ * @param lang Language from context
*/
- boolean doValidate(AbstractMetadata metadata, String lang);
+ Pair doValidate(AbstractMetadata metadata, String lang);
/**
* Used by the validate embedded service. The validation report is stored in the session.
@@ -100,7 +99,7 @@ Pair doValidate(UserSession session, String schema, String meta
/**
* Adds the namespace to the element
- *
+ *
* @param md
* @param ns
*/
@@ -108,7 +107,7 @@ Pair doValidate(UserSession session, String schema, String meta
/**
* Helper function to prevent loop on dependencies
- *
+ *
* @param metadataManager
*/
void setMetadataManager(IMetadataManager metadataManager);
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 261d9a442a..09b5c4faa4 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
@@ -410,13 +410,13 @@ public void indexMetadata(final String metadataId,
fields.put(IndexFields.DRAFT, "n");
fields.put(IndexFields.INDEXING_ERROR_FIELD, true);
fields.put(IndexFields.INDEXING_ERROR_MSG, String.format(
- "Schema '%s' is not registerd in this catalog. Install it or remove those records",
+ "Schema '%s' is not registered in this catalog. Install it or remove those records",
schema
));
searchManager.index(null, md, indexKey, fields, metadataType,
forceRefreshReaders, indexingMode);
Log.error(Geonet.DATA_MANAGER, String.format(
- "Record %s / Schema '%s' is not registerd in this catalog. Install it or remove those records. Record is indexed indexing error flag.",
+ "Record %s / Schema '%s' is not registered in this catalog. Install it or remove those records. Record is indexed indexing error flag.",
metadataId, schema));
} else {
@@ -527,7 +527,11 @@ public void indexMetadata(final String metadataId,
// TODO: Check if ignore INSPIRE validation?
if (!type.equalsIgnoreCase("inspire")) {
- if (status == MetadataValidationStatus.INVALID && vi.isRequired()) {
+ // If never validated and required then set status to never validated.
+ if (status == MetadataValidationStatus.NEVER_CALCULATED && vi.isRequired()) {
+ isValid = "-1";
+ }
+ if (status == MetadataValidationStatus.INVALID && vi.isRequired() && isValid != "-1") {
isValid = "0";
}
} else {
@@ -585,6 +589,8 @@ private Multimap buildFieldsForPrivileges(int recordId) {
List operationsAllowed = operationAllowedRepository.findAllById_MetadataId(recordId);
Multimap privilegesFields = ArrayListMultimap.create();
boolean isPublishedToAll = false;
+ boolean isPublishedToIntranet = false;
+ boolean isPublishedToGuest = false;
for (OperationAllowed operationAllowed : operationsAllowed) {
OperationAllowedId operationAllowedId = operationAllowed.getId();
@@ -601,6 +607,10 @@ private Multimap buildFieldsForPrivileges(int recordId) {
if (g.get().getId() == ReservedGroup.all.getId()) {
isPublishedToAll = true;
+ } else if (g.get().getId() == ReservedGroup.intranet.getId()) {
+ isPublishedToIntranet = true;
+ } else if (g.get().getId() == ReservedGroup.guest.getId()) {
+ isPublishedToGuest = true;
}
}
}
@@ -611,6 +621,19 @@ private Multimap buildFieldsForPrivileges(int recordId) {
} else {
privilegesFields.put(Geonet.IndexFieldNames.IS_PUBLISHED_TO_ALL, false);
}
+
+ if (isPublishedToIntranet) {
+ privilegesFields.put(Geonet.IndexFieldNames.IS_PUBLISHED_TO_INTRANET, true);
+ } else {
+ privilegesFields.put(Geonet.IndexFieldNames.IS_PUBLISHED_TO_INTRANET, false);
+ }
+
+ if (isPublishedToGuest) {
+ privilegesFields.put(Geonet.IndexFieldNames.IS_PUBLISHED_TO_GUEST, true);
+ } else {
+ privilegesFields.put(Geonet.IndexFieldNames.IS_PUBLISHED_TO_GUEST, false);
+ }
+
return privilegesFields;
}
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 cf8a38e6a7..61ba225106 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
@@ -32,23 +32,27 @@
import jeeves.transaction.TransactionManager;
import jeeves.transaction.TransactionTask;
import jeeves.xlink.Processor;
-import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
+import org.fao.geonet.api.records.attachments.Store;
import org.fao.geonet.constants.Edit;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.constants.Params;
import org.fao.geonet.domain.*;
+import org.fao.geonet.events.history.RecordDeletedEvent;
+import org.fao.geonet.events.md.MetadataPreRemove;
+import org.fao.geonet.exceptions.UnAuthorizedException;
import org.fao.geonet.kernel.*;
import org.fao.geonet.kernel.datamanager.*;
+import org.fao.geonet.kernel.mef.MEFLib;
import org.fao.geonet.kernel.schema.MetadataSchema;
import org.fao.geonet.kernel.schema.SchemaPlugin;
import org.fao.geonet.kernel.search.EsSearchManager;
import org.fao.geonet.kernel.search.IndexingMode;
-import org.fao.geonet.kernel.search.MetaSearcher;
import org.fao.geonet.kernel.search.index.BatchOpsMetadataReindexer;
import org.fao.geonet.kernel.setting.SettingManager;
import org.fao.geonet.kernel.setting.Settings;
+import org.fao.geonet.kernel.setting.SettingInfo;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.repository.*;
import org.fao.geonet.repository.specification.MetadataFileUploadSpecs;
@@ -289,14 +293,9 @@ protected void deleteMetadataFromDB(ServiceContext context, String id) throws Ex
AbstractMetadata metadata = metadataUtils.findOne(Integer.valueOf(id));
if (!settingManager.getValueAsBool(Settings.SYSTEM_XLINK_ALLOW_REFERENCED_DELETION)
&& metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE) {
-
-// TODOES
- throw new NotImplementedException("SYSTEM_XLINK_ALLOW_REFERENCED_DELETION not implemented in ES.");
-// MetaSearcher searcher = searcherForReferencingMetadata(context, metadata);
-// Map result = ((LuceneSearcher) searcher).getAllMdInfo(context, 1);
-// if (result.size() > 0) {
-// throw new Exception("this template is referenced.");
-// }
+ if (this.hasReferencingMetadata(context, metadata)) {
+ throw new UnAuthorizedException("This template is referenced.", metadata);
+ }
}
// --- remove operations
@@ -322,22 +321,6 @@ public javax.persistence.criteria.Path getPath(Root
getXmlSerializer().delete(id, context);
}
- private MetaSearcher searcherForReferencingMetadata(ServiceContext context, AbstractMetadata metadata)
- throws Exception {
- // TODOES
- throw new NotImplementedException("searcherForReferencingMetadata not implemented in ES.");
-// MetaSearcher searcher = context.getBean(SearchManager.class).newSearcher(SearcherType.LUCENE,
-// Geonet.File.SEARCH_LUCENE);
-// Element parameters = new Element(Jeeves.Elem.REQUEST);
-// parameters.addContent(new Element(Geonet.IndexFieldNames.XLINK).addContent("*" + metadata.getUuid() + "*"));
-// parameters.addContent(new Element(Geonet.SearchResult.BUILD_SUMMARY).setText("false"));
-// parameters.addContent(new Element(SearchParameter.ISADMIN).addContent("true"));
-// parameters.addContent(new Element(SearchParameter.ISTEMPLATE).addContent("y or n"));
-// ServiceConfig config = new ServiceConfig();
-// searcher.search(context, parameters, config);
-// return searcher;
- }
-
// --------------------------------------------------------------------------
// ---
// --- Metadata thumbnail API
@@ -366,6 +349,36 @@ public void deleteMetadata(ServiceContext context, String metadataId) throws Exc
// _entityManager.clear();
}
+ /**
+ * Delete the record with the id metadataId and additionally take care of cleaning up resources, send events, ...
+ */
+ @Override
+ public void purgeMetadata(ServiceContext context, String metadataId, boolean withBackup) throws Exception {
+ AbstractMetadata metadata = metadataUtils.findOne(metadataId);
+ Store store = context.getBean("resourceStore", Store.class);
+
+ MetadataPreRemove preRemoveEvent = new MetadataPreRemove(metadata);
+ ApplicationContextHolder.get().publishEvent(preRemoveEvent);
+
+ if (metadata.getDataInfo().getType() != MetadataType.SUB_TEMPLATE
+ && metadata.getDataInfo().getType() != MetadataType.TEMPLATE_OF_SUB_TEMPLATE && withBackup) {
+ MEFLib.backupRecord(metadata, context);
+ }
+
+ boolean approved = true;
+ if (metadata instanceof MetadataDraft) {
+ approved = false;
+ }
+
+ store.delResources(context, metadata.getUuid(), approved);
+
+ RecordDeletedEvent recordDeletedEvent = new RecordDeletedEvent(
+ metadata.getId(), metadata.getUuid(), new LinkedHashMap<>(),
+ context.getUserSession().getUserIdAsInt(), metadata.getData());
+ deleteMetadata(context, metadataId);
+ recordDeletedEvent.publish(ApplicationContextHolder.get());
+ }
+
/**
* @param context
* @param metadataId
@@ -393,8 +406,7 @@ public String createMetadata(ServiceContext context, String templateId, String g
}
/**
- * Creates a new metadata duplicating an existing template with an specified
- * uuid.
+ * Creates a new metadata duplicating an existing template with a specified uuid.
*
* @param isTemplate
* @param fullRightsForGroup
@@ -421,7 +433,7 @@ public String createMetadata(ServiceContext context, String templateId, String g
xml = duplicateMetadata(schema, xml, context);
} else if (type == MetadataType.SUB_TEMPLATE
- || type == MetadataType.TEMPLATE_OF_SUB_TEMPLATE) {
+ || type == MetadataType.TEMPLATE_OF_SUB_TEMPLATE) {
xml.setAttribute("uuid", uuid);
}
@@ -515,7 +527,7 @@ private void setMetadataTitle(String schema, Element xml, String language, boole
* @param createDate date of creation
* @param changeDate date of modification
* @param ufo whether to apply automatic changes
- * @param indexingMode whether to index this metadata
+ * @param indexingMode whether to index this metadata
* @return id, as a string
* @throws Exception hmm
*/
@@ -717,7 +729,7 @@ public void apply(@Nonnull Metadata entity) {
*/
@Override
public synchronized AbstractMetadata updateMetadata(final ServiceContext context, final String metadataId, final Element md,
- final boolean validate, final boolean ufo, final String lang, final String changeDate,
+ final boolean validate, final boolean ufo, final String lang, String changeDate,
final boolean updateDateStamp, final IndexingMode indexingMode) throws Exception {
Log.trace(Geonet.DATA_MANAGER, "Update record with id " + metadataId);
@@ -730,13 +742,22 @@ public synchronized AbstractMetadata updateMetadata(final ServiceContext context
}
String schema = metadataSchemaUtils.getMetadataSchema(metadataId);
+ final AbstractMetadata metadata = metadataUtils.findOne(metadataId);
+
+ if (updateDateStamp) {
+ if (StringUtils.isEmpty(changeDate)) {
+ changeDate = new ISODate().toString();
+ metadata.getDataInfo().setChangeDate(new ISODate());
+ } else {
+ metadata.getDataInfo().setChangeDate(new ISODate(changeDate));
+ }
+ }
+
String uuidBeforeUfo = null;
if (ufo) {
String parentUuid = null;
Integer intId = Integer.valueOf(metadataId);
- final AbstractMetadata metadata = metadataUtils.findOne(metadataId);
-
uuidBeforeUfo = findUuid(metadataXml, schema, metadata);
metadataXml = updateFixedInfo(schema, Optional.of(intId), uuidBeforeUfo, metadataXml, parentUuid,
@@ -746,9 +767,6 @@ public synchronized AbstractMetadata updateMetadata(final ServiceContext context
// --- force namespace prefix for iso19139 metadata
setNamespacePrefixUsingSchemas(schema, metadataXml);
- // Notifies the metadata change to metatada notifier service
- final AbstractMetadata metadata = metadataUtils.findOne(metadataId);
-
String uuid = findUuid(metadataXml, schema, metadata);
metadataUtils.checkMetadataWithSameUuidExist(uuid, metadata.getId());
@@ -896,15 +914,8 @@ private Element buildInfoElem(ServiceContext context, String id, String version)
}
// add baseUrl of this site (from settings)
- String protocol = settingManager.getValue(Settings.SYSTEM_SERVER_PROTOCOL);
- String host = settingManager.getValue(Settings.SYSTEM_SERVER_HOST);
- String port = settingManager.getValue(Settings.SYSTEM_SERVER_PORT);
- if (port.equals("80")) {
- port = "";
- } else {
- port = ":" + port;
- }
- addElement(info, Edit.Info.Elem.BASEURL, protocol + "://" + host + port + context.getBaseUrl());
+ SettingInfo si = new SettingInfo();
+ addElement(info, Edit.Info.Elem.BASEURL, si.getSiteUrl() + context.getBaseUrl());
addElement(info, Edit.Info.Elem.LOCSERV, "/srv/en");
return info;
}
@@ -951,8 +962,7 @@ public Element updateFixedInfo(String schema, Optional metadataId, Stri
if (metadata != null) {
changeDate = metadata.getDataInfo().getChangeDate().getDateAndTime();
createDate = metadata.getDataInfo().getCreateDate().getDateAndTime();
- }
- else {
+ } else {
createDate = new ISODate().toString();
}
env.addContent(new Element("changeDate").setText(changeDate));
@@ -1007,8 +1017,8 @@ public Element updateFixedInfo(String schema, Optional metadataId, Stri
Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(
metadata != null
&& (
- metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE
- || metadata.getDataInfo().getType() == MetadataType.TEMPLATE_OF_SUB_TEMPLATE)?
+ metadata.getDataInfo().getType() == MetadataType.SUB_TEMPLATE
+ || metadata.getDataInfo().getType() == MetadataType.TEMPLATE_OF_SUB_TEMPLATE) ?
Geonet.File.UPDATE_FIXED_INFO_SUBTEMPLATE :
Geonet.File.UPDATE_FIXED_INFO);
result = Xml.transform(result, styleSheet);
@@ -1222,13 +1232,13 @@ private void setNamespacePrefixUsingSchemas(String schema, Element md) throws Ex
* Applies a xslt process when duplicating a metadata, typically to remove identifiers
* or other information like DOI (Digital Object Identifiers) and returns the updated metadata.
*
- * @param schema Metadata schema.
- * @param md Metadata to duplicate.
+ * @param schema Metadata schema.
+ * @param md Metadata to duplicate.
* @param srvContext
- * @return If the xslt process exists, the metadata processed, otherwise the original metadata.
+ * @return If the xslt process exists, the metadata processed, otherwise the original metadata.
* @throws Exception
*/
- private Element duplicateMetadata(String schema, Element md, ServiceContext srvContext) throws Exception {
+ private Element duplicateMetadata(String schema, Element md, ServiceContext srvContext) throws Exception {
Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(
Geonet.File.DUPLICATE_METADATA);
@@ -1329,4 +1339,10 @@ public boolean isValid(Integer id) {
}
return true;
}
+
+ 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;
+ }
+
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java
index ee0300922f..150bd65a81 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataStatus.java
@@ -96,6 +96,21 @@ public MetadataStatus getStatus(int metadataId) throws Exception {
}
}
+ /**
+ * Return previous workflow status for the metadata id
+ */
+ @Override
+ public MetadataStatus getPreviousStatus(int metadataId) throws Exception {
+ String sortField = SortUtils.createPath(MetadataStatus_.id, MetadataStatus_.changeDate);
+ List metadataStatusList = metadataStatusRepository.findAllByMetadataIdAndByType(
+ metadataId, StatusValueType.workflow, Sort.by(Sort.Direction.DESC, sortField));
+ if (metadataStatusList.isEmpty() || metadataStatusList.size() == 1) {
+ return null;
+ } else {
+ return metadataStatusList.get(1);
+ }
+ }
+
/**
* Return all status for the metadata id
*/
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java
index 1baae5cb26..64ad3fc04f 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataUtils.java
@@ -27,8 +27,10 @@
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
import org.apache.commons.lang.NotImplementedException;
+import org.apache.commons.lang.StringUtils;
import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.NodeInfo;
+import org.fao.geonet.api.exception.ResourceNotFoundException;
import org.fao.geonet.constants.Edit;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.domain.*;
@@ -36,6 +38,8 @@
import org.fao.geonet.kernel.SchemaManager;
import org.fao.geonet.kernel.XmlSerializer;
import org.fao.geonet.kernel.datamanager.*;
+import org.fao.geonet.kernel.schema.MetadataSchema;
+import org.fao.geonet.kernel.schema.SavedQuery;
import org.fao.geonet.kernel.search.EsSearchManager;
import org.fao.geonet.kernel.search.IndexingMode;
import org.fao.geonet.kernel.search.index.IndexingList;
@@ -47,7 +51,9 @@
import org.fao.geonet.repository.specification.OperationAllowedSpecs;
import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
+import org.fao.geonet.web.DefaultLanguage;
import org.jdom.Element;
+import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
@@ -60,9 +66,12 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.PostConstruct;
+import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
+import java.util.regex.Pattern;
+import static org.fao.geonet.kernel.setting.Settings.*;
import static org.fao.geonet.repository.specification.MetadataSpecs.hasMetadataUuid;
public class BaseMetadataUtils implements IMetadataUtils {
@@ -79,6 +88,10 @@ public class BaseMetadataUtils implements IMetadataUtils {
protected SchemaManager schemaManager;
@Autowired
protected MetadataRatingByIpRepository ratingByIpRepository;
+
+ @Autowired
+ protected LanguageRepository languageRepository;
+
@Autowired
@Lazy
protected SettingManager settingManager;
@@ -95,6 +108,9 @@ public class BaseMetadataUtils implements IMetadataUtils {
@Autowired(required = false)
protected XmlSerializer xmlSerializer;
+ @Autowired
+ private DefaultLanguage defaultLanguage;
+
private Path stylePath;
protected IMetadataManager metadataManager;
@@ -249,15 +265,15 @@ public String extractUUID(String schema, Element md) throws Exception {
@Override
public String extractDefaultLanguage(String schema, Element md) throws Exception {
Path styleSheet = metadataSchemaUtils.getSchemaDir(schema).resolve(Geonet.File.EXTRACT_DEFAULT_LANGUAGE);
- String defaultLanguage = Xml.transform(md, styleSheet).getText().trim();
+ String defaultLanguageValue = Xml.transform(md, styleSheet).getText().trim();
if (Log.isDebugEnabled(Geonet.DATA_MANAGER))
- Log.debug(Geonet.DATA_MANAGER, "Extracted default language '" + defaultLanguage + "' for schema '" + schema + "'");
+ Log.debug(Geonet.DATA_MANAGER, "Extracted default language '" + defaultLanguageValue + "' for schema '" + schema + "'");
// --- needed to detach md from the document
md.detach();
- return defaultLanguage;
+ return defaultLanguageValue;
}
/**
@@ -308,6 +324,79 @@ public LinkedHashMap extractTitles(@Nonnull String id) throws Ex
return null;
}
+ @Override
+ public String getPermalink(String uuid, String language) {
+ Boolean doiIsFirst = settingManager.getValueAsBool(METADATA_URL_SITEMAPDOIFIRST, false);
+
+ if (Boolean.TRUE.equals(doiIsFirst)) {
+ String doi = null;
+ try {
+ doi = getDoi(uuid);
+ } catch (Exception e) {
+ // DOI not supported for schema
+ }
+ if (StringUtils.isNotEmpty(doi)) {
+ return doi;
+ }
+ }
+
+
+ String sitemapLinkUrl = settingManager.getValue(METADATA_URL_SITEMAPLINKURL);
+ String defaultLink = settingManager.getNodeURL() + "api/records/" + uuid + "?language=all";
+ String permalink = buildUrl(uuid, language, sitemapLinkUrl);
+ return StringUtils.isNotEmpty(permalink) ? permalink : defaultLink;
+ }
+
+ @Override
+ public String getDefaultUrl(String uuid, String language) {
+ String dynamicAppLinkUrl = settingManager.getValue(METADATA_URL_DYNAMICAPPLINKURL);
+ if ("all".equals(language)) {
+ language = defaultLanguage.getLanguage();
+ }
+ String defaultLink = settingManager.getNodeURL() + language + "/catalog.search#/metadata/" + uuid;
+ String url = buildUrl(uuid, language, dynamicAppLinkUrl);
+ return StringUtils.isNotEmpty(url) ? url : defaultLink;
+ }
+
+ private String buildUrl(String uuid, String language, String url) {
+ if (StringUtils.isNotEmpty(url)) {
+ Map substitutions = new HashMap<>();
+ substitutions.put("{{UUID}}", uuid);
+ substitutions.put("{{LANG}}", StringUtils.isEmpty(language) ? "" : language);
+ try {
+ String resourceId = getResourceIdentifier(uuid);
+ substitutions.put("{{RESOURCEID}}", StringUtils.isEmpty(resourceId) ? "" : resourceId);
+ } catch (Exception e) {
+ // No resource identifier xpath defined in schema
+ }
+ for (Map.Entry s : substitutions.entrySet()) {
+ if (url.toUpperCase().contains(s.getKey())) {
+ url = url.replaceAll("(?i)" + Pattern.quote(s.getKey()), s.getValue());
+ }
+ }
+ }
+ return url;
+ }
+
+
+ @Override
+ public String getDoi(String uuid) throws ResourceNotFoundException, IOException, JDOMException {
+ AbstractMetadata metadata = findOneByUuid(uuid);
+ final MetadataSchema schema = metadataSchemaUtils
+ .getSchema(metadata.getDataInfo().getSchemaId());
+ Element xml = metadata.getXmlData(false);
+ return schema.queryString(SavedQuery.DOI_GET, xml);
+ }
+
+ @Override
+ public String getResourceIdentifier(String uuid) throws ResourceNotFoundException, JDOMException, IOException {
+ AbstractMetadata metadata = findOneByUuid(uuid);
+ final MetadataSchema schema = metadataSchemaUtils
+ .getSchema(metadata.getDataInfo().getSchemaId());
+ Element xml = metadata.getXmlData(false);
+ return schema.queryString(SavedQuery.RESOURCEID_GET, xml);
+ }
+
/**
* @param schema
* @param md
@@ -425,12 +514,9 @@ public void setTemplate(final int id, final MetadataType type, final String titl
@Override
public void setTemplateExt(final int id, final MetadataType metadataType) throws Exception {
- metadataRepository.update(id, new Updater() {
- @Override
- public void apply(@Nonnull Metadata metadata) {
- final MetadataDataInfo dataInfo = metadata.getDataInfo();
- dataInfo.setType(metadataType);
- }
+ metadataRepository.update(id, metadata -> {
+ final MetadataDataInfo dataInfo = metadata.getDataInfo();
+ dataInfo.setType(metadataType);
});
}
@@ -450,14 +536,11 @@ public void setHarvested(int id, String harvestUuid) throws Exception {
*/
@Override
public void setSubtemplateTypeAndTitleExt(final int id, String title) throws Exception {
- metadataRepository.update(id, new Updater() {
- @Override
- public void apply(@Nonnull Metadata metadata) {
- final MetadataDataInfo dataInfo = metadata.getDataInfo();
- dataInfo.setType(MetadataType.SUB_TEMPLATE);
- if (title != null) {
- dataInfo.setTitle(title);
- }
+ metadataRepository.update(id, metadata -> {
+ final MetadataDataInfo dataInfo = metadata.getDataInfo();
+ dataInfo.setType(MetadataType.SUB_TEMPLATE);
+ if (title != null) {
+ dataInfo.setTitle(title);
}
});
}
@@ -470,14 +553,11 @@ public void setHarvestedExt(int id, String harvestUuid) throws Exception {
@Override
public void setHarvestedExt(final int id, final String harvestUuid, final Optional harvestUri)
throws Exception {
- metadataRepository.update(id, new Updater() {
- @Override
- public void apply(Metadata metadata) {
- MetadataHarvestInfo harvestInfo = metadata.getHarvestInfo();
- harvestInfo.setUuid(harvestUuid);
- harvestInfo.setHarvested(harvestUuid != null);
- harvestInfo.setUri(harvestUri.orNull());
- }
+ metadataRepository.update(id, metadata -> {
+ MetadataHarvestInfo harvestInfo = metadata.getHarvestInfo();
+ harvestInfo.setUuid(harvestUuid);
+ harvestInfo.setHarvested(harvestUuid != null);
+ harvestInfo.setUri(harvestUri.orNull());
});
}
@@ -488,12 +568,7 @@ public void apply(Metadata metadata) {
*/
@Override
public void updateDisplayOrder(final String id, final String displayOrder) throws Exception {
- metadataRepository.update(Integer.valueOf(id), new Updater() {
- @Override
- public void apply(Metadata entity) {
- entity.getDataInfo().setDisplayOrder(Integer.parseInt(displayOrder));
- }
- });
+ metadataRepository.update(Integer.valueOf(id), entity -> entity.getDataInfo().setDisplayOrder(Integer.parseInt(displayOrder)));
}
/**
@@ -504,14 +579,9 @@ public void increasePopularity(ServiceContext srvContext, String id) throws Exce
// READONLYMODE
if (!srvContext.getBean(NodeInfo.class).isReadOnly()) {
int iId = Integer.parseInt(id);
- metadataRepository.update(iId, new Updater() {
- @Override
- public void apply(Metadata entity) {
- entity.getDataInfo().setPopularity(
- entity.getDataInfo().getPopularity() + 1
- );
- }
- });
+ metadataRepository.update(iId, entity -> entity.getDataInfo().setPopularity(
+ entity.getDataInfo().getPopularity() + 1
+ ));
final java.util.Optional metadata = metadataRepository.findById(iId);
if (metadata.isPresent()) {
@@ -551,12 +621,7 @@ public int rateMetadata(final int metadataId, final String ipAddress, final int
if (Log.isDebugEnabled(Geonet.DATA_MANAGER))
Log.debug(Geonet.DATA_MANAGER, "Setting rating for id:" + metadataId + " --> rating is:" + newRating);
- metadataRepository.update(metadataId, new Updater() {
- @Override
- public void apply(Metadata entity) {
- entity.getDataInfo().setRating(newRating);
- }
- });
+ metadataRepository.update(metadataId, entity -> entity.getDataInfo().setRating(newRating));
// And register the metadata to be indexed in the near future
indexingList.add(metadataId);
@@ -583,7 +648,7 @@ public Element removeMetadataInfo(Element md) throws Exception {
// Drop Geonet namespace declaration. It may be contained
// multiple times, so loop on all.
- final List additionalNamespaces = new ArrayList(md.getAdditionalNamespaces());
+ final List additionalNamespaces = new ArrayList<>(md.getAdditionalNamespaces());
for (Namespace n : additionalNamespaces) {
if (Edit.NAMESPACE.getURI().equals(n.getURI())) {
md.removeNamespaceDeclaration(Edit.NAMESPACE);
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java
index 6f80c38156..96a3015cad 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/base/BaseMetadataValidator.java
@@ -65,6 +65,11 @@
import org.springframework.context.annotation.Lazy;
import org.springframework.transaction.annotation.Transactional;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
@@ -359,14 +364,17 @@ private Element getSchemaTronXmlReport(String schema, Element md, String lang, M
* @param lang Language from context
*/
@Override
- public boolean doValidate(AbstractMetadata metadata, String lang) {
+ public Pair doValidate(AbstractMetadata metadata, String lang) {
String schema = metadata.getDataInfo().getSchemaId();
int metadataId = metadata.getId();
+ Element errorReport = new Element("report", Edit.NAMESPACE);
+ errorReport.setAttribute("id", String.valueOf(metadataId), Edit.NAMESPACE);
+
Element md;
try {
md = metadata.getXmlData(false);
} catch (IOException | JDOMException e) {
- return false;
+ return Pair.read(errorReport, false);
}
List validations = new ArrayList<>();
@@ -381,15 +389,19 @@ public boolean doValidate(AbstractMetadata metadata, String lang) {
xsdErrorCount = xsdErrors.getContent().size();
}
if (xsdErrorCount > 0) {
+ errorReport.addContent(xsdErrors);
validations.add(new MetadataValidation().setId(new MetadataValidationId(metadataId, "xsd"))
.setStatus(MetadataValidationStatus.INVALID).setRequired(true).setNumTests(xsdErrorCount)
.setNumFailures(xsdErrorCount));
- LOGGER.debug("Invalid.");
+
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(" - XSD error: {}", Xml.getString(xsdErrors));
+ }
valid = false;
} else {
validations.add(new MetadataValidation().setId(new MetadataValidationId(metadataId, "xsd"))
.setStatus(MetadataValidationStatus.VALID).setRequired(true).setNumTests(1).setNumFailures(0));
- LOGGER.debug("Valid.");
+ LOGGER.debug(" - XSD Valid.");
}
try {
metadataManager.getEditLib().enumerateTree(md);
@@ -411,6 +423,10 @@ public boolean doValidate(AbstractMetadata metadata, String lang) {
if (failedAssert.size() > 0 || failedSchematronVerification.size() > 0) {
valid = false;
+ errorReport.addContent(schemaTronReport);
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug(" - Schematron error: {}", Xml.getString(schemaTronReport));
+ }
}
}
} catch (Exception e) {
@@ -423,7 +439,7 @@ public boolean doValidate(AbstractMetadata metadata, String lang) {
saveValidationStatus(metadataId, validations);
- return valid;
+ return Pair.read(errorReport, valid);
}
/**
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java
index d3982a7583..65d3a794f1 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataIndexer.java
@@ -86,17 +86,17 @@ protected Multimap addExtraFields(AbstractMetadata fullMd) {
List statuses = statusRepository.findAllByMetadataIdAndByType(fullMd.getId(), StatusValueType.workflow, statusSort);
if (!statuses.isEmpty()) {
MetadataStatus stat = statuses.get(0);
- status = String.valueOf(stat.getStatusValue().getId());
+ status = String.valueOf(stat.getStatusValue().getName());
}
// get status of draft
statuses = statusRepository.findAllByMetadataIdAndByType(metadataDraft.getId(), StatusValueType.workflow, statusSort);
if (!statuses.isEmpty()) {
MetadataStatus stat = statuses.get(0);
- statusDraft = String.valueOf(stat.getStatusValue().getId());
+ statusDraft = String.valueOf(stat.getStatusValue().getName());
}
- extraFields.put(Geonet.IndexFieldNames.STATUS_WORKFLOW, status + statusDraft);
+ extraFields.put(Geonet.IndexFieldNames.STATUS_WORKFLOW, status + "-" + statusDraft);
} else {
Log.trace(Geonet.DATA_MANAGER,
@@ -109,8 +109,8 @@ protected Multimap addExtraFields(AbstractMetadata fullMd) {
List statuses = statusRepository.findAllByMetadataIdAndByType(fullMd.getId(), StatusValueType.workflow, statusSort);
if (!statuses.isEmpty()) {
MetadataStatus stat = statuses.get(0);
- String status = String.valueOf(stat.getStatusValue().getId());
- extraFields.put(Geonet.IndexFieldNames.STATUS_WORKFLOW, status);
+ String status = String.valueOf(stat.getStatusValue().getName());
+ extraFields.put(Geonet.IndexFieldNames.STATUS_WORKFLOW, status);
}
}
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java
index 9872a5476b..07db4799fe 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataManager.java
@@ -93,6 +93,7 @@ public void deleteMetadata(ServiceContext context, String metadataId) throws Exc
// _entityManager.flush();
// _entityManager.clear();
}
+
/**
* For update of owner info.
*/
diff --git a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java
index a71d64f731..040acbf4ac 100644
--- a/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java
+++ b/core/src/main/java/org/fao/geonet/kernel/datamanager/draft/DraftMetadataUtils.java
@@ -24,6 +24,7 @@
package org.fao.geonet.kernel.datamanager.draft;
import com.google.common.base.Optional;
+import com.google.common.collect.Sets;
import jeeves.server.context.ServiceContext;
import org.eclipse.jetty.io.RuntimeIOException;
import org.fao.geonet.api.records.attachments.StoreUtils;
@@ -88,6 +89,7 @@ public class DraftMetadataUtils extends BaseMetadataUtils {
IMetadataUtils metadataUtils;
private ServiceContext context;
+ private Set listOfStatusToTriggerDraftCreation = Sets.newHashSet(StatusValue.Status.APPROVED);
public void init(ServiceContext context, Boolean force) throws Exception {
this.context = context;
@@ -235,7 +237,7 @@ public AbstractMetadata findOne(int id) {
java.util.Optional md = metadataDraftRepository.findById(id);
- return md.isPresent()?md.get():null;
+ return md.isPresent() ? md.get() : null;
}
@Override
@@ -407,7 +409,7 @@ public List findAllIdsBy(Specification extends AbstractMetadata> spec
/**
* Start an editing session. This will record the original metadata record in
* the session under the
- * {@link org.fao.geonet.constants.Geonet.Session#METADATA_BEFORE_ANY_CHANGES} +
+ * {@link Geonet.Session#METADATA_BEFORE_ANY_CHANGES} +
* id session property.
*
* The record contains geonet:info element.
@@ -433,7 +435,9 @@ public Integer startEditingSession(ServiceContext context, String id) throws Exc
Log.trace(Geonet.DATA_MANAGER, "Editing draft with id " + id);
} else if (isMdWorkflowEnable
&& (context.getBean(IMetadataManager.class) instanceof DraftMetadataManager)
- && metadataStatus.getCurrentStatus(Integer.valueOf(id)).equals(StatusValue.Status.APPROVED)) {
+ && listOfStatusToTriggerDraftCreation.contains(
+ metadataStatus.getCurrentStatus(Integer.parseInt(id)))
+ ) {
id = createDraft(context, id, md);
Log.trace(Geonet.DATA_MANAGER, "Creating draft with id " + id + " to edit.");
@@ -568,6 +572,10 @@ protected String createDraft(ServiceContext context, String templateId, String g
Integer status = Integer.valueOf(StatusValue.Status.DRAFT);
java.util.Optional statusValue = statusValueRepository.findById(status);
+ String lang = context.getLanguage();
+ ResourceBundle messages = ResourceBundle.getBundle("org.fao.geonet.api.Messages",
+ new Locale(lang));
+
if (statusValue.isPresent()) {
for (Integer mdId : metadataIds) {
MetadataStatus metadataStatus = new MetadataStatus();
@@ -576,7 +584,7 @@ protected String createDraft(ServiceContext context, String templateId, String g
metadataStatus.setChangeDate(new ISODate());
metadataStatus.setUserId(author);
metadataStatus.setStatusValue(statusValue.get());
- metadataStatus.setChangeMessage("Editing instance created");
+ metadataStatus.setChangeMessage(messages.getString("metadata_status_editing_instance_created_text"));
metadataStatus.setTitles(metadataUtils.extractTitles(newMetadata.getDataInfo().getSchemaId(), xml));
List listOfStatusChange = new ArrayList<>(1);
@@ -607,13 +615,13 @@ public void cloneFiles(AbstractMetadata original, AbstractMetadata dest) {
@Override
public void replaceFiles(AbstractMetadata original, AbstractMetadata dest) {
try {
- boolean oldApproved=true;
- boolean newApproved=false;
+ boolean oldApproved = true;
+ boolean newApproved = false;
// If destination is approved then this is a working copy so the original will not be approved.
if (metadataUtils.isMetadataApproved(dest.getId())) {
- oldApproved=false;
- newApproved=true;
+ oldApproved = false;
+ newApproved = true;
}
StoreUtils.replaceDataDir(context, original.getUuid(), dest.getUuid(), oldApproved, newApproved);
cloneStoreFileUploadRequests(original, dest);
@@ -643,7 +651,7 @@ public void cancelEditingSession(ServiceContext context, String id) throws Excep
// --- remove metadata
xmlSerializer.delete(id, ServiceContext.get());
- searchManager.delete(id);
+ searchManager.delete(String.format("+id:%s", id));
// Unset METADATA_EDITING_CREATED_DRAFT flag
context.getUserSession().removeProperty(Geonet.Session.METADATA_EDITING_CREATED_DRAFT);
@@ -674,4 +682,11 @@ private void cloneStoreFileUploadRequests(AbstractMetadata original, AbstractMet
}
}
+ public void setListOfStatusCreatingDraft(Set listOfStatusCreatingDraft) {
+ this.listOfStatusToTriggerDraftCreation = listOfStatusCreatingDraft;
+ }
+
+ public Set getListOfStatusCreatingDraft() {
+ return listOfStatusToTriggerDraftCreation;
+ }
}
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 f2caa9191c..77f7d18f00 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
@@ -45,6 +45,7 @@
import org.fao.geonet.kernel.datamanager.IMetadataValidator;
import org.fao.geonet.kernel.search.IndexingMode;
import org.fao.geonet.kernel.setting.SettingManager;
+import org.fao.geonet.kernel.setting.Settings;
import org.fao.geonet.repository.*;
import org.fao.geonet.utils.FilePathChecker;
import org.fao.geonet.utils.Log;
@@ -65,7 +66,7 @@
public class Importer {
@Deprecated
public static List doImport(final Element params, final ServiceContext context, final Path mefFile,
- final Path stylePath) throws Exception {
+ final Path stylePath) throws Exception {
String fileType = Util.getParam(params, "file_type", "mef");
String style = Util.getParam(params, Params.STYLESHEET, "_none_");
String uuidAction = Util.getParam(params, Params.UUID_ACTION, Params.NOTHING);
@@ -81,8 +82,8 @@ public static List doImport(final Element params, final ServiceContext c
}
public static List doImport(String fileType, final MEFLib.UuidAction uuidAction, final String style, final String source,
- final MetadataType isTemplate, final String[] category, final String groupId, final boolean validate, final boolean assign,
- final ServiceContext context, final Path mefFile) throws Exception {
+ final MetadataType isTemplate, final String[] category, final String groupId, final boolean validate, final boolean assign,
+ final ServiceContext context, final Path mefFile) throws Exception {
ApplicationContext applicationContext = ApplicationContextHolder.get();
final DataManager dm = applicationContext.getBean(DataManager.class);
final SettingManager sm = applicationContext.getBean(SettingManager.class);
@@ -252,8 +253,7 @@ public void handleInfo(Element info, int index) throws Exception {
FilePathChecker.verify(style);
final GeonetworkDataDirectory dataDirectory = applicationContext.getBean(GeonetworkDataDirectory.class);
- Path stylePath = dataDirectory.getWebappDir().resolve(Geonet.Path.IMPORT_STYLESHEETS);
- Path xsltPath = stylePath.resolve(style + ".xsl");
+ Path xsltPath = dataDirectory.getXsltConversion(style);
if (Files.exists(xsltPath)) {
md.add(index, Xml.transform(md.get(index), xsltPath));
} else {
@@ -480,8 +480,8 @@ public static void addCategoriesToMetadata(AbstractMetadata metadata, Element fi
}
public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List md, String schema, int index, String source,
- String sourceName, Map sourceTranslations, ServiceContext context, List id, String createDate,
- String changeDate, String groupId, MetadataType isTemplate) throws Exception {
+ String sourceName, Map sourceTranslations, ServiceContext context, List id, String createDate,
+ String changeDate, String groupId, MetadataType isTemplate) throws Exception {
GeonetContext gc = (GeonetContext) context.getHandlerContext(Geonet.CONTEXT_NAME);
DataManager dm = gc.getBean(DataManager.class);
@@ -513,31 +513,68 @@ public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List<
}
}
- try {
- if (dm.existsMetadataUuid(uuid) && uuidAction != MEFLib.UuidAction.NOTHING) {
- // user has privileges to replace the existing metadata
+ boolean metadataExist = dm.existsMetadataUuid(uuid);
+
+ SettingManager settingManager = gc.getBean(SettingManager.class);
+ boolean isMdWorkflowEnable = settingManager.getValueAsBool(Settings.METADATA_WORKFLOW_ENABLE);
+
+ 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){
+ if (isMdWorkflowEnable) {
+ throw new UnAuthorizedException("Overwrite mode is not allowed when workflow is enabled. Use the metadata editor.", null);
+ }
+
+ String recordToUpdateId = dm.getMetadataId(uuid);
+ if (dm.getAccessManager().canEdit(context, recordToUpdateId)) {
+ MetadataValidationRepository metadataValidationRepository =
+ context.getBean(MetadataValidationRepository.class);
+ List validationStatus = metadataValidationRepository
+ .findAllById_MetadataId(Integer.parseInt(recordToUpdateId));
+
+ // Refresh validation status if set
+ boolean validate = !validationStatus.isEmpty();
+ metadataManager.updateMetadata(
+ context, recordToUpdateId, md.get(index),
+ validate, true,
+ context.getLanguage(),
+ null, true, IndexingMode.full);
+ metadataId = recordToUpdateId;
+ } else {
+ throw new UnAuthorizedException("User has no privilege to overwrite existing metadata", null);
+ }
+ } 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);
+ }
+
+ try {
if (dm.getAccessManager().canEdit(context, dm.getMetadataId(uuid))) {
if (Log.isDebugEnabled(Geonet.MEF)) {
Log.debug(Geonet.MEF, "Deleting existing metadata with UUID : " + uuid);
}
metadataManager.deleteMetadata(context, dm.getMetadataId(uuid));
metadataManager.flush();
- }
- // user does not hav privileges to replace the existing metadata
- else {
+ } else {
throw new UnAuthorizedException("User has no privilege to replace existing metadata", null);
}
+ } catch (Exception e) {
+ throw new Exception(" Existing metadata with UUID " + uuid + " could not be deleted. Error is: " + e.getMessage());
}
- } catch (Exception e) {
- throw new Exception(" Existing metadata with UUID " + uuid + " could not be deleted. Error is: " + e.getMessage());
+ metadataId = insertMetadata(uuid, md, schema, index, source, context, createDate, changeDate, groupId, isTemplate, dm, metadataManager);
+ } else {
+ metadataId = insertMetadata(uuid, md, schema, index, source, context, createDate, changeDate, groupId, isTemplate, dm, metadataManager);
}
+ id.add(index, metadataId);
+
+ }
+
+ private static String insertMetadata(String uuid, List md, String schema, int index, String source, ServiceContext context, String createDate, String changeDate, String groupId, MetadataType isTemplate, DataManager dm, IMetadataManager metadataManager) throws Exception {
if (Log.isDebugEnabled(Geonet.MEF))
Log.debug(Geonet.MEF, "Adding metadata with uuid:" + uuid);
- //
- // insert metadata
- //
int userid = context.getUserSession().getUserIdAsInt();
String docType = null, category = null;
boolean ufo = false;
@@ -547,15 +584,11 @@ public static void importRecord(String uuid, MEFLib.UuidAction uuidAction, List<
createDate, changeDate, ufo, IndexingMode.none);
dm.activateWorkflowIfConfigured(context, metadataId, groupId);
-
- id.add(index, metadataId);
-
+ return metadataId;
}
- // --------------------------------------------------------------------------
-
private static void saveFile(ServiceContext context, String id, MetadataResourceVisibility access, String file, String changeDate,
- InputStream is) throws Exception {
+ InputStream is) throws Exception {
final Store store = context.getBean("resourceStore", Store.class);
final IMetadataUtils metadataUtils = context.getBean(IMetadataUtils.class);
final String metadataUuid = metadataUtils.getMetadataUuid(id);
@@ -610,7 +643,7 @@ private static Group addPrivileges(final ServiceContext context, final DataManag
* Add operations according to information file.
*/
private static Set addOperations(final ServiceContext context, final DataManager dm, final Element group,
- final int metadataId, final int grpId) {
+ final int metadataId, final int grpId) {
@SuppressWarnings("unchecked") List operations = group.getChildren("operation");
Set toAdd = new HashSet();
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 c8fb568977..04d8e9cff3 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
@@ -1,5 +1,5 @@
//=============================================================================
-//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
+//=== Copyright (C) 2001-2022 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
@@ -24,6 +24,7 @@
package org.fao.geonet.kernel.mef;
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;
@@ -32,13 +33,7 @@
import org.fao.geonet.api.records.attachments.Store;
import org.fao.geonet.api.records.attachments.StoreUtils;
import org.fao.geonet.constants.Geonet;
-import org.fao.geonet.domain.AbstractMetadata;
-import org.fao.geonet.domain.MetadataRelation;
-import org.fao.geonet.domain.MetadataResource;
-import org.fao.geonet.domain.MetadataResourceVisibility;
-import org.fao.geonet.domain.MetadataType;
-import org.fao.geonet.domain.Pair;
-import org.fao.geonet.domain.ReservedOperation;
+import org.fao.geonet.domain.*;
import org.fao.geonet.kernel.DataManager;
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.kernel.mef.MEFLib.Format;
@@ -74,7 +69,7 @@ class MEF2Exporter {
public static Path doExport(ServiceContext context, Set uuids,
Format format, boolean skipUUID, Path stylePath, boolean resolveXlink,
boolean removeXlinkAttribute, boolean skipError, boolean addSchemaLocation) throws Exception {
- return doExport(context, uuids, format, skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation, false);
+ return doExport(context, uuids, format, skipUUID, stylePath, resolveXlink, removeXlinkAttribute, skipError, addSchemaLocation, false);
}
/**
@@ -93,7 +88,7 @@ public static Path doExport(ServiceContext context, Set uuids,
EsSearchManager searchManager = context.getBean(EsSearchManager.class);
String contextLang = context.getLanguage() == null ? Geonet.DEFAULT_LANGUAGE : context.getLanguage();
try (
- FileSystem zipFs = ZipUtil.createZipFs(file);
+ FileSystem zipFs = ZipUtil.createZipFs(file)
) {
StringBuilder csvBuilder = new StringBuilder("\"schema\";\"uuid\";\"id\";\"type\";\"isHarvested\";\"title\";\"abstract\"\n");
Element html = new Element("html").addContent(new Element("head").addContent(Arrays.asList(
@@ -120,8 +115,7 @@ public static Path doExport(ServiceContext context, Set uuids,
)));
Element body = new Element("body");
html.addContent(body);
- for (Object uuid1 : uuids) {
- String uuid = (String) uuid1;
+ for (String uuid : uuids) {
final String cleanUUID = cleanForCsv(uuid);
AbstractMetadata md = context.getBean(IMetadataUtils.class).findOneByUuid(uuid);
@@ -129,7 +123,7 @@ public static Path doExport(ServiceContext context, Set uuids,
//Here we just care if we need the approved version explicitly.
//IMetadataUtils already filtered draft for non editors.
- if(approved) {
+ if (approved) {
md = context.getBean(MetadataRepository.class).findOneByUuid(uuid);
}
String id = String.valueOf(md.getId());
@@ -188,6 +182,9 @@ public static Path doExport(ServiceContext context, Set uuids,
}
Files.write(zipFs.getPath("/index.csv"), csvBuilder.toString().getBytes(Constants.CHARSET));
Files.write(zipFs.getPath("/index.html"), Xml.getString(html).getBytes(Constants.CHARSET));
+ } catch (Exception e) {
+ FileUtils.deleteQuietly(file.toFile());
+ throw e;
}
return file;
}
@@ -252,7 +249,7 @@ private static void createMetadataFolder(ServiceContext context,
final Store store = context.getBean("resourceStore", Store.class);
final List publicResources = store.getResources(context, metadata.getUuid(),
- MetadataResourceVisibility.PUBLIC, null, true);
+ MetadataResourceVisibility.PUBLIC, null, true);
// --- save thumbnails and maps
diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java
index 9009a56773..aac7c9323b 100644
--- a/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java
+++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEFExporter.java
@@ -1,5 +1,5 @@
//=============================================================================
-//=== Copyright (C) 2001-2007 Food and Agriculture Organization of the
+//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
@@ -24,31 +24,27 @@
package org.fao.geonet.kernel.mef;
import jeeves.server.context.ServiceContext;
-import static org.fao.geonet.kernel.mef.MEFConstants.FILE_INFO;
-import static org.fao.geonet.kernel.mef.MEFConstants.FILE_METADATA;
-
-import java.nio.file.FileSystem;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-
+import org.apache.commons.io.FileUtils;
import org.fao.geonet.Constants;
import org.fao.geonet.ZipUtil;
import org.fao.geonet.api.records.attachments.Store;
import org.fao.geonet.api.records.attachments.StoreUtils;
import org.fao.geonet.constants.Geonet;
-import org.fao.geonet.domain.AbstractMetadata;
-import org.fao.geonet.domain.MetadataResource;
-import org.fao.geonet.domain.MetadataResourceVisibility;
-import org.fao.geonet.domain.MetadataType;
-import org.fao.geonet.domain.Pair;
-import org.fao.geonet.domain.ReservedOperation;
+import org.fao.geonet.domain.*;
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.kernel.mef.MEFLib.Format;
import org.fao.geonet.kernel.mef.MEFLib.Version;
import org.fao.geonet.lib.Lib;
import org.fao.geonet.utils.Log;
+import java.nio.file.FileSystem;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+
+import static org.fao.geonet.kernel.mef.MEFConstants.FILE_INFO;
+import static org.fao.geonet.kernel.mef.MEFConstants.FILE_METADATA;
+
/**
* Export MEF file
@@ -146,9 +142,10 @@ private static Path export(ServiceContext context, boolean approved, Format form
}
}
+ } catch (Exception e) {
+ FileUtils.deleteQuietly(file.toFile());
+ throw e;
}
return file;
}
}
-
-// =============================================================================
diff --git a/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java b/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java
index 2f3ef31284..ab78e4bc31 100644
--- a/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java
+++ b/core/src/main/java/org/fao/geonet/kernel/mef/MEFLib.java
@@ -28,13 +28,9 @@
import static org.fao.geonet.kernel.mef.MEFConstants.FS;
import static org.fao.geonet.kernel.mef.MEFConstants.VERSION;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
import java.net.URISyntaxException;
+import java.net.URLEncoder;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystem;
import java.nio.file.Files;
@@ -47,6 +43,7 @@
import javax.annotation.Nonnull;
import org.apache.commons.io.IOUtils;
+import org.fao.geonet.Constants;
import org.fao.geonet.GeonetContext;
import org.fao.geonet.ZipUtil;
import org.fao.geonet.constants.Edit;
@@ -68,10 +65,13 @@
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.kernel.SchemaManager;
import org.fao.geonet.kernel.setting.SettingManager;
+import org.fao.geonet.lib.Lib;
import org.fao.geonet.repository.GroupRepository;
import org.fao.geonet.repository.OperationAllowedRepository;
import org.fao.geonet.repository.OperationRepository;
import org.fao.geonet.utils.BinaryFile;
+import org.fao.geonet.utils.IO;
+import org.fao.geonet.utils.Log;
import org.fao.geonet.utils.Xml;
import org.jdom.Attribute;
import org.jdom.Document;
@@ -529,10 +529,51 @@ static String getChangeDate(List files, String fileName)
throw new Exception("File not found in info.xml : " + fileName);
}
+ public static void backupRecord(AbstractMetadata metadata, ServiceContext context) {
+ Log.trace(Geonet.DATA_MANAGER, "Backing up record " + metadata.getId());
+ Path outDir = Lib.resource.getRemovedDir(metadata.getId());
+ Path outFile;
+ try {
+ // When metadata records contains character not supported by filesystem
+ // it may be an issue. eg. acri-st.fr/96443
+ outFile = outDir.resolve(URLEncoder.encode(metadata.getUuid(), Constants.ENCODING) + ".zip");
+ } catch (UnsupportedEncodingException e1) {
+ outFile = outDir.resolve(String.format(
+ "backup-%s-%s.mef",
+ new Date(), metadata.getUuid()));
+ }
+
+ Path file = null;
+ try {
+ file = doExport(context, metadata.getUuid(), "full", false, true, false, false, true);
+ Files.createDirectories(outDir);
+ try (InputStream is = IO.newInputStream(file);
+ OutputStream os = Files.newOutputStream(outFile)) {
+ BinaryFile.copy(is, os);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Error performing backup on record '" + metadata.getUuid() + "'. Contact the system administrator if the problem persists: " + e.getMessage(), e);
+ } finally {
+ if (file != null) {
+ IO.deleteFile(file, false, Geonet.MEF);
+ }
+ }
+ }
+
public enum UuidAction {
GENERATEUUID("generateUUID"),
NOTHING("nothing"),
- OVERWRITE("overwrite");
+
+ /**
+ * Update the XML of the metadata record.
+ */
+ OVERWRITE("overwrite"),
+
+ /**
+ * Remove the metadata (and privileges, status, ...)
+ * and insert the new one with the same UUID.
+ */
+ REMOVE_AND_REPLACE("removeAndReplace");
String name;
UuidAction(String name) {
@@ -552,7 +593,7 @@ public static UuidAction parse(String value) {
public enum Format {
/**
- * Only metadata record and infomation
+ * Only metadata record and information
*/
SIMPLE,
/**
diff --git a/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java b/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java
index 8d3c93708e..e8678d483a 100644
--- a/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.java
+++ b/core/src/main/java/org/fao/geonet/kernel/metadata/DefaultStatusActions.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)
//===
@@ -23,6 +23,7 @@
package org.fao.geonet.kernel.metadata;
+import com.google.common.base.Joiner;
import jeeves.server.UserSession;
import jeeves.server.context.ServiceContext;
import org.apache.commons.lang.StringUtils;
@@ -30,9 +31,7 @@
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.domain.*;
import org.fao.geonet.events.md.MetadataStatusChanged;
-import org.fao.geonet.kernel.AccessManager;
import org.fao.geonet.kernel.DataManager;
-import org.fao.geonet.kernel.datamanager.IMetadataManager;
import org.fao.geonet.kernel.datamanager.IMetadataStatus;
import org.fao.geonet.kernel.datamanager.IMetadataUtils;
import org.fao.geonet.kernel.setting.SettingManager;
@@ -40,20 +39,15 @@
import org.fao.geonet.repository.*;
import org.fao.geonet.repository.specification.GroupSpecs;
import org.fao.geonet.util.MailUtil;
-import org.fao.geonet.util.XslUtil;
import org.fao.geonet.utils.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import java.text.MessageFormat;
import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import static org.fao.geonet.kernel.setting.Settings.SYSTEM_FEEDBACK_EMAIL;
-import com.google.common.base.Joiner;
-
public class DefaultStatusActions implements StatusActions {
protected ServiceContext context;
@@ -66,9 +60,11 @@ public class DefaultStatusActions implements StatusActions {
protected String siteName;
protected UserSession session;
protected boolean emailNotes = true;
- private String from, replyTo, replyToDescr;
- private StatusValueRepository _statusValueRepository;
+ private String replyTo;
+ private String replyToDescr;
+ private StatusValueRepository statusValueRepository;
protected IMetadataStatus metadataStatusManager;
+ private IMetadataUtils metadataRepository;
/**
* Constructor.
@@ -83,14 +79,14 @@ public void init(ServiceContext context) throws Exception {
this.context = context;
ApplicationContext applicationContext = ApplicationContextHolder.get();
- this._statusValueRepository = applicationContext.getBean(StatusValueRepository.class);
+ this.statusValueRepository = applicationContext.getBean(StatusValueRepository.class);
this.metadataUtils = applicationContext.getBean(IMetadataUtils.class);
this.language = context.getLanguage();
SettingManager sm = applicationContext.getBean(SettingManager.class);
siteName = sm.getSiteName();
- from = sm.getValue(SYSTEM_FEEDBACK_EMAIL);
+ String from = sm.getValue(SYSTEM_FEEDBACK_EMAIL);
if (from == null || from.length() == 0) {
context.error("Mail feedback address not configured, email notifications won't be sent.");
@@ -109,6 +105,8 @@ public void init(ServiceContext context) throws Exception {
dm = applicationContext.getBean(DataManager.class);
metadataStatusManager = applicationContext.getBean(IMetadataStatus.class);
siteUrl = sm.getSiteURL(context);
+
+ metadataRepository = context.getBean(IMetadataUtils.class);
}
/**
@@ -120,13 +118,11 @@ public void init(ServiceContext context) throws Exception {
public void onEdit(int id, boolean minorEdit) throws Exception {
if (Log.isTraceEnabled(Geonet.DATA_MANAGER)) {
Log.trace(Geonet.DATA_MANAGER, "DefaultStatusActions.onEdit(" + id + ", " + minorEdit + ") with status "
- + dm.getCurrentStatus(id));
+ + dm.getCurrentStatus(id));
}
if (!minorEdit && dm.getCurrentStatus(id).equals(StatusValue.Status.APPROVED)) {
- //if (!minorEdit && dm.getCurrentStatus(id).equals(StatusValue.Status.APPROVED)
- // && (context.getBean(IMetadataManager.class) instanceof DraftMetadataManager)) {
ResourceBundle messages = ResourceBundle.getBundle("org.fao.geonet.api.Messages",
- new Locale(this.language));
+ new Locale(this.language));
String changeMessage = String.format(messages.getString("status_email_text"), replyToDescr, replyTo, id);
Log.trace(Geonet.DATA_MANAGER, "Set DRAFT to current record with id " + id);
dm.setStatus(context, id, Integer.valueOf(StatusValue.Status.DRAFT), new ISODate(), changeMessage);
@@ -140,15 +136,19 @@ public void onEdit(int id, boolean minorEdit) throws Exception {
* @return
* @throws Exception
*/
- public Set onStatusChange(List listOfStatus) throws Exception {
+ public Map onStatusChange(List listOfStatus) throws Exception {
+
+ if (listOfStatus.stream().map(MetadataStatus::getMetadataId).distinct().count() != listOfStatus.size()) {
+ throw new IllegalArgumentException("Multiple status update received on the same metadata");
+ }
- Set unchanged = new HashSet();
+ Map results = new HashMap<>();
- // -- process the metadata records to set status
+ // process the metadata records to set status
for (MetadataStatus status : listOfStatus) {
MetadataStatus currentStatus = dm.getStatus(status.getMetadataId());
- String currentStatusId = (currentStatus != null)?
- String.valueOf(currentStatus.getStatusValue().getId()):"";
+ String currentStatusId = (currentStatus != null) ?
+ String.valueOf(currentStatus.getStatusValue().getId()) : "";
String statusId = status.getStatusValue().getId() + "";
@@ -156,21 +156,32 @@ public Set onStatusChange(List listOfStatus) throws Exc
listOfId.add(status.getMetadataId());
- // --- For the workflow, if the status is already set to value
+ // For the workflow, if the status is already set to value
// of status then do nothing. This does not apply to task and event.
if (status.getStatusValue().getType().equals(StatusValueType.workflow) &&
- (statusId).equals(currentStatusId)) {
+ (statusId).equals(currentStatusId)) {
if (context.isDebugEnabled())
context.debug(String.format("Metadata %s already has status %s ",
status.getMetadataId(), status.getStatusValue().getId()));
- unchanged.add(status.getMetadataId());
+ results.put(status.getMetadataId(), StatusChangeType.UNCHANGED);
continue;
}
- // --- set status, indexing is assumed to take place later
- metadataStatusManager.setStatusExt(status);
+ // if not possible to go from one status to the other, don't continue
+ AbstractMetadata metadata = metadataRepository.findOne(status.getMetadataId());
+ if (!isStatusChangePossible(session.getProfile(), metadata, currentStatusId, statusId)) {
+ results.put(status.getMetadataId(), StatusChangeType.UNCHANGED);
+ continue;
+ }
+
+ // debug output if necessary
+ if (context.isDebugEnabled())
+ context.debug("Change status of metadata with id " + status.getMetadataId() + " from " + currentStatusId + " to " + statusId);
- // --- inform content reviewers if the status is submitted
+ // we know we are allowed to do the change, apply any side effects
+ boolean deleted = applyStatusChange(status.getMetadataId(), status, statusId);
+
+ // inform content reviewers if the status is submitted
try {
notify(getUserToNotify(status), status);
} catch (Exception e) {
@@ -179,21 +190,40 @@ public Set onStatusChange(List listOfStatus) throws Exc
status.getMetadataId(), status.getStatusValue().getId(), e.getMessage()));
}
- //Throw events
+ if (deleted) {
+ results.put(status.getMetadataId(), StatusChangeType.DELETED);
+ } else {
+ results.put(status.getMetadataId(), StatusChangeType.UPDATED);
+ }
+ // throw events
Log.trace(Geonet.DATA_MANAGER, "Throw workflow events.");
for (Integer mid : listOfId) {
- if (!unchanged.contains(mid)) {
+ if (results.get(mid) != StatusChangeType.DELETED) {
Log.debug(Geonet.DATA_MANAGER, " > Status changed for record (" + mid + ") to status " + status);
context.getApplicationContext().publishEvent(new MetadataStatusChanged(
- metadataUtils.findOne(Integer.valueOf(mid)),
+ metadataUtils.findOne(mid),
status.getStatusValue(), status.getChangeMessage(),
- status.getUserId()));
+ status.getUserId()
+ ));
}
}
}
- return unchanged;
+ return results;
+ }
+
+ /**
+ * Placeholder to apply any side effects.
+ * eg. if APPROVED, publish a record,
+ * if RETIRED, unpublish or delete the record.
+ */
+ private boolean applyStatusChange(int metadataId, MetadataStatus status, String toStatusId) throws Exception {
+ boolean deleted = false;
+ if (!deleted) {
+ metadataStatusManager.setStatusExt(status);
+ }
+ return deleted;
}
@@ -206,6 +236,10 @@ public Set onStatusChange(List listOfStatus) throws Exc
* @throws Exception
*/
protected void notify(List userToNotify, MetadataStatus status) throws Exception {
+ if ((userToNotify == null) || userToNotify.isEmpty()) {
+ return;
+ }
+
ResourceBundle messages = ResourceBundle.getBundle("org.fao.geonet.api.Messages", new Locale(this.language));
String translatedStatusName = getTranslatedStatusName(status.getStatusValue().getId());
@@ -213,13 +247,11 @@ protected void notify(List userToNotify, MetadataStatus status) throws Exc
String subjectTemplate = "";
try {
subjectTemplate = messages
- .getString("status_change_" + status.getStatusValue().getName() + "_email_subject");
+ .getString("status_change_" + status.getStatusValue().getName() + "_email_subject");
} catch (MissingResourceException e) {
subjectTemplate = messages.getString("status_change_default_email_subject");
}
- String subject = MessageFormat.format(subjectTemplate, siteName, translatedStatusName, replyToDescr // Author of
- // the
- // change
+ String subject = MessageFormat.format(subjectTemplate, siteName, translatedStatusName, replyToDescr // Author of the change
);
Set listOfId = new HashSet<>(1);
@@ -232,22 +264,30 @@ protected void notify(List userToNotify, MetadataStatus status) throws Exc
textTemplate = messages.getString("status_change_default_email_text");
}
+ // Replace link in message
+ ApplicationContext applicationContext = ApplicationContextHolder.get();
+ SettingManager sm = applicationContext.getBean(SettingManager.class);
+ textTemplate = textTemplate.replace("{{link}}", sm.getNodeURL()+ "api/records/'{{'index:uuid'}}'");
+
UserRepository userRepository = context.getBean(UserRepository.class);
User owner = userRepository.findById(status.getOwner()).orElse(null);
- String message = MessageFormat.format(textTemplate, replyToDescr, // Author of the change
- status.getChangeMessage(), translatedStatusName, status.getChangeDate(), status.getDueDate(),
- status.getCloseDate(),
- owner == null ? "" : Joiner.on(" ").skipNulls().join( owner.getName(), owner.getSurname()),
- siteUrl);
-
IMetadataUtils metadataRepository = ApplicationContextHolder.get().getBean(IMetadataUtils.class);
AbstractMetadata metadata = metadataRepository.findOne(status.getMetadataId());
+ String metadataUrl = metadataUtils.getDefaultUrl(metadata.getUuid(), this.language);
+
+ String message = MessageFormat.format(textTemplate, replyToDescr, // Author of the change
+ status.getChangeMessage(), translatedStatusName, status.getChangeDate(), status.getDueDate(),
+ status.getCloseDate(),
+ owner == null ? "" : Joiner.on(" ").skipNulls().join(owner.getName(), owner.getSurname()),
+ metadataUrl);
+
+
subject = MailUtil.compileMessageWithIndexFields(subject, metadata.getUuid(), this.language);
message = MailUtil.compileMessageWithIndexFields(message, metadata.getUuid(), this.language);
for (User user : userToNotify) {
- String salutation = Joiner.on(" ").skipNulls().join( user.getName(), user.getSurname());
+ String salutation = Joiner.on(" ").skipNulls().join(user.getName(), user.getSurname());
//If we have a salutation then end it with a ","
if (StringUtils.isEmpty(salutation)) {
salutation = "";
@@ -267,6 +307,16 @@ protected void notify(List userToNotify, MetadataStatus status) throws Exc
*/
protected List getUserToNotify(MetadataStatus status) {
StatusValueNotificationLevel notificationLevel = status.getStatusValue().getNotificationLevel();
+
+ // If new status is DRAFT and previous status is not SUBMITTED (which means a rejection),
+ // ignore notifications as the DRAFT status is used also when creating the working copy.
+ // We don't want to notify when creating a working copy.
+ if (status.getStatusValue().getId() == Integer.parseInt(StatusValue.Status.DRAFT) &&
+ ((StringUtils.isEmpty(status.getPreviousState())) ||
+ (Integer.parseInt(status.getPreviousState()) != Integer.parseInt(StatusValue.Status.SUBMITTED)))) {
+ return new ArrayList<>();
+ }
+
// TODO: Status does not provide batch update
// So taking care of one record at a time.
// Currently the code could notify a mix of reviewers
@@ -276,7 +326,7 @@ protected List getUserToNotify(MetadataStatus status) {
return getUserToNotify(notificationLevel, listOfId, status.getOwner());
}
- static public List getUserToNotify(StatusValueNotificationLevel notificationLevel, Set recordIds, Integer ownerId) {
+ public static List getUserToNotify(StatusValueNotificationLevel notificationLevel, Set recordIds, Integer ownerId) {
UserRepository userRepository = ApplicationContextHolder.get().getBean(UserRepository.class);
List users = new ArrayList<>();
@@ -331,7 +381,7 @@ static public List getUserToNotify(StatusValueNotificationLevel notificati
return users;
}
- static public List getGroupToNotify(StatusValueNotificationLevel notificationLevel, List groupNames) {
+ public static List getGroupToNotify(StatusValueNotificationLevel notificationLevel, List groupNames) {
GroupRepository groupRepository = ApplicationContextHolder.get().getBean(GroupRepository.class);
List groups = new ArrayList<>();
@@ -360,10 +410,10 @@ protected void unsetAllOperations(int mdId) throws Exception {
private String getTranslatedStatusName(int statusValueId) {
String translatedStatusName = "";
- StatusValue s = _statusValueRepository.findOneById(statusValueId);
+ StatusValue s = statusValueRepository.findOneById(statusValueId);
if (s == null) {
translatedStatusName = statusValueId
- + " (Status not found in database translation table. Check the content of the StatusValueDes table.)";
+ + " (Status not found in database translation table. Check the content of the StatusValueDes table.)";
} else {
translatedStatusName = s.getLabel(this.language);
}
@@ -377,7 +427,7 @@ private String getTranslatedStatusName(int statusValueId) {
* @param subject Subject to be used for email notices
* @param message Text of the mail
*/
- protected void sendEmail(String sendTo, String subject, String message) throws Exception {
+ protected void sendEmail(String sendTo, String subject, String message) {
if (!emailNotes) {
context.info("Would send email \nTo: " + sendTo + "\nSubject: " + subject + "\n Message:\n" + message);
@@ -385,11 +435,41 @@ protected void sendEmail(String sendTo, String subject, String message) throws E
ApplicationContext applicationContext = ApplicationContextHolder.get();
SettingManager sm = applicationContext.getBean(SettingManager.class);
// Doesn't make sense go further without any mailserver set...
- if(StringUtils.isNotBlank(sm.getValue(Settings.SYSTEM_FEEDBACK_MAILSERVER_HOST))) {
+ if (StringUtils.isNotBlank(sm.getValue(Settings.SYSTEM_FEEDBACK_MAILSERVER_HOST))) {
List to = new ArrayList<>();
to.add(sendTo);
MailUtil.sendMail(to, subject, message, null, sm, replyTo, replyToDescr);
}
}
}
+
+ /**
+ * Placeholder to test whether a given status change for a given role is allowed or not.
+ *
+ *
+ * @param profile the role that tries to execute the status change
+ * @param fromStatus the status from which we start
+ * @param toStatus the status to which we'd like to change
+ * @return whether the change is allowed
+ */
+ private boolean isStatusChangePossible(Profile profile, AbstractMetadata metadata, String fromStatus, String toStatus) throws Exception {
+ return true;
+ // Example:
+ // if (StringUtils.isEmpty(fromStatus) && toStatus.equals(StatusValue.Status.DRAFT))
+ // return true;
+ // // figure out whether we can switch from status to status, depending on the profile
+ // Set toProfiles = new HashSet<>();
+ // switch (profile) {
+ // case Editor:
+ // toProfiles = getEditorFlow().get(fromStatus);
+ // break;
+ // case Administrator:
+ // toProfiles = getAdminFlow().get(fromStatus);
+ // break;
+ // case Reviewer:
+ // toProfiles = getReviewerFlow().get(fromStatus);
+ // break;
+ // }
+ // return toProfiles != null && toProfiles.contains(toStatus);
+ }
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/metadata/StatusActions.java b/core/src/main/java/org/fao/geonet/kernel/metadata/StatusActions.java
index 2165558573..9a4aecff58 100644
--- a/core/src/main/java/org/fao/geonet/kernel/metadata/StatusActions.java
+++ b/core/src/main/java/org/fao/geonet/kernel/metadata/StatusActions.java
@@ -26,6 +26,7 @@
import jeeves.server.context.ServiceContext;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import org.fao.geonet.domain.ISODate;
@@ -37,6 +38,6 @@ public interface StatusActions {
public void onEdit(int id, boolean minorEdit) throws Exception;
- public Set onStatusChange(List status) throws Exception;
+ public Map onStatusChange(List status) throws Exception;
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/search/SearchParameter.java b/core/src/main/java/org/fao/geonet/kernel/metadata/StatusChangeType.java
similarity index 57%
rename from core/src/main/java/org/fao/geonet/kernel/search/SearchParameter.java
rename to core/src/main/java/org/fao/geonet/kernel/metadata/StatusChangeType.java
index 74114e8868..81d3983ffa 100644
--- a/core/src/main/java/org/fao/geonet/kernel/search/SearchParameter.java
+++ b/core/src/main/java/org/fao/geonet/kernel/metadata/StatusChangeType.java
@@ -1,5 +1,5 @@
-//==============================================================================
-//=== Copyright (C) 2001-2010 Food and Agriculture Organization of the
+//=============================================================================
+//=== Copyright (C) 2001-2023 Food and Agriculture Organization of the
//=== United Nations (FAO-UN), United Nations World Food Programme (WFP)
//=== and United Nations Environment Programme (UNEP)
//===
@@ -20,25 +20,10 @@
//=== Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
//=== Rome - Italy. email: geonetwork@osgeo.org
//==============================================================================
-package org.fao.geonet.kernel.search;
+package org.fao.geonet.kernel.metadata;
-/**
- * Parameter names used in search.
- *
- * @author heikki doeleman
- */
-public class SearchParameter {
- public static final String GROUP = "group";
- public static final String UUID = "uuid";
- public static final String ANY = "any";
- public static final String ALL = "all";
- public static final String OR = "or";
- public static final String WITHOUT = "without";
- public static final String DOWNLOAD = "download";
- public static final String TEMPLATE = "template";
- public static final String TYPE = "type";
- public static final String INSPIRE = "inspire";
- public static final String TITLE = "title";
- public static final String VALID = "valid";
- public static final String ROOT = "_root";
+public enum StatusChangeType {
+ UPDATED,
+ UNCHANGED,
+ DELETED
}
diff --git a/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java
index 6248c99223..d07dccc595 100644
--- a/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java
+++ b/core/src/main/java/org/fao/geonet/kernel/schema/MetadataSchema.java
@@ -668,6 +668,3 @@ savedQuery, getName(),
getNamespaces());
}
}
-
-//==============================================================================
-
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 8b3d829175..e07c3dfb48 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
@@ -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)
*
@@ -68,7 +68,6 @@
import org.fao.geonet.repository.specification.MetadataSpecs;
import org.fao.geonet.utils.Xml;
import org.jdom.Element;
-import org.jdom.JDOMException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -101,16 +100,17 @@ public class EsSearchManager implements ISearchManager {
public static final String FIELDNAME = "name";
public static final String FIELDSTRING = "string";
- public static Map relatedIndexFields;
- public static Set FIELDLIST_CORE;
- public static Set FIELDLIST_RELATED;
- public static Set FIELDLIST_UUID;
+ public static final Map RELATED_INDEX_FIELDS;
+ public static final Set FIELDLIST_CORE;
+ public static final Set FIELDLIST_RELATED;
+ public static final Map FIELDLIST_RELATED_SCRIPTED;
+ public static final Set FIELDLIST_UUID;
static {
FIELDLIST_UUID = ImmutableSet.builder()
.add(Geonet.IndexFieldNames.UUID).build();
- relatedIndexFields = ImmutableMap.builder()
+ RELATED_INDEX_FIELDS = ImmutableMap.builder()
.put("children", "parentUuid")
.put("brothersAndSisters", "parentUuid")
.put("services", "recordOperateOn")
@@ -140,7 +140,7 @@ public class EsSearchManager implements ISearchManager {
.add(Geonet.IndexFieldNames.UUID)
.add(Geonet.IndexFieldNames.RESOURCETITLE)
.add(Geonet.IndexFieldNames.RESOURCETITLE + "Object")
- .add("overview.*")
+ //.add("overview.*")
.add("link")
.add("format")
.add("resourceType")
@@ -151,6 +151,11 @@ public class EsSearchManager implements ISearchManager {
.add(Geonet.IndexFieldNames.RESOURCEABSTRACT + "Object")
.add("operatesOn")
.build();
+
+ FIELDLIST_RELATED_SCRIPTED = ImmutableMap.builder()
+ // Elasticsearch scripted field to get the first overview url. Scripted fields must return single values.
+ .put("overview", "return params['_source'].overview == null ? [] : params['_source'].overview.stream().map(f -> f.url).findFirst().orElse('');")
+ .build();
}
@Value("${es.index.records:gn-records}")
@@ -207,7 +212,7 @@ private void addMDFields(Element doc, Path schemaDir,
IndexingMode indexingMode) {
final Path styleSheet = getXSLTForIndexing(schemaDir, metadataType);
try {
- Map indexParams = new HashMap();
+ Map indexParams = new HashMap<>();
indexParams.put("fastIndexMode", indexingMode.equals(IndexingMode.core));
Element fields = Xml.transform(metadata, styleSheet, indexParams);
@@ -227,10 +232,8 @@ private void addMDFields(Element doc, Path schemaDir,
}
private void addMoreFields(Element doc, Multimap fields) {
- fields.entries().forEach(e -> {
- doc.addContent(new Element(e.getKey())
- .setText(String.valueOf(e.getValue())));
- });
+ fields.entries().forEach(e -> doc.addContent(new Element(e.getKey())
+ .setText(String.valueOf(e.getValue()))));
}
public Element makeField(String name, String value) {
@@ -326,21 +329,21 @@ private void createIndex(String indexId, String indexName, boolean dropIndexFirs
public void end() {
}
- public UpdateResponse updateFields(String id, Map fields) throws Exception {
- fields.put("indexingDate", new Date());
+ 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);
}
- public BulkResponse updateFields(String id, Multimap