diff --git a/source/java/org/alfresco/repo/web/scripts/facet/FacetQNameUtils.java b/source/java/org/alfresco/repo/web/scripts/facet/FacetQNameUtils.java
new file mode 100644
index 0000000000..a00ba23cb6
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/facet/FacetQNameUtils.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.repo.web.scripts.facet;
+
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * This class provides some simple utility methods for dealing with {@link QName QNames}
+ * within the faceted search feature.
+ * These are not intended for general use, or else they'd be in the {@link QName} class.
+ * @since 5.0
+ */
+public abstract class FacetQNameUtils
+{
+ /**
+ * This method converts the supplied qname string into a {@link QName} object.
+ * It accepts both short and long form qname strings.
+ *
+ * @param s a qname string, such as "cm:name" or "{http://www.alfresco.org/model/content/1.0}name"
+ * @param resolver this is needed to convert any qname prefixes into their long form.
+ * @return the QName instance.
+ * @throws NullPointerException if the provided string is {@code null}.
+ * @throws IllegalArgumentException if the provided string could not be recognised as a valid QName.
+ */
+ public static QName createQName(String s, NamespacePrefixResolver resolver)
+ {
+ final QName result;
+
+ if (s.length() < 2) { throw new IllegalArgumentException("Cannot convert string '" + s + "'"); }
+
+ if (s.charAt(0) == QName.NAMESPACE_BEGIN &&
+ s.substring(1).contains(Character.toString(QName.NAMESPACE_END)))
+ {
+ // Assume it's a long-form qname.
+ result = QName.createQName(s);
+ }
+ else if ( !s.contains(Character.toString(QName.NAMESPACE_BEGIN)) &&
+ s.contains(Character.toString(QName.NAMESPACE_PREFIX)))
+ {
+ // Assume it's a short-form qname.
+ result = QName.createQName(s, resolver);
+ }
+ else
+ {
+ // We're not sure what sort of qname this is supposed to be.
+ throw new IllegalArgumentException("Cannot convert string '" + s + "'");
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPost.java b/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPost.java
index 490dca46a8..36a1cf67b2 100644
--- a/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPost.java
+++ b/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPost.java
@@ -80,7 +80,10 @@ public class SolrFacetConfigAdminPost extends AbstractSolrFacetConfigAdminWebScr
validateFilterID(filterID);
final String facetQNameStr = json.getString(PARAM_FACET_QNAME);
- final QName facetQName = QName.createQName(facetQNameStr, namespaceService);
+ // Note: we're using this util class here because we need to be able to deal with
+ // qnames without a URI e.g. "{}SITE" and *not* have them default to the cm: namespace
+ // which happens with the 'normal' Alfresco QName code.
+ final QName facetQName = FacetQNameUtils.createQName(facetQNameStr, namespaceService);
final String displayName = json.getString(PARAM_DISPLAY_NAME);
final String displayControl = json.getString(PARAM_DISPLAY_CONTROL);
final int maxFilters = json.getInt(PARAM_MAX_FILTERS);
diff --git a/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPut.java b/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPut.java
index 6288b2311f..08291915a3 100644
--- a/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPut.java
+++ b/source/java/org/alfresco/repo/web/scripts/facet/SolrFacetConfigAdminPut.java
@@ -141,7 +141,7 @@ public class SolrFacetConfigAdminPut extends AbstractSolrFacetConfigAdminWebScri
// Note that in resolving the QName string here, we expect there to be some facet QNames which are not
// really QNames. These are SOLR/SearchService 'specials', examples being "SITE" or "TAG".
// These will be resolved here to a QName with no namespace.
- final QName facetQName = (facetQNameStr == null) ? null : QName.createQName(facetQNameStr, namespaceService);
+ final QName facetQName = (facetQNameStr == null) ? null : FacetQNameUtils.createQName(facetQNameStr, namespaceService);
final String displayName = getValue(String.class, json.opt(PARAM_DISPLAY_NAME), null);
final String displayControl = getValue(String.class, json.opt(PARAM_DISPLAY_CONTROL), null);
final int maxFilters = getValue(Integer.class, json.opt(PARAM_MAX_FILTERS), -1);
diff --git a/source/test-java/org/alfresco/repo/web/scripts/facet/FacetQNameUtilsTest.java b/source/test-java/org/alfresco/repo/web/scripts/facet/FacetQNameUtilsTest.java
new file mode 100644
index 0000000000..026879bd16
--- /dev/null
+++ b/source/test-java/org/alfresco/repo/web/scripts/facet/FacetQNameUtilsTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005-2014 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.repo.web.scripts.facet;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.alfresco.service.namespace.NamespaceException;
+import org.alfresco.service.namespace.NamespacePrefixResolver;
+import org.alfresco.service.namespace.QName;
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link FacetQNameUtils}.
+ * @since 5.0
+ */
+public class FacetQNameUtilsTest
+{
+ /** A test-only namespace resolver. */
+ private final NamespacePrefixResolver resolver = new NamespacePrefixResolver()
+ {
+ private final List uris = Arrays.asList(new String[] { "http://www.alfresco.org/model/foo/1.0" });
+ private final List prefixes = Arrays.asList(new String[] { "foo" });
+
+ @Override public Collection getURIs() { return this.uris; }
+ @Override public Collection getPrefixes() { return this.prefixes; }
+
+ @Override public Collection getPrefixes(String namespaceURI) throws NamespaceException
+ {
+ if (uris.contains(namespaceURI))
+ { return prefixes; }
+ else
+ { throw new NamespaceException("Unrecognised namespace: " + namespaceURI); }
+ }
+
+ @Override public String getNamespaceURI(String prefix) throws NamespaceException
+ {
+ if (prefixes.contains(prefix))
+ { return "http://www.alfresco.org/model/foo/1.0"; }
+ else
+ { throw new NamespaceException("Unrecognised prefix: " + prefix); }
+ }
+ };
+
+ @Test public void canCreateFromShortForm() throws Exception
+ {
+ assertEquals(QName.createQName("http://www.alfresco.org/model/foo/1.0", "localName"),
+ FacetQNameUtils.createQName("foo:localName", resolver));
+ }
+
+ @Test public void canCreateFromLongForm() throws Exception
+ {
+ assertEquals(QName.createQName("http://www.alfresco.org/model/foo/1.0", "localName"),
+ FacetQNameUtils.createQName("{http://www.alfresco.org/model/foo/1.0}localName", resolver));
+ }
+
+ // Note: it doesn't really make sense to have a short-form qname with no prefix.
+ @Test public void canCreateFromLongFormWithNoPrefix() throws Exception
+ {
+ assertEquals(QName.createQName(null, "localName"),
+ FacetQNameUtils.createQName("{}localName", resolver));
+ }
+
+ @Test public void canCreateLongFormQnameWithUnrecognisedUri() throws Exception
+ {
+ // Intentionally no validation of URIs against dictionary.
+ assertEquals(QName.createQName("http://www.alfresco.org/model/illegal/1.0", "localName"),
+ FacetQNameUtils.createQName("{http://www.alfresco.org/model/illegal/1.0}localName", resolver));
+ }
+
+ @Test (expected=NamespaceException.class)
+ public void shortFormQnameWithUnrecognisedPrefixFails() throws Exception
+ {
+ FacetQNameUtils.createQName("illegal:localName", resolver);
+ }
+}
\ No newline at end of file
diff --git a/source/test-java/org/alfresco/repo/web/scripts/facet/FacetRestApiTest.java b/source/test-java/org/alfresco/repo/web/scripts/facet/FacetRestApiTest.java
index b2b39d2174..42ece36c25 100644
--- a/source/test-java/org/alfresco/repo/web/scripts/facet/FacetRestApiTest.java
+++ b/source/test-java/org/alfresco/repo/web/scripts/facet/FacetRestApiTest.java
@@ -35,14 +35,18 @@ import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
+import org.alfresco.util.GUID;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.collections.CollectionUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
+import org.springframework.extensions.webscripts.TestWebScriptServer.DeleteRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.GetRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.PostRequest;
+import org.springframework.extensions.webscripts.TestWebScriptServer.PutRequest;
import org.springframework.extensions.webscripts.TestWebScriptServer.Response;
-import org.springframework.extensions.webscripts.TestWebScriptServer.*;
/**
* This class tests the ReST API of the {@link SolrFacetService}.
@@ -504,7 +508,35 @@ public class FacetRestApiTest extends BaseWebScriptTest
}, SEARCH_ADMIN_USER);
}
-
+
+ /** The REST API should accept both 'cm:name' and '{http://www.alfresco.org/model/content/1.0}name' forms of filter IDs. */
+ public void testCreateFacetWithLongFormQnameFilterId() throws Exception
+ {
+ final JSONObject filter = new JSONObject();
+ final String filterName = "filter" + GUID.generate();
+ filters.add(filterName);
+ filter.put("filterID", filterName);
+ // This is the long-form qname that needs to be acceptable.
+ filter.put("facetQName", "{http://www.alfresco.org/model/content/1.0}testLongQname");
+ filter.put("displayName", "facet-menu.facet.testLongQname");
+ filter.put("displayControl", "alfresco/search/FacetFilters/testLongQname");
+ filter.put("maxFilters", 5);
+ filter.put("hitThreshold", 1);
+ filter.put("minFilterValueLength", 4);
+ filter.put("sortBy", "ALPHABETICALLY");
+
+ AuthenticationUtil.runAs(new RunAsWork()
+ {
+ @Override
+ public Void doWork() throws Exception
+ {
+ // Post the filter
+ sendRequest(new PostRequest(POST_FACETS_URL, filter.toString(), "application/json"), 200);
+ return null;
+ }
+ }, SEARCH_ADMIN_USER);
+ }
+
public void testUpdateSingleValue() throws Exception
{
// Build the Filter object