diff --git a/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStoreResource.java b/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStoreResource.java index 336cbf4e865..08f665b4c4e 100644 --- a/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStoreResource.java +++ b/core/src/main/java/org/fao/geonet/api/records/attachments/FilesystemStoreResource.java @@ -84,7 +84,7 @@ public FilesystemStoreResource(String metadataUuid, @Override public String getId() { - return UrlEscapers.urlFragmentEscaper().escape(metadataUuid) + + return UrlEscapers.urlPathSegmentEscaper().escape(metadataUuid) + "/attachments/" + UrlEscapers.urlFragmentEscaper().escape(filename); } diff --git a/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java b/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java index f09a537da72..961c327b366 100644 --- a/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java +++ b/services/src/main/java/org/fao/geonet/api/mapservers/MapServersApi.java @@ -351,7 +351,7 @@ public void deleteMapserver( // @Authorization(value = "basicAuth") // } ) - @RequestMapping(value = "/{mapserverId}/records/{metadataUuid}", + @RequestMapping(value = "/{mapserverId}/records/{metadataUuid:.+}", method = RequestMethod.GET, produces = { MediaType.TEXT_PLAIN_VALUE @@ -405,7 +405,7 @@ public String getMapserverResource( // @Authorization(value = "basicAuth") // } ) - @RequestMapping(value = "/{mapserverId}/records/{metadataUuid}", + @RequestMapping(value = "/{mapserverId}/records/{metadataUuid:.+}", method = RequestMethod.PUT, produces = { MediaType.TEXT_PLAIN_VALUE @@ -460,7 +460,7 @@ public String publishMapserverResource( // }) ) @RequestMapping( - value = "/{mapserverId}/records/{metadataUuid}", + value = "/{mapserverId}/records/{metadataUuid:.+}", method = RequestMethod.DELETE, produces = { MediaType.TEXT_PLAIN_VALUE diff --git a/services/src/main/java/org/fao/geonet/api/records/DoiApi.java b/services/src/main/java/org/fao/geonet/api/records/DoiApi.java index ed767e39022..47855baef57 100644 --- a/services/src/main/java/org/fao/geonet/api/records/DoiApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/DoiApi.java @@ -69,7 +69,7 @@ public class DoiApi { @io.swagger.v3.oas.annotations.Operation( summary = "Check that a record can be submitted to DataCite for DOI creation. " + "DataCite requires some fields to be populated.") - @RequestMapping(value = "/{metadataUuid}/doi/checkPreConditions", + @RequestMapping(value = "/{metadataUuid:.+}/doi/checkPreConditions", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE @@ -104,7 +104,7 @@ ResponseEntity> checkDoiStatus( @io.swagger.v3.oas.annotations.Operation( summary = "Submit a record to the Datacite metadata store in order to create a DOI.") - @RequestMapping(value = "/{metadataUuid}/doi", + @RequestMapping(value = "/{metadataUuid:.+}/doi", method = RequestMethod.PUT, produces = { MediaType.APPLICATION_JSON_VALUE @@ -141,7 +141,7 @@ ResponseEntity> createDoi( @io.swagger.v3.oas.annotations.Operation( summary = "Remove a DOI (this is not recommended, DOI are supposed to be persistent once created. This is mainly here for testing).") - @RequestMapping(value = "/{metadataUuid}/doi", + @RequestMapping(value = "/{metadataUuid:.+}/doi", method = RequestMethod.DELETE, produces = { MediaType.APPLICATION_JSON_VALUE diff --git a/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java b/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java index a368a671bd5..8a8dfa43d56 100644 --- a/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/InspireValidationApi.java @@ -125,7 +125,7 @@ public class InspireValidationApi { @io.swagger.v3.oas.annotations.Operation( summary = "Get test suites available.", description = "TG13, TG2, ...") - @RequestMapping(value = "/{metadataUuid}/validate/inspire/testsuites", + @RequestMapping(value = "/{metadataUuid:.+}/validate/inspire/testsuites", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE @@ -153,7 +153,7 @@ Map getTestSuites( + "An INSPIRE endpoint must be configured in Settings. " + "This activates an asyncronous process, this method does not return any report. " + "This method returns an id to be used to get the report.") - @RequestMapping(value = "/{metadataUuid}/validate/inspire", + @RequestMapping(value = "/{metadataUuid:.+}/validate/inspire", method = RequestMethod.PUT, produces = { MediaType.TEXT_PLAIN_VALUE diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java index 2f6650dd926..9775d93ff37 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataApi.java @@ -31,6 +31,7 @@ import jeeves.services.ReadWriteController; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; +import org.fao.geonet.Constants; import org.fao.geonet.api.API; import org.fao.geonet.api.ApiParams; import org.fao.geonet.api.ApiUtils; @@ -65,6 +66,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.net.URLEncoder; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -160,22 +162,22 @@ public String getRecord( List accept = Arrays.asList(acceptHeader.split(",")); String defaultFormatter = "xsl-view"; + String encodedUuid = URLEncoder.encode(metadataUuid, Constants.ENCODING); if (accept.contains(MediaType.TEXT_HTML_VALUE) || accept.contains(MediaType.APPLICATION_XHTML_XML_VALUE) || accept.contains("application/pdf")) { - return "forward:" + (metadataUuid + "/formatters/" + defaultFormatter); + return "redirect:" + (encodedUuid + "/formatters/" + defaultFormatter); } else if (accept.contains(MediaType.APPLICATION_XML_VALUE) || accept.contains(MediaType.APPLICATION_JSON_VALUE)) { - return "forward:" + (metadataUuid + "/formatters/xml"); + return "redirect:" + (encodedUuid + "/formatters/xml"); } else if (accept.contains("application/zip") || accept.contains(MEF_V1_ACCEPT_TYPE) || accept.contains(MEF_V2_ACCEPT_TYPE)) { - return "forward:" + (metadataUuid + "/formatters/zip"); + return "redirect:" + (encodedUuid + "/formatters/zip"); } else { - // FIXME this else is never reached because any of the accepted medias match one of the previous if conditions. response.setHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_XHTML_XML_VALUE); //response.sendRedirect(metadataUuid + "/formatters/" + defaultFormatter); - return "forward:" + (metadataUuid + "/formatters/" + defaultFormatter); + return "redirect:" + (encodedUuid + "/formatters/" + defaultFormatter); } } @@ -184,8 +186,8 @@ public String getRecord( description = "") @RequestMapping(value = { - "/{metadataUuid}/formatters/xml", - "/{metadataUuid}/formatters/json" + "/{metadataUuid:.+}/formatters/xml", + "/{metadataUuid:.+}/formatters/json" }, method = RequestMethod.GET, produces = { @@ -310,7 +312,7 @@ Object getRecordAs( description = "Metadata Exchange Format (MEF) is returned. MEF is a ZIP file containing " + "the metadata as XML and some others files depending on the version requested. " + "See http://geonetwork-opensource.org/manuals/trunk/eng/users/annexes/mef-format.html.") - @RequestMapping(value = "/{metadataUuid}/formatters/zip", + @RequestMapping(value = "/{metadataUuid:.+}/formatters/zip", method = RequestMethod.GET, consumes = { MediaType.ALL_VALUE @@ -549,7 +551,7 @@ public RelatedResponse getAssociatedResources( description = "Retrieve related services, datasets, onlines, thumbnails, sources, ... " + "to this records.
" + "More info") - @RequestMapping(value = "/{metadataUuid}/featureCatalog", + @RequestMapping(value = "/{metadataUuid:.+}/featureCatalog", method = RequestMethod.GET, produces = { MediaType.APPLICATION_XML_VALUE, diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java index 96fb5c9176d..380b4be49ee 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataInsertDeleteApi.java @@ -168,7 +168,7 @@ public class MetadataInsertDeleteApi { + "By default, a backup is made in ZIP format. After that, " + "the record attachments are removed, the document removed " + "from the index and then from the database.") - @RequestMapping(value = "/{metadataUuid}", method = RequestMethod.DELETE) + @RequestMapping(value = "/{metadataUuid:.+}", method = RequestMethod.DELETE) @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Record deleted."), @ApiResponse(responseCode = "403", description = ApiParams.API_RESPONSE_NOT_ALLOWED_CAN_EDIT)}) @ResponseStatus(HttpStatus.NO_CONTENT) diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java index f40d2e293b0..4d7a9830a54 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataProcessApi.java @@ -85,7 +85,7 @@ public class MetadataProcessApi { @io.swagger.v3.oas.annotations.Operation(summary = "Get suggestions", description ="Analyze the record an suggest processes to improve the quality of the record.
" + "More info") - @RequestMapping(value = "/{metadataUuid}/processes", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "/{metadataUuid:.+}/processes", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Record suggestions."), @@ -133,7 +133,7 @@ List getSuggestions( } @io.swagger.v3.oas.annotations.Operation(summary = "Preview process result", description =API_OP_NOTE_PROCESS) - @RequestMapping(value = "/{metadataUuid}/processes/{process:.+}", method = { + @RequestMapping(value = "/{metadataUuid:.+}/processes/{process:.+}", method = { RequestMethod.GET}, produces = MediaType.APPLICATION_XML_VALUE) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -158,7 +158,7 @@ ResponseEntity processRecordPreview( } @io.swagger.v3.oas.annotations.Operation(summary = "Apply a process", description =API_OP_NOTE_PROCESS) - @RequestMapping(value = "/{metadataUuid}/processes/{process:.+}", method = { + @RequestMapping(value = "/{metadataUuid:.+}/processes/{process:.+}", method = { RequestMethod.POST,}, produces = MediaType.APPLICATION_XML_VALUE) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataSavedQueryApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataSavedQueryApi.java index 071e4e3f6cc..a523d84398f 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataSavedQueryApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataSavedQueryApi.java @@ -59,7 +59,7 @@ */ @Service @RequestMapping(value = { - "/{portal}/api/records/{metadataUuid}" + "/{portal}/api/records/{metadataUuid:.+}" }) @Tag(name = API_CLASS_RECORD_TAG, description = API_CLASS_RECORD_OPS) diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java index 8170d9de5fa..da4051c4e3d 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataSharingApi.java @@ -160,7 +160,7 @@ public static Vector retrievePrivileges(ServiceContext conte @io.swagger.v3.oas.annotations.Operation( summary = "Set privileges for ALL group to publish the metadata for all users.") @RequestMapping( - value = "/{metadataUuid}/publish", + value = "/{metadataUuid:.+}/publish", method = RequestMethod.PUT ) @ApiResponses(value = { @@ -187,7 +187,7 @@ public void publish( @io.swagger.v3.oas.annotations.Operation( summary = "Unsets privileges for ALL group to publish the metadata for all users.") @RequestMapping( - value = "/{metadataUuid}/unpublish", + value = "/{metadataUuid:.+}/unpublish", method = RequestMethod.PUT ) @ApiResponses(value = { @@ -223,7 +223,7 @@ public void unpublish( "administrator, a reviewer or the owner of the record.
" + "More info") @RequestMapping( - value = "/{metadataUuid}/sharing", + value = "/{metadataUuid:.+}/sharing", method = RequestMethod.PUT ) @ApiResponses(value = { @@ -453,7 +453,7 @@ private void setOperations( summary = "Get record sharing settings", description = "Return current sharing options for a record.") @RequestMapping( - value = "/{metadataUuid}/sharing", + value = "/{metadataUuid:.+}/sharing", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE ) @@ -549,7 +549,7 @@ public SharingResponse getRecordSharingSettings( summary = "Set record group", description = "A record is related to one group.") @RequestMapping( - value = "/{metadataUuid}/group", + value = "/{metadataUuid:.+}/group", method = RequestMethod.PUT ) @ApiResponses(value = { @@ -738,7 +738,7 @@ MetadataProcessingReport setGroupAndOwner( summary = "Set record group and owner", description = "") @RequestMapping( - value = "/{metadataUuid}/ownership", + value = "/{metadataUuid:.+}/ownership", method = RequestMethod.PUT ) @ResponseStatus(HttpStatus.CREATED) diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataSocialApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataSocialApi.java index 364c3c9b8bb..ef7713c3890 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataSocialApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataSocialApi.java @@ -85,7 +85,7 @@ public class MetadataSocialApi { "When a remote rating is applied, the local rating is not updated. It will be updated on the next " + "harvest run (FIXME ?).") @RequestMapping( - value = "/{metadataUuid}/rate", + value = "/{metadataUuid:.+}/rate", method = RequestMethod.PUT ) @ResponseStatus(HttpStatus.CREATED) diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java index bcf51b1378f..252a368a758 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataTagApi.java @@ -86,7 +86,7 @@ public class MetadataTagApi { description = "Tags are used to classify information.
" + "More info") @RequestMapping( - value = "/{metadataUuid}/tags", + value = "/{metadataUuid:.+}/tags", produces = { MediaType.APPLICATION_JSON_VALUE }, @@ -114,7 +114,7 @@ public Set getRecordTags( summary = "Add tags to a record", description = "") @RequestMapping( - value = "/{metadataUuid}/tags", + value = "/{metadataUuid:.+}/tags", method = RequestMethod.PUT) @ResponseStatus(value = HttpStatus.CREATED) @ApiResponses(value = { @@ -181,7 +181,7 @@ public void tagRecord( summary = "Delete tags of a record", description = "") @RequestMapping( - value = "/{metadataUuid}/tags", + value = "/{metadataUuid:.+}/tags", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.NO_CONTENT) @ApiResponses(value = { diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataValidateApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataValidateApi.java index 0e98aa22866..7930d1e7087 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataValidateApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataValidateApi.java @@ -152,7 +152,7 @@ public static void restructureReportToHavePatternRuleHierarchy(Element errorRepo @io.swagger.v3.oas.annotations.Operation(summary = "Validate a record", description = "User MUST be able to edit the record to validate it. " + "FIXME : id MUST be the id of the current metadata record in session ?") - @RequestMapping(value = "/{metadataUuid}/validate/internal", method = RequestMethod.PUT, produces = { + @RequestMapping(value = "/{metadataUuid:.+}/validate/internal", method = RequestMethod.PUT, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) @ResponseStatus(HttpStatus.CREATED) @PreAuthorize("hasAuthority('Editor')") diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java index 5d9442630dd..4a185a93d7e 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataVersionningApi.java @@ -72,7 +72,7 @@ public class MetadataVersionningApi { summary = "(Experimental) Enable version control", description = "") @RequestMapping( - value = "/{metadataUuid}/versions", + value = "/{metadataUuid:.+}/versions", method = RequestMethod.PUT) @ResponseStatus(value = HttpStatus.OK) @PreAuthorize("hasAuthority('Editor')") diff --git a/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java b/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java index 112b5826b2d..c3f85837100 100644 --- a/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/MetadataWorkflowApi.java @@ -145,7 +145,7 @@ private enum State { @io.swagger.v3.oas.annotations.Operation(summary = "Get record status history", description = "") - @RequestMapping(value = "/{metadataUuid}/status", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) + @RequestMapping(value = "/{metadataUuid:.+}/status", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public List getRecordStatusHistory( @@ -169,7 +169,7 @@ public List getRecordStatusHistory( } @io.swagger.v3.oas.annotations.Operation(summary = "Get record status history by type", description = "") - @RequestMapping(value = "/{metadataUuid}/status/{type}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) + @RequestMapping(value = "/{metadataUuid:.+}/status/{type}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public List getRecordStatusHistoryByType( @@ -194,7 +194,7 @@ public List getRecordStatusHistoryByType( } @io.swagger.v3.oas.annotations.Operation(summary = "Get last workflow status for a record", description = "") - @RequestMapping(value = "/{metadataUuid}/status/workflow/last", method = RequestMethod.GET, produces = { + @RequestMapping(value = "/{metadataUuid:.+}/status/workflow/last", method = RequestMethod.GET, produces = { MediaType.APPLICATION_JSON_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Record status."), @@ -234,7 +234,7 @@ public MetadataWorkflowStatusResponse getStatus( } @io.swagger.v3.oas.annotations.Operation(summary = "Set the record status", description = "") - @RequestMapping(value = "/{metadataUuid}/status", method = RequestMethod.PUT) + @RequestMapping(value = "/{metadataUuid:.+}/status", method = RequestMethod.PUT) @PreAuthorize("hasAuthority('Editor')") @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Status updated."), @ApiResponse(responseCode = "400", description = "Metadata workflow not enabled."), @@ -291,7 +291,7 @@ public void setStatus(@Parameter(description = API_PARAM_RECORD_UUID, required = summary = "Close a record task", description = "") @RequestMapping( - value = "/{metadataUuid}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/close", + value = "/{metadataUuid:.+}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/close", method = RequestMethod.PUT ) @PreAuthorize("hasAuthority('Editor')") @@ -321,7 +321,7 @@ public void closeTask(@Parameter(description = API_PARAM_RECORD_UUID, required = } @io.swagger.v3.oas.annotations.Operation(summary = "Delete a record status", description = "") - @RequestMapping(value = "/{metadataUuid}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}", method = RequestMethod.DELETE) + @RequestMapping(value = "/{metadataUuid:.+}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}", method = RequestMethod.DELETE) @PreAuthorize("hasAuthority('Administrator')") @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Status removed."), @ApiResponse(responseCode = "404", description = "Status not found."), @@ -348,7 +348,7 @@ public void deleteRecordStatus( } @io.swagger.v3.oas.annotations.Operation(summary = "Delete all record status", description = "") - @RequestMapping(value = "/{metadataUuid}/status", method = RequestMethod.DELETE) + @RequestMapping(value = "/{metadataUuid:.+}/status", method = RequestMethod.DELETE) @PreAuthorize("hasAuthority('Administrator')") @ApiResponses(value = {@ApiResponse(responseCode = "204", description = "Status removed."), @ApiResponse(responseCode = "404", description = "Status not found."), @@ -471,7 +471,7 @@ private MetadataStatus convertParameter(int id, String uuid, MetadataStatusParam summary = "Get saved content from the status record before changes", description = "") @RequestMapping( - value = "/{metadataUuid}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/before", + value = "/{metadataUuid:.+}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/before", method = RequestMethod.GET, produces = { MediaType.APPLICATION_XML_VALUE @@ -501,7 +501,7 @@ public String showStatusBefore( @io.swagger.v3.oas.annotations.Operation( summary = "Get saved content from the status record after changes" ) - @RequestMapping(value = "/{metadataUuid}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/after", + @RequestMapping(value = "/{metadataUuid:.+}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/after", method = RequestMethod.GET, produces = { MediaType.APPLICATION_XML_VALUE @@ -531,7 +531,7 @@ public String showStatusAfter( @io.swagger.v3.oas.annotations.Operation( summary = "Restore saved content from a status record") @RequestMapping( - value = "/{metadataUuid}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/restore", + value = "/{metadataUuid:.+}/status/{statusId:[0-9]+}.{userId:[0-9]+}.{changeDate}/restore", method = RequestMethod.POST ) @PreAuthorize("hasAuthority('Editor')") diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java index 8a259e676cd..331ed9b8199 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsActionsApi.java @@ -96,7 +96,7 @@ public void init() { //response = MetadataResource.class) ) @RequestMapping( - value = "/{portal}/api/records/{metadataUuid}/attachments/print-thumbnail", + value = "/{portal}/api/records/{metadataUuid:.+}/attachments/print-thumbnail", method = RequestMethod.PUT) @ResponseStatus(HttpStatus.CREATED) @ApiResponses(value = { diff --git a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java index 9acd4bceccb..6eb92de5cc6 100644 --- a/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/attachments/AttachmentsApi.java @@ -72,7 +72,7 @@ */ @EnableWebMvc @Service -@RequestMapping(value = {"/{portal}/api/records/{metadataUuid}/attachments"}) +@RequestMapping(value = {"/{portal}/api/records/{metadataUuid:.+}/attachments"}) @Tag(name = "records", description = "Metadata record operations") public class AttachmentsApi { public static final Integer MIN_IMAGE_SIZE = 1; diff --git a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java index 7685f8db47b..f355e9a376e 100644 --- a/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/editing/MetadataEditingApi.java @@ -96,7 +96,7 @@ public class MetadataEditingApi { SchemaManager schemaManager; @io.swagger.v3.oas.annotations.Operation(summary = "Edit a record", description = "Return HTML form for editing.") - @RequestMapping(value = "/{metadataUuid}/editor", method = RequestMethod.GET, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor", method = RequestMethod.GET, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -160,7 +160,7 @@ public void startEditing( } @io.swagger.v3.oas.annotations.Operation(summary = "Save edits", description = "Save the HTML form content.") - @RequestMapping(value = "/{metadataUuid}/editor", method = RequestMethod.POST, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor", method = RequestMethod.POST, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -433,7 +433,7 @@ public void saveEdits( } @io.swagger.v3.oas.annotations.Operation(summary = "Cancel edits", description = "Cancel current editing session.") - @RequestMapping(value = "/{metadataUuid}/editor", method = RequestMethod.DELETE, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor", method = RequestMethod.DELETE, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.NO_CONTENT) @@ -452,7 +452,7 @@ public void cancelEdits(@Parameter(description = API_PARAM_RECORD_UUID, required } @io.swagger.v3.oas.annotations.Operation(summary = "Add element", description = "") - @RequestMapping(value = "/{metadataUuid}/editor/elements", method = RequestMethod.PUT, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor/elements", method = RequestMethod.PUT, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.OK) @@ -494,7 +494,7 @@ public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required } @io.swagger.v3.oas.annotations.Operation(summary = "Reorder element", description = "") - @RequestMapping(value = "/{metadataUuid}/editor/elements/{direction}", method = RequestMethod.PUT, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor/elements/{direction}", method = RequestMethod.PUT, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.CREATED) @@ -515,7 +515,7 @@ public void addElement(@Parameter(description = API_PARAM_RECORD_UUID, required } @io.swagger.v3.oas.annotations.Operation(summary = "Delete element", description = "") - @RequestMapping(value = "/{metadataUuid}/editor/elements", method = RequestMethod.DELETE, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor/elements", method = RequestMethod.DELETE, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.NO_CONTENT) @@ -539,7 +539,7 @@ public void deleteElement( } @io.swagger.v3.oas.annotations.Operation(summary = "Delete attribute", description = "") - @RequestMapping(value = "/{metadataUuid}/editor/attributes", method = RequestMethod.DELETE, consumes = { + @RequestMapping(value = "/{metadataUuid:.+}/editor/attributes", method = RequestMethod.DELETE, consumes = { MediaType.ALL_VALUE}, produces = {MediaType.APPLICATION_XML_VALUE}) @PreAuthorize("hasAuthority('Editor')") @ResponseStatus(HttpStatus.NO_CONTENT) diff --git a/services/src/main/java/org/fao/geonet/api/records/extent/MetadataExtentApi.java b/services/src/main/java/org/fao/geonet/api/records/extent/MetadataExtentApi.java index 4e586398e6b..4b41834c4c2 100644 --- a/services/src/main/java/org/fao/geonet/api/records/extent/MetadataExtentApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/extent/MetadataExtentApi.java @@ -117,7 +117,7 @@ public class MetadataExtentApi { summary = "Get record extents as image", description = API_EXTENT_DESCRIPTION) @RequestMapping( - value = "/{metadataUuid}/extents.png", + value = "/{metadataUuid:.+}/extents.png", produces = { MediaType.IMAGE_PNG_VALUE }, @@ -155,7 +155,7 @@ public HttpEntity getAllRecordExtentAsImage( summary = "Get list of record extents", description = API_EXTENT_DESCRIPTION) @RequestMapping( - value = "/{metadataUuid}/extents.json", + value = "/{metadataUuid:.+}/extents.json", produces = { MediaType.APPLICATION_JSON_VALUE }, @@ -217,7 +217,7 @@ public List getAllRecordExtentAsJson( summary = "Get one record extent as image", description = API_EXTENT_DESCRIPTION) @RequestMapping( - value = "/{metadataUuid}/extents/{geometryIndex}.png", + value = "/{metadataUuid:.+}/extents/{geometryIndex}.png", produces = { MediaType.IMAGE_PNG_VALUE }, diff --git a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java index ce96ed67083..7ead35ed3da 100644 --- a/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java +++ b/services/src/main/java/org/fao/geonet/api/records/formatters/FormatterApi.java @@ -166,7 +166,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IO } @RequestMapping(value = { - "/{portal}/api/records/{metadataUuid}/formatters/{formatterId}" + "/{portal}/api/records/{metadataUuid:.+}/formatters/{formatterId}" }, method = RequestMethod.GET, produces = { diff --git a/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java b/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java index 0fc2841e202..1530a6e09cc 100644 --- a/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java +++ b/services/src/main/java/org/fao/geonet/api/userfeedback/UserFeedbackAPI.java @@ -173,7 +173,7 @@ public ResponseEntity deleteUserFeedback( * @throws Exception the exception */ @io.swagger.v3.oas.annotations.Operation(summary = "Provides an average rating for a metadata record") - @RequestMapping(value = "/records/{metadataUuid}/userfeedbackrating", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) + @RequestMapping(value = "/records/{metadataUuid:.+}/userfeedbackrating", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public RatingAverage getMetadataRating( @@ -302,7 +302,7 @@ public UserFeedbackDTO getUserComment( description = " This list will include also the draft user feedback if the client is logged as reviewer." ) @RequestMapping( - value = "/records/{metadataUuid}/userfeedback", + value = "/records/{metadataUuid:.+}/userfeedback", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) @@ -487,7 +487,7 @@ public ResponseEntity newUserFeedback( summary = "Send an email to catalogue administrator or record's contact", description = "") @RequestMapping( - value = "/records/{metadataUuid}/alert", + value = "/records/{metadataUuid:.+}/alert", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.POST) @ResponseStatus(HttpStatus.CREATED) diff --git a/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js b/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js index 79e5ff760a7..3cdeddc55bc 100644 --- a/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js +++ b/web-ui/src/main/resources/catalog/components/catalog/CatalogService.js @@ -81,7 +81,7 @@ * @return {HttpPromise} Future object */ validate: function(id) { - return $http.put('../api/records/' + id + '/validate/internal'); + return $http.put('../api/records/' + encodeURIComponent(id) + '/validate/internal'); }, /** @@ -98,7 +98,7 @@ */ validateDirectoryEntry: function(id, newState) { var param = '?isvalid=' + (newState ? 'true' : 'false'); - return $http.put('../api/records/' + id + '/validate/internal' + param); + return $http.put('../api/records/' + encodeURIComponent(id) + '/validate/internal' + param); }, /** diff --git a/web-ui/src/main/resources/catalog/components/edit/fieldupload/FieldUploadDirective.js b/web-ui/src/main/resources/catalog/components/edit/fieldupload/FieldUploadDirective.js index be55bce59bd..f4a21a93ca3 100644 --- a/web-ui/src/main/resources/catalog/components/edit/fieldupload/FieldUploadDirective.js +++ b/web-ui/src/main/resources/catalog/components/edit/fieldupload/FieldUploadDirective.js @@ -137,7 +137,7 @@ scope.queue = []; scope.filestoreUploadOptions = { autoUpload: true, - url: '../api/records/' + gnCurrentEdit.uuid + + url: '../api/records/' + encodeURIComponent(gnCurrentEdit.uuid) + '/attachments?visibility=public', dropZone: $('#gn-overview-dropzone'), singleUpload: true, diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcDirective.js b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcDirective.js index c0eef236ac3..78168908b2c 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcDirective.js +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcDirective.js @@ -245,7 +245,7 @@ scope.queue = []; scope.filestoreUploadOptions = { autoUpload: true, - url: '../api/records/' + gnCurrentEdit.uuid + + url: '../api/records/' + encodeURIComponent(gnCurrentEdit.uuid) + '/attachments?visibility=public', dropZone: $('#gn-overview-dropzone'), singleUpload: true, @@ -623,7 +623,7 @@ }); return $http.put('../api/records/' + - scope.gnCurrentEdit.uuid + + encodeURIComponent(scope.gnCurrentEdit.uuid) + '/attachments/print-thumbnail', null, { params: { jsonConfig: angular.fromJson(jsonSpec), diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js index 581a744bcfc..a55e3e20173 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js @@ -214,7 +214,7 @@ getAllResources: function(types) { var defer = $q.defer(); - var url = '../api/records/' + gnCurrentEdit.uuid + '/related' + + var url = '../api/records/' + encodeURIComponent(gnCurrentEdit.uuid) + '/related' + (angular.isArray(types) ? '?' + types.join('&type=') : ''); $http.get(url, { headers: { diff --git a/web-ui/src/main/resources/catalog/components/edit/validationreport/ValidationReportService.js b/web-ui/src/main/resources/catalog/components/edit/validationreport/ValidationReportService.js index c9fa9d4e6ad..e2554dedc0f 100644 --- a/web-ui/src/main/resources/catalog/components/edit/validationreport/ValidationReportService.js +++ b/web-ui/src/main/resources/catalog/components/edit/validationreport/ValidationReportService.js @@ -35,7 +35,7 @@ return { get: function() { return $http.put( - '../api/records/' + gnCurrentEdit.uuid + '/validate/internal'); + '../api/records/' + encodeURIComponent(gnCurrentEdit.uuid) + '/validate/internal'); }, errorCheck: function() { return this.get() diff --git a/web-ui/src/main/resources/catalog/components/filestore/FileStoreDirective.js b/web-ui/src/main/resources/catalog/components/filestore/FileStoreDirective.js index b8302fb417a..e4ef665a7c1 100644 --- a/web-ui/src/main/resources/catalog/components/filestore/FileStoreDirective.js +++ b/web-ui/src/main/resources/catalog/components/filestore/FileStoreDirective.js @@ -143,7 +143,7 @@ scope.queue = []; scope.filestoreUploadOptions = { autoUpload: scope.autoUpload, - url: '../api/records/' + scope.uuid + + url: '../api/records/' + encodeURIComponent(scope.uuid) + '/attachments?visibility=' + defaultStatus, dropZone: $('#' + scope.id), singleUpload: false, diff --git a/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js b/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js index 89b35704aca..d880e0de51d 100644 --- a/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js +++ b/web-ui/src/main/resources/catalog/components/filestore/FileStoreService.js @@ -34,7 +34,7 @@ return { get: function(metadataUuid, filter) { return $http.get('../api/records/' + - metadataUuid + '/attachments', { + encodeURIComponent(metadataUuid) + '/attachments', { params: { filter: filter, _random: Math.floor(Math.random() * 10000), diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js index 8369f62e1b3..2cd8f5c4449 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionService.js @@ -417,7 +417,7 @@ this.assignCategories = function(metadataId, categories) { var defer = $q.defer(); - $http.get('../records/' + metadataId + + $http.get('../records/' + encodeURIComponent(metadataId) + '/tags?id=' + categories.join('&id=')) .success(function(data) { defer.resolve(data); diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionsDirective.js b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionsDirective.js index 2c2a1dd9e73..5b063620f56 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionsDirective.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/MetadataActionsDirective.js @@ -295,7 +295,7 @@ method = 'delete'; } $http[method]('../api/records/' + - scope.metadataUuid + '/tags?id=' + c.id) + encodeURIComponent(scope.metadataUuid) + '/tags?id=' + c.id) .then(function() { if (existIndex === -1) { scope.ids.push(c.id); diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js index 130ae5f30b1..37368c9da96 100644 --- a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js +++ b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js @@ -46,7 +46,7 @@ var canceller = $q.defer(); var request = $http({ method: 'get', - url: '../api/records/' + uuidOrId + '/related?' + + url: '../api/records/' + encodeURIComponent(uuidOrId) + '/related?' + (types ? 'type=' + types.split('|').join('&type=') : ''), diff --git a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewDirective.js b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewDirective.js index 8544fb4715f..bac2053b669 100644 --- a/web-ui/src/main/resources/catalog/components/search/mdview/mdviewDirective.js +++ b/web-ui/src/main/resources/catalog/components/search/mdview/mdviewDirective.js @@ -56,17 +56,19 @@ var hyperlinkTagName = 'A'; if (element.get(0).tagName === hyperlinkTagName) { - var url = window.location.pathname + '#/' + - (scope.md.draft == 'y' ? 'metadraf' : 'metadata') + - '/' + scope.md.uuid + - (scope.formatter === undefined || scope.formatter == '' ? - '' : - formatter); + var url = window.location.pathname + + (window.location.search === '?debug' ? '?debug' : '') + + '#/' + + (scope.md.draft == 'y' ? 'metadraf' : 'metadata') + + '/' + + encodeURIComponent(scope.md.uuid) + + (scope.formatter === undefined || scope.formatter == '' + ? '' : formatter); element.attr('href', url); } else { element.on('click', function(e) { - gnMdView.setLocationUuid(scope.md.uuid, formatter); + gnMdView.setLocationUuid(encodeURIComponent(scope.md.uuid), formatter); }); } if (scope.records && scope.records.length) { diff --git a/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js b/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js index 8365e353cdc..3916fc3f752 100644 --- a/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js +++ b/web-ui/src/main/resources/catalog/components/search/searchmanager/LocationService.js @@ -116,7 +116,7 @@ this.getUuid = function() { if (this.isMdView()) { - return $location.path().split('/')[2]; + return $location.path().replace(/\/metadata\/(.*)($|\/formatters.*)/, '$1'); } }; diff --git a/web-ui/src/main/resources/catalog/components/userfeedback/GnUserfeedbackDirective.js b/web-ui/src/main/resources/catalog/components/userfeedback/GnUserfeedbackDirective.js index 3cc1b15a965..9c359ebaebc 100644 --- a/web-ui/src/main/resources/catalog/components/userfeedback/GnUserfeedbackDirective.js +++ b/web-ui/src/main/resources/catalog/components/userfeedback/GnUserfeedbackDirective.js @@ -48,7 +48,7 @@ var numberOfCommentsDisplayed = size || -1; return $http({ method: 'GET', - url: '../api/records/' + metatdataUUID + + url: '../api/records/' + encodeURIComponent(metatdataUUID) + '/userfeedback?size=' + numberOfCommentsDisplayed, isArray: true }); @@ -57,7 +57,7 @@ this.loadRating = function(metatdataUUID) { return $http({ method: 'GET', - url: '../api/records/' + metatdataUUID + '/userfeedbackrating', + url: '../api/records/' + encodeURIComponent(metatdataUUID) + '/userfeedbackrating', isArray: false }); }; diff --git a/web-ui/src/main/resources/catalog/components/validationtools/GnmdInspireValidationDirective.js b/web-ui/src/main/resources/catalog/components/validationtools/GnmdInspireValidationDirective.js index c0024114e97..80b8d235ed0 100644 --- a/web-ui/src/main/resources/catalog/components/validationtools/GnmdInspireValidationDirective.js +++ b/web-ui/src/main/resources/catalog/components/validationtools/GnmdInspireValidationDirective.js @@ -56,7 +56,7 @@ scope.md = gnCurrentEdit.metadata; $http({ method: 'GET', - url: '../api/records/' + scope.inspMdUuid + + url: '../api/records/' + encodeURIComponent(scope.inspMdUuid) + '/validate/inspire/testsuites' }).then(function(r) { scope.testsuites = r.data; diff --git a/web-ui/src/main/resources/catalog/views/default/directives/partials/mdactionmenu.html b/web-ui/src/main/resources/catalog/views/default/directives/partials/mdactionmenu.html index bf5a709ca65..b8b5f7d306d 100644 --- a/web-ui/src/main/resources/catalog/views/default/directives/partials/mdactionmenu.html +++ b/web-ui/src/main/resources/catalog/views/default/directives/partials/mdactionmenu.html @@ -207,7 +207,7 @@
  • -   {{f.label | translate}} diff --git a/web-ui/src/main/resources/catalog/views/default/templates/recordView.html b/web-ui/src/main/resources/catalog/views/default/templates/recordView.html index 64cd619be05..1ea2d8951f6 100644 --- a/web-ui/src/main/resources/catalog/views/default/templates/recordView.html +++ b/web-ui/src/main/resources/catalog/views/default/templates/recordView.html @@ -531,7 +531,7 @@

    {{'extent' | translate}} + data-ng-src="../api/records/{{mdView.current.record.uuid | encodeURIComponent}}/extents.png"/>

    diff --git a/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml b/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml index 46831cffaad..1e616c3804e 100644 --- a/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml +++ b/web/src/main/webapp/WEB-INF/config-security/config-security-core.xml @@ -63,8 +63,32 @@ + +