mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-14 17:58:59 +00:00
Merged HEAD-BUG-FIX (5.0/Cloud) to HEAD (5.0/Cloud)
85028: Merged PLATFORM1 (5.0/Cloud) to HEAD-BUG-FIX (5.0/Cloud) 84196: Addition of what I'm calling 'synthetic' facetable properties to SolrFacetService and REST API as part of ACE-2639. A synthetic property is something that is technically *NOT* a property, but which can be treated as such for the purposes of SOLR facetting/filtering. Currently we only support two synthetic properties: size and mimetype, which are defined within any Alfresco property of type d:content. This includes, but is not limited to, the cm:content property in all cm:content types in Alfresco. (Be careful with your cm:content. That name is heavily overloaded.) This checkin only supports what we need for 5.0. It is expressly not designed for resuse/extension in a general way. However, I have tried to push the less stable parts of this work into the implementation of the service and REST API and limit what's in the service interface to the more stable parts of the work. Also note that I had to add some l10n/i18n key-values for these synthetic properties. I've called the mimetype 'MIME type' in the l10n'd text as I have translations for that in all languages. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@85343 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -29,7 +29,7 @@ import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.alfresco.repo.i18n.StaticMessageLookup;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.repo.search.impl.solr.facet.SolrFacetService.SyntheticPropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.i18n.MessageLookup;
|
||||
import org.alfresco.service.namespace.NamespaceException;
|
||||
@@ -39,6 +39,7 @@ import org.alfresco.util.ModelUtil;
|
||||
import org.alfresco.util.ScriptPagingDetails;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.extensions.surf.util.I18NUtil;
|
||||
import org.springframework.extensions.webscripts.Cache;
|
||||
import org.springframework.extensions.webscripts.Status;
|
||||
import org.springframework.extensions.webscripts.WebScriptException;
|
||||
@@ -53,6 +54,7 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
|
||||
public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScript
|
||||
{
|
||||
public static final Log logger = LogFactory.getLog(FacetablePropertiesGet.class);
|
||||
|
||||
public static final String PROPERTIES_KEY = "properties";
|
||||
|
||||
private static final String QUERY_PARAM_MAX_ITEMS = "maxItems";
|
||||
@@ -64,17 +66,15 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
private static final String QUERY_PARAM_LOCALE = "locale";
|
||||
|
||||
private NamespaceService namespaceService;
|
||||
private MessageLookup messageLookup;
|
||||
private MessageLookup messageLookup;
|
||||
|
||||
public FacetablePropertiesGet() { messageLookup = new StaticMessageLookup(); }
|
||||
|
||||
public FacetablePropertiesGet()
|
||||
{
|
||||
messageLookup = new StaticMessageLookup();
|
||||
}
|
||||
public void setNamespaceService (NamespaceService service) { this.namespaceService = service; }
|
||||
|
||||
@Override protected Map<String, Object> executeImpl(final WebScriptRequest req, final Status status, final Cache cache)
|
||||
{
|
||||
// Allow all authenticated users view the filters
|
||||
// Allow all authenticated users in
|
||||
return unprotectedExecuteImpl(req, status, cache);
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
final Locale userLocale = (userLocaleString == null) ? Locale.getDefault() : new Locale(userLocaleString);
|
||||
|
||||
// There are multiple defined URIs for this REST endpoint. Some define a "classname" template var.
|
||||
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
||||
final Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
|
||||
final String contentClassName = templateVars.get(TEMPLATE_VAR_CLASSNAME);
|
||||
|
||||
QName contentClassQName;
|
||||
@@ -98,14 +98,20 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
|
||||
final Map<String, Object> model = new HashMap<>();
|
||||
|
||||
final SortedSet<FacetablePropertyData> facetableProperties;
|
||||
final SortedSet<FacetablePropertyFTLModel> facetableProperties;
|
||||
if (contentClassQName == null)
|
||||
{
|
||||
facetableProperties = toFacetablePropertyDataSet(facetService.getFacetableProperties(), userLocale);
|
||||
facetableProperties = toFacetablePropertyModel(facetService.getFacetableProperties(), userLocale);
|
||||
|
||||
final List<SyntheticPropertyDefinition> facetableSyntheticProperties = facetService.getFacetableSyntheticProperties();
|
||||
facetableProperties.addAll(toFacetablePropertyModel_(facetableSyntheticProperties, userLocale));
|
||||
}
|
||||
else
|
||||
{
|
||||
facetableProperties = toFacetablePropertyDataSet(facetService.getFacetableProperties(contentClassQName), userLocale);
|
||||
facetableProperties = toFacetablePropertyModel(facetService.getFacetableProperties(contentClassQName), userLocale);
|
||||
|
||||
final List<SyntheticPropertyDefinition> facetableSyntheticProperties = facetService.getFacetableSyntheticProperties(contentClassQName);
|
||||
facetableProperties.addAll(toFacetablePropertyModel_(facetableSyntheticProperties, userLocale));
|
||||
}
|
||||
|
||||
// The webscript allows for some further filtering of results:
|
||||
@@ -117,16 +123,16 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
{
|
||||
filters.add(new ResultFilter()
|
||||
{
|
||||
@Override public boolean filter(FacetablePropertyData facetableProperty)
|
||||
@Override public boolean filter(FacetablePropertyFTLModel facetableProperty)
|
||||
{
|
||||
final QName propQName = facetableProperty.getPropertyDefinition().getName();
|
||||
final QName propQName = facetableProperty.getQname();
|
||||
Collection<String> prefixes = namespaceService.getPrefixes(propQName.getNamespaceURI());
|
||||
return prefixes.contains(namespaceFilter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
List<FacetablePropertyData> filteredFacetableProperties = filter(facetableProperties, filters);
|
||||
List<FacetablePropertyFTLModel> filteredFacetableProperties = filter(facetableProperties, filters);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
@@ -144,94 +150,131 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
return model;
|
||||
}
|
||||
|
||||
/** This type defines the (inclusion) filtering of {@link FacetablePropertyData} in the response to this webscript. */
|
||||
/**
|
||||
* This type defines the (inclusion) filtering of {@link FacetablePropertyFTLModel}
|
||||
* in the response to this webscript.
|
||||
*/
|
||||
private static interface ResultFilter
|
||||
{
|
||||
public boolean filter(FacetablePropertyData facetableProperty);
|
||||
public boolean filter(FacetablePropertyFTLModel facetableProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a new List instance containing only those {@link FacetablePropertyData data} that
|
||||
* This method returns a new List instance containing only those {@link FacetablePropertyFTLModel data} that
|
||||
* satisfy all {@link ResultFilter filters}.
|
||||
*/
|
||||
private List<FacetablePropertyData> filter(Collection<FacetablePropertyData> propsData, List<ResultFilter> filters)
|
||||
private List<FacetablePropertyFTLModel> filter(Collection<FacetablePropertyFTLModel> propsData, List<ResultFilter> filters)
|
||||
{
|
||||
List<FacetablePropertyData> filteredResult = new ArrayList<>();
|
||||
List<FacetablePropertyFTLModel> filteredResult = new ArrayList<>();
|
||||
|
||||
for (FacetablePropertyData prop : propsData)
|
||||
for (FacetablePropertyFTLModel prop : propsData)
|
||||
{
|
||||
boolean passedAllFilters = true;
|
||||
for (ResultFilter filter : filters)
|
||||
{
|
||||
if (!filter.filter(prop)) { passedAllFilters = false; }
|
||||
}
|
||||
if (passedAllFilters) { filteredResult.add(prop); }
|
||||
if (passedAllFilters) { filteredResult.add(prop); }
|
||||
}
|
||||
|
||||
return filteredResult;
|
||||
}
|
||||
|
||||
/** This method returns a {@link FacetablePropertyData} for the specified {@link PropertyDefinition}. */
|
||||
private FacetablePropertyData toFacetablePropertyData(PropertyDefinition propDef, Locale locale)
|
||||
/** This method returns a {@link FacetablePropertyFTLModel} for the specified {@link PropertyDefinition}. */
|
||||
private FacetablePropertyFTLModel toFacetablePropertyModel(PropertyDefinition propDef, Locale locale)
|
||||
{
|
||||
String title = propDef.getTitle(messageLookup, locale);
|
||||
return new FacetablePropertyData(propDef, title);
|
||||
return new FacetablePropertyFTLModel(propDef, title);
|
||||
}
|
||||
|
||||
private SortedSet<FacetablePropertyData> toFacetablePropertyDataSet(Collection<PropertyDefinition> propDefs, Locale locale)
|
||||
private FacetablePropertyFTLModel toFacetablePropertyModel_(SyntheticPropertyDefinition propDef,
|
||||
Locale locale)
|
||||
{
|
||||
SortedSet<FacetablePropertyData> result = new TreeSet<>();
|
||||
// Note the hard-coded assumption here that all synthetic properties are defined only
|
||||
// within the cm:content property type. This code is not designed to be extended.
|
||||
// TODO We may need to make this code extensible in a future release.
|
||||
//
|
||||
// See e.g. content-model.properties for usage of this i18n key.
|
||||
final String i18nKeyPrefix = "cm_contentmodel.property.cm_content.cm_content.";
|
||||
final String localisedTitle = I18NUtil.getMessage(i18nKeyPrefix + propDef.syntheticPropertyName, locale);
|
||||
|
||||
return new SyntheticFacetablePropertyFTLModel(propDef.containingPropertyDef,
|
||||
localisedTitle,
|
||||
propDef.syntheticPropertyName,
|
||||
propDef.dataTypeDefinition);
|
||||
}
|
||||
|
||||
private SortedSet<FacetablePropertyFTLModel> toFacetablePropertyModel(Collection<PropertyDefinition> propDefs,
|
||||
Locale locale)
|
||||
{
|
||||
SortedSet<FacetablePropertyFTLModel> result = new TreeSet<>();
|
||||
for (PropertyDefinition propDef : propDefs)
|
||||
{
|
||||
result.add(toFacetablePropertyData(propDef, locale));
|
||||
result.add(toFacetablePropertyModel(propDef, locale));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private SortedSet<FacetablePropertyFTLModel> toFacetablePropertyModel_(Collection<SyntheticPropertyDefinition> propDefs,
|
||||
Locale locale)
|
||||
{
|
||||
SortedSet<FacetablePropertyFTLModel> result = new TreeSet<>();
|
||||
for (SyntheticPropertyDefinition propDef : propDefs)
|
||||
{
|
||||
result.add(toFacetablePropertyModel_(propDef, locale));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** A simple POJO/DTO intended primarily for use in an FTL model and rendering in the JSON API. */
|
||||
public static class FacetablePropertyData implements Comparable<FacetablePropertyData>
|
||||
public static class FacetablePropertyFTLModel implements Comparable<FacetablePropertyFTLModel>
|
||||
{
|
||||
private final PropertyDefinition propDef;
|
||||
private final String localisedTitle;
|
||||
private final String displayName;
|
||||
/** The Alfresco property definition which declares this facetable property. */
|
||||
protected final PropertyDefinition propDef;
|
||||
|
||||
public FacetablePropertyData(PropertyDefinition propDef, String localisedTitle)
|
||||
/** The localised title for this property. */
|
||||
protected final String localisedTitle;
|
||||
|
||||
/** A display name for this property. */
|
||||
protected String displayName;
|
||||
|
||||
/**
|
||||
* @param propDef The {@link PropertyDefinition}.
|
||||
* @param localisedTitle The localised title for this property e.g. "Titre".
|
||||
*/
|
||||
public FacetablePropertyFTLModel(PropertyDefinition propDef, String localisedTitle)
|
||||
{
|
||||
this.propDef = propDef;
|
||||
this.localisedTitle = localisedTitle;
|
||||
this.displayName = propDef.getName().getPrefixString() +
|
||||
(localisedTitle == null ? "" : " (" + localisedTitle + ")");
|
||||
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
|
||||
}
|
||||
|
||||
public PropertyDefinition getPropertyDefinition() { return this.propDef; }
|
||||
public String getLocalisedTitle() { return this.localisedTitle; }
|
||||
public String getDisplayName() { return this.displayName; }
|
||||
// We use "*Qname*" (small 'n') in these accessors to make the FTL easier to write.
|
||||
public String getShortQname() { return propDef.getName().getPrefixString(); }
|
||||
|
||||
@Override public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
|
||||
result = prime * result + ((localisedTitle == null) ? 0 : localisedTitle.hashCode());
|
||||
result = prime * result + ((propDef == null) ? 0 : propDef.hashCode());
|
||||
return result;
|
||||
}
|
||||
public QName getQname() { return propDef.getName(); }
|
||||
|
||||
public String getTitle() { return localisedTitle; }
|
||||
|
||||
public String getDisplayName() { return displayName; }
|
||||
|
||||
public QName getContainerClassType() { return propDef.getContainerClass().getName(); }
|
||||
|
||||
public QName getDataType() { return propDef.getDataType().getName(); }
|
||||
|
||||
public QName getModelQname() { return propDef.getModel().getName(); }
|
||||
|
||||
@Override public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
FacetablePropertyData other = (FacetablePropertyData) obj;
|
||||
if (this == obj) { return true; }
|
||||
if (obj == null) { return false; }
|
||||
if (getClass() != obj.getClass()) { return false; }
|
||||
|
||||
FacetablePropertyFTLModel other = (FacetablePropertyFTLModel) obj;
|
||||
if (displayName == null)
|
||||
{
|
||||
if (other.displayName != null)
|
||||
return false;
|
||||
} else if (!displayName.equals(other.displayName))
|
||||
return false;
|
||||
if (other.displayName != null) { return false; }
|
||||
} else if (!displayName.equals(other.displayName)) { return false; }
|
||||
if (localisedTitle == null)
|
||||
{
|
||||
if (other.localisedTitle != null)
|
||||
@@ -247,18 +290,66 @@ public class FacetablePropertiesGet extends AbstractSolrFacetConfigAdminWebScrip
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int compareTo(FacetablePropertyData that)
|
||||
@Override public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((displayName == null) ? 0 : displayName.hashCode());
|
||||
result = prime * result + ((localisedTitle == null) ? 0 : localisedTitle.hashCode());
|
||||
result = prime * result + ((propDef == null) ? 0 : propDef.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override public int compareTo(FacetablePropertyFTLModel that)
|
||||
{
|
||||
final int modelComparison = this.propDef.getModel().getName().compareTo(that.propDef.getModel().getName());
|
||||
final int classComparison = this.propDef.getContainerClass().getName().compareTo(that.propDef.getContainerClass().getName());
|
||||
final int propComparison = this.propDef.getName().compareTo(that.propDef.getName());
|
||||
// this comparison matters because it incorporates SyntheticProperties like size & mimetype. See below.
|
||||
final int propWithSynthetic = this.getShortQname().compareTo(that.getShortQname());
|
||||
|
||||
final int result;
|
||||
if (modelComparison != 0) { result = modelComparison; }
|
||||
else if (classComparison != 0) { result = classComparison; }
|
||||
else { result = propComparison; }
|
||||
else if (propComparison != 0) { result = propComparison; }
|
||||
else { result = propWithSynthetic; }
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class represents a facetable property, which is not actually an Alfresco
|
||||
* content property. Examples are the {@code size} and {@code MIME type} fields
|
||||
* within the {@code cm:content} property type.
|
||||
*/
|
||||
public static class SyntheticFacetablePropertyFTLModel extends FacetablePropertyFTLModel
|
||||
{
|
||||
/** This is the name of the synthetic property e.g. "size". Not localised. */
|
||||
private final String syntheticPropertyName;
|
||||
|
||||
/** The type of this synthetic data property. */
|
||||
private final QName datatype;
|
||||
|
||||
/**
|
||||
* @param containingPropDef The {@link PropertyDefinition}.
|
||||
* @param localisedTitle The localised title of this synthetic property e.g. "taille".
|
||||
* @param syntheticPropertyName The synthetic property name e.g. "size".
|
||||
*/
|
||||
public SyntheticFacetablePropertyFTLModel(PropertyDefinition containingPropDef,
|
||||
String localisedTitle,
|
||||
String syntheticPropertyName,
|
||||
QName datatype)
|
||||
{
|
||||
super(containingPropDef, localisedTitle);
|
||||
this.syntheticPropertyName = syntheticPropertyName;
|
||||
this.datatype = datatype;
|
||||
this.displayName = getShortQname() + (localisedTitle == null ? "" : " (" + localisedTitle + ")");
|
||||
}
|
||||
|
||||
@Override public String getShortQname() { return super.getShortQname() +
|
||||
"." + this.syntheticPropertyName; }
|
||||
|
||||
@Override public QName getDataType() { return datatype; }
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user