/*
* #%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.action.impl;
import static org.apache.commons.lang3.BooleanUtils.isNotTrue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
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.DispositionSchedule;
import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.Period;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Action to implement the consequences of a change to the value of the DispositionActionDefinition
* properties. When these properties are changed on a disposition schedule, then any associated
* disposition actions may need to be updated as a consequence.
*
* @author Neil McErlean
*/
public class BroadcastDispositionActionDefinitionUpdateAction extends RMActionExecuterAbstractBase
{
/** Logger */
private static Log logger = LogFactory.getLog(BroadcastDispositionActionDefinitionUpdateAction.class);
public static final String NAME = "broadcastDispositionActionDefinitionUpdate";
public static final String CHANGED_PROPERTIES = "changedProperties";
private BehaviourFilter behaviourFilter;
public void setBehaviourFilter(BehaviourFilter behaviourFilter)
{
this.behaviourFilter = behaviourFilter;
}
/**
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action,
* org.alfresco.service.cmr.repository.NodeRef)
*/
@SuppressWarnings("unchecked")
@Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef)
{
if (!RecordsManagementModel.TYPE_DISPOSITION_ACTION_DEFINITION.equals(getNodeService().getType(actionedUponNodeRef)))
{
return;
}
List<QName> changedProps = (List<QName>)action.getParameterValue(CHANGED_PROPERTIES);
// Navigate up the containment hierarchy to get the record category grandparent and schedule.
NodeRef dispositionScheduleNode = getNodeService().getPrimaryParent(actionedUponNodeRef).getParentRef();
NodeRef rmContainer = getNodeService().getPrimaryParent(dispositionScheduleNode).getParentRef();
DispositionSchedule dispositionSchedule = getDispositionService().getAssociatedDispositionSchedule(rmContainer);
behaviourFilter.disableBehaviour();
try
{
List<NodeRef> disposableItems = getDispositionService().getDisposableItems(dispositionSchedule);
for (NodeRef disposableItem : disposableItems)
{
updateDisposableItem(dispositionSchedule, disposableItem, actionedUponNodeRef, changedProps);
}
}
finally
{
behaviourFilter.enableBehaviour();
}
}
/**
*
* @param ds
* @param disposableItem
* @param dispositionActionDefinition
* @param changedProps
*/
private void updateDisposableItem(DispositionSchedule ds, NodeRef disposableItem, NodeRef dispositionActionDefinition, List<QName> changedProps)
{
// We need to check that this folder is under the management of the disposition schedule that
// has been updated
DispositionSchedule itemDs = getDispositionService().getDispositionSchedule(disposableItem);
if (itemDs != null &&
itemDs.getNodeRef().equals(ds.getNodeRef()))
{
if (getNodeService().hasAspect(disposableItem, ASPECT_DISPOSITION_LIFECYCLE))
{
// disposition lifecycle already exists for node so process changes
processActionDefinitionChanges(dispositionActionDefinition, changedProps, disposableItem);
}
else
{
// disposition lifecycle does not exist on the node so setup disposition
getDispositionService().updateNextDispositionAction(disposableItem);
}
// update rolled up search information
rollupSearchProperties(disposableItem);
}
}
/**
* Manually update the rolled up search properties
*
* @param disposableItem disposable item
*/
private void rollupSearchProperties(NodeRef disposableItem)
{
DispositionAction da = getDispositionService().getNextDispositionAction(disposableItem);
if (da != null)
{
Map<QName, Serializable> props = getNodeService().getProperties(disposableItem);
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, getNodeService().getProperty(da.getNodeRef(), PROP_DISPOSITION_EVENTS_ELIGIBLE));
DispositionActionDefinition daDefinition = da.getDispositionActionDefinition();
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);
}
List<EventCompletionDetails> events = da.getEventCompletionDetails();
List<String> list = new ArrayList<String>(events.size());
for (EventCompletionDetails event : events)
{
list.add(event.getEventName());
}
props.put(PROP_RS_DISPOSITION_EVENTS, (Serializable)list);
getNodeService().setProperties(disposableItem, props);
}
}
/**
* Processes all the changes applied to the given disposition
* action definition node for the given record or folder node.
*
* @param dispositionActionDef The disposition action definition node
* @param changedProps The set of properties changed on the action definition
* @param recordOrFolder The record or folder the changes potentially need to be applied to
*/
private void processActionDefinitionChanges(NodeRef dispositionActionDef, List<QName> changedProps, NodeRef recordOrFolder)
{
// check that the step being edited is the current step for the folder,
// if not, the change has no effect on the current step so ignore
DispositionAction nextAction = getDispositionService().getNextDispositionAction(recordOrFolder);
if (doesChangedStepAffectNextAction(dispositionActionDef, nextAction))
{
// the change does effect the nextAction for this node
// so go ahead and determine what needs updating
if ((changedProps.contains(PROP_DISPOSITION_PERIOD) || changedProps.contains(PROP_DISPOSITION_PERIOD_PROPERTY))
&& isNotTrue((Boolean) getNodeService().getProperty(nextAction.getNodeRef(), PROP_MANUALLY_SET_AS_OF)))
{
persistPeriodChanges(dispositionActionDef, nextAction);
}
if (changedProps.contains(PROP_DISPOSITION_EVENT) || changedProps.contains(PROP_DISPOSITION_EVENT_COMBINATION))
{
nextAction.refreshEvents();
}
if (changedProps.contains(PROP_DISPOSITION_ACTION_NAME))
{
String action = (String)getNodeService().getProperty(dispositionActionDef, PROP_DISPOSITION_ACTION_NAME);
getNodeService().setProperty(nextAction.getNodeRef(), PROP_DISPOSITION_ACTION, action);
}
}
}
/**
* Determines whether the disposition action definition (step) being
* updated has any effect on the given next action
*
* @param dispositionActionDef The disposition action definition node
* @param nextAction The next disposition action
* @return true if the step change affects the next action
*/
private boolean doesChangedStepAffectNextAction(NodeRef dispositionActionDef,
DispositionAction nextAction)
{
boolean affectsNextAction = false;
if (dispositionActionDef != null && nextAction != null)
{
// check whether the id of the action definition node being changed
// is the same as the id of the next action
String nextActionId = nextAction.getId();
if (dispositionActionDef.getId().equals(nextActionId))
{
affectsNextAction = true;
}
}
return affectsNextAction;
}
/**
* Persists any changes made to the period on the given disposition action
* definition on the given next action.
*
* @param dispositionActionDef The disposition action definition node
* @param nextAction The next disposition action
*/
protected void persistPeriodChanges(NodeRef dispositionActionDef, DispositionAction nextAction)
{
NodeRef dispositionedNode = getNodeService().getPrimaryParent(nextAction.getNodeRef()).getParentRef();
DispositionActionDefinition definition = nextAction.getDispositionActionDefinition();
Date newAsOfDate = getDispositionService().calculateAsOfDate(dispositionedNode, definition, false);
if (logger.isDebugEnabled())
{
logger.debug("Set disposition as of date for next action '" + nextAction.getName() +
"' (" + nextAction.getNodeRef() + ") to: " + newAsOfDate);
}
getNodeService().setProperty(nextAction.getNodeRef(), PROP_DISPOSITION_AS_OF, newAsOfDate);
}
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
// Intentionally empty
}
}