diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
index 9d786a52df..e0d445e149 100644
--- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
+++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-webscript-context.xml
@@ -571,6 +571,9 @@
class="org.alfresco.repo.web.scripts.substitutionsuggestions.RmSubstitutionSuggestionsGet"
parent="webscript">
+
+
+
\ No newline at end of file
diff --git a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/substitutionsuggestions/rm-substitutionsuggestions.get.desc.xml b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/substitutionsuggestions/rm-substitutionsuggestions.get.desc.xml
index 0211de2081..b77167b3c0 100644
--- a/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/substitutionsuggestions/rm-substitutionsuggestions.get.desc.xml
+++ b/rm-server/config/alfresco/templates/webscripts/org/alfresco/repository/substitutionsuggestions/rm-substitutionsuggestions.get.desc.xml
@@ -1,7 +1,7 @@
Get substitution suggestions for RM
Gets a collection of substitution suggestions for a text fragment for RM.
- /api/rm/rm-substitutionsuggestions?fragment={fragment?}
+ /api/rm/rm-substitutionsuggestions?fragment={fragment}&path={path?}
argument
user
required
diff --git a/rm-server/source/java/org/alfresco/repo/web/scripts/substitutionsuggestions/RmSubstitutionSuggestionsGet.java b/rm-server/source/java/org/alfresco/repo/web/scripts/substitutionsuggestions/RmSubstitutionSuggestionsGet.java
index 456be8a96d..6eaac4db2a 100644
--- a/rm-server/source/java/org/alfresco/repo/web/scripts/substitutionsuggestions/RmSubstitutionSuggestionsGet.java
+++ b/rm-server/source/java/org/alfresco/repo/web/scripts/substitutionsuggestions/RmSubstitutionSuggestionsGet.java
@@ -23,7 +23,18 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import org.alfresco.model.ContentModel;
+import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
+import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
+import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.repo.action.parameter.ParameterProcessorComponent;
+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.StoreRef;
+import org.alfresco.service.cmr.security.AccessStatus;
+import org.alfresco.service.namespace.QName;
+import org.apache.cxf.common.util.StringUtils;
import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status;
@@ -43,14 +54,59 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
private final static String SUBSTITUTIONS_MODEL_KEY = "substitutions";
- private ParameterProcessorComponent parameterProcessorComponent;
+ private final static String RECORD_FOLDER_TYPE = "recordFolder";
+ private final static String RECORD_CATEGORY_TYPE = "recordCategory";
+ private final static String CREATE_CAPABILITY = "Create";
+ private final static String VIEW_CAPABILITY = "ViewRecords";
+
+ private final static int PATH_SUBSTITUTION_MINIMUM_FRAGMENT_LENGTH = 0;
+ private final static int PATH_SUBSTITUTION_MAXIMUM_NUMBER_RESULTS = 10;
+
+ private ParameterProcessorComponent parameterProcessorComponent;
+ private NodeService nodeService;
+ private FilePlanService filePlanService;
+ private CapabilityService capabilityService;
+
+ /**
+ * Set the parameter processor component bean
+ *
+ * @param parameterProcessorComponent
+ */
public void setParameterProcessorComponent(ParameterProcessorComponent parameterProcessorComponent)
{
this.parameterProcessorComponent = parameterProcessorComponent;
}
/**
+ * Set the parameter processor component bean
+ *
+ * @param parameterProcessorComponent
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * @param filePlanService file plan service
+ */
+ public void setFilePlanService(FilePlanService filePlanService)
+ {
+ this.filePlanService = filePlanService;
+ }
+
+ /**
+ * @param filePlanService file plan service
+ */
+ public void setCapabilityService(CapabilityService capabilityService)
+ {
+ this.capabilityService = capabilityService;
+ }
+
+ /**
+ * Return a list of substitutions for the given fragment.
+ *
* @see org.springframework.extensions.webscripts.DeclarativeWebScript#executeImpl(org.springframework.extensions.webscripts.WebScriptRequest,
* org.springframework.extensions.webscripts.Status,
* org.springframework.extensions.webscripts.Cache)
@@ -63,7 +119,7 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
List substitutionSuggestions = new ArrayList();
- substitutionSuggestions.addAll(getSubPathSuggestions(path, fragment));
+ substitutionSuggestions.addAll(getSubPathSuggestions(req, path, fragment));
substitutionSuggestions.addAll(this.parameterProcessorComponent.getSubstitutionSuggestions(fragment));
Map model = new HashMap();
@@ -72,12 +128,148 @@ public class RmSubstitutionSuggestionsGet extends DeclarativeWebScript
return model;
}
- private List getSubPathSuggestions(final String path, final String fragment) {
+ /**
+ * Return a list of path suggestions for the path fragment supplied.
+ *
+ * @param path
+ * @param fragment
+ * @return
+ */
+ private List getSubPathSuggestions(WebScriptRequest req, final String path, final String fragment) {
List pathSuggestions = new ArrayList();
- if(path != null)
+ if((path != null) && path.startsWith("/") && (fragment != null) && (fragment.length() >= PATH_SUBSTITUTION_MINIMUM_FRAGMENT_LENGTH))
{
- // TODO - populate path suggestions
+ String[] pathFragments = path.split("/");
+
+ NodeRef currentNode = getFilePlan(req);
+ for(String pathFragment : pathFragments)
+ {
+ // ignore empty elements of the path produced by split
+ if(!pathFragment.isEmpty())
+ {
+ boolean foundThisPathFragment = false;
+ List children = nodeService.getChildAssocs(currentNode);
+ for (ChildAssociationRef childAssoc : children) {
+ NodeRef childNodeRef = childAssoc.getChildRef();
+ String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
+ if(fileName.equals(pathFragment) && isNodeRefAppropriateForPathSuggestion(childNodeRef))
+ {
+ foundThisPathFragment = true;
+ currentNode = childNodeRef;
+ break;
+ }
+ }
+ if(!foundThisPathFragment)
+ {
+ currentNode = null;
+ break;
+ }
+ }
+ }
+
+ if(currentNode != null)
+ {
+ String lowerCaseFragment = fragment.toLowerCase();
+ List children = nodeService.getChildAssocs(currentNode);
+ for (ChildAssociationRef childAssoc : children) {
+ NodeRef childNodeRef = childAssoc.getChildRef();
+ String fileName = (String) nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME);
+ if((fragment.isEmpty() || fileName.toLowerCase().startsWith(lowerCaseFragment)) && isNodeRefAppropriateForPathSuggestion(childNodeRef))
+ {
+ pathSuggestions.add("/" + fileName);
+ if(pathSuggestions.size() >= PATH_SUBSTITUTION_MAXIMUM_NUMBER_RESULTS)
+ {
+ break;
+ }
+ }
+ }
+ }
}
return pathSuggestions;
}
+
+ /**
+ * Utility method to get the file plan from the passed parameters.
+ *
+ * @param req
+ * @return
+ */
+ protected NodeRef getFilePlan(WebScriptRequest req)
+ {
+ NodeRef filePlan = null;
+
+ Map templateVars = req.getServiceMatch().getTemplateVars();
+ String siteId = templateVars.get("siteid");
+ if (siteId != null)
+ {
+ filePlan = filePlanService.getFilePlanBySiteId(siteId);
+ }
+
+ if (filePlan == null)
+ {
+ String storeType = templateVars.get("store_type");
+ String storeId = templateVars.get("store_id");
+ String id = templateVars.get("id");
+
+ if (StringUtils.isEmpty(storeType) == false &&
+ StringUtils.isEmpty(storeId) == false &&
+ StringUtils.isEmpty(id) == false)
+ {
+ StoreRef storeRef = new StoreRef(storeType, storeId);
+ NodeRef nodeRef = new NodeRef(storeRef, id);
+ if (filePlanService.isFilePlan(nodeRef) == true)
+ {
+ filePlan = nodeRef;
+ }
+ }
+ }
+
+ if (filePlan == null)
+ {
+ // Assume we are in a legacy repository and we will grab the default file plan
+ filePlan = filePlanService.getFilePlanBySiteId(FilePlanService.DEFAULT_RM_SITE_ID);
+ }
+
+ return filePlan;
+ }
+
+ /**
+ * Identifies record category and record folder types of nodeRef
+ *
+ * @param nodeRef Instance of NodeRef to be tested
+ * @return True if the passed NodeRef instance is a record category or record folder
+ */
+ private boolean isNodeRefAppropriateForPathSuggestion(NodeRef nodeRef)
+ {
+ // check node type
+ QName type = nodeService.getType(nodeRef);
+ String typeLocalName = type.getLocalName();
+ boolean isCorrectType = (RECORD_FOLDER_TYPE.equals(typeLocalName) || RECORD_CATEGORY_TYPE.equals(typeLocalName));
+
+ // check permissions
+ boolean canView = false;
+ if(isCorrectType)
+ {
+ Capability createCapability = capabilityService.getCapability(CREATE_CAPABILITY);
+ Capability viewCapability = capabilityService.getCapability(VIEW_CAPABILITY);
+ if ((createCapability != null) && (viewCapability != null))
+ {
+ List requiredCapabilities = new ArrayList();
+ requiredCapabilities.add(CREATE_CAPABILITY);
+ requiredCapabilities.add(VIEW_CAPABILITY);
+ Map map = capabilityService.getCapabilitiesAccessState(nodeRef, requiredCapabilities);
+ if (map.containsKey(createCapability) && map.containsKey(viewCapability))
+ {
+ AccessStatus createAccessStatus = map.get(createCapability);
+ AccessStatus viewAccessStatus = map.get(viewCapability);
+ if (createAccessStatus.equals(AccessStatus.ALLOWED) && viewAccessStatus.equals(AccessStatus.ALLOWED))
+ {
+ canView = true;
+ }
+ }
+ }
+ }
+
+ return isCorrectType && canView;
+ }
}
\ No newline at end of file