RM-1445m Implemented link-to rule. Also fixed RM-1466 and RM-1470 while refactoring common code

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@68076 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Mark Hibbins
2014-04-25 15:51:57 +00:00
parent 7583392341
commit 706dc33384
9 changed files with 205 additions and 49 deletions

View File

@@ -19,6 +19,8 @@ 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.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.StringUtils;
/**
@@ -27,13 +29,16 @@ import org.springframework.util.StringUtils;
* @author Mark Hibbins
* @since 2.2
*/
public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractBase
public abstract class CopyMoveLinkFileToBaseAction extends RMActionExecuterAbstractBase
{
private static Log logger = LogFactory.getLog(CopyMoveLinkFileToBaseAction.class);
/** action parameters */
public static final String PARAM_DESTINATION_RECORD_FOLDER = "destinationRecordFolder";
public static final String PARAM_PATH = "path";
public static final String PARAM_CREATE_RECORD_PATH = "createRecordPath";
public static final String ACTION_FILETO = "fileTo";
public static final String ACTION_LINKTO = "linkTo";
/** file folder service */
private FileFolderService fileFolderService;
@@ -42,11 +47,11 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
private FilePlanService filePlanService;
/** action modes */
public enum CopyMoveFileToActionMode
public enum CopyMoveLinkFileToActionMode
{
COPY, MOVE
COPY, MOVE, LINK
};
protected CopyMoveFileToActionMode mode;
protected CopyMoveLinkFileToActionMode mode;
/**
* @param fileFolderService file folder service
@@ -80,22 +85,20 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
@Override
protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
{
if (nodeService.exists(actionedUponNodeRef) &&
(freezeService.isFrozen(actionedUponNodeRef) == false) &&
(!ACTION_FILETO.equals(action.getActionDefinitionName()) || !recordService.isFiled(actionedUponNodeRef)) &&
(!(ACTION_FILETO.equals(action.getActionDefinitionName()) && RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER.equals(nodeService.getType(actionedUponNodeRef)))))
String actionName = action.getActionDefinitionName();
if (isOkToProceedWithAction(actionedUponNodeRef, actionName))
{
boolean targetIsUnfiledRecord;
QName actionedUponType = nodeService.getType(actionedUponNodeRef);;
boolean targetIsUnfiledRecords;
if (ACTION_FILETO.equals(action.getActionDefinitionName()))
{
targetIsUnfiledRecord = false;
targetIsUnfiledRecords = false;
}
else
{
QName actionedUponType = nodeService.getType(actionedUponNodeRef);
targetIsUnfiledRecord = (dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && !recordService
.isFiled(actionedUponNodeRef))
|| RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER.equals(actionedUponType);
targetIsUnfiledRecords = (dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && !recordService.isFiled(actionedUponNodeRef))
|| TYPE_UNFILED_RECORD_FOLDER.equals(actionedUponType);
}
// first look to see if the destination record folder has been specified
@@ -103,13 +106,11 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
if (recordFolder == null)
{
// get the reference to the record folder based on the relative path
recordFolder = createOrResolvePath(action, actionedUponNodeRef, targetIsUnfiledRecord);
recordFolder = createOrResolvePath(action, actionedUponNodeRef, targetIsUnfiledRecords);
}
if (recordFolder == null)
{
throw new AlfrescoRuntimeException("Unable to execute file to action, because the destination record folder could not be determined.");
}
// now we have the reference to the target folder we can do some final checks to see if the action is valid
validateActionPostPathResolution(actionedUponNodeRef, recordFolder, actionName, targetIsUnfiledRecords);
final NodeRef finalRecordFolder = recordFolder;
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
@@ -119,19 +120,23 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
{
try
{
if(mode == CopyMoveFileToActionMode.MOVE)
if(mode == CopyMoveLinkFileToActionMode.MOVE)
{
fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
}
else
else if(mode == CopyMoveLinkFileToActionMode.COPY)
{
fileFolderService.copy(actionedUponNodeRef, finalRecordFolder, null);
}
else if(mode == CopyMoveLinkFileToActionMode.LINK)
{
recordService.link(actionedUponNodeRef, finalRecordFolder);
}
}
catch (FileNotFoundException fileNotFound)
{
throw new AlfrescoRuntimeException(
"Unable to execute file to action, because the " + (mode == CopyMoveFileToActionMode.MOVE ? "move" : "copy") + " operation failed.",
"Unable to execute file to action, because the " + (mode == CopyMoveLinkFileToActionMode.MOVE ? "move" : "copy") + " operation failed.",
fileNotFound
);
}
@@ -142,6 +147,83 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
}
}
/**
* Return true if the passed parameters to the action are valid for the given action
*
* @param actionedUponNodeRef
* @param actionName
* @return
*/
private boolean isOkToProceedWithAction(NodeRef actionedUponNodeRef, String actionName)
{
// Check that the incoming parameters are valid prior to performing any action
boolean okToProceed = false;
if(nodeService.exists(actionedUponNodeRef) && !freezeService.isFrozen(actionedUponNodeRef))
{
QName actionedUponType = nodeService.getType(actionedUponNodeRef);
if(ACTION_FILETO.equals(actionName))
{
// file to action can only be performed on unfiled records
okToProceed = !recordService.isFiled(actionedUponNodeRef) && dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT);
if(!okToProceed && logger.isDebugEnabled())
{
logger.debug("Unable to run " + actionName + " action on a node that isn't unfiled and a sub-class of content type");
}
}
else if(ACTION_LINKTO.equals(actionName))
{
// link to action can only be performed on filed records
okToProceed = recordService.isFiled(actionedUponNodeRef) && dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT);
if(!okToProceed && logger.isDebugEnabled())
{
logger.debug("Unable to run " + actionName + " action on a node that isn't filed and a sub-class of content type");
}
}
else
{
okToProceed = true;
}
}
return okToProceed;
}
/**
* Do a final validation for the parameters and the resolve target path
*
* @param actionedUponNodeRef
* @param target
* @param actionName
* @param targetIsUnfiledRecords
*/
private void validateActionPostPathResolution(NodeRef actionedUponNodeRef, NodeRef target, String actionName, boolean targetIsUnfiledRecords)
{
QName actionedUponType = nodeService.getType(actionedUponNodeRef);
// now we have the reference to the target folder we can do some final checks to see if the action is valid
if (target == null)
{
throw new AlfrescoRuntimeException("Unable to run " + actionName + " action, because the destination record folder could not be determined.");
}
if(targetIsUnfiledRecords)
{
QName targetFolderType = nodeService.getType(target);
if(!TYPE_UNFILED_RECORD_CONTAINER.equals(targetFolderType) && !TYPE_UNFILED_RECORD_FOLDER.equals(targetFolderType))
{
throw new AlfrescoRuntimeException("Unable to run " + actionName + " action, because the destination record folder is an inappropriate type.");
}
}
else
{
if(recordFolderService.isRecordFolder(target) && !dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT) && (recordFolderService.isRecordFolder(actionedUponNodeRef) || filePlanService.isRecordCategory(actionedUponNodeRef)))
{
throw new AlfrescoRuntimeException("Unable to run " + actionName + " action, because the destination record folder is an inappropriate type. A record folder cannot contain another folder or a category");
}
else if(filePlanService.isRecordCategory(target) && dictionaryService.isSubClass(actionedUponType, ContentModel.TYPE_CONTENT))
{
throw new AlfrescoRuntimeException("Unable to run " + actionName + " action, because the destination record folder is an inappropriate type. A record category cannot contain a record");
}
}
}
/**
* Create or resolve the path specified in the action's path parameter
*
@@ -197,12 +279,8 @@ public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractB
if(create)
{
creating = true;
nodeRef = createChild(
action,
parent,
childName,
targetisUnfiledRecords,
lastPathElement && (ContentModel.TYPE_CONTENT.equals(nodeService.getType(actionedUponNodeRef)) || RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT.equals(nodeService.getType(actionedUponNodeRef))));
boolean lastAsFolder = lastPathElement && (ContentModel.TYPE_CONTENT.equals(nodeService.getType(actionedUponNodeRef)) || RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT.equals(nodeService.getType(actionedUponNodeRef)));
nodeRef = createChild(action, parent, childName, targetisUnfiledRecords, lastAsFolder);
}
else
{

View File

@@ -7,7 +7,7 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl;
* @author Mark Hibbins
* @since 2.2
*/
public class CopyToAction extends CopyMoveFileToBaseAction
public class CopyToAction extends CopyMoveLinkFileToBaseAction
{
/** action name */
public static final String NAME = "copyTo";
@@ -16,6 +16,6 @@ public class CopyToAction extends CopyMoveFileToBaseAction
public void init()
{
super.init();
this.mode = CopyMoveFileToActionMode.COPY;
this.mode = CopyMoveLinkFileToActionMode.COPY;
}
}

