/* * Copyright (C) 2005 Alfresco, Inc. * * Licensed under the Mozilla Public License version 1.1 * with a permitted attribution clause. You may obtain a * copy of the License at * * http://www.alfresco.org/legal/license.txt * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied. See the License for the specific * language governing permissions and limitations under the * License. */ package org.alfresco.web.bean; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent; import javax.faces.model.SelectItem; import org.alfresco.config.ConfigService; import org.alfresco.model.ContentModel; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.CachingDateFormat; import org.alfresco.web.app.Application; import org.alfresco.web.bean.repository.Repository; import org.alfresco.web.config.ClientConfigElement; import org.alfresco.web.config.ClientConfigElement.CustomProperty; import org.alfresco.web.data.IDataContainer; import org.alfresco.web.data.QuickSort; import org.alfresco.web.ui.common.component.UIPanel.ExpandedEvent; import org.alfresco.web.ui.repo.component.UISearchCustomProperties; /** * Provides the form state and action event handling for the Advanced Search UI. *

* Integrates with the web-client ConfigService to retrieve configuration of custom * meta-data searching fields. Custom fields can be configured to appear in the UI * and they are they automatically added to the search query by this bean. * * @author Kevin Roast */ public class AdvancedSearchBean { /** * Default constructor */ public AdvancedSearchBean() { // initial state of progressive panels that don't use the default panels.put("categories-panel", false); panels.put("attrs-panel", false); panels.put("custom-panel", false); } // ------------------------------------------------------------------------------ // Bean property getters and setters /** * @param navigator The NavigationBean to set. */ public void setNavigator(NavigationBean navigator) { this.navigator = navigator; } /** * @param nodeService The NodeService to set. */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param namespaceService The NamespaceService to set. */ public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } /** * @return Returns the progressive panels expanded state map. */ public Map getPanels() { return this.panels; } /** * @param panels The progressive panels expanded state map. */ public void setPanels(Map panels) { this.panels = panels; } /** * @return Returns the folder to search, null for all. */ public String getLookin() { return this.lookin; } /** * @param lookin The folder to search in or null for all. */ public void setLookin(String lookIn) { this.lookin = lookIn; } /** * @return Returns the location. */ public NodeRef getLocation() { return this.location; } /** * @param location The location to set. */ public void setLocation(NodeRef location) { this.location = location; } /** * @return Returns the search mode. */ public String getMode() { return this.mode; } /** * @param mode The search mode to set. */ public void setMode(String mode) { this.mode = mode; } /** * @return Returns the text to search for. */ public String getText() { return this.text; } /** * @param text The text to set. */ public void setText(String text) { this.text = text; } /** * @return Returns the category. */ public NodeRef getCategory() { return this.category; } /** * @param category The category to set. */ public void setCategory(NodeRef category) { this.category = category; } /** * @return Returns true to search location children, false for just the specified location. */ public boolean getLocationChildren() { return this.locationChildren; } /** * @param locationChildren True to search location children, false for just the specified location. */ public void setLocationChildren(boolean locationChildren) { this.locationChildren = locationChildren; } /** * @return Returns true to search category children, false for just the specified category. */ public boolean getCategoryChildren() { return this.categoryChildren; } /** * @param categoryChildren True to search category children, false for just the specified category. */ public void setCategoryChildren(boolean categoryChildren) { this.categoryChildren = categoryChildren; } /** * @return Returns the createdDateFrom. */ public Date getCreatedDateFrom() { return this.createdDateFrom; } /** * @param createdDateFrom The createdDateFrom to set. */ public void setCreatedDateFrom(Date createdDate) { this.createdDateFrom = createdDate; } /** * @return Returns the description. */ public String getDescription() { return this.description; } /** * @param description The description to set. */ public void setDescription(String description) { this.description = description; } /** * @return Returns the modifiedDateFrom. */ public Date getModifiedDateFrom() { return this.modifiedDateFrom; } /** * @param modifiedDateFrom The modifiedDateFrom to set. */ public void setModifiedDateFrom(Date modifiedDate) { this.modifiedDateFrom = modifiedDate; } /** * @return Returns the createdDateTo. */ public Date getCreatedDateTo() { return this.createdDateTo; } /** * @param createdDateTo The createdDateTo to set. */ public void setCreatedDateTo(Date createdDateTo) { this.createdDateTo = createdDateTo; } /** * @return Returns the modifiedDateTo. */ public Date getModifiedDateTo() { return this.modifiedDateTo; } /** * @param modifiedDateTo The modifiedDateTo to set. */ public void setModifiedDateTo(Date modifiedDateTo) { this.modifiedDateTo = modifiedDateTo; } /** * @return Returns the title. */ public String getTitle() { return this.title; } /** * @param title The title to set. */ public void setTitle(String title) { this.title = title; } /** * @return Returns the author. */ public String getAuthor() { return this.author; } /** * @param author The author to set. */ public void setAuthor(String author) { this.author = author; } /** * @return Returns the modifiedDateChecked. */ public boolean isModifiedDateChecked() { return this.modifiedDateChecked; } /** * @param modifiedDateChecked The modifiedDateChecked to set. */ public void setModifiedDateChecked(boolean modifiedDateChecked) { this.modifiedDateChecked = modifiedDateChecked; } /** * @return Returns the createdDateChecked. */ public boolean isCreatedDateChecked() { return this.createdDateChecked; } /** * @return Returns the content type currenty selected */ public String getContentType() { return this.contentType; } /** * @param contentType Sets the currently selected content type */ public void setContentType(String contentType) { this.contentType = contentType; } /** * @return Returns the contentFormat. */ public String getContentFormat() { return this.contentFormat; } /** * @param contentFormat The contentFormat to set. */ public void setContentFormat(String contentFormat) { this.contentFormat = contentFormat; } /** * @return Returns the custom properties Map. */ public Map getCustomProperties() { return this.customProperties; } /** * @param customProperties The custom properties Map to set. */ public void setCustomProperties(Map customProperties) { this.customProperties = customProperties; } /** * @param createdDateChecked The createdDateChecked to set. */ public void setCreatedDateChecked(boolean createdDateChecked) { this.createdDateChecked = createdDateChecked; } /** * @return Returns a list of content object types to allow the user to select from */ public List getContentTypes() { if (this.contentTypes == null) { FacesContext context = FacesContext.getCurrentInstance(); // add the well known cm:content object type by default this.contentTypes = new ArrayList(5); this.contentTypes.add(new SelectItem(ContentModel.TYPE_CONTENT.toString(), Application.getMessage(context, MSG_CONTENT))); // add any configured content sub-types to the list List types = getClientConfig().getContentTypes(); if (types != null) { DictionaryService dictionaryService = Repository.getServiceRegistry(context).getDictionaryService(); for (String type : types) { QName idQName = Repository.resolveToQName(type); TypeDefinition typeDef = dictionaryService.getType(idQName); if (typeDef != null && dictionaryService.isSubClass(typeDef.getName(), ContentModel.TYPE_CONTENT)) { // try and get label from the dictionary String label = typeDef.getTitle(); // else just use the localname if (label == null) { label = idQName.getLocalName(); } this.contentTypes.add(new SelectItem(idQName.toString(), label)); } } // make sure the list is sorted by the label QuickSort sorter = new QuickSort(this.contentTypes, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); } } return this.contentTypes; } /** * @return Returns a list of content formats to allow the user to select from */ public List getContentFormats() { if (this.contentFormats == null) { this.contentFormats = new ArrayList(80); ServiceRegistry registry = Repository.getServiceRegistry(FacesContext.getCurrentInstance()); MimetypeService mimetypeService = registry.getMimetypeService(); // get the mime type display names Map mimeTypes = mimetypeService.getDisplaysByMimetype(); for (String mimeType : mimeTypes.keySet()) { this.contentFormats.add(new SelectItem(mimeType, mimeTypes.get(mimeType))); } // make sure the list is sorted by the values QuickSort sorter = new QuickSort(this.contentFormats, "label", true, IDataContainer.SORT_CASEINSENSITIVE); sorter.sort(); // add the "All Formats" constant marker at the top of the list (default selection) this.contentFormats.add(0, new SelectItem("", Application.getMessage(FacesContext.getCurrentInstance(), MSG_ALL_FORMATS))); } return this.contentFormats; } // ------------------------------------------------------------------------------ // Action event handlers /** * Handler to clear the advanced search screen form details */ public void reset(ActionEvent event) { this.text = ""; this.mode = MODE_ALL; this.lookin = LOOKIN_ALL; this.contentType = null; this.location = null; this.category = null; this.title = null; this.description = null; this.author = null; this.createdDateFrom = null; this.modifiedDateFrom = null; this.createdDateChecked = false; this.modifiedDateChecked = false; this.customProperties.clear(); } /** * Handler to perform a search based on the current criteria */ public String search() { String outcome = null; if (this.text != null && this.text.length() != 0) { // construct the Search Context and set on the navigation bean // then simply navigating to the browse screen will cause it pickup the Search Context SearchContext search = new SearchContext(); search.setText(this.text); if (this.mode.equals(MODE_ALL)) { search.setMode(SearchContext.SEARCH_ALL); } else if (this.mode.equals(MODE_FILES_TEXT)) { search.setMode(SearchContext.SEARCH_FILE_NAMES_CONTENTS); } else if (this.mode.equals(MODE_FILES)) { search.setMode(SearchContext.SEARCH_FILE_NAMES); } else if (this.mode.equals(MODE_FOLDERS)) { search.setMode(SearchContext.SEARCH_SPACE_NAMES); } // additional attributes search if (this.description != null && this.description.length() != 0) { search.addAttributeQuery(ContentModel.PROP_DESCRIPTION, this.description); } if (this.title != null && this.title.length() != 0) { search.addAttributeQuery(ContentModel.PROP_TITLE, this.title); } if (this.author != null && this.author.length() != 0) { search.addAttributeQuery(ContentModel.PROP_AUTHOR, this.author); } if (this.contentFormat != null && this.contentFormat.length() != 0) { search.setMimeType(this.contentFormat); } if (this.createdDateChecked == true) { SimpleDateFormat df = CachingDateFormat.getDateFormat(); String strCreatedDate = df.format(this.createdDateFrom); String strCreatedDateTo = df.format(this.createdDateTo); search.addRangeQuery(ContentModel.PROP_CREATED, strCreatedDate, strCreatedDateTo, true); } if (this.modifiedDateChecked == true) { SimpleDateFormat df = CachingDateFormat.getDateFormat(); String strModifiedDate = df.format(this.modifiedDateFrom); String strModifiedDateTo = df.format(this.modifiedDateTo); search.addRangeQuery(ContentModel.PROP_MODIFIED, strModifiedDate, strModifiedDateTo, true); } // walk each of the custom properties add add them as additional attributes for (String qname : this.customProperties.keySet()) { Object value = this.customProperties.get(qname); DataTypeDefinition typeDef = getCustomPropertyLookup().get(qname); if (typeDef != null) { QName typeName = typeDef.getName(); if (DataTypeDefinition.DATE.equals(typeName) || DataTypeDefinition.DATETIME.equals(typeName)) { // only apply date to search if the user has checked the enable checkbox if (value != null && Boolean.valueOf(value.toString()) == true) { SimpleDateFormat df = CachingDateFormat.getDateFormat(); String strDateFrom = df.format(this.customProperties.get( UISearchCustomProperties.PREFIX_DATE_FROM + qname)); String strDateTo = df.format(this.customProperties.get( UISearchCustomProperties.PREFIX_DATE_TO + qname)); search.addRangeQuery(QName.createQName(qname), strDateFrom, strDateTo, true); } } else if (DataTypeDefinition.BOOLEAN.equals(typeName)) { if (((Boolean)value) == true) { search.addFixedValueQuery(QName.createQName(qname), value.toString()); } } else if (DataTypeDefinition.NODE_REF.equals(typeName) || DataTypeDefinition.CATEGORY.equals(typeName)) { if (value != null) { search.addFixedValueQuery(QName.createQName(qname), value.toString()); } } else if (DataTypeDefinition.INT.equals(typeName) || DataTypeDefinition.LONG.equals(typeName) || DataTypeDefinition.FLOAT.equals(typeName) || DataTypeDefinition.DOUBLE.equals(typeName)) { String strVal = value.toString(); if (strVal != null && strVal.length() != 0) { search.addFixedValueQuery(QName.createQName(qname), strVal); } } else { // by default use toString() value - this is for text fields and unknown types String strVal = value.toString(); if (strVal != null && strVal.length() != 0) { search.addAttributeQuery(QName.createQName(qname), strVal); } } } } // location path search if (this.lookin.equals(LOOKIN_OTHER) && this.location != null) { search.setLocation(SearchContext.getPathFromSpaceRef( new NodeRef(Repository.getStoreRef(), this.location.getId()), this.locationChildren)); } // category path search if (this.category != null) { search.setCategories(new String[]{SearchContext.getPathFromSpaceRef( new NodeRef(Repository.getStoreRef(), this.category.getId()), this.categoryChildren)}); } // content type restriction if (this.contentType != null) { search.setContentType(this.contentType); } // set the Search Context onto the top-level navigator bean // this causes the browse screen to switch into search results view this.navigator.setSearchContext(search); outcome = "browse"; } return outcome; } /** * @return ClientConfigElement */ private ClientConfigElement getClientConfig() { if (clientConfigElement == null) { ConfigService configService = Application.getConfigService(FacesContext.getCurrentInstance()); clientConfigElement = (ClientConfigElement)configService.getGlobalConfig().getConfigElement( ClientConfigElement.CONFIG_ELEMENT_ID); } return clientConfigElement; } /** * Helper map to lookup custom property QName strings against a DataTypeDefinition * * @return custom property lookup Map */ private Map getCustomPropertyLookup() { if (customPropertyLookup == null) { customPropertyLookup = new HashMap(7, 1.0f); List customProps = getClientConfig().getCustomProperties(); if (customProps != null) { DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService(); for (CustomProperty customProp : customProps) { PropertyDefinition propDef = null; QName propQName = Repository.resolveToQName(customProp.Property); if (customProp.Type != null) { QName type = Repository.resolveToQName(customProp.Type); TypeDefinition typeDef = dd.getType(type); propDef = typeDef.getProperties().get(propQName); } else if (customProp.Aspect != null) { QName aspect = Repository.resolveToQName(customProp.Aspect); AspectDefinition aspectDef = dd.getAspect(aspect); propDef = aspectDef.getProperties().get(propQName); } customPropertyLookup.put(propQName.toString(), propDef.getDataType()); } } } return customPropertyLookup; } /** * Save the state of the progressive panel that was expanded/collapsed */ public void expandPanel(ActionEvent event) { if (event instanceof ExpandedEvent) { this.panels.put(event.getComponent().getId(), ((ExpandedEvent)event).State); } } // ------------------------------------------------------------------------------ // Private data private static final String MSG_CONTENT = "content"; private static final String MSG_ALL_FORMATS = "all_formats"; private static final String MODE_ALL = "all"; private static final String MODE_FILES_TEXT = "files_text"; private static final String MODE_FILES = "files"; private static final String MODE_FOLDERS = "folders"; private static final String LOOKIN_ALL = "all"; private static final String LOOKIN_OTHER = "other"; /** The NodeService to be used by the bean */ private NodeService nodeService; /** The NamespaceService to be used by the bean */ private NamespaceService namespaceService; /** The NavigationBean reference */ private NavigationBean navigator; /** Client Config reference */ private ClientConfigElement clientConfigElement = null; /** Progressive panel UI state */ private Map panels = new HashMap(5, 1.0f); /** custom property names to values */ private Map customProperties = new HashMap(5, 1.0f); /** lookup of custom property QName string to DataTypeDefinition for the property */ private Map customPropertyLookup = null; /** content types to for restricting searches */ private List contentTypes; /** content format list restricting searches */ private List contentFormats; /** content type selection */ private String contentType; /** content format selection */ private String contentFormat; /** the text to search for */ private String text = ""; /** search mode */ private String mode = MODE_ALL; /** folder lookin mode */ private String lookin = LOOKIN_ALL; /** Space Selector location */ private NodeRef location = null; /** categories to search */ private NodeRef category = null; /** title attribute to search */ private String title = null; /** description attribute to search */ private String description = null; /** created attribute to search from */ private Date createdDateFrom = null; /** created attribute to search to */ private Date createdDateTo = null; /** modified attribute to search from */ private Date modifiedDateFrom = null; /** modified attribute to search to */ private Date modifiedDateTo = null; /** true to search location children as well as location */ private boolean locationChildren = true; /** true to search category children as well as category */ private boolean categoryChildren = true; /** author (creator) attribute to search */ private String author = null; private boolean modifiedDateChecked = false; private boolean createdDateChecked = false; }