diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java index c5291a59bbf..168e4e2a63c 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/AbstractStore.java @@ -1,6 +1,6 @@ /* * ============================================================================= - * === Copyright (C) 2019 Food and Agriculture Organization of the + * === Copyright (C) 2024 Food and Agriculture Organization of the * === United Nations (FAO-UN), United Nations World Food Programme (WFP) * === and United Nations Environment Programme (UNEP) * === @@ -44,12 +44,16 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Base64; import java.util.List; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; public abstract class AbstractStore implements Store { + protected static final String RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR = ":"; + protected static final String RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_ESCAPED_SEPARATOR = "\\:"; + @Override public final List getResources(final ServiceContext context, final String metadataUuid, final Sort sort, final String filter) throws Exception { @@ -279,4 +283,28 @@ public String toString() { } }; } + + private String escapeResourceManagementExternalProperties(String value) { + return value.replace(RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR, RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_ESCAPED_SEPARATOR); +} + + /** + * Create an encoded base 64 object id contains the following fields to uniquely identify the resource + * The fields are separated by a colon ":" + * @param type to identify type of storage - document/folder + * @param visibility of the resource public/private + * @param metadataId internal metadata id + * @param version identifier which can be used to directly get this version. + * @param resourceId or filename of the resource + * @return based 64 object id + */ + protected String getResourceManagementExternalPropertiesObjectId(final String type, final MetadataResourceVisibility visibility, final Integer metadataId, final String version, + final String resourceId) { + return Base64.getEncoder().encodeToString( + ((type + RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR + + escapeResourceManagementExternalProperties(visibility == null ? "" : visibility.toString().toLowerCase()) + RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR + + metadataId + RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR + + escapeResourceManagementExternalProperties(version == null ? "" : version) + RESOURCE_MANAGEMENT_EXTERNAL_PROPERTIES_SEPARATOR + + escapeResourceManagementExternalProperties(resourceId)).getBytes())); + } } diff --git a/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java b/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java index de258b3a711..37fe8501899 100644 --- a/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java +++ b/datastorages/cmis/src/main/java/org/fao/geonet/api/records/attachments/CMISStore.java @@ -1,6 +1,6 @@ /* * ============================================================================= - * === Copyright (C) 2001-2016 Food and Agriculture Organization of the + * === Copyright (C) 2001-2024 Food and Agriculture Organization of the * === United Nations (FAO-UN), United Nations World Food Programme (WFP) * === and United Nations Environment Programme (UNEP) * === @@ -627,8 +627,10 @@ private GeonetworkDataDirectory getDataDirectory(ServiceContext context) { /** * get external resource management for the supplied resource. * Replace the following + * {objectId} type:visibility:metadataId:version:resourceId in base64 encoding * {id} resource id - * {type:folder:document} // If the type is folder then type "folder" will be displayed else if document then "document" will be displayed + * {type:folder:document} // Custom return type based on type. If the type is folder then type "folder" will be displayed else if document then "document" will be displayed + * {type} // If the type is folder then type "folder" will be displayed else if document then "document" will be displayed * {uuid} metadatauuid * {metadataid} metadataid * {visibility} visibility @@ -657,16 +659,27 @@ protected MetadataResourceExternalManagementProperties getMetadataResourceExtern ) { String metadataResourceExternalManagementPropertiesUrl = cmisConfiguration.getExternalResourceManagementUrl(); if (!StringUtils.isEmpty(metadataResourceExternalManagementPropertiesUrl)) { + // {objectid} objectId // It will be the type:visibility:metadataId:version:resourceId in base64 + // i.e. folder::100::100 # Folder in resource 100 + // i.e. document:public:100:v1:sample.jpg # public document 100 version v1 name sample.jpg + if (metadataResourceExternalManagementPropertiesUrl.contains("{objectid}")) { + metadataResourceExternalManagementPropertiesUrl = metadataResourceExternalManagementPropertiesUrl.replaceAll("(\\{objectid\\})", + getResourceManagementExternalPropertiesObjectId((type == null ? "document" : (type instanceof Folder ? "folder" : "document")), visibility, metadataId, version, resourceId)); + } // {id} id if (metadataResourceExternalManagementPropertiesUrl.contains("{id}")) { metadataResourceExternalManagementPropertiesUrl = metadataResourceExternalManagementPropertiesUrl.replaceAll("(\\{id\\})", (resourceId==null?"":resourceId)); } - // {type:folder:document} // If the type is folder then type "folder" will be displayed else if document then "document" will be displayed + // {type:folder:document} // Custom return type based on type. If the type is folder then type "folder" will be displayed else if document then "document" will be displayed if (metadataResourceExternalManagementPropertiesUrl.contains("{type:")) { metadataResourceExternalManagementPropertiesUrl = metadataResourceExternalManagementPropertiesUrl.replaceAll("\\{type:([a-zA-Z0-9]*?):([a-zA-Z0-9]*?)\\}", (type==null?"":(type instanceof Folder?"$1":"$2"))); } - + // {type} // If the type is folder then type "folder" will be displayed else if document then "document" will be displayed + if (metadataResourceExternalManagementPropertiesUrl.contains("{type}")) { + metadataResourceExternalManagementPropertiesUrl = metadataResourceExternalManagementPropertiesUrl.replaceAll("(\\{type\\})", + (type == null ? "document" : (type instanceof Folder ? "folder" : "document"))); + } // {uuid} metadatauuid if (metadataResourceExternalManagementPropertiesUrl.contains("{uuid}")) { metadataResourceExternalManagementPropertiesUrl = metadataResourceExternalManagementPropertiesUrl.replaceAll("(\\{uuid\\})", (metadataUuid==null?"":metadataUuid)); diff --git a/datastorages/cmis/src/main/java/org/fao/geonet/resources/CMISConfiguration.java b/datastorages/cmis/src/main/java/org/fao/geonet/resources/CMISConfiguration.java index 257ef3246d6..87b76ec0821 100644 --- a/datastorages/cmis/src/main/java/org/fao/geonet/resources/CMISConfiguration.java +++ b/datastorages/cmis/src/main/java/org/fao/geonet/resources/CMISConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2001-2016 Food and Agriculture Organization of the + * Copyright (C) 2001-2024 Food and Agriculture Organization of the * United Nations (FAO-UN), United Nations World Food Programme (WFP) * and United Nations Environment Programme (UNEP) * @@ -59,26 +59,28 @@ public class CMISConfiguration { private Session client = null; - public final static Integer CMIS_MAX_ITEMS_PER_PAGE = 1000; - public final static String CMIS_FOLDER_DELIMITER = "/"; // Specs indicate that "/" is the folder delimiter/separator - not sure if other delimiter can be used?. - public final static String CMIS_SECONDARY_PROPERTY_SEPARATOR = "->"; - private final String CMIS_DEFAULT_WEBSERVICES_ACL_SERVICE = "/services/ACLService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_DISCOVERY_SERVICE = "/services/DiscoveryService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_MULTIFILING_SERVICE = "/services/MultiFilingService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_NAVIGATION_SERVICE = "/services/NavigationService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_OBJECT_SERVICE = "/services/ObjectService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_POLICY_SERVICE = "/services/PolicyService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_RELATIONSHIP_SERVICE = "/services/RelationshipService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_REPOSITORY_SERVICE = "/services/RepositoryService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_VERSIONING_SERVICE = "/services/VersioningService?wsdl"; - private final String CMIS_DEFAULT_WEBSERVICES_BASE_URL_SERVICE = "/cmis"; - private final String CMIS_DEFAULT_BROWSER_URL_SERVICE = "/browser"; - private final String CMIS_DEFAULT_ATOMPUB_URL_SERVICE = "/atom"; - - private final String CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_WINDOW_PARAMETERS = "toolbar=0,width=600,height=600"; - private final Boolean CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_MODAL_ENABLED = true; - private final Boolean CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_FOLDER_ENABLED = true; - private final Boolean CMIS_DEFAULT_VERSIONING_ENABLED = false; + // DFO change to 100. Due to bug with open text cmis where if max is set to 1000, it will return 100 but if it is set to 100 it will return all records. + // https://dev.azure.com/foc-poc/EDH-CDE/_workitems/edit/95878 + public static final Integer CMIS_MAX_ITEMS_PER_PAGE = 100; + public static final String CMIS_FOLDER_DELIMITER = "/"; // Specs indicate that "/" is the folder delimiter/separator - not sure if other delimiter can be used?. + public static final String CMIS_SECONDARY_PROPERTY_SEPARATOR = "->"; + private static final String CMIS_DEFAULT_WEBSERVICES_ACL_SERVICE = "/services/ACLService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_DISCOVERY_SERVICE = "/services/DiscoveryService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_MULTIFILING_SERVICE = "/services/MultiFilingService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_NAVIGATION_SERVICE = "/services/NavigationService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_OBJECT_SERVICE = "/services/ObjectService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_POLICY_SERVICE = "/services/PolicyService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_RELATIONSHIP_SERVICE = "/services/RelationshipService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_REPOSITORY_SERVICE = "/services/RepositoryService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_VERSIONING_SERVICE = "/services/VersioningService?wsdl"; + private static final String CMIS_DEFAULT_WEBSERVICES_BASE_URL_SERVICE = "/cmis"; + private static final String CMIS_DEFAULT_BROWSER_URL_SERVICE = "/browser"; + private static final String CMIS_DEFAULT_ATOMPUB_URL_SERVICE = "/atom"; + + private static final String CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_WINDOW_PARAMETERS = "toolbar=0,width=600,height=600"; + private static final Boolean CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_MODAL_ENABLED = true; + private static final Boolean CMIS_DEFAULT_EXTERNAL_RESOURCE_MANAGEMENT_FOLDER_ENABLED = true; + private static final Boolean CMIS_DEFAULT_VERSIONING_ENABLED = false; private String servicesBaseUrl; private String bindingType; @@ -111,7 +113,6 @@ public class CMISConfiguration { * Property name for validation status that is expected to be an integer with values of null, 0, 1, 2 * (See MetadataResourceExternalManagementProperties.ValidationStatus for code meaning) * Property name follows the same format as cmisMetadataUUIDPropertyName - * * If null then validation status will default to UNKNOWN. */ private String externalResourceManagementValidationStatusPropertyName; @@ -505,7 +506,6 @@ public void setExternalResourceManagementValidationStatusPropertyName(String ext String.format("Invalid format for property name %s property will not be used", externalResourceManagementValidationStatusPropertyName)); this.externalResourceManagementValidationStatusPropertyName = null; this.externalResourceManagementValidationStatusSecondaryProperty = false; - return; } else { this.externalResourceManagementValidationStatusSecondaryProperty = true; } @@ -514,7 +514,7 @@ public void setExternalResourceManagementValidationStatusPropertyName(String ext public MetadataResourceExternalManagementProperties.ValidationStatus getValidationStatusDefaultValue() { // We only need to set the default if there is a status property supplied, and it is not already set - if (this.defaultStatus == null && !org.springframework.util.StringUtils.isEmpty(getExternalResourceManagementValidationStatusPropertyName())) { + if (this.defaultStatus == null && org.springframework.util.StringUtils.hasLength(getExternalResourceManagementValidationStatusPropertyName())) { if (getExternalResourceManagementValidationStatusDefaultValue() != null) { // If a default property name does exist then use it this.defaultStatus = MetadataResourceExternalManagementProperties.ValidationStatus.valueOf(getExternalResourceManagementValidationStatusDefaultValue()); @@ -536,9 +536,8 @@ public void init() { } // default factory implementation - Map parameters = new HashMap(); + Map parameters = new HashMap<>(); - this.baseRepositoryPath = baseRepositoryPath; if (this.baseRepositoryPath == null) { this.baseRepositoryPath = ""; } @@ -609,7 +608,7 @@ public void init() { } } } else { - // Try to find the repository name for the id that we have specified.. + // Try to find the repository name for the id that we have specified. try { for (Repository repository : factory.getRepositories(parameters)) { if (repository.getId().equalsIgnoreCase(this.repositoryId)) { @@ -633,7 +632,7 @@ public void init() { repositoryUrl + "' using product '" + client.getRepositoryInfo().getProductName() + "' version '" + client.getRepositoryInfo().getProductVersion() + "'."); - // Check if we can parse the secondary parameters from human readable to secondary ids. + // Check if we can parse the secondary parameters from human-readable to secondary ids. parsedCmisMetadataUUIDPropertyName = parseSecondaryProperty(client, cmisMetadataUUIDPropertyName); parsedExternalResourceManagementValidationStatusPropertyName = parseSecondaryProperty(client, externalResourceManagementValidationStatusPropertyName); @@ -743,7 +742,7 @@ public boolean existExternalResourceManagementValidationStatusSecondaryProperty( } /** - * Generte a full url based on the supplied entered serviceurl and the default. + * Generate a full url based on the supplied entered serviceUrl and the default. * * @param baseUrl Base url * @param serviceUrl Supplied service url (This could start with / or http. If it starts with http then ignore baseUrl) diff --git a/datastorages/cmis/src/main/resources/config-store/config-cmis-overrides.properties b/datastorages/cmis/src/main/resources/config-store/config-cmis-overrides.properties index f0a62c1920a..4c154639ca5 100644 --- a/datastorages/cmis/src/main/resources/config-store/config-cmis-overrides.properties +++ b/datastorages/cmis/src/main/resources/config-store/config-cmis-overrides.properties @@ -11,8 +11,8 @@ cmis.external.resource.management.window.parameters=${CMIS_EXTERNAL_RESOURCE_MAN cmis.external.resource.management.modal.enabled=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_MODAL_ENABLED:#{null}} cmis.external.resource.management.folder.enabled=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_FOLDER_ENABLED:#{null}} cmis.external.resource.management.folder.root=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_FOLDER_ROOT:#{null}} -cmis.external.resource.status.property.name=${CMIS_EXTERNAL_RESOURCE_STATUS_PROPERTY_NAME:#{null}} -cmis.external.resource.management.status.default.value=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_STATUS_DEFAULT_VALUE:#{null}} +cmis.external.resource.management.validation.status.property.name=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_VALIDATION_STATUS_PROPERTY_NAME:#{null}} +cmis.external.resource.management.validation.status.default.value=${CMIS_EXTERNAL_RESOURCE_MANAGEMENT_VALIDATION_STATUS_DEFAULT_VALUE:#{null}} cmis.versioning.enabled=${CMIS_VERSIONING_ENABLED:#{null}} cmis.versioning.state=#{'${CMIS_VERSIONING_STATE:MAJOR}'.toUpperCase()} diff --git a/datastorages/cmis/src/main/resources/config-store/config-cmis.xml b/datastorages/cmis/src/main/resources/config-store/config-cmis.xml index 76abe73572c..1c302788b5c 100644 --- a/datastorages/cmis/src/main/resources/config-store/config-cmis.xml +++ b/datastorages/cmis/src/main/resources/config-store/config-cmis.xml @@ -1,6 +1,6 @@