diff --git a/source/java/org/alfresco/repo/domain/node/NodeEntity.java b/source/java/org/alfresco/repo/domain/node/NodeEntity.java index ed37001c87..ee9ef82499 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeEntity.java +++ b/source/java/org/alfresco/repo/domain/node/NodeEntity.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.domain.node; +import org.alfresco.repo.security.permissions.PermissionCheckValue; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.Pair; @@ -27,7 +28,7 @@ import org.alfresco.util.Pair; * @author Derek Hulley * @since 3.4 */ -public class NodeEntity implements Node +public class NodeEntity implements Node, PermissionCheckValue { private boolean locked; diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQuery.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQuery.java new file mode 100644 index 0000000000..ee868b6bef --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQuery.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2011 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.node.getchildren; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.alfresco.query.CannedQuery; +import org.alfresco.query.CannedQueryParameters; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.domain.query.CannedQueryDAO; +import org.alfresco.repo.query.NodeBackedEntity; +import org.alfresco.repo.query.AbstractQNameAwareCannedQueryFactory.NestedComparator; +import org.alfresco.repo.query.AbstractQNameAwareCannedQueryFactory.NodeBackedEntityComparator; +import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * This class provides support for {@link CannedQuery canned queries} which + * filter by Auditable Properties + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenAuditableCannedQuery extends AbstractCannedQueryPermissions +{ + private Log logger = LogFactory.getLog(getClass()); + + private static final String QUERY_NAMESPACE = "alfresco.query.auditable"; + private static final String QUERY_SELECT_GET_NODES = "select_GetChildrenAuditableCannedQuery"; + + private final CannedQueryDAO cannedQueryDAO; + + public GetChildrenAuditableCannedQuery( + CannedQueryDAO cannedQueryDAO, + MethodSecurityBean methodSecurity, + CannedQueryParameters params) + { + super(params, methodSecurity); + this.cannedQueryDAO = cannedQueryDAO; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetChildrenAuditable query params"); + + GetChildrenAuditableCannedQueryParams paramBean = (GetChildrenAuditableCannedQueryParams) paramBeanObj; + + boolean filterByCreator = (paramBean.getCreatorFilter() != null); + boolean filterByModifier = (paramBean.getModifierFilter() != null); + boolean filterByCreatedDate = (paramBean.getCreatedBefore() != null && paramBean.getCreatedAfter() != null); + boolean filterByModifiedDate = (paramBean.getModifiedBefore() != null && paramBean.getModifiedAfter() != null); + + // note: refer to SQL for specific DB filtering (eg.parent nodes etc) + List results = cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_NODES, paramBean, 0, Integer.MAX_VALUE); + + // TODO Should this be case insensitive? + List filtered = new ArrayList(results.size()); + for (NodeBackedEntity result : results) + { + boolean nextNodeIsAcceptable = true; + + // Creator/Modifier filtering + if(filterByCreator || filterByModifier) + { + String creator = result.getCreator(); + String modifier = result.getModifier(); + if(modifier == null) + { + modifier = creator; + } + + if(filterByCreator) + { + if(! paramBean.getCreatorFilter().equals(creator)) + { + nextNodeIsAcceptable = false; + } + } + if(filterByModifier) + { + if(! paramBean.getModifierFilter().equals(modifier)) + { + nextNodeIsAcceptable = false; + } + } + } + + // Date filtering + if(filterByCreatedDate || filterByModifiedDate) + { + Date createdDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getCreatedDate()); + Date modifiedDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getModifiedDate()); + if(modifiedDate == null) + { + modifiedDate = createdDate; + } + + if(filterByCreatedDate) + { + if(createdDate.before(paramBean.getCreatedAfter()) || + createdDate.after(paramBean.getCreatedBefore())) + { + // Outside period + nextNodeIsAcceptable = false; + } + } + if(filterByModifiedDate) + { + if(modifiedDate.before(paramBean.getModifiedAfter()) || + modifiedDate.after(paramBean.getModifiedBefore())) + { + // Outside period + nextNodeIsAcceptable = false; + } + } + } + + // Did it make the cut + if (nextNodeIsAcceptable) + { + filtered.add(result); + } + } + + List> sortPairs = parameters.getSortDetails().getSortPairs(); + + // Do the sorting + if (sortPairs != null && !sortPairs.isEmpty()) + { + List, SortOrder>> comparators = + new ArrayList,SortOrder>>(); + for(Pair sortPair : sortPairs) + { + final QName sortProperty = (QName)sortPair.getFirst(); + final NodeBackedEntityComparator comparator = new NodeBackedEntityComparator(sortProperty); + comparators.add(new Pair, SortOrder>(comparator, sortPair.getSecond())); + } + NestedComparator comparator = new NestedComparator(comparators); + + // Sort + Collections.sort(filtered, comparator); + } + + if (start != null) + { + logger.debug("Base query: "+filtered.size()+" in "+(System.currentTimeMillis()-start)+" msecs"); + } + + return filtered; + } + + @Override + protected boolean isApplyPostQuerySorting() + { + // No post-query sorting. It's done within the queryAndFilter() method above. + return false; + } +} diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java new file mode 100644 index 0000000000..2749fb3f81 --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2011 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.node.getchildren; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQuery; +import org.alfresco.query.CannedQueryFactory; +import org.alfresco.query.CannedQueryPageDetails; +import org.alfresco.query.CannedQueryParameters; +import org.alfresco.query.CannedQuerySortDetails; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.query.AbstractQNameAwareCannedQueryFactory; +import org.alfresco.repo.query.NodeBackedEntity; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; + +/** + * A {@link CannedQueryFactory} for various queries relating to getting + * {@link NodeBackedEntity} entires filtering by auditable properties. + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenAuditableCannedQueryFactory extends AbstractQNameAwareCannedQueryFactory +{ + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + final GetChildrenAuditableCannedQuery cq = new GetChildrenAuditableCannedQuery( + cannedQueryDAO, methodSecurity, parameters + ); + + return (CannedQuery) cq; + } + + public CannedQuery getCannedQuery(NodeRef parentNodeRef, QName contentType, + String createdBy, Date createdFrom, Date createdTo, + String modifiedBy, Date modifiedFrom, Date modifiedTo, + CannedQuerySortDetails sortDetails, PagingRequest pagingReq) + { + ParameterCheck.mandatory("parentNodeRef", parentNodeRef); + ParameterCheck.mandatory("contentType", contentType); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + //FIXME Need tenant service like for GetChildren? + GetChildrenAuditableCannedQueryParams paramBean = new GetChildrenAuditableCannedQueryParams( + getNodeId(parentNodeRef), + getQNameId(ContentModel.PROP_NAME), + getQNameId(contentType), + createdBy, createdFrom, createdTo, + modifiedBy, modifiedFrom, modifiedTo + ); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters( + paramBean, cqpd, sortDetails, requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + public CannedQuerySortDetails createDateAscendingCQSortDetails() + { + List> sort = new ArrayList>(); + sort.add(new Pair(ContentModel.PROP_CREATED, SortOrder.ASCENDING)); + sort.add(new Pair(ContentModel.PROP_MODIFIED, SortOrder.ASCENDING)); + + return new CannedQuerySortDetails(sort); + } + + public CannedQuerySortDetails createDateDescendingCQSortDetails() + { + List> sort = new ArrayList>(); + sort.add(new Pair(ContentModel.PROP_CREATED, SortOrder.DESCENDING)); + sort.add(new Pair(ContentModel.PROP_MODIFIED, SortOrder.DESCENDING)); + + return new CannedQuerySortDetails(sort); + } +} diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryParams.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryParams.java new file mode 100644 index 0000000000..395cfb9f77 --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryParams.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2011 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.node.getchildren; + +import java.util.Date; + +import org.alfresco.repo.query.NodeBackedEntity; + +/** + * Parameter objects for {@link GetChildrenAuditableCannedQuery}. + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenAuditableCannedQueryParams extends NodeBackedEntity +{ + private String creatorFilter; + private Date createdBefore; + private Date createdAfter; + private String modifierFilter; + private Date modifiedBefore; + private Date modifiedAfter; + + public GetChildrenAuditableCannedQueryParams(Long parentNodeId, + Long nameQNameId, + Long contentTypeQNameId, + String creatorFilter, + Date createdFrom, Date createdTo, + String modifierFilter, + Date modifiedFrom, Date modifiedTo) + + { + super(parentNodeId, nameQNameId, contentTypeQNameId); + this.creatorFilter = creatorFilter; + this.createdAfter = createdFrom; + this.createdBefore = createdTo; + this.modifierFilter = modifierFilter; + this.modifiedAfter = modifiedFrom; + this.modifiedBefore = modifiedTo; + } + + public String getCreatorFilter() + { + return creatorFilter; + } + + public Date getCreatedBefore() + { + return createdBefore; + } + + public Date getCreatedAfter() + { + return createdAfter; + } + + public String getModifierFilter() + { + return modifierFilter; + } + + public Date getModifiedBefore() + { + return modifiedBefore; + } + + public Date getModifiedAfter() + { + return modifiedAfter; + } +} diff --git a/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java b/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java index f546e7c4c3..366abb630c 100644 --- a/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +import org.alfresco.model.ContentModel; import org.alfresco.query.AbstractCannedQueryFactory; import org.alfresco.query.CannedQueryPageDetails; import org.alfresco.query.CannedQuerySortDetails; @@ -32,7 +33,6 @@ import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.repo.tenant.TenantService; -import org.alfresco.service.cmr.calendar.CalendarEntry; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -202,6 +202,45 @@ public abstract class AbstractQNameAwareCannedQueryFactory extends AbstractCa } } + /** + * An instance of a {@link PropertyBasedComparator} for a {@link NodeBackedEntity} + */ + public static class NodeBackedEntityComparator extends PropertyBasedComparator + { + public NodeBackedEntityComparator(QName comparableProperty) + { + super(comparableProperty); + } + + @Override + protected Comparable getProperty(NodeBackedEntity entity) { + if (comparableProperty.equals(ContentModel.PROP_CREATED)) + { + return entity.getCreatedDate(); + } + else if (comparableProperty.equals(ContentModel.PROP_MODIFIED)) + { + return entity.getModifiedDate(); + } + else if (comparableProperty.equals(ContentModel.PROP_CREATOR)) + { + return entity.getCreator(); + } + else if (comparableProperty.equals(ContentModel.PROP_MODIFIER)) + { + return entity.getModifier(); + } + else if (comparableProperty.equals(ContentModel.PROP_NAME)) + { + return entity.getName(); + } + else + { + throw new IllegalArgumentException("Unsupported calendar sort property: "+comparableProperty); + } + } + } + public static class NestedComparator implements Comparator { private List, SortOrder>> comparators; diff --git a/source/java/org/alfresco/repo/query/NodeBackedEntity.java b/source/java/org/alfresco/repo/query/NodeBackedEntity.java index c9a72f9408..f55780f08a 100644 --- a/source/java/org/alfresco/repo/query/NodeBackedEntity.java +++ b/source/java/org/alfresco/repo/query/NodeBackedEntity.java @@ -19,6 +19,7 @@ package org.alfresco.repo.query; import org.alfresco.repo.domain.node.NodeEntity; +import org.alfresco.repo.security.permissions.PermissionCheckValue; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -28,13 +29,16 @@ import org.alfresco.service.cmr.repository.NodeRef; * @author Nick Burch * @since 4.0 */ -public abstract class NodeBackedEntity +public class NodeBackedEntity implements PermissionCheckValue { private Long id; // node id - + private String name; private NodeEntity node; - private String name; + // Supplemental query-related parameters + private Long parentNodeId; + private Long nameQNameId; + private Long contentTypeQNameId; /** * Default constructor @@ -43,6 +47,16 @@ public abstract class NodeBackedEntity { } + /** + * Query constructor + */ + public NodeBackedEntity(Long parentNodeId, Long nameQNameId, Long contentTypeQNameId) + { + this.parentNodeId = parentNodeId; + this.nameQNameId = nameQNameId; + this.contentTypeQNameId = contentTypeQNameId; + } + public Long getId() { return id; @@ -71,6 +85,18 @@ public abstract class NodeBackedEntity return ((node != null && node.getAuditableProperties() != null) ? node.getAuditableProperties().getAuditCreator() : null); } + // helper (ISO 8061) + public String getModifiedDate() + { + return ((node != null && node.getAuditableProperties() != null) ? node.getAuditableProperties().getAuditModified() : null); + } + + // helper + public String getModifier() + { + return ((node != null && node.getAuditableProperties() != null) ? node.getAuditableProperties().getAuditModifier() : null); + } + public NodeEntity getNode() { return node; @@ -90,4 +116,22 @@ public abstract class NodeBackedEntity { this.name = name; } + + + // Supplemental query-related parameters + + public Long getParentNodeId() + { + return parentNodeId; + } + + public Long getNameQNameId() + { + return nameQNameId; + } + + public Long getContentTypeQNameId() + { + return contentTypeQNameId; + } }