From 3e5620cc87243193a3a5caf084619f9f05be49a6 Mon Sep 17 00:00:00 2001 From: Mark Hibbins Date: Mon, 17 Feb 2014 17:08:22 +0000 Subject: [PATCH] RM-1144, Path substitution suggestion functionality git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@62684 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../rm-webscript-context.xml | 3 + .../rm-substitutionsuggestions.get.desc.xml | 2 +- .../RmSubstitutionSuggestionsGet.java | 202 +++++++++++++++++- 3 files changed, 201 insertions(+), 6 deletions(-) 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