mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
RM-1198 & RM-1199 - Added functionality for Copy-to and Move-to rule actions
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@65697 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -79,6 +79,16 @@ fileTo.title=File to
|
|||||||
fileTo.description=Files a record to the specified record folder.
|
fileTo.description=Files a record to the specified record folder.
|
||||||
fileTo.path.display-label=Path to Record Folder
|
fileTo.path.display-label=Path to Record Folder
|
||||||
fileTo.createRecordPath.display-label=Create Record Path
|
fileTo.createRecordPath.display-label=Create Record Path
|
||||||
|
# Copy to
|
||||||
|
copyTo.title=Copy to
|
||||||
|
copyTo.description=Copies a record to the specified record folder.
|
||||||
|
copyTo.path.display-label=Path to Record Folder
|
||||||
|
copyTo.createRecordPath.display-label=Create Record Path
|
||||||
|
# Move to
|
||||||
|
moveTo.title=Move to
|
||||||
|
moveTo.description=Moves a record to the specified record folder.
|
||||||
|
moveTo.path.display-label=Path to Record Folder
|
||||||
|
moveTo.createRecordPath.display-label=Create Record Path
|
||||||
# Reject
|
# Reject
|
||||||
reject.title=Reject
|
reject.title=Reject
|
||||||
reject.description=Rejects a record and moves the document to its original location
|
reject.description=Rejects a record and moves the document to its original location
|
||||||
|
@@ -855,6 +855,62 @@
|
|||||||
<property name="allowParameterSubstitutions" value="true"/>
|
<property name="allowParameterSubstitutions" value="true"/>
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
|
<!-- Copy To -->
|
||||||
|
|
||||||
|
<bean id="copyTo_proxy" parent="rmProxyAction">
|
||||||
|
<property name="target" ref="copyTo"/>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<idref bean="copyTo_security"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="copyTo_security" class="org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor" parent="actionSecurity">
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.CreateModifyDestroyEvents
|
||||||
|
org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW
|
||||||
|
org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="copyTo" class="org.alfresco.module.org_alfresco_module_rm.action.impl.CopyToAction" parent="rmAction">
|
||||||
|
<property name="fileFolderService" ref="FileFolderService"/>
|
||||||
|
<property name="filePlanService" ref="FilePlanService" />
|
||||||
|
<property name="publicAction" value="true"/>
|
||||||
|
<property name="allowParameterSubstitutions" value="true"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<!-- Move To -->
|
||||||
|
|
||||||
|
<bean id="moveTo_proxy" parent="rmProxyAction">
|
||||||
|
<property name="target" ref="moveTo"/>
|
||||||
|
<property name="interceptorNames">
|
||||||
|
<list>
|
||||||
|
<idref bean="moveTo_security"/>
|
||||||
|
</list>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="moveTo_security" class="org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityInterceptor" parent="actionSecurity">
|
||||||
|
<property name="objectDefinitionSource">
|
||||||
|
<value>
|
||||||
|
org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.execute=RM_CAP.0.rma:filePlanComponent.CreateModifyDestroyEvents
|
||||||
|
org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementAction.*=RM_ALLOW
|
||||||
|
org.alfresco.repo.action.executer.ActionExecuter.*=RM_ALLOW
|
||||||
|
</value>
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="moveTo" class="org.alfresco.module.org_alfresco_module_rm.action.impl.MoveToAction" parent="rmAction">
|
||||||
|
<property name="fileFolderService" ref="FileFolderService"/>
|
||||||
|
<property name="filePlanService" ref="FilePlanService" />
|
||||||
|
<property name="publicAction" value="true"/>
|
||||||
|
<property name="allowParameterSubstitutions" value="true"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
<!-- RequestInfo action -->
|
<!-- RequestInfo action -->
|
||||||
|
|
||||||
<bean id="requestInfo_proxy" parent="rmProxyAction">
|
<bean id="requestInfo_proxy" parent="rmProxyAction">
|
||||||
|
@@ -0,0 +1,290 @@
|
|||||||
|
package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.alfresco.error.AlfrescoRuntimeException;
|
||||||
|
import org.alfresco.model.ContentModel;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
|
||||||
|
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
|
||||||
|
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
||||||
|
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
||||||
|
import org.alfresco.service.cmr.action.Action;
|
||||||
|
import org.alfresco.service.cmr.action.ParameterDefinition;
|
||||||
|
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||||
|
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.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File To action implementation.
|
||||||
|
*
|
||||||
|
* @author Mark Hibbins
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public abstract class CopyMoveFileToBaseAction extends RMActionExecuterAbstractBase
|
||||||
|
{
|
||||||
|
/** 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";
|
||||||
|
|
||||||
|
/** file folder service */
|
||||||
|
private FileFolderService fileFolderService;
|
||||||
|
|
||||||
|
/** file plan service */
|
||||||
|
private FilePlanService filePlanService;
|
||||||
|
|
||||||
|
/** action modes */
|
||||||
|
public enum CopyMoveFileToActionMode
|
||||||
|
{
|
||||||
|
COPY, MOVE
|
||||||
|
};
|
||||||
|
protected CopyMoveFileToActionMode mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param fileFolderService file folder service
|
||||||
|
*/
|
||||||
|
public void setFileFolderService(FileFolderService fileFolderService)
|
||||||
|
{
|
||||||
|
this.fileFolderService = fileFolderService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param filePlanService file plan service
|
||||||
|
*/
|
||||||
|
public void setFilePlanService(FilePlanService filePlanService)
|
||||||
|
{
|
||||||
|
this.filePlanService = filePlanService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#addParameterDefinitions(java.util.List)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
|
||||||
|
{
|
||||||
|
paramList.add(new ParameterDefinitionImpl(PARAM_PATH, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_PATH)));
|
||||||
|
paramList.add(new ParameterDefinitionImpl(PARAM_CREATE_RECORD_PATH, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_CREATE_RECORD_PATH)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
|
||||||
|
*/
|
||||||
|
@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)))))
|
||||||
|
{
|
||||||
|
boolean targetIsUnfiledRecord =
|
||||||
|
!ACTION_FILETO.equals(action.getActionDefinitionName()) && (
|
||||||
|
((ContentModel.TYPE_CONTENT.equals(nodeService.getType(actionedUponNodeRef)) || RecordsManagementModel.TYPE_NON_ELECTRONIC_DOCUMENT.equals(nodeService.getType(actionedUponNodeRef))) &&
|
||||||
|
!recordService.isFiled(actionedUponNodeRef)) ||
|
||||||
|
RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER.equals(nodeService.getType(actionedUponNodeRef))
|
||||||
|
);
|
||||||
|
|
||||||
|
// first look to see if the destination record folder has been specified
|
||||||
|
NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER);
|
||||||
|
if (recordFolder == null)
|
||||||
|
{
|
||||||
|
// get the reference to the record folder based on the relative path
|
||||||
|
recordFolder = createOrResolvePath(action, actionedUponNodeRef, targetIsUnfiledRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recordFolder == null)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to execute file to action, because the destination record folder could not be determined.");
|
||||||
|
}
|
||||||
|
|
||||||
|
final NodeRef finalRecordFolder = recordFolder;
|
||||||
|
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public Void doWork() throws Exception
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(mode == CopyMoveFileToActionMode.MOVE)
|
||||||
|
{
|
||||||
|
fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileFolderService.copy(actionedUponNodeRef, finalRecordFolder, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException fileNotFound)
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException(
|
||||||
|
"Unable to execute file to action, because the " + (mode == CopyMoveFileToActionMode.MOVE ? "move" : "copy") + " operation failed.",
|
||||||
|
fileNotFound
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or resolve the path specified in the action's path parameter
|
||||||
|
*
|
||||||
|
* @param action
|
||||||
|
* @param actionedUponNodeRef
|
||||||
|
* @param targetisUnfiledRecords true is the target is in unfiled records
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private NodeRef createOrResolvePath(Action action, NodeRef actionedUponNodeRef, boolean targetisUnfiledRecords)
|
||||||
|
{
|
||||||
|
// get the starting context
|
||||||
|
NodeRef context = getContext(action, actionedUponNodeRef, targetisUnfiledRecords);
|
||||||
|
NodeRef path = context;
|
||||||
|
|
||||||
|
// get the path we wish to resolve
|
||||||
|
String pathParameter = (String)action.getParameterValue(PARAM_PATH);
|
||||||
|
String[] pathElementsArray = StringUtils.tokenizeToStringArray(pathParameter, "/", false, true);
|
||||||
|
if((pathElementsArray != null) && (pathElementsArray.length > 0))
|
||||||
|
{
|
||||||
|
// get the create parameter
|
||||||
|
Boolean createValue = (Boolean)action.getParameterValue(PARAM_CREATE_RECORD_PATH);
|
||||||
|
boolean create = createValue == null ? false : createValue.booleanValue();
|
||||||
|
|
||||||
|
// create or resolve the specified path
|
||||||
|
path = createOrResolvePath(action, context, actionedUponNodeRef, Arrays.asList(pathElementsArray), targetisUnfiledRecords, create, false);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or resolve the specified path
|
||||||
|
*
|
||||||
|
* @param action Action to use for reporting if anything goes wrong
|
||||||
|
* @param parent Parent of path to be created
|
||||||
|
* @param actionedUponNodeRef The node subject to the file/move/copy action
|
||||||
|
* @param pathElements The elements of the path to be created
|
||||||
|
* @param targetisUnfiledRecords true if the target is within unfiled records
|
||||||
|
* @param create true if the path should be creeated if it does not exist
|
||||||
|
* @param creating true if we have already created the parent and therefore can skip the check to see if the next path element already exists
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private NodeRef createOrResolvePath(Action action, NodeRef parent, NodeRef actionedUponNodeRef, List<String> pathElements, boolean targetisUnfiledRecords, boolean create, boolean creating)
|
||||||
|
{
|
||||||
|
NodeRef nodeRef = null;
|
||||||
|
String childName = pathElements.get(0);
|
||||||
|
boolean lastPathElement = pathElements.size() == 1;
|
||||||
|
if(!creating)
|
||||||
|
{
|
||||||
|
nodeRef = getChild(parent, childName);
|
||||||
|
}
|
||||||
|
if(nodeRef == null)
|
||||||
|
{
|
||||||
|
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))));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to execute " + action.getActionDefinitionName() + " action, because the destination path could not be determined.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(pathElements.size() > 1)
|
||||||
|
{
|
||||||
|
nodeRef = createOrResolvePath(action, nodeRef, actionedUponNodeRef, pathElements.subList(1, pathElements.size()), targetisUnfiledRecords, create, creating);
|
||||||
|
}
|
||||||
|
return nodeRef;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the specified child node ref of the specified parent if it exists, otherwise return null
|
||||||
|
*
|
||||||
|
* @param parent
|
||||||
|
* @param childName
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private NodeRef getChild(NodeRef parent, String childName)
|
||||||
|
{
|
||||||
|
NodeRef child = null;
|
||||||
|
List<ChildAssociationRef> children = nodeService.getChildAssocs(parent);
|
||||||
|
for (ChildAssociationRef childAssoc : children) {
|
||||||
|
NodeRef childNodeRef = childAssoc.getChildRef();
|
||||||
|
String existingChildName = (String)nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
||||||
|
if(existingChildName.equals(childName))
|
||||||
|
{
|
||||||
|
child = childNodeRef;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the specified child of the specified parent
|
||||||
|
*
|
||||||
|
* @param action Action to use for reporting if anything goes wrong
|
||||||
|
* @param parent Parent of the child to be created
|
||||||
|
* @param childName The name of the child to be created
|
||||||
|
* @param targetisUnfiledRecords true if the child is being created in the unfiled directory (determines type as unfiled container child)
|
||||||
|
* @param lastAsFolder true if this is the last element of the pathe being created and it should be created as a folder. ignored if targetIsUnfiledRecords is true
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private NodeRef createChild(Action action, NodeRef parent, String childName, boolean targetisUnfiledRecords, boolean lastAsFolder)
|
||||||
|
{
|
||||||
|
NodeRef child = null;
|
||||||
|
if(targetisUnfiledRecords)
|
||||||
|
{
|
||||||
|
child = this.fileFolderService.create(parent, childName, RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER).getNodeRef();
|
||||||
|
}
|
||||||
|
else if(lastAsFolder)
|
||||||
|
{
|
||||||
|
child = recordFolderService.createRecordFolder(parent, childName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(RecordsManagementModel.TYPE_RECORD_FOLDER.equals(nodeService.getType(parent)))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to execute " + action.getActionDefinitionName() + " action, because the destination path could not be created.");
|
||||||
|
}
|
||||||
|
child = this.filePlanService.createRecordCategory(parent, childName);
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the context. This will be the unfiled records container of the context if targetisUnfiledRecords is true
|
||||||
|
*
|
||||||
|
* @param action
|
||||||
|
* @param actionedUponNodeRef
|
||||||
|
* @param targetisUnfiledRecords
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private NodeRef getContext(Action action, NodeRef actionedUponNodeRef, boolean targetisUnfiledRecords)
|
||||||
|
{
|
||||||
|
NodeRef context = filePlanService.getFilePlan(actionedUponNodeRef);
|
||||||
|
if(targetisUnfiledRecords && (context != null) && nodeService.exists(context))
|
||||||
|
{
|
||||||
|
context = filePlanService.getUnfiledContainer(context);
|
||||||
|
}
|
||||||
|
if((context == null) || (!nodeService.exists(context)))
|
||||||
|
{
|
||||||
|
throw new AlfrescoRuntimeException("Unable to execute " + action.getActionDefinitionName() + " action, because the path resolution context could not be determined.");
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File To action implementation.
|
||||||
|
*
|
||||||
|
* @author Mark Hibbins
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public class CopyToAction extends CopyMoveFileToBaseAction
|
||||||
|
{
|
||||||
|
/** action name */
|
||||||
|
public static final String NAME = "copyTo";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init()
|
||||||
|
{
|
||||||
|
super.init();
|
||||||
|
this.mode = CopyMoveFileToActionMode.COPY;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,24 +1,5 @@
|
|||||||
package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.alfresco.error.AlfrescoRuntimeException;
|
|
||||||
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
|
|
||||||
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
|
|
||||||
import org.alfresco.repo.action.ParameterDefinitionImpl;
|
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
||||||
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
|
|
||||||
import org.alfresco.service.cmr.action.Action;
|
|
||||||
import org.alfresco.service.cmr.action.ParameterDefinition;
|
|
||||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
|
||||||
import org.alfresco.service.cmr.model.FileNotFoundException;
|
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
|
||||||
import org.apache.commons.lang.ArrayUtils;
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File To action implementation.
|
* File To action implementation.
|
||||||
@@ -26,256 +7,15 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Roy Wetherall
|
* @author Roy Wetherall
|
||||||
* @since 2.1
|
* @since 2.1
|
||||||
*/
|
*/
|
||||||
public class FileToAction extends RMActionExecuterAbstractBase
|
public class FileToAction extends CopyMoveFileToBaseAction
|
||||||
{
|
{
|
||||||
/** action name */
|
/** action name */
|
||||||
public static final String NAME = "fileTo";
|
public static final String NAME = "fileTo";
|
||||||
|
|
||||||
/** 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";
|
|
||||||
|
|
||||||
/** file folder service */
|
|
||||||
private FileFolderService fileFolderService;
|
|
||||||
|
|
||||||
/** file plan service */
|
|
||||||
private FilePlanService filePlanService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param fileFolderService file folder service
|
|
||||||
*/
|
|
||||||
public void setFileFolderService(FileFolderService fileFolderService)
|
|
||||||
{
|
|
||||||
this.fileFolderService = fileFolderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param filePlanService file plan service
|
|
||||||
*/
|
|
||||||
public void setFilePlanService(FilePlanService filePlanService)
|
|
||||||
{
|
|
||||||
this.filePlanService = filePlanService;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase#addParameterDefinitions(java.util.List)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void addParameterDefinitions(List<ParameterDefinition> paramList)
|
public void init()
|
||||||
{
|
{
|
||||||
paramList.add(new ParameterDefinitionImpl(PARAM_PATH, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_PATH)));
|
super.init();
|
||||||
paramList.add(new ParameterDefinitionImpl(PARAM_CREATE_RECORD_PATH, DataTypeDefinition.BOOLEAN, false, getParamDisplayLabel(PARAM_CREATE_RECORD_PATH)));
|
this.mode = CopyMoveFileToActionMode.MOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void executeImpl(final Action action, final NodeRef actionedUponNodeRef)
|
|
||||||
{
|
|
||||||
if (nodeService.exists(actionedUponNodeRef) &&
|
|
||||||
!freezeService.isFrozen(actionedUponNodeRef) &&
|
|
||||||
!recordService.isFiled(actionedUponNodeRef))
|
|
||||||
{
|
|
||||||
// first look to see if the destination record folder has been specified
|
|
||||||
NodeRef recordFolder = (NodeRef)action.getParameterValue(PARAM_DESTINATION_RECORD_FOLDER);
|
|
||||||
if (recordFolder == null)
|
|
||||||
{
|
|
||||||
// get the reference to the record folder based on the relative path
|
|
||||||
recordFolder = createOrResolveRecordFolder(action, actionedUponNodeRef);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recordFolder == null)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute file to action, because the destination record folder could not be determined.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recordFolderService.isRecordFolder(recordFolder))
|
|
||||||
{
|
|
||||||
final NodeRef finalRecordFolder = recordFolder;
|
|
||||||
|
|
||||||
AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public Void doWork() throws Exception
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
fileFolderService.move(actionedUponNodeRef, finalRecordFolder, null);
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException fileNotFound)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute file to action, because the move operation failed.", fileNotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute file to action, becuase the destination was not a record folder.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param action
|
|
||||||
* @param actionedUponNodeRef
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private NodeRef createOrResolveRecordFolder(Action action, NodeRef actionedUponNodeRef)
|
|
||||||
{
|
|
||||||
// TODO check the action for a context node reference
|
|
||||||
// the file plan node always provides the context
|
|
||||||
NodeRef context = filePlanService.getFilePlan(actionedUponNodeRef);
|
|
||||||
if (context == null)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute fileTo action, because the path resolution context could not be found.");
|
|
||||||
}
|
|
||||||
else if (!nodeService.exists(context))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute fileTo action, because the context for the relative path does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for the path parameter
|
|
||||||
String path = (String)action.getParameterValue(PARAM_PATH);
|
|
||||||
String[] pathValues = ArrayUtils.EMPTY_STRING_ARRAY;
|
|
||||||
|
|
||||||
if (path != null && !path.isEmpty())
|
|
||||||
{
|
|
||||||
pathValues = StringUtils.tokenizeToStringArray(path, "/", false, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// look for the creation strategy
|
|
||||||
boolean create = false;
|
|
||||||
Boolean createValue = (Boolean)action.getParameterValue(PARAM_CREATE_RECORD_PATH);
|
|
||||||
if (createValue != null)
|
|
||||||
{
|
|
||||||
create = createValue.booleanValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try and get child
|
|
||||||
NodeRef recordFolder = resolvePath(context, pathValues);
|
|
||||||
|
|
||||||
if (recordFolder == null)
|
|
||||||
{
|
|
||||||
if (create)
|
|
||||||
{
|
|
||||||
// get the parent into which we are going to create the new record folder
|
|
||||||
NodeRef parent = resolveParent(context, pathValues, create);
|
|
||||||
if (parent == null)
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to create new record folder, because destination parent could not be found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we are trying to create a record folder in a record category
|
|
||||||
if (!filePlanService.isRecordCategory(parent))
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to create nre record folder, beacuse the parent is not a record category.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the new record folders name
|
|
||||||
String recordFolderName = pathValues[pathValues.length-1];
|
|
||||||
recordFolder = recordFolderService.createRecordFolder(parent, recordFolderName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new AlfrescoRuntimeException("Unable to execute FileTo action, because the destination record folder does not exist.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return recordFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param pathValues
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private NodeRef resolvePath(final NodeRef context, final String[] pathValues)
|
|
||||||
{
|
|
||||||
return resolvePath(context, pathValues, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param pathValues
|
|
||||||
* @param create Create any missing path elements
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private NodeRef resolvePath(final NodeRef context, final String[] pathValues, boolean create)
|
|
||||||
{
|
|
||||||
NodeRef result = null;
|
|
||||||
FileInfo fileInfo = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
List<String> pathValueList = new ArrayList<String>(Arrays.asList(pathValues));
|
|
||||||
fileInfo = fileFolderService.resolveNamePath(context, pathValueList, false);
|
|
||||||
if((fileInfo == null) && create)
|
|
||||||
{
|
|
||||||
NodeRef parent = this.filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
|
|
||||||
for(int i = 1; i <= pathValueList.size(); i++)
|
|
||||||
{
|
|
||||||
List<String> partialPathValueList = pathValueList.subList(0, i);
|
|
||||||
fileInfo = fileFolderService.resolveNamePath(context, partialPathValueList, false);
|
|
||||||
if(fileInfo == null)
|
|
||||||
{
|
|
||||||
parent = this.filePlanService.createRecordCategory(parent, partialPathValueList.get(partialPathValueList.size() - 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parent = fileInfo.getNodeRef();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException e)
|
|
||||||
{
|
|
||||||
// ignore, checking for null
|
|
||||||
}
|
|
||||||
if (fileInfo != null)
|
|
||||||
{
|
|
||||||
result = fileInfo.getNodeRef();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param pathValues
|
|
||||||
* @param create Create any missing path elements
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private NodeRef resolveParent(NodeRef context, String[] pathValues, boolean create)
|
|
||||||
{
|
|
||||||
NodeRef result = null;
|
|
||||||
|
|
||||||
if (ArrayUtils.isEmpty(pathValues))
|
|
||||||
{
|
|
||||||
// this should never occur since if the path is empty then the context it the resolution of the
|
|
||||||
// path .. the context must already exist
|
|
||||||
throw new AlfrescoRuntimeException("Unable to resolve the parent, because no valid path was specified.");
|
|
||||||
}
|
|
||||||
else if (pathValues.length == 1)
|
|
||||||
{
|
|
||||||
// the context is the parent
|
|
||||||
result = context;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pathValues = (String[])ArrayUtils.remove(pathValues, pathValues.length-1);
|
|
||||||
result = resolvePath(context, pathValues, create);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,21 @@
|
|||||||
|
package org.alfresco.module.org_alfresco_module_rm.action.impl;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File To action implementation.
|
||||||
|
*
|
||||||
|
* @author Mark Hibbins
|
||||||
|
* @since 2.2
|
||||||
|
*/
|
||||||
|
public class MoveToAction extends CopyMoveFileToBaseAction
|
||||||
|
{
|
||||||
|
/** action name */
|
||||||
|
public static final String NAME = "moveTo";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init()
|
||||||
|
{
|
||||||
|
super.init();
|
||||||
|
this.mode = CopyMoveFileToActionMode.MOVE;
|
||||||
|
}
|
||||||
|
}
|
@@ -51,11 +51,14 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
{
|
{
|
||||||
private final static String FRAGMENT_PARAMETER = "fragment";
|
private final static String FRAGMENT_PARAMETER = "fragment";
|
||||||
private final static String PATH_PARAMETER = "path";
|
private final static String PATH_PARAMETER = "path";
|
||||||
|
private final static String UNFILED_PARAMETER = "unfiled";
|
||||||
|
private final static String UNFILED = "true";
|
||||||
|
|
||||||
private final static String SUBSTITUTIONS_MODEL_KEY = "substitutions";
|
private final static String SUBSTITUTIONS_MODEL_KEY = "substitutions";
|
||||||
|
|
||||||
private final static String RECORD_FOLDER_TYPE = "recordFolder";
|
private final static String RECORD_FOLDER_TYPE = "recordFolder";
|
||||||
private final static String RECORD_CATEGORY_TYPE = "recordCategory";
|
private final static String RECORD_CATEGORY_TYPE = "recordCategory";
|
||||||
|
private final static String UNFILED_RECORD_FOLDER_TYPE = "unfiledRecordContainerChild";
|
||||||
|
|
||||||
private final static String CREATE_CAPABILITY = "Create";
|
private final static String CREATE_CAPABILITY = "Create";
|
||||||
private final static String VIEW_CAPABILITY = "ViewRecords";
|
private final static String VIEW_CAPABILITY = "ViewRecords";
|
||||||
@@ -139,12 +142,14 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
{
|
{
|
||||||
String fragment = req.getParameter(FRAGMENT_PARAMETER);
|
String fragment = req.getParameter(FRAGMENT_PARAMETER);
|
||||||
String path = req.getParameter(PATH_PARAMETER);
|
String path = req.getParameter(PATH_PARAMETER);
|
||||||
|
String unfiledString = req.getParameter(UNFILED_PARAMETER);
|
||||||
|
boolean unfiled = (unfiledString != null) && UNFILED.equals(unfiledString);
|
||||||
|
|
||||||
List<String> substitutionSuggestions = new ArrayList<String>();
|
List<String> substitutionSuggestions = new ArrayList<String>();
|
||||||
|
|
||||||
if((fragment != null) && (fragment.length() >= this.substitutionMinimumFragmentSize))
|
if((fragment != null) && (fragment.length() >= this.substitutionMinimumFragmentSize))
|
||||||
{
|
{
|
||||||
substitutionSuggestions.addAll(getSubPathSuggestions(req, path, fragment));
|
substitutionSuggestions.addAll(getSubPathSuggestions(req, path, fragment, unfiled));
|
||||||
substitutionSuggestions.addAll(this.parameterProcessorComponent.getSubstitutionSuggestions(fragment));
|
substitutionSuggestions.addAll(this.parameterProcessorComponent.getSubstitutionSuggestions(fragment));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,13 +166,13 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
* @param fragment
|
* @param fragment
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private List<String> getSubPathSuggestions(WebScriptRequest req, final String path, final String fragment) {
|
private List<String> getSubPathSuggestions(WebScriptRequest req, final String path, final String fragment, boolean unfiled) {
|
||||||
List<String> pathSuggestions = new ArrayList<String>();
|
List<String> pathSuggestions = new ArrayList<String>();
|
||||||
if((path != null) && path.startsWith("/") && (fragment != null))
|
if((path != null) && path.startsWith("/") && (fragment != null))
|
||||||
{
|
{
|
||||||
String[] pathFragments = path.split("/");
|
String[] pathFragments = path.split("/");
|
||||||
|
|
||||||
NodeRef currentNode = getFilePlan(req);
|
NodeRef currentNode = getFilePlan(req, unfiled);
|
||||||
for(String pathFragment : pathFragments)
|
for(String pathFragment : pathFragments)
|
||||||
{
|
{
|
||||||
// ignore empty elements of the path produced by split
|
// ignore empty elements of the path produced by split
|
||||||
@@ -178,7 +183,7 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
for (ChildAssociationRef childAssoc : children) {
|
for (ChildAssociationRef childAssoc : children) {
|
||||||
NodeRef childNodeRef = childAssoc.getChildRef();
|
NodeRef childNodeRef = childAssoc.getChildRef();
|
||||||
String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
||||||
if(fileName.equals(pathFragment) && isNodeRefAppropriateForPathSuggestion(childNodeRef))
|
if(fileName.equals(pathFragment) && isNodeRefAppropriateForPathSuggestion(childNodeRef, unfiled))
|
||||||
{
|
{
|
||||||
foundThisPathFragment = true;
|
foundThisPathFragment = true;
|
||||||
currentNode = childNodeRef;
|
currentNode = childNodeRef;
|
||||||
@@ -200,7 +205,7 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
for (ChildAssociationRef childAssoc : children) {
|
for (ChildAssociationRef childAssoc : children) {
|
||||||
NodeRef childNodeRef = childAssoc.getChildRef();
|
NodeRef childNodeRef = childAssoc.getChildRef();
|
||||||
String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
|
||||||
if((fragment.isEmpty() || fileName.toLowerCase().startsWith(lowerCaseFragment)) && isNodeRefAppropriateForPathSuggestion(childNodeRef))
|
if((fragment.isEmpty() || fileName.toLowerCase().startsWith(lowerCaseFragment)) && isNodeRefAppropriateForPathSuggestion(childNodeRef, unfiled))
|
||||||
{
|
{
|
||||||
pathSuggestions.add("/" + fileName);
|
pathSuggestions.add("/" + fileName);
|
||||||
if(pathSuggestions.size() >= pathSubstitutionMaximumNumberSuggestions)
|
if(pathSuggestions.size() >= pathSubstitutionMaximumNumberSuggestions)
|
||||||
@@ -220,7 +225,7 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
* @param req
|
* @param req
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected NodeRef getFilePlan(WebScriptRequest req)
|
protected NodeRef getFilePlan(WebScriptRequest req, boolean unfiled)
|
||||||
{
|
{
|
||||||
NodeRef filePlan = null;
|
NodeRef filePlan = null;
|
||||||
|
|
||||||
@@ -256,7 +261,7 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
|
filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filePlan;
|
return unfiled ? filePlanService.getUnfiledContainer(filePlan) : filePlan;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,12 +270,14 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
|
|||||||
* @param nodeRef Instance of NodeRef to be tested
|
* @param nodeRef Instance of NodeRef to be tested
|
||||||
* @return True if the passed NodeRef instance is a record category or record folder
|
* @return True if the passed NodeRef instance is a record category or record folder
|
||||||
*/
|
*/
|
||||||
private boolean isNodeRefAppropriateForPathSuggestion(NodeRef nodeRef)
|
private boolean isNodeRefAppropriateForPathSuggestion(NodeRef nodeRef, boolean unfiled)
|
||||||
{
|
{
|
||||||
// check node type
|
// check node type
|
||||||
QName type = nodeService.getType(nodeRef);
|
QName type = nodeService.getType(nodeRef);
|
||||||
String typeLocalName = type.getLocalName();
|
String typeLocalName = type.getLocalName();
|
||||||
boolean isCorrectType = (RECORD_FOLDER_TYPE.equals(typeLocalName) || RECORD_CATEGORY_TYPE.equals(typeLocalName));
|
boolean isCorrectType =
|
||||||
|
(!unfiled && (RECORD_FOLDER_TYPE.equals(typeLocalName) || RECORD_CATEGORY_TYPE.equals(typeLocalName)) ||
|
||||||
|
(unfiled && UNFILED_RECORD_FOLDER_TYPE.equals(typeLocalName)));
|
||||||
|
|
||||||
// check permissions
|
// check permissions
|
||||||
boolean canView = false;
|
boolean canView = false;
|
||||||
|
@@ -42,13 +42,13 @@ import org.springframework.util.StringUtils;
|
|||||||
*/
|
*/
|
||||||
public class FileToActionTest extends BaseRMTestCase
|
public class FileToActionTest extends BaseRMTestCase
|
||||||
{
|
{
|
||||||
private static final String PATH = "rmcontainer/rmfolder";
|
private static final String PATH = "rmContainer/rmFolder";
|
||||||
private static final String PATH2 = "/rmcontainer/rmfolder";
|
private static final String PATH2 = "/rmContainer/rmFolder";
|
||||||
private static final String PATH_BAD = "monkey/rmfolder";
|
private static final String PATH_BAD = "monkey/rmFolder";
|
||||||
private static final String PATH_CREATE = "rmcontainer/newrmfolder";
|
private static final String PATH_CREATE = "rmContainer/newRmFolder";
|
||||||
private static final String LONG_PATH_CREATE = "/rmcontainer/one/two/three/four/newrmfolder";
|
private static final String LONG_PATH_CREATE = "/rmContainer/one/two/three/four/newRmFolder";
|
||||||
|
|
||||||
private static final String PATH_SUB1 = "rmcontainer/${node.cm:title}";
|
private static final String PATH_SUB1 = "rmContainer/${node.cm:title}";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isCollaborationSiteTest()
|
protected boolean isCollaborationSiteTest()
|
||||||
@@ -181,19 +181,19 @@ public class FileToActionTest extends BaseRMTestCase
|
|||||||
public void testCreate() throws Exception
|
public void testCreate() throws Exception
|
||||||
{
|
{
|
||||||
initRecord();
|
initRecord();
|
||||||
createRecord(PATH_CREATE, "newrmfolder");
|
createRecord(PATH_CREATE, "newRmFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreateSub() throws Exception
|
public void testCreateSub() throws Exception
|
||||||
{
|
{
|
||||||
initRecord();
|
initRecord();
|
||||||
createRecord(PATH_SUB1, "mytestvalue", "rmcontainer/mytestvalue");
|
createRecord(PATH_SUB1, "mytestvalue", "rmContainer/mytestvalue");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testCreatePath() throws Exception
|
public void testCreatePath() throws Exception
|
||||||
{
|
{
|
||||||
initRecord();
|
initRecord();
|
||||||
createRecord(LONG_PATH_CREATE, "newrmfolder", "rmcontainer/one/two/three/four/newrmfolder");
|
createRecord(LONG_PATH_CREATE, "newRmFolder", "rmContainer/one/two/three/four/newRmFolder");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRecord(String path, String name)
|
private void createRecord(String path, String name)
|
||||||
|
Reference in New Issue
Block a user