/* * Copyright (C) 2005-2013 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 java.util.Set; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; 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.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.AuthorityService; 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 static final int MAX_EXPECTED_ADMINS = 5; // TODO refine non-admin paging private NodeDAO nodeDAO; private QNameDAO qnameDAO; private CannedQueryDAO cannedQueryDAO; private TenantService tenantService; private NodeService nodeService; private AuthorityService authorityService; public GetPeopleCannedQuery( NodeDAO nodeDAO, QNameDAO qnameDAO, CannedQueryDAO cannedQueryDAO, TenantService tenantService, NodeService nodeService, AuthorityService authorityService, CannedQueryParameters params) { super(params); this.nodeDAO = nodeDAO; this.qnameDAO = qnameDAO; this.cannedQueryDAO = cannedQueryDAO; this.tenantService = tenantService; this.nodeService = nodeService; this.authorityService = authorityService; } @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 ((pattern == null) || (pattern.equals(""))) { // note: although no pattern means no filtering required, set to match all in case sort required pattern = "%"; } else if ((! pattern.endsWith("%")) && (! pattern.endsWith("*"))) { // implicit startsWith match pattern = pattern + "%"; } // Set filter pattern (should not be null) params.setPattern(pattern); if (paramBean.getExclusiveAspects() != null) { Set qnamesIds = qnameDAO.convertQNamesToIds(paramBean.getExclusiveAspects(), false); params.setExcludeAspectIds(new ArrayList(qnamesIds)); } if (paramBean.getInclusiveAspects() != null) { Set qnamesIds = qnameDAO.convertQNamesToIds(paramBean.getInclusiveAspects(), false); params.setIncludeAspectIds(new ArrayList(qnamesIds)); } // 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 List result = new ArrayList(100); final PersonQueryCallback c = new DefaultPersonQueryCallback(result, paramBean.getIncludeAdministrators()); PersonResultHandler resultHandler = new PersonResultHandler(c); int offset = parameters.getPageDetails().getSkipResults(); int totalResultCountMax = parameters.getTotalResultCountMax(); int origOffset = offset; int origLimit = totalResultCountMax > 0 ? totalResultCountMax : parameters.getPageDetails().getPageSize(); long newLimit = (long)origLimit; // to enable hasMore flag newLimit++; boolean excludeAdmins = (! paramBean.getIncludeAdministrators()); if (excludeAdmins) { // TODO refine - non-admin paging offset = 0; newLimit = offset + (long)newLimit + MAX_EXPECTED_ADMINS; } if (newLimit > Integer.MAX_VALUE) { newLimit = Integer.MAX_VALUE; } cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_PEOPLE, params, offset, (int)newLimit, resultHandler); resultHandler.done(); if (start != null) { logger.debug("Base query: "+result.size()+" in "+(System.currentTimeMillis()-start)+" msecs"); } if (excludeAdmins) { // TODO refine - non-admin paging long max = origOffset + (long)origLimit; if (max > result.size()) { max = result.size(); } result = result.subList(origOffset, (int)max); } 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) { int offset = super.getParameters().getPageDetails().getSkipResults(); Integer size = offset + results.size(); return new Pair(size, size); } protected interface PersonQueryCallback { boolean handle(NodeRef personRef); } protected class DefaultPersonQueryCallback implements PersonQueryCallback { private List children; private boolean includeAdministrators; public DefaultPersonQueryCallback(final List children, boolean includeAdministrators) { this.children = children; this.includeAdministrators = includeAdministrators; } @Override public boolean handle(NodeRef personRef) { // TODO refine - return username as part of query if (includeAdministrators == false) { String userName = (String) nodeService.getProperty(personRef, ContentModel.PROP_USERNAME); if (authorityService.isAdminAuthority(userName)) { return true; } } 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() { } } }