mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
. Optimize ApplicationScriptUtils.toJSON() - threadlocal cache for namespace resolution to avoid DD access, smarter retrieval of cm:person properties - 40% quicker or more in some cases
. Convert short qnames to long qnames in our templates to avoid DD access to resolve full qnames . Optimize out N+1 queries from calling RatingService.getRating() unless the document has at least one Like (which is available in the rolled up property already present on the node) - up to 99% quicker when building "Likes" JSON structure...! . Added new optimized method to FileFolderService to retrieve a cm:name based path - only the String for each path element not the full FileInfo structure for each (avoid full getProperties() - 70% quicker to build webdav URL overall before/after to retrieve doclist2 script (8x concurrent threads x25 repeats etc.) Before: 1030ms After: 645ms Also improves original doclist script (used by dashlets) and single node retrievals. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@47448 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -431,6 +431,7 @@
|
|||||||
org.alfresco.service.cmr.model.FileFolderService.create=ACL_NODE.0.sys:base.CreateChildren
|
org.alfresco.service.cmr.model.FileFolderService.create=ACL_NODE.0.sys:base.CreateChildren
|
||||||
org.alfresco.service.cmr.model.FileFolderService.delete=ACL_NODE.0.sys:base.DeleteNode
|
org.alfresco.service.cmr.model.FileFolderService.delete=ACL_NODE.0.sys:base.DeleteNode
|
||||||
org.alfresco.service.cmr.model.FileFolderService.getNamePath=ACL_NODE.1.sys:base.ReadProperties
|
org.alfresco.service.cmr.model.FileFolderService.getNamePath=ACL_NODE.1.sys:base.ReadProperties
|
||||||
|
org.alfresco.service.cmr.model.FileFolderService.getNameOnlyPath=ACL_NODE.1.sys:base.ReadProperties
|
||||||
org.alfresco.service.cmr.model.FileFolderService.resolveNamePath=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
|
org.alfresco.service.cmr.model.FileFolderService.resolveNamePath=ACL_ALLOW,AFTER_ACL_NODE.sys:base.ReadProperties
|
||||||
org.alfresco.service.cmr.model.FileFolderService.getFileInfo=ACL_NODE.0.sys:base.ReadProperties
|
org.alfresco.service.cmr.model.FileFolderService.getFileInfo=ACL_NODE.0.sys:base.ReadProperties
|
||||||
org.alfresco.service.cmr.model.FileFolderService.getReader=ACL_NODE.0.sys:base.ReadContent
|
org.alfresco.service.cmr.model.FileFolderService.getReader=ACL_NODE.0.sys:base.ReadContent
|
||||||
|
@@ -258,6 +258,7 @@
|
|||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean id="usernamePropertiesDecorator" parent="baseDecorator" class="org.alfresco.repo.jscript.app.UsernamePropertyDecorator">
|
<bean id="usernamePropertiesDecorator" parent="baseDecorator" class="org.alfresco.repo.jscript.app.UsernamePropertyDecorator">
|
||||||
|
<property name="nodeService" ref="nodeService"/>
|
||||||
<property name="personService" ref="PersonService" />
|
<property name="personService" ref="PersonService" />
|
||||||
<property name="propertyNames">
|
<property name="propertyNames">
|
||||||
<set>
|
<set>
|
||||||
@@ -266,7 +267,7 @@
|
|||||||
<value>cm:workingCopyOwner</value>
|
<value>cm:workingCopyOwner</value>
|
||||||
<value>cm:lockOwner</value>
|
<value>cm:lockOwner</value>
|
||||||
<value>cm:owner</value>
|
<value>cm:owner</value>
|
||||||
<value>qshare:sharedBy</value>
|
<value>qshare:sharedBy</value>
|
||||||
</set>
|
</set>
|
||||||
</property>
|
</property>
|
||||||
</bean>
|
</bean>
|
||||||
|
@@ -103,11 +103,11 @@ import org.alfresco.service.namespace.NamespaceService;
|
|||||||
import org.alfresco.service.namespace.QName;
|
import org.alfresco.service.namespace.QName;
|
||||||
import org.alfresco.service.namespace.RegexQNamePattern;
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
||||||
import org.alfresco.util.FileFilterMode;
|
import org.alfresco.util.FileFilterMode;
|
||||||
|
import org.alfresco.util.FileFilterMode.Client;
|
||||||
import org.alfresco.util.GUID;
|
import org.alfresco.util.GUID;
|
||||||
import org.alfresco.util.ISO8601DateFormat;
|
import org.alfresco.util.ISO8601DateFormat;
|
||||||
import org.alfresco.util.ISO9075;
|
import org.alfresco.util.ISO9075;
|
||||||
import org.alfresco.util.Pair;
|
import org.alfresco.util.Pair;
|
||||||
import org.alfresco.util.FileFilterMode.Client;
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -956,9 +956,9 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
|
|||||||
this.properties = new ContentAwareScriptableQNameMap<String, Serializable>(this, this.services);
|
this.properties = new ContentAwareScriptableQNameMap<String, Serializable>(this, this.services);
|
||||||
|
|
||||||
Map<QName, Serializable> props = null;
|
Map<QName, Serializable> props = null;
|
||||||
if (nodeInfo != null)
|
if (this.nodeInfo != null)
|
||||||
{
|
{
|
||||||
props = nodeInfo.getProperties();
|
props = this.nodeInfo.getProperties();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1334,7 +1334,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
|
|||||||
{
|
{
|
||||||
if (getIsContainer() || getIsDocument())
|
if (getIsContainer() || getIsDocument())
|
||||||
{
|
{
|
||||||
List<FileInfo> paths = this.services.getFileFolderService().getNamePath(null, getNodeRef());
|
List<String> paths = this.services.getFileFolderService().getNameOnlyPath(null, getNodeRef());
|
||||||
|
|
||||||
// build up the webdav url
|
// build up the webdav url
|
||||||
StringBuilder path = new StringBuilder(128);
|
StringBuilder path = new StringBuilder(128);
|
||||||
@@ -1344,7 +1344,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
|
|||||||
for (int i=1; i<paths.size(); i++)
|
for (int i=1; i<paths.size(); i++)
|
||||||
{
|
{
|
||||||
path.append("/")
|
path.append("/")
|
||||||
.append(URLEncoder.encode(paths.get(i).getName()));
|
.append(URLEncoder.encode(paths.get(i)));
|
||||||
}
|
}
|
||||||
url = path.toString();
|
url = path.toString();
|
||||||
}
|
}
|
||||||
@@ -2774,7 +2774,7 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider
|
|||||||
/**
|
/**
|
||||||
* Creates a thumbnail for the content property of the node.
|
* Creates a thumbnail for the content property of the node.
|
||||||
*
|
*
|
||||||
* The thumbnail name correspionds to pre-set thumbnail details stored in the
|
* The thumbnail name corresponds to pre-set thumbnail details stored in the
|
||||||
* repository.
|
* repository.
|
||||||
*
|
*
|
||||||
* If the thumbnail is created asynchronously then the result will be null and creation
|
* If the thumbnail is created asynchronously then the result will be null and creation
|
||||||
|
@@ -1,11 +1,27 @@
|
|||||||
/**
|
/*
|
||||||
|
* Copyright (C) 2005-2013 Alfresco Software Limited.
|
||||||
*
|
*
|
||||||
|
* This file is part of Alfresco
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
*/
|
*/
|
||||||
package org.alfresco.repo.jscript.app;
|
package org.alfresco.repo.jscript.app;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -18,7 +34,7 @@ import org.alfresco.service.cmr.lock.LockService;
|
|||||||
import org.alfresco.service.cmr.lock.LockStatus;
|
import org.alfresco.service.cmr.lock.LockStatus;
|
||||||
import org.alfresco.service.cmr.model.FileFolderService;
|
import org.alfresco.service.cmr.model.FileFolderService;
|
||||||
import org.alfresco.service.cmr.model.FileInfo;
|
import org.alfresco.service.cmr.model.FileInfo;
|
||||||
import org.alfresco.service.cmr.repository.ContentReader;
|
import org.alfresco.service.cmr.repository.ContentData;
|
||||||
import org.alfresco.service.cmr.repository.ContentService;
|
import org.alfresco.service.cmr.repository.ContentService;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
import org.alfresco.service.cmr.repository.NodeService;
|
import org.alfresco.service.cmr.repository.NodeService;
|
||||||
@@ -42,6 +58,7 @@ import org.springframework.extensions.surf.util.URLEncoder;
|
|||||||
* JSON Conversion Component
|
* JSON Conversion Component
|
||||||
*
|
*
|
||||||
* @author Roy Wetherall
|
* @author Roy Wetherall
|
||||||
|
* @author Kevin Roast
|
||||||
*/
|
*/
|
||||||
public class JSONConversionComponent
|
public class JSONConversionComponent
|
||||||
{
|
{
|
||||||
@@ -52,11 +69,21 @@ public class JSONConversionComponent
|
|||||||
private static Log logger = LogFactory.getLog(JSONConversionComponent.class);
|
private static Log logger = LogFactory.getLog(JSONConversionComponent.class);
|
||||||
|
|
||||||
/** Registered decorators */
|
/** Registered decorators */
|
||||||
protected Map<QName, PropertyDecorator> propertyDecorators = new HashMap<QName, PropertyDecorator>(3);
|
protected Map<QName, PropertyDecorator> propertyDecorators = new HashMap<QName, PropertyDecorator>(8);
|
||||||
|
|
||||||
/** User permissions */
|
/** User permissions */
|
||||||
protected String[] userPermissions;
|
protected String[] userPermissions;
|
||||||
|
|
||||||
|
/** Thread local cache of namespace prefixes for long QName to short prefix name conversions */
|
||||||
|
protected static ThreadLocal<Map<String, String>> namespacePrefixCache = new ThreadLocal<Map<String, String>>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected Map<String, String> initialValue()
|
||||||
|
{
|
||||||
|
return new HashMap<String, String>(8);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/** Services */
|
/** Services */
|
||||||
protected NodeService nodeService;
|
protected NodeService nodeService;
|
||||||
protected PublicServiceAccessService publicServiceAccessService;
|
protected PublicServiceAccessService publicServiceAccessService;
|
||||||
@@ -66,6 +93,7 @@ public class JSONConversionComponent
|
|||||||
protected ContentService contentService;
|
protected ContentService contentService;
|
||||||
protected PermissionService permissionService;
|
protected PermissionService permissionService;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nodeService node service
|
* @param nodeService node service
|
||||||
*/
|
*/
|
||||||
@@ -148,16 +176,19 @@ public class JSONConversionComponent
|
|||||||
* implementation.
|
* implementation.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public String toJSON(NodeRef nodeRef, boolean useShortQNames)
|
public String toJSON(final NodeRef nodeRef, final boolean useShortQNames)
|
||||||
{
|
{
|
||||||
JSONObject json = new JSONObject();
|
final JSONObject json = new JSONObject();
|
||||||
|
|
||||||
if (this.nodeService.exists(nodeRef) == true)
|
if (this.nodeService.exists(nodeRef))
|
||||||
{
|
{
|
||||||
if (publicServiceAccessService.hasAccess(ServiceRegistry.NODE_SERVICE.getLocalName(), "getProperties", nodeRef) == AccessStatus.ALLOWED)
|
if (publicServiceAccessService.hasAccess(ServiceRegistry.NODE_SERVICE.getLocalName(), "getProperties", nodeRef) == AccessStatus.ALLOWED)
|
||||||
{
|
{
|
||||||
|
// init namespace prefix cache
|
||||||
|
namespacePrefixCache.get().clear();
|
||||||
|
|
||||||
// Get node info
|
// Get node info
|
||||||
FileInfo nodeInfo = fileFolderService.getFileInfo(nodeRef);
|
FileInfo nodeInfo = this.fileFolderService.getFileInfo(nodeRef);
|
||||||
|
|
||||||
// Set root values
|
// Set root values
|
||||||
setRootValues(nodeInfo, json, useShortQNames);
|
setRootValues(nodeInfo, json, useShortQNames);
|
||||||
@@ -166,7 +197,7 @@ public class JSONConversionComponent
|
|||||||
json.put("permissions", permissionsToJSON(nodeRef));
|
json.put("permissions", permissionsToJSON(nodeRef));
|
||||||
|
|
||||||
// add properties
|
// add properties
|
||||||
json.put("properties", propertiesToJSON(nodeRef, useShortQNames));
|
json.put("properties", propertiesToJSON(nodeRef, nodeInfo.getProperties(), useShortQNames));
|
||||||
|
|
||||||
// add aspects
|
// add aspects
|
||||||
json.put("aspects", apsectsToJSON(nodeRef, useShortQNames));
|
json.put("aspects", apsectsToJSON(nodeRef, useShortQNames));
|
||||||
@@ -184,9 +215,9 @@ public class JSONConversionComponent
|
|||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected void setRootValues(FileInfo nodeInfo, JSONObject rootJSONObject, boolean useShortQNames)
|
protected void setRootValues(final FileInfo nodeInfo, final JSONObject rootJSONObject, final boolean useShortQNames)
|
||||||
{
|
{
|
||||||
NodeRef nodeRef = nodeInfo.getNodeRef();
|
final NodeRef nodeRef = nodeInfo.getNodeRef();
|
||||||
|
|
||||||
rootJSONObject.put("nodeRef", nodeInfo.getNodeRef().toString());
|
rootJSONObject.put("nodeRef", nodeInfo.getNodeRef().toString());
|
||||||
rootJSONObject.put("type", nameToString(nodeInfo.getType(), useShortQNames));
|
rootJSONObject.put("type", nameToString(nodeInfo.getType(), useShortQNames));
|
||||||
@@ -194,7 +225,7 @@ public class JSONConversionComponent
|
|||||||
rootJSONObject.put("isLocked", isLocked(nodeInfo.getNodeRef()));
|
rootJSONObject.put("isLocked", isLocked(nodeInfo.getNodeRef()));
|
||||||
|
|
||||||
rootJSONObject.put("isLink", nodeInfo.isLink());
|
rootJSONObject.put("isLink", nodeInfo.isLink());
|
||||||
if (nodeInfo.isLink() == true)
|
if (nodeInfo.isLink())
|
||||||
{
|
{
|
||||||
NodeRef targetNodeRef = nodeInfo.getLinkNodeRef();
|
NodeRef targetNodeRef = nodeInfo.getLinkNodeRef();
|
||||||
if (targetNodeRef != null)
|
if (targetNodeRef != null)
|
||||||
@@ -208,9 +239,8 @@ public class JSONConversionComponent
|
|||||||
|
|
||||||
if (nodeInfo.isFolder() == false)
|
if (nodeInfo.isFolder() == false)
|
||||||
{
|
{
|
||||||
ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
|
final ContentData cdata = nodeInfo.getContentData();
|
||||||
|
if (cdata != null)
|
||||||
if (reader != null)
|
|
||||||
{
|
{
|
||||||
String contentURL = MessageFormat.format(
|
String contentURL = MessageFormat.format(
|
||||||
CONTENT_DOWNLOAD_API_URL, new Object[]{
|
CONTENT_DOWNLOAD_API_URL, new Object[]{
|
||||||
@@ -220,23 +250,24 @@ public class JSONConversionComponent
|
|||||||
URLEncoder.encode(nodeInfo.getName())});
|
URLEncoder.encode(nodeInfo.getName())});
|
||||||
|
|
||||||
rootJSONObject.put("contentURL", contentURL);
|
rootJSONObject.put("contentURL", contentURL);
|
||||||
rootJSONObject.put("mimetype", reader.getMimetype());
|
rootJSONObject.put("mimetype", cdata.getMimetype());
|
||||||
rootJSONObject.put("encoding", reader.getEncoding());
|
rootJSONObject.put("encoding", cdata.getEncoding());
|
||||||
rootJSONObject.put("size", reader.getSize());
|
rootJSONObject.put("size", cdata.getSize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the work of converting node permissions to JSON.
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
* @return
|
* @return
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected JSONObject permissionsToJSON(NodeRef nodeRef)
|
protected JSONObject permissionsToJSON(final NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
JSONObject permissionsJSON = new JSONObject();
|
final JSONObject permissionsJSON = new JSONObject();
|
||||||
if (AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, PermissionService.READ_PERMISSIONS)) == true)
|
if (AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, PermissionService.READ_PERMISSIONS)) == true)
|
||||||
{
|
{
|
||||||
permissionsJSON.put("inherited", permissionService.getInheritParentPermissions(nodeRef));
|
permissionsJSON.put("inherited", permissionService.getInheritParentPermissions(nodeRef));
|
||||||
@@ -247,14 +278,15 @@ public class JSONConversionComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the work of converting user permissions to JSON.
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected JSONObject userPermissionsToJSON(NodeRef nodeRef)
|
protected JSONObject userPermissionsToJSON(final NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
JSONObject userPermissionJSON = new JSONObject();
|
final JSONObject userPermissionJSON = new JSONObject();
|
||||||
for (String userPermission : this.userPermissions)
|
for (String userPermission : this.userPermissions)
|
||||||
{
|
{
|
||||||
boolean hasPermission = AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, userPermission));
|
boolean hasPermission = AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, userPermission));
|
||||||
@@ -273,12 +305,12 @@ public class JSONConversionComponent
|
|||||||
* @return the JSON value
|
* @return the JSON value
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
protected Object propertyToJSON(NodeRef nodeRef, QName propertyName, String key, Serializable value)
|
protected Object propertyToJSON(final NodeRef nodeRef, final QName propertyName, final String key, final Serializable value)
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
// Has a decorator has been registered for this property?
|
// Has a decorator has been registered for this property?
|
||||||
if (propertyDecorators.containsKey(propertyName) == true)
|
if (propertyDecorators.containsKey(propertyName))
|
||||||
{
|
{
|
||||||
JSONAware jsonAware = propertyDecorators.get(propertyName).decorate(propertyName, nodeRef, value);
|
JSONAware jsonAware = propertyDecorators.get(propertyName).decorate(propertyName, nodeRef, value);
|
||||||
if (jsonAware != null)
|
if (jsonAware != null)
|
||||||
@@ -326,16 +358,16 @@ public class JSONConversionComponent
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
|
* @param map
|
||||||
* @param useShortQNames
|
* @param useShortQNames
|
||||||
* @return
|
* @return
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
protected JSONObject propertiesToJSON(NodeRef nodeRef, boolean useShortQNames)
|
protected JSONObject propertiesToJSON(NodeRef nodeRef, Map<QName, Serializable> properties, boolean useShortQNames)
|
||||||
{
|
{
|
||||||
JSONObject propertiesJSON = new JSONObject();
|
JSONObject propertiesJSON = new JSONObject();
|
||||||
|
|
||||||
Map<QName, Serializable> properties = nodeService.getProperties(nodeRef);
|
|
||||||
for (QName propertyName : properties.keySet())
|
for (QName propertyName : properties.keySet())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -357,6 +389,7 @@ public class JSONConversionComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the work of converting aspects to JSON.
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
* @param useShortQNames
|
* @param useShortQNames
|
||||||
@@ -378,6 +411,7 @@ public class JSONConversionComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the work of converting all set permissions to JSON.
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
* @return
|
* @return
|
||||||
@@ -402,17 +436,27 @@ public class JSONConversionComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Convert a qname to a string - either full or short prefixed named.
|
||||||
*
|
*
|
||||||
* @param qname
|
* @param qname
|
||||||
* @param isShortName
|
* @param isShortName
|
||||||
* @return
|
* @return qname string.
|
||||||
*/
|
*/
|
||||||
private String nameToString(QName qname, boolean isShortName)
|
private String nameToString(final QName qname, final boolean isShortName)
|
||||||
{
|
{
|
||||||
String result = null;
|
String result;
|
||||||
if (isShortName == true)
|
if (isShortName)
|
||||||
{
|
{
|
||||||
result = qname.toPrefixString(namespaceService);
|
final Map<String, String> cache = namespacePrefixCache.get();
|
||||||
|
String prefix = cache.get(qname.getNamespaceURI());
|
||||||
|
if (prefix == null)
|
||||||
|
{
|
||||||
|
// first request for this namespace prefix, get and cache result
|
||||||
|
Collection<String> prefixes = this.namespaceService.getPrefixes(qname.getNamespaceURI());
|
||||||
|
prefix = prefixes.size() != 0 ? prefixes.iterator().next() : "";
|
||||||
|
cache.put(qname.getNamespaceURI(), prefix);
|
||||||
|
}
|
||||||
|
result = prefix + QName.NAMESPACE_PREFIX + qname.getLocalName();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -422,11 +466,12 @@ public class JSONConversionComponent
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Return true if the node is locked.
|
||||||
*
|
*
|
||||||
* @param nodeRef
|
* @param nodeRef
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean isLocked(NodeRef nodeRef)
|
private boolean isLocked(final NodeRef nodeRef)
|
||||||
{
|
{
|
||||||
boolean locked = false;
|
boolean locked = false;
|
||||||
|
|
||||||
|
@@ -19,7 +19,6 @@
|
|||||||
package org.alfresco.repo.jscript.app;
|
package org.alfresco.repo.jscript.app;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.alfresco.model.ContentModel;
|
import org.alfresco.model.ContentModel;
|
||||||
import org.alfresco.service.cmr.repository.NodeRef;
|
import org.alfresco.service.cmr.repository.NodeRef;
|
||||||
@@ -61,9 +60,8 @@ public class UsernamePropertyDecorator extends BasePropertyDecorator
|
|||||||
if (this.personService.personExists(username))
|
if (this.personService.personExists(username))
|
||||||
{
|
{
|
||||||
NodeRef personRef = this.personService.getPerson(username);
|
NodeRef personRef = this.personService.getPerson(username);
|
||||||
Map<QName, Serializable> properties = this.nodeService.getProperties(personRef);
|
firstName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME);
|
||||||
firstName = (String)properties.get(ContentModel.PROP_FIRSTNAME);
|
lastName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME);
|
||||||
lastName = (String)properties.get(ContentModel.PROP_LASTNAME);
|
|
||||||
}
|
}
|
||||||
else if (username.equals("System") || username.startsWith("System@"))
|
else if (username.equals("System") || username.startsWith("System@"))
|
||||||
{
|
{
|
||||||
|
@@ -1303,6 +1303,22 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
return FileFolderUtil.makeFolders(service, parentNodeRef, pathElements, folderTypeQName);
|
return FileFolderUtil.makeFolders(service, parentNodeRef, pathElements, folderTypeQName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file or folder information from the root down to and including the node provided.
|
||||||
|
* <ul>
|
||||||
|
* <li>The root node can be of any type and is not included in the path list.</li>
|
||||||
|
* <li>Only the primary path is considered. If the target node is not a descendant of the
|
||||||
|
* root along purely primary associations, then an exception is generated.</li>
|
||||||
|
* <li>If an invalid type is encountered along the path, then an exception is generated.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
|
||||||
|
* node must be assumed.
|
||||||
|
* @param nodeRef a reference to the file or folder
|
||||||
|
* @return Returns a list of file/folder infos from the root (excluded) down to and
|
||||||
|
* including the destination file or folder
|
||||||
|
* @throws FileNotFoundException if the node could not be found
|
||||||
|
*/
|
||||||
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
|
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException
|
||||||
{
|
{
|
||||||
// check the root
|
// check the root
|
||||||
@@ -1334,13 +1350,13 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// we found the root and expect to be building the path up
|
// we found the root and expect to be building the path up
|
||||||
//Run as system as the user could not have access to all folders in the path, see ALF-13816
|
// Run as system as the user could not have access to all folders in the path, see ALF-13816
|
||||||
FileInfo pathInfo = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<FileInfo>()
|
FileInfo pathInfo = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<FileInfo>()
|
||||||
{
|
{
|
||||||
public FileInfo doWork() throws Exception
|
public FileInfo doWork() throws Exception
|
||||||
{
|
{
|
||||||
return toFileInfo(childNodeRef, true);
|
return toFileInfo(childNodeRef, true);
|
||||||
}
|
}
|
||||||
}, AuthenticationUtil.getSystemUserName());
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
// we can't append a path element to the results if there is already a (non-folder) file at the tail
|
// we can't append a path element to the results if there is already a (non-folder) file at the tail
|
||||||
@@ -1373,6 +1389,85 @@ public class FileFolderServiceImpl implements FileFolderService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file or folder names from the root down to and including the node provided.
|
||||||
|
* <ul>
|
||||||
|
* <li>The root node can be of any type and is not included in the path list.</li>
|
||||||
|
* <li>Only the primary path is considered. If the target node is not a descendant of the
|
||||||
|
* root along purely primary associations, then an exception is generated.</li>
|
||||||
|
* <li>If an invalid type is encountered along the path, then an exception is generated.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
|
||||||
|
* node must be assumed.
|
||||||
|
* @param nodeRef a reference to the file or folder
|
||||||
|
* @return Returns a list of file/folder names from the root (excluded) down to and
|
||||||
|
* including the destination file or folder
|
||||||
|
* @throws FileNotFoundException if the node could not be found
|
||||||
|
*/
|
||||||
|
public List<String> getNameOnlyPath(NodeRef rootNodeRef, final NodeRef nodeRef) throws FileNotFoundException
|
||||||
|
{
|
||||||
|
// check the root
|
||||||
|
if (rootNodeRef == null)
|
||||||
|
{
|
||||||
|
rootNodeRef = nodeService.getRootNode(nodeRef.getStoreRef());
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final NodeRef rNodeRef = rootNodeRef;
|
||||||
|
final ArrayList<String> results = new ArrayList<String>(10);
|
||||||
|
// Run as system as the user could not have access to all folders in the path, see ALF-13816
|
||||||
|
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Void>()
|
||||||
|
{
|
||||||
|
public Void doWork() throws Exception
|
||||||
|
{
|
||||||
|
// get the primary path
|
||||||
|
Path path = nodeService.getPath(nodeRef);
|
||||||
|
// iterate and turn the results into file info objects
|
||||||
|
boolean foundRoot = false;
|
||||||
|
for (Path.Element element : path)
|
||||||
|
{
|
||||||
|
// ignore everything down to the root
|
||||||
|
Path.ChildAssocElement assocElement = (Path.ChildAssocElement) element;
|
||||||
|
final NodeRef childNodeRef = assocElement.getRef().getChildRef();
|
||||||
|
if (childNodeRef.equals(rNodeRef))
|
||||||
|
{
|
||||||
|
// just found the root - but we don't put in an entry for it
|
||||||
|
foundRoot = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (!foundRoot)
|
||||||
|
{
|
||||||
|
// keep looking for the root
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
results.add(nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME).toString());
|
||||||
|
}
|
||||||
|
// check that we found the root
|
||||||
|
if (!foundRoot)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(nodeRef);
|
||||||
|
}
|
||||||
|
// done
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
{
|
||||||
|
logger.debug("Built name path for node: \n" +
|
||||||
|
" root: " + rNodeRef + "\n" +
|
||||||
|
" node: " + nodeRef + "\n" +
|
||||||
|
" path: " + results);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}, AuthenticationUtil.getSystemUserName());
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
catch (InvalidNodeRefException e)
|
||||||
|
{
|
||||||
|
throw new FileNotFoundException(nodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public FileInfo resolveNamePath(NodeRef rootNodeRef, List<String> pathElements) throws FileNotFoundException
|
public FileInfo resolveNamePath(NodeRef rootNodeRef, List<String> pathElements) throws FileNotFoundException
|
||||||
{
|
{
|
||||||
return resolveNamePath(rootNodeRef, pathElements, true);
|
return resolveNamePath(rootNodeRef, pathElements, true);
|
||||||
|
@@ -839,6 +839,36 @@ public class FileFolderServiceImplTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testGetNameOnlyPath() throws Exception
|
||||||
|
{
|
||||||
|
FileInfo fileInfo = getByName(NAME_L1_FILE_A, false);
|
||||||
|
assertNotNull(fileInfo);
|
||||||
|
NodeRef nodeRef = fileInfo.getNodeRef();
|
||||||
|
|
||||||
|
List<String> infoPaths = fileFolderService.getNameOnlyPath(workingRootNodeRef, nodeRef);
|
||||||
|
assertEquals("Not enough elements", 2, infoPaths.size());
|
||||||
|
assertEquals("First level incorrent", NAME_L0_FOLDER_A, infoPaths.get(0));
|
||||||
|
assertEquals("Second level incorrent", NAME_L1_FILE_A, infoPaths.get(1));
|
||||||
|
|
||||||
|
// pass in a null root and make sure that it still works
|
||||||
|
infoPaths = fileFolderService.getNameOnlyPath(null, nodeRef);
|
||||||
|
assertEquals("Not enough elements", 3, infoPaths.size());
|
||||||
|
assertEquals("First level incorrent", workingRootNodeRef.getId(), infoPaths.get(0));
|
||||||
|
assertEquals("Second level incorrent", NAME_L0_FOLDER_A, infoPaths.get(1));
|
||||||
|
assertEquals("Third level incorrent", NAME_L1_FILE_A, infoPaths.get(2));
|
||||||
|
|
||||||
|
// check that a non-aligned path is detected
|
||||||
|
NodeRef startRef = getByName(NAME_L0_FOLDER_B, true).getNodeRef();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fileFolderService.getNameOnlyPath(startRef, nodeRef);
|
||||||
|
fail("Failed to detect non-aligned path from root to target node");
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testGetNamePathDoesNotReturnPathContainingNonLeafFileNode() throws Exception
|
public void testGetNamePathDoesNotReturnPathContainingNonLeafFileNode() throws Exception
|
||||||
{
|
{
|
||||||
|
@@ -90,7 +90,7 @@ public class WebDavServiceImpl implements WebDavService
|
|||||||
|
|
||||||
if (getIsContainer(typeName) || getIsDocument(typeName))
|
if (getIsContainer(typeName) || getIsDocument(typeName))
|
||||||
{
|
{
|
||||||
List<FileInfo> paths = fileFolderService.getNamePath(getRootNode().getNodeForCurrentTenant(), nodeRef);
|
List<String> paths = fileFolderService.getNameOnlyPath(getRootNode().getNodeForCurrentTenant(), nodeRef);
|
||||||
|
|
||||||
// build up the webdav url
|
// build up the webdav url
|
||||||
StringBuilder path = new StringBuilder(128);
|
StringBuilder path = new StringBuilder(128);
|
||||||
@@ -99,7 +99,7 @@ public class WebDavServiceImpl implements WebDavService
|
|||||||
for (int i=0; i<paths.size(); i++)
|
for (int i=0; i<paths.size(); i++)
|
||||||
{
|
{
|
||||||
path.append("/")
|
path.append("/")
|
||||||
.append(URLEncoder.encode(paths.get(i).getName()));
|
.append(URLEncoder.encode(paths.get(i)));
|
||||||
}
|
}
|
||||||
url = path.toString();
|
url = path.toString();
|
||||||
}
|
}
|
||||||
|
@@ -309,6 +309,25 @@ public interface FileFolderService
|
|||||||
@Auditable(parameters = {"nodeRef"})
|
@Auditable(parameters = {"nodeRef"})
|
||||||
public void delete(NodeRef nodeRef);
|
public void delete(NodeRef nodeRef);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the file or folder information from the root down to and including the node provided.
|
||||||
|
* <ul>
|
||||||
|
* <li>The root node can be of any type and is not included in the path list.</li>
|
||||||
|
* <li>Only the primary path is considered. If the target node is not a descendant of the
|
||||||
|
* root along purely primary associations, then an exception is generated.</li>
|
||||||
|
* <li>If an invalid type is encountered along the path, then an exception is generated.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
|
||||||
|
* node must be assumed.
|
||||||
|
* @param nodeRef a reference to the file or folder
|
||||||
|
* @return Returns a list of file/folder infos from the root (excluded) down to and
|
||||||
|
* including the destination file or folder
|
||||||
|
* @throws FileNotFoundException if the node could not be found
|
||||||
|
*/
|
||||||
|
@Auditable(parameters = {"rootNodeRef", "nodeRef"})
|
||||||
|
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the file or folder names from the root down to and including the node provided.
|
* Get the file or folder names from the root down to and including the node provided.
|
||||||
* <ul>
|
* <ul>
|
||||||
@@ -319,14 +338,14 @@ public interface FileFolderService
|
|||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
|
* @param rootNodeRef the start of the returned path, or null if the <b>store</b> root
|
||||||
* node must be assumed.
|
* node must be assumed.
|
||||||
* @param nodeRef a reference to the file or folder
|
* @param nodeRef a reference to the file or folder
|
||||||
* @return Returns a list of file/folder infos from the root (excluded) down to and
|
* @return Returns a list of file/folder names from the root (excluded) down to and
|
||||||
* including the destination file or folder
|
* including the destination file or folder
|
||||||
* @throws FileNotFoundException if the node could not be found
|
* @throws FileNotFoundException if the node could not be found
|
||||||
*/
|
*/
|
||||||
@Auditable(parameters = {"rootNodeRef", "nodeRef"})
|
@Auditable(parameters = {"rootNodeRef", "nodeRef"})
|
||||||
public List<FileInfo> getNamePath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException;
|
public List<String> getNameOnlyPath(NodeRef rootNodeRef, NodeRef nodeRef) throws FileNotFoundException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a file or folder name path from a given root node down to the final node.
|
* Resolve a file or folder name path from a given root node down to the final node.
|
||||||
|
Reference in New Issue
Block a user