diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml
index 6a77f1e014..b6e104e713 100644
--- a/config/alfresco/authentication-services-context.xml
+++ b/config/alfresco/authentication-services-context.xml
@@ -338,7 +338,8 @@
-
+
+
@@ -350,6 +351,14 @@
+
+
+
+
+
+
+
+
diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
index b7d31a23a0..b2cd233f20 100644
--- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
+++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml
@@ -72,13 +72,16 @@ Inbound settings from iBatis
-
+
+
+
+
@@ -223,6 +226,7 @@ Inbound settings from iBatis
+
diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-people-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-people-common-SqlMap.xml
new file mode 100644
index 0000000000..5946c497f5
--- /dev/null
+++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-people-common-SqlMap.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java b/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java
index 870f18bdba..6ac430299a 100644
--- a/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java
+++ b/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2011 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -64,8 +64,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
private RepoCtx ctx = null;
- private volatile boolean busy;
-
public void setActivityPostServiceImpl(ActivityPostServiceImpl activityPostServiceImpl)
{
this.activityPostServiceImpl = activityPostServiceImpl;
@@ -135,8 +133,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
{
ctx = new RepoCtx(sysAdminParams, repoEndPoint);
ctx.setUserNamesAreCaseSensitive(userNamesAreCaseSensitive);
-
- busy = false;
}
/**
@@ -151,11 +147,6 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
abstract public int getEstimatedGridSize();
- protected boolean isActive()
- {
- return busy;
- }
-
public void execute() throws JobExecutionException
{
checkProperties();
@@ -171,10 +162,11 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
}
String lockToken = null;
+ LockCallback lockCallback = null;
try
{
- JobLockRefreshCallback lockCallback = new LockCallback();
+ lockCallback = new LockCallback();
lockToken = acquireLock(lockCallback);
@@ -213,7 +205,7 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
}
finally
{
- releaseLock(lockToken);
+ releaseLock(lockCallback, lockToken);
}
}
@@ -221,6 +213,8 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
private class LockCallback implements JobLockRefreshCallback
{
+ private volatile boolean busy = false;
+
@Override
public boolean isActive()
{
@@ -251,7 +245,7 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
// Got the lock - now register the refresh callback which will keep the lock alive
jobLockService.refreshLock(lockToken, LOCK_QNAME, LOCK_TTL, lockCallback);
- busy = true;
+ ((LockCallback)lockCallback).busy = true;
if (logger.isDebugEnabled())
{
@@ -261,11 +255,11 @@ public abstract class AbstractFeedGenerator implements FeedGenerator
return lockToken;
}
- private void releaseLock(String lockToken)
+ private void releaseLock(LockCallback lockCallback, String lockToken)
{
- if (lockToken != null)
+ if (lockCallback != null && lockToken != null)
{
- busy = false;
+ ((LockCallback)lockCallback).busy = false;
jobLockService.releaseLock(lockToken, LOCK_QNAME);
diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java
index f9e4d0ed49..dc14a650a7 100644
--- a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java
+++ b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java
@@ -969,6 +969,10 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac
}
else
{
+ if(valueStr.indexOf("\u0000") != -1)
+ {
+ valueStr = valueStr.replaceAll("\u0000", "");
+ }
// Keep the trimmed value
value = valueStr;
}
diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
index a0525ee504..cd1ba4b97c 100644
--- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
+++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java
@@ -1831,6 +1831,13 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// The node's version has moved on so no need to invalidate caches
}
+ // ALF-16366: Ensure index impact is accounted for. If the node is being deleted we would expect the
+ // appropriate events to be fired manually
+ if (!nodeUpdate.isUpdateTypeQNameId() || !getNodeNotNull(nodeId, false).getDeleted(qnameDAO))
+ {
+ nodeIndexer.indexUpdateNode(oldNode.getNodeRef());
+ }
+
// Done
if (isDebugEnabled)
{
diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java
index c9b9ca21af..29147d9f0c 100644
--- a/source/java/org/alfresco/repo/jscript/People.java
+++ b/source/java/org/alfresco/repo/jscript/People.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2011 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -48,7 +48,6 @@ import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.security.PersonService.PersonInfo;
import org.alfresco.service.cmr.usage.ContentUsageService;
-import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyMap;
@@ -76,7 +75,6 @@ public final class People extends BaseScopableProcessorExtension implements Init
private AuthorityDAO authorityDAO;
private AuthorityService authorityService;
private PersonService personService;
- private NamespaceService namespaceService;
private MutableAuthenticationService authenticationService;
private ContentUsageService contentUsageService;
private TenantService tenantService;
@@ -85,7 +83,10 @@ public final class People extends BaseScopableProcessorExtension implements Init
private StoreRef storeRef;
private ValueDerivingMapFactory valueDerivingMapFactory;
private int numRetries = 10;
-
+
+ private int defaultListMaxResults = 5000;
+
+ private static final String HINT_CQ_SUFFIX = " [hint:useCQ]";
public void afterPropertiesSet() throws Exception
{
@@ -192,11 +193,6 @@ public final class People extends BaseScopableProcessorExtension implements Init
this.personService = personService;
}
- public void setNamespaceService(NamespaceService namespaceService)
- {
- this.namespaceService = namespaceService;
- }
-
/**
* @param contentUsageService the ContentUsageService to set
*/
@@ -233,6 +229,11 @@ public final class People extends BaseScopableProcessorExtension implements Init
this.userRegistrySynchronizer = userRegistrySynchronizer;
}
+ public void setDefaultListMaxResults(int defaultListMaxResults)
+ {
+ this.defaultListMaxResults = defaultListMaxResults;
+ }
+
/**
* Delete a Person with the given username
*
@@ -508,23 +509,27 @@ public final class People extends BaseScopableProcessorExtension implements Init
*/
public Scriptable getPeople(String filter, int maxResults)
{
- Object[] people = null;
-
- // TODO - remove open-ended query (eg cutoff at default/configurable max, eg. 5000 people)
- if (maxResults <= 0)
+ boolean useCQ = false;
+ if (filter != null)
{
- maxResults = Integer.MAX_VALUE;
+ if (filter.endsWith(HINT_CQ_SUFFIX))
+ {
+ useCQ = true;
+ filter = filter.substring(0, filter.length()-HINT_CQ_SUFFIX.length());
+ }
}
- if (filter == null || filter.length() == 0)
+ Object[] people = null;
+
+ if ((maxResults <= 0) || (maxResults > defaultListMaxResults))
{
- PagingRequest pagingRequest = new PagingRequest(maxResults, null);
- List persons = personService.getPeople(null, true, null, pagingRequest).getPage();
- people = new Object[persons.size()];
- for (int i=0; i> filterProps = new ArrayList>(3);
- filterProps.add(new Pair(ContentModel.PROP_FIRSTNAME, propVal));
- filterProps.add(new Pair(ContentModel.PROP_LASTNAME, propVal));
- filterProps.add(new Pair(ContentModel.PROP_USERNAME, propVal));
-
- PagingRequest pagingRequest = new PagingRequest(maxResults, null);
- List persons = personService.getPeople(filterProps, true, null, pagingRequest).getPage();
- people = new Object[persons.size()];
- for (int i=0; i 0)
- {
- query.append('"');
- }
- else
- {
- query.append("*\"");
- }
- }
- else
- {
- // scan for non-fts-alfresco property search tokens
- int nonFtsTokens = 0;
- while (t.hasMoreTokens())
- {
- if (t.nextToken().indexOf(':') == -1) nonFtsTokens++;
- }
- t = new StringTokenizer(term, " ");
-
+ }
+ }
+ }
+
+ if (people == null)
+ {
+ people = new Object[0];
+ }
+
+ return Context.getCurrentContext().newArray(getScope(), people);
+ }
+
+ // canned query
+ private Object[] getPeopleImplDB(String term, int maxResults)
+ {
+ Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+ Object[] people = null;
+
+ // simple non-FTS filter: firstname or lastname or username starting with term (ignoring case)
+
+ List filterProps = new ArrayList(3);
+ filterProps.add(ContentModel.PROP_FIRSTNAME);
+ filterProps.add(ContentModel.PROP_LASTNAME);
+ filterProps.add(ContentModel.PROP_USERNAME);
+
+ List> sortProps = new ArrayList>(1);
+ sortProps.add(new Pair(ContentModel.PROP_USERNAME, true));
+
+ PagingRequest pagingRequest = new PagingRequest(maxResults, null);
+ List persons = personService.getPeople(term, filterProps, sortProps, pagingRequest).getPage();
+ people = new Object[persons.size()];
+ for (int i=0; i 0)
+ {
+ query.append('"');
+ }
+ else
+ {
+ query.append("*\"");
+ }
+ }
+ else
+ {
+ // scan for non-fts-alfresco property search tokens
+ int nonFtsTokens = 0;
+ while (t.hasMoreTokens())
+ {
+ if (t.nextToken().indexOf(':') == -1) nonFtsTokens++;
+ }
+ t = new StringTokenizer(term, " ");
+
// multiple terms supplied - look for first and second name etc.
// assume first term is first name, any more are second i.e. "Fraun van de Wiels"
// also allow fts-alfresco property search to reduce results
@@ -618,77 +666,77 @@ public final class People extends BaseScopableProcessorExtension implements Init
query.append("firstName:\"");
query.append(term);
query.append("*\" ");
-
- firstToken = false;
- }
- else
- {
- if (tokenSurname)
- {
- query.append("OR ");
- }
- query.append("lastName:\"");
- query.append(term);
- query.append("*\" ");
-
- tokenSurname = true;
- }
- }
- }
- else
- {
- // fts-alfresco property search i.e. "location:maidenhead"
- propIndex = term.lastIndexOf(':');
- query.append(term.substring(0, propIndex+1))
- .append('"')
- .append(term.substring(propIndex+1))
- .append('"');
-
- propertySearch = true;
- }
- }
- }
- query.append(")");
-
- // define the search parameters
- params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
- params.addStore(this.storeRef);
- params.setQuery(query.toString());
- if (maxResults > 0)
- {
- params.setLimitBy(LimitBy.FINAL_SIZE);
- params.setLimit(maxResults);
- }
-
- ResultSet results = null;
- try
- {
- results = services.getSearchService().query(params);
- people = results.getNodeRefs().toArray();
- }
- catch (Throwable err)
- {
- // hide query parse error from users
- if (logger.isDebugEnabled())
- logger.debug("Failed to execute people search: " + query.toString(), err);
- }
- finally
- {
- if (results != null)
- {
- results.close();
- }
- }
- }
- }
- }
-
- if (people == null)
- {
- people = new Object[0];
- }
-
- return Context.getCurrentContext().newArray(getScope(), people);
+
+ firstToken = false;
+ }
+ else
+ {
+ if (tokenSurname)
+ {
+ query.append("OR ");
+ }
+ query.append("lastName:\"");
+ query.append(term);
+ query.append("*\" ");
+
+ tokenSurname = true;
+ }
+ }
+ }
+ else
+ {
+ // fts-alfresco property search i.e. "location:maidenhead"
+ propIndex = term.lastIndexOf(':');
+ query.append(term.substring(0, propIndex+1))
+ .append('"')
+ .append(term.substring(propIndex+1))
+ .append('"');
+
+ propertySearch = true;
+ }
+ }
+ }
+ query.append(")");
+
+ // define the search parameters
+ params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
+ params.addStore(this.storeRef);
+ params.setQuery(query.toString());
+ if (maxResults > 0)
+ {
+ params.setLimitBy(LimitBy.FINAL_SIZE);
+ params.setLimit(maxResults);
+ }
+
+ ResultSet results = null;
+ try
+ {
+ results = services.getSearchService().query(params);
+ people = results.getNodeRefs().toArray();
+
+ if (start != null)
+ {
+ logger.debug("getPeople: search - "+people.length+" items (in "+(System.currentTimeMillis()-start)+" msecs)");
+ }
+ }
+ catch (Throwable err)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Failed to execute people search: " + query.toString(), err);
+ }
+
+ throw err;
+ }
+ finally
+ {
+ if (results != null)
+ {
+ results.close();
+ }
+ }
+
+ return people;
}
/**
diff --git a/source/java/org/alfresco/repo/security/person/FilterSortPersonEntity.java b/source/java/org/alfresco/repo/security/person/FilterSortPersonEntity.java
new file mode 100644
index 0000000000..591099a57c
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/person/FilterSortPersonEntity.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2012 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.security.person;
+
+
+/**
+ * Filterable/Sortable Person Entity
+ *
+ * Can be optionally filtered/sorted by (up to) three properties - note: sort applied in same order as filter properties (if sort order is not null for given property)
+ *
+ * @author janv
+ * @since 4.1.2
+ */
+public class FilterSortPersonEntity
+{
+ private Long parentNodeId;
+
+ private Long prop1qnameId =null;
+ private Boolean sort1asc = null;
+
+ private Long prop2qnameId = null;
+ private Boolean sort2asc = null;
+
+ private Long prop3qnameId = null;
+ private Boolean sort3asc = null;
+
+ private String pattern;
+
+
+ /**
+ * Default constructor
+ */
+ public FilterSortPersonEntity()
+ {
+ }
+
+ public Long getParentNodeId()
+ {
+ return parentNodeId;
+ }
+
+ public void setParentNodeId(Long parentNodeId)
+ {
+ this.parentNodeId = parentNodeId;
+ }
+
+ public String getPattern()
+ {
+ return pattern;
+ }
+
+ protected String escape(String s, char escapeChar)
+ {
+ StringBuilder sb = new StringBuilder();
+ int idx = -1;
+ int offset = 0;
+ do
+ {
+ idx = s.indexOf(escapeChar, offset);
+ if(idx != -1)
+ {
+ sb.append(s.substring(offset, idx));
+ sb.append("\\");
+ sb.append(escapeChar);
+ offset = idx + 1;
+ }
+ }
+ while(idx != -1);
+ sb.append(s.substring(offset));
+ return sb.toString();
+ }
+
+ public void setPattern(String pattern)
+ {
+ if (pattern != null)
+ {
+ // escape the '%' character with '\' (standard SQL escape character)
+ //pattern = escape(pattern, '%');
+
+ // replace the wildcard character '*' with the one used in database queries i.e. '%'
+ this.pattern = pattern.replace('*', '%');
+ }
+ }
+
+ public Long getProp1qnameId()
+ {
+ return prop1qnameId;
+ }
+
+ public void setProp1qnameId(Long prop1qnameId)
+ {
+ this.prop1qnameId = prop1qnameId;
+ }
+
+ public Boolean getSort1asc()
+ {
+ return sort1asc;
+ }
+
+ public void setSort1asc(Boolean sort1asc)
+ {
+ this.sort1asc = sort1asc;
+ }
+
+ public Long getProp2qnameId()
+ {
+ return prop2qnameId;
+ }
+
+ public void setProp2qnameId(Long prop2qnameId)
+ {
+ this.prop2qnameId = prop2qnameId;
+ }
+
+ public Boolean getSort2asc()
+ {
+ return sort2asc;
+ }
+
+ public void setSort2asc(Boolean sort2asc)
+ {
+ this.sort2asc = sort2asc;
+ }
+
+ public Long getProp3qnameId()
+ {
+ return prop3qnameId;
+ }
+
+ public void setProp3qnameId(Long prop3qnameId)
+ {
+ this.prop3qnameId = prop3qnameId;
+ }
+
+ public Boolean getSort3asc()
+ {
+ return sort3asc;
+ }
+
+ public void setSort3asc(Boolean sort3asc)
+ {
+ this.sort3asc = sort3asc;
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQuery.java b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQuery.java
new file mode 100644
index 0000000000..4bd7b995bd
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQuery.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2005-2012 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.security.person;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.query.AbstractCannedQuery;
+import org.alfresco.query.CannedQueryParameters;
+import org.alfresco.query.CannedQuerySortDetails;
+import org.alfresco.query.CannedQuerySortDetails.SortOrder;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.domain.query.CannedQueryDAO;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * GetPeople canned query
+ *
+ * To get paged list of children of a parent node filtered by child type.
+ * Also optionally filtered and/or sorted by one or more properties (up to three).
+ *
+ * @author janv
+ * @since 4.1.2
+ */
+public class GetPeopleCannedQuery extends AbstractCannedQuery
+{
+ private Log logger = LogFactory.getLog(getClass());
+
+ private static final String QUERY_NAMESPACE = "alfresco.query.people";
+ private static final String QUERY_SELECT_GET_PEOPLE = "select_GetPeopleCannedQuery";
+
+ public static final int MAX_FILTER_SORT_PROPS = 3;
+
+ private NodeDAO nodeDAO;
+ private QNameDAO qnameDAO;
+ private CannedQueryDAO cannedQueryDAO;
+ private TenantService tenantService;
+
+ public GetPeopleCannedQuery(
+ NodeDAO nodeDAO,
+ QNameDAO qnameDAO,
+ CannedQueryDAO cannedQueryDAO,
+ TenantService tenantService,
+ CannedQueryParameters params)
+ {
+ super(params);
+
+ this.nodeDAO = nodeDAO;
+ this.qnameDAO = qnameDAO;
+ this.cannedQueryDAO = cannedQueryDAO;
+ this.tenantService = tenantService;
+ }
+
+ @Override
+ protected List queryAndFilter(CannedQueryParameters parameters)
+ {
+ Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
+
+ // Get parameters
+ GetPeopleCannedQueryParams paramBean = (GetPeopleCannedQueryParams)parameters.getParameterBean();
+
+ // Get parent node
+ NodeRef parentRef = paramBean.getParentRef();
+ ParameterCheck.mandatory("nodeRef", parentRef);
+ Pair nodePair = nodeDAO.getNodePair(parentRef);
+ if (nodePair == null)
+ {
+ throw new InvalidNodeRefException("Parent node does not exist: " + parentRef, parentRef);
+ }
+ Long parentNodeId = nodePair.getFirst();
+
+ // Set query params - note: currently using SortableChildEntity to hold (supplemental-) query params
+ FilterSortPersonEntity params = new FilterSortPersonEntity();
+
+ // Set parent node id
+ params.setParentNodeId(parentNodeId);
+
+ // Get filter details
+ final List filterProps = paramBean.getFilterProps();
+
+ // Get sort details
+ CannedQuerySortDetails sortDetails = parameters.getSortDetails();
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final List> sortPairs = (List)sortDetails.getSortPairs();
+
+ String pattern = paramBean.getPattern();
+ if ((sortPairs.size() > 0) && ((pattern == null) || (pattern.equals(""))))
+ {
+ // note: although no pattern means no filtering required, we currently need to match all if sort required
+ pattern = "%";
+ }
+ else if ((! pattern.endsWith("%")) && (! pattern.endsWith("*")))
+ {
+ // implicit startsWith match
+ pattern = pattern + "%";
+ }
+
+ // Set filter pattern
+ params.setPattern(pattern);
+
+ // Set sort / filter params
+ // Note - need to keep the sort properties in their requested order
+ List sortFilterProps = new ArrayList(MAX_FILTER_SORT_PROPS);
+ Map sortAsc = new HashMap(MAX_FILTER_SORT_PROPS);
+
+ // add sort props first
+ for (Pair sort : sortPairs)
+ {
+ QName sortQName = sort.getFirst();
+ if ((filterProps.size() > 0) && (! filterProps.contains(sortQName)))
+ {
+ throw new AlfrescoRuntimeException("GetPeople: cannot sort by a non-filter property: "+sortQName+" (filterStringProps="+filterProps+")");
+ }
+
+ if (! sortFilterProps.contains(sortQName))
+ {
+ sortFilterProps.add(sortQName);
+ sortAsc.put(sortQName, sort.getSecond().equals(SortOrder.ASCENDING));
+ }
+ }
+
+ // add any additional filter props (not part of sort)
+ for (QName filterQName : filterProps)
+ {
+ if (! sortFilterProps.contains(filterQName))
+ {
+ sortFilterProps.add(filterQName);
+ sortAsc.put(filterQName, null);
+ }
+ }
+
+ int filterSortPropCnt = sortFilterProps.size();
+
+ if (filterSortPropCnt > MAX_FILTER_SORT_PROPS)
+ {
+ throw new AlfrescoRuntimeException("GetPeople: exceeded maximum number filter/sort properties: (max="+MAX_FILTER_SORT_PROPS+", actual="+filterSortPropCnt);
+ }
+
+ filterSortPropCnt = setFilterSortParams(sortFilterProps, sortAsc, params);
+
+ // filtered and/or sorted - note: permissions not applicable for getPeople
+ final List result = new ArrayList(100);
+ final PersonQueryCallback c = new DefaultPersonQueryCallback(result);
+ PersonResultHandler resultHandler = new PersonResultHandler(c);
+
+ int offset = parameters.getPageDetails().getSkipResults();
+ int limit = parameters.getPageDetails().getPageSize();
+ if (limit != Integer.MAX_VALUE)
+ {
+ // to enable hasMore flag
+ limit++;
+ }
+
+ cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_PEOPLE, params, offset, limit, resultHandler);
+ resultHandler.done();
+
+ if (start != null)
+ {
+ logger.debug("Base query: "+result.size()+" in "+(System.currentTimeMillis()-start)+" msecs");
+ }
+
+ return result;
+ }
+
+ // Set filter/sort props (between 0 and 3)
+ private int setFilterSortParams(List filterSortProps, Map sortAsc, FilterSortPersonEntity params)
+ {
+ int cnt = 0;
+ int propCnt = 0;
+
+ for (QName filterSortProp : filterSortProps)
+ {
+ Long sortQNameId = getQNameId(filterSortProp);
+ Boolean sortOrder = sortAsc.get(filterSortProp); // true = ascending, false = descending, null = unsorted
+
+ if (sortQNameId != null)
+ {
+ if (propCnt == 0)
+ {
+ params.setProp1qnameId(sortQNameId);
+ params.setSort1asc(sortOrder);
+ }
+ else if (propCnt == 1)
+ {
+ params.setProp2qnameId(sortQNameId);
+ params.setSort2asc(sortOrder);
+ }
+ else if (propCnt == 2)
+ {
+ params.setProp3qnameId(sortQNameId);
+ params.setSort3asc(sortOrder);
+ }
+ else
+ {
+ // belts and braces
+ throw new AlfrescoRuntimeException("GetPeople: unexpected - cannot set sort parameter: "+cnt);
+ }
+
+ propCnt++;
+ }
+ else
+ {
+ logger.warn("Skipping filter/sort param - cannot find: "+filterSortProp);
+ break;
+ }
+
+ cnt++;
+ }
+
+ return cnt;
+ }
+
+ private Long getQNameId(QName sortPropQName)
+ {
+ Pair qnamePair = qnameDAO.getQName(sortPropQName);
+ return (qnamePair == null ? null : qnamePair.getFirst());
+ }
+
+ @Override
+ protected boolean isApplyPostQuerySorting()
+ {
+ // note: sorted as part of the query impl
+ return false;
+ }
+
+ @Override
+ protected boolean isApplyPostQueryPermissions()
+ {
+ return false;
+ }
+
+ @Override
+ protected boolean isApplyPostQueryPaging()
+ {
+ return false;
+ }
+
+ @Override
+ protected Pair getTotalResultCount(List results)
+ {
+ return null;
+ }
+
+ protected interface PersonQueryCallback
+ {
+ boolean handle(NodeRef personRef);
+ }
+
+ protected class DefaultPersonQueryCallback implements PersonQueryCallback
+ {
+ private List children;
+
+ public DefaultPersonQueryCallback(final List children)
+ {
+ this.children = children;
+ }
+
+ @Override
+ public boolean handle(NodeRef personRef)
+ {
+ children.add(tenantService.getBaseName(personRef));
+
+ // More results
+ return true;
+ }
+ }
+
+ protected class PersonResultHandler implements CannedQueryDAO.ResultHandler
+ {
+ private final PersonQueryCallback resultsCallback;
+
+ private PersonResultHandler(PersonQueryCallback resultsCallback)
+ {
+ this.resultsCallback = resultsCallback;
+ }
+
+ public boolean handleResult(String uuid)
+ {
+ // Call back
+ return resultsCallback.handle(new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, uuid));
+ }
+
+ public void done()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java
new file mode 100644
index 0000000000..5e1ab4822e
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryFactory.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2005-2012 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.security.person;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.alfresco.query.AbstractCannedQueryFactory;
+import org.alfresco.query.CannedQuery;
+import org.alfresco.query.CannedQueryPageDetails;
+import org.alfresco.query.CannedQueryParameters;
+import org.alfresco.query.CannedQuerySortDetails;
+import org.alfresco.query.CannedQuerySortDetails.SortOrder;
+import org.alfresco.query.PagingRequest;
+import org.alfresco.repo.domain.node.NodeDAO;
+import org.alfresco.repo.domain.qname.QNameDAO;
+import org.alfresco.repo.domain.query.CannedQueryDAO;
+import org.alfresco.repo.tenant.TenantService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.util.Pair;
+import org.alfresco.util.ParameterCheck;
+import org.alfresco.util.PropertyCheck;
+
+/**
+ * GetPeople canned query factory - to get paged list of people
+ *
+ * @author janv
+ * @since 4.1.2
+ */
+public class GetPeopleCannedQueryFactory extends AbstractCannedQueryFactory
+{
+ protected NodeDAO nodeDAO;
+ protected QNameDAO qnameDAO;
+ protected CannedQueryDAO cannedQueryDAO;
+ protected TenantService tenantService;
+
+ public void setNodeDAO(NodeDAO nodeDAO)
+ {
+ this.nodeDAO = nodeDAO;
+ }
+
+ public void setQnameDAO(QNameDAO qnameDAO)
+ {
+ this.qnameDAO = qnameDAO;
+ }
+
+ public void setCannedQueryDAO(CannedQueryDAO cannedQueryDAO)
+ {
+ this.cannedQueryDAO = cannedQueryDAO;
+ }
+
+ public void setTenantService(TenantService tenantService)
+ {
+ this.tenantService = tenantService;
+ }
+
+ @Override
+ public CannedQuery getCannedQuery(CannedQueryParameters parameters)
+ {
+ return (CannedQuery) new GetPeopleCannedQuery(nodeDAO, qnameDAO, cannedQueryDAO, tenantService, parameters);
+ }
+
+ public CannedQuery getCannedQuery(NodeRef parentRef, String pattern, List filterProps, List> sortProps, PagingRequest pagingRequest)
+ {
+ ParameterCheck.mandatory("parentRef", parentRef);
+ ParameterCheck.mandatory("pagingRequest", pagingRequest);
+
+ int requestTotalCountMax = pagingRequest.getRequestTotalCountMax();
+
+ // specific query params - context (parent) and inclusive filters (property values)
+ GetPeopleCannedQueryParams paramBean = new GetPeopleCannedQueryParams(tenantService.getName(parentRef), filterProps, pattern);
+
+ // page details
+ CannedQueryPageDetails cqpd = new CannedQueryPageDetails(pagingRequest.getSkipCount(), pagingRequest.getMaxItems(), CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT);
+
+ // sort details
+ CannedQuerySortDetails cqsd = null;
+ if (sortProps != null)
+ {
+ List> sortPairs = new ArrayList>(sortProps.size());
+ for (Pair sortProp : sortProps)
+ {
+ sortPairs.add(new Pair(sortProp.getFirst(), (sortProp.getSecond() ? SortOrder.ASCENDING : SortOrder.DESCENDING)));
+ }
+
+ cqsd = new CannedQuerySortDetails(sortPairs);
+ }
+
+ // create query params holder
+ CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, requestTotalCountMax, pagingRequest.getQueryExecutionId());
+
+ // return canned query instance
+ return getCannedQuery(params);
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception
+ {
+ super.afterPropertiesSet();
+
+ PropertyCheck.mandatory(this, "tenantService", tenantService);
+ PropertyCheck.mandatory(this, "nodeDAO", nodeDAO);
+ PropertyCheck.mandatory(this, "qnameDAO", qnameDAO);
+ PropertyCheck.mandatory(this, "cannedQueryDAO", cannedQueryDAO);
+ }
+}
diff --git a/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryParams.java b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryParams.java
new file mode 100644
index 0000000000..ac77150313
--- /dev/null
+++ b/source/java/org/alfresco/repo/security/person/GetPeopleCannedQueryParams.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2005-2012 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.security.person;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * GetPeople CQ parameters - for query context and filtering
+ *
+ * @author janv
+ * @since 4.1.2
+ */
+public class GetPeopleCannedQueryParams
+{
+ private NodeRef parentRef;
+ private List filterProps = Collections.emptyList();
+ private String pattern = null;
+
+ public GetPeopleCannedQueryParams(
+ NodeRef parentRef,
+ List filterProps,
+ String pattern)
+ {
+ this.parentRef = parentRef;
+ if (filterProps != null) { this.filterProps = filterProps; }
+
+ this.pattern = pattern;
+ }
+
+ public NodeRef getParentRef()
+ {
+ return parentRef;
+ }
+
+ public List getFilterProps()
+ {
+ return filterProps;
+ }
+
+ public String getPattern()
+ {
+ return pattern;
+ }
+}
diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
index 5d5220f821..6aadca05f0 100644
--- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
+++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java
@@ -20,7 +20,6 @@ package org.alfresco.repo.security.person;
import java.io.Serializable;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -29,12 +28,10 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
-import org.alfresco.query.CannedQuery;
import org.alfresco.query.CannedQueryFactory;
import org.alfresco.query.CannedQueryResults;
import org.alfresco.query.PagingRequest;
@@ -81,9 +78,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
-import org.alfresco.service.cmr.search.LimitBy;
-import org.alfresco.service.cmr.search.ResultSet;
-import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
@@ -115,7 +109,8 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
{
private static Log logger = LogFactory.getLog(PersonServiceImpl.class);
- private static final String CANNED_QUERY_PEOPLE_LIST = "peopleGetChildrenCannedQueryFactory";
+ private static final String CANNED_QUERY_PEOPLE_LIST = "getPeopleCannedQueryFactory";
+ private static final String CANNED_QUERY_PEOPLE_LIST_DEPRECATED = "peopleGetChildrenCannedQueryFactory";
private static final String DELETE = "DELETE";
private static final String SPLIT = "SPLIT";
@@ -1190,71 +1185,65 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
return refs;
}
+ private List getFilterProps(List> stringPropFilters, boolean filterIgnoreCase)
+ {
+ List filterProps = null;
+ if (stringPropFilters != null)
+ {
+ filterProps = new ArrayList(stringPropFilters.size());
+ for (Pair filterProp : stringPropFilters)
+ {
+ String filterStr = filterProp.getSecond();
+ if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*")))
+ {
+ // The wildcard means no filtering is needed on this property
+ continue;
+ }
+ else if (filterStr.endsWith("*"))
+ {
+ // The trailing * is implicit
+ filterStr = filterStr.substring(0, filterStr.length()-1);
+ }
+
+ // Turn this into a canned query filter
+ filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH)));
+ }
+ }
+
+ return filterProps;
+ }
+
/**
* {@inheritDoc}
*/
- public PagingResults getPeople(List> stringPropFilters, boolean filterIgnoreCase, List> sortProps, PagingRequest pagingRequest)
+ public PagingResults getPeople(String pattern, List filterStringProps, List> sortProps, PagingRequest pagingRequest)
{
ParameterCheck.mandatory("pagingRequest", pagingRequest);
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
-
- // TODO Remove this ALF-14127 hot fix code (calling nonCannedGetPeopleQuery(...) once this canned query does not fetch all rows from the database,
- // which is very slow when there are a lot of users. 10,000 user takes about 4 seconds. The customer has 90,000.
+
CannedQueryResults cqResults = null;
- String searchValue = null;
- if (filterIgnoreCase && pagingRequest != null && pagingRequest.getQueryExecutionId() == null && pagingRequest.getSkipCount() == 0)
- {
- searchValue = getSearchOnNameValue(stringPropFilters);
- }
- if (searchValue != null)
- {
- cqResults = nonCannedGetPeopleQuery(searchValue, pagingRequest);
- }
- else
- {
- NodeRef contextNodeRef = getPeopleContainer();
- Set childTypeQNames = new HashSet(1);
- childTypeQNames.add(ContentModel.TYPE_PERSON);
+ NodeRef contextNodeRef = getPeopleContainer();
- // get canned query
- GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST);
+ // get canned query
+ GetPeopleCannedQueryFactory getPeopleCannedQueryFactory = (GetPeopleCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST);
- List filterProps = null;
- if (stringPropFilters != null)
- {
- filterProps = new ArrayList(stringPropFilters.size());
- for (Pair filterProp : stringPropFilters)
- {
- String filterStr = filterProp.getSecond();
- if ((filterStr == null) || (filterStr.equals("")) || (filterStr.equals("*")))
- {
- // The wildcard means no filtering is needed on this property
- continue;
- }
- else if (filterStr.endsWith("*"))
- {
- // The trailing * is implicit
- filterStr = filterStr.substring(0, filterStr.length()-1);
- }
-
- // Turn this into a canned query filter
- filterProps.add(new FilterPropString(filterProp.getFirst(), filterStr, (filterIgnoreCase ? FilterTypeString.STARTSWITH_IGNORECASE : FilterTypeString.STARTSWITH)));
- }
- }
+ GetPeopleCannedQuery cq = (GetPeopleCannedQuery)getPeopleCannedQueryFactory.getCannedQuery(contextNodeRef, pattern, filterStringProps, sortProps, pagingRequest);
- GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest);
-
- // execute canned query
- cqResults = cq.execute();
- }
+ // execute canned query
+ cqResults = cq.execute();
final CannedQueryResults results = cqResults;
- final List nodeRefs;
+ List nodeRefs;
if (results.getPageCount() > 0)
{
nodeRefs = results.getPages().get(0);
+ if (nodeRefs.size() > pagingRequest.getMaxItems())
+ {
+ // eg. since hasMoreItems added one (for a pre-paged result)
+ nodeRefs = nodeRefs.subList(0, pagingRequest.getMaxItems());
+ }
}
else
{
@@ -1284,9 +1273,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
{
logger.debug(
"getPeople: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs " +
- "[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
- ",totalCount="+totalCount+",filters="+stringPropFilters+
- ",filtersIgnoreCase="+filterIgnoreCase+"]");
+ "[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
+ ",totalCount="+totalCount+",pattern="+pattern+",filterStringProps="+filterStringProps+
+ ",sortProps="+sortProps+"]");
}
}
@@ -1326,255 +1315,104 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per
}
/**
- * If the search is on first, last and user name only with the same value, return that value,
- * otherwise return null.
+ * {@inheritDoc}
*/
- // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
- private String getSearchOnNameValue(List> stringPropFilters)
- {
- String filter = null;
- if (stringPropFilters != null && stringPropFilters.size() == 3)
- {
- // Does not check we don't have duplicates.
- for (int i=0; i < 3; i++)
- {
- Pair pair = stringPropFilters.get(i);
- if (i == 0)
- {
- filter = pair.getSecond().trim();
- if (filter == null || filter.length() == 0)
- {
- filter = null;
- break;
- }
- }
-
- if ((i != 0 && !filter.equals(pair.getSecond().trim()) || !NAME_SEARCH_NAMES.contains(pair.getFirst())))
- {
- filter = null;
- break;
- }
- }
- }
- return filter;
- }
-
- // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
- private static final List NAME_SEARCH_NAMES = Arrays.asList(new QName[] {
- ContentModel.PROP_FIRSTNAME, ContentModel.PROP_LASTNAME, ContentModel.PROP_USERNAME});
-
- /**
- * Use Solr search based on code in org.alfresco.repo.jscript.People.getPeople(String, int)
- */
- // TODO Remove this ALF-14127 hot fix code once this canned query does not fetch all rows from the database.
- private CannedQueryResults nonCannedGetPeopleQuery(String filter, PagingRequest pagingRequest)
+ public PagingResults getPeople(List> stringPropFilters, boolean filterIgnoreCase, List> sortProps, PagingRequest pagingRequest)
{
+ ParameterCheck.mandatory("pagingRequest", pagingRequest);
+
Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null);
- int maxResults = pagingRequest != null ? pagingRequest.getMaxItems() : Integer.MAX_VALUE;
- if (maxResults <= 0)
+
+ CannedQueryResults cqResults = null;
+
+ NodeRef contextNodeRef = getPeopleContainer();
+
+ Set childTypeQNames = new HashSet(1);
+ childTypeQNames.add(ContentModel.TYPE_PERSON);
+
+ // get canned query
+ GetChildrenCannedQueryFactory getChildrenCannedQueryFactory = (GetChildrenCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_PEOPLE_LIST_DEPRECATED);
+
+ List filterProps = getFilterProps(stringPropFilters, filterIgnoreCase);
+
+ GetChildrenCannedQuery cq = (GetChildrenCannedQuery)getChildrenCannedQueryFactory.getCannedQuery(contextNodeRef, null, null, childTypeQNames, filterProps, sortProps, pagingRequest);
+
+ // execute canned query
+ cqResults = cq.execute();
+
+ final CannedQueryResults results = cqResults;
+ final List nodeRefs;
+ if (results.getPageCount() > 0)
{
- maxResults = Integer.MAX_VALUE;
- }
-
- String term = filter.replace("\\", "").replace("\"", "");
- StringTokenizer t = new StringTokenizer(term, " ");
- int propIndex = term.indexOf(':');
-
- SearchParameters params = new SearchParameters();
- params.addQueryTemplate("_PERSON", "|%firstName OR |%lastName OR |%userName");
- params.setDefaultFieldName("_PERSON");
-
- StringBuilder query = new StringBuilder(256);
-
- query.append("TYPE:\"").append(ContentModel.TYPE_PERSON).append("\" AND (");
-
- if (t.countTokens() == 1)
- {
- // single word with no field will go against _PERSON and expand
-
- // fts-alfresco property search i.e. location:"maidenhead"
- query.append(term.substring(0, propIndex + 1)).append('"')
- .append(term.substring(propIndex + 1));
- if (propIndex > 0)
- {
- query.append('"');
- }
- else
- {
- query.append("*\"");
- }
+ nodeRefs = results.getPages().get(0);
}
else
{
- // scan for non-fts-alfresco property search tokens
- int nonFtsTokens = 0;
- while (t.hasMoreTokens())
- {
- if (t.nextToken().indexOf(':') == -1)
- nonFtsTokens++;
- }
- t = new StringTokenizer(term, " ");
-
- // multiple terms supplied - look for first and second name etc.
- // assume first term is first name, any more are second i.e.
- // "Fraun van de Wiels"
- // also allow fts-alfresco property search to reduce results
- params.setDefaultOperator(SearchParameters.Operator.AND);
- boolean firstToken = true;
- boolean tokenSurname = false;
- boolean propertySearch = false;
- while (t.hasMoreTokens())
- {
- term = t.nextToken();
- if (!propertySearch && term.indexOf(':') == -1)
- {
- if (nonFtsTokens == 1)
- {
- // simple search: first name, last name and username
- // starting with term
- query.append("_PERSON:\"");
- query.append(term);
- query.append("*\" ");
- }
- else
- {
- if (firstToken)
- {
- query.append("firstName:\"");
- query.append(term);
- query.append("*\" ");
-
- firstToken = false;
- }
- else
- {
- if (tokenSurname)
- {
- query.append("OR ");
- }
- query.append("lastName:\"");
- query.append(term);
- query.append("*\" ");
-
- tokenSurname = true;
- }
- }
- }
- else
- {
- // fts-alfresco property search i.e. "location:maidenhead"
- propIndex = term.indexOf(':');
- query.append(term.substring(0, propIndex + 1)).append('"')
- .append(term.substring(propIndex + 1)).append('"');
-
- propertySearch = true;
- }
- }
+ nodeRefs = Collections.emptyList();
}
- query.append(")");
-
- // define the search parameters
- params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO);
- params.addStore(this.storeRef);
- params.setQuery(query.toString());
- if (maxResults > 0)
+
+ // set total count
+ final Pair totalCount;
+ if (pagingRequest.getRequestTotalCountMax() > 0)
{
- params.setLimitBy(LimitBy.FINAL_SIZE);
- params.setLimit(maxResults);
+ totalCount = results.getTotalResultCount();
}
-
- ResultSet results = null;
- List resultNodeRefs = null;
- try
+ else
{
- results = searchService.query(params);
- resultNodeRefs = results.getNodeRefs();
+ totalCount = null;
}
- catch (Throwable err)
+
+ if (start != null)
{
- resultNodeRefs = Collections.emptyList();
+ int cnt = results.getPagedResultCount();
+ int skipCount = pagingRequest.getSkipCount();
+ int maxItems = pagingRequest.getMaxItems();
+ boolean hasMoreItems = results.hasMoreItems();
+ int pageNum = (skipCount / maxItems) + 1;
- // hide query parse error from users
if (logger.isDebugEnabled())
- logger.debug("Failed to execute people search: " + query.toString(), err);
- }
- finally
- {
- if (results != null)
{
- results.close();
+ logger.debug(
+ "getPeople: "+cnt+" items in "+(System.currentTimeMillis()-start)+" msecs " +
+ "[pageNum="+pageNum+",skip="+skipCount+",max="+maxItems+",hasMorePages="+hasMoreItems+
+ ",totalCount="+totalCount+",filters="+stringPropFilters+
+ ",filtersIgnoreCase="+filterIgnoreCase+",sortProps="+sortProps+"]");
}
}
-
- // Turn NodeRefs into a single page of results.
- final List nodRefs = resultNodeRefs;
- CannedQueryResults cqResults = new CannedQueryResults()
+
+ final List personInfos = new ArrayList(nodeRefs.size());
+ for (NodeRef nodeRef : nodeRefs)
+ {
+ Map props = nodeService.getProperties(nodeRef);
+ personInfos.add(new PersonInfo(nodeRef,
+ (String)props.get(ContentModel.PROP_USERNAME),
+ (String)props.get(ContentModel.PROP_FIRSTNAME),
+ (String)props.get(ContentModel.PROP_LASTNAME)));
+ }
+
+ return new PagingResults()
{
- @Override
- public CannedQuery getOriginatingQuery()
- {
- return null;
- }
-
@Override
public String getQueryExecutionId()
{
- return null;
+ return results.getQueryExecutionId();
}
-
@Override
- public Pair getTotalResultCount()
+ public List getPage()
{
- int size = nodRefs.size();
- return new Pair(size, size);
+ return personInfos;
}
-
- @Override
- public int getPagedResultCount()
- {
- return nodRefs.size();
- }
-
- @Override
- public int getPageCount()
- {
- return 1;
- }
-
- @Override
- public NodeRef getSingleResult()
- {
- if (nodRefs.size() != 1)
- {
- throw new IllegalStateException(
- "There must be exactly one page of one result available.");
- }
- return nodRefs.get(0);
- }
-
- @Override
- public List getPage()
- {
- return nodRefs;
- }
-
- @Override
- public List> getPages()
- {
- return Collections.singletonList(getPage());
- }
-
@Override
public boolean hasMoreItems()
{
- return false;
+ return results.hasMoreItems();
+ }
+ @Override
+ public Pair getTotalResultCount()
+ {
+ return totalCount;
}
};
- if (logger.isDebugEnabled())
- {
- logger.debug("nonCannedGetPeopleQuery(\""+filter+"\", "+maxResults+") "+cqResults.getTotalResultCount()+" in "+(System.currentTimeMillis()-start)+" msecs ");
- }
- return cqResults;
}
/**
diff --git a/source/java/org/alfresco/repo/security/person/PersonTest.java b/source/java/org/alfresco/repo/security/person/PersonTest.java
index 692c47cb5b..89791d3758 100644
--- a/source/java/org/alfresco/repo/security/person/PersonTest.java
+++ b/source/java/org/alfresco/repo/security/person/PersonTest.java
@@ -560,7 +560,6 @@ public class PersonTest extends TestCase
personService.deletePerson("Derek");
assertEquals(2, getPeopleCount());
-
}
public void testPeopleFiltering()
@@ -581,6 +580,113 @@ public class PersonTest extends TestCase
PagingRequest pr = new PagingRequest(100, null);
+ List filters = new ArrayList(4);
+
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ assertEquals(2, personService.getPeople("y", filters, null, pr).getPage().size());
+
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ filters.add(ContentModel.PROP_FIRSTNAME);
+ filters.add(ContentModel.PROP_LASTNAME);
+ assertEquals(3, personService.getPeople("b", filters, null, pr).getPage().size());
+
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ assertEquals(2, personService.getPeople("A", filters, null, pr).getPage().size()); // includes "admin"
+
+ personService.deletePerson("aa");
+
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ assertEquals(1, personService.getPeople("a", filters, null, pr).getPage().size()); // includes "admin"
+
+ // a* is the same as a
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ assertEquals(1, personService.getPeople("a*", filters, null, pr).getPage().size()); // includes "admin"
+
+ // * means everyone
+ filters.clear();
+ filters.add(ContentModel.PROP_USERNAME);
+ assertEquals(5, getPeopleCount());
+ assertEquals(5, personService.getPeople("*", filters, null, pr).getPage().size());
+ }
+
+ public void testPeopleSortingPaging()
+ {
+ personService.setCreateMissingPeople(false);
+
+ assertEquals(2, getPeopleCount());
+
+ NodeRef p1 = personService.getPerson(AuthenticationUtil.getAdminUserName()); // admin - by default
+ NodeRef p2 = personService.getPerson(AuthenticationUtil.getGuestUserName()); // guest - by default
+
+ NodeRef p3 = personService.createPerson(createDefaultProperties("aa", "Aa", "Aa", "aa@aa", "alfresco", rootNodeRef));
+ NodeRef p4 = personService.createPerson(createDefaultProperties("cc", "Cc", "Cc", "cc@cc", "alfresco", rootNodeRef));
+ NodeRef p5 = personService.createPerson(createDefaultProperties("hh", "Hh", "Hh", "hh@hh", "alfresco", rootNodeRef));
+ NodeRef p6 = personService.createPerson(createDefaultProperties("bb", "Bb", "Bb", "bb@bb", "alfresco", rootNodeRef));
+ NodeRef p7 = personService.createPerson(createDefaultProperties("dd", "Dd", "Dd", "dd@dd", "alfresco", rootNodeRef));
+
+
+
+ assertEquals(7, getPeopleCount());
+
+ List> sort = new ArrayList>(1);
+ sort.add(new Pair(ContentModel.PROP_USERNAME, true));
+
+ // page 1
+ PagingRequest pr = new PagingRequest(0, 2, null);
+ PagingResults ppr = personService.getPeople(null, null, sort, pr);
+ List results = ppr.getPage();
+ assertEquals(2, results.size());
+ assertEquals(p3, results.get(0).getNodeRef());
+ assertEquals(p1, results.get(1).getNodeRef());
+
+ // page 2
+ pr = new PagingRequest(2, 2, null);
+ ppr = personService.getPeople(null, null, sort, pr);
+ results = ppr.getPage();
+ assertEquals(2, results.size());
+ assertEquals(p6, results.get(0).getNodeRef());
+ assertEquals(p4, results.get(1).getNodeRef());
+
+ // page 3
+ pr = new PagingRequest(4, 2, null);
+ ppr = personService.getPeople(null, null, sort, pr);
+ results = ppr.getPage();
+ assertEquals(2, results.size());
+ assertEquals(p7, results.get(0).getNodeRef());
+ assertEquals(p2, results.get(1).getNodeRef());
+
+ // page 4
+ pr = new PagingRequest(6, 2, null);
+ ppr = personService.getPeople(null, null, sort, pr);
+ results = ppr.getPage();
+ assertEquals(1, results.size());
+ assertEquals(p5, results.get(0).getNodeRef());
+ }
+
+ // note: this test can be removed as and when we remove the deprecated "getPeople" impl
+ public void testPeopleFiltering_deprecatedCQ_via_getChildren()
+ {
+ personService.setCreateMissingPeople(false);
+
+ assertEquals(2, getPeopleCount());
+
+ checkPeopleContain(AuthenticationUtil.getAdminUserName());
+ checkPeopleContain(AuthenticationUtil.getGuestUserName());
+
+ personService.createPerson(createDefaultProperties("aa", "Aa", "Aa", "aa@aa", "alfresco", rootNodeRef));
+ personService.createPerson(createDefaultProperties("bc", "c", "C", "bc@bc", "alfresco", rootNodeRef));
+ personService.createPerson(createDefaultProperties("yy", "B", "D", "yy@yy", "alfresco", rootNodeRef));
+ personService.createPerson(createDefaultProperties("Yz", "yz", "B", "yz@yz", "alfresco", rootNodeRef));
+
+ assertEquals(6, getPeopleCount());
+
+ PagingRequest pr = new PagingRequest(100, null);
+
List> filters = new ArrayList>(4);
filters.clear();
@@ -618,7 +724,8 @@ public class PersonTest extends TestCase
assertEquals(5, personService.getPeople(filters, true, null, pr).getPage().size());
}
- public void testPeopleSortingPaging()
+ // note: this test can be removed as and when we remove the deprecated "getPeople" impl
+ public void testPeopleSortingPaging_deprecatedCQ_via_getChildren()
{
personService.setCreateMissingPeople(false);
diff --git a/source/java/org/alfresco/service/cmr/security/PersonService.java b/source/java/org/alfresco/service/cmr/security/PersonService.java
index 685d1f4c70..3f683d2c01 100644
--- a/source/java/org/alfresco/service/cmr/security/PersonService.java
+++ b/source/java/org/alfresco/service/cmr/security/PersonService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2005-2011 Alfresco Software Limited.
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
*
* This file is part of Alfresco
*
@@ -274,7 +274,25 @@ public interface PersonService
/**
* Get paged list of people optionally filtered and/or sorted
-
+ *
+ * Note: the pattern is applied to filter props (0 to 3) as startsWithIgnoreCase, which are OR'ed together, for example: cm:userName or cm:firstName or cm:lastName
+ *
+ * @param pattern pattern to apply to filter props - "startsWith" and "ignoreCase"
+ * @param filterProps list of filter properties (these are OR'ed)
+ * @param sortProps sort property, eg. cm:username ascending
+ * @param pagingRequest skip, max + optional query execution id
+ * @return
+ *
+ * @author janv
+ * @since 4.1.2
+ */
+ @Auditable(parameters = {"pattern", "filterProps", "sortProps", "pagingRequest"})
+ public PagingResults getPeople(String pattern, List filterProps, List> sortProps, PagingRequest pagingRequest);
+
+
+ /**
+ * Get paged list of people optionally filtered and/or sorted
+ *
* @param filterProps list of filter properties (with "startsWith" values), eg. cm:username "al" might match "alex", "alice", ...
* @param filterIgnoreCase true to ignore case when filtering, false to be case-sensitive when filtering
* @param sortProps sort property, eg. cm:username ascending
@@ -282,6 +300,7 @@ public interface PersonService
*
* @author janv
* @since 4.0
+ * @deprecated see getPeople(String pattern, List filterProps, List> sortProps, PagingRequest pagingRequest)
*/
@Auditable(parameters = {"stringPropFilters", "filterIgnoreCase", "sortProps", "pagingRequest"})
public PagingResults getPeople(List> stringPropFilters, boolean filterIgnoreCase, List> sortProps, PagingRequest pagingRequest);