diff --git a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
index 7dbf92b9852..8af88040406 100644
--- a/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
+++ b/dspace-api/src/main/java/org/dspace/content/authority/DSpaceControlledVocabulary.java
@@ -203,6 +203,15 @@ public Choices getBestMatch(String text, String locale) {
return new Choices(choices.toArray(new Choice[choices.size()]), 0, choices.size(), Choices.CF_AMBIGUOUS, false);
}
+ /**
+ * - If the string doesn't contain any single quotes, it wraps the string in single quotes.
+ * - If the string contains single quotes but no double quotes, it wraps the string in double quotes.
+ * - If the string contains both single and double quotes,
+ * it constructs an XPath expression using the concat function.
+ * The goal is to allow the string to be safely used in XPath expressions regardless of the presence of quotes.
+ * @param text
+ * @return
+ */
private String escapeQuotes(String text) {
// If we don't have any quote then enquote string in single quote
if (!text.contains("'")) {
@@ -228,7 +237,7 @@ private String escapeQuotes(String text) {
sb.append(",");
}
- sb.append(String.format("\"%s\",'\"'", text.substring(lastPos, nextPos - lastPos)));
+ sb.append(String.format("\"%s\",'\"'", text.substring(lastPos, nextPos)));
lastPos = ++nextPos;
// Find next occurrence
diff --git a/dspace-api/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml b/dspace-api/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
index 9c81c75a675..c2ef1182c9c 100644
--- a/dspace-api/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
+++ b/dspace-api/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
@@ -367,6 +367,12 @@
Datorlingvistik
+
+ Datorlingvistik
+
+
+ Datorlingvistik
+
Datorlingvistik
diff --git a/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml b/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
index 9c81c75a675..c2ef1182c9c 100644
--- a/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
+++ b/dspace-server-webapp/src/test/data/dspaceFolder/config/controlled-vocabularies/srsc.xml
@@ -367,6 +367,12 @@
Datorlingvistik
+
+ Datorlingvistik
+
+
+ Datorlingvistik
+
Datorlingvistik
diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java
index 84dda6765a6..1c9490971da 100644
--- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java
+++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/VocabularyEntryDetailsIT.java
@@ -491,10 +491,45 @@ public void testSearchWithComma() throws Exception {
}
@Test
- public void testSearchWithColumn() throws Exception {
+ public void testSearchWithColon() throws Exception {
String tokenAdmin = getAuthToken(admin.getEmail(), password);
getClient(tokenAdmin).perform(get("/api/submission/vocabularies/srsc/entries?filter=test:example&exact=true"))
.andExpect(status().isOk())
.andExpect(jsonPath("_embedded.entries[0].display").value("test:example"));
}
+
+ @Test
+ public void testSearchWithApostropheAndQuoteReversed() throws Exception {
+ String tokenAdmin = getAuthToken(admin.getEmail(), password);
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=test2\"test'example\"'\"" +
+ "&exact==false"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("test2\"test'example\"'\"test"));
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=test2\"test'exam" +
+ "&exact==false"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("test2\"test'example\"'\"test"));
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=test2\"test'exam" +
+ "&exact==true"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("test2\"test'example\"'\"test"));
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=University's s\"" +
+ "&exact==false"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("University's s\"P\"apers"));
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=University's s\"P\"" +
+ "&exact==false"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("University's s\"P\"apers"));
+ getClient(tokenAdmin).perform(
+ get("/api/submission/vocabularies/srsc/entries?filter=University's s\"P\"apers" +
+ "&exact==true"))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("_embedded.entries[0].display").value("University's s\"P\"apers"));
+ }
}