mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
. First pass of the Saved Searches functionality for the web-client
. Saved Searches bootstrap folder . Document details and Space details page now show a copy-and-paste NodeRef link . Minor bug fixes to Breadcrumb and ActionLink components git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2104 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -17,13 +17,19 @@
|
||||
package org.alfresco.web.bean;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.search.impl.lucene.QueryParser;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
@@ -35,6 +41,12 @@ import org.alfresco.util.ISO9075;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.dom4j.Document;
|
||||
import org.dom4j.DocumentHelper;
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.io.OutputFormat;
|
||||
import org.dom4j.io.SAXReader;
|
||||
import org.dom4j.io.XMLWriter;
|
||||
|
||||
/**
|
||||
* Holds the context required to build a search query and can return the populated query.
|
||||
@@ -43,6 +55,28 @@ import org.apache.commons.logging.LogFactory;
|
||||
*/
|
||||
public final class SearchContext implements Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 6730844584074229969L;
|
||||
|
||||
/** XML serialization elements */
|
||||
private static final String ELEMENT_VALUE = "value";
|
||||
private static final String ELEMENT_FIXED_VALUES = "fixed-values";
|
||||
private static final String ELEMENT_INCLUSIVE = "inclusive";
|
||||
private static final String ELEMENT_UPPER = "upper";
|
||||
private static final String ELEMENT_LOWER = "lower";
|
||||
private static final String ELEMENT_RANGE = "range";
|
||||
private static final String ELEMENT_RANGES = "ranges";
|
||||
private static final String ELEMENT_NAME = "name";
|
||||
private static final String ELEMENT_ATTRIBUTE = "attribute";
|
||||
private static final String ELEMENT_ATTRIBUTES = "attributes";
|
||||
private static final String ELEMENT_MIMETYPE = "mimetype";
|
||||
private static final String ELEMENT_CONTENT_TYPE = "content-type";
|
||||
private static final String ELEMENT_CATEGORY = "category";
|
||||
private static final String ELEMENT_CATEGORIES = "categories";
|
||||
private static final String ELEMENT_LOCATION = "location";
|
||||
private static final String ELEMENT_MODE = "mode";
|
||||
private static final String ELEMENT_TEXT = "text";
|
||||
private static final String ELEMENT_SEARCH = "search";
|
||||
|
||||
/** Search mode constants */
|
||||
public final static int SEARCH_ALL = 0;
|
||||
public final static int SEARCH_FILE_NAMES_CONTENTS = 1;
|
||||
@@ -55,18 +89,12 @@ public final class SearchContext implements Serializable
|
||||
/** mode for the search */
|
||||
private int mode = SearchContext.SEARCH_ALL;
|
||||
|
||||
/** folder node location for the search */
|
||||
/** folder XPath location for the search */
|
||||
private String location = null;
|
||||
|
||||
/** categories to add to the search */
|
||||
private String[] categories = new String[0];
|
||||
|
||||
/** 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;
|
||||
|
||||
/** content type to restrict search against */
|
||||
private String contentType = null;
|
||||
|
||||
@@ -96,7 +124,7 @@ public final class SearchContext implements Serializable
|
||||
String query;
|
||||
|
||||
// the QName for the well known "name" attribute
|
||||
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
|
||||
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, ELEMENT_NAME));
|
||||
|
||||
// match against content text
|
||||
String text = this.text.trim();
|
||||
@@ -375,7 +403,7 @@ public final class SearchContext implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the node to search from or null for all.
|
||||
* @return Returns the node XPath to search in or null for all.
|
||||
*/
|
||||
public String getLocation()
|
||||
{
|
||||
@@ -383,7 +411,7 @@ public final class SearchContext implements Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* @param location The node to search from or null for all..
|
||||
* @param location The node XPATH to search from or null for all..
|
||||
*/
|
||||
public void setLocation(String location)
|
||||
{
|
||||
@@ -452,38 +480,6 @@ public final class SearchContext implements Serializable
|
||||
{
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional attribute to search against
|
||||
@@ -496,6 +492,11 @@ public final class SearchContext implements Serializable
|
||||
this.queryAttributes.put(qname, value);
|
||||
}
|
||||
|
||||
public String getAttributeQuery(QName qname)
|
||||
{
|
||||
return this.queryAttributes.get(qname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional range attribute to search against
|
||||
*
|
||||
@@ -509,6 +510,11 @@ public final class SearchContext implements Serializable
|
||||
this.rangeAttributes.put(qname, new RangeProperties(qname, lower, upper, inclusive));
|
||||
}
|
||||
|
||||
public RangeProperties getRangeProperty(QName qname)
|
||||
{
|
||||
return this.rangeAttributes.get(qname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an additional fixed value attribute to search against
|
||||
*
|
||||
@@ -520,11 +526,216 @@ public final class SearchContext implements Serializable
|
||||
this.queryFixedValues.put(qname, value);
|
||||
}
|
||||
|
||||
public String getFixedValueQuery(QName qname)
|
||||
{
|
||||
return this.queryFixedValues.get(qname);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return this SearchContext as XML
|
||||
*
|
||||
* Example:
|
||||
* <code>
|
||||
* <?xml version="1.0" encoding="UTF-8"?>
|
||||
* <search>
|
||||
* <text>CDATA</text>
|
||||
* <mode>int</mode>
|
||||
* <location>XPath</location>
|
||||
* <categories>
|
||||
* <category>XPath</category>
|
||||
* </categories>
|
||||
* <content-type>String</content-type>
|
||||
* <mimetype>String</mimetype>
|
||||
* <attributes>
|
||||
* <attribute name="String">String</attribute>
|
||||
* </attributes>
|
||||
* <ranges>
|
||||
* <range name="String">
|
||||
* <lower>String</lower>
|
||||
* <upper>String</upper>
|
||||
* <inclusive>boolean</inclusive>
|
||||
* </range>
|
||||
* </ranges>
|
||||
* <fixed-values>
|
||||
* <value name="String">String</value>
|
||||
* </fixed-values>
|
||||
* </search>
|
||||
* </code>
|
||||
*/
|
||||
public String toXML()
|
||||
{
|
||||
try
|
||||
{
|
||||
NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
|
||||
|
||||
Document doc = DocumentHelper.createDocument();
|
||||
|
||||
Element root = doc.addElement(ELEMENT_SEARCH);
|
||||
|
||||
root.addElement(ELEMENT_TEXT).addCDATA(this.text);
|
||||
root.addElement(ELEMENT_MODE).addText(Integer.toString(this.mode));
|
||||
if (this.location != null)
|
||||
{
|
||||
root.addElement(ELEMENT_LOCATION).addText(this.location);
|
||||
}
|
||||
|
||||
Element categories = root.addElement(ELEMENT_CATEGORIES);
|
||||
for (String path : this.categories)
|
||||
{
|
||||
categories.addElement(ELEMENT_CATEGORY).addText(path);
|
||||
}
|
||||
|
||||
if (this.contentType != null)
|
||||
{
|
||||
root.addElement(ELEMENT_CONTENT_TYPE).addText(this.contentType);
|
||||
}
|
||||
if (this.mimeType != null)
|
||||
{
|
||||
root.addElement(ELEMENT_MIMETYPE).addText(this.mimeType);
|
||||
}
|
||||
|
||||
Element attributes = root.addElement(ELEMENT_ATTRIBUTES);
|
||||
for (QName attrName : this.queryAttributes.keySet())
|
||||
{
|
||||
attributes.addElement(ELEMENT_ATTRIBUTE)
|
||||
.addAttribute(ELEMENT_NAME, attrName.toPrefixString(ns))
|
||||
.addCDATA(this.queryAttributes.get(attrName));
|
||||
}
|
||||
|
||||
Element ranges = root.addElement(ELEMENT_RANGES);
|
||||
for (QName rangeName : this.rangeAttributes.keySet())
|
||||
{
|
||||
RangeProperties rangeProps = this.rangeAttributes.get(rangeName);
|
||||
Element range = ranges.addElement(ELEMENT_RANGE);
|
||||
range.addAttribute(ELEMENT_NAME, rangeName.toPrefixString(ns));
|
||||
range.addElement(ELEMENT_LOWER).addText(rangeProps.lower);
|
||||
range.addElement(ELEMENT_UPPER).addText(rangeProps.upper);
|
||||
range.addElement(ELEMENT_INCLUSIVE).addText(Boolean.toString(rangeProps.inclusive));
|
||||
}
|
||||
|
||||
Element values = root.addElement(ELEMENT_FIXED_VALUES);
|
||||
for (QName valueName : this.queryFixedValues.keySet())
|
||||
{
|
||||
values.addElement(ELEMENT_VALUE)
|
||||
.addAttribute(ELEMENT_NAME, valueName.toPrefixString(ns))
|
||||
.addCDATA(this.queryFixedValues.get(valueName));
|
||||
}
|
||||
|
||||
StringWriter out = new StringWriter(1024);
|
||||
XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
|
||||
writer.setWriter(out);
|
||||
writer.write(doc);
|
||||
|
||||
return out.toString();
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to export SearchContext to XML.", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a SearchContext from an XML definition
|
||||
*
|
||||
* @param xml XML format SearchContext @see #toXML()
|
||||
*/
|
||||
public SearchContext fromXML(String xml)
|
||||
{
|
||||
try
|
||||
{
|
||||
NamespaceService ns = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getNamespaceService();
|
||||
|
||||
// get the root element
|
||||
SAXReader reader = new SAXReader();
|
||||
Document document = reader.read(new StringReader(xml));
|
||||
Element rootElement = document.getRootElement();
|
||||
Element textElement = rootElement.element(ELEMENT_TEXT);
|
||||
if (textElement != null)
|
||||
{
|
||||
this.text = textElement.getText();
|
||||
}
|
||||
Element modeElement = rootElement.element(ELEMENT_MODE);
|
||||
if (modeElement != null)
|
||||
{
|
||||
this.mode = Integer.parseInt(modeElement.getText());
|
||||
}
|
||||
Element locationElement = rootElement.element(ELEMENT_LOCATION);
|
||||
if (locationElement != null)
|
||||
{
|
||||
this.location = locationElement.getText();
|
||||
}
|
||||
Element categoriesElement = rootElement.element(ELEMENT_CATEGORIES);
|
||||
if (categoriesElement != null)
|
||||
{
|
||||
List<String> categories = new ArrayList<String>(4);
|
||||
for (Iterator i=categoriesElement.elementIterator(ELEMENT_CATEGORY); i.hasNext(); /**/)
|
||||
{
|
||||
Element categoryElement = (Element)i.next();
|
||||
categories.add(categoryElement.getText());
|
||||
}
|
||||
this.categories = categories.toArray(this.categories);
|
||||
}
|
||||
Element contentTypeElement = rootElement.element(ELEMENT_CONTENT_TYPE);
|
||||
if (contentTypeElement != null)
|
||||
{
|
||||
this.contentType = contentTypeElement.getText();
|
||||
}
|
||||
Element mimetypeElement = rootElement.element(ELEMENT_MIMETYPE);
|
||||
if (mimetypeElement != null)
|
||||
{
|
||||
this.mimeType = mimetypeElement.getText();
|
||||
}
|
||||
Element attributesElement = rootElement.element(ELEMENT_ATTRIBUTES);
|
||||
if (attributesElement != null)
|
||||
{
|
||||
for (Iterator i=attributesElement.elementIterator(ELEMENT_ATTRIBUTE); i.hasNext(); /**/)
|
||||
{
|
||||
Element attrElement = (Element)i.next();
|
||||
QName qname = QName.createQName(attrElement.attributeValue(ELEMENT_NAME), ns);
|
||||
addAttributeQuery(qname, attrElement.getText());
|
||||
}
|
||||
}
|
||||
Element rangesElement = rootElement.element(ELEMENT_RANGES);
|
||||
if (rangesElement != null)
|
||||
{
|
||||
for (Iterator i=rangesElement.elementIterator(ELEMENT_RANGE); i.hasNext(); /**/)
|
||||
{
|
||||
Element rangeElement = (Element)i.next();
|
||||
Element lowerElement = rangeElement.element(ELEMENT_LOWER);
|
||||
Element upperElement = rangeElement.element(ELEMENT_UPPER);
|
||||
Element incElement = rangeElement.element(ELEMENT_INCLUSIVE);
|
||||
if (lowerElement != null && upperElement != null && incElement != null)
|
||||
{
|
||||
QName qname = QName.createQName(rangeElement.attributeValue(ELEMENT_NAME), ns);
|
||||
addRangeQuery(qname,
|
||||
lowerElement.getText(), upperElement.getText(),
|
||||
Boolean.parseBoolean(incElement.getText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Element valuesElement = rootElement.element(ELEMENT_FIXED_VALUES);
|
||||
if (valuesElement != null)
|
||||
{
|
||||
for (Iterator i=valuesElement.elementIterator(ELEMENT_VALUE); i.hasNext(); /**/)
|
||||
{
|
||||
Element valueElement = (Element)i.next();
|
||||
QName qname = QName.createQName(valueElement.attributeValue(ELEMENT_NAME), ns);
|
||||
addFixedValueQuery(qname, valueElement.getText());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
throw new AlfrescoRuntimeException("Failed to import SearchContext from XML.", err);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple wrapper class for range query attribute properties
|
||||
*/
|
||||
private static class RangeProperties
|
||||
static class RangeProperties
|
||||
{
|
||||
QName qname;
|
||||
String lower;
|
||||
|
Reference in New Issue
Block a user