diff --git a/config/alfresco/blog-context.xml b/config/alfresco/blog-context.xml index 2e2e0d3d58..8c93d84e45 100644 --- a/config/alfresco/blog-context.xml +++ b/config/alfresco/blog-context.xml @@ -2,8 +2,107 @@ - - + + + + org.alfresco.repo.blog.BlogService + + + + + + + + + + + + + + + + + + + + + + ${server.transaction.mode.default} + + + + + + + + + + + + + org.alfresco.repo.blog.BlogService.getDrafts=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.repo.blog.BlogService.getPublished=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.repo.blog.BlogService.getPublishedExternally=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.repo.blog.BlogService.getMyDraftsAndAllPublished=ACL_NODE.0.sys:base.ReadChildren,AFTER_ACL_NODE.sys:base.ReadProperties + org.alfresco.repo.blog.BlogService.*=ACL_ALLOW + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/repo/blog/BlogPostInfo.java b/source/java/org/alfresco/repo/blog/BlogPostInfo.java new file mode 100644 index 0000000000..b297407eed --- /dev/null +++ b/source/java/org/alfresco/repo/blog/BlogPostInfo.java @@ -0,0 +1,42 @@ +/* + * 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; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * A simple data object for storage of blog-related data. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public interface BlogPostInfo +{ + /** + * Gets the NodeRef representing this blog-post. + */ + public NodeRef getNodeRef(); + + /** + * Gets the {@link ContentModel#PROP_NAME cm:name} of the blog post. + * @return + */ + public String getName(); +} diff --git a/source/java/org/alfresco/repo/blog/BlogPostInfoImpl.java b/source/java/org/alfresco/repo/blog/BlogPostInfoImpl.java new file mode 100644 index 0000000000..1ee3d79676 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/BlogPostInfoImpl.java @@ -0,0 +1,101 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * A simple data object responsible for holding information relevant to blog post NodeRefs. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class BlogPostInfoImpl implements BlogPostInfo +{ + private NodeRef nodeRef; + private Map properties = new HashMap(); + + BlogPostInfoImpl(NodeRef nodeRef, Map properties) + { + this.nodeRef = nodeRef; + this.properties = properties; + } + + /** + * @see #getNodeRef() + * @see NodeRef#equals(Object) + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (this == obj) + { + return true; + } + else if (obj instanceof BlogPostInfoImpl == false) + { + return false; + } + BlogPostInfoImpl that = (BlogPostInfoImpl) obj; + return (this.getNodeRef().equals(that.getNodeRef())); + } + + /** + * @see #getNodeRef() + * @see NodeRef#hashCode() + */ + @Override + public int hashCode() + { + return getNodeRef().hashCode(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append(BlogPostInfo.class.getSimpleName()) + .append("[name=").append(getName()) + .append(", nodeRef=").append(nodeRef); + sb.append("]"); + return sb.toString(); + } + + @Override + public NodeRef getNodeRef() + { + return nodeRef; + } + + @Override + public String getName() + { + return (String) properties.get(ContentModel.PROP_NAME); + } +} diff --git a/source/java/org/alfresco/repo/blog/BlogService.java b/source/java/org/alfresco/repo/blog/BlogService.java new file mode 100644 index 0000000000..0175916e0f --- /dev/null +++ b/source/java/org/alfresco/repo/blog/BlogService.java @@ -0,0 +1,122 @@ +/* + * 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; + +import java.util.Date; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteService; + +/** + * The Blog Service handles the management (CRUD) of Alfresco blog data, namely the blog posts which are + * exposed in the Share UI under the "Blog" heading. The {@link BlogIntegrationService}, a separate service, is + * concerned with the integration of Alfresco blog content with external Blog-hosting sites. + *

+ * Please note that this service is a work in progress and currently exists primarily to support the blogs REST API. + * + * @author Neil Mc Erlean (based on existing webscript controllers in the REST API) + * @since 4.0 + */ +public interface BlogService +{ + /** + * Creates a new blog post within the specified container node. + * + * @param blogContainerNode the container node for blog posts (under the site). + * @param blogTitle the title of the blog post. + * @param blogContent text/html content of the blog post. + * @param isDraft true if the blog post is a draft post, else false. + * + * @return The {@link ChildAssociationRef} of the newly created blog post. + * + * @see SiteService#getContainer(String, String) to retrieve the blogContainerNode + */ + ChildAssociationRef createBlogPost(NodeRef blogContainerNode, String blogTitle, + String blogContent, boolean isDraft); + + /** + * Gets the draft blog posts created by the specified user. + * + * @param blogContainerNode the container node for blog posts (under the site). + * @param username to limit results to blogs with this cm:creator. null means all users. + * @param pagingReq an object defining the paging parameters for the result set. + * + * @return a {@link PagingResults} object containing some or all of the results (subject to paging). + * + * @see SiteService#getContainer(String, String) to retrieve the blogContainerNode + */ + PagingResults getDrafts(NodeRef blogContainerNode, String username, PagingRequest pagingReq); + + /** + * Gets the (internally, Alfresco-) published blog posts. + * + * @param blogContainerNode the container node for blog posts (under the site). + * @param fromDate an inclusive date limit for the results (more recent than). + * @param toDate an inclusive date limit for the results (before). + * @param byUser if not null limits results to posts by the specified user. + * if null results will be by all users. + * @param pagingReq an object defining the paging parameters for the result set. + * + * @return a {@link PagingResults} object containing some or all of the results (subject to paging). + * + * @see SiteService#getContainer(String, String) to retrieve the blogContainerNode + */ + PagingResults getPublished(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, PagingRequest pagingReq); + + /** + * Gets blog posts published externally (i.e. to an external blog hosting site). + * + * @param blogContainerNode the container node for blog posts (under the site). + * @param pagingReq an object defining the paging parameters for the result set. + * + * @return a {@link PagingResults} object containing some or all of the results (subject to paging). + * + * @see SiteService#getContainer(String, String) to retrieve the blogContainerNode + */ + PagingResults getPublishedExternally(NodeRef blogContainerNode, PagingRequest pagingReq); + + /** + * Gets draft blog posts by the currently authenticated user along with all published posts. + * + * @param blogContainerNode the container node for blog posts (under the site). + * @param fromDate an inclusive date limit for the results (more recent than). + * @param toDate an inclusive date limit for the results (before). + * @param tag if specified, only returns posts tagged with this tag. + * @param pagingReq an object defining the paging parameters for the result set. + * + * @return a {@link PagingResults} object containing some or all of the results (subject to paging). + * + * @see SiteService#getContainer(String, String) to retrieve the blogContainerNode + * + * @deprecated This method is a domain-specific query used by the Blog REST API and is not considered suitable for general use. + */ + PagingResults getMyDraftsAndAllPublished(NodeRef blogContainerNode, Date fromDate, Date toDate, + String tag, PagingRequest pagingReq); + + /** + * Returns true if the specified blog-post node is a 'draft' blog post. + * + * @param blogPostNode a NodeRef representing a blog-post. + * @return true if it is a draft post, else false. + */ + boolean isDraftBlogPost(NodeRef blogPostNode); +} diff --git a/source/java/org/alfresco/repo/blog/BlogServiceImpl.java b/source/java/org/alfresco/repo/blog/BlogServiceImpl.java new file mode 100644 index 0000000000..98d47cc2c2 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/BlogServiceImpl.java @@ -0,0 +1,316 @@ +/* + * 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; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQueryFactory; +import org.alfresco.query.CannedQueryResults; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.blog.cannedqueries.DraftsAndPublishedBlogPostsCannedQuery; +import org.alfresco.repo.blog.cannedqueries.DraftsAndPublishedBlogPostsCannedQueryFactory; +import org.alfresco.repo.blog.cannedqueries.GetBlogPostsCannedQuery; +import org.alfresco.repo.blog.cannedqueries.GetBlogPostsCannedQueryFactory; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; +import org.alfresco.util.registry.NamedObjectRegistry; + +/** + * @author Neil Mc Erlean (based on existing webscript controllers in the REST API) + * @since 4.0 + */ +public class BlogServiceImpl implements BlogService +{ + /** + * For backwards compatibility with pre-Swift, we are asking the query to give us an accurate total count of how many + * blog-post nodes there are. This may need to change in the future - certainly if the current 'brute force' query + * is replaced by a database query. + */ + private static final int MAX_QUERY_ENTRY_COUNT = 10000; + + // Injected services + private NamedObjectRegistry> cannedQueryRegistry; + private GetBlogPostsCannedQueryFactory draftPostsCannedQueryFactory; + private GetBlogPostsCannedQueryFactory publishedPostsCannedQueryFactory; + private GetBlogPostsCannedQueryFactory publishedExternallyPostsCannedQueryFactory; + + private DraftsAndPublishedBlogPostsCannedQueryFactory draftsAndPublishedBlogPostsCannedQueryFactory; + + private ContentService contentService; + private NodeService nodeService; + private PermissionService permissionService; + private TaggingService taggingService; + + public void setCannedQueryRegistry(NamedObjectRegistry> cannedQueryRegistry) + { + this.cannedQueryRegistry = cannedQueryRegistry; + } + + public void setDraftBlogPostsCannedQueryFactory(GetBlogPostsCannedQueryFactory cannedQueryFactory) + { + this.draftPostsCannedQueryFactory = cannedQueryFactory; + } + + public void setPublishedBlogPostsCannedQueryFactory(GetBlogPostsCannedQueryFactory cannedQueryFactory) + { + this.publishedPostsCannedQueryFactory = cannedQueryFactory; + } + + public void setPublishedExternallyBlogPostsCannedQueryFactory(GetBlogPostsCannedQueryFactory cannedQueryFactory) + { + this.publishedExternallyPostsCannedQueryFactory = cannedQueryFactory; + } + + public void setDraftsAndPublishedBlogPostsCannedQueryFactory(DraftsAndPublishedBlogPostsCannedQueryFactory cannedQueryFactory) + { + this.draftsAndPublishedBlogPostsCannedQueryFactory = cannedQueryFactory; + } + + public void setContentService(ContentService contentService) + { + this.contentService = contentService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.blog.BlogService#isDraftBlogPost(org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + public boolean isDraftBlogPost(NodeRef blogPostNode) + { + return nodeService.getProperty(blogPostNode, ContentModel.PROP_PUBLISHED) == null; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.blog.BlogService#createBlogPost(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.util.List, boolean) + */ + @Override + public ChildAssociationRef createBlogPost(NodeRef blogContainerNode, String blogTitle, + String blogContent, boolean isDraft) + { + String nodeName = getUniqueChildName(blogContainerNode, "post"); + + // we simply create a new file inside the blog folder + Map nodeProps = new HashMap(); + nodeProps.put(ContentModel.PROP_NAME, nodeName); + nodeProps.put(ContentModel.PROP_TITLE, blogTitle); + QName assocName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeName); + ChildAssociationRef postNode = nodeService.createNode(blogContainerNode, ContentModel.ASSOC_CONTAINS, assocName, + ContentModel.TYPE_CONTENT, nodeProps); + + ContentWriter writer = contentService.getWriter(postNode.getChildRef(), ContentModel.PROP_CONTENT, true); + + // Blog posts are always HTML (based on the JavaScript this class replaces.) + writer.setMimetype(MimetypeMap.MIMETYPE_HTML); + writer.setEncoding("UTF-8"); + writer.putContent(blogContent); + + if (isDraft) + { + // Comment from the old JavaScript: + // disable permission inheritance. The result is that only the creator will have access to the draft + permissionService.setInheritParentPermissions(postNode.getChildRef(), false); + } + else + { + setOrUpdateReleasedAndUpdatedDates(postNode.getChildRef()); + } + + return postNode; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.blog.BlogService#getMyDrafts(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.query.PagingRequest) + */ + @Override + public PagingResults getDrafts(NodeRef blogContainerNode, String username, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + // get canned query + pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); + GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)draftPostsCannedQueryFactory.getGetDraftsCannedQuery(blogContainerNode, username, pagingReq); + + // execute canned query + CannedQueryResults results = cq.execute(); + + return createPagedResults(pagingReq, results); + } + + /** + * This method creates a paged result set based on the supplied {@link PagingRequest} and {@link CannedQueryResults}. + */ + private PagingResults createPagedResults(PagingRequest pagingReq, CannedQueryResults results) + { + List nodeRefs = null; + if (results.getPageCount() > 0) + { + nodeRefs = results.getPages().get(0); + } + else + { + nodeRefs = Collections.emptyList(); + } + + // set total count + Pair totalCount = null; + if (pagingReq.getRequestTotalCountMax() > 0) + { + totalCount = results.getTotalResultCount(); + } + + boolean hasMoreItems = results.hasMoreItems(); + + return new PagingBlogPostInfoResultsImpl(nodeRefs, hasMoreItems, totalCount, results.getQueryExecutionId(), true); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.blog.BlogService#getPublishedExternally(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.query.PagingRequest) + */ + @Override + public PagingResults getPublishedExternally(NodeRef blogContainerNode, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + // get canned query + pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); + GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)publishedExternallyPostsCannedQueryFactory.getGetPublishedExternallyCannedQuery(blogContainerNode, pagingReq); + + // execute canned query + CannedQueryResults results = cq.execute(); + + return createPagedResults(pagingReq, results); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.blog.BlogService#getPublished(org.alfresco.service.cmr.repository.NodeRef, java.util.Date, java.util.Date, java.lang.String, org.alfresco.query.PagingRequest) + */ + @Override + public PagingResults getPublished(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + // get canned query + pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); + GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)publishedPostsCannedQueryFactory.getGetPublishedCannedQuery(blogContainerNode, fromDate, toDate, byUser, pagingReq); + + // execute canned query + CannedQueryResults results = cq.execute(); + + return createPagedResults(pagingReq, results); + } + + /** + * @deprecated + */ + @Override + public PagingResults getMyDraftsAndAllPublished(NodeRef blogContainerNode, Date createdFrom, Date createdTo, String tag, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + // get canned query + pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + DraftsAndPublishedBlogPostsCannedQuery cq = (DraftsAndPublishedBlogPostsCannedQuery)draftsAndPublishedBlogPostsCannedQueryFactory.getCannedQuery(blogContainerNode, createdFrom, createdTo, currentUser, tag, pagingReq); + + // execute canned query + CannedQueryResults results = cq.execute(); + + return createPagedResults(pagingReq, results); + } + + + private String getUniqueChildName(NodeRef parentNode, String prefix) + { + return prefix + "-" + System.currentTimeMillis(); + } + + + /** + * This method is taken from the previous JavaScript webscript controllers. + * @param blogPostNode + */ + private void setOrUpdateReleasedAndUpdatedDates(NodeRef blogPostNode) + { + // make sure the syndication aspect has been added + if (!nodeService.hasAspect(blogPostNode, ContentModel.ASPECT_SYNDICATION)) + { + nodeService.addAspect(blogPostNode, ContentModel.ASPECT_SYNDICATION, null); + } + + // (re-)enable permission inheritance which got disable for draft posts + // only set if was previously draft - as only the owner/admin can do this + if (!permissionService.getInheritParentPermissions(blogPostNode)) + { + permissionService.setInheritParentPermissions(blogPostNode, true); + } + + // check whether the published date has been set + if (nodeService.getProperty(blogPostNode, ContentModel.PROP_PUBLISHED) == null) + { + nodeService.setProperty(blogPostNode, ContentModel.PROP_PUBLISHED, new Date()); + } + else + { + // set/update the updated date + nodeService.setProperty(blogPostNode, ContentModel.PROP_UPDATED, new Date()); + } + } +} diff --git a/source/java/org/alfresco/repo/blog/BlogServiceImplTest.java b/source/java/org/alfresco/repo/blog/BlogServiceImplTest.java new file mode 100644 index 0000000000..ec2eec2c52 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/BlogServiceImplTest.java @@ -0,0 +1,540 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +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.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.cmr.tagging.TaggingService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyMap; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.ApplicationContext; + +/** + * Test cases for {@link BlogServiceImpl}. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class BlogServiceImplTest +{ + + private static final ApplicationContext testContext = ApplicationContextHelper.getApplicationContext(); + + // injected services + private static MutableAuthenticationService AUTHENTICATION_SERVICE; + private static BehaviourFilter BEHAVIOUR_FILTER; + private static BlogService BLOG_SERVICE; + private static DictionaryService DICTIONARY_SERVICE; + private static NodeService NODE_SERVICE; + private static PersonService PERSON_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + private static SiteService SITE_SERVICE; + private static TaggingService TAGGING_SERVICE; + + private static final String TEST_USER = BlogServiceImplTest.class.getSimpleName() + "_testuser"; + private static final String ADMIN_USER = AuthenticationUtil.getAdminUserName(); + + + /** + * Temporary test nodes (created during a test method) that need deletion after the test method. + */ + private List testNodesToTidy = new ArrayList(); + /** + * Temporary test nodes (created BeforeClass) that need deletion after this test class. + */ + private static List CLASS_TEST_NODES_TO_TIDY = new ArrayList(); + + private static NodeRef BLOG_CONTAINER_NODE; + + @BeforeClass public static void initTestsContext() throws Exception + { + AUTHENTICATION_SERVICE = (MutableAuthenticationService)testContext.getBean("authenticationService"); + BEHAVIOUR_FILTER = (BehaviourFilter)testContext.getBean("policyBehaviourFilter"); + BLOG_SERVICE = (BlogService)testContext.getBean("blogService"); + DICTIONARY_SERVICE = (DictionaryService)testContext.getBean("dictionaryService"); + NODE_SERVICE = (NodeService)testContext.getBean("nodeService"); + PERSON_SERVICE = (PersonService)testContext.getBean("personService"); + TRANSACTION_HELPER = (RetryingTransactionHelper)testContext.getBean("retryingTransactionHelper"); + SITE_SERVICE = (SiteService)testContext.getBean("siteService"); + TAGGING_SERVICE = (TaggingService)testContext.getBean("TaggingService"); + + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + createUser(TEST_USER); + + // We need to create the test site as the test user so that they can contribute content to it in tests below. + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + createTestSiteWithBlogContainer(); + } + + private static void createTestSiteWithBlogContainer() throws Exception + { + BLOG_CONTAINER_NODE = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public NodeRef execute() throws Throwable + { + SiteInfo site = SITE_SERVICE.createSite("BlogSitePreset", BlogServiceImplTest.class.getSimpleName() + "_testSite" + System.currentTimeMillis(), + "test site title", "test site description", SiteVisibility.PUBLIC); + CLASS_TEST_NODES_TO_TIDY.add(site.getNodeRef()); + + NodeRef result = SITE_SERVICE.getContainer(site.getShortName(), "blog"); + + if (result == null) + { + result = NODE_SERVICE.createNode(site.getNodeRef(), ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "blog"), ContentModel.TYPE_FOLDER, null).getChildRef(); + CLASS_TEST_NODES_TO_TIDY.add(result); + } + + return result; + } + }); + } + + /** + * By default, all tests are run as the admin user. + */ + @Before public void setAdminUser() + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + } + + @After public void deleteTestNodes() throws Exception + { + performDeletionOfNodes(testNodesToTidy); + } + + @AfterClass public static void deleteClassTestNodesAndUsers() throws Exception + { + performDeletionOfNodes(CLASS_TEST_NODES_TO_TIDY); + deleteUser(TEST_USER); + } + + /** + * Deletes the specified NodeRefs, if they exist. + * @param nodesToDelete + */ + private static void performDeletionOfNodes(final List nodesToDelete) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + + for (NodeRef node : nodesToDelete) + { + if (NODE_SERVICE.exists(node)) NODE_SERVICE.deleteNode(node); + } + + return null; + } + }); + } + + @Test public void createDraftBlogPostsAndGetPagedResults() throws Exception + { + final int arbitraryNumberGreaterThanPageSize = 42; + final List submittedBlogPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() + { + @Override + public List execute() throws Throwable + { + List results = new ArrayList(); + + for (int i = 0; i < arbitraryNumberGreaterThanPageSize; i++) + { + ChildAssociationRef newBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "title_" + i, "Hello world", true); + + results.add(newBlogPost.getChildRef()); + testNodesToTidy.add(newBlogPost.getChildRef()); + } + + return results; + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + List recoveredBlogPosts = new ArrayList(arbitraryNumberGreaterThanPageSize); + + final int pageSize = 10; + PagingRequest pagingReq = new PagingRequest(0, pageSize, null); + + PagingResults pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); + assertEquals("Wrong total result count.", arbitraryNumberGreaterThanPageSize, (int)pagedResults.getTotalResultCount().getFirst()); + + while (pagedResults.hasMoreItems()) + { + recoveredBlogPosts.addAll(pagedResults.getPage()); + pagingReq = new PagingRequest(pagingReq.getSkipCount() + pageSize, pageSize, null); + pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); + } + // and the last page, which only has 2 items in it. + recoveredBlogPosts.addAll(pagedResults.getPage()); + + assertEquals("Wrong number of blog posts.", submittedBlogPosts.size(), recoveredBlogPosts.size()); + + // Check the list is sorted by cm:created, descending order. + assertNodeRefsAreSortedBy(recoveredBlogPosts, ContentModel.PROP_CREATED, false); + + return null; + } + }); + } + + /** + * This method asserts that the given List has NodeRefs in order of the specified date property. + * + * @param blogPosts + * @param property a Date property + * @param ascendingOrder true if ascending order, false for descending. + */ + private void assertNodeRefsAreSortedBy(List blogPosts, QName property, boolean ascendingOrder) + { + final PropertyDefinition propertyDef = DICTIONARY_SERVICE.getProperty(property); + assertNotNull("Property not recognised.", propertyDef); + assertEquals("Property was not a Date", DataTypeDefinition.DATETIME, propertyDef.getDataType().getName()); + + if (blogPosts.size() > 1) + { + for (int i = 0; i < blogPosts.size() - 1; i++) + { + NodeRef nodeRef1 = blogPosts.get(i).getNodeRef(); + NodeRef nodeRef2 = blogPosts.get(i + 1).getNodeRef(); + Date date1 = (Date) NODE_SERVICE.getProperty(nodeRef1, property); + Date date2 = (Date) NODE_SERVICE.getProperty(nodeRef2, property); + + if (ascendingOrder) + { + assertTrue("BlogPosts not asc-sorted by " + property + ". Error at index " + i, date1.before(date2)); + } + else + { + assertTrue("BlogPosts not desc-sorted by " + property + ". Error at index " + i, date1.after(date2)); + } + } + } + } + + @Test public void createTaggedDraftBlogPost() throws Exception + { + final List tags = Arrays.asList(new String[]{"foo", "bar"}); + + final NodeRef blogPost = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + + @Override + public NodeRef execute() throws Throwable + { + ChildAssociationRef newBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "draftWithTag", "Hello world", true); + TAGGING_SERVICE.addTags(newBlogPost.getChildRef(), tags); + testNodesToTidy.add(newBlogPost.getChildRef()); + + return newBlogPost.getChildRef(); + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + PagingRequest pagingReq = new PagingRequest(0, 10, null); + + PagingResults pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); + assertEquals("Expected one blog post", 1, pagedResults.getPage().size()); + + NodeRef blogNode = pagedResults.getPage().get(0).getNodeRef(); + assertEquals("Incorrect NodeRef.", blogNode, blogPost); + + List recoveredTags = TAGGING_SERVICE.getTags(blogNode); + assertEquals("Incorrect tags.", tags, recoveredTags); + + return null; + } + }); + } + + /** + * This test uses two different users to create draft and internally published blog posts. + * Then it ensures that each user sees the correct posts when they retrieve them from the service. + */ + @Test public void multipleUsersCreateDraftsAndPublishedPostsAndBrowse() throws Exception + { + // Admin creates a draft and an internally-published blog post. + final Pair adminPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() + { + + @Override + public Pair execute() throws Throwable + { + ChildAssociationRef newDraftBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "adminDraft", "", true); + testNodesToTidy.add(newDraftBlogPost.getChildRef()); + + ChildAssociationRef newPublishedBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "adminPublished", "", false); + testNodesToTidy.add(newPublishedBlogPost.getChildRef()); + + return new Pair(newDraftBlogPost.getChildRef(), newPublishedBlogPost.getChildRef()); + } + }); + + // Then another user does the same. + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + final Pair userPosts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() + { + + @Override + public Pair execute() throws Throwable + { + ChildAssociationRef newDraftBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "userDraft", "", true); + testNodesToTidy.add(newDraftBlogPost.getChildRef()); + + ChildAssociationRef newPublishedBlogPost = BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "userPublished", "", false); + testNodesToTidy.add(newPublishedBlogPost.getChildRef()); + + return new Pair(newDraftBlogPost.getChildRef(), newPublishedBlogPost.getChildRef()); + } + }); + + // Now check what we see from the service. + AuthenticationUtil.setFullyAuthenticatedUser(ADMIN_USER); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + PagingRequest pagingReq = new PagingRequest(0, 10, null); + + PagingResults pagedDraftResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); + assertEquals("Wrong number of admin draft blog posts", 1, pagedDraftResults.getPage().size()); + NodeRef blogNode = pagedDraftResults.getPage().get(0).getNodeRef(); + assertEquals("Incorrect admin draft NodeRef.", blogNode, adminPosts.getFirst()); + + PagingResults pagedPublishedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, null, null, ADMIN_USER, pagingReq); + assertEquals("Wrong number of admin published blog posts", 1, pagedPublishedResults.getPage().size()); + blogNode = pagedPublishedResults.getPage().get(0).getNodeRef(); + assertEquals("Incorrect admin published NodeRef.", blogNode, adminPosts.getSecond()); + + return null; + } + }); + + + AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + PagingRequest pagingReq = new PagingRequest(0, 10, null); + + PagingResults pagedDraftResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, TEST_USER, pagingReq); + assertEquals("Wrong number of user draft blog posts", 1, pagedDraftResults.getPage().size()); + NodeRef blogNode = pagedDraftResults.getPage().get(0).getNodeRef(); + assertEquals("Incorrect user draft NodeRef.", blogNode, userPosts.getFirst()); + + PagingResults pagedPublishedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, null, null, TEST_USER, pagingReq); + assertEquals("Wrong number of user published blog posts", 1, pagedPublishedResults.getPage().size()); + blogNode = pagedPublishedResults.getPage().get(0).getNodeRef(); + assertEquals("Incorrect user published NodeRef.", blogNode, userPosts.getSecond()); + + return null; + } + }); + } + + @Test public void getBlogPostsFilteredByDateRange() throws Exception + { + final int numberOfPosts = 31 + 31 + 29; + + final List posts = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback>() + { + @Override + public List execute() throws Throwable + { + List results = new ArrayList(); + + for (int i = 0; i < numberOfPosts; i++) + { + ChildAssociationRef newBlogPost = + BLOG_SERVICE.createBlogPost(BLOG_CONTAINER_NODE, "date-specific-post" + i, "", false); + testNodesToTidy.add(newBlogPost.getChildRef()); + + results.add(newBlogPost.getChildRef()); + } + + return results; + } + }); + + // Now go through and set their creation dates to specific points in the past. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // FROM 1st December 1999 + final Calendar current = Calendar.getInstance(); + current.set(1999, 11, 1, 11, 0); + + // should give us: + // 31 posts in december 99 + // 31 posts in january 00 + // 29 posts in february 00 + + Date currentDate = current.getTime(); + for (NodeRef nr : posts) + { + // We'll permanently turn off auditing on this node. + // This should allow us to set the cm:created date without auditing overwriting our value. + // These nodes get deleted after the test anyway. + BEHAVIOUR_FILTER.disableBehaviour(nr, ContentModel.ASPECT_AUDITABLE); + + // Yes, cm:published will be before cm:created. But I don't think that matter. + NODE_SERVICE.setProperty(nr, ContentModel.PROP_PUBLISHED, currentDate); + + current.add(Calendar.DATE, 1); + currentDate = current.getTime(); + + } + + return null; + } + }); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + PagingRequest pagingReq = new PagingRequest(0, 100, null); + + Calendar cal = Calendar.getInstance(); + cal.set(1999, 11, 1, 0, 0, 0); + Date firstDec99 = cal.getTime(); + + cal.set(2000, 0, 1, 0, 0, 0); + Date firstJan00 = cal.getTime(); + + cal.set(2000, 1, 1, 0, 0, 0); + Date firstFeb00 = cal.getTime(); + + cal.set(2000, 2, 1, 0, 0, 0); + Date firstMar00 = cal.getTime(); + + PagingResults pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstDec99, firstJan00, null, pagingReq); + assertEquals("Wrong number of user blog posts", 31, pagedResults.getPage().size()); + + pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstFeb00, firstMar00, null, pagingReq); + assertEquals("Wrong number of user blog posts", 29, pagedResults.getPage().size()); + + pagedResults = BLOG_SERVICE.getPublished(BLOG_CONTAINER_NODE, firstJan00, firstMar00, null, pagingReq); + assertEquals("Wrong number of user blog posts", 31 + 29, pagedResults.getPage().size()); + + return null; + } + }); + } + + private static void createUser(final String userName) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (!AUTHENTICATION_SERVICE.authenticationExists(userName)) + { + AUTHENTICATION_SERVICE.createAuthentication(userName, "PWD".toCharArray()); + } + + if (!PERSON_SERVICE.personExists(userName)) + { + PropertyMap ppOne = new PropertyMap(); + ppOne.put(ContentModel.PROP_USERNAME, userName); + ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName"); + ppOne.put(ContentModel.PROP_LASTNAME, "lastName"); + ppOne.put(ContentModel.PROP_EMAIL, "email@email.com"); + ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle"); + + PERSON_SERVICE.createPerson(ppOne); + } + + return null; + } + }); + } + + private static void deleteUser(final String userName) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if (PERSON_SERVICE.personExists(userName)) + { + PERSON_SERVICE.deletePerson(userName); + } + + return null; + } + }); + } +} diff --git a/source/java/org/alfresco/repo/blog/DraftAndPublishedBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/DraftAndPublishedBlogPostsCannedQueryParams.java new file mode 100644 index 0000000000..10e2d4bc39 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/DraftAndPublishedBlogPostsCannedQueryParams.java @@ -0,0 +1,76 @@ +/* + * 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; + +import java.util.Date; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Parameters for {@link DraftsAndPublishedBlogPostsCannedQuery}. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class DraftAndPublishedBlogPostsCannedQueryParams +{ + private final NodeRef blogContainerNode; + private final String cmCreator; + private final Date createdFromDate; + private final Date createdToDate; + private final String tag; + + public DraftAndPublishedBlogPostsCannedQueryParams(NodeRef blogContainerNodeRef, + String cmCreator, + Date createdFromDate, + Date createdToDate, + String tag) + { + this.blogContainerNode = blogContainerNodeRef; + this.cmCreator = cmCreator; + this.createdFromDate = createdFromDate; + this.createdToDate = createdToDate; + this.tag = tag; + } + + public NodeRef getBlogContainerNode() + { + return blogContainerNode; + } + + public String getCmCreator() + { + return cmCreator; + } + + public Date getCreatedFromDate() + { + return createdFromDate; + } + + public Date getCreatedToDate() + { + return createdToDate; + } + + public String getTag() + { + return tag; + } +} diff --git a/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQuery.java new file mode 100644 index 0000000000..39e48a49b0 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQuery.java @@ -0,0 +1,173 @@ +/* + * 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; + +import java.lang.reflect.Method; +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.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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.tagging.TaggingService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * This is a {@link CannedQuery} for the rather particular 'get my drafts and all published' blog-post query. + * + * @author Neil Mc Erlean + * @since 4.0 + * + * @see BlogService#getMyDraftsAndAllPublished(NodeRef, Date, Date, String, org.alfresco.query.PagingRequest) + */ +public class DraftsAndPublishedBlogPostsCannedQuery extends AbstractCannedQueryPermissions +{ + private final NodeService rawNodeService; + private final TaggingService taggingService; + + public DraftsAndPublishedBlogPostsCannedQuery( + NodeService rawNodeService, + TaggingService taggingService, + MethodSecurityInterceptor methodSecurityInterceptor, + Method method, + CannedQueryParameters params, + String queryExecutionId) + { + super(params, queryExecutionId, methodSecurityInterceptor, method); + this.rawNodeService = rawNodeService; + this.taggingService = taggingService; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetBlogPosts query params"); + + DraftAndPublishedBlogPostsCannedQueryParams paramBean = (DraftAndPublishedBlogPostsCannedQueryParams) paramBeanObj; + + String requestedTag = paramBean.getTag(); + Date createdFromDate = paramBean.getCreatedFromDate(); + Date createdToDate = paramBean.getCreatedToDate(); + + List childAssocs = getAllBlogNodes(paramBean.getBlogContainerNode()); + + List filteredNodeRefs = new ArrayList(); + for (ChildAssociationRef chAssRef : childAssocs) + { + NodeRef nextBlogNode = chAssRef.getChildRef(); + + // Is this next node in the list to be included in the results? + boolean nextNodeIsAcceptable = true; + + // Return all published Blog Posts + if (rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != 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())) + { + 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 && actualCreatedDate.before(createdFromDate)) + { + nextNodeIsAcceptable = false; + } + if (createdToDate != null && actualCreatedDate.after(createdToDate)) + { + nextNodeIsAcceptable = false; + } + } + + // Only return blog posts tagged with the specified tag string. + if (requestedTag != null && !taggingService.getTags(nextBlogNode).contains(requestedTag)) + { + nextNodeIsAcceptable = false; + } + + + if (nextNodeIsAcceptable) + { + filteredNodeRefs.add(new BlogPostInfoImpl(nextBlogNode, rawNodeService.getProperties(nextBlogNode))); + } + } + + List> sortPairs = parameters.getSortDetails().getSortPairs(); + // For now, the BlogService only sorts by a single property. + if (sortPairs != null && !sortPairs.isEmpty()) + { + Pair sortPair = sortPairs.get(0); + + QName sortProperty = (QName) sortPair.getFirst(); + final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); + + if (sortPair.getSecond() == SortOrder.DESCENDING) + { + Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + } + } + + + return filteredNodeRefs; + } + + private List getAllBlogNodes(NodeRef containerNode) + { + final Set childNodeTypes = new HashSet(); + childNodeTypes.add(ContentModel.TYPE_CONTENT); + + // This will, of course, retrieve all the blog posts which may be a very long list. + List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); + return childAssocs; + } + + @Override + protected boolean isApplyPostQuerySorting() + { + // No post-query sorting. It's done within the queryAndFilter() method above. + return false; + } + + @Override + protected boolean isApplyPostQueryPermissions() + { + return true; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQueryFactory.java new file mode 100644 index 0000000000..1d218f108d --- /dev/null +++ b/source/java/org/alfresco/repo/blog/DraftsAndPublishedBlogPostsCannedQueryFactory.java @@ -0,0 +1,174 @@ +/* + * 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; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +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.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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. + * + * @since 4.0 + * @author Neil Mc Erlean. + */ +public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +{ + private MethodSecurityInterceptor methodSecurityInterceptor; + private String methodName; + private Object methodService; + private NodeService rawNodeService; + private TaggingService taggingService; + + public void setRawNodeService(NodeService nodeService) + { + this.rawNodeService = nodeService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + public void setMethodSecurityInterceptor(MethodSecurityInterceptor methodSecurityInterceptor) + { + this.methodSecurityInterceptor = methodSecurityInterceptor; + } + + public void setMethodName(String methodName) + { + this.methodName = methodName; + } + + public void setMethodService(Object methodService) + { + this.methodService = methodService; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + Method method = null; + for (Method m : methodService.getClass().getMethods()) + { + // note: currently matches first found + if (m.getName().equals(methodName)) + { + method = m; + break; + } + } + + if (method == null) + { + throw new AlfrescoRuntimeException("Method not found: "+methodName); + } + + // if not passed in (TODO or not in future cache) then generate a new query execution id + String queryExecutionId = (parameters.getQueryExecutionId() == null ? super.getQueryExecutionId(parameters) : parameters.getQueryExecutionId()); + + final DraftsAndPublishedBlogPostsCannedQuery cq = new DraftsAndPublishedBlogPostsCannedQuery(rawNodeService, taggingService, + methodSecurityInterceptor, method, parameters, queryExecutionId); + return (CannedQuery) cq; + } + + public CannedQuery getCannedQuery(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, String tag, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + //FIXME Need tenant service like for GetChildren? + DraftAndPublishedBlogPostsCannedQueryParams paramBean = new DraftAndPublishedBlogPostsCannedQueryParams(blogContainerNode, + byUser, + fromDate, toDate, tag); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_PUBLISHED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // 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, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT); + return cqpd; + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + PropertyCheck.mandatory(this, "methodSecurityInterceptor", methodSecurityInterceptor); + PropertyCheck.mandatory(this, "methodService", methodService); + PropertyCheck.mandatory(this, "methodName", methodName); + } +} diff --git a/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQuery.java new file mode 100644 index 0000000000..26cdbc7c95 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQuery.java @@ -0,0 +1,183 @@ +/* + * 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; + +import java.lang.reflect.Method; +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.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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.namespace.QName; +import org.alfresco.util.Pair; + +/** + * This class provides support for several {@link CannedQuery canned queries} used by the + * {@link BlogService}. + * + * @author Neil Mc Erlean + * @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; + + public GetBlogPostsCannedQuery( + NodeService rawNodeService, + MethodSecurityInterceptor methodSecurityInterceptor, + Method method, + CannedQueryParameters params, + String queryExecutionId) + { + super(params, queryExecutionId, methodSecurityInterceptor, method); + this.rawNodeService = rawNodeService; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetBlogPosts query params"); + + GetBlogPostsCannedQueryParams paramBean = (GetBlogPostsCannedQueryParams) paramBeanObj; + String requestedCreator = paramBean.getCmCreator(); + boolean isPublished = paramBean.getIsPublished(); + Date publishedFromDate = paramBean.getPublishedFromDate(); + Date publishedToDate = paramBean.getPublishedToDate(); + List 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()); + + List filteredNodeRefs = new ArrayList(); + for (ChildAssociationRef chAssRef : childAssocs) + { + // Is the nextBlogPostNode going to be included or not? + boolean nextNodeIsAcceptable = true; + + NodeRef nextBlogNode = chAssRef.getChildRef(); + + // Only return blog-posts whose cm:published status matches that requested. + final boolean nextBlogNodeIsPublished = rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != null; + if (nextBlogNodeIsPublished != 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)) + { + 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 && actualPublishedDate.before(publishedFromDate)) + { + nextNodeIsAcceptable = false; + } + if (publishedToDate != null && actualPublishedDate.after(publishedToDate)) + { + 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 BlogPostInfoImpl(nextBlogNode, rawNodeService.getProperties(nextBlogNode))); + } + } + + List> sortPairs = parameters.getSortDetails().getSortPairs(); + + // For now, the BlogService only sorts by a single property. + if (sortPairs != null && !sortPairs.isEmpty()) + { + Pair sortPair = sortPairs.get(0); + + QName sortProperty = (QName) sortPair.getFirst(); + final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); + + if (sortPair.getSecond() == SortOrder.DESCENDING) + { + Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + } + } + + return filteredNodeRefs; + } + + private List getAllBlogNodes(NodeRef containerNode) + { + final Set childNodeTypes = new HashSet(); + childNodeTypes.add(ContentModel.TYPE_CONTENT); + + // This will, of course, retrieve all the blog posts which may be a very long list. + List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); + return childAssocs; + } + + + @Override + protected boolean isApplyPostQuerySorting() + { + // No post-query sorting. It's done within the queryAndFilter() method above. + return false; + } + + @Override + protected boolean isApplyPostQueryPermissions() + { + return true; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryFactory.java new file mode 100644 index 0000000000..01b6839dac --- /dev/null +++ b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryFactory.java @@ -0,0 +1,223 @@ +/* + * 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; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +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.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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. + * @since 4.0 + * + * @see BlogService#getDrafts(NodeRef, String, PagingRequest) + * @see BlogService#getPublished(NodeRef, Date, Date, String, PagingRequest) + */ +public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +{ + private MethodSecurityInterceptor methodSecurityInterceptor; + private String methodName; + private Object methodService; + private NodeService rawNodeService; + + public void setRawNodeService(NodeService nodeService) + { + this.rawNodeService = nodeService; + } + + public void setMethodSecurityInterceptor(MethodSecurityInterceptor methodSecurityInterceptor) + { + this.methodSecurityInterceptor = methodSecurityInterceptor; + } + + public void setMethodName(String methodName) + { + this.methodName = methodName; + } + + public void setMethodService(Object methodService) + { + this.methodService = methodService; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + Method method = null; + for (Method m : methodService.getClass().getMethods()) + { + // note: currently matches first found + if (m.getName().equals(methodName)) + { + method = m; + break; + } + } + + if (method == null) + { + throw new AlfrescoRuntimeException("Method not found: "+methodName); + } + + String queryExecutionId = (parameters.getQueryExecutionId() == null ? super.getQueryExecutionId(parameters) : parameters.getQueryExecutionId()); + + final GetBlogPostsCannedQuery cq = new GetBlogPostsCannedQuery(rawNodeService, methodSecurityInterceptor, method, parameters, queryExecutionId); + return (CannedQuery) cq; + } + + public CannedQuery getGetDraftsCannedQuery(NodeRef blogContainerNode, String username, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + //FIXME Need tenant service like for GetChildren? + boolean isPublished = false; + List requiredAspects = null; + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + username, + isPublished, + null, null, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_CREATED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + public CannedQuery getGetPublishedExternallyCannedQuery(NodeRef blogContainerNode, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + boolean isPublished = true; + List requiredAspects = Arrays.asList(new QName[]{BlogIntegrationModel.ASPECT_BLOG_POST}); + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + null, + isPublished, + null, null, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(BlogIntegrationModel.PROP_POSTED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + public CannedQuery getGetPublishedCannedQuery(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + boolean isPublished = true; + List requiredAspects = null; + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + byUser, + isPublished, + fromDate, toDate, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_PUBLISHED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // 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, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT); + return cqpd; + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + PropertyCheck.mandatory(this, "methodSecurityInterceptor", methodSecurityInterceptor); + PropertyCheck.mandatory(this, "methodService", methodService); + PropertyCheck.mandatory(this, "methodName", methodName); + } +} diff --git a/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryParams.java new file mode 100644 index 0000000000..9f2edcfcea --- /dev/null +++ b/source/java/org/alfresco/repo/blog/GetBlogPostsCannedQueryParams.java @@ -0,0 +1,94 @@ +/* + * 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; + +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}. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class GetBlogPostsCannedQueryParams +{ + 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; + + public GetBlogPostsCannedQueryParams(NodeRef blogContainerNodeRef, + String cmCreator, + boolean isPublished, + Date publishedFromDate, + Date publishedToDate, + List requiredAspects) + { + this.blogContainerNode = blogContainerNodeRef; + 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; + } + + public String getCmCreator() + { + return cmCreator; + } + + public boolean getIsPublished() + { + return this.isPublished; + } + + public Date getPublishedFromDate() + { + return publishedFromDate; + } + + public Date getPublishedToDate() + { + return publishedToDate; + } + + public List getRequiredAspects() + { + return Collections.unmodifiableList(this.requiredAspects); + } +} diff --git a/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResults.java b/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResults.java new file mode 100644 index 0000000000..c71a25fba4 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResults.java @@ -0,0 +1,28 @@ +/* + * 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; + +import org.alfresco.query.PagingResults; + +/** + * @since 4.0 + */ +public interface PagingBlogPostInfoResults extends PagingResults +{ +} diff --git a/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResultsImpl.java b/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResultsImpl.java new file mode 100644 index 0000000000..1616e99cf3 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/PagingBlogPostInfoResultsImpl.java @@ -0,0 +1,80 @@ +/* + * 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; + +import java.util.List; + +import org.alfresco.query.PermissionedResults; +import org.alfresco.util.Pair; + +/** + * A simple results holder object for data relating to {@link BlogPostInfo blog-posts}, as + * returned by the {@link BlogService} + * + * @since 4.0 + * @author Neil Mc Erlean. + */ +class PagingBlogPostInfoResultsImpl implements PagingBlogPostInfoResults, PermissionedResults +{ + private List blogPosts; + + private boolean hasMoreItems; + private Pair totalResultCount; + private String queryExecutionId; + private boolean permissionsApplied; + + public PagingBlogPostInfoResultsImpl(List nodeInfos, boolean hasMoreItems, Pair totalResultCount, String queryExecutionId, boolean permissionsApplied) + { + this.blogPosts = nodeInfos; + this.hasMoreItems = hasMoreItems; + this.totalResultCount = totalResultCount; + this.queryExecutionId = queryExecutionId; + this.permissionsApplied = permissionsApplied; + } + + @Override + public List getPage() + { + return blogPosts; + } + + @Override + public boolean hasMoreItems() + { + return hasMoreItems; + } + + @Override + public Pair getTotalResultCount() + { + return totalResultCount; + } + + @Override + public String getQueryExecutionId() + { + return queryExecutionId; + } + + @Override + public boolean permissionsApplied() + { + return permissionsApplied; + } +} diff --git a/source/java/org/alfresco/repo/blog/PropertyBasedComparator.java b/source/java/org/alfresco/repo/blog/PropertyBasedComparator.java new file mode 100644 index 0000000000..7908a45731 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/PropertyBasedComparator.java @@ -0,0 +1,70 @@ +/* + * 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; + +import java.util.Comparator; + +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 diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/BlogPostInfoImpl.java b/source/java/org/alfresco/repo/blog/cannedqueries/BlogPostInfoImpl.java new file mode 100644 index 0000000000..4122da6a0d --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/BlogPostInfoImpl.java @@ -0,0 +1,102 @@ +/* + * 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.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.blog.BlogPostInfo; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * A simple data object responsible for holding information relevant to blog post NodeRefs. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class BlogPostInfoImpl implements BlogPostInfo +{ + private NodeRef nodeRef; + private Map properties = new HashMap(); + + BlogPostInfoImpl(NodeRef nodeRef, Map properties) + { + this.nodeRef = nodeRef; + this.properties = properties; + } + + /** + * @see #getNodeRef() + * @see NodeRef#equals(Object) + */ + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + else if (this == obj) + { + return true; + } + else if (obj instanceof BlogPostInfoImpl == false) + { + return false; + } + BlogPostInfoImpl that = (BlogPostInfoImpl) obj; + return (this.getNodeRef().equals(that.getNodeRef())); + } + + /** + * @see #getNodeRef() + * @see NodeRef#hashCode() + */ + @Override + public int hashCode() + { + return getNodeRef().hashCode(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(80); + sb.append(BlogPostInfo.class.getSimpleName()) + .append("[name=").append(getName()) + .append(", nodeRef=").append(nodeRef); + sb.append("]"); + return sb.toString(); + } + + @Override + public NodeRef getNodeRef() + { + return nodeRef; + } + + @Override + public String getName() + { + return (String) properties.get(ContentModel.PROP_NAME); + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java new file mode 100644 index 0000000000..dd4443a044 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQuery.java @@ -0,0 +1,175 @@ +/* + * 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.lang.reflect.Method; +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.BlogPostInfo; +import org.alfresco.repo.blog.BlogService; +import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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.tagging.TaggingService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * This is a {@link CannedQuery} for the rather particular 'get my drafts and all published' blog-post query. + * + * @author Neil Mc Erlean + * @since 4.0 + * + * @see BlogService#getMyDraftsAndAllPublished(NodeRef, Date, Date, String, org.alfresco.query.PagingRequest) + */ +public class DraftsAndPublishedBlogPostsCannedQuery extends AbstractCannedQueryPermissions +{ + private final NodeService rawNodeService; + private final TaggingService taggingService; + + public DraftsAndPublishedBlogPostsCannedQuery( + NodeService rawNodeService, + TaggingService taggingService, + MethodSecurityInterceptor methodSecurityInterceptor, + Method method, + CannedQueryParameters params, + String queryExecutionId) + { + super(params, queryExecutionId, methodSecurityInterceptor, method); + this.rawNodeService = rawNodeService; + this.taggingService = taggingService; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetBlogPosts query params"); + + DraftsAndPublishedBlogPostsCannedQueryParams paramBean = (DraftsAndPublishedBlogPostsCannedQueryParams) paramBeanObj; + + String requestedTag = paramBean.getTag(); + Date createdFromDate = paramBean.getCreatedFromDate(); + Date createdToDate = paramBean.getCreatedToDate(); + + List childAssocs = getAllBlogNodes(paramBean.getBlogContainerNode()); + + List filteredNodeRefs = new ArrayList(); + for (ChildAssociationRef chAssRef : childAssocs) + { + NodeRef nextBlogNode = chAssRef.getChildRef(); + + // Is this next node in the list to be included in the results? + boolean nextNodeIsAcceptable = true; + + // Return all published Blog Posts + if (rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != 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())) + { + 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 && actualCreatedDate.before(createdFromDate)) + { + nextNodeIsAcceptable = false; + } + if (createdToDate != null && actualCreatedDate.after(createdToDate)) + { + nextNodeIsAcceptable = false; + } + } + + // Only return blog posts tagged with the specified tag string. + if (requestedTag != null && !taggingService.getTags(nextBlogNode).contains(requestedTag)) + { + nextNodeIsAcceptable = false; + } + + + if (nextNodeIsAcceptable) + { + filteredNodeRefs.add(new BlogPostInfoImpl(nextBlogNode, rawNodeService.getProperties(nextBlogNode))); + } + } + + List> sortPairs = parameters.getSortDetails().getSortPairs(); + // For now, the BlogService only sorts by a single property. + if (sortPairs != null && !sortPairs.isEmpty()) + { + Pair sortPair = sortPairs.get(0); + + QName sortProperty = (QName) sortPair.getFirst(); + final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); + + if (sortPair.getSecond() == SortOrder.DESCENDING) + { + Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + } + } + + + return filteredNodeRefs; + } + + private List getAllBlogNodes(NodeRef containerNode) + { + final Set childNodeTypes = new HashSet(); + childNodeTypes.add(ContentModel.TYPE_CONTENT); + + // This will, of course, retrieve all the blog posts which may be a very long list. + List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); + return childAssocs; + } + + @Override + protected boolean isApplyPostQuerySorting() + { + // No post-query sorting. It's done within the queryAndFilter() method above. + return false; + } + + @Override + protected boolean isApplyPostQueryPermissions() + { + return true; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java new file mode 100644 index 0000000000..bb657fa51f --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryFactory.java @@ -0,0 +1,175 @@ +/* + * 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.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +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.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.blog.BlogPostInfo; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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. + * + * @since 4.0 + * @author Neil Mc Erlean. + */ +public class DraftsAndPublishedBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +{ + private MethodSecurityInterceptor methodSecurityInterceptor; + private String methodName; + private Object methodService; + private NodeService rawNodeService; + private TaggingService taggingService; + + public void setRawNodeService(NodeService nodeService) + { + this.rawNodeService = nodeService; + } + + public void setTaggingService(TaggingService taggingService) + { + this.taggingService = taggingService; + } + + public void setMethodSecurityInterceptor(MethodSecurityInterceptor methodSecurityInterceptor) + { + this.methodSecurityInterceptor = methodSecurityInterceptor; + } + + public void setMethodName(String methodName) + { + this.methodName = methodName; + } + + public void setMethodService(Object methodService) + { + this.methodService = methodService; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + Method method = null; + for (Method m : methodService.getClass().getMethods()) + { + // note: currently matches first found + if (m.getName().equals(methodName)) + { + method = m; + break; + } + } + + if (method == null) + { + throw new AlfrescoRuntimeException("Method not found: "+methodName); + } + + // if not passed in (TODO or not in future cache) then generate a new query execution id + String queryExecutionId = (parameters.getQueryExecutionId() == null ? super.getQueryExecutionId(parameters) : parameters.getQueryExecutionId()); + + final DraftsAndPublishedBlogPostsCannedQuery cq = new DraftsAndPublishedBlogPostsCannedQuery(rawNodeService, taggingService, + methodSecurityInterceptor, method, parameters, queryExecutionId); + return (CannedQuery) cq; + } + + public CannedQuery getCannedQuery(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, String tag, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + //FIXME Need tenant service like for GetChildren? + DraftsAndPublishedBlogPostsCannedQueryParams paramBean = new DraftsAndPublishedBlogPostsCannedQueryParams(blogContainerNode, + byUser, + fromDate, toDate, tag); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_PUBLISHED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // 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, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT); + return cqpd; + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + PropertyCheck.mandatory(this, "methodSecurityInterceptor", methodSecurityInterceptor); + PropertyCheck.mandatory(this, "methodService", methodService); + PropertyCheck.mandatory(this, "methodName", methodName); + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java new file mode 100644 index 0000000000..1777a70b6b --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/DraftsAndPublishedBlogPostsCannedQueryParams.java @@ -0,0 +1,76 @@ +/* + * 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.Date; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Parameters for {@link DraftsAndPublishedBlogPostsCannedQuery}. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class DraftsAndPublishedBlogPostsCannedQueryParams +{ + 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) + { + this.blogContainerNode = blogContainerNodeRef; + this.cmCreator = cmCreator; + this.createdFromDate = createdFromDate; + this.createdToDate = createdToDate; + this.tag = tag; + } + + public NodeRef getBlogContainerNode() + { + return blogContainerNode; + } + + public String getCmCreator() + { + return cmCreator; + } + + public Date getCreatedFromDate() + { + return createdFromDate; + } + + public Date getCreatedToDate() + { + return createdToDate; + } + + public String getTag() + { + return tag; + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java new file mode 100644 index 0000000000..203f23b06f --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQuery.java @@ -0,0 +1,185 @@ +/* + * 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.lang.reflect.Method; +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.BlogPostInfo; +import org.alfresco.repo.blog.BlogService; +import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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.namespace.QName; +import org.alfresco.util.Pair; + +/** + * This class provides support for several {@link CannedQuery canned queries} used by the + * {@link BlogService}. + * + * @author Neil Mc Erlean + * @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; + + public GetBlogPostsCannedQuery( + NodeService rawNodeService, + MethodSecurityInterceptor methodSecurityInterceptor, + Method method, + CannedQueryParameters params, + String queryExecutionId) + { + super(params, queryExecutionId, methodSecurityInterceptor, method); + this.rawNodeService = rawNodeService; + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetBlogPosts query params"); + + GetBlogPostsCannedQueryParams paramBean = (GetBlogPostsCannedQueryParams) paramBeanObj; + String requestedCreator = paramBean.getCmCreator(); + boolean isPublished = paramBean.getIsPublished(); + Date publishedFromDate = paramBean.getPublishedFromDate(); + Date publishedToDate = paramBean.getPublishedToDate(); + List 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()); + + List filteredNodeRefs = new ArrayList(); + for (ChildAssociationRef chAssRef : childAssocs) + { + // Is the nextBlogPostNode going to be included or not? + boolean nextNodeIsAcceptable = true; + + NodeRef nextBlogNode = chAssRef.getChildRef(); + + // Only return blog-posts whose cm:published status matches that requested. + final boolean nextBlogNodeIsPublished = rawNodeService.getProperty(nextBlogNode, ContentModel.PROP_PUBLISHED) != null; + if (nextBlogNodeIsPublished != 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)) + { + 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 && actualPublishedDate.before(publishedFromDate)) + { + nextNodeIsAcceptable = false; + } + if (publishedToDate != null && actualPublishedDate.after(publishedToDate)) + { + 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 BlogPostInfoImpl(nextBlogNode, rawNodeService.getProperties(nextBlogNode))); + } + } + + List> sortPairs = parameters.getSortDetails().getSortPairs(); + + // For now, the BlogService only sorts by a single property. + if (sortPairs != null && !sortPairs.isEmpty()) + { + Pair sortPair = sortPairs.get(0); + + QName sortProperty = (QName) sortPair.getFirst(); + final PropertyBasedComparator createdDateComparator = new PropertyBasedComparator(sortProperty, rawNodeService); + + if (sortPair.getSecond() == SortOrder.DESCENDING) + { + Collections.sort(filteredNodeRefs, Collections.reverseOrder(createdDateComparator)); + } + } + + return filteredNodeRefs; + } + + private List getAllBlogNodes(NodeRef containerNode) + { + final Set childNodeTypes = new HashSet(); + childNodeTypes.add(ContentModel.TYPE_CONTENT); + + // This will, of course, retrieve all the blog posts which may be a very long list. + List childAssocs = rawNodeService.getChildAssocs(containerNode, childNodeTypes); + return childAssocs; + } + + + @Override + protected boolean isApplyPostQuerySorting() + { + // No post-query sorting. It's done within the queryAndFilter() method above. + return false; + } + + @Override + protected boolean isApplyPostQueryPermissions() + { + return true; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java new file mode 100644 index 0000000000..faad28702f --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryFactory.java @@ -0,0 +1,225 @@ +/* + * 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.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +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.PagingRequest; +import org.alfresco.query.CannedQuerySortDetails.SortOrder; +import org.alfresco.repo.blog.BlogPostInfo; +import org.alfresco.repo.blog.BlogService; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor; +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. + * @since 4.0 + * + * @see BlogService#getDrafts(NodeRef, String, PagingRequest) + * @see BlogService#getPublished(NodeRef, Date, Date, String, PagingRequest) + */ +public class GetBlogPostsCannedQueryFactory extends AbstractCannedQueryFactory +{ + private MethodSecurityInterceptor methodSecurityInterceptor; + private String methodName; + private Object methodService; + private NodeService rawNodeService; + + public void setRawNodeService(NodeService nodeService) + { + this.rawNodeService = nodeService; + } + + public void setMethodSecurityInterceptor(MethodSecurityInterceptor methodSecurityInterceptor) + { + this.methodSecurityInterceptor = methodSecurityInterceptor; + } + + public void setMethodName(String methodName) + { + this.methodName = methodName; + } + + public void setMethodService(Object methodService) + { + this.methodService = methodService; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + Method method = null; + for (Method m : methodService.getClass().getMethods()) + { + // note: currently matches first found + if (m.getName().equals(methodName)) + { + method = m; + break; + } + } + + if (method == null) + { + throw new AlfrescoRuntimeException("Method not found: "+methodName); + } + + String queryExecutionId = (parameters.getQueryExecutionId() == null ? super.getQueryExecutionId(parameters) : parameters.getQueryExecutionId()); + + final GetBlogPostsCannedQuery cq = new GetBlogPostsCannedQuery(rawNodeService, methodSecurityInterceptor, method, parameters, queryExecutionId); + return (CannedQuery) cq; + } + + public CannedQuery getGetDraftsCannedQuery(NodeRef blogContainerNode, String username, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + //FIXME Need tenant service like for GetChildren? + boolean isPublished = false; + List requiredAspects = null; + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + username, + isPublished, + null, null, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_CREATED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + public CannedQuery getGetPublishedExternallyCannedQuery(NodeRef blogContainerNode, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + boolean isPublished = true; + List requiredAspects = Arrays.asList(new QName[]{BlogIntegrationModel.ASPECT_BLOG_POST}); + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + null, + isPublished, + null, null, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(BlogIntegrationModel.PROP_POSTED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + public CannedQuery getGetPublishedCannedQuery(NodeRef blogContainerNode, Date fromDate, Date toDate, String byUser, PagingRequest pagingReq) + { + ParameterCheck.mandatory("blogContainerNode", blogContainerNode); + ParameterCheck.mandatory("pagingReq", pagingReq); + + int requestTotalCountMax = pagingReq.getRequestTotalCountMax(); + + boolean isPublished = true; + List requiredAspects = null; + GetBlogPostsCannedQueryParams paramBean = new GetBlogPostsCannedQueryParams(blogContainerNode, + byUser, + isPublished, + fromDate, toDate, + requiredAspects); + + CannedQueryPageDetails cqpd = createCQPageDetails(pagingReq); + CannedQuerySortDetails cqsd = createCQSortDetails(ContentModel.PROP_PUBLISHED, SortOrder.DESCENDING); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, cqsd, AuthenticationUtil.getRunAsUser(), requestTotalCountMax, pagingReq.getQueryExecutionId()); + + // 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, CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, CannedQueryPageDetails.DEFAULT_PAGE_COUNT); + return cqpd; + } + + @Override + public void afterPropertiesSet() throws Exception + { + super.afterPropertiesSet(); + + PropertyCheck.mandatory(this, "methodSecurityInterceptor", methodSecurityInterceptor); + PropertyCheck.mandatory(this, "methodService", methodService); + PropertyCheck.mandatory(this, "methodName", methodName); + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java new file mode 100644 index 0000000000..c0de0d42e2 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/GetBlogPostsCannedQueryParams.java @@ -0,0 +1,94 @@ +/* + * 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.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}. + * + * @author Neil Mc Erlean + * @since 4.0 + */ +public class GetBlogPostsCannedQueryParams +{ + 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; + + public GetBlogPostsCannedQueryParams(NodeRef blogContainerNodeRef, + String cmCreator, + boolean isPublished, + Date publishedFromDate, + Date publishedToDate, + List requiredAspects) + { + this.blogContainerNode = blogContainerNodeRef; + 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; + } + + public String getCmCreator() + { + return cmCreator; + } + + public boolean getIsPublished() + { + return this.isPublished; + } + + public Date getPublishedFromDate() + { + return publishedFromDate; + } + + public Date getPublishedToDate() + { + return publishedToDate; + } + + public List getRequiredAspects() + { + return Collections.unmodifiableList(this.requiredAspects); + } +} diff --git a/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java b/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java new file mode 100644 index 0000000000..95d7558976 --- /dev/null +++ b/source/java/org/alfresco/repo/blog/cannedqueries/PropertyBasedComparator.java @@ -0,0 +1,71 @@ +/* + * 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.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 diff --git a/source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java b/source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java index fedc0efc2d..4034c3ca65 100644 --- a/source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java +++ b/source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java @@ -30,6 +30,7 @@ import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.forms.FormService; import org.alfresco.repo.imap.ImapService; import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.nodelocator.NodeLocatorService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; @@ -327,6 +328,13 @@ public class MockedTestServiceRegistry implements ServiceRegistry } + public NodeLocatorService getNodeLocatorService() + { + // TODO Auto-generated method stub + return null; + } + + public FileFolderService getFileFolderService() { // TODO Auto-generated method stub diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java index 38e93f8736..b7081a4f41 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java @@ -39,6 +39,7 @@ import org.alfresco.cmis.CMISResultSet; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.query.PagingResults; import org.alfresco.query.PermissionedResults; +import org.alfresco.repo.blog.BlogPostInfo; import org.alfresco.repo.search.SimpleResultSetMetaData; import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet; import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; @@ -952,6 +953,10 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, { testNodeRef = ((FileInfo) nextObject).getNodeRef(); } + else if (BlogPostInfo.class.isAssignableFrom(nextObject.getClass())) + { + testNodeRef = ((BlogPostInfo) nextObject).getNodeRef(); + } else if (Pair.class.isAssignableFrom(nextObject.getClass())) { testNodeRef = (NodeRef) ((Pair)nextObject).getSecond(); @@ -960,7 +965,7 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, { throw new ACLEntryVoterException( "The specified parameter is not a collection of " + - "NodeRefs, ChildAssociationRefs, FileInfos or Pair"); + "StoreRefs, NodeRefs, ChildAssociationRefs, FileInfos, BlogPostInfos or Pair"); } } else if (cad.typeString.equals(AFTER_ACL_PARENT)) diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index b8183d66e0..7262b1c537 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -28,6 +28,7 @@ import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.forms.FormService; import org.alfresco.repo.imap.ImapService; import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.nodelocator.NodeLocatorService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionService; @@ -532,7 +533,16 @@ public class ServiceDescriptorRegistry { return (RatingService)getService(RATING_SERVICE); } - + + /* + * (non-Javadoc) + * @see org.alfresco.service.ServiceRegistry#getNodeLocatorService() + */ + public NodeLocatorService getNodeLocatorService() + { + return (NodeLocatorService)getService(NODE_LOCATOR_SERVICE); + } + /* (non-Javadoc) * @see org.alfresco.service.ServiceRegistry#getInvitationService() */ diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 7f5dc117f4..77cfad5749 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -28,6 +28,7 @@ import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.forms.FormService; import org.alfresco.repo.imap.ImapService; import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.nodelocator.NodeLocatorService; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.admin.RepoAdminService; @@ -136,6 +137,7 @@ public interface ServiceRegistry static final QName RENDITION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RenditionService"); static final QName RATING_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RatingService"); static final QName REPO_ADMIN_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "RepoAdminService"); + static final QName NODE_LOCATOR_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NodeLocatorService"); // WCM / AVM static final QName AVM_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AVMService"); @@ -508,6 +510,13 @@ public interface ServiceRegistry @NotAuditable RatingService getRatingService(); + /** + * Get the node locator service (or null if one is not provided) + * @return + */ + @NotAuditable + NodeLocatorService getNodeLocatorService(); + /** * Get the invitation service (or null if one is not provided) * @return the invitation service