View File

@@ -7,7 +7,7 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl;
* @author Roy Wetherall
* @since 2.1
*/
public class FileToAction extends CopyMoveFileToBaseAction
public class FileToAction extends CopyMoveLinkFileToBaseAction
{
/** action name */
public static final String NAME = "fileTo";
@@ -16,6 +16,6 @@ public class FileToAction extends CopyMoveFileToBaseAction
public void init()
{
super.init();
this.mode = CopyMoveFileToActionMode.MOVE;
this.mode = CopyMoveLinkFileToActionMode.MOVE;
}
}

View File

@@ -0,0 +1,21 @@
package org.alfresco.module.org_alfresco_module_rm.action.impl;
/**
* Link To action implementation.
*
* @author Mark Hibbins
* @since 2.2
*/
public class LinkToAction extends CopyMoveLinkFileToBaseAction
{
/** action name */
public static final String NAME = "linkTo";
@Override
public void init()
{
super.init();
this.mode = CopyMoveLinkFileToActionMode.LINK;
}
}

View File

@@ -7,7 +7,7 @@ package org.alfresco.module.org_alfresco_module_rm.action.impl;
* @author Mark Hibbins
* @since 2.2
*/
public class MoveToAction extends CopyMoveFileToBaseAction
public class MoveToAction extends CopyMoveLinkFileToBaseAction
{
/** action name */
public static final String NAME = "moveTo";
@@ -16,6 +16,6 @@ public class MoveToAction extends CopyMoveFileToBaseAction
public void init()
{
super.init();
this.mode = CopyMoveFileToActionMode.MOVE;
this.mode = CopyMoveLinkFileToActionMode.MOVE;
}
}

