/*
* #%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;
import java.util.Date;
import java.util.List;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.RegexQNamePattern;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* @author Roy Wetherall
*/
public abstract class RMDispositionActionExecuterAbstractBase extends RMActionExecuterAbstractBase
{
/** I18N */
private static final String MSG_RECORD_NOT_DECLARED = "rm.action.record-not-declared";
private static final String MSG_EXPECTED_RECORD_LEVEL = "rm.action.expected-record-level";
private static final String MSG_NOT_ALL_RECORDS_DECLARED = "rm.action.not-all-records-declared";
private static final String MSG_NOT_ELIGIBLE = "rm.action.not-eligible";
private static final String MSG_NO_DISPOITION_INSTRUCTIONS = "rm.action.no-disposition-instructions";
private static final String MSG_NO_DIS_LIFECYCLE_SET = "rm.action.no-disposition-lisfecycle-set";
private static final String MSG_NEXT_DISP_NOT_SET = "rm.action.next-disp-not-set";
private static final String MSG_NOT_NEXT_DISP = "rm.action.not-next-disp";
private static final String MSG_NOT_RECORD_FOLDER = "rm.action.not-record-folder";
/** Parameter value indicating whether we should be doing non-error raising state checks */
public static final String PARAM_NO_ERROR_CHECK = "rm.no-error-check";
/**
* All children of this implementation are disposition actions.
*
* @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#isDispositionAction()
*/
@Override
public boolean isDispositionAction()
{
return true;
}
/**
* Indicates whether the disposition is marked complete
*
* @return <code>true</code> if marked complete, <code>false</code> otherwise
*/
public boolean getSetDispositionActionComplete()
{
return true;
}
/**
* Indicates whether we should validate the next disposition action is the action we are
* trying to execute.
*
* @return
*/
protected boolean checkNextDispositionAction(NodeRef actionedUponNodeRef)
{
return true;
}
/**
* Indicated whether we should validate the disposition action is eligible or not.
*
* @param actionedUponNodeRef
* @return
*/
protected boolean checkEligibility(NodeRef actionedUponNodeRef)
{
return true;
}
/**
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action,
* org.alfresco.service.cmr.repository.NodeRef)
*/
@Override
protected void executeImpl(Action action, NodeRef actionedUponNodeRef)
{
NodeRef nextDispositionActionNodeRef = getNextDispostionAction(actionedUponNodeRef);
// determine whether we should be raising errors during state checking or not
boolean checkError = true;
Boolean checkErrorValue = (Boolean)action.getParameterValue(PARAM_NO_ERROR_CHECK);
if (checkErrorValue != null)
{
checkError = checkErrorValue.booleanValue();
}
// Check the validity of the action (is it the next action, are we dealing with the correct type of object for
// the disposition level?
DispositionSchedule di = checkDispositionActionExecutionValidity(actionedUponNodeRef, nextDispositionActionNodeRef, checkError);
if (di != null)
{
// Check the eligibility of the action
if (!checkEligibility(actionedUponNodeRef) ||
getDispositionService().isNextDispositionActionEligible(actionedUponNodeRef))
{
if (di.isRecordLevelDisposition())
{
// Check that we do indeed have a record
if (getRecordService().isRecord(actionedUponNodeRef))
{
// Can only execute disposition action on record if declared
if (getRecordService().isDeclared(actionedUponNodeRef))
{
// Indicate that the disposition action is underway
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT, new Date());
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_BY, AuthenticationUtil.getRunAsUser());
// Execute record level disposition
executeRecordLevelDisposition(action, actionedUponNodeRef);
if (getNodeService().exists(nextDispositionActionNodeRef) &&
getSetDispositionActionComplete())
{
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_AT, new Date());
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_BY, AuthenticationUtil.getRunAsUser());
}
}
else
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_RECORD_NOT_DECLARED, getName(), actionedUponNodeRef.toString()));
}
}
else
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_EXPECTED_RECORD_LEVEL, getName(), actionedUponNodeRef.toString()));
}
}
else
{
if (getRecordFolderService().isRecordFolder(actionedUponNodeRef))
{
if (getRecordFolderService().isRecordFolderDeclared(actionedUponNodeRef))
{
// Indicate that the disposition action is underway
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_AT, new Date());
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_STARTED_BY, AuthenticationUtil.getRunAsUser());
executeRecordFolderLevelDisposition(action, actionedUponNodeRef);
// Indicate that the disposition action is compelte
if (getNodeService().exists(nextDispositionActionNodeRef) &&
getSetDispositionActionComplete())
{
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_AT, new Date());
getNodeService().setProperty(nextDispositionActionNodeRef, PROP_DISPOSITION_ACTION_COMPLETED_BY, AuthenticationUtil.getRunAsUser());
}
}
else
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ALL_RECORDS_DECLARED, getName(), actionedUponNodeRef.toString()));
}
}
else
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_RECORD_FOLDER, getName(), actionedUponNodeRef.toString()));
}
}
if (getNodeService().exists(actionedUponNodeRef) && getSetDispositionActionComplete())
{
// Update the disposition schedule
getDispositionService().updateNextDispositionAction(actionedUponNodeRef);
}
}
else
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_ELIGIBLE, getName(), actionedUponNodeRef.toString()));
}
}
}
/**
* @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List)
*/
@Override
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
{
// TODO add the "checkEligibility" parameter
}
/**
* @param action
* @param record
*/
protected abstract void executeRecordLevelDisposition(Action action, NodeRef record);
/**
* @param action
* @param recordFolder
*/
protected abstract void executeRecordFolderLevelDisposition(Action action, NodeRef recordFolder);
/**
* @param nodeRef
* @return
*/
protected DispositionSchedule checkDispositionActionExecutionValidity(NodeRef nodeRef, NodeRef nextDispositionActionNodeRef, boolean throwError)
{
// Check the node has associated disposition instructions
DispositionSchedule di = getDispositionService().getDispositionSchedule(nodeRef);
if (di == null)
{
if (throwError)
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_DISPOITION_INSTRUCTIONS, getName(), nodeRef.toString()));
}
else
{
return null;
}
}
// Check the node has the disposition schedule aspect applied
if (!getNodeService().hasAspect(nodeRef, ASPECT_DISPOSITION_LIFECYCLE))
{
if (throwError)
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NO_DIS_LIFECYCLE_SET, getName(), nodeRef.toString()));
}
else
{
return null;
}
}
if (checkNextDispositionAction(nodeRef))
{
// Check this the next disposition action
NodeRef nextDispositionAction = nextDispositionActionNodeRef;
if (nextDispositionAction == null)
{
if (throwError)
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NEXT_DISP_NOT_SET, getName(), nodeRef.toString()));
}
else
{
return null;
}
}
String actionName = (String) getNodeService().getProperty(nextDispositionAction, PROP_DISPOSITION_ACTION);
if (actionName == null || !actionName.equals(getName()))
{
if (throwError)
{
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_NOT_NEXT_DISP, getName(), nodeRef.toString()));
}
else
{
return null;
}
}
}
return di;
}
/**
* Get the next disposition action node. Null if none present.
*
* @param nodeRef
* the disposable node reference
* @return NodeRef the next disposition action, null if none
*/
private NodeRef getNextDispostionAction(NodeRef nodeRef)
{
NodeRef result = null;
List<ChildAssociationRef> assocs = getNodeService().getChildAssocs(nodeRef, ASSOC_NEXT_DISPOSITION_ACTION, RegexQNamePattern.MATCH_ALL);
if (assocs.size() != 0)
{
result = assocs.get(0).getChildRef();
}
return result;
}
}