mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@18931 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
372 lines
15 KiB
Java
372 lines
15 KiB
Java
/*
|
|
* Copyright (C) 2005-2010 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
package org.alfresco.repo.forum;
|
|
|
|
import java.io.Serializable;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Collections;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.springframework.extensions.surf.util.I18NUtil;
|
|
import org.alfresco.model.ApplicationModel;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.model.ForumModel;
|
|
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
|
import org.alfresco.repo.copy.CopyDetails;
|
|
import org.alfresco.repo.copy.CopyServicePolicies;
|
|
import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback;
|
|
import org.alfresco.repo.node.NodeServicePolicies;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
|
import org.alfresco.service.cmr.model.FileExistsException;
|
|
import org.alfresco.service.cmr.model.FileFolderService;
|
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
|
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.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.dao.ConcurrencyFailureException;
|
|
|
|
/**
|
|
* Discussion-specific behaviours.
|
|
*
|
|
* @author Derek Hulley
|
|
* @since 3.2
|
|
*/
|
|
public class DiscussableAspect implements
|
|
NodeServicePolicies.OnAddAspectPolicy,
|
|
CopyServicePolicies.OnCopyNodePolicy,
|
|
CopyServicePolicies.OnCopyCompletePolicy
|
|
{
|
|
private static final String KEY_WORKING_COPIES = DiscussableAspect.class.getName() + ".WorkingCopies";
|
|
|
|
private static final Log logger = LogFactory.getLog(DiscussableAspect.class);
|
|
|
|
private PolicyComponent policyComponent;
|
|
private NodeService nodeService;
|
|
private FileFolderService fileFolderService;
|
|
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
public final void setFileFolderService(FileFolderService fileFolderService)
|
|
{
|
|
this.fileFolderService = fileFolderService;
|
|
}
|
|
|
|
/**
|
|
* Initialise method
|
|
*/
|
|
public void init()
|
|
{
|
|
// All forum-related copy behaviour uses the same copy callback
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
|
ForumModel.ASPECT_DISCUSSABLE,
|
|
new JavaBehaviour(this, "onAddAspect"));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
|
ForumModel.ASPECT_DISCUSSABLE,
|
|
new JavaBehaviour(this, "getCopyCallback"));
|
|
this.policyComponent.bindClassBehaviour(
|
|
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
|
ForumModel.ASPECT_DISCUSSABLE,
|
|
new JavaBehaviour(this, "onCopyComplete"));
|
|
}
|
|
|
|
/**
|
|
* @return Returns {@link DiscussableAspectCopyBehaviourCallback}
|
|
*/
|
|
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
|
{
|
|
return DiscussableAspectCopyBehaviourCallback.INSTANCE;
|
|
}
|
|
|
|
/**
|
|
* Copy behaviour for the <b>fm:discussable</b> aspect.
|
|
* <p>
|
|
* Only the aspect is copied (to get the default behaviour). All topics are copied later.
|
|
*
|
|
* @author Derek Hulley
|
|
* @since 3.2
|
|
*/
|
|
private static class DiscussableAspectCopyBehaviourCallback extends DoNothingCopyBehaviourCallback
|
|
{
|
|
private static final CopyBehaviourCallback INSTANCE = new DiscussableAspectCopyBehaviourCallback();
|
|
|
|
/**
|
|
* Copy the aspect over only if the source document has also been checked out
|
|
*/
|
|
@Override
|
|
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
|
{
|
|
if (copyDetails.getSourceNodeAspectQNames().contains(ContentModel.ASPECT_WORKING_COPY) &&
|
|
!copyDetails.isTargetNodeIsNew())
|
|
{
|
|
// We are copying back from a working copy to the original node (probably)
|
|
// We need to do a full merge of the discussions. Keep track of the nodes
|
|
// that need this behaviour and complete the copy after the copy completes.
|
|
Set<NodeRef> nodeRefs = TransactionalResourceHelper.getSet(KEY_WORKING_COPIES);
|
|
nodeRefs.add(copyDetails.getSourceNodeRef());
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Ensure that the node has a <b>fm:forum</b> child node otherwise create one
|
|
*/
|
|
public void onAddAspect(NodeRef discussableNodeRef, QName aspectTypeQName)
|
|
{
|
|
String name = (String)this.nodeService.getProperty(
|
|
discussableNodeRef,
|
|
ContentModel.PROP_NAME);
|
|
String forumName = I18NUtil.getMessage("discussion.discussion_for", new Object[] {name});
|
|
|
|
NodeRef forumNodeRef = getForum(discussableNodeRef);
|
|
|
|
if (forumNodeRef == null)
|
|
{
|
|
Map<QName, Serializable> forumProps = new HashMap<QName, Serializable>(1);
|
|
forumProps.put(ContentModel.PROP_NAME, forumName);
|
|
|
|
ChildAssociationRef childRef = nodeService.createNode(
|
|
discussableNodeRef,
|
|
ForumModel.ASSOC_DISCUSSION,
|
|
QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "discussion"),
|
|
ForumModel.TYPE_FORUM, forumProps);
|
|
|
|
forumNodeRef = childRef.getChildRef();
|
|
}
|
|
else
|
|
{
|
|
// Just adjust the name
|
|
nodeService.setProperty(forumNodeRef, ContentModel.PROP_NAME, forumName);
|
|
}
|
|
|
|
// apply the uifacets aspect
|
|
Map<QName, Serializable> uiFacetsProps = new HashMap<QName, Serializable>(5);
|
|
uiFacetsProps.put(ApplicationModel.PROP_ICON, "forum");
|
|
this.nodeService.addAspect(forumNodeRef, ApplicationModel.ASPECT_UIFACETS, uiFacetsProps);
|
|
|
|
// Done
|
|
if (logger.isDebugEnabled())
|
|
{
|
|
logger.debug(
|
|
"Created forum node for discussion: \n" +
|
|
" Discussable Node: " + discussableNodeRef + "\n" +
|
|
" Forum Node: " + forumNodeRef);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieves the forum node the the given discussable
|
|
*
|
|
* @return Returns the <b>fm:forum</b> node or <tt>null</tt>
|
|
*/
|
|
private NodeRef getForum(NodeRef discussableNodeRef)
|
|
{
|
|
List<ChildAssociationRef> destChildren = nodeService.getChildAssocs(
|
|
discussableNodeRef,
|
|
ForumModel.ASSOC_DISCUSSION,
|
|
RegexQNamePattern.MATCH_ALL);
|
|
// Take the first one
|
|
if (destChildren.size() == 0)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
// We just take the first one
|
|
ChildAssociationRef discussionAssoc = destChildren.get(0);
|
|
return discussionAssoc.getChildRef();
|
|
}
|
|
}
|
|
//
|
|
// /**
|
|
// * Copies the discussions to the new node, whilst being sensitive to any existing discussions.
|
|
// */
|
|
// public void onCopyComplete(
|
|
// QName classQName,
|
|
// NodeRef sourceNodeRef,
|
|
// NodeRef targetNodeRef,
|
|
// boolean copyToNewNode,
|
|
// Map<NodeRef,NodeRef> copyMap)
|
|
// {
|
|
// // if the copy is not a new node it is a checkin, we therefore
|
|
// // need to copy any discussions from the working copy document
|
|
// // to the document being checked in
|
|
// if (copyToNewNode)
|
|
// {
|
|
// // We don't care about new copies, just copies to existing nodes
|
|
// return;
|
|
// }
|
|
// List<ChildAssociationRef> sourceChildren = nodeService.getChildAssocs(sourceNodeRef,
|
|
// ForumModel.ASSOC_DISCUSSION, RegexQNamePattern.MATCH_ALL);
|
|
//
|
|
// if (sourceChildren.size() != 1)
|
|
// {
|
|
// throw new CheckOutCheckInServiceException(
|
|
// "The source node has the discussable aspect but does not have 1 child, it has " +
|
|
// sourceChildren.size() + " children!");
|
|
// }
|
|
//
|
|
// NodeRef sourceForum = sourceChildren.get(0).getChildRef();
|
|
//
|
|
// // get the forum for the destination node, it's created if necessary
|
|
// NodeRef destinationForum = getDestinationForum(targetNodeRef);
|
|
//
|
|
// // copy any topics from the source forum to the destination forum
|
|
// int copied = 0;
|
|
// List<ChildAssociationRef> sourceForums = nodeService.getChildAssocs(sourceForum);
|
|
// for (ChildAssociationRef childRef : sourceForums)
|
|
// {
|
|
// String topicName = null;
|
|
// NodeRef childNode = childRef.getChildRef();
|
|
// if (nodeService.getType(childNode).equals(ForumModel.TYPE_TOPIC))
|
|
// {
|
|
// try
|
|
// {
|
|
// // work out the name for the copied topic
|
|
// String childName = nodeService.getProperty(childNode,
|
|
// ContentModel.PROP_NAME).toString();
|
|
// Serializable labelProp = nodeService.getProperty(targetNodeRef,
|
|
// ContentModel.PROP_VERSION_LABEL);
|
|
// if (labelProp == null)
|
|
// {
|
|
// SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
|
|
// topicName = childName + " - " + dateFormat.format(new Date());
|
|
// }
|
|
// else
|
|
// {
|
|
// topicName = childName + " (" + labelProp.toString() + ")";
|
|
// }
|
|
//
|
|
// fileFolderService.copy(childNode, destinationForum, topicName);
|
|
// copied++;
|
|
// }
|
|
// catch (FileNotFoundException fnfe)
|
|
// {
|
|
// throw new CheckOutCheckInServiceException(
|
|
// "Failed to copy topic from working copy to checked out content", fnfe);
|
|
// }
|
|
// catch (FileExistsException fee)
|
|
// {
|
|
// throw new CheckOutCheckInServiceException("Failed to checkin content as a topic called " +
|
|
// topicName + " already exists on the checked out content", fee);
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if (logger.isDebugEnabled())
|
|
// logger.debug("Copied " + copied + " topics from the working copy to the checked out content");
|
|
// }
|
|
//
|
|
|
|
public void onCopyComplete(
|
|
QName classRef,
|
|
NodeRef sourceNodeRef,
|
|
NodeRef targetNodeRef,
|
|
boolean copyToNewNode,
|
|
Map<NodeRef, NodeRef> copyMap)
|
|
{
|
|
Set<NodeRef> workingCopyNodeRefs = TransactionalResourceHelper.getSet(KEY_WORKING_COPIES);
|
|
if (!workingCopyNodeRefs.contains(sourceNodeRef))
|
|
{
|
|
// This is not one of the nodes that needs to have discussions copied over
|
|
return;
|
|
}
|
|
|
|
// First check that the source node has forums
|
|
NodeRef sourceForumNodeRef = getForum(sourceNodeRef);
|
|
if (sourceForumNodeRef == null)
|
|
{
|
|
// Missing! Clean the source node up!
|
|
nodeService.removeAspect(sourceNodeRef, ForumModel.ASPECT_DISCUSSABLE);
|
|
return;
|
|
}
|
|
|
|
// The aspect may or may not exist on the target node
|
|
if (!nodeService.hasAspect(targetNodeRef, ForumModel.ASPECT_DISCUSSABLE))
|
|
{
|
|
// Add the aspect
|
|
nodeService.addAspect(targetNodeRef, ForumModel.ASPECT_DISCUSSABLE, null);
|
|
}
|
|
// Get the forum node
|
|
NodeRef targetForumNodeRef = getForum(targetNodeRef);
|
|
// Merge the forum topics
|
|
List<ChildAssociationRef> topicAssocRefs = nodeService.getChildAssocs(
|
|
sourceForumNodeRef,
|
|
Collections.singleton(ForumModel.TYPE_TOPIC));
|
|
int copied = 0;
|
|
for (ChildAssociationRef topicAssocRef : topicAssocRefs)
|
|
{
|
|
NodeRef topicNodeRef = topicAssocRef.getChildRef();
|
|
try
|
|
{
|
|
// work out the name for the copied topic
|
|
String topicName;
|
|
String topicNodeName = nodeService.getProperty(topicNodeRef, ContentModel.PROP_NAME).toString();
|
|
Serializable labelProp = nodeService.getProperty(targetNodeRef, ContentModel.PROP_VERSION_LABEL);
|
|
if (labelProp == null)
|
|
{
|
|
SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss");
|
|
topicName = topicNodeName + " - " + dateFormat.format(new Date());
|
|
}
|
|
else
|
|
{
|
|
topicName = topicNodeName + " (" + labelProp.toString() + ")";
|
|
}
|
|
|
|
if (fileFolderService.searchSimple(targetForumNodeRef, topicName) != null)
|
|
{
|
|
// A topic with that name already exists
|
|
continue;
|
|
}
|
|
fileFolderService.copy(topicNodeRef, targetForumNodeRef, topicName);
|
|
copied++;
|
|
}
|
|
catch (FileExistsException e)
|
|
{
|
|
// We checked for this, so this is a concurrency condition
|
|
throw new ConcurrencyFailureException("Target topic exists: " + e.getMessage(), e);
|
|
}
|
|
catch (FileNotFoundException e)
|
|
{
|
|
// The node was there, but now it's gone
|
|
throw new ConcurrencyFailureException("Forum was deleted: " + e.getMessage(), e);
|
|
}
|
|
}
|
|
}
|
|
}
|