diff --git a/config/alfresco/blog-context.xml b/config/alfresco/blog-context.xml index 00906c0c4f..7f33059085 100644 --- a/config/alfresco/blog-context.xml +++ b/config/alfresco/blog-context.xml @@ -93,8 +93,10 @@ - - + + + + - + + + + + + @@ -180,6 +185,7 @@ Inbound settings from iBatis + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/blog-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/blog-common-SqlMap.xml new file mode 100644 index 0000000000..599e55277c --- /dev/null +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/blog-common-SqlMap.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/AbstractBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/cannedqueries/AbstractBlogPostsCannedQueryFactory.java new file mode 100644 index 0000000000..5fd2b4cfac --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/AbstractBlogPostsCannedQueryFactory.java @@ -0,0 +1,212 @@ +/* + * 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.blog.cannedqueries; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +import org.alfresco.model.BlogIntegrationModel; +import org.alfresco.model.ContentModel; +import org.alfresco.query.AbstractCannedQueryFactory; +import org.alfresco.query.CannedQueryPageDetails; +import org.alfresco.query.CannedQuerySortDetails; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.blog.BlogService.BlogPostInfo; +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.security.permissions.impl.acegi.MethodSecurityBean; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Neil Mc Erlean, janv + * @since 4.0 + */ +public abstract class AbstractBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +{ + private Log logger = LogFactory.getLog(getClass()); + + protected MethodSecurityBean methodSecurity; + 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; + } + + public void setMethodSecurity(MethodSecurityBean methodSecurity) + { + this.methodSecurity = methodSecurity; + } + + protected CannedQuerySortDetails createCQSortDetails(QName sortProp, SortOrder sortOrder) + { + CannedQuerySortDetails cqsd = null; + List> sortPairs = new ArrayList>(); + sortPairs.add(new Pair(sortProp, sortOrder)); + cqsd = new CannedQuerySortDetails(sortPairs); + return cqsd; + } + + protected CannedQueryPageDetails createCQPageDetails(PagingRequest pagingReq) + { + int skipCount = pagingReq.getSkipCount(); + if (skipCount == -1) + { + skipCount = CannedQueryPageDetails.DEFAULT_SKIP_RESULTS; + } + + int maxItems = pagingReq.getMaxItems(); + if (maxItems == -1) + { + maxItems = CannedQueryPageDetails.DEFAULT_PAGE_SIZE; + } + + // page details + CannedQueryPageDetails cqpd = new CannedQueryPageDetails(skipCount, maxItems); + return cqpd; + } + + protected Long getQNameId(QName qname) + { + Pair qnamePair = qnameDAO.getQName(qname); + if (qnamePair == null) + { + if (logger.isTraceEnabled()) + { + logger.trace("QName does not exist: " + qname); // possible ... eg. blg:blogPost if a blog has never been posted externally + } + return null; + } + return qnamePair.getFirst(); + } + + protected Long getNodeId(NodeRef nodeRef) + { + Pair nodePair = nodeDAO.getNodePair(tenantService.getName(nodeRef)); + if (nodePair == null) + { + throw new InvalidNodeRefException("Node ref does not exist: " + nodeRef, nodeRef); + } + return nodePair.getFirst(); + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + PropertyCheck.mandatory(this, "methodSecurity", methodSecurity); + PropertyCheck.mandatory(this, "nodeDAO", nodeDAO); + PropertyCheck.mandatory(this, "qnameDAO", qnameDAO); + PropertyCheck.mandatory(this, "cannedQueryDAO", cannedQueryDAO); + PropertyCheck.mandatory(this, "tenantService", tenantService); + } + + /** + * Utility class to sort {@link BlogPostInfo}s on the basis of a Comparable property. + * Comparisons of two null properties are considered 'equal' by this comparator. + * Comparisons involving one null and one non-null property will return the null property as + * being 'before' the non-null property. + * + * Note that it is the responsibility of the calling code to ensure that the specified + * property values actually implement Comparable themselves. + */ + protected static class PropertyBasedComparator implements Comparator + { + private QName comparableProperty; + + public PropertyBasedComparator(QName comparableProperty) + { + this.comparableProperty = comparableProperty; + } + + @SuppressWarnings("unchecked") + @Override + public int compare(BlogEntity nr1, BlogEntity nr2) + { + Comparable prop1 = null; + Comparable prop2 = null; + if (comparableProperty.equals(ContentModel.PROP_PUBLISHED)) + { + prop1 = nr1.getPublishedDate(); + prop2 = nr2.getPublishedDate(); + } + else if (comparableProperty.equals(ContentModel.PROP_CREATED)) + { + prop1 = nr1.getCreatedDate(); + prop1 = nr2.getCreatedDate(); + } + else if (comparableProperty.equals(BlogIntegrationModel.PROP_POSTED)) + { + prop1 = nr1.getPostedDate(); + prop1 = nr2.getPostedDate(); + } + else + { + throw new IllegalArgumentException("Unsupported blog sort property: "+comparableProperty); + } + + if (prop1 == null && prop2 == null) + { + return 0; + } + else if (prop1 == null && prop2 != null) + { + return -1; + } + else if (prop1 != null && prop2 == null) + { + return 1; + } + else + { + return prop1.compareTo(prop2); + } + } + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/BlogEntity.java b/source/java/org/alfresco/repo/blog/cannedqueries/BlogEntity.java new file mode 100644 index 0000000000..a8d2500020 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/BlogEntity.java @@ -0,0 +1,169 @@ +/* + * 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.blog.cannedqueries; + +import org.alfresco.repo.domain.node.NodeEntity; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Blog Entity - used by GetBlogs CQ + * + * @author janv + * @since 4.0 + */ +public class BlogEntity +{ + private Long id; // node id + + private NodeEntity node; + + private String name; + + private String publishedDate; + private String postedDate; + + // Supplemental query-related parameters + private Long parentNodeId; + private Long nameQNameId; + private Long publishedQNameId; + private Long contentTypeQNameId; + + private Long blogIntAspectQNameId; + private Long blogIntPostedQNameId; + + /** + * Default constructor + */ + public BlogEntity() + { + } + + public BlogEntity(Long parentNodeId, Long nameQNameId, Long publishedQNameId, Long contentTypeQNameId, Long blogIntAspectQNameId, Long blogIntPostedQNameId) + { + this.parentNodeId = parentNodeId; + this.nameQNameId = nameQNameId; + this.publishedQNameId = publishedQNameId; + this.contentTypeQNameId = contentTypeQNameId; + + this.blogIntAspectQNameId = blogIntAspectQNameId; + this.blogIntPostedQNameId = blogIntPostedQNameId; + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + // helper + public NodeRef getNodeRef() + { + return (node != null ? node.getNodeRef() : null); + } + + // helper (ISO 8061) + public String getCreatedDate() + { + return ((node != null && node.getAuditableProperties() != null) ? node.getAuditableProperties().getAuditCreated() : null); + } + + // helper + public String getCreator() + { + return ((node != null && node.getAuditableProperties() != null) ? node.getAuditableProperties().getAuditCreator() : null); + } + + public NodeEntity getNode() + { + return node; + } + + public void setNode(NodeEntity childNode) + { + this.node = childNode; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + // (ISO-8061) + public String getPublishedDate() + { + return publishedDate; + } + + public void setPublishedDate(String published) + { + this.publishedDate = published; + } + + // (ISO-8061) + public String getPostedDate() + { + return postedDate; + } + + public void setPostedDate(String postedDateISO8061) + { + this.postedDate = postedDateISO8061; + } + + // Supplemental query-related parameters + + public Long getParentNodeId() + { + return parentNodeId; + } + + public Long getNameQNameId() + { + return nameQNameId; + } + + public Long getPublishedQNameId() + { + return publishedQNameId; + } + + public Long getContentTypeQNameId() + { + return contentTypeQNameId; + } + + public Long getBlogIntAspectQNameId() + { + return blogIntAspectQNameId; + } + + public Long getBlogIntPostedQNameId() + { + return blogIntPostedQNameId; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java index 7f41ab0367..c4f01f06e2 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java @@ -21,24 +21,24 @@ package org.alfresco.repo.blog.cannedqueries; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryParameters; import org.alfresco.query.CannedQuerySortDetails.SortOrder; import org.alfresco.repo.blog.BlogService; import org.alfresco.repo.blog.BlogService.BlogPostInfo; +import org.alfresco.repo.blog.cannedqueries.AbstractBlogPostsCannedQueryFactory.PropertyBasedComparator; +import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; -import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; /** * This is a {@link CannedQuery} for the rather particular 'get my drafts and all published' blog-post query. @@ -50,81 +50,101 @@ import org.alfresco.util.Pair; */ public class DraftsAndPublishedBlogPostsCannedQuery extends AbstractCannedQueryPermissions { - private final NodeService rawNodeService; + private Log logger = LogFactory.getLog(getClass()); + + private static final String QUERY_NAMESPACE = "alfresco.blog"; + private static final String QUERY_SELECT_GET_BLOGS = "select_GetBlogsCannedQuery"; + + private final CannedQueryDAO cannedQueryDAO; private final TaggingService taggingService; public DraftsAndPublishedBlogPostsCannedQuery( - NodeService rawNodeService, + CannedQueryDAO cannedQueryDAO, TaggingService taggingService, MethodSecurityBean methodSecurity, CannedQueryParameters params) { super(params, methodSecurity); - this.rawNodeService = rawNodeService; + this.cannedQueryDAO = cannedQueryDAO; this.taggingService = taggingService; } @Override protected List queryAndFilter(CannedQueryParameters parameters) { + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + Object paramBeanObj = parameters.getParameterBean(); if (paramBeanObj == null) throw new NullPointerException("Null GetBlogPosts query params"); DraftsAndPublishedBlogPostsCannedQueryParams paramBean = (DraftsAndPublishedBlogPostsCannedQueryParams) paramBeanObj; - + String requestedCreator = paramBean.getCmCreator(); String requestedTag = paramBean.getTag(); Date createdFromDate = paramBean.getCreatedFromDate(); Date createdToDate = paramBean.getCreatedToDate(); - List childAssocs = getAllBlogNodes(paramBean.getBlogContainerNode()); + // note: refer to SQL for specific DB filtering (eg.parent node and optionally blog integration aspect, etc) + List results = cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_BLOGS, paramBean, 0, Integer.MAX_VALUE); - List filteredNodeRefs = new ArrayList(); - for (ChildAssociationRef chAssRef : childAssocs) + List filtered = new ArrayList(results.size()); + for (BlogEntity result : results) { - NodeRef nextBlogNode = chAssRef.getChildRef(); - // Is this next node in the list to be included in the results? boolean nextNodeIsAcceptable = true; + Date actualPublishedDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getPublishedDate()); + + String actualCreator = result.getCreator(); + Date actualCreatedDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getCreatedDate()); + // Return all published Blog Posts - if (rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != null) + if (actualPublishedDate != null) { // Intentionally empty } else { // We're relying on cm:published being null below i.e. we are dealing with draft blog posts. - if (!rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_CREATOR).equals(paramBean.getCmCreator())) + if (requestedCreator != null) + { + if (! requestedCreator.equals(actualCreator)) + { + nextNodeIsAcceptable = false; + } + } + else { nextNodeIsAcceptable = false; } } // Only return blogs created within the specified dates - Date actualCreatedDate = (Date) rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_CREATED); - if (actualCreatedDate != null) + if ((createdFromDate != null) || (createdToDate != null)) { - if (createdFromDate != null && actualCreatedDate.before(createdFromDate)) + if (actualCreatedDate != null) { - nextNodeIsAcceptable = false; - } - if (createdToDate != null && actualCreatedDate.after(createdToDate)) - { - nextNodeIsAcceptable = false; + if (createdFromDate != null && actualCreatedDate.before(createdFromDate)) + { + nextNodeIsAcceptable = false; + } + if (createdToDate != null && actualCreatedDate.after(createdToDate)) + { + nextNodeIsAcceptable = false; + } } } + // TODO review use-case and either remove or push-down // Only return blog posts tagged with the specified tag string. - if (requestedTag != null && !taggingService.getTags(nextBlogNode).contains(requestedTag)) + if (requestedTag != null && !taggingService.getTags(result.getNode().getNodeRef()).contains(requestedTag)) { nextNodeIsAcceptable = false; } - if (nextNodeIsAcceptable) { - filteredNodeRefs.add(new BlogPostInfo(nextBlogNode, (String)rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_NAME))); + filtered.add(result); } } @@ -135,26 +155,26 @@ public class DraftsAndPublishedBlogPostsCannedQuery extends AbstractCannedQueryP Pair sortPair = sortPairs.get(0); QName sortProperty = (QName) sortPair.getFirst(); - final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); - + final PropertyBasedComparator comparator = new PropertyBasedComparator(sortProperty); + if (sortPair.getSecond() == SortOrder.DESCENDING) { - Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + Collections.sort(filtered, Collections.reverseOrder(comparator)); } } + List blogPostInfos = new ArrayList(filtered.size()); + for (BlogEntity result : filtered) + { + blogPostInfos.add(new BlogPostInfo(result.getNodeRef(), result.getName())); + } - return filteredNodeRefs; - } - - private List getAllBlogNodes(NodeRef containerNode) - { - final Set childNodeTypes = new HashSet(); - childNodeTypes.add(ContentModel.TYPE_CONTENT); + if (start != null) + { + logger.debug("Base query: "+blogPostInfos.size()+" in "+(System.currentTimeMillis()-start)+" msecs"); + } - // This will, of course, retrieve all the blog posts which may be a very long list. - List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); - return childAssocs; + return blogPostInfos; } @Override diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java index 63913d3e90..87589ea0b5 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java @@ -18,54 +18,31 @@ */ package org.alfresco.repo.blog.cannedqueries; -import java.util.ArrayList; import java.util.Date; -import java.util.List; import org.alfresco.model.ContentModel; -import org.alfresco.query.AbstractCannedQueryFactory; 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.CannedQuerySortDetails.SortOrder; import org.alfresco.query.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; import org.alfresco.repo.blog.BlogService.BlogPostInfo; -import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.tagging.TaggingService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; -import org.alfresco.util.PropertyCheck; /** * A {@link CannedQueryFactory} for the creation of {@link DraftsAndPublishedBlogPostsCannedQuery}s. * - * Currently, this is implemented using calls to lower-level services, notably the {@link NodeService} rather - * than database queries. This may change in the future. - * + * @author Neil Mc Erlean * @since 4.0 - * @author Neil Mc Erlean. */ -public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractBlogPostsCannedQueryFactory { - private MethodSecurityBean methodSecurity; - private NodeService rawNodeService; private TaggingService taggingService; - public void setMethodSecurity(MethodSecurityBean methodSecurity) - { - this.methodSecurity = methodSecurity; - } - - public void setRawNodeService(NodeService nodeService) - { - this.rawNodeService = nodeService; - } - public void setTaggingService(TaggingService taggingService) { this.taggingService = taggingService; @@ -75,7 +52,7 @@ public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCanne public CannedQuery getCannedQuery(CannedQueryParameters parameters) { final DraftsAndPublishedBlogPostsCannedQuery cq = new DraftsAndPublishedBlogPostsCannedQuery( - rawNodeService, taggingService, + cannedQueryDAO, taggingService, methodSecurity, parameters); return (CannedQuery) cq; @@ -89,7 +66,11 @@ public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCanne int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); //FIXME Need tenant service like for GetChildren? - DraftsAndPublishedBlogPostsCannedQueryParams paramBean = new DraftsAndPublishedBlogPostsCannedQueryParams(blogContainerNode, + DraftsAndPublishedBlogPostsCannedQueryParams paramBean = new DraftsAndPublishedBlogPostsCannedQueryParams( + getNodeId(blogContainerNode), + getQNameId(ContentModel.PROP_NAME), + getQNameId(ContentModel.PROP_PUBLISHED), + getQNameId(ContentModel.TYPE_CONTENT), byUser, fromDate, toDate, tag); @@ -101,41 +82,5 @@ public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCanne // return canned query instance return getCannedQuery(params); -} - - private CannedQuerySortDetails createCQSortDetails(QName sortProp, SortOrder sortOrder) - { - CannedQuerySortDetails cqsd = null; - List> sortPairs = new ArrayList>(); - sortPairs.add(new Pair(sortProp, sortOrder)); - cqsd = new CannedQuerySortDetails(sortPairs); - return cqsd; - } - - private CannedQueryPageDetails createCQPageDetails(PagingRequest pagingReq) - { - int skipCount = pagingReq.getSkipCount(); - if (skipCount == -1) - { - skipCount = CannedQueryPageDetails.DEFAULT_SKIP_RESULTS; - } - - int maxItems = pagingReq.getMaxItems(); - if (maxItems == -1) - { - maxItems = CannedQueryPageDetails.DEFAULT_PAGE_SIZE; - } - - // page details - CannedQueryPageDetails cqpd = new CannedQueryPageDetails(skipCount, maxItems); - return cqpd; - } - - @Override - public void afterPropertiesSet() throws Exception - { - super.afterPropertiesSet(); - - PropertyCheck.mandatory(this, "methodSecurity", methodSecurity); } } diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java index 1777a70b6b..1088c30660 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java @@ -20,40 +20,36 @@ package org.alfresco.repo.blog.cannedqueries; import java.util.Date; -import org.alfresco.service.cmr.repository.NodeRef; - /** * Parameters for {@link DraftsAndPublishedBlogPostsCannedQuery}. * * @author Neil Mc Erlean * @since 4.0 */ -public class DraftsAndPublishedBlogPostsCannedQueryParams +public class DraftsAndPublishedBlogPostsCannedQueryParams extends BlogEntity { - private final NodeRef blogContainerNode; private final String cmCreator; private final Date createdFromDate; private final Date createdToDate; private final String tag; - public DraftsAndPublishedBlogPostsCannedQueryParams(NodeRef blogContainerNodeRef, - String cmCreator, - Date createdFromDate, - Date createdToDate, - String tag) + public DraftsAndPublishedBlogPostsCannedQueryParams(Long blogContainerNodeId, + Long nameQNameId, + Long publishedQNameId, + Long contentTypeQNameId, + String cmCreator, + Date createdFromDate, + Date createdToDate, + String tag) { - this.blogContainerNode = blogContainerNodeRef; + super(blogContainerNodeId, nameQNameId, publishedQNameId, contentTypeQNameId, null, null); + this.cmCreator = cmCreator; this.createdFromDate = createdFromDate; this.createdToDate = createdToDate; this.tag = tag; } - public NodeRef getBlogContainerNode() - { - return blogContainerNode; - } - public String getCmCreator() { return cmCreator; diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java index bc3c256cb3..fb520ee2bc 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java @@ -21,50 +21,54 @@ package org.alfresco.repo.blog.cannedqueries; import java.util.ArrayList; import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; -import java.util.Set; -import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryParameters; import org.alfresco.query.CannedQuerySortDetails.SortOrder; import org.alfresco.repo.blog.BlogService; import org.alfresco.repo.blog.BlogService.BlogPostInfo; +import org.alfresco.repo.blog.cannedqueries.AbstractBlogPostsCannedQueryFactory.PropertyBasedComparator; +import org.alfresco.repo.domain.node.AuditablePropertiesEntity; +import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; -import org.alfresco.service.cmr.repository.ChildAssociationRef; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; +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 several {@link CannedQuery canned queries} used by the * {@link BlogService}. * - * @author Neil Mc Erlean + * @author Neil Mc Erlean, janv * @since 4.0 */ public class GetBlogPostsCannedQuery extends AbstractCannedQueryPermissions { - /* - * This must be the small n nodeService, not the big N NodeService. See below. - */ - private final NodeService rawNodeService; + private Log logger = LogFactory.getLog(getClass()); + + private static final String QUERY_NAMESPACE = "alfresco.blog"; + private static final String QUERY_SELECT_GET_BLOGS = "select_GetBlogsCannedQuery"; + + private final CannedQueryDAO cannedQueryDAO; public GetBlogPostsCannedQuery( - NodeService rawNodeService, + CannedQueryDAO cannedQueryDAO, MethodSecurityBean methodSecurity, CannedQueryParameters params) { super(params, methodSecurity); - this.rawNodeService = rawNodeService; + 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 GetBlogPosts query params"); @@ -74,66 +78,57 @@ public class GetBlogPostsCannedQuery extends AbstractCannedQueryPermissions requiredAspects = paramBean.getRequiredAspects(); - // Retrieve all blog-post nodes under the blogContainer root. This could potentially - // be a long list of NodeRefs and it is possible that future optimisation towards DB queries - // would avoid the retrieval of potentially long lists like this. - // It is however important to retrieve the full list of relevant nodes before any sorting - // is applied. Otherwise it would be possible to have nodes that were not retrieved, which after sorting - // could be at the front of this list. - // For that reason, we must use the small n nodeService, and not the large N NodeService, because the - // latter truncates results. - List childAssocs = getAllBlogNodes(paramBean.getBlogContainerNode()); + // note: refer to SQL for specific DB filtering (eg.parent node and optionally blog integration aspect, etc) + List results = cannedQueryDAO.executeQuery(QUERY_NAMESPACE, QUERY_SELECT_GET_BLOGS, paramBean, 0, Integer.MAX_VALUE); - List filteredNodeRefs = new ArrayList(); - for (ChildAssociationRef chAssRef : childAssocs) + List filtered = new ArrayList(results.size()); + for (BlogEntity result : results) { - // Is the nextBlogPostNode going to be included or not? boolean nextNodeIsAcceptable = true; - NodeRef nextBlogNode = chAssRef.getChildRef(); + Date actualPublishedDate = DefaultTypeConverter.INSTANCE.convert(Date.class, result.getPublishedDate()); - // Only return blog-posts whose cm:published status matches that requested. - final boolean nextBlogNodeIsPublished = rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != null; - if (nextBlogNodeIsPublished != isPublished) + // Only return blog-posts whose cm:published status matches that requested (ie. a blog "is published" when published date is not null) + boolean blogIsPublished = (actualPublishedDate != null); + if (blogIsPublished != isPublished) { nextNodeIsAcceptable = false; } // Only return blog posts whose creator matches the given username, if there is one. - if (requestedCreator != null && !rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_CREATOR).equals(requestedCreator)) + if (requestedCreator != null) { - nextNodeIsAcceptable = false; + AuditablePropertiesEntity auditProps = result.getNode().getAuditableProperties(); + if ((auditProps == null) || (! requestedCreator.equals(auditProps.getAuditCreator()))) + { + nextNodeIsAcceptable = false; + } } // Only return blogs published within the specified dates - Date actualPublishedDate = (Date) rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED); - if (actualPublishedDate != null) + if ((publishedFromDate != null) || (publishedToDate != null)) { - if (publishedFromDate != null && actualPublishedDate.before(publishedFromDate)) + if (actualPublishedDate != null) { - nextNodeIsAcceptable = false; + if (publishedFromDate != null && actualPublishedDate.before(publishedFromDate)) + { + nextNodeIsAcceptable = false; + } + if (publishedToDate != null && actualPublishedDate.after(publishedToDate)) + { + nextNodeIsAcceptable = false; + } } - if (publishedToDate != null && actualPublishedDate.after(publishedToDate)) + else { nextNodeIsAcceptable = false; } } - // Only those with the required aspects. - for (QName aspect : requiredAspects) - { - if (!rawNodeService.hasAspect(nextBlogNode, aspect)) - { - nextNodeIsAcceptable = false; - } - } - - // If all the above conditions are true... if (nextNodeIsAcceptable) { - filteredNodeRefs.add(new BlogPostInfo(nextBlogNode, (String)rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_NAME))); + filtered.add(result); } } @@ -145,27 +140,27 @@ public class GetBlogPostsCannedQuery extends AbstractCannedQueryPermissions sortPair = sortPairs.get(0); QName sortProperty = (QName) sortPair.getFirst(); - final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); - + final PropertyBasedComparator comparator = new PropertyBasedComparator(sortProperty); + if (sortPair.getSecond() == SortOrder.DESCENDING) { - Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + Collections.sort(filtered, Collections.reverseOrder(comparator)); } } - return filteredNodeRefs; - } - - private List getAllBlogNodes(NodeRef containerNode) - { - final Set childNodeTypes = new HashSet(); - childNodeTypes.add(ContentModel.TYPE_CONTENT); + List blogPostInfos = new ArrayList(filtered.size()); + for (BlogEntity result : filtered) + { + blogPostInfos.add(new BlogPostInfo(result.getNodeRef(), result.getName())); + } - // This will, of course, retrieve all the blog posts which may be a very long list. - List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); - return childAssocs; + if (start != null) + { + logger.debug("Base query: "+blogPostInfos.size()+" in "+(System.currentTimeMillis()-start)+" msecs"); + } + + return blogPostInfos; } - @Override protected boolean isApplyPostQuerySorting() diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java index 936cdcfc5e..ea58b3243e 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java @@ -18,61 +18,38 @@ */ package org.alfresco.repo.blog.cannedqueries; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; -import java.util.List; import org.alfresco.model.BlogIntegrationModel; import org.alfresco.model.ContentModel; -import org.alfresco.query.AbstractCannedQueryFactory; 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.CannedQuerySortDetails.SortOrder; import org.alfresco.query.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; import org.alfresco.repo.blog.BlogService; import org.alfresco.repo.blog.BlogService.BlogPostInfo; -import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.Pair; import org.alfresco.util.ParameterCheck; -import org.alfresco.util.PropertyCheck; /** * A {@link CannedQueryFactory} for various queries relating to {@link BlogPostInfo blog-posts}. - * Currently, this is implemented using calls to lower-level services, notably the {@link NodeService} rather - * than database queries. This may change in the future. * - * @author Neil Mc Erlean. + * @author Neil Mc Erlean * @since 4.0 * * @see BlogService#getDrafts(NodeRef, String, PagingRequest) * @see BlogService#getPublished(NodeRef, Date, Date, String, PagingRequest) */ -public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +public class GetBlogPostsCannedQueryFactory extends AbstractBlogPostsCannedQueryFactory { - private MethodSecurityBean methodSecurity; - private NodeService rawNodeService; - - public void setMethodSecurity(MethodSecurityBean methodSecurity) - { - this.methodSecurity = methodSecurity; - } - - public void setRawNodeService(NodeService nodeService) - { - this.rawNodeService = nodeService; - } - @Override public CannedQuery getCannedQuery(CannedQueryParameters parameters) { - final GetBlogPostsCannedQuery cq = new GetBlogPostsCannedQuery(rawNodeService, methodSecurity, parameters); + final GetBlogPostsCannedQuery cq = new GetBlogPostsCannedQuery(cannedQueryDAO, methodSecurity, parameters); + return (CannedQuery) cq; } @@ -85,12 +62,14 @@ public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory requiredAspects = null; - GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(getNodeId(blogContainerNode), + getQNameId(ContentModel.PROP_NAME), + getQNameId(ContentModel.PROP_PUBLISHED), + getQNameId(ContentModel.TYPE_CONTENT), username, isPublished, null, null, - requiredAspects); + null, null); CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_CREATED, SortOrder.DESCENDING); @@ -110,12 +89,24 @@ public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory requiredAspects = Arrays.asList(new QName[]{BlogIntegrationModel.ASPECT_BLOG_POST}); - GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + + Long blogIntAspectQNameId = getQNameId(BlogIntegrationModel.ASPECT_BLOG_POST); + if (blogIntAspectQNameId == null) + { + // possible if no blogs have ever been published externally + blogIntAspectQNameId = -1L; // run the query but should return empty results + } + + // published externally if it has the BLOG_POST aspect + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(getNodeId(blogContainerNode), + getQNameId(ContentModel.PROP_NAME), + getQNameId(ContentModel.PROP_PUBLISHED), + getQNameId(ContentModel.TYPE_CONTENT), null, isPublished, null, null, - requiredAspects); + blogIntAspectQNameId, + getQNameId(BlogIntegrationModel.PROP_POSTED)); CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); CannedQuerySortDetails cqsd = createCQSortDetails(BlogIntegrationModel.PROP_POSTED, SortOrder.DESCENDING); @@ -135,12 +126,14 @@ public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory requiredAspects = null; - GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(getNodeId(blogContainerNode), + getQNameId(ContentModel.PROP_NAME), + getQNameId(ContentModel.PROP_PUBLISHED), + getQNameId(ContentModel.TYPE_CONTENT), byUser, isPublished, fromDate, toDate, - requiredAspects); + null, null); CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_PUBLISHED, SortOrder.DESCENDING); @@ -151,40 +144,4 @@ public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory> sortPairs = new ArrayList>(); - sortPairs.add(new Pair(sortProp, sortOrder)); - cqsd = new CannedQuerySortDetails(sortPairs); - return cqsd; - } - - private CannedQueryPageDetails createCQPageDetails(PagingRequest pagingReq) - { - int skipCount = pagingReq.getSkipCount(); - if (skipCount == -1) - { - skipCount = CannedQueryPageDetails.DEFAULT_SKIP_RESULTS; - } - - int maxItems = pagingReq.getMaxItems(); - if (maxItems == -1) - { - maxItems = CannedQueryPageDetails.DEFAULT_PAGE_SIZE; - } - - // page details - CannedQueryPageDetails cqpd = new CannedQueryPageDetails(skipCount, maxItems, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT); - return cqpd; - } - - @Override - public void afterPropertiesSet() throws Exception - { - super.afterPropertiesSet(); - - PropertyCheck.mandatory(this, "methodSecurityInterceptor", methodSecurity); - } } diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java index c0de0d42e2..17fe99edfe 100644 --- a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java @@ -18,12 +18,7 @@ */ package org.alfresco.repo.blog.cannedqueries; -import java.util.Collections; import java.util.Date; -import java.util.List; - -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.namespace.QName; /** * Parameter objects for {@link GetBlogPostsCannedQuery}. @@ -31,40 +26,37 @@ import org.alfresco.service.namespace.QName; * @author Neil Mc Erlean * @since 4.0 */ -public class GetBlogPostsCannedQueryParams +public class GetBlogPostsCannedQueryParams extends BlogEntity { - private final NodeRef blogContainerNode; private final String cmCreator; + /** * true means the blog-posts should be cm:published, false means they should not. */ private final boolean isPublished; + private final Date publishedFromDate; private final Date publishedToDate; - private final List requiredAspects; + private final Long blogIntAspectQNameId; - public GetBlogPostsCannedQueryParams(NodeRef blogContainerNodeRef, + public GetBlogPostsCannedQueryParams(Long blogContainerNodeId, + Long nameQNameId, + Long publishedQNameId, + Long contentTypeQNameId, String cmCreator, boolean isPublished, Date publishedFromDate, Date publishedToDate, - List requiredAspects) + Long blogIntAspectQNameId, + Long blogIntPostedQNameId) { - this.blogContainerNode = blogContainerNodeRef; + super(blogContainerNodeId, nameQNameId, publishedQNameId, contentTypeQNameId, blogIntAspectQNameId, blogIntPostedQNameId); + this.cmCreator = cmCreator; this.isPublished = isPublished; this.publishedFromDate = publishedFromDate; this.publishedToDate = publishedToDate; - if (requiredAspects == null) - { - requiredAspects = Collections.emptyList(); - } - this.requiredAspects = requiredAspects; - } - - public NodeRef getBlogContainerNode() - { - return blogContainerNode; + this.blogIntAspectQNameId = blogIntAspectQNameId; } public String getCmCreator() @@ -87,8 +79,8 @@ public class GetBlogPostsCannedQueryParams return publishedToDate; } - public List getRequiredAspects() + public Long getBlogIntAspectQNameId() { - return Collections.unmodifiableList(this.requiredAspects); + return blogIntAspectQNameId; } } diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java b/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java deleted file mode 100644 index 7a481aea71..0000000000 --- a/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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.blog.cannedqueries; - -import java.util.Comparator; - -import org.alfresco.repo.blog.BlogService.BlogPostInfo; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.QName; - -/** - * Utility class to sort {@link BlogPostInfo}s on the basis of a Comparable property. - * Comparisons of two null properties are considered 'equal' by this comparator. - * Comparisons involving one null and one non-null property will return the null property as - * being 'before' the non-null property. - * - * Note that it is the responsibility of the calling code to ensure that the specified - * property values actually implement Comparable themselves. - */ -class PropertyBasedComparator implements Comparator -{ - private QName comparableProperty; - private NodeService nodeService; - - public PropertyBasedComparator(QName comparableProperty, NodeService nodeService) - { - this.comparableProperty = comparableProperty; - this.nodeService = nodeService; - } - - @SuppressWarnings("unchecked") - @Override - public int compare(BlogPostInfo nr1, BlogPostInfo nr2) - { - Comparable prop1 = (Comparable) nodeService.getProperty(nr1.getNodeRef(), comparableProperty); - Comparable prop2 = (Comparable) nodeService.getProperty(nr2.getNodeRef(), comparableProperty); - - if (prop1 == null && prop2 == null) - { - return 0; - } - else if (prop1 == null && prop2 != null) - { - return -1; - } - else if (prop1 != null && prop2 == null) - { - return 1; - } - else - { - return prop1.compareTo(prop2); - } - } -} \ No newline at end of file