View File

@@ -39,19 +39,19 @@ public interface RecordService
/**
* Register a record metadata aspect.
* <p>
* The file plan type indicates which file plan type the aspect applied to. Null indicates that
* The file plan type indicates which file plan type the aspect applied to. Null indicates that
* the aspect applies to rma:filePlan.
* <p>
* A record metadata aspect can be registered more than once if it applies to more than one
* A record metadata aspect can be registered more than once if it applies to more than one
* file plan type.
*
*
* @param recordMetadataAspect record metadata aspect qualified name
* @param filePlanType file plan type
*
*
* @since 2.2
*/
void registerRecordMetadataAspect(QName recordMetadataAspect, QName filePlanType);
/**
* Disables the property editable check.
*/
@@ -66,35 +66,35 @@ public interface RecordService
* Gets a list of all the record meta-data aspects
*
* @return {@link Set}<{@link QName}> list of record meta-data aspects
*
*
* @deprecated since 2.2, file plan component required to provide context
*/
@Deprecated
Set<QName> getRecordMetaDataAspects();
/**
* Gets a list of all the record metadata aspects relevant to the file plan type of the
* file plan component provided.
* <p>
* If a null context is provided all record meta-data aspects are returned, but this is not
* If a null context is provided all record meta-data aspects are returned, but this is not
* recommended.
*
* @param nodeRef node reference to file plan component providing context
* @return {@link Set}<{@link QName}> list of record meta-data aspects
*
*
* @since 2.2
*/
Set<QName> getRecordMetadataAspects(NodeRef nodeRef);
/**
* Gets a list of all the record metadata aspect that relate to the provided file plan type.
* <p>
* If null is provided for the file plan type then record metadata aspects for the default
* If null is provided for the file plan type then record metadata aspects for the default
* file plan type (rma:filePlan) are returned.
*
*
* @param filePlanType file plan type
* @return{@link Set}<{@link QName}> list of record meta-data aspects
*
*
* @since 2.2
*/
Set<QName> getRecordMetadataAspects(QName filePlanType);
@@ -214,4 +214,12 @@ public interface RecordService
* @param nodeRef The document node reference from which a record will be created
*/
void makeRecord(NodeRef nodeRef);
/**
* Creates a link for the specified document in target
*
* @param nodeRef The document node reference for which a link will be created
* @param folder The folder in which the link will be created
*/
void link(NodeRef nodeRef, NodeRef folder);
}

View File

@@ -635,7 +635,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
if (getRecordMetadataAspectsMap().containsKey(recordMetadataAspect))
{
// get the current set of file plan types for this aspect
filePlanTypes = (Set<QName>)getRecordMetadataAspectsMap().get(recordMetadataAspect);
filePlanTypes = getRecordMetadataAspectsMap().get(recordMetadataAspect);
}
else
{
@@ -717,6 +717,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#createRecord(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, boolean)
*/
@Override
public void createRecord(final NodeRef filePlan, final NodeRef nodeRef, final boolean isLinked)
{
ParameterCheck.mandatory("filePlan", filePlan);
@@ -773,7 +774,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
// save the information about the originating details
Map<QName, Serializable> aspectProperties = new HashMap<QName, Serializable>(3);
aspectProperties.put(PROP_RECORD_ORIGINATING_LOCATION, (Serializable) parentAssoc.getParentRef());
aspectProperties.put(PROP_RECORD_ORIGINATING_LOCATION, parentAssoc.getParentRef());
aspectProperties.put(PROP_RECORD_ORIGINATING_USER_ID, userId);
aspectProperties.put(PROP_RECORD_ORIGINATING_CREATION_DATE, new Date());
nodeService.addAspect(nodeRef, ASPECT_RECORD_ORIGINATING_DETAILS, aspectProperties);
@@ -1391,4 +1392,19 @@ public class RecordServiceImpl extends BaseBehaviourBean
logger.info(I18NUtil.getMessage(MSG_NODE_HAS_ASPECT, nodeRef.toString(), typeQName.toString()));
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.record.RecordService#link(NodeRef, NodeRef)
*/
@Override
public void link(NodeRef nodeRef, NodeRef folder)
{
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("folder", folder);
if(isRecord(nodeRef) && isRecordFolder(folder))
{
nodeService.addChild(folder, nodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, nodeService.getProperty(nodeRef, ContentModel.PROP_NAME).toString()));
}
}
}