mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-07 18:25:23 +00:00
59895: Merged V4.2-BUG-FIX (4.2.2) to HEAD-BUG-FIX (Cloud/4.3) 59748: Merged DEV to V4.2-BUG-FIX (4.2.2) 59567: MNT-10278: "Format" column is empty for Dublin Core View Implemented renderer for mymetype. Added mimetypeDisplayName property to JSON root values to be able to display human readable mimetype description without additional calls from JavaScript code. Added localized strings for unknown types in English and Russian languages. Edited dublin_core view sample to use mimetype instead of cm:mimetype. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@62214 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
507 lines
17 KiB
Java
507 lines
17 KiB
Java
/*
|
|
* 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;
|
|
|
|
import java.io.Serializable;
|
|
import java.text.MessageFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.service.ServiceRegistry;
|
|
import org.alfresco.service.cmr.lock.LockService;
|
|
import org.alfresco.service.cmr.lock.LockStatus;
|
|
import org.alfresco.service.cmr.model.FileFolderService;
|
|
import org.alfresco.service.cmr.model.FileInfo;
|
|
import org.alfresco.service.cmr.repository.ContentData;
|
|
import org.alfresco.service.cmr.repository.ContentService;
|
|
import org.alfresco.service.cmr.repository.MimetypeService;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.cmr.security.AccessPermission;
|
|
import org.alfresco.service.cmr.security.AccessStatus;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.cmr.security.PublicServiceAccessService;
|
|
import org.alfresco.service.namespace.NamespaceException;
|
|
import org.alfresco.service.namespace.NamespaceService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.util.ISO8601DateFormat;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.json.JSONException;
|
|
import org.json.simple.JSONArray;
|
|
import org.json.simple.JSONAware;
|
|
import org.json.simple.JSONObject;
|
|
import org.springframework.extensions.surf.util.URLEncoder;
|
|
|
|
/**
|
|
* JSON Conversion Component
|
|
*
|
|
* @author Roy Wetherall
|
|
* @author Kevin Roast
|
|
*/
|
|
public class JSONConversionComponent
|
|
{
|
|
/** Content download API URL template */
|
|
private final static String CONTENT_DOWNLOAD_API_URL = "/api/node/content/{0}/{1}/{2}/{3}";
|
|
|
|
/** Logger */
|
|
private static Log logger = LogFactory.getLog(JSONConversionComponent.class);
|
|
|
|
/** Registered decorators */
|
|
protected Map<QName, PropertyDecorator> propertyDecorators = new HashMap<QName, PropertyDecorator>(8);
|
|
|
|
/** User permissions */
|
|
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 */
|
|
protected NodeService nodeService;
|
|
protected PublicServiceAccessService publicServiceAccessService;
|
|
protected NamespaceService namespaceService;
|
|
protected FileFolderService fileFolderService;
|
|
protected LockService lockService;
|
|
protected ContentService contentService;
|
|
protected PermissionService permissionService;
|
|
protected MimetypeService mimetypeService;
|
|
|
|
|
|
/**
|
|
* @param nodeService node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* @param publicServiceAccessService public service access service
|
|
*/
|
|
public void setPublicServiceAccessService(PublicServiceAccessService publicServiceAccessService)
|
|
{
|
|
this.publicServiceAccessService = publicServiceAccessService;
|
|
}
|
|
|
|
/**
|
|
* @param namespaceService namespace service
|
|
*/
|
|
public void setNamespaceService(NamespaceService namespaceService)
|
|
{
|
|
this.namespaceService = namespaceService;
|
|
}
|
|
|
|
/**
|
|
* @param fileFolderService file folder service
|
|
*/
|
|
public void setFileFolderService(FileFolderService fileFolderService)
|
|
{
|
|
this.fileFolderService = fileFolderService;
|
|
}
|
|
|
|
/**
|
|
* @param lockService lock service
|
|
*/
|
|
public void setLockService(LockService lockService)
|
|
{
|
|
this.lockService = lockService;
|
|
}
|
|
|
|
/**
|
|
* @param permissionService permission service
|
|
*/
|
|
public void setPermissionService(PermissionService permissionService)
|
|
{
|
|
this.permissionService = permissionService;
|
|
}
|
|
|
|
/**
|
|
* @param userPermissions user permissions
|
|
*/
|
|
public void setUserPermissions(String[] userPermissions)
|
|
{
|
|
this.userPermissions = userPermissions;
|
|
}
|
|
|
|
/**
|
|
* @param contentService content service
|
|
*/
|
|
public void setContentService(ContentService contentService)
|
|
{
|
|
this.contentService = contentService;
|
|
}
|
|
|
|
/**
|
|
* @param mimetypeService mimetype service
|
|
*/
|
|
public void setMimetypeService(MimetypeService mimetypeService)
|
|
{
|
|
this.mimetypeService = mimetypeService;
|
|
}
|
|
|
|
/**
|
|
* Register a property decorator;
|
|
*
|
|
* @param propertyDecorator
|
|
*/
|
|
public void registerPropertyDecorator(PropertyDecorator propertyDecorator)
|
|
{
|
|
for (QName propertyName : propertyDecorator.getPropertyNames())
|
|
{
|
|
propertyDecorators.put(propertyName, propertyDecorator);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Convert a node reference to a JSON string. Selects the correct converter based on selection
|
|
* implementation.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public String toJSON(final NodeRef nodeRef, final boolean useShortQNames)
|
|
{
|
|
final JSONObject json = new JSONObject();
|
|
|
|
if (this.nodeService.exists(nodeRef))
|
|
{
|
|
if (publicServiceAccessService.hasAccess(ServiceRegistry.NODE_SERVICE.getLocalName(), "getProperties", nodeRef) == AccessStatus.ALLOWED)
|
|
{
|
|
// init namespace prefix cache
|
|
namespacePrefixCache.get().clear();
|
|
|
|
// Get node info
|
|
FileInfo nodeInfo = this.fileFolderService.getFileInfo(nodeRef);
|
|
|
|
// Set root values
|
|
setRootValues(nodeInfo, json, useShortQNames);
|
|
|
|
// add permissions
|
|
json.put("permissions", permissionsToJSON(nodeRef));
|
|
|
|
// add properties
|
|
json.put("properties", propertiesToJSON(nodeRef, nodeInfo.getProperties(), useShortQNames));
|
|
|
|
// add aspects
|
|
json.put("aspects", apsectsToJSON(nodeRef, useShortQNames));
|
|
}
|
|
}
|
|
|
|
return json.toJSONString();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param nodeInfo
|
|
* @param rootJSONObject
|
|
* @param useShortQNames
|
|
* @throws JSONException
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected void setRootValues(final FileInfo nodeInfo, final JSONObject rootJSONObject, final boolean useShortQNames)
|
|
{
|
|
final NodeRef nodeRef = nodeInfo.getNodeRef();
|
|
|
|
rootJSONObject.put("nodeRef", nodeInfo.getNodeRef().toString());
|
|
rootJSONObject.put("type", nameToString(nodeInfo.getType(), useShortQNames));
|
|
rootJSONObject.put("isContainer", nodeInfo.isFolder()); //node.getIsContainer() || node.getIsLinkToContainer());
|
|
rootJSONObject.put("isLocked", isLocked(nodeInfo.getNodeRef()));
|
|
|
|
rootJSONObject.put("isLink", nodeInfo.isLink());
|
|
if (nodeInfo.isLink())
|
|
{
|
|
NodeRef targetNodeRef = nodeInfo.getLinkNodeRef();
|
|
if (targetNodeRef != null)
|
|
{
|
|
rootJSONObject.put("linkedNode", toJSON(targetNodeRef, useShortQNames));
|
|
}
|
|
}
|
|
|
|
// TODO should this be moved to the property output since we may have more than one content property
|
|
// or a non-standard content property
|
|
|
|
if (nodeInfo.isFolder() == false)
|
|
{
|
|
final ContentData cdata = nodeInfo.getContentData();
|
|
if (cdata != null)
|
|
{
|
|
String contentURL = MessageFormat.format(
|
|
CONTENT_DOWNLOAD_API_URL, new Object[]{
|
|
nodeRef.getStoreRef().getProtocol(),
|
|
nodeRef.getStoreRef().getIdentifier(),
|
|
nodeRef.getId(),
|
|
URLEncoder.encode(nodeInfo.getName())});
|
|
|
|
rootJSONObject.put("contentURL", contentURL);
|
|
rootJSONObject.put("mimetype", cdata.getMimetype());
|
|
Map<String, String> mimetypeDescriptions;
|
|
mimetypeDescriptions = mimetypeService.getDisplaysByMimetype();
|
|
|
|
if (mimetypeDescriptions.containsKey(cdata.getMimetype()))
|
|
{
|
|
rootJSONObject.put("mimetypeDisplayName", mimetypeDescriptions.get(cdata.getMimetype()));
|
|
}
|
|
rootJSONObject.put("encoding", cdata.getEncoding());
|
|
rootJSONObject.put("size", cdata.getSize());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles the work of converting node permissions to JSON.
|
|
*
|
|
* @param nodeRef
|
|
* @return
|
|
* @throws JSONException
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected JSONObject permissionsToJSON(final NodeRef nodeRef)
|
|
{
|
|
final JSONObject permissionsJSON = new JSONObject();
|
|
if (AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, PermissionService.READ_PERMISSIONS)) == true)
|
|
{
|
|
permissionsJSON.put("inherited", permissionService.getInheritParentPermissions(nodeRef));
|
|
permissionsJSON.put("roles", allSetPermissionsToJSON(nodeRef));
|
|
permissionsJSON.put("user", userPermissionsToJSON(nodeRef));
|
|
}
|
|
return permissionsJSON;
|
|
}
|
|
|
|
/**
|
|
* Handles the work of converting user permissions to JSON.
|
|
*
|
|
* @param nodeRef
|
|
* @return
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected JSONObject userPermissionsToJSON(final NodeRef nodeRef)
|
|
{
|
|
final JSONObject userPermissionJSON = new JSONObject();
|
|
for (String userPermission : this.userPermissions)
|
|
{
|
|
boolean hasPermission = AccessStatus.ALLOWED.equals(permissionService.hasPermission(nodeRef, userPermission));
|
|
userPermissionJSON.put(userPermission, hasPermission);
|
|
}
|
|
return userPermissionJSON;
|
|
}
|
|
|
|
/**
|
|
* Handles the work of converting values to JSON.
|
|
*
|
|
* @param nodeRef
|
|
* @param propertyName
|
|
* @param key
|
|
* @param value
|
|
* @return the JSON value
|
|
*/
|
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
protected Object propertyToJSON(final NodeRef nodeRef, final QName propertyName, final String key, final Serializable value)
|
|
{
|
|
if (value != null)
|
|
{
|
|
// Has a decorator has been registered for this property?
|
|
if (propertyDecorators.containsKey(propertyName))
|
|
{
|
|
JSONAware jsonAware = propertyDecorators.get(propertyName).decorate(propertyName, nodeRef, value);
|
|
if (jsonAware != null)
|
|
{
|
|
return jsonAware;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Built-in data type processing
|
|
if (value instanceof Date)
|
|
{
|
|
JSONObject dateObj = new JSONObject();
|
|
dateObj.put("value", JSONObject.escape(value.toString()));
|
|
dateObj.put("iso8601", JSONObject.escape(ISO8601DateFormat.format((Date)value)));
|
|
return dateObj;
|
|
}
|
|
else if (value instanceof List)
|
|
{
|
|
// Convert the List to a JSON list by recursively calling propertyToJSON
|
|
List<Object> jsonList = new ArrayList<Object>(((List<Serializable>) value).size());
|
|
for (Serializable listItem : (List<Serializable>) value)
|
|
{
|
|
jsonList.add(propertyToJSON(nodeRef, propertyName, key, listItem));
|
|
}
|
|
return jsonList;
|
|
}
|
|
else if (value instanceof Double)
|
|
{
|
|
return (Double.isInfinite((Double)value) || Double.isNaN((Double)value) ? null : value.toString());
|
|
}
|
|
else if (value instanceof Float)
|
|
{
|
|
return (Float.isInfinite((Float)value) || Float.isNaN((Float)value) ? null : value.toString());
|
|
}
|
|
else
|
|
{
|
|
return value.toString();
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param nodeRef
|
|
* @param map
|
|
* @param useShortQNames
|
|
* @return
|
|
* @throws JSONException
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected JSONObject propertiesToJSON(NodeRef nodeRef, Map<QName, Serializable> properties, boolean useShortQNames)
|
|
{
|
|
JSONObject propertiesJSON = new JSONObject();
|
|
|
|
for (QName propertyName : properties.keySet())
|
|
{
|
|
try
|
|
{
|
|
String key = nameToString(propertyName, useShortQNames);
|
|
Serializable value = properties.get(propertyName);
|
|
|
|
propertiesJSON.put(key, propertyToJSON(nodeRef, propertyName, key, value));
|
|
}
|
|
catch (NamespaceException ne)
|
|
{
|
|
// ignore properties that do not have a registered namespace
|
|
if (logger.isDebugEnabled())
|
|
logger.debug("Ignoring property '" + propertyName + "' as its namespace is not registered");
|
|
}
|
|
}
|
|
|
|
return propertiesJSON;
|
|
}
|
|
|
|
/**
|
|
* Handles the work of converting aspects to JSON.
|
|
*
|
|
* @param nodeRef
|
|
* @param useShortQNames
|
|
* @return
|
|
* @throws JSONException
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected JSONArray apsectsToJSON(NodeRef nodeRef, boolean useShortQNames)
|
|
{
|
|
JSONArray aspectsJSON = new JSONArray();
|
|
|
|
Set<QName> aspects = this.nodeService.getAspects(nodeRef);
|
|
for (QName aspect : aspects)
|
|
{
|
|
aspectsJSON.add(nameToString(aspect, useShortQNames));
|
|
}
|
|
|
|
return aspectsJSON;
|
|
}
|
|
|
|
/**
|
|
* Handles the work of converting all set permissions to JSON.
|
|
*
|
|
* @param nodeRef
|
|
* @return
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
protected JSONArray allSetPermissionsToJSON(NodeRef nodeRef)
|
|
{
|
|
Set<AccessPermission> acls = permissionService.getAllSetPermissions(nodeRef);
|
|
JSONArray permissions = new JSONArray();
|
|
for (AccessPermission permission : acls)
|
|
{
|
|
StringBuilder buf = new StringBuilder(64);
|
|
buf.append(permission.getAccessStatus())
|
|
.append(';')
|
|
.append(permission.getAuthority())
|
|
.append(';')
|
|
.append(permission.getPermission())
|
|
.append(';').append(permission.isSetDirectly() ? "DIRECT" : "INHERITED");
|
|
permissions.add(buf.toString());
|
|
}
|
|
return permissions;
|
|
}
|
|
|
|
/**
|
|
* Convert a qname to a string - either full or short prefixed named.
|
|
*
|
|
* @param qname
|
|
* @param isShortName
|
|
* @return qname string.
|
|
*/
|
|
private String nameToString(final QName qname, final boolean isShortName)
|
|
{
|
|
String result;
|
|
if (isShortName)
|
|
{
|
|
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
|
|
{
|
|
result = qname.toString();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Return true if the node is locked.
|
|
*
|
|
* @param nodeRef
|
|
* @return
|
|
*/
|
|
private boolean isLocked(final NodeRef nodeRef)
|
|
{
|
|
boolean locked = false;
|
|
|
|
if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true)
|
|
{
|
|
LockStatus lockStatus = lockService.getLockStatus(nodeRef);
|
|
if (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER)
|
|
{
|
|
locked = true;
|
|
}
|
|
}
|
|
|
|
return locked;
|
|
}
|
|
}
|