mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Fixed MOB-426: Refactor CopyService: Apply new pattern to existing policy handlers
Fallout: 1. Policy handling for 'onCopy' has been changed to 'getCopyCallback' 2. All existing policy usage was refactored to control behaviour more closely 3. The default child association behaviour has changed: 3.1 Types and aspects control their own child association behaviour 3.2 cm:folder recurses into primary children, but merely copies the secondary association 3.3 cm:rule recurses into primary children 3.4 unless behaviour is defined for a child association, there is no recursion or copying 4. Node association behavior has changed 4.1 There is no copying of node associations. Each type and aspect must handle this by recording nodes and fixing up the required associations in the onCopyComplete. 4.2 If there is a requirement, this can be added to the callback later See 'org.alfresco.repo.copy.AbstractCopyBehaviourCallback' and derived classes for examples. Areas to test with particular attention: 1. Normal copy behaviour 2. Copy of documents with discussions 3. Check-in check-out 4. Check-in, check-out of documents where a discussion was added to working copy 5. Copies of documents with thumbnails 6. Copies of documents with rules 7. Copying of hierarchies that contain rules to copy to another location within the hierarchy 8. Copying into folders where named children already exist git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@13915 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -71,7 +71,10 @@
|
||||
</bean>
|
||||
|
||||
<!-- Action Service -->
|
||||
<bean id="actionService" class="org.alfresco.repo.action.ActionServiceImpl">
|
||||
<bean id="actionService" class="org.alfresco.repo.action.ActionServiceImpl" init-method="init">
|
||||
<property name="policyComponent">
|
||||
<ref bean="policyComponent" />
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="NodeService" />
|
||||
</property>
|
||||
@@ -102,12 +105,15 @@
|
||||
<!-- Actions Aspect -->
|
||||
|
||||
<bean id="actionsAspect" class="org.alfresco.repo.action.ActionsAspect" init-method="init">
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="policyComponent">
|
||||
<ref bean="policyComponent"/>
|
||||
</property>
|
||||
<property name="behaviourFilter">
|
||||
<ref bean="policyBehaviourFilter"/>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService"/>
|
||||
</property>
|
||||
|
@@ -308,6 +308,7 @@
|
||||
<value>alfresco.messages.permissions-service</value>
|
||||
<value>alfresco.messages.content-service</value>
|
||||
<value>alfresco.messages.coci-service</value>
|
||||
<value>alfresco.messages.discussion-messages</value>
|
||||
<value>alfresco.messages.template-service</value>
|
||||
<value>alfresco.messages.lock-service</value>
|
||||
<value>alfresco.messages.patch-service</value>
|
||||
@@ -385,13 +386,13 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="admSearchService" class="org.alfresco.repo.search.SearcherComponent">
|
||||
<bean id="admSearchService" class="org.alfresco.repo.search.SearcherComponent">
|
||||
<property name="indexerAndSearcherFactory">
|
||||
<ref bean="admLuceneIndexerAndSearcherFactory" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="versionSearchService" class="org.alfresco.repo.search.SearcherComponent">
|
||||
|
||||
<bean id="versionSearchService" class="org.alfresco.repo.search.SearcherComponent">
|
||||
<property name="indexerAndSearcherFactory">
|
||||
<ref bean="admLuceneUnIndexedIndexerAndSearcherFactory" />
|
||||
</property>
|
||||
@@ -418,7 +419,7 @@
|
||||
</entry>
|
||||
</map>
|
||||
</property>
|
||||
<property name="redirectedStoreBindings">
|
||||
<property name="redirectedStoreBindings">
|
||||
<map>
|
||||
<entry key="workspace://lightWeightVersionStore">
|
||||
<ref bean="admLuceneUnIndexedIndexerAndSearcherFactory"></ref>
|
||||
@@ -450,9 +451,9 @@
|
||||
</bean>
|
||||
|
||||
<bean id="indexThreadPoolExecutor" class="org.alfresco.util.ThreadPoolExecutorFactoryBean">
|
||||
<property name="poolName">
|
||||
<value>indexThread</value>
|
||||
</property>
|
||||
<property name="poolName">
|
||||
<value>indexThread</value>
|
||||
</property>
|
||||
<property name="corePoolSize">
|
||||
<value>10</value>
|
||||
</property>
|
||||
@@ -513,32 +514,32 @@
|
||||
<property name="lockPollInterval">
|
||||
<value>${lucene.lock.poll.interval}</value>
|
||||
</property>
|
||||
<property name="defaultMLIndexAnalysisMode">
|
||||
<property name="defaultMLIndexAnalysisMode">
|
||||
<value>EXACT_LANGUAGE_AND_ALL</value>
|
||||
</property>
|
||||
<property name="defaultMLSearchAnalysisMode">
|
||||
<property name="defaultMLSearchAnalysisMode">
|
||||
<value>EXACT_LANGUAGE_AND_ALL</value>
|
||||
</property>
|
||||
<property name="threadPoolExecutor">
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
<property name="threadPoolExecutor">
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
<property name="bulkLoader">
|
||||
<ref bean="hibernateL1CacheBulkLoader"></ref>
|
||||
</property>
|
||||
<!-- over-ride to false for pre 3.0 behaviour -->
|
||||
<property name="postSortDateTime">
|
||||
<value>true</value>
|
||||
</property>
|
||||
<ref bean="hibernateL1CacheBulkLoader"></ref>
|
||||
</property>
|
||||
<!-- over-ride to false for pre 3.0 behaviour -->
|
||||
<property name="postSortDateTime">
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<!-- Indexer and searchers for lucene -->
|
||||
|
||||
|
||||
<!-- Indexer and searchers for lucene -->
|
||||
<bean id="admLuceneUnIndexedIndexerAndSearcherFactory"
|
||||
class="org.alfresco.repo.search.impl.lucene.ADMLuceneUnIndexedIndexAndSearcherFactory">
|
||||
<property name="nodeService">
|
||||
<ref bean="mlAwareNodeService" />
|
||||
</property>
|
||||
<property name="tenantService">
|
||||
<property name="tenantService">
|
||||
<ref bean="tenantService"/>
|
||||
</property>
|
||||
<property name="dictionaryService">
|
||||
@@ -580,18 +581,18 @@
|
||||
<property name="lockPollInterval">
|
||||
<value>${lucene.lock.poll.interval}</value>
|
||||
</property>
|
||||
<property name="defaultMLIndexAnalysisMode">
|
||||
<property name="defaultMLIndexAnalysisMode">
|
||||
<value>EXACT_LANGUAGE_AND_ALL</value>
|
||||
</property>
|
||||
<property name="defaultMLSearchAnalysisMode">
|
||||
<property name="defaultMLSearchAnalysisMode">
|
||||
<value>EXACT_LANGUAGE_AND_ALL</value>
|
||||
</property>
|
||||
<property name="threadPoolExecutor">
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
<property name="threadPoolExecutor">
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Indexer and searchers for lucene -->
|
||||
<!-- Indexer and searchers for lucene -->
|
||||
<bean id="avmLuceneIndexerAndSearcherFactory"
|
||||
class="org.alfresco.repo.search.impl.lucene.AVMLuceneIndexerAndSearcherFactory">
|
||||
<property name="nodeService">
|
||||
@@ -612,13 +613,13 @@
|
||||
<property name="contentService">
|
||||
<ref bean="contentService" />
|
||||
</property>
|
||||
<property name="contentStore">
|
||||
<ref bean="fileContentStore"/>
|
||||
</property>
|
||||
<property name="avmService">
|
||||
<property name="contentStore">
|
||||
<ref bean="fileContentStore"/>
|
||||
</property>
|
||||
<property name="avmService">
|
||||
<ref bean="avmService" />
|
||||
</property>
|
||||
<property name="avmSyncService">
|
||||
<property name="avmSyncService">
|
||||
<ref bean="avmSyncService" />
|
||||
</property>
|
||||
<property name="queryRegister">
|
||||
@@ -655,12 +656,12 @@
|
||||
<value>EXACT_LANGUAGE_AND_ALL</value>
|
||||
</property>
|
||||
<property name="threadPoolExecutor">
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
<!-- over-ride to false for pre 3.0 behaviour -->
|
||||
<property name="postSortDateTime">
|
||||
<value>true</value>
|
||||
</property>
|
||||
<ref bean="indexThreadPoolExecutor"></ref>
|
||||
</property>
|
||||
<!-- over-ride to false for pre 3.0 behaviour -->
|
||||
<property name="postSortDateTime">
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
@@ -669,7 +670,7 @@
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService" />
|
||||
</property>
|
||||
<property name="publicNodeService">
|
||||
<property name="publicNodeService">
|
||||
<ref bean="NodeService" />
|
||||
</property>
|
||||
<property name="tenantService">
|
||||
@@ -716,7 +717,7 @@
|
||||
<!-- -->
|
||||
|
||||
|
||||
<bean id="versionService" class="org.alfresco.repo.version.Version2ServiceImpl" init-method="initialise">
|
||||
<bean id="versionService" class="org.alfresco.repo.version.Version2ServiceImpl" init-method="initialise">
|
||||
<property name="nodeService">
|
||||
<ref bean="NodeService" />
|
||||
</property>
|
||||
@@ -895,7 +896,7 @@
|
||||
|
||||
<!-- Version models -->
|
||||
<value>org/alfresco/repo/version/version_model.xml</value> <!-- deprecated -->
|
||||
<value>org/alfresco/repo/version/version2_model.xml</value>
|
||||
<value>org/alfresco/repo/version/version2_model.xml</value>
|
||||
|
||||
<!-- Email model -->
|
||||
<value>alfresco/model/emailServerModel.xml</value>
|
||||
@@ -1016,7 +1017,7 @@
|
||||
<ref bean="NodeService" />
|
||||
</property>
|
||||
<property name="fileFolderService">
|
||||
<ref bean="FileFolderService" />
|
||||
<ref bean="fileFolderService" />
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
@@ -1040,7 +1041,7 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="luceneFullTextSearchIndexer" class="org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexerImpl">
|
||||
<bean id="luceneFullTextSearchIndexer" class="org.alfresco.repo.search.impl.lucene.fts.FullTextSearchIndexerImpl">
|
||||
<property name="indexerAndSearcherFactory">
|
||||
<ref bean="indexerAndSearcherFactory" />
|
||||
</property>
|
||||
@@ -1053,9 +1054,9 @@
|
||||
<ref bean="transactionService" />
|
||||
</property>
|
||||
<property name="factories">
|
||||
<set>
|
||||
<ref bean="admLuceneIndexerAndSearcherFactory" />
|
||||
</set>
|
||||
<set>
|
||||
<ref bean="admLuceneIndexerAndSearcherFactory" />
|
||||
</set>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService" />
|
||||
|
3
config/alfresco/messages/discussion-messages.properties
Normal file
3
config/alfresco/messages/discussion-messages.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
# Discussion-related messages
|
||||
|
||||
discussion.discussion_for={0} discussion
|
@@ -2,74 +2,77 @@
|
||||
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
|
||||
|
||||
<beans>
|
||||
|
||||
|
||||
<!-- Rules Service -->
|
||||
|
||||
<bean id="ruleService" class="org.alfresco.repo.rule.RuleServiceImpl">
|
||||
<property name="nodeService">
|
||||
<ref bean="NodeService"/>
|
||||
</property>
|
||||
<property name="runtimeNodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="actionService">
|
||||
<ref bean="ActionService"/>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="NodeService"/>
|
||||
</property>
|
||||
<property name="runtimeNodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="actionService">
|
||||
<ref bean="ActionService"/>
|
||||
</property>
|
||||
<property name="runtimeActionService">
|
||||
<ref bean="actionService"/>
|
||||
</property>
|
||||
<property name="dictionaryService">
|
||||
<ref bean="dictionaryService"/>
|
||||
</property>
|
||||
<property name="permissionService">
|
||||
<ref bean="permissionService"/>
|
||||
</property>
|
||||
<property name="rulesDisabled">
|
||||
<value>false</value>
|
||||
</property>
|
||||
<ref bean="actionService"/>
|
||||
</property>
|
||||
<property name="dictionaryService">
|
||||
<ref bean="dictionaryService"/>
|
||||
</property>
|
||||
<property name="permissionService">
|
||||
<ref bean="permissionService"/>
|
||||
</property>
|
||||
<property name="rulesDisabled">
|
||||
<value>false</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Rules Aspect -->
|
||||
|
||||
<bean id="rulesAspect" class="org.alfresco.repo.rule.RulesAspect" init-method="init">
|
||||
<property name="policyComponent">
|
||||
<ref bean="policyComponent"/>
|
||||
</property>
|
||||
<property name="behaviourFilter">
|
||||
<ref bean="policyBehaviourFilter"/>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="policyComponent">
|
||||
<ref bean="policyComponent"/>
|
||||
</property>
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService"/>
|
||||
</property>
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- I18N -->
|
||||
|
||||
<bean id="ruleResourceBundles" class="org.alfresco.i18n.ResourceBundleBootstrapComponent">
|
||||
<property name="resourceBundles">
|
||||
<list>
|
||||
<value>alfresco.messages.rule-config</value>
|
||||
</list>
|
||||
</property>
|
||||
<property name="resourceBundles">
|
||||
<list>
|
||||
<value>alfresco.messages.rule-config</value>
|
||||
</list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<!-- Rule types -->
|
||||
|
||||
<bean id="rule-type-base" abstract="true" init-method="init">
|
||||
<property name="ruleService">
|
||||
<ref bean="ruleService"/>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
<ref bean="ruleService"/>
|
||||
</property>
|
||||
<property name="nodeService">
|
||||
<ref bean="nodeService"/>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="inbound" class="org.alfresco.repo.rule.RuleTypeImpl" parent="rule-type-base">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<ref bean="on-create-node-trigger"/>
|
||||
<ref bean="on-create-child-association-trigger"/>
|
||||
<ref bean="on-content-create-trigger"/>
|
||||
<ref bean="on-create-child-association-trigger"/>
|
||||
<ref bean="on-content-create-trigger"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
@@ -86,8 +89,8 @@
|
||||
<bean id="outbound" class="org.alfresco.repo.rule.RuleTypeImpl" parent="rule-type-base">
|
||||
<constructor-arg>
|
||||
<list>
|
||||
<ref bean="on-delete-child-association-trigger"/>
|
||||
<ref bean="on-delete-node-trigger"/>
|
||||
<ref bean="on-delete-child-association-trigger"/>
|
||||
<ref bean="on-delete-node-trigger"/>
|
||||
</list>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
@@ -114,10 +117,10 @@
|
||||
|
||||
<bean id="on-create-node-trigger" class="org.alfresco.repo.rule.ruletrigger.CreateNodeRuleTrigger" parent="rule-trigger-base">
|
||||
<property name="dictionaryService">
|
||||
<ref bean="dictionaryService"/>
|
||||
<ref bean="dictionaryService"/>
|
||||
</property>
|
||||
<property name="isClassBehaviour">
|
||||
<value>true</value>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
@@ -135,7 +138,7 @@
|
||||
|
||||
<bean id="on-delete-child-association-trigger" class="org.alfresco.repo.rule.ruletrigger.BeforeDeleteChildAssociationRuleTrigger" parent="rule-trigger-base">
|
||||
<property name="executeRuleImmediately">
|
||||
<value>true</value>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
@@ -144,7 +147,7 @@
|
||||
<value>beforeDeleteNode</value>
|
||||
</property>
|
||||
<property name="executeRuleImmediately">
|
||||
<value>true</value>
|
||||
<value>true</value>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,147 +24,172 @@
|
||||
*/
|
||||
package org.alfresco.repo.action;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.policy.Behaviour;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.policy.BehaviourFilter;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.rule.RuleService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
|
||||
/**
|
||||
* Class containing behaviour for the actions aspect
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
public class ActionsAspect
|
||||
public class ActionsAspect implements CopyServicePolicies.OnCopyNodePolicy, CopyServicePolicies.OnCopyCompletePolicy
|
||||
{
|
||||
private Behaviour onAddAspectBehaviour;
|
||||
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
private PolicyComponent policyComponent;
|
||||
private BehaviourFilter behaviourFilter;
|
||||
private RuleService ruleService;
|
||||
private NodeService nodeService;
|
||||
|
||||
private NodeService nodeService;
|
||||
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
|
||||
{
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setRuleService(RuleService ruleService)
|
||||
public void setRuleService(RuleService ruleService)
|
||||
{
|
||||
this.ruleService = ruleService;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
ActionModel.ASPECT_ACTIONS,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
||||
public void init()
|
||||
{
|
||||
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
|
||||
PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter);
|
||||
PropertyCheck.mandatory(this, "ruleService", ruleService);
|
||||
PropertyCheck.mandatory(this, "nodeService", nodeService);
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ActionModel.ASPECT_ACTIONS,
|
||||
new JavaBehaviour(this, "onCopyComplete"));
|
||||
|
||||
this.onAddAspectBehaviour = new JavaBehaviour(this, "onAddAspect");
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
||||
ActionModel.ASPECT_ACTIONS,
|
||||
new JavaBehaviour(this, "onCopyComplete"));
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
ActionModel.ASPECT_ACTIONS,
|
||||
onAddAspectBehaviour);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper to diable the on add aspect policy behaviour. Helpful when importing,
|
||||
* copying and other bulk respstorative operations.
|
||||
*
|
||||
* TODO will eventually be redundant when policies can be enabled/diabled in the
|
||||
* policy componenet
|
||||
*/
|
||||
public void disbleOnAddAspect()
|
||||
{
|
||||
this.onAddAspectBehaviour.disable();
|
||||
new JavaBehaviour(this, "onAddAspect"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to enable the on add aspect policy behaviour. Helpful when importing,
|
||||
* copying and other bulk respstorative operations.
|
||||
* On add aspect policy behaviour
|
||||
*
|
||||
* TODO will eventually be redundant when policies can be enabled/diabled in the
|
||||
* policy componenet
|
||||
* @param nodeRef
|
||||
* @param aspectTypeQName
|
||||
*/
|
||||
public void enableOnAddAspect()
|
||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
this.onAddAspectBehaviour.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* On add aspect policy behaviour
|
||||
* @param nodeRef
|
||||
* @param aspectTypeQName
|
||||
*/
|
||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
this.ruleService.disableRules(nodeRef);
|
||||
try
|
||||
{
|
||||
this.nodeService.createNode(
|
||||
this.nodeService.createNode(
|
||||
nodeRef,
|
||||
ActionModel.ASSOC_ACTION_FOLDER,
|
||||
ActionModel.ASSOC_ACTION_FOLDER,
|
||||
ContentModel.TYPE_SYSTEM_FOLDER);
|
||||
ActionModel.ASSOC_ACTION_FOLDER,
|
||||
ContentModel.TYPE_SYSTEM_FOLDER);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.ruleService.enableRules(nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns {@link ActionsAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
return new ActionsAspectCopyBehaviourCallback(behaviourFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the default copy behaviour to include cascading to action folders.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class ActionsAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private final BehaviourFilter behaviourFilter;
|
||||
private ActionsAspectCopyBehaviourCallback(BehaviourFilter behaviourFilter)
|
||||
{
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the aspect behaviour for this node
|
||||
*
|
||||
* @return Returns <tt>true</tt>
|
||||
*/
|
||||
@Override
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
NodeRef targetNodeRef = copyDetails.getTargetNodeRef();
|
||||
behaviourFilter.disableBehaviour(targetNodeRef, ActionModel.ASPECT_ACTIONS);
|
||||
// Always copy
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always cascades to the action folders
|
||||
*/
|
||||
@Override
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
|
||||
if (childAssocRef.getTypeQName().equals(ActionModel.ASSOC_ACTION_FOLDER))
|
||||
{
|
||||
return ChildAssocCopyAction.COPY_CHILD;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException(
|
||||
"Behaviour should have been invoked: \n" +
|
||||
" Aspect: " + this.getClass().getName() + "\n" +
|
||||
" " + childAssocCopyDetails + "\n" +
|
||||
" " + copyDetails);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enable aspect behaviour for the source node
|
||||
*/
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
{
|
||||
copyDetails.addAspect(ActionModel.ASPECT_ACTIONS);
|
||||
|
||||
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(
|
||||
sourceNodeRef,
|
||||
RegexQNamePattern.MATCH_ALL,
|
||||
ActionModel.ASSOC_ACTION_FOLDER);
|
||||
for (ChildAssociationRef assoc : assocs)
|
||||
{
|
||||
copyDetails.addChildAssociation(classRef, assoc, true);
|
||||
}
|
||||
|
||||
this.onAddAspectBehaviour.disable();
|
||||
}
|
||||
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyToNew,
|
||||
Map<NodeRef, NodeRef> copyMap)
|
||||
{
|
||||
this.onAddAspectBehaviour.enable();
|
||||
}
|
||||
Map<NodeRef, NodeRef> copyMap)
|
||||
{
|
||||
behaviourFilter.enableBehaviour(sourceNodeRef, ActionModel.ASPECT_ACTIONS);
|
||||
}
|
||||
}
|
||||
|
@@ -25,19 +25,25 @@
|
||||
|
||||
package org.alfresco.repo.coci;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.lock.LockService;
|
||||
import org.alfresco.service.cmr.lock.LockStatus;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
public class WorkingCopyAspect
|
||||
public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy
|
||||
{
|
||||
/**
|
||||
* Policy component
|
||||
@@ -91,9 +97,13 @@ public class WorkingCopyAspect
|
||||
{
|
||||
// Register copy behaviour for the working copy aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.TYPE_CMOBJECT,
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_WORKING_COPY,
|
||||
new JavaBehaviour(this, "onCopy"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
|
||||
// register onBeforeDelete class behaviour for the working copy aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
@@ -102,27 +112,6 @@ public class WorkingCopyAspect
|
||||
new JavaBehaviour(this, "beforeDeleteNode"));
|
||||
}
|
||||
|
||||
/**
|
||||
* onCopy policy behaviour
|
||||
*
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(QName, NodeRef, StoreRef, boolean, PolicyScope)
|
||||
*/
|
||||
public void onCopy(
|
||||
QName sourceClassRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
{
|
||||
if (copyToNewNode == false)
|
||||
{
|
||||
// Make sure that the name of the node is not updated with the working copy name
|
||||
copyDetails.removeProperty(ContentModel.PROP_NAME);
|
||||
}
|
||||
|
||||
// NOTE: the working copy aspect is not added since it should not be copyied
|
||||
}
|
||||
|
||||
/**
|
||||
* beforeDeleteNode policy behaviour
|
||||
*
|
||||
@@ -147,5 +136,63 @@ public class WorkingCopyAspect
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns {@link WorkingCopyAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
return WorkingCopyAspectCopyBehaviourCallback.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dual behaviour to ensure that <b>cm:name</b> is not copied if the source node has the
|
||||
* <b>cm:workingCopy</b> aspect, and to prevent the <b>cm:workingCopy</b> aspect from
|
||||
* being carried to the new node.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class WorkingCopyAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private static WorkingCopyAspectCopyBehaviourCallback instance = new WorkingCopyAspectCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* Disallows copying of the {@link ContentModel#ASPECT_WORKING_COPY <b>cm:workingCopy</b>} aspect.
|
||||
*/
|
||||
@Override
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
if (classQName.equals(ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents copying off the {@link ContentModel#PROP_NAME <b>cm:name</b>} property.
|
||||
*/
|
||||
@Override
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName, CopyDetails copyDetails, Map<QName, Serializable> properties)
|
||||
{
|
||||
if (classQName.equals(ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
else if (copyDetails.getSourceNodeAspectQNames().contains(ContentModel.ASPECT_WORKING_COPY))
|
||||
{
|
||||
properties.remove(ContentModel.PROP_NAME);
|
||||
return properties;
|
||||
}
|
||||
else
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Abstract implementation to allow for easier migration if the interface changes.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public abstract class AbstractCopyBehaviourCallback implements CopyBehaviourCallback
|
||||
{
|
||||
private static final String KEY_NODEREF_REPOINTING_PREFIX = "recordNodeRefPropertiesForRepointing-";
|
||||
|
||||
/**
|
||||
* @return Returns {@link ChildAssocRecurseAction#RESPECT_RECURSE_FLAG}
|
||||
*/
|
||||
public ChildAssocRecurseAction getChildAssociationRecurseAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
return ChildAssocRecurseAction.RESPECT_RECURSE_FLAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException always
|
||||
*/
|
||||
protected void throwExceptionForUnexpectedBehaviour(CopyDetails copyDetails, String ... otherDetails)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
sb.append("Behaviour should have been invoked: \n" +
|
||||
" Aspect: " + this.getClass().getName() + "\n" +
|
||||
" " + copyDetails + "\n");
|
||||
for (String otherDetail : otherDetails)
|
||||
{
|
||||
sb.append(" ").append(otherDetail).append("\n");
|
||||
}
|
||||
throw new IllegalStateException(sb.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to transactionally record <code>NodeRef</code> properties so that they
|
||||
* can later be fixed up to point to the relative, after-copy locations.
|
||||
* <p>
|
||||
* When the copy has been completed, the second stage of the process can be applied.
|
||||
*
|
||||
* @param sourceNodeRef the node that is being copied
|
||||
* @param properties the node properties being copied
|
||||
* @param propertyQName the qualified name of the property to check
|
||||
*
|
||||
* @see #repointNodeRefs(NodeRef, QName, Map, NodeService)
|
||||
*/
|
||||
public void recordNodeRefsForRepointing(
|
||||
NodeRef sourceNodeRef,
|
||||
Map<QName, Serializable> properties,
|
||||
QName propertyQName)
|
||||
{
|
||||
Serializable parameterValue = properties.get(propertyQName);
|
||||
if (parameterValue != null &&
|
||||
(parameterValue instanceof Collection || parameterValue instanceof NodeRef))
|
||||
{
|
||||
String key = KEY_NODEREF_REPOINTING_PREFIX + propertyQName.toString();
|
||||
// Store it for later
|
||||
Map<NodeRef, Serializable> map = TransactionalResourceHelper.getMap(key);
|
||||
map.put(sourceNodeRef, parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The second stage of the <code>NodeRef</code> repointing. Call this method to have
|
||||
* any <code>NodeRef</code> properties readjusted to reflect the copied node hierarchy.
|
||||
* Only use this method if it a requirement for the particular type or aspect that you
|
||||
* are coding for.
|
||||
*
|
||||
* @param sourceNodeRef the source node
|
||||
* @param propertyQName the target node i.e. the copy of the source node
|
||||
* @param copyMap the full hierarchy copy map of source to copies
|
||||
*
|
||||
* @see #recordNodeRefsForRepointing(NodeRef, Map, QName)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void repointNodeRefs(
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef targetNodeRef,
|
||||
QName propertyQName,
|
||||
Map<NodeRef, NodeRef> copyMap,
|
||||
NodeService nodeService)
|
||||
{
|
||||
String key = KEY_NODEREF_REPOINTING_PREFIX + propertyQName.toString();
|
||||
Map<NodeRef, Serializable> map = TransactionalResourceHelper.getMap(key);
|
||||
Serializable value = map.get(sourceNodeRef);
|
||||
if (value == null)
|
||||
{
|
||||
// Don't bother. The source node did not have a NodeRef property
|
||||
return;
|
||||
}
|
||||
Serializable newValue = null;
|
||||
if (value instanceof Collection)
|
||||
{
|
||||
Collection<Serializable> oldList = (Collection<Serializable>) value;
|
||||
List<Serializable> newList = new ArrayList<Serializable>(oldList.size());
|
||||
for (Serializable oldListValue : oldList)
|
||||
{
|
||||
Serializable newListValue = oldListValue;
|
||||
if (oldListValue instanceof NodeRef)
|
||||
{
|
||||
newListValue = repointNodeRef(copyMap, (NodeRef) value);
|
||||
}
|
||||
// Put the value in the new list even though the new list might be discarded
|
||||
newList.add(newListValue);
|
||||
// Check if the value changed
|
||||
if (!newListValue.equals(oldListValue))
|
||||
{
|
||||
// The value changed, so the new list will have to be set onto the target node
|
||||
newValue = newListValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value instanceof NodeRef)
|
||||
{
|
||||
NodeRef newNodeRef = repointNodeRef(copyMap, (NodeRef) value);
|
||||
if (!newNodeRef.equals(value))
|
||||
{
|
||||
// The value changed, so the new list will have to be set onto the target node
|
||||
newValue = newNodeRef;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("Should only have Collections and NodeRef values");
|
||||
}
|
||||
// Fix the node property on the target, if necessary
|
||||
if (newValue != null)
|
||||
{
|
||||
nodeService.setProperty(targetNodeRef, propertyQName, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private NodeRef repointNodeRef(Map<NodeRef, NodeRef> copyMap, NodeRef pointerNodeRef)
|
||||
{
|
||||
NodeRef copiedPointerNodeRef = copyMap.get(pointerNodeRef);
|
||||
if (copiedPointerNodeRef == null)
|
||||
{
|
||||
return pointerNodeRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
return copiedPointerNodeRef;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Handles compound behavioural callbacks for the same dictionary class (type or aspect).
|
||||
* <p>
|
||||
* When multiple policy handlers register callback for the same model class, an instance
|
||||
* of this class is used to resolve the calls. The behaviour is sometimes able to resolve
|
||||
* conflicts and sometimes not. Look at the individual callback methods to see how
|
||||
* conflicts are handled.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class CompoundCopyBehaviourCallback extends AbstractCopyBehaviourCallback
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(CompoundCopyBehaviourCallback.class);
|
||||
|
||||
private QName classQName;
|
||||
private List<CopyBehaviourCallback> callbacks;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param classQName the
|
||||
*/
|
||||
public CompoundCopyBehaviourCallback(QName classQName)
|
||||
{
|
||||
this.classQName = classQName;
|
||||
callbacks = new ArrayList<CopyBehaviourCallback>(2);
|
||||
}
|
||||
|
||||
public void addBehaviour(CopyBehaviourCallback callback)
|
||||
{
|
||||
callbacks.add(callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(1024);
|
||||
sb.append("\n")
|
||||
.append("CompoundCopyBehaviourCallback: \n")
|
||||
.append(" Model Class: ").append(classQName);
|
||||
boolean first = true;
|
||||
for (CopyBehaviourCallback callback : callbacks)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append(" ").append(callback.getClass().getName());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual callbacks effectively have a veto on the copy i.e. if one of the
|
||||
* callbacks returns <tt>false</tt> for {@link CopyBehaviourCallback#mustCopy(NodeRef)},
|
||||
* then the copy will NOT proceed. However, a warning is generated indicating that
|
||||
* there is a conflict in the defined behaviour.
|
||||
*
|
||||
* @return Returns <tt>true</tt> if all registered callbacks return <tt>true</tt>
|
||||
* or <b><tt>false</tt> if any of the registered callbacks return <tt>false</tt></b>.
|
||||
*/
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
CopyBehaviourCallback firstVeto = null;
|
||||
for (CopyBehaviourCallback callback : callbacks)
|
||||
{
|
||||
boolean mustCopyLocal = callback.getMustCopy(classQName, copyDetails);
|
||||
if (firstVeto == null && !mustCopyLocal)
|
||||
{
|
||||
firstVeto = callback;
|
||||
}
|
||||
if (mustCopyLocal && firstVeto != null)
|
||||
{
|
||||
// The callback says 'copy' but there is already a veto in place
|
||||
logger.warn(
|
||||
"CopyBehaviourCallback '" + callback.getClass().getName() + "' " +
|
||||
"is attempting to induce a copy when callback '" + firstVeto.getClass().getName() +
|
||||
"' has already vetoed it. Copying of '" + copyDetails.getSourceNodeRef() +
|
||||
"' will not occur.");
|
||||
}
|
||||
}
|
||||
// Done
|
||||
if (firstVeto == null)
|
||||
{
|
||||
// Allowed by all
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"All copy behaviours voted for a copy of node \n" +
|
||||
" " + copyDetails + "\n" +
|
||||
" " + this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Vetoed
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Copy behaviour vetoed for node " + copyDetails.getSourceNodeRef() + "\n" +
|
||||
" First veto: " + firstVeto.getClass().getName() + "\n" +
|
||||
" " + copyDetails + "\n" +
|
||||
" " + this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link ChildAssocCopyAction} ordering to drive priority i.e. a vote
|
||||
* to copy will override a vote NOT to copy.
|
||||
*
|
||||
* @return Returns the most lively choice of action
|
||||
*/
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssocCopyAction bestAction = ChildAssocCopyAction.IGNORE;
|
||||
for (CopyBehaviourCallback callback : callbacks)
|
||||
{
|
||||
ChildAssocCopyAction action = callback.getChildAssociationCopyAction(
|
||||
classQName,
|
||||
copyDetails,
|
||||
childAssocCopyDetails);
|
||||
if (action.compareTo(bestAction) > 0)
|
||||
{
|
||||
// We've trumped the last best one
|
||||
bestAction = action;
|
||||
}
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Child association copy behaviour: " + bestAction + "\n" +
|
||||
" " + childAssocCopyDetails + "\n" +
|
||||
" " + copyDetails + "\n" +
|
||||
" " + this);
|
||||
}
|
||||
return bestAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses the {@link ChildAssocRecurseAction} ordering to drive recursive copy behaviour.
|
||||
*
|
||||
* @return Returns the most lively choice of action
|
||||
*/
|
||||
@Override
|
||||
public ChildAssocRecurseAction getChildAssociationRecurseAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssocRecurseAction bestAction = ChildAssocRecurseAction.RESPECT_RECURSE_FLAG;
|
||||
for (CopyBehaviourCallback callback : callbacks)
|
||||
{
|
||||
ChildAssocRecurseAction action = callback.getChildAssociationRecurseAction(
|
||||
classQName,
|
||||
copyDetails,
|
||||
childAssocCopyDetails);
|
||||
if (action.compareTo(bestAction) > 0)
|
||||
{
|
||||
// We've trumped the last best one
|
||||
bestAction = action;
|
||||
}
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Child association recursion behaviour: " + bestAction + "\n" +
|
||||
" " + childAssocCopyDetails + "\n" +
|
||||
" " + copyDetails + "\n" +
|
||||
" " + this);
|
||||
}
|
||||
return bestAction;
|
||||
}
|
||||
|
||||
/**
|
||||
* The lowest common denominator applies here. The properties are passed to each
|
||||
* callback in turn. The resulting map is then passed to the next callback and so
|
||||
* on. If any callback removes or alters properties, these will not be recoverable.
|
||||
*
|
||||
* @return Returns the least properties assigned for the copy by any individual
|
||||
* callback handler
|
||||
*/
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties)
|
||||
{
|
||||
Map<QName, Serializable> copyProperties = new HashMap<QName, Serializable>(properties);
|
||||
for (CopyBehaviourCallback callback : callbacks)
|
||||
{
|
||||
copyProperties = callback.getCopyProperties(classQName, copyDetails, properties);
|
||||
}
|
||||
// Done
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug(
|
||||
"Copy properties: \n" +
|
||||
" " + copyDetails + "\n" +
|
||||
" " + this + "\n" +
|
||||
" Before: " + properties + "\n" +
|
||||
" After: " + copyProperties);
|
||||
}
|
||||
return copyProperties;
|
||||
}
|
||||
}
|
246
source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java
Normal file
246
source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* A callback to modify copy behaviour associated with a given type or aspect. This
|
||||
* callback is called per type and per aspect.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public interface CopyBehaviourCallback
|
||||
{
|
||||
/**
|
||||
* Description of how the copy process should traverse a child association.
|
||||
* The order of this enum denotes the priority when mixing behaviour as well;
|
||||
* that is to say that a 'forced recursion' will occur even if an 'ignore' is
|
||||
* also provided by the registered behaviour callbacks.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public enum ChildAssocCopyAction implements Comparable<ChildAssocCopyAction>
|
||||
{
|
||||
/**
|
||||
* Ignore the child association
|
||||
*/
|
||||
IGNORE
|
||||
{
|
||||
},
|
||||
/**
|
||||
* Copy the association only, keeping the existing child node
|
||||
*/
|
||||
COPY_ASSOC
|
||||
{
|
||||
},
|
||||
/**
|
||||
* Traverse the child association and <b>copy</b> the child node
|
||||
*/
|
||||
COPY_CHILD
|
||||
{
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Description of how the copy process should behave when copying an association.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public enum ChildAssocRecurseAction implements Comparable<ChildAssocRecurseAction>
|
||||
{
|
||||
/**
|
||||
* Respect the client's recursion decision
|
||||
*/
|
||||
RESPECT_RECURSE_FLAG
|
||||
{
|
||||
},
|
||||
/**
|
||||
* Force all further copies of the source hierarchy to recurse into children.
|
||||
* This allows behaviour to force a copy of a subtree that it expects to
|
||||
* exist.
|
||||
* <p>
|
||||
* <b>NOTE</b>: Any part of the source subtree can still terminate the recursion,
|
||||
* so this is mainly useful where the subtree contains the default
|
||||
* behaviour.
|
||||
*/
|
||||
FORCE_RECURSE
|
||||
{
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple bean class to convey information to the callback methods dealing with
|
||||
* copying of child associations.
|
||||
*
|
||||
* @see CopyBehaviourCallback#getChildAssociationCopyAction(QName, CopyDetails, ChildAssociationRef, NodeRef, boolean)
|
||||
* @see CopyBehaviourCallback#getChildAssociationRecurseAction(QName, CopyDetails, ChildAssociationRef, boolean)
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public static final class CopyChildAssociationDetails
|
||||
{
|
||||
private final ChildAssociationRef childAssocRef;
|
||||
private final NodeRef targetNodeRef;
|
||||
private final boolean targetNodeIsNew;
|
||||
private final boolean copyChildren;
|
||||
|
||||
public CopyChildAssociationDetails(
|
||||
ChildAssociationRef childAssocRef,
|
||||
NodeRef targetNodeRef,
|
||||
boolean targetNodeIsNew,
|
||||
boolean copyChildren)
|
||||
{
|
||||
this.childAssocRef = childAssocRef;
|
||||
this.targetNodeRef = targetNodeRef;
|
||||
this.targetNodeIsNew = targetNodeIsNew;
|
||||
this.copyChildren = copyChildren;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(256);
|
||||
sb.append("CopyChildAssociationDetails ")
|
||||
.append("[ childAssocRef=").append(childAssocRef)
|
||||
.append(", targetNodeRef=").append(targetNodeRef)
|
||||
.append(", targetNodeIsNew=").append(targetNodeIsNew)
|
||||
.append(", copyChildren=").append(copyChildren)
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the association being examined
|
||||
*/
|
||||
public final ChildAssociationRef getChildAssocRef()
|
||||
{
|
||||
return childAssocRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the target node that will be the
|
||||
* new parent if the association is copied
|
||||
*/
|
||||
public final NodeRef getTargetNodeRef()
|
||||
{
|
||||
return targetNodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Returns <tt>true</tt> if the {@link #getTargetNodeRef() target node}
|
||||
* has been newly created by the copy process or <tt>false</tt> if it
|
||||
* is a node that existed prior to the copy
|
||||
*/
|
||||
public final boolean isTargetNodeIsNew()
|
||||
{
|
||||
return targetNodeIsNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current recursion behaviour. This can be ignored and even altered, if required.
|
||||
*
|
||||
* @return Returns <tt>true</tt> if the copy process is currently recursing to
|
||||
* child associations or <tt>false</tt> if not.
|
||||
*/
|
||||
public final boolean isCopyChildren()
|
||||
{
|
||||
return copyChildren;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if this type or aspect must be copied. If the callback is for a type
|
||||
* (not aspect) then this determines if the node is copied at all. If the callback
|
||||
* is for an aspect, then this determines if the aspect is copied.
|
||||
*
|
||||
* @param classQName the name of the class that this is being invoked for
|
||||
* @param copyDetails the source node's copy details for quick reference
|
||||
* @return <tt>true</tt> if the type or aspect that this behaviour
|
||||
* represents must be copied.
|
||||
*/
|
||||
boolean getMustCopy(QName classQName, CopyDetails copyDetails);
|
||||
|
||||
/**
|
||||
* Determine if a copy should copy the child, the association only or do nothing with
|
||||
* the given association.
|
||||
* <p>
|
||||
* This is called regardless of whether 'cascade' copy has been selected by the client
|
||||
* of the copy. Some type and aspect behaviour will mandate a copy of the child
|
||||
* associations regardless of whether recursion is on.
|
||||
*
|
||||
* @param classQName the name of the class that this is being invoked for
|
||||
* @param copyDetails the source node's copy details for quick reference
|
||||
* @param childAssocCopyDetails all other details relating to the child association
|
||||
* @return Returns the copy {@link ChildAssocCopyAction action} to take
|
||||
* with the given child association
|
||||
*/
|
||||
ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails);
|
||||
|
||||
/**
|
||||
* Once the child association copy action has been chosen, the policy callback can
|
||||
* dictate whether or not to force further recursion. This cannot prevent
|
||||
* behaviour further down the hierarchy from stopping the copy.
|
||||
*
|
||||
* @param classQName the name of the class that this is being invoked for
|
||||
* @param copyDetails the source node's copy details for quick reference
|
||||
* @param childAssocCopyDetails all other details relating to the child association
|
||||
* @return Returns the type of {@link ChildAssocRecurseAction recursion}
|
||||
* to perform after having copied the child association
|
||||
*
|
||||
* @see #getChildAssociationCopyAction(QName, CopyDetails, ChildAssociationRef, boolean)
|
||||
*/
|
||||
ChildAssocRecurseAction getChildAssociationRecurseAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails);
|
||||
|
||||
/**
|
||||
* Modify the properties that are copied across.
|
||||
*
|
||||
* @param classQName the name of the class that this is being invoked for
|
||||
* @param copyDetails the source node's copy details for quick reference
|
||||
* @param properties the type- or aspect-specific properties that can be copied.
|
||||
* The map can be manipulated and returned as required.
|
||||
* @return Returns the type or aspect properties that should be copied.
|
||||
*/
|
||||
Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties);
|
||||
}
|
166
source/java/org/alfresco/repo/copy/CopyDetails.java
Normal file
166
source/java/org/alfresco/repo/copy/CopyDetails.java
Normal file
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Simple Java bean that contains the details of a copy process underway.
|
||||
*
|
||||
* @see CopyServicePolicies
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class CopyDetails
|
||||
{
|
||||
private final NodeRef sourceNodeRef;
|
||||
private final QName sourceNodeTypeQName;
|
||||
private final Set<QName> sourceNodeAspectQNames;
|
||||
private final Map<QName, Serializable> sourceNodeProperties;
|
||||
private final NodeRef targetParentNodeRef;
|
||||
private final NodeRef targetNodeRef;
|
||||
private final boolean targetNodeIsNew;
|
||||
private final QName assocTypeQName;
|
||||
private final QName assocQName;
|
||||
|
||||
public CopyDetails(
|
||||
NodeRef sourceNodeRef,
|
||||
QName sourceNodeTypeQName,
|
||||
Set<QName> sourceNodeAspectQNames,
|
||||
Map<QName, Serializable> sourceNodeProperties,
|
||||
NodeRef targetParentNodeRef,
|
||||
NodeRef targetNodeRef,
|
||||
boolean targetNodeIsNew,
|
||||
QName assocTypeQName,
|
||||
QName assocQName)
|
||||
{
|
||||
this.sourceNodeRef = sourceNodeRef;
|
||||
this.sourceNodeTypeQName = sourceNodeTypeQName;
|
||||
this.sourceNodeAspectQNames = sourceNodeAspectQNames;
|
||||
this.sourceNodeProperties = sourceNodeProperties;
|
||||
this.targetParentNodeRef = targetParentNodeRef;
|
||||
this.targetNodeRef = targetNodeRef;
|
||||
this.targetNodeIsNew = targetNodeIsNew;
|
||||
this.assocTypeQName = assocTypeQName;
|
||||
this.assocQName = assocQName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder(512);
|
||||
sb.append("CopyDetails")
|
||||
.append(" [source=").append(sourceNodeRef)
|
||||
.append(", targetParent=").append(targetParentNodeRef)
|
||||
.append(", targetNode=").append(targetNodeRef)
|
||||
.append(", targetNodeIsNew=").append(targetNodeIsNew)
|
||||
.append(", assocTypeQName=").append(assocTypeQName)
|
||||
.append(", assocQName=").append(assocQName)
|
||||
.append("]");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the source node
|
||||
*/
|
||||
public final NodeRef getSourceNodeRef()
|
||||
{
|
||||
return sourceNodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type of the source node
|
||||
*/
|
||||
public final QName getSourceNodeTypeQName()
|
||||
{
|
||||
return this.sourceNodeTypeQName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the aspects associated with the source node
|
||||
*/
|
||||
public final Set<QName> getSourceNodeAspectQNames()
|
||||
{
|
||||
return sourceNodeAspectQNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the properties associated with the source node
|
||||
*/
|
||||
public final Map<QName, Serializable> getSourceNodeProperties()
|
||||
{
|
||||
return sourceNodeProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node under which the new/existing copy will be placed
|
||||
*/
|
||||
public final NodeRef getTargetParentNodeRef()
|
||||
{
|
||||
return targetParentNodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node to which the copy will occur. The node may not
|
||||
* <i>yet</i> exist.
|
||||
*/
|
||||
public final NodeRef getTargetNodeRef()
|
||||
{
|
||||
return targetNodeRef;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the {@link #getTargetNodeRef() target node} was newly-created
|
||||
* for the copy or if it pre-existed.
|
||||
*
|
||||
* @return <tt>true</tt> if the node was created by the copy
|
||||
*/
|
||||
public final boolean isTargetNodeIsNew()
|
||||
{
|
||||
return targetNodeIsNew;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new association type qualified name
|
||||
*/
|
||||
public final QName getAssocTypeQName()
|
||||
{
|
||||
return assocTypeQName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the association path qualified name
|
||||
*/
|
||||
public final QName getAssocQName()
|
||||
{
|
||||
return assocQName;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -27,61 +27,125 @@ package org.alfresco.repo.copy;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.policy.ClassPolicy;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* @author Roy Wetherall
|
||||
* Policies for the CopyService.
|
||||
* <p>
|
||||
* A typical registration and invocation would look like this:
|
||||
* <code><pre>
|
||||
* public void init()
|
||||
* {
|
||||
* this.policyComponent.bindClassBehaviour(
|
||||
* QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
* ActionModel.ASPECT_ACTIONS,
|
||||
* new JavaBehaviour(this, "getCopyCallback"));
|
||||
* this.policyComponent.bindClassBehaviour(
|
||||
* QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
||||
* ActionModel.ASPECT_ACTIONS,
|
||||
* new JavaBehaviour(this, "onCopyComplete"));
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
* {
|
||||
* return new XyzAspectCopyBehaviourCallback();
|
||||
* }
|
||||
*
|
||||
* private static class XyzAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
* {
|
||||
* // Override methods any to achieve the desired behaviour
|
||||
*
|
||||
* public boolean mustCopyChildAssociation(QName classQName, CopyDetails copyDetails, ChildAssociationRef childAssocRef)
|
||||
* {
|
||||
* ...
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* public void onCopyComplete(
|
||||
* NodeRef sourceNodeRef,
|
||||
* NodeRef targetNodeRef,
|
||||
* boolean copyToNewNode,
|
||||
* Map<NodeRef,NodeRef> copyMap)
|
||||
* {
|
||||
* ...
|
||||
* }
|
||||
* </pre></code>
|
||||
*
|
||||
* @author Derek Hulley
|
||||
*/
|
||||
public interface CopyServicePolicies
|
||||
{
|
||||
/**
|
||||
* Policy invoked when a <b>node</b> is copied
|
||||
*/
|
||||
public interface OnCopyNodePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
* Policy invoked when a <b>node</b> is copied.
|
||||
* <p>
|
||||
* <b>Note:</b> Copy policies are used to modify the copy behaviour. Rather than attempt to
|
||||
* determine, up front, the behaviour that applies for all types and aspects,
|
||||
* the callbacks are used to lazily adjust the behaviour.
|
||||
* <p>
|
||||
* Implementing this policy is particularly important if aspects want to partake in the copy process.
|
||||
* The behaviour can change whether or not the aspect is copied and which of the properties to carry
|
||||
* to the new node.
|
||||
* <p>
|
||||
* If no behaviour is registered or no callback is given, then
|
||||
* the {@link DefaultCopyBehaviourCallback default behaviour} is assumed. Several pre-defined behaviours
|
||||
* exist to simplify the callbacks, including:
|
||||
* <ul>
|
||||
* <li>Do nothing: {@link DoNothingCopyBehaviourCallback}</li>
|
||||
* <li>Default: {@link DefaultCopyBehaviourCallback}</li>
|
||||
* </ul>
|
||||
* The {@link DefaultCopyBehaviourCallback} is probably the best starting point for further
|
||||
* callback implementations; overriding the class allows the behaviour to be overridden, provided
|
||||
* that this policy method is implemented.
|
||||
* <p>
|
||||
* <b>Note: </b> A 'class' is either a type or an aspect.
|
||||
*/
|
||||
public interface OnCopyNodePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
* @param classRef the type of node being copied
|
||||
* @param sourceNodeRef node being copied
|
||||
* @param destinationStoreRef the destination store reference
|
||||
* @param copyToNewNode indicates whether we are copying to a new node or not
|
||||
* @param copyDetails modifiable <b>node</b> details
|
||||
* Called for all types and aspects before copying a node.
|
||||
*
|
||||
* @deprecated <b>WARNING:</b> This method will be replaced with a more
|
||||
* flexible and future-proof policy callback in the post-3.1 / Labs 3D code.
|
||||
* @param classRef the type or aspect qualified name
|
||||
* @param copyDetails the details of the impending copy
|
||||
* @return Return the callback that will be used to modify the copy behaviour for this
|
||||
* dictionary class. Return <tt>null</tt> to assume the default copy the helper to carry information back to the Copy Service. If this is not used, then
|
||||
* neither the aspect nor any of its properties will be copied.
|
||||
*
|
||||
* @see CopyServicePolicies
|
||||
*
|
||||
* @since V3.2
|
||||
*/
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails);
|
||||
|
||||
static Arg ARG_0 = Arg.KEY;
|
||||
static Arg ARG_1 = Arg.KEY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final callback after the copy (including any cascading) has been completed. This should
|
||||
* be used where post-copy manipulation of nodes is required in order to enforce adherence
|
||||
* to a particular dictionary or business model.
|
||||
* <p>
|
||||
* The copy map contains all the nodes created during the copy, this helps to re-map
|
||||
* any potentially relative associations.
|
||||
*/
|
||||
public interface OnCopyCompletePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
* @param classRef the type of the node that was copied
|
||||
* @param sourceNodeRef the origional node
|
||||
* @param targetNodeRef the destination node
|
||||
* @param copyMap a map containing all the nodes that have been created during the copy
|
||||
*/
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef targetNodeRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails);
|
||||
}
|
||||
|
||||
/**
|
||||
* Policy invoked when the copy operation invoked on a <b>node</b> is complete.
|
||||
* <p>
|
||||
* The copy map contains all the nodes created during the copy, this helps to re-map
|
||||
* any potentially relative associations.
|
||||
*/
|
||||
public interface OnCopyCompletePolicy extends ClassPolicy
|
||||
{
|
||||
/**
|
||||
* @param classRef the type of the node that was copied
|
||||
* @param sourceNodeRef the origional node
|
||||
* @param destinationRef the destination node
|
||||
* @param copyMap a map containing all the nodes that have been created during the copy
|
||||
*/
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyToNewNode,
|
||||
Map<NodeRef, NodeRef> copyMap);
|
||||
|
||||
static Arg ARG_0 = Arg.KEY;
|
||||
Map<NodeRef, NodeRef> copyMap);
|
||||
|
||||
static Arg ARG_0 = Arg.KEY;
|
||||
static Arg ARG_1 = Arg.KEY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
|
||||
/**
|
||||
* The default behaviour that a type of aspect implements if there is no associated
|
||||
* <{@link CopyBehaviourCallback behaviour}.
|
||||
* <p>
|
||||
* This implementation is {@link #getInstance() stateless} and therefore thread-safe.
|
||||
* <p>
|
||||
* The default behaviour is:
|
||||
* <ul>
|
||||
* <li><b>Must Copy:</b> YES</li>
|
||||
* <li><b>Must Cascade:</b> YES, if cascade is on</li>
|
||||
* <li><b>Properties to Copy:</b>ALL</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class DefaultCopyBehaviourCallback extends AbstractCopyBehaviourCallback
|
||||
{
|
||||
private static CopyBehaviourCallback instance = new DefaultCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* @return Returns a stateless singleton
|
||||
*/
|
||||
public static CopyBehaviourCallback getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behaviour: Always copy
|
||||
*
|
||||
* @return Returns <tt>true</tt> always
|
||||
*/
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behaviour: Cascade if we are copying children <b>AND</b> the association is primary
|
||||
*
|
||||
* @return Returns <tt>true</tt> if the association is primary and <code>copyChildren == true</code>
|
||||
*/
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
if (!childAssocCopyDetails.isCopyChildren())
|
||||
{
|
||||
return ChildAssocCopyAction.IGNORE;
|
||||
}
|
||||
if (childAssocCopyDetails.getChildAssocRef().isPrimary())
|
||||
{
|
||||
return ChildAssocCopyAction.COPY_CHILD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ChildAssocCopyAction.COPY_ASSOC;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default behaviour: Copy all associated properties
|
||||
*
|
||||
* @return Returns all the properties passes in
|
||||
*/
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties)
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.copy;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Simple <i>copy behaviour</i> to prevent any copying.
|
||||
* <p>
|
||||
* This implementation is {@link #getInstance() stateless} and therefore thread-safe.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class DoNothingCopyBehaviourCallback extends AbstractCopyBehaviourCallback
|
||||
{
|
||||
private static CopyBehaviourCallback instance = new DoNothingCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* @return Returns a stateless singleton
|
||||
*/
|
||||
public static CopyBehaviourCallback getInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns <tt>false</tt> always
|
||||
*/
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns <tt>false</tt> always
|
||||
*/
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
return ChildAssocCopyAction.IGNORE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns an empty map always
|
||||
*/
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties)
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -22,97 +22,88 @@
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
|
||||
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.alfresco.i18n.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.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.coci.CheckOutCheckInServiceException;
|
||||
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.cmr.repository.StoreRef;
|
||||
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;
|
||||
|
||||
public class DiscussableAspect
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* Policy component
|
||||
*/
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
/**
|
||||
* The node service
|
||||
*/
|
||||
private NodeService nodeService;
|
||||
|
||||
/**
|
||||
* The file folder service
|
||||
*/
|
||||
private FileFolderService fileFolderService;
|
||||
|
||||
/**
|
||||
* Sets the policy component
|
||||
*
|
||||
* @param policyComponent the policy component
|
||||
*/
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the node service
|
||||
*
|
||||
* @param nodeService the node service
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the file folder service
|
||||
*
|
||||
* @param fileFolderService the file folder service
|
||||
*/
|
||||
public void setFileFolderService(FileFolderService fileFolderService)
|
||||
public final void setFileFolderService(FileFolderService fileFolderService)
|
||||
{
|
||||
this.fileFolderService = fileFolderService;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialise method
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
// Register copy behaviour for the discussable aspect
|
||||
// All forum-related copy behaviour uses the same copy callback
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
ForumModel.ASPECT_DISCUSSABLE,
|
||||
new JavaBehaviour(this, "onCopy"));
|
||||
|
||||
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,
|
||||
@@ -120,148 +111,267 @@ public class DiscussableAspect
|
||||
}
|
||||
|
||||
/**
|
||||
* onCopy policy behaviour
|
||||
*
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(QName, NodeRef, StoreRef, boolean, PolicyScope)
|
||||
* @return Returns {@link DiscussableAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public void onCopy(
|
||||
QName sourceClassRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
// NOTE: we intentionally don't do anything in here, this stops the discussable
|
||||
// aspect from being added to the new copied node - the behaviour we want.
|
||||
return DiscussableAspectCopyBehaviourCallback.INSTANCE;
|
||||
}
|
||||
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyNewNode,
|
||||
Map<NodeRef, NodeRef> copyMap)
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
// 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 (copyNewNode == false)
|
||||
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)
|
||||
{
|
||||
List<ChildAssociationRef> sourceChildren = this.nodeService.getChildAssocs(sourceNodeRef,
|
||||
ForumModel.ASSOC_DISCUSSION, RegexQNamePattern.MATCH_ALL);
|
||||
|
||||
if (sourceChildren.size() != 1)
|
||||
if (copyDetails.getSourceNodeAspectQNames().contains(ContentModel.ASPECT_WORKING_COPY) &&
|
||||
!copyDetails.isTargetNodeIsNew())
|
||||
{
|
||||
throw new CheckOutCheckInServiceException(
|
||||
"The source node has the discussable aspect but does not have 1 child, it has " +
|
||||
sourceChildren.size() + " children!");
|
||||
// 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());
|
||||
}
|
||||
|
||||
NodeRef sourceForum = sourceChildren.get(0).getChildRef();
|
||||
|
||||
// get the forum for the destination node, it's created if necessary
|
||||
NodeRef destinationForum = getDestinationForum(destinationRef);
|
||||
|
||||
// copy any topics from the source forum to the destination forum
|
||||
int copied = 0;
|
||||
List<ChildAssociationRef> sourceForums = this.nodeService.getChildAssocs(sourceForum);
|
||||
for (ChildAssociationRef childRef : sourceForums)
|
||||
{
|
||||
String topicName = null;
|
||||
NodeRef childNode = childRef.getChildRef();
|
||||
if (this.nodeService.getType(childNode).equals(ForumModel.TYPE_TOPIC))
|
||||
{
|
||||
try
|
||||
{
|
||||
// work out the name for the copied topic
|
||||
String childName = this.nodeService.getProperty(childNode,
|
||||
ContentModel.PROP_NAME).toString();
|
||||
Serializable labelProp = this.nodeService.getProperty(destinationRef,
|
||||
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() + ")";
|
||||
}
|
||||
|
||||
this.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");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves or creates the forum node for the given destination node
|
||||
*
|
||||
* @param destNodeRef The node to get the forum for
|
||||
* @return NodeRef representing the forum
|
||||
* Ensure that the node has a <b>fm:forum</b> child node otherwise create one
|
||||
*/
|
||||
private NodeRef getDestinationForum(NodeRef destNodeRef)
|
||||
public void onAddAspect(NodeRef discussableNodeRef, QName aspectTypeQName)
|
||||
{
|
||||
NodeRef destinationForum = null;
|
||||
String name = (String)this.nodeService.getProperty(
|
||||
discussableNodeRef,
|
||||
ContentModel.PROP_NAME);
|
||||
String forumName = I18NUtil.getMessage("discussion.discussion_for", new Object[] {name});
|
||||
|
||||
if (this.nodeService.hasAspect(destNodeRef, ForumModel.ASPECT_DISCUSSABLE))
|
||||
NodeRef forumNodeRef = getForum(discussableNodeRef);
|
||||
|
||||
if (forumNodeRef == null)
|
||||
{
|
||||
List<ChildAssociationRef> destChildren = this.nodeService.getChildAssocs(destNodeRef,
|
||||
ForumModel.ASSOC_DISCUSSION, RegexQNamePattern.MATCH_ALL);
|
||||
Map<QName, Serializable> forumProps = new HashMap<QName, Serializable>(1);
|
||||
forumProps.put(ContentModel.PROP_NAME, forumName);
|
||||
|
||||
if (destChildren.size() != 1)
|
||||
{
|
||||
throw new IllegalStateException("Locked node has the discussable aspect but does not have 1 child, it has " +
|
||||
destChildren.size() + " children!");
|
||||
}
|
||||
|
||||
destinationForum = destChildren.get(0).getChildRef();
|
||||
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
|
||||
{
|
||||
// create the forum - TODO: Move this to a repo discussion service so that it can
|
||||
// be shared between here and the discussion wizard
|
||||
|
||||
// add the discussable aspect
|
||||
this.nodeService.addAspect(destNodeRef, ForumModel.ASPECT_DISCUSSABLE, null);
|
||||
|
||||
// create a child forum space using the child association just introduced by
|
||||
// adding the discussable aspect
|
||||
String name = (String)this.nodeService.getProperty(destNodeRef,
|
||||
ContentModel.PROP_NAME);
|
||||
String forumName = I18NUtil.getMessage("coci_service.discussion_for", new Object[] {name});
|
||||
|
||||
Map<QName, Serializable> forumProps = new HashMap<QName, Serializable>(1);
|
||||
forumProps.put(ContentModel.PROP_NAME, forumName);
|
||||
|
||||
ChildAssociationRef childRef = this.nodeService.createNode(destNodeRef,
|
||||
ForumModel.ASSOC_DISCUSSION,
|
||||
QName.createQName(NamespaceService.FORUMS_MODEL_1_0_URI, "discussion"),
|
||||
ForumModel.TYPE_FORUM, forumProps);
|
||||
|
||||
destinationForum = childRef.getChildRef();
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// apply the uifacets aspect
|
||||
Map<QName, Serializable> uiFacetsProps = new HashMap<QName, Serializable>(5);
|
||||
uiFacetsProps.put(ApplicationModel.PROP_ICON, "forum");
|
||||
this.nodeService.addAspect(destinationForum, ApplicationModel.ASPECT_UIFACETS, uiFacetsProps);
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
return destinationForum;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -35,7 +36,10 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
@@ -194,11 +198,11 @@ public class LockServiceImpl implements LockService,
|
||||
ContentModel.ASPECT_LOCKABLE,
|
||||
new JavaBehaviour(this, "beforeDeleteNode"));
|
||||
|
||||
// Register onCopy class behaviour
|
||||
// Register copy class behaviour
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_LOCKABLE,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
|
||||
// Register the onCreateVersion behavior for the version aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
@@ -542,22 +546,34 @@ public class LockServiceImpl implements LockService,
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCopy behaviour implementation for the lock aspect.
|
||||
* <p>
|
||||
* Ensures that the propety values of the lock aspect are not copied onto
|
||||
* the destination node.
|
||||
* @return Returns {@link LockableAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
// Add the lock aspect, but do not copy any of the properties
|
||||
copyDetails.addAspect(ContentModel.ASPECT_LOCKABLE);
|
||||
return LockableAspectCopyBehaviourCallback.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the default copy behaviour to prevent copying of lockable properties.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class LockableAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private static final CopyBehaviourCallback INSTANCE = new LockableAspectCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* @return Returns an empty map
|
||||
*/
|
||||
@Override
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName, CopyDetails copyDetails, Map<QName, Serializable> properties)
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that node is not locked.
|
||||
*
|
||||
|
@@ -27,7 +27,10 @@ package org.alfresco.repo.model.ml;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.ContentServicePolicies;
|
||||
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.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
@@ -65,9 +68,9 @@ public class EmptyTranslationAspect implements
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION,
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onContentUpdate"),
|
||||
@@ -94,8 +97,6 @@ public class EmptyTranslationAspect implements
|
||||
|
||||
/**
|
||||
* Copy a <b>cm:mlEmptyTranslation<b> is not permit.
|
||||
*
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope)
|
||||
*/
|
||||
public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails)
|
||||
{
|
||||
@@ -104,8 +105,6 @@ public class EmptyTranslationAspect implements
|
||||
|
||||
/**
|
||||
* If a content is added to a <b>cm:mlEmptyTranslation<b>, remove this aspect.
|
||||
*
|
||||
* @see org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy#onContentUpdate(org.alfresco.service.cmr.repository.NodeRef, boolean)
|
||||
*/
|
||||
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
||||
{
|
||||
@@ -115,4 +114,35 @@ public class EmptyTranslationAspect implements
|
||||
nodeService.removeAspect(nodeRef, ContentModel.ASPECT_TEMPORARY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the NO-OP copy behaviour to generate an exception if copied. In other words,
|
||||
* the presence of {@link ContentModel#ASPECT_MULTILINGUAL_EMPTY_TRANSLATION} should prevent
|
||||
* a node from being copied; if this is not done by the copy client, it is enforced here.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class EmptyTranslationAspectCopyBehaviourCallback extends DoNothingCopyBehaviourCallback
|
||||
{
|
||||
private static final CopyBehaviourCallback INSTANCE = new EmptyTranslationAspectCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException always
|
||||
*/
|
||||
@Override
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
throw new IllegalStateException(
|
||||
"Nodes with " + ContentModel.ASPECT_MULTILINGUAL_EMPTY_TRANSLATION + " may not be copied");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns {@link EmptyTranslationAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
return EmptyTranslationAspectCopyBehaviourCallback.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
@@ -29,15 +29,16 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
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.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.ml.MultilingualContentService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
@@ -71,9 +72,9 @@ public class MultilingualDocumentAspect implements
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_MULTILINGUAL_DOCUMENT,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteNode"),
|
||||
@@ -114,12 +115,12 @@ public class MultilingualDocumentAspect implements
|
||||
|
||||
/**
|
||||
* The copy of a <b>cm:mlDocument</b> can't keep the Multilingual aspect.
|
||||
*
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope)
|
||||
*
|
||||
* @return Returns the {@link DoNothingCopyBehaviourCallback}
|
||||
*/
|
||||
public void onCopyNode(QName classRef, NodeRef sourceNodeRef, StoreRef destinationStoreRef, boolean copyToNewNode, PolicyScope copyDetails)
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
copyDetails.removeAspect(ContentModel.ASPECT_MULTILINGUAL_DOCUMENT);
|
||||
return DoNothingCopyBehaviourCallback.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,8 +145,6 @@ public class MultilingualDocumentAspect implements
|
||||
*
|
||||
* If the locale of a pivot translation is modified, the pivot locale reference of the mlContainer
|
||||
* must be modified too.
|
||||
*
|
||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map)
|
||||
*/
|
||||
public void onUpdateProperties(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after)
|
||||
{
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
* Copyright (C) 2005-2009 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@@ -25,12 +25,12 @@
|
||||
package org.alfresco.repo.node;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
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.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
@@ -63,21 +63,16 @@ public class ReferenceableAspect implements CopyServicePolicies.OnCopyNodePolicy
|
||||
{
|
||||
// disable copy for referencable aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_REFERENCEABLE,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing
|
||||
* @return Returns {@link DoNothingCopyBehaviourCallback}
|
||||
*/
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
// don't copy
|
||||
return DoNothingCopyBehaviourCallback.getInstance();
|
||||
}
|
||||
}
|
||||
|
@@ -25,7 +25,11 @@
|
||||
package org.alfresco.repo.node;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
@@ -63,9 +67,25 @@ public class TemporaryAspect implements CopyServicePolicies.OnCopyNodePolicy
|
||||
{
|
||||
// disable copy for referencable aspect
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_TEMPORARY,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ContentModel#ASPECT_TEMPORARY <b>sys:temporary</b>} aspect is only copied
|
||||
* if the copy is clean i.e. not to an existing node.
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
if (copyDetails.getTargetNodeRef() != null)
|
||||
{
|
||||
return DoNothingCopyBehaviourCallback.getInstance();
|
||||
}
|
||||
else
|
||||
{
|
||||
return DefaultCopyBehaviourCallback.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -35,7 +35,9 @@ import org.alfresco.service.namespace.QName;
|
||||
public interface BehaviourFilter
|
||||
{
|
||||
/**
|
||||
* Disable behaviour for all nodes
|
||||
* Disable behaviour for all nodes.
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param className the type/aspect behaviour to disable
|
||||
* @return true => already disabled
|
||||
@@ -44,6 +46,8 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Disable behaviour for specific node
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param nodeRef the node to disable for
|
||||
* @param className the type/aspect behaviour to disable
|
||||
@@ -53,6 +57,8 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Enable behaviour for all nodes
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param className the type/aspect behaviour to enable
|
||||
*/
|
||||
@@ -60,6 +66,8 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Enable behaviour for specific node
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param nodeRef the node to enable for
|
||||
* @param className the type/aspect behaviour to enable
|
||||
@@ -68,18 +76,25 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Enable all behaviours for specific node
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param nodeRef the node to enable for
|
||||
*/
|
||||
public void enableBehaviours(NodeRef nodeRef);
|
||||
|
||||
/**
|
||||
* Enable all behaviours
|
||||
* Enable all behaviours i.e. undo all disable calls - both at the
|
||||
* node and class level.
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*/
|
||||
public void enableAllBehaviours();
|
||||
|
||||
/**
|
||||
* Determine if behaviour is enabled across all nodes.
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param className the behaviour to test for
|
||||
* @return true => behaviour is enabled
|
||||
@@ -88,10 +103,12 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Determine if behaviour is enabled for specific node.
|
||||
*
|
||||
* <p>
|
||||
* Note: A node behaviour is enabled only when:
|
||||
* a) the behaviour is not disabled across all nodes
|
||||
* b) the behaviour is not disabled specifically for the provided node
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @param nodeRef the node to test for
|
||||
* @param className the behaviour to test for
|
||||
@@ -101,6 +118,8 @@ public interface BehaviourFilter
|
||||
|
||||
/**
|
||||
* Determine if any behaviours have been disabled?
|
||||
* <p>
|
||||
* The change applies <b>ONLY</b> to the current trasaction.
|
||||
*
|
||||
* @return true => behaviours have been filtered
|
||||
*/
|
||||
|
@@ -25,25 +25,26 @@
|
||||
package org.alfresco.repo.policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.repo.tenant.TenantService;
|
||||
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
|
||||
/**
|
||||
* Implementation of Behaviour Filter.
|
||||
* Implementation of Behaviour Filter. All methods operate on transactionally-bound
|
||||
* resources. Behaviour will therefore never span transactions; the filter state has
|
||||
* the same lifespan as the transaction in which it was created.
|
||||
*
|
||||
* @author David Caruana
|
||||
*/
|
||||
public class BehaviourFilterImpl implements BehaviourFilter
|
||||
{
|
||||
// Thread local storage of filters
|
||||
ThreadLocal<List<QName>> classFilter = new ThreadLocal<List<QName>>();
|
||||
ThreadLocal<Map<NodeRef,List<QName>>> nodeRefFilter = new ThreadLocal<Map<NodeRef,List<QName>>>();
|
||||
private static final String KEY_CLASS_FILTER = "BehaviourFilterImpl.classFilter";
|
||||
private static final String KEY_NODEREF_FILTER = "BehaviourFilterImpl.nodeRefFilter";
|
||||
|
||||
// Dictionary Service
|
||||
private DictionaryService dictionaryService;
|
||||
@@ -67,43 +68,27 @@ public class BehaviourFilterImpl implements BehaviourFilter
|
||||
this.tenantService = tenantService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#disableBehaviour(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean disableBehaviour(QName className)
|
||||
{
|
||||
List<QName> classNames = classFilter.get();
|
||||
if (classNames == null)
|
||||
{
|
||||
classNames = new ArrayList<QName>();
|
||||
classFilter.set(classNames);
|
||||
}
|
||||
boolean alreadyDisabled = classNames.contains(className);
|
||||
List<QName> classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER);
|
||||
boolean alreadyDisabled = classFilters.contains(className);
|
||||
if (!alreadyDisabled)
|
||||
{
|
||||
classNames.add(className);
|
||||
classFilters.add(className);
|
||||
}
|
||||
return alreadyDisabled;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#disableBehaviour(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean disableBehaviour(NodeRef nodeRef, QName className)
|
||||
{
|
||||
nodeRef = tenantService.getName(nodeRef);
|
||||
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters == null)
|
||||
{
|
||||
filters = new HashMap<NodeRef,List<QName>>();
|
||||
nodeRefFilter.set(filters);
|
||||
}
|
||||
List<QName> classNames = filters.get(nodeRef);
|
||||
Map<NodeRef,List<QName>> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
List<QName> classNames = nodeRefFilters.get(nodeRef);
|
||||
if (classNames == null)
|
||||
{
|
||||
classNames = new ArrayList<QName>();
|
||||
filters.put(nodeRef, classNames);
|
||||
nodeRefFilters.put(nodeRef, classNames);
|
||||
}
|
||||
boolean alreadyDisabled = classNames.contains(className);
|
||||
if (!alreadyDisabled)
|
||||
@@ -113,69 +98,42 @@ public class BehaviourFilterImpl implements BehaviourFilter
|
||||
return alreadyDisabled;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviour(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public void enableBehaviour(QName className)
|
||||
{
|
||||
List<QName> classNames = classFilter.get();
|
||||
if (classNames != null)
|
||||
{
|
||||
classNames.remove(className);
|
||||
}
|
||||
List<QName> classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER);
|
||||
classFilters.remove(className);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviour(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public void enableBehaviour(NodeRef nodeRef, QName className)
|
||||
{
|
||||
nodeRef = tenantService.getName(nodeRef);
|
||||
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
Map<NodeRef,List<QName>> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
List<QName> classNames = nodeRefFilters.get(nodeRef);
|
||||
if (classNames != null)
|
||||
{
|
||||
List<QName> classNames = filters.get(nodeRef);
|
||||
if (classNames != null)
|
||||
{
|
||||
classNames.remove(className);
|
||||
}
|
||||
classNames.remove(className);
|
||||
if (classNames.size() == 0)
|
||||
{
|
||||
filters.remove(nodeRef);
|
||||
nodeRefFilters.remove(nodeRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableBehaviours(org.alfresco.service.cmr.repository.NodeRef)
|
||||
*/
|
||||
public void enableBehaviours(NodeRef nodeRef)
|
||||
{
|
||||
nodeRef = tenantService.getName(nodeRef);
|
||||
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
{
|
||||
filters.remove(nodeRef);
|
||||
}
|
||||
Map<NodeRef,List<QName>> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
nodeRefFilters.remove(nodeRef);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#enableAllBehaviours()
|
||||
*/
|
||||
public void enableAllBehaviours()
|
||||
{
|
||||
Map<NodeRef,List<QName>> filters = nodeRefFilter.get();
|
||||
if (filters != null)
|
||||
{
|
||||
filters.clear();
|
||||
}
|
||||
Map<NodeRef,List<QName>> filters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
filters.clear();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isEnabled(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean isEnabled(NodeRef nodeRef, QName className)
|
||||
{
|
||||
// check global filters
|
||||
@@ -187,46 +145,16 @@ public class BehaviourFilterImpl implements BehaviourFilter
|
||||
nodeRef = tenantService.getName(nodeRef);
|
||||
|
||||
// check node level filters
|
||||
Map<NodeRef,List<QName>> nodeFilters = nodeRefFilter.get();
|
||||
if (nodeFilters != null)
|
||||
Map<NodeRef,List<QName>> filters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
List<QName> nodeClassFilters = filters.get(nodeRef);
|
||||
if (nodeClassFilters != null)
|
||||
{
|
||||
List<QName> nodeClassFilters = nodeFilters.get(nodeRef);
|
||||
if (nodeClassFilters != null)
|
||||
{
|
||||
boolean filtered = nodeClassFilters.contains(className);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (QName filterName : nodeClassFilters)
|
||||
{
|
||||
filtered = dictionaryService.isSubClass(className, filterName);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isEnabled(org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public boolean isEnabled(QName className)
|
||||
{
|
||||
// check global class filters
|
||||
List<QName> classFilters = classFilter.get();
|
||||
if (classFilters != null)
|
||||
{
|
||||
boolean filtered = classFilters.contains(className);
|
||||
boolean filtered = nodeClassFilters.contains(className);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (QName filterName : classFilters)
|
||||
for (QName filterName : nodeClassFilters)
|
||||
{
|
||||
filtered = dictionaryService.isSubClass(className, filterName);
|
||||
if (filtered)
|
||||
@@ -235,18 +163,35 @@ public class BehaviourFilterImpl implements BehaviourFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isEnabled(QName className)
|
||||
{
|
||||
// check global class filters
|
||||
List<QName> classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER);
|
||||
boolean filtered = classFilters.contains(className);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (QName classFilter : classFilters)
|
||||
{
|
||||
filtered = dictionaryService.isSubClass(className, classFilter);
|
||||
if (filtered)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.repo.policy.BehaviourFilter#isActivated()
|
||||
*/
|
||||
public boolean isActivated()
|
||||
{
|
||||
List<QName> classFilters = classFilter.get();
|
||||
Map<NodeRef,List<QName>> nodeFilters = nodeRefFilter.get();
|
||||
return (classFilters != null && !classFilters.isEmpty()) || (nodeFilters != null && !nodeFilters.isEmpty());
|
||||
List<QName> classFilters = TransactionalResourceHelper.getList(KEY_CLASS_FILTER);
|
||||
Map<NodeRef,List<QName>> nodeRefFilters = TransactionalResourceHelper.getMap(KEY_NODEREF_FILTER);
|
||||
return (!classFilters.isEmpty()) || (!nodeRefFilters.isEmpty());
|
||||
}
|
||||
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -24,149 +24,201 @@
|
||||
*/
|
||||
package org.alfresco.repo.rule;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.policy.Behaviour;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.policy.BehaviourFilter;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.rule.RuleService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.util.PropertyCheck;
|
||||
|
||||
/**
|
||||
* Class containing behaviour for the rules aspect
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
public class RulesAspect
|
||||
public class RulesAspect implements
|
||||
CopyServicePolicies.OnCopyNodePolicy,
|
||||
CopyServicePolicies.OnCopyCompletePolicy
|
||||
{
|
||||
private Behaviour onAddAspectBehaviour;
|
||||
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
private PolicyComponent policyComponent;
|
||||
private BehaviourFilter behaviourFilter;
|
||||
private RuleService ruleService;
|
||||
private NodeService nodeService;
|
||||
|
||||
private NodeService nodeService;
|
||||
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
|
||||
{
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
public void setRuleService(RuleService ruleService)
|
||||
public void setRuleService(RuleService ruleService)
|
||||
{
|
||||
this.ruleService = ruleService;
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
RuleModel.ASPECT_RULES,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
||||
RuleModel.ASPECT_RULES,
|
||||
new JavaBehaviour(this, "onCopyComplete"));
|
||||
|
||||
this.onAddAspectBehaviour = new JavaBehaviour(this, "onAddAspect");
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
public void init()
|
||||
{
|
||||
PropertyCheck.mandatory(this, "policyComponent", policyComponent);
|
||||
PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter);
|
||||
PropertyCheck.mandatory(this, "ruleService", ruleService);
|
||||
PropertyCheck.mandatory(this, "nodeService", nodeService);
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
RuleModel.ASPECT_RULES,
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyComplete"),
|
||||
RuleModel.ASPECT_RULES,
|
||||
new JavaBehaviour(this, "onCopyComplete"));
|
||||
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
RuleModel.ASPECT_RULES,
|
||||
onAddAspectBehaviour);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to diable the on add aspect policy behaviour. Helpful when importing,
|
||||
* copying and other bulk respstorative operations.
|
||||
*
|
||||
* TODO will eventually be redundant when policies can be enabled/diabled in the
|
||||
* policy componenet
|
||||
*/
|
||||
public void disbleOnAddAspect()
|
||||
{
|
||||
this.onAddAspectBehaviour.disable();
|
||||
new JavaBehaviour(this, "onAddAspect"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to enable the on add aspect policy behaviour. Helpful when importing,
|
||||
* copying and other bulk respstorative operations.
|
||||
*
|
||||
* TODO will eventually be redundant when policies can be enabled/diabled in the
|
||||
* policy componenet
|
||||
* Creates the rules folder below the node
|
||||
*/
|
||||
public void enableOnAddAspect()
|
||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
this.onAddAspectBehaviour.enable();
|
||||
}
|
||||
|
||||
/**
|
||||
* On add aspect policy behaviour
|
||||
* @param nodeRef
|
||||
* @param aspectTypeQName
|
||||
*/
|
||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
this.ruleService.disableRules(nodeRef);
|
||||
try
|
||||
{
|
||||
int count = this.nodeService.getChildAssocs(nodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER).size();
|
||||
int count = this.nodeService.getChildAssocs(nodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER).size();
|
||||
if (count == 0)
|
||||
{
|
||||
this.nodeService.createNode(
|
||||
this.nodeService.createNode(
|
||||
nodeRef,
|
||||
RuleModel.ASSOC_RULE_FOLDER,
|
||||
RuleModel.ASSOC_RULE_FOLDER,
|
||||
ContentModel.TYPE_SYSTEM_FOLDER);
|
||||
RuleModel.ASSOC_RULE_FOLDER,
|
||||
RuleModel.ASSOC_RULE_FOLDER,
|
||||
ContentModel.TYPE_SYSTEM_FOLDER);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.ruleService.enableRules(nodeRef);
|
||||
}
|
||||
}
|
||||
|
||||
public void onCopyNode(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns {@link RulesAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
return new RulesAspectCopyBehaviourCallback(behaviourFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy behaviour for the 'rules' model
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private class RulesAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private final BehaviourFilter behaviourFilter;
|
||||
boolean behaviourDisabled = false;
|
||||
|
||||
private RulesAspectCopyBehaviourCallback(BehaviourFilter behaviourFilter)
|
||||
{
|
||||
this.behaviourFilter = behaviourFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the aspect behaviour for this node
|
||||
*
|
||||
* @return Returns <tt>true</tt>
|
||||
*/
|
||||
@Override
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
NodeRef targetNodeRef = copyDetails.getTargetNodeRef();
|
||||
behaviourFilter.disableBehaviour(targetNodeRef, RuleModel.ASPECT_RULES);
|
||||
// Always copy
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Always copy into rules folders
|
||||
*
|
||||
* @return Returns {@link ChildAssocCopyAction#COPY_CHILD}
|
||||
* for {@link RuleModel#ASSOC_RULE_FOLDER}
|
||||
*/
|
||||
@Override
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
|
||||
if (childAssocRef.getTypeQName().equals(RuleModel.ASSOC_RULE_FOLDER))
|
||||
{
|
||||
return ChildAssocCopyAction.COPY_CHILD;
|
||||
}
|
||||
else
|
||||
{
|
||||
super.throwExceptionForUnexpectedBehaviour(copyDetails, childAssocCopyDetails.toString());
|
||||
return null; // Never reached
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force copy recursion after copying a rules folder
|
||||
*
|
||||
* @return Returns {@link ChildAssocRecurseAction#FORCE_RECURSE}
|
||||
* for {@link RuleModel#ASSOC_RULE_FOLDER}
|
||||
*/
|
||||
@Override
|
||||
public ChildAssocRecurseAction getChildAssociationRecurseAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
|
||||
if (childAssocRef.getTypeQName().equals(RuleModel.ASSOC_RULE_FOLDER))
|
||||
{
|
||||
return ChildAssocRecurseAction.FORCE_RECURSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
super.throwExceptionForUnexpectedBehaviour(copyDetails, childAssocCopyDetails.toString());
|
||||
return null; // Never reached
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enable aspect behaviour for the source node
|
||||
*/
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
{
|
||||
copyDetails.addAspect(RuleModel.ASPECT_RULES);
|
||||
|
||||
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(
|
||||
sourceNodeRef,
|
||||
RegexQNamePattern.MATCH_ALL,
|
||||
RuleModel.ASSOC_RULE_FOLDER);
|
||||
for (ChildAssociationRef assoc : assocs)
|
||||
{
|
||||
copyDetails.addChildAssociation(classRef, assoc, true);
|
||||
}
|
||||
|
||||
this.onAddAspectBehaviour.disable();
|
||||
}
|
||||
|
||||
public void onCopyComplete(
|
||||
QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationRef,
|
||||
boolean copyToNew,
|
||||
Map<NodeRef, NodeRef> copyMap)
|
||||
{
|
||||
this.onAddAspectBehaviour.enable();
|
||||
}
|
||||
Map<NodeRef, NodeRef> copyMap)
|
||||
{
|
||||
behaviourFilter.enableBehaviour(destinationRef, RuleModel.ASPECT_RULES);
|
||||
}
|
||||
}
|
||||
|
@@ -25,16 +25,19 @@
|
||||
package org.alfresco.repo.thumbnail;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.Behaviour;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.service.cmr.action.Action;
|
||||
import org.alfresco.service.cmr.action.ActionService;
|
||||
import org.alfresco.service.cmr.action.CompositeAction;
|
||||
@@ -45,11 +48,9 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
|
||||
/**
|
||||
@@ -127,9 +128,9 @@ public class ThumbnailedAspect implements NodeServicePolicies.OnUpdateProperties
|
||||
ContentModel.ASPECT_THUMBNAILED,
|
||||
new JavaBehaviour(this, "onUpdateProperties", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_THUMBNAILED,
|
||||
new JavaBehaviour(this, "onCopyNode"));
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,31 +230,70 @@ public class ThumbnailedAspect implements NodeServicePolicies.OnUpdateProperties
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.StoreRef, boolean, org.alfresco.repo.policy.PolicyScope)
|
||||
* @return Returns {@link ThumbnailedAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public void onCopyNode( QName classRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
// Add the automatic update property
|
||||
copyDetails.addProperty(
|
||||
ContentModel.ASPECT_THUMBNAILED,
|
||||
ContentModel.PROP_AUTOMATIC_UPDATE,
|
||||
this.nodeService.getProperty(sourceNodeRef, ContentModel.PROP_AUTOMATIC_UPDATE));
|
||||
return ThumbnailedAspectCopyBehaviourCallback.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Behaviour for the {@link ContentModel#ASPECT_THUMBNAILED <b>cm:thumbnailed</b>} aspect.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class ThumbnailedAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private static final CopyBehaviourCallback INSTANCE = new ThumbnailedAspectCopyBehaviourCallback();
|
||||
|
||||
if (copyToNewNode == true)
|
||||
/**
|
||||
* @return Returns <tt>true</tt> always
|
||||
*/
|
||||
@Override
|
||||
public boolean getMustCopy(QName classQName, CopyDetails copyDetails)
|
||||
{
|
||||
List<ChildAssociationRef> assocs = this.nodeService.getChildAssocs(
|
||||
sourceNodeRef,
|
||||
ContentModel.ASSOC_THUMBNAILS,
|
||||
RegexQNamePattern.MATCH_ALL);
|
||||
for (ChildAssociationRef assoc : assocs)
|
||||
{
|
||||
copyDetails.addChildAssociation(classRef, assoc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy thumbnail-related associations, {@link ContentModel#ASSOC_THUMBNAILS} regardless of
|
||||
* cascade options.
|
||||
*/
|
||||
@Override
|
||||
public ChildAssocCopyAction getChildAssociationCopyAction(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
CopyChildAssociationDetails childAssocCopyDetails)
|
||||
{
|
||||
ChildAssociationRef childAssocRef = childAssocCopyDetails.getChildAssocRef();
|
||||
if (childAssocRef.getTypeQName().equals(ContentModel.ASSOC_THUMBNAILS))
|
||||
{
|
||||
return ChildAssocCopyAction.COPY_CHILD;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException(
|
||||
"Behaviour should have been invoked: \n" +
|
||||
" Aspect: " + this.getClass().getName() + "\n" +
|
||||
" " + childAssocCopyDetails + "\n" +
|
||||
" " + copyDetails);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy only the {@link ContentModel#PROP_AUTOMATIC_UPDATE}
|
||||
*/
|
||||
@Override
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties)
|
||||
{
|
||||
Map<QName, Serializable> newProperties = new HashMap<QName, Serializable>(5);
|
||||
Serializable value = properties.get(ContentModel.PROP_AUTOMATIC_UPDATE);
|
||||
newProperties.put(ContentModel.PROP_AUTOMATIC_UPDATE, value);
|
||||
return newProperties;
|
||||
}
|
||||
// otherwise we don't care about copying the associations over or we will get duplicates
|
||||
}
|
||||
}
|
||||
|
@@ -25,22 +25,25 @@
|
||||
package org.alfresco.repo.version;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.i18n.I18NUtil;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.ContentServicePolicies;
|
||||
import org.alfresco.repo.copy.CopyBehaviourCallback;
|
||||
import org.alfresco.repo.copy.CopyDetails;
|
||||
import org.alfresco.repo.copy.CopyServicePolicies;
|
||||
import org.alfresco.repo.copy.DefaultCopyBehaviourCallback;
|
||||
import org.alfresco.repo.node.NodeServicePolicies;
|
||||
import org.alfresco.repo.policy.Behaviour;
|
||||
import org.alfresco.repo.policy.JavaBehaviour;
|
||||
import org.alfresco.repo.policy.PolicyComponent;
|
||||
import org.alfresco.repo.policy.PolicyScope;
|
||||
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.repository.StoreRef;
|
||||
import org.alfresco.service.cmr.version.Version;
|
||||
import org.alfresco.service.cmr.version.VersionService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
@@ -52,20 +55,21 @@ import org.alfresco.service.namespace.QName;
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
public class VersionableAspect implements ContentServicePolicies.OnContentUpdatePolicy,
|
||||
NodeServicePolicies.OnAddAspectPolicy,
|
||||
NodeServicePolicies.OnRemoveAspectPolicy,
|
||||
NodeServicePolicies.OnDeleteNodePolicy,
|
||||
VersionServicePolicies.AfterCreateVersionPolicy
|
||||
NodeServicePolicies.OnAddAspectPolicy,
|
||||
NodeServicePolicies.OnRemoveAspectPolicy,
|
||||
NodeServicePolicies.OnDeleteNodePolicy,
|
||||
VersionServicePolicies.AfterCreateVersionPolicy,
|
||||
CopyServicePolicies.OnCopyNodePolicy
|
||||
{
|
||||
/** The i18n'ized messages */
|
||||
private static final String MSG_INITIAL_VERSION = "create_version.initial_version";
|
||||
private static final String MSG_AUTO_VERSION = "create_version.auto_version";
|
||||
|
||||
/** Transaction resource key */
|
||||
private static final String KEY_VERSIONED_NODEREFS = "versioned_noderefs";
|
||||
|
||||
/** The i18n'ized messages */
|
||||
private static final String MSG_INITIAL_VERSION = "create_version.initial_version";
|
||||
private static final String MSG_AUTO_VERSION = "create_version.auto_version";
|
||||
|
||||
/** Transaction resource key */
|
||||
private static final String KEY_VERSIONED_NODEREFS = "versioned_noderefs";
|
||||
|
||||
/** The policy component */
|
||||
private PolicyComponent policyComponent;
|
||||
private PolicyComponent policyComponent;
|
||||
|
||||
/** The node service */
|
||||
private NodeService nodeService;
|
||||
@@ -75,26 +79,26 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
|
||||
/** Auto version behaviour */
|
||||
private Behaviour autoVersionBehaviour;
|
||||
|
||||
|
||||
/**
|
||||
* Set the policy component
|
||||
*
|
||||
* @param policyComponent the policy component
|
||||
*/
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
public void setPolicyComponent(PolicyComponent policyComponent)
|
||||
{
|
||||
this.policyComponent = policyComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the version service
|
||||
*
|
||||
* @param versionService the version service
|
||||
*/
|
||||
/**
|
||||
* Set the version service
|
||||
*
|
||||
* @param versionService the version service
|
||||
*/
|
||||
public void setVersionService(VersionService versionService)
|
||||
{
|
||||
this.versionService = versionService;
|
||||
}
|
||||
this.versionService = versionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the node service
|
||||
@@ -109,25 +113,25 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
/**
|
||||
* Initialise the versionable aspect policies
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
public void init()
|
||||
{
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
new JavaBehaviour(this, "onAddAspect", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveAspect"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
new JavaBehaviour(this, "onRemoveAspect", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
new JavaBehaviour(this, "onDeleteNode", Behaviour.NotificationFrequency.TRANSACTION_COMMIT));
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "afterCreateVersion"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "afterCreateVersion"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
new JavaBehaviour(this, "afterCreateVersion", Behaviour.NotificationFrequency.EVERY_EVENT));
|
||||
|
||||
|
||||
autoVersionBehaviour = new JavaBehaviour(this, "onContentUpdate", Behaviour.NotificationFrequency.TRANSACTION_COMMIT);
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
ContentServicePolicies.ON_CONTENT_UPDATE,
|
||||
@@ -136,57 +140,72 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
|
||||
// Register the copy behaviour
|
||||
this.policyComponent.bindClassBehaviour(
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "onCopyNode"),
|
||||
QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"),
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
new JavaBehaviour(this, "onCopy"));
|
||||
}
|
||||
new JavaBehaviour(this, "getCopyCallback"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
|
||||
*/
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
|
||||
{
|
||||
if (isNodeArchived == false)
|
||||
{
|
||||
// If we are perminantly deleting the node then we need to remove the associated version history
|
||||
this.versionService.deleteVersionHistory(childAssocRef.getChildRef());
|
||||
}
|
||||
// otherwise we do nothing since we need to hold onto the version history in case the node is restored later
|
||||
}
|
||||
|
||||
/**
|
||||
* OnCopy behaviour implementation for the version aspect.
|
||||
* <p>
|
||||
* Ensures that the propety values of the version aspect are not copied onto
|
||||
* the destination node.
|
||||
*
|
||||
* @see org.alfresco.repo.copy.CopyServicePolicies.OnCopyNodePolicy#onCopyNode(QName, NodeRef, StoreRef, boolean, PolicyScope)
|
||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnDeleteNodePolicy#onDeleteNode(org.alfresco.service.cmr.repository.ChildAssociationRef, boolean)
|
||||
*/
|
||||
public void onCopy(
|
||||
QName sourceClassRef,
|
||||
NodeRef sourceNodeRef,
|
||||
StoreRef destinationStoreRef,
|
||||
boolean copyToNewNode,
|
||||
PolicyScope copyDetails)
|
||||
public void onDeleteNode(ChildAssociationRef childAssocRef, boolean isNodeArchived)
|
||||
{
|
||||
// Add the version aspect, but do not copy the version label
|
||||
copyDetails.addAspect(ContentModel.ASPECT_VERSIONABLE);
|
||||
copyDetails.addProperty(
|
||||
ContentModel.ASPECT_VERSIONABLE,
|
||||
ContentModel.PROP_AUTO_VERSION,
|
||||
this.nodeService.getProperty(sourceNodeRef, ContentModel.PROP_AUTO_VERSION));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* On add aspect policy behaviour
|
||||
if (isNodeArchived == false)
|
||||
{
|
||||
// If we are perminantly deleting the node then we need to remove the associated version history
|
||||
this.versionService.deleteVersionHistory(childAssocRef.getChildRef());
|
||||
}
|
||||
// otherwise we do nothing since we need to hold onto the version history in case the node is restored later
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the {@link VersionableAspectCopyBehaviourCallback}
|
||||
*/
|
||||
public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails)
|
||||
{
|
||||
return VersionableAspectCopyBehaviourCallback.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy behaviour for the <b>cm:versionable</b> aspect
|
||||
*
|
||||
* @param nodeRef
|
||||
* @param aspectTypeQName
|
||||
*/
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
private static class VersionableAspectCopyBehaviourCallback extends DefaultCopyBehaviourCallback
|
||||
{
|
||||
private static final CopyBehaviourCallback INSTANCE = new VersionableAspectCopyBehaviourCallback();
|
||||
|
||||
/**
|
||||
* Copy the aspect, but only the {@link ContentModel#PROP_AUTO_VERSION} property
|
||||
*/
|
||||
@Override
|
||||
public Map<QName, Serializable> getCopyProperties(
|
||||
QName classQName,
|
||||
CopyDetails copyDetails,
|
||||
Map<QName, Serializable> properties)
|
||||
{
|
||||
Serializable value = properties.get(ContentModel.PROP_AUTO_VERSION);
|
||||
if (value != null)
|
||||
{
|
||||
return Collections.singletonMap(ContentModel.PROP_AUTO_VERSION, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On add aspect policy behaviour
|
||||
*
|
||||
* @param nodeRef
|
||||
* @param aspectTypeQName
|
||||
*/
|
||||
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
if (this.nodeService.exists(nodeRef) == true && aspectTypeQName.equals(ContentModel.ASPECT_VERSIONABLE) == true)
|
||||
{
|
||||
if (this.nodeService.exists(nodeRef) == true && aspectTypeQName.equals(ContentModel.ASPECT_VERSIONABLE) == true)
|
||||
{
|
||||
boolean initialVersion = true;
|
||||
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_INITIAL_VERSION);
|
||||
@@ -210,16 +229,16 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.node.NodeServicePolicies.OnRemoveAspectPolicy#onRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
|
||||
*/
|
||||
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
// When the versionable aspect is removed from a node, then delete the associatied verison history
|
||||
this.versionService.deleteVersionHistory(nodeRef);
|
||||
}
|
||||
public void onRemoveAspect(NodeRef nodeRef, QName aspectTypeQName)
|
||||
{
|
||||
// When the versionable aspect is removed from a node, then delete the associatied verison history
|
||||
this.versionService.deleteVersionHistory(nodeRef);
|
||||
}
|
||||
|
||||
/**
|
||||
* On content update policy bahaviour
|
||||
@@ -227,47 +246,47 @@ public class VersionableAspect implements ContentServicePolicies.OnContentUpdate
|
||||
* @param nodeRef the node reference
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
||||
public void onContentUpdate(NodeRef nodeRef, boolean newContent)
|
||||
{
|
||||
if (this.nodeService.exists(nodeRef) == true && this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == true
|
||||
&& this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)
|
||||
&& this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_TEMPORARY) == false)
|
||||
{
|
||||
Map<NodeRef, NodeRef> versionedNodeRefs = (Map)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
|
||||
if (versionedNodeRefs == null || versionedNodeRefs.containsKey(nodeRef) == false)
|
||||
{
|
||||
// Determine whether the node is auto versionable or not
|
||||
boolean autoVersion = false;
|
||||
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_AUTO_VERSION);
|
||||
if (value != null)
|
||||
{
|
||||
// If the value is not null then
|
||||
autoVersion = value.booleanValue();
|
||||
}
|
||||
// else this means that the default value has not been set and the versionable aspect was applied pre-1.1
|
||||
|
||||
if (autoVersion == true)
|
||||
{
|
||||
// Create the auto-version
|
||||
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(1);
|
||||
versionProperties.put(Version.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_AUTO_VERSION));
|
||||
this.versionService.createVersion(nodeRef, versionProperties);
|
||||
}
|
||||
}
|
||||
Map<NodeRef, NodeRef> versionedNodeRefs = (Map)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
|
||||
if (versionedNodeRefs == null || versionedNodeRefs.containsKey(nodeRef) == false)
|
||||
{
|
||||
// Determine whether the node is auto versionable or not
|
||||
boolean autoVersion = false;
|
||||
Boolean value = (Boolean)this.nodeService.getProperty(nodeRef, ContentModel.PROP_AUTO_VERSION);
|
||||
if (value != null)
|
||||
{
|
||||
// If the value is not null then
|
||||
autoVersion = value.booleanValue();
|
||||
}
|
||||
// else this means that the default value has not been set and the versionable aspect was applied pre-1.1
|
||||
|
||||
if (autoVersion == true)
|
||||
{
|
||||
// Create the auto-version
|
||||
Map<String, Serializable> versionProperties = new HashMap<String, Serializable>(1);
|
||||
versionProperties.put(Version.PROP_DESCRIPTION, I18NUtil.getMessage(MSG_AUTO_VERSION));
|
||||
this.versionService.createVersion(nodeRef, versionProperties);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.repo.version.VersionServicePolicies.OnCreateVersionPolicy#onCreateVersion(org.alfresco.service.namespace.QName, org.alfresco.service.cmr.repository.NodeRef, java.util.Map, org.alfresco.repo.policy.PolicyScope)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterCreateVersion(NodeRef versionableNode, Version version)
|
||||
{
|
||||
Map<NodeRef, NodeRef> versionedNodeRefs = (Map<NodeRef, NodeRef>)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
|
||||
if (versionedNodeRefs == null)
|
||||
{
|
||||
versionedNodeRefs = new HashMap<NodeRef, NodeRef>();
|
||||
AlfrescoTransactionSupport.bindResource(KEY_VERSIONED_NODEREFS, versionedNodeRefs);
|
||||
}
|
||||
versionedNodeRefs.put(versionableNode, versionableNode);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public void afterCreateVersion(NodeRef versionableNode, Version version)
|
||||
{
|
||||
Map<NodeRef, NodeRef> versionedNodeRefs = (Map<NodeRef, NodeRef>)AlfrescoTransactionSupport.getResource(KEY_VERSIONED_NODEREFS);
|
||||
if (versionedNodeRefs == null)
|
||||
{
|
||||
versionedNodeRefs = new HashMap<NodeRef, NodeRef>();
|
||||
AlfrescoTransactionSupport.bindResource(KEY_VERSIONED_NODEREFS, versionedNodeRefs);
|
||||
}
|
||||
versionedNodeRefs.put(versionableNode, versionableNode);
|
||||
}
|
||||
}
|
||||
|
@@ -78,20 +78,20 @@ public interface CopyService
|
||||
* <b>cm:name</b> property and catch the {@link DuplicateChildNodeNameException}.
|
||||
*
|
||||
* @param sourceNodeRef the node reference used as the source of the copy
|
||||
* @param destinationParent the intended parent of the new node
|
||||
* @param destinationAssocTypeQName the type of the new child assoc
|
||||
* @param destinationQName the qualified name of the child association from the
|
||||
* @param targetParentNodeRef the intended parent of the new node
|
||||
* @param assocTypeQName the type of the new child assoc
|
||||
* @param assocQName the qualified name of the child association from the
|
||||
* parent to the new node
|
||||
* @param copyChildren indicates that the children of the node should also be copied
|
||||
* @param copyChildren indicates that the children of the node should also be copied
|
||||
*
|
||||
* @return the new node reference
|
||||
*/
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "destinationParent", "destinationAssocTypeQName", "destinationQName", "copyChildren"})
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "targetParentNodeRef", "assocTypeQName", "assocQName", "copyChildren"})
|
||||
public NodeRef copy(
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationParent,
|
||||
QName destinationAssocTypeQName,
|
||||
QName destinationQName,
|
||||
NodeRef targetParentNodeRef,
|
||||
QName assocTypeQName,
|
||||
QName assocQName,
|
||||
boolean copyChildren);
|
||||
|
||||
/**
|
||||
@@ -100,20 +100,20 @@ public interface CopyService
|
||||
* Ensures the copy name is the same as the origional or is renamed to prevent duplicate names.
|
||||
*
|
||||
* @param sourceNodeRef the node reference used as the source of the copy
|
||||
* @param destinationParent the intended parent of the new node
|
||||
* @param destinationAssocTypeQName the type of the new child assoc
|
||||
* @param destinationQName the qualified name of the child association from the
|
||||
* @param targetParentNodeRef the intended parent of the new node
|
||||
* @param assocTypeQName the type of the new child assoc
|
||||
* @param assocQName the qualified name of the child association from the
|
||||
* parent to the new node
|
||||
* @param copyChildren indicates that the children of the node should also be copied
|
||||
* @param copyChildren indicates that the children of the node should also be copied
|
||||
*
|
||||
* @return the new node reference
|
||||
*/
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "destinationParent", "destinationAssocTypeQName", "destinationQName", "copyChildren"})
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "targetParentNodeRef", "assocTypeQName", "assocQName", "copyChildren"})
|
||||
public NodeRef copyAndRename(
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationParent,
|
||||
QName destinationAssocTypeQName,
|
||||
QName destinationQName,
|
||||
NodeRef targetParentNodeRef,
|
||||
QName assocTypeQName,
|
||||
QName assocQName,
|
||||
boolean copyChildren);
|
||||
|
||||
/**
|
||||
@@ -122,18 +122,18 @@ public interface CopyService
|
||||
* @see CopyService#copy(NodeRef, NodeRef, QName, QName, boolean)
|
||||
*
|
||||
* @param sourceNodeRef the node reference used as the source of the copy
|
||||
* @param destinationParent the intended parent of the new node
|
||||
* @param destinationAssocTypeQName the type of the new child assoc
|
||||
* @param destinationQName the qualified name of the child association from the
|
||||
* @param targetParentNodeRef the intended parent of the new node
|
||||
* @param assocTypeQName the type of the new child assoc
|
||||
* @param assocQName the qualified name of the child association from the
|
||||
* parent to the new node
|
||||
* @return the new node reference
|
||||
*/
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "destinationParent", "destinationAssocTypeQName", "destinationQName"})
|
||||
@Auditable(key = Auditable.Key.ARG_0, parameters = {"sourceNodeRef", "targetParentNodeRef", "assocTypeQName", "assocQName"})
|
||||
public NodeRef copy(
|
||||
NodeRef sourceNodeRef,
|
||||
NodeRef destinationParent,
|
||||
QName destinationAssocTypeQName,
|
||||
QName destinationQName);
|
||||
NodeRef targetParentNodeRef,
|
||||
QName assocTypeQName,
|
||||
QName assocQName);
|
||||
|
||||
/**
|
||||
* Copies the state of one node on top of another.
|
||||
|
Reference in New Issue
Block a user