/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* -
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
* -
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* -
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* -
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.module.org_alfresco_module_rm.model.behaviour;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementServiceRegistry;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionDefinition;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionActionImpl;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionScheduleImpl;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordDefinition;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.TransactionalResourceHelper;
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.Period;
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;
/**
* Search behaviour class.
*
* Manages the collapse of data onto the supporting aspect on the record/record folder.
*
* @author Roy Wetherall
* @since 1.0
*/
public class RecordsManagementSearchBehaviour implements RecordsManagementModel
{
/** logger */
private static Log logger = LogFactory.getLog(RecordsManagementSearchBehaviour.class);
/** Policy component */
private PolicyComponent policyComponent;
/** Node service */
private NodeService nodeService;
/** Disposition service */
private DispositionService dispositionService;
/** Records management service registry */
private RecordsManagementServiceRegistry recordsManagementServiceRegistry;
/** Vital record service */
private VitalRecordService vitalRecordService;
/** Record folder service */
private RecordFolderService recordFolderService;
/** Record service*/
private RecordService recordService;
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param dispositionService the disposition service
*/
public void setDispositionService(DispositionService dispositionService)
{
this.dispositionService = dispositionService;
}
/**
* @param policyComponent the policyComponent to set
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* @param recordsManagementServiceRegistry the records management service registry
*/
public void setRecordsManagementServiceRegistry(RecordsManagementServiceRegistry recordsManagementServiceRegistry)
{
this.recordsManagementServiceRegistry = recordsManagementServiceRegistry;
}
/**
* @param vitalRecordService vital record service
*/
public void setVitalRecordService(VitalRecordService vitalRecordService)
{
this.vitalRecordService = vitalRecordService;
}
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/** on add search aspect behaviour */
private JavaBehaviour onAddSearchAspect = new JavaBehaviour(this, "rmSearchAspectAdd", NotificationFrequency.TRANSACTION_COMMIT);
/** disposition action behaviours */
private JavaBehaviour jbDispositionActionCreate = new JavaBehaviour(this, "dispositionActionCreate", NotificationFrequency.TRANSACTION_COMMIT);
private JavaBehaviour jbDispositionActionPropertiesUpdate = new JavaBehaviour(this, "dispositionActionPropertiesUpdate", NotificationFrequency.TRANSACTION_COMMIT);
/** disposition lifecycle behaviours */
private JavaBehaviour jbDispositionLifeCycleAspect = new JavaBehaviour(this, "onAddDispositionLifecycleAspect", NotificationFrequency.TRANSACTION_COMMIT);
/** disposition schedule behaviours */
private JavaBehaviour jbDispositionSchedulePropertiesUpdate = new JavaBehaviour(this, "dispositionSchedulePropertiesUpdate", NotificationFrequency.TRANSACTION_COMMIT);
/** event update behaviours */
private JavaBehaviour jbEventExecutionUpdate = new JavaBehaviour(this, "eventExecutionUpdate", NotificationFrequency.TRANSACTION_COMMIT);
private JavaBehaviour jbEventExecutionDelete = new JavaBehaviour(this, "eventExecutionDelete", NotificationFrequency.TRANSACTION_COMMIT);
/** Array of behaviours related to disposition schedule artifacts */
private JavaBehaviour[] jbDispositionBehaviours =
{
jbDispositionActionCreate,
jbDispositionActionPropertiesUpdate,
jbDispositionSchedulePropertiesUpdate,
jbEventExecutionUpdate,
jbEventExecutionDelete,
jbDispositionLifeCycleAspect
};
/**
* Initialisation method
*/
public void init()
{
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
TYPE_DISPOSITION_ACTION,
jbDispositionActionCreate);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
TYPE_DISPOSITION_ACTION,
jbDispositionActionPropertiesUpdate);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
TYPE_DISPOSITION_SCHEDULE,
jbDispositionSchedulePropertiesUpdate);
this.policyComponent.bindAssociationBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"),
TYPE_DISPOSITION_ACTION,
ASSOC_EVENT_EXECUTIONS,
jbEventExecutionUpdate);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onDeleteNode"),
TYPE_EVENT_EXECUTION,
jbEventExecutionDelete);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ASPECT_RM_SEARCH,
onAddSearchAspect);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ASPECT_DISPOSITION_LIFECYCLE,
jbDispositionLifeCycleAspect);
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ASPECT_RECORD,
new JavaBehaviour(this, "onAddRecordAspect", NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateNode"),
TYPE_RECORD_FOLDER,
new JavaBehaviour(this, "recordFolderCreate", NotificationFrequency.TRANSACTION_COMMIT));
// Vital Records Review Details Rollup
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onAddAspect"),
ASPECT_VITAL_RECORD_DEFINITION,
new JavaBehaviour(this, "vitalRecordDefintionAddAspect", NotificationFrequency.TRANSACTION_COMMIT));
this.policyComponent.bindClassBehaviour(
QName.createQName(NamespaceService.ALFRESCO_URI, "onUpdateProperties"),
ASPECT_VITAL_RECORD_DEFINITION,
new JavaBehaviour(this, "vitalRecordDefintionUpdateProperties", NotificationFrequency.TRANSACTION_COMMIT));
}
/**
* Disabled disposition schedule behaviour
*/
public void disableDispositionScheduleBehaviour()
{
for (JavaBehaviour jb : jbDispositionBehaviours)
{
jb.disable();
}
}
/**
* Enables disposition schedule behaviour
*/
public void enableDispositionScheduleBehaviour()
{
for (JavaBehaviour jb : jbDispositionBehaviours)
{
jb.enable();
}
}
/**
* Ensures the search aspect for the given node is present, complete and correct.
*
* @param recordOrFolder node reference to record or record folder
*/
public void fixupSearchAspect(NodeRef recordOrFolder)
{
// for now only deal with record folders
if (recordFolderService.isRecordFolder(recordOrFolder))
{
// ensure the search aspect is applied
applySearchAspect(recordOrFolder);
// setup the properties relating to the disposition schedule
setupDispositionScheduleProperties(recordOrFolder);
// setup the properties relating to the disposition lifecycle
DispositionAction da = dispositionService.getNextDispositionAction(recordOrFolder);
if (da != null)
{
updateDispositionActionProperties(recordOrFolder, da.getNodeRef());
setupDispositionActionEvents(recordOrFolder, da);
}
// setup the properties relating to the vital record indicator
setVitalRecordDefintionDetails(recordOrFolder);
}
}
/**
* Updates the disposition action properties
*
* @param nodeRef node reference
* @param before value of properties before
* @param after value of properties after
*/
public void dispositionActionPropertiesUpdate(final NodeRef nodeRef, final Map<QName, Serializable> before, final Map<QName, Serializable> after)
{
if (nodeService.exists(nodeRef))
{
AuthenticationUtil.runAs(new RunAsWork<Void>()
{
@Override
public Void doWork()
{
ChildAssociationRef assoc = nodeService.getPrimaryParent(nodeRef);
if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION))
{
// Get the record (or record folder)
NodeRef record = assoc.getParentRef();
// Apply the search aspect
applySearchAspect(record);
// Update disposition properties
updateDispositionActionProperties(record, nodeRef);
}
return null;
}}, AuthenticationUtil.getSystemUserName());
}
}
/**
* Helper method to apply the search aspect
*
* @param nodeRef node reference
*/
private void applySearchAspect(NodeRef nodeRef)
{
onAddSearchAspect.disable();
try
{
if (!nodeService.hasAspect(nodeRef, ASPECT_RM_SEARCH))
{
nodeService.addAspect(nodeRef, ASPECT_RM_SEARCH , null);
if (logger.isDebugEnabled())
{
logger.debug("Added search aspect to node: " + nodeRef);
}
}
}
finally
{
onAddSearchAspect.enable();
}
}
/**
* On add record aspect behaviour implementation
*
* @param nodeRef node reference
* @param aspectTypeQName aspect type qname
*/
public void onAddRecordAspect(final NodeRef nodeRef, final QName aspectTypeQName)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork()
{
if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_RECORD))
{
applySearchAspect(nodeRef);
setupDispositionScheduleProperties(nodeRef);
}
return null;
}
});
}
/**
* On addition of the disposition lifecycle aspect
* @param nodeRef
* @param aspectTypeQName
*/
public void onAddDispositionLifecycleAspect(final NodeRef nodeRef, final QName aspectTypeQName)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork()
{
if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_RECORD))
{
applySearchAspect(nodeRef);
setupDispositionScheduleProperties(nodeRef);
}
return null;
}
});
}
/**
* On create record folder behaviour implmentation
*
* @param childAssocRef child association reference
*/
public void recordFolderCreate(final ChildAssociationRef childAssocRef)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
NodeRef nodeRef = childAssocRef.getChildRef();
if (nodeService.exists(nodeRef))
{
applySearchAspect(nodeRef);
setupDispositionScheduleProperties(nodeRef);
}
return null;
}
});
}
/**
* Helper method to setup the disposition schedule properties
*
* @param recordOrFolder node reference of record or record folder
*/
private void setupDispositionScheduleProperties(NodeRef recordOrFolder)
{
if (!methodCached("setupDispositionScheduleProperties", recordOrFolder))
{
DispositionSchedule ds = dispositionService.getDispositionSchedule(recordOrFolder);
if (ds == null)
{
nodeService.setProperty(recordOrFolder, PROP_RS_HAS_DISPOITION_SCHEDULE, false);
}
else
{
nodeService.setProperty(recordOrFolder, PROP_RS_HAS_DISPOITION_SCHEDULE, true);
setDispositionScheduleProperties(recordOrFolder, ds);
}
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchHasDispositionSchedule for node " + recordOrFolder +
" to: " + (ds != null));
}
}
}
/**
* On disposition action create behaviour implementation
*
* @param childAssocRef child association reference
*/
public void dispositionActionCreate(final ChildAssociationRef childAssocRef)
{
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
NodeRef child = childAssocRef.getChildRef();
if (nodeService.exists(child) &&
childAssocRef.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION))
{
// Get the record (or record folder)
NodeRef record = childAssocRef.getParentRef();
// Apply the search aspect
applySearchAspect(record);
// Update disposition properties
updateDispositionActionProperties(record, childAssocRef.getChildRef());
// Clear the events
nodeService.setProperty(record, PROP_RS_DISPOSITION_EVENTS, null);
}
return null;
}
});
}
/**
* Helper method to determine whether a method has been called in this transaction
* already, or not.
* <P>
* Prevents work if we get unexpected behaviours firing.
*
* @param method method name (can be any unique string)
* @return boolean true if already called in this transaction, false otherwise
*/
private boolean methodCached(String method, NodeRef nodeRef)
{
boolean result = true;
Set<String> methods = TransactionalResourceHelper.getSet("rm.seachrollup.methodCache");
String key = method + "|" + nodeRef;
if (!methods.contains(key))
{
result = false;
methods.add(key);
}
return result;
}
/**
* On update disposition action properties behaviour implementation
*
* @param record record node reference
* @param dispositionAction disposition action
*/
private void updateDispositionActionProperties(NodeRef record, NodeRef dispositionAction)
{
Map<QName, Serializable> props = nodeService.getProperties(record);
DispositionAction da = new DispositionActionImpl(recordsManagementServiceRegistry, dispositionAction);
props.put(PROP_RS_DISPOSITION_ACTION_NAME, da.getName());
props.put(PROP_RS_DISPOSITION_ACTION_AS_OF, da.getAsOfDate());
props.put(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE, nodeService.getProperty(dispositionAction, PROP_DISPOSITION_EVENTS_ELIGIBLE));
DispositionActionDefinition daDefinition = da.getDispositionActionDefinition();
if (daDefinition != null)
{
Period period = daDefinition.getPeriod();
if (period != null)
{
props.put(PROP_RS_DISPOSITION_PERIOD, period.getPeriodType());
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, period.getExpression());
}
else
{
props.put(PROP_RS_DISPOSITION_PERIOD, null);
props.put(PROP_RS_DISPOSITION_PERIOD_EXPRESSION, null);
}
}
nodeService.setProperties(record, props);
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchDispositionActionName for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_NAME));
logger.debug("Set rma:recordSearchDispositionActionAsOf for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_ACTION_AS_OF));
logger.debug("Set rma:recordSearchDispositionEventsEligible for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_EVENTS_ELIGIBLE));
logger.debug("Set rma:recordSearchDispositionPeriod for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD));
logger.debug("Set rma:recordSearchDispositionPeriodExpression for node " + record + " to: " +
props.get(PROP_RS_DISPOSITION_PERIOD_EXPRESSION));
}
}
/**
* On update of event execution information behaviour\
*
* @param childAssocRef child association reference
* @param isNewNode true if a new node, false otherwise
*/
@SuppressWarnings("unchecked")
public void eventExecutionUpdate(ChildAssociationRef childAssocRef, boolean isNewNode)
{
NodeRef dispositionAction = childAssocRef.getParentRef();
NodeRef eventExecution = childAssocRef.getChildRef();
if (nodeService.exists(dispositionAction) &&
nodeService.exists(eventExecution))
{
ChildAssociationRef assoc = nodeService.getPrimaryParent(dispositionAction);
if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION))
{
// Get the record (or record folder)
NodeRef record = assoc.getParentRef();
// Apply the search aspect
applySearchAspect(record);
Collection<String> events = (Collection<String>)nodeService.getProperty(record, PROP_RS_DISPOSITION_EVENTS);
if (events == null)
{
events = new ArrayList<>(1);
}
events.add((String)nodeService.getProperty(eventExecution, PROP_EVENT_EXECUTION_NAME));
nodeService.setProperty(record, PROP_RS_DISPOSITION_EVENTS, (Serializable)events);
}
}
}
/**
* On event execution delete behaviour implementation.
*
* @param childAssocRef child association reference
* @param isNodeArchived true if node is archived on delete, false otherwise
*/
public void eventExecutionDelete(ChildAssociationRef childAssocRef, boolean isNodeArchived)
{
NodeRef dispositionActionNode = childAssocRef.getParentRef();
if (nodeService.exists(dispositionActionNode))
{
ChildAssociationRef assoc = nodeService.getPrimaryParent(dispositionActionNode);
if (assoc.getTypeQName().equals(ASSOC_NEXT_DISPOSITION_ACTION))
{
// Get the record (or record folder)
NodeRef record = assoc.getParentRef();
// Apply the search aspect
applySearchAspect(record);
// make sure the list of events match the action definition
setupDispositionActionEvents(record, dispositionService.getNextDispositionAction(record));
}
}
}
/**
* Helper method to setup disposition action events.
*
* @param nodeRef node reference
* @param da disposition action
*/
private void setupDispositionActionEvents(NodeRef nodeRef, DispositionAction da)
{
if (!methodCached("setupDispositionActionEvents", nodeRef))
{
if (da != null)
{
List<String> eventNames = null;
List<EventCompletionDetails> eventsList = da.getEventCompletionDetails();
if (eventsList.size() > 0)
{
eventNames = new ArrayList<String>(eventsList.size());
for (EventCompletionDetails event : eventsList)
{
eventNames.add(event.getEventName());
}
}
// set the property
nodeService.setProperty(nodeRef, PROP_RS_DISPOSITION_EVENTS, (Serializable)eventNames);
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchDispositionEvents for node " + nodeRef + " to: " + eventNames);
}
}
}
}
/**
* On add search aspect behaviour implementation.
*
* @param nodeRef node reference
* @param aspectTypeQName aspect type qname
*/
public void rmSearchAspectAdd(final NodeRef nodeRef, final QName aspectTypeQName)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork()
{
if (nodeService.exists(nodeRef))
{
// Initialise the search parameteres as required
setVitalRecordDefintionDetails(nodeRef);
}
return null;
}
});
}
/**
* On add aspect vital record defintion behaviour implementation.
*
* @param nodeRef node reference
* @param aspectTypeQName aspect tyep qname
*/
public void vitalRecordDefintionAddAspect(final NodeRef nodeRef, final QName aspectTypeQName)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork()
{
// Only care about record folders
if (nodeService.exists(nodeRef) && recordFolderService.isRecordFolder(nodeRef))
{
updateVitalRecordDefinitionValues(nodeRef);
}
return null;
}
});
}
/**
* On update vital record definition properties behaviour implementation.
*
* @param nodeRef node reference
* @param before before properties
* @param after after properties
*/
public void vitalRecordDefintionUpdateProperties(final NodeRef nodeRef, final Map<QName, Serializable> before, final Map<QName, Serializable> after)
{
AuthenticationUtil.runAsSystem(new AuthenticationUtil.RunAsWork<Void>()
{
@Override
public Void doWork()
{
// Only care about record folders
if (nodeService.exists(nodeRef) && recordFolderService.isRecordFolder(nodeRef))
{
Set<QName> props = new HashSet<QName>(1);
props.add(PROP_REVIEW_PERIOD);
Set<QName> changed = determineChangedProps(before, after);
changed.retainAll(props);
if (!changed.isEmpty())
{
updateVitalRecordDefinitionValues(nodeRef);
}
}
return null;
}
});
}
/**
* Helper method to update the vital record defintion values
*
* @param nodeRef node reference
*/
private void updateVitalRecordDefinitionValues(NodeRef nodeRef)
{
if (!methodCached("updateVitalRecordDefinitionValues", nodeRef))
{
// ensure the folder itself reflects the correct details
applySearchAspect(nodeRef);
setVitalRecordDefintionDetails(nodeRef);
List<NodeRef> records = recordService.getRecords(nodeRef);
for (NodeRef record : records)
{
// Apply the search aspect
applySearchAspect(record);
// Set the vital record definition details
setVitalRecordDefintionDetails(record);
}
}
}
/**
* Helper method to set vital record definition details.
*
* @param nodeRef node reference
*/
private void setVitalRecordDefintionDetails(NodeRef nodeRef)
{
if (!methodCached("setVitalRecordDefinitionDetails", nodeRef))
{
VitalRecordDefinition vrd = vitalRecordService.getVitalRecordDefinition(nodeRef);
if (vrd != null && vrd.isEnabled() && vrd.getReviewPeriod() != null)
{
// Set the property values
nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD, vrd.getReviewPeriod().getPeriodType());
nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION, vrd.getReviewPeriod().getExpression());
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchVitalRecordReviewPeriod for node " + nodeRef + " to: " +
vrd.getReviewPeriod().getPeriodType());
logger.debug("Set rma:recordSearchVitalRecordReviewPeriodExpression for node " + nodeRef + " to: " +
vrd.getReviewPeriod().getExpression());
}
}
else
{
// Clear the vital record properties
nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD, null);
nodeService.setProperty(nodeRef, PROP_RS_VITAL_RECORD_REVIEW_PERIOD_EXPRESSION, null);
}
}
}
/**
* Updates the disposition schedule properties
*
* @param nodeRef node reference
* @param before properties before
* @param after properties after
*/
public void dispositionSchedulePropertiesUpdate(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after)
{
if (nodeService.exists(nodeRef))
{
// create the schedule object and get the record category for it
DispositionSchedule schedule = new DispositionScheduleImpl(recordsManagementServiceRegistry, nodeService, nodeRef);
NodeRef recordCategoryNode = nodeService.getPrimaryParent(schedule.getNodeRef()).getParentRef();
if (schedule.isRecordLevelDisposition())
{
for (NodeRef recordFolder : getRecordFolders(recordCategoryNode))
{
for (NodeRef record : recordService.getRecords(recordFolder))
{
applySearchAspect(record);
setDispositionScheduleProperties(record, schedule);
}
}
}
else
{
for (NodeRef recordFolder : getRecordFolders(recordCategoryNode))
{
applySearchAspect(recordFolder);
setDispositionScheduleProperties(recordFolder, schedule);
}
}
}
}
/**
* Helper method to set disposition schedule properties
*
* @param recordOrFolder node reference
* @param schedule dispostion schedule
*/
private void setDispositionScheduleProperties(NodeRef recordOrFolder, DispositionSchedule schedule)
{
if (schedule != null)
{
nodeService.setProperty(recordOrFolder, PROP_RS_DISPOITION_AUTHORITY, schedule.getDispositionAuthority());
nodeService.setProperty(recordOrFolder, PROP_RS_DISPOITION_INSTRUCTIONS, schedule.getDispositionInstructions());
if (logger.isDebugEnabled())
{
logger.debug("Set rma:recordSearchDispositionAuthority for node " + recordOrFolder + " to: " + schedule.getDispositionAuthority());
logger.debug("Set rma:recordSearchDispositionInstructions for node " + recordOrFolder + " to: " + schedule.getDispositionInstructions());
}
}
}
/**
* This method compares the oldProps map against the newProps map and returns
* a set of QNames of the properties that have changed. Changed here means one of
* <ul>
* <li>the property has been removed</li>
* <li>the property has had its value changed</li>
* <li>the property has been added</li>
* </ul>
*/
private Set<QName> determineChangedProps(Map<QName, Serializable> oldProps, Map<QName, Serializable> newProps)
{
Set<QName> result = new HashSet<QName>();
for (Map.Entry<QName, Serializable> entry : oldProps.entrySet())
{
QName qn = entry.getKey();
if (newProps.get(qn) == null || !newProps.get(qn).equals(entry.getValue()))
{
result.add(qn);
}
}
for (QName qn : newProps.keySet())
{
if (oldProps.get(qn) == null)
{
result.add(qn);
}
}
return result;
}
/**
* Helper method to get the record folders contained in the provided record category.
*
* @param recordCategoryNode record category node reference
* @return List<NodeRef> contained record folders
*/
private List<NodeRef> getRecordFolders(NodeRef recordCategoryNode)
{
List<NodeRef> results = new ArrayList<NodeRef>(8);
List<ChildAssociationRef> folderAssocs = nodeService.getChildAssocs(recordCategoryNode,
ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef folderAssoc : folderAssocs)
{
NodeRef folder = folderAssoc.getChildRef();
if (recordFolderService.isRecordFolder(folder))
{
results.add(folder);
}
}
return results;
}
}