From 1c8c572a850d65ba05c6adecd68e6d8aa643deb2 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 17 Aug 2011 13:59:23 +0000 Subject: [PATCH] ALF-9153 Start on the discussion replies CQ git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29831 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/discussions-services-context.xml | 11 ++ .../alfresco/ibatis/alfresco-SqlMapConfig.xml | 1 + .../query-auditable-common-SqlMap.xml | 38 ++++++ .../discussion/DiscussionServiceImpl.java | 25 +++- .../discussion/DiscussionServiceImplTest.java | 18 +++ ...etChildrenAuditableCannedQueryFactory.java | 22 --- ...nWithTargetAssocsAuditableCannedQuery.java | 127 ++++++++++++++++++ ...rgetAssocsAuditableCannedQueryFactory.java | 86 ++++++++++++ ...argetAssocsAuditableCannedQueryParams.java | 39 ++++++ .../AbstractQNameAwareCannedQueryFactory.java | 19 +++ .../repo/query/NodeWithTargetsEntity.java | 84 ++++++++++++ 11 files changed, 447 insertions(+), 23 deletions(-) create mode 100644 source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQuery.java create mode 100644 source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryFactory.java create mode 100644 source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryParams.java create mode 100644 source/java/org/alfresco/repo/query/NodeWithTargetsEntity.java diff --git a/config/alfresco/discussions-services-context.xml b/config/alfresco/discussions-services-context.xml index 8a716a667c..703cc1f9ba 100644 --- a/config/alfresco/discussions-services-context.xml +++ b/config/alfresco/discussions-services-context.xml @@ -49,8 +49,19 @@ + + + + + + + + + + + diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml index f88b627f7a..a54fcbf5bc 100644 --- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml +++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml @@ -71,6 +71,7 @@ Inbound settings from iBatis + diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-auditable-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-auditable-common-SqlMap.xml index 70f7154405..6adaf92bac 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-auditable-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-auditable-common-SqlMap.xml @@ -14,6 +14,15 @@ + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/discussion/DiscussionServiceImpl.java b/source/java/org/alfresco/repo/discussion/DiscussionServiceImpl.java index 7177269fef..1d7b3abf1c 100644 --- a/source/java/org/alfresco/repo/discussion/DiscussionServiceImpl.java +++ b/source/java/org/alfresco/repo/discussion/DiscussionServiceImpl.java @@ -36,7 +36,10 @@ import org.alfresco.query.PagingResults; import org.alfresco.repo.domain.node.NodeDAO; import org.alfresco.repo.node.getchildren.GetChildrenAuditableCannedQuery; import org.alfresco.repo.node.getchildren.GetChildrenAuditableCannedQueryFactory; +import org.alfresco.repo.node.getchildren.GetChildrenWithTargetAssocsAuditableCannedQuery; +import org.alfresco.repo.node.getchildren.GetChildrenWithTargetAssocsAuditableCannedQueryFactory; import org.alfresco.repo.query.NodeBackedEntity; +import org.alfresco.repo.query.NodeWithTargetsEntity; import org.alfresco.repo.site.SiteServiceImpl; import org.alfresco.service.cmr.discussion.DiscussionService; import org.alfresco.service.cmr.discussion.PostInfo; @@ -67,6 +70,8 @@ public class DiscussionServiceImpl implements DiscussionService public static final String DISCUSSION_COMPONENT = "discussions"; protected static final String CANNED_QUERY_GET_CHILDREN = "discussionGetChildrenCannedQueryFactory"; + protected static final String CANNED_QUERY_GET_CHILDREN_TARGETS = "discussionGetChildrenWithTargetAssocsAuditableCannedQueryFactory"; + protected static final int MAX_REPLIES_FETCH_SIZE = 1000; /** * The logger @@ -588,7 +593,25 @@ public class DiscussionServiceImpl implements DiscussionService @Override public PostWithReplies listPostReplies(PostInfo primaryPost, int levels) { - // TODO Auto-generated method stub + // Grab the factory + GetChildrenWithTargetAssocsAuditableCannedQueryFactory cqFactory = (GetChildrenWithTargetAssocsAuditableCannedQueryFactory)cannedQueryRegistry.getNamedObject(CANNED_QUERY_GET_CHILDREN_TARGETS); + + // Sort by date + CannedQuerySortDetails sorting = cqFactory.createDateAscendingCQSortDetails(); + + // Run the canned query + GetChildrenWithTargetAssocsAuditableCannedQuery cq = (GetChildrenWithTargetAssocsAuditableCannedQuery)cqFactory.getCannedQuery( + primaryPost.getTopic().getNodeRef(), ForumModel.TYPE_POST, + ContentModel.ASSOC_REFERENCES, sorting, new PagingRequest(MAX_REPLIES_FETCH_SIZE)); + + // Execute the canned query + CannedQueryResults results = cq.execute(); + + // Prepare to invert + // TODO + Map idToNode = new HashMap(); + + // All done return null; } diff --git a/source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java b/source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java index 470d003b4c..65b549a9a5 100644 --- a/source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java +++ b/source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java @@ -672,6 +672,24 @@ public class DiscussionServiceImplTest } } + /** + * Ensures that the listing / nesting of replies is correct + */ + @Test public void replyListing() throws Exception + { + // Create two sites and test + TopicInfo siteT1 = DISCUSSION_SERVICE.createTopic(DISCUSSION_SITE.getShortName(), "ST1"); + TopicInfo nodeT1 = DISCUSSION_SERVICE.createTopic(FORUM_NODE, "NT1"); + testNodesToTidy.add(siteT1.getNodeRef()); + testNodesToTidy.add(nodeT1.getNodeRef()); + + for(TopicInfo topic : new TopicInfo[] {siteT1, nodeT1}) + { + // Listing initially gives nothing + + } + } + /** * Ensures that when we try to write an entry to the * container of a new site, it is correctly setup for us. diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java index 2749fb3f81..e7635271d3 100644 --- a/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenAuditableCannedQueryFactory.java @@ -18,9 +18,7 @@ */ 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; @@ -29,12 +27,10 @@ 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; /** @@ -91,22 +87,4 @@ public class GetChildrenAuditableCannedQueryFactory extends AbstractQNameAwareCa // 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/GetChildrenWithTargetAssocsAuditableCannedQuery.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQuery.java new file mode 100644 index 0000000000..c79dbdddd7 --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQuery.java @@ -0,0 +1,127 @@ +/* + * 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.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.NodeWithTargetsEntity; +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.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 and Target Assocs + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenWithTargetAssocsAuditableCannedQuery 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_GetChildrenWithTargetAssocsAuditableCannedQuery"; + + private final CannedQueryDAO cannedQueryDAO; + + public GetChildrenWithTargetAssocsAuditableCannedQuery( + 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 GetChildrenWithTargetAssocsAuditable query params"); + + GetChildrenWithTargetAssocsAuditableCannedQueryParams paramBean = (GetChildrenWithTargetAssocsAuditableCannedQueryParams) paramBeanObj; + + // 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); + + List filtered = new ArrayList(results.size()); + for (NodeWithTargetsEntity result : results) + { + boolean nextNodeIsAcceptable = true; + + // Note - all filtering is currently done in the database + + // 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/GetChildrenWithTargetAssocsAuditableCannedQueryFactory.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryFactory.java new file mode 100644 index 0000000000..7788a6e3dc --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryFactory.java @@ -0,0 +1,86 @@ +/* + * 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 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.repo.query.AbstractQNameAwareCannedQueryFactory; +import org.alfresco.repo.query.NodeWithTargetsEntity; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; + +/** + * A {@link CannedQueryFactory} for various queries relating to getting + * {@link NodeWithTargetsEntity} entires filtering by auditable properties. + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenWithTargetAssocsAuditableCannedQueryFactory extends AbstractQNameAwareCannedQueryFactory +{ + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + final GetChildrenWithTargetAssocsAuditableCannedQuery cq = new GetChildrenWithTargetAssocsAuditableCannedQuery( + cannedQueryDAO, methodSecurity, parameters + ); + + return (CannedQuery) cq; + } + + public CannedQuery getCannedQuery(NodeRef parentNodeRef, + QName contentType, QName assocType, + 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? + GetChildrenWithTargetAssocsAuditableCannedQueryParams paramBean = new GetChildrenWithTargetAssocsAuditableCannedQueryParams( + getNodeId(parentNodeRef), + getQNameId(ContentModel.PROP_NAME), + getQNameId(contentType), + getQNameId(assocType) + ); + + 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); + } +} diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryParams.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryParams.java new file mode 100644 index 0000000000..bfad079b51 --- /dev/null +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenWithTargetAssocsAuditableCannedQueryParams.java @@ -0,0 +1,39 @@ +/* + * 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 org.alfresco.repo.query.NodeWithTargetsEntity; + +/** + * Parameter objects for {@link GetChildrenWithTargetAssocsAuditableCannedQuery}. + * + * @author Nick Burch + * @since 4.0 + */ +public class GetChildrenWithTargetAssocsAuditableCannedQueryParams extends NodeWithTargetsEntity +{ + public GetChildrenWithTargetAssocsAuditableCannedQueryParams(Long parentNodeId, + Long nameQNameId, + Long contentTypeQNameId, + Long assocTypeId) + + { + super(parentNodeId, nameQNameId, contentTypeQNameId, assocTypeId); + } +} diff --git a/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java b/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java index 366abb630c..463ad81d16 100644 --- a/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/query/AbstractQNameAwareCannedQueryFactory.java @@ -155,6 +155,24 @@ public abstract class AbstractQNameAwareCannedQueryFactory extends AbstractCa return nodePair.getFirst(); } + 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); + } + /** * Utility class to sort Entities on the basis of a Comparable property. * Comparisons of two null properties are considered 'equal' by this comparator. @@ -212,6 +230,7 @@ public abstract class AbstractQNameAwareCannedQueryFactory extends AbstractCa super(comparableProperty); } + @SuppressWarnings("unchecked") @Override protected Comparable getProperty(NodeBackedEntity entity) { if (comparableProperty.equals(ContentModel.PROP_CREATED)) diff --git a/source/java/org/alfresco/repo/query/NodeWithTargetsEntity.java b/source/java/org/alfresco/repo/query/NodeWithTargetsEntity.java new file mode 100644 index 0000000000..7b103c8744 --- /dev/null +++ b/source/java/org/alfresco/repo/query/NodeWithTargetsEntity.java @@ -0,0 +1,84 @@ +/* + * 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.query; + +import java.util.List; + +import org.alfresco.repo.domain.node.NodeEntity; + +/** + * Parent class of Canned Query Entities which are a + * {@link NodeEntity} with additional properties + * + * @author Nick Burch + * @since 4.0 + */ +public class NodeWithTargetsEntity extends NodeBackedEntity +{ + private List targetIds; + private List targetAssocTypeIds; + + // Supplemental query-related parameters + private Long assocTypeId; + + /** + * Default constructor + */ + public NodeWithTargetsEntity() + { + } + + /** + * Query constructor + */ + public NodeWithTargetsEntity(Long parentNodeId, Long nameQNameId, Long contentTypeQNameId, Long assocTypeId) + { + super(parentNodeId, nameQNameId, contentTypeQNameId); + this.assocTypeId = assocTypeId; + } + + public List getTargetIds() + { + return targetIds; + } + + public void setTargetIds(List targetIds) + { + this.targetIds = targetIds; + } + + public List getTargetAssocTypeIds() + { + return targetAssocTypeIds; + } + + public void setTargetAssocTypeIds(List targetAssocTypeIds) + { + this.targetAssocTypeIds = targetAssocTypeIds; + } + + /** + * If set, the ID of the assocation type to limit + * the target assocs to. + */ + public Long getAssocTypeId() + { + return assocTypeId; + } +}