diff --git a/source/java/org/alfresco/rest/api/Nodes.java b/source/java/org/alfresco/rest/api/Nodes.java index feb411a543..27deba6ec4 100644 --- a/source/java/org/alfresco/rest/api/Nodes.java +++ b/source/java/org/alfresco/rest/api/Nodes.java @@ -23,8 +23,8 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.api; - +package org.alfresco.rest.api; + import java.io.InputStream; import java.util.List; import java.util.Map; @@ -45,7 +45,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.QName; import org.springframework.extensions.webscripts.servlet.FormData; - + /** * File Folder (Nodes) API * @@ -54,31 +54,31 @@ import org.springframework.extensions.webscripts.servlet.FormData; * @author Gethin James * @author steveglover */ -public interface Nodes -{ - /** - * Get the node representation for the given node. +public interface Nodes +{ + /** + * Get the node representation for the given node. * - * @param nodeId String - * @return Node - */ - Node getNode(String nodeId); - - /** - * Get the document representation for the given node. + * @param nodeId String + * @return Node + */ + Node getNode(String nodeId); + + /** + * Get the document representation for the given node. * - * @param nodeRef NodeRef - * @return Document - */ - Document getDocument(NodeRef nodeRef); - - /** - * Get the folder representation for the given node. + * @param nodeRef NodeRef + * @return Document + */ + Document getDocument(NodeRef nodeRef); + + /** + * Get the folder representation for the given node. * - * @param nodeRef NodeRef - * @return Folder - */ - Folder getFolder(NodeRef nodeRef); + * @param nodeRef NodeRef + * @return Folder + */ + Folder getFolder(NodeRef nodeRef); /** * Get the folder or document representation (as appropriate) for the given node. @@ -274,6 +274,7 @@ public interface Nodes String PARAM_INCLUDE_PATH = "path"; String PARAM_INCLUDE_ASPECTNAMES = "aspectNames"; String PARAM_INCLUDE_ISLINK = "isLink"; + String PARAM_INCLUDE_ISLOCKED = "isLocked"; String PARAM_INCLUDE_ALLOWABLEOPERATIONS = "allowableOperations"; String PARAM_INCLUDE_ASSOCIATION = "association"; @@ -295,10 +296,10 @@ public interface Nodes String PARAM_VERSION_MAJOR = "majorVersion"; // true if major, false if minor String PARAM_VERSION_COMMENT = "comment"; - String PARAM_OVERWRITE = "overwrite"; - String PARAM_AUTO_RENAME = "autoRename"; - + String PARAM_OVERWRITE = "overwrite"; + String PARAM_AUTO_RENAME = "autoRename"; + String PARAM_ISPRIMARY = "isPrimary"; String PARAM_ASSOC_TYPE = "assocType"; -} +} diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 0bcc498134..3e1f384d36 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -22,9 +22,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . * #L% - */ -package org.alfresco.rest.api.impl; - + */ +package org.alfresco.rest.api.impl; + import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -122,6 +122,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockStatus; import org.alfresco.service.cmr.model.FileExistsException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; @@ -159,37 +160,37 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.extensions.surf.util.Content; -import org.springframework.extensions.webscripts.servlet.FormData; - -/** - * Centralises access to file/folder/node services and maps between representations. - * +import org.springframework.extensions.webscripts.servlet.FormData; + +/** + * Centralises access to file/folder/node services and maps between representations. + * * Note: * This class was originally used for returning some basic node info when listing Favourites. * * It has now been re-purposed and extended to implement the new Nodes (RESTful) API for * managing files & folders, as well as custom node types. - * - * @author steveglover + * + * @author steveglover * @author janv * @author Jamal Kaabi-Mofrad * - * @since publicapi1.0 - */ -public class NodesImpl implements Nodes -{ + * @since publicapi1.0 + */ +public class NodesImpl implements Nodes +{ private static final Log logger = LogFactory.getLog(NodesImpl.class); private enum Type - { + { // Note: ordered - DOCUMENT, FOLDER - } - - private NodeService nodeService; - private DictionaryService dictionaryService; - private FileFolderService fileFolderService; + DOCUMENT, FOLDER + } + + private NodeService nodeService; + private DictionaryService dictionaryService; + private FileFolderService fileFolderService; private NamespaceService namespaceService; private PermissionService permissionService; private MimetypeService mimetypeService; @@ -201,13 +202,13 @@ public class NodesImpl implements Nodes private AuthorityService authorityService; private ThumbnailService thumbnailService; private SiteService siteService; - private ActivityPoster poster; - private RetryingTransactionHelper retryingTransactionHelper; - private NodeAssocService nodeAssocService; - private LockService lockService; - - private enum Activity_Type - { + private ActivityPoster poster; + private RetryingTransactionHelper retryingTransactionHelper; + private NodeAssocService nodeAssocService; + private LockService lockService; + + private enum Activity_Type + { ADDED, UPDATED, DELETED, DOWNLOADED } @@ -219,7 +220,7 @@ public class NodesImpl implements Nodes private Repository repositoryHelper; private ServiceRegistry sr; private Set defaultIgnoreTypesAndAspects; - + // ignore types/aspects private Set ignoreQNames; @@ -252,13 +253,13 @@ public class NodesImpl implements Nodes this.personService = sr.getPersonService(); this.ownableService = sr.getOwnableService(); this.authorityService = sr.getAuthorityService(); - this.thumbnailService = sr.getThumbnailService(); - this.siteService = sr.getSiteService(); - this.retryingTransactionHelper = sr.getRetryingTransactionHelper(); - this.lockService = sr.getLockService(); - - if (defaultIgnoreTypesAndAspects != null) - { + this.thumbnailService = sr.getThumbnailService(); + this.siteService = sr.getSiteService(); + this.retryingTransactionHelper = sr.getRetryingTransactionHelper(); + this.lockService = sr.getLockService(); + + if (defaultIgnoreTypesAndAspects != null) + { ignoreQNames = new HashSet<>(defaultIgnoreTypesAndAspects.size()); for (String type : defaultIgnoreTypesAndAspects) { @@ -271,7 +272,7 @@ public class NodesImpl implements Nodes { this.sr = sr; } - + public void setBehaviourFilter(BehaviourFilter behaviourFilter) { this.behaviourFilter = behaviourFilter; @@ -361,20 +362,20 @@ public class NodesImpl implements Nodes @Override public NodeRef validateNode(String nodeId) { - //belts-and-braces - if (nodeId == null) - { - throw new InvalidArgumentException("Missing nodeId"); - } - + //belts-and-braces + if (nodeId == null) + { + throw new InvalidArgumentException("Missing nodeId"); + } + return validateNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId); } - + @Override public NodeRef validateNode(StoreRef storeRef, String nodeId) { String versionLabel = null; - + int idx = nodeId.indexOf(";"); if (idx != -1) { @@ -386,11 +387,11 @@ public class NodesImpl implements Nodes throw new EntityNotFoundException(nodeId); } } - + NodeRef nodeRef = new NodeRef(storeRef, nodeId); return validateNode(nodeRef); } - + @Override public NodeRef validateNode(NodeRef nodeRef) { @@ -398,10 +399,10 @@ public class NodesImpl implements Nodes { throw new EntityNotFoundException(nodeRef.getId()); } - + return nodeRef; } - + /* * Check that nodes exists and matches given expected/excluded type(s). */ @@ -427,10 +428,10 @@ public class NodesImpl implements Nodes { throw new EntityNotFoundException(nodeRef.getId()); } - + return typeMatches(getNodeType(nodeRef), expectedTypes, excludedTypes); } - + private QName getNodeType(NodeRef nodeRef) { return nodeService.getType(nodeRef); @@ -458,7 +459,7 @@ public class NodesImpl implements Nodes allExpectedTypes.addAll(dictionaryService.getSubTypes(expectedType, true)); } } - + Set allExcludedTypes = new HashSet<>(); if (excludedTypes != null) { @@ -467,7 +468,7 @@ public class NodesImpl implements Nodes allExcludedTypes.addAll(dictionaryService.getSubTypes(excludedType, true)); } } - + boolean inExpected = allExpectedTypes.contains(type); boolean excluded = allExcludedTypes.contains(type); return (inExpected && !excluded); @@ -477,23 +478,23 @@ public class NodesImpl implements Nodes * @deprecated review usage (backward compat') */ @Override - public Node getNode(String nodeId) - { + public Node getNode(String nodeId) + { NodeRef nodeRef = validateNode(nodeId); - + return new Node(nodeRef, null, nodeService.getProperties(nodeRef), null, sr); - } + } /** * @deprecated review usage (backward compat') */ - public Node getNode(NodeRef nodeRef) - { + public Node getNode(NodeRef nodeRef) + { return new Node(nodeRef, null, nodeService.getProperties(nodeRef), null, sr); - } + } - private Type getType(NodeRef nodeRef) - { + private Type getType(NodeRef nodeRef) + { return getType(getNodeType(nodeRef), nodeRef); } @@ -552,14 +553,14 @@ public class NodesImpl implements Nodes } return null; // unknown - } - - /** + } + + /** * @deprecated note: currently required for backwards compat' (Favourites API) - */ + */ @Override - public Document getDocument(NodeRef nodeRef) - { + public Document getDocument(NodeRef nodeRef) + { Type type = getType(nodeRef); if ((type != null) && type.equals(Type.DOCUMENT)) { @@ -582,7 +583,7 @@ public class NodesImpl implements Nodes { throw new InvalidArgumentException("Node is not a file: "+nodeRef.getId()); } - } + } private void setCommonProps(Node node, NodeRef nodeRef, Map properties) { @@ -593,12 +594,12 @@ public class NodesImpl implements Nodes node.setCreatedBy((String)properties.get(ContentModel.PROP_CREATOR)); } - /** + /** * @deprecated note: currently required for backwards compat' (Favourites API) - */ + */ @Override - public Folder getFolder(NodeRef nodeRef) - { + public Folder getFolder(NodeRef nodeRef) + { Type type = getType(nodeRef); if ((type != null) && type.equals(Type.FOLDER)) { @@ -612,7 +613,7 @@ public class NodesImpl implements Nodes { throw new InvalidArgumentException("Node is not a folder: "+nodeRef.getId()); } - } + } private NodeRef getParentNodeRef(NodeRef nodeRef) { @@ -881,9 +882,11 @@ public class NodesImpl implements Nodes node.setProperties(mapFromNodeProperties(properties, includeParam, mapUserInfo)); } + Set aspects = null; if (includeParam.contains(PARAM_INCLUDE_ASPECTNAMES)) { - node.setAspectNames(mapFromNodeAspects(nodeService.getAspects(nodeRef))); + aspects = nodeService.getAspects(nodeRef); + node.setAspectNames(mapFromNodeAspects(aspects)); } if (includeParam.contains(PARAM_INCLUDE_ISLINK)) @@ -892,6 +895,12 @@ public class NodesImpl implements Nodes node.setIsLink(isLink); } + if (includeParam.contains(PARAM_INCLUDE_ISLOCKED)) + { + boolean isLocked = isLocked(nodeRef, aspects); + node.setIsLocked(isLocked); + } + if (includeParam.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS)) { // note: refactor when requirements change @@ -906,9 +915,9 @@ public class NodesImpl implements Nodes String perm = kv.getKey(); String op = kv.getValue(); - if (perm.equals(PermissionService.ADD_CHILDREN) && Type.DOCUMENT.equals(type)) + if (perm.equals(PermissionService.ADD_CHILDREN) && Type.DOCUMENT.equals(type)) { - // special case: do not return "create" (as an allowable op) for file/content types - note: 'type' can be null + // special case: do not return "create" (as an allowable op) for file/content types - note: 'type' can be null continue; } else if (perm.equals(PermissionService.DELETE) && (isSpecialNode(nodeRef, nodeTypeQName))) @@ -1592,10 +1601,10 @@ public class NodesImpl implements Nodes props = mapToNodeProperties(nodeInfo.getProperties()); } - // Optionally, lookup by relative path - String relativePath = nodeInfo.getRelativePath(); - parentNodeRef = getOrCreatePath(parentNodeRef, relativePath); - + // Optionally, lookup by relative path + String relativePath = nodeInfo.getRelativePath(); + parentNodeRef = getOrCreatePath(parentNodeRef, relativePath); + // Existing file/folder name handling boolean autoRename = Boolean.valueOf(parameters.getParameter(PARAM_AUTO_RENAME)); if (autoRename && (isContent || isSubClass(nodeTypeQName, ContentModel.TYPE_FOLDER))) @@ -1613,29 +1622,29 @@ public class NodesImpl implements Nodes { assocTypeQName = getAssocType(nodeInfo.getAssociation().getAssocType()); } - - Boolean versionMajor = null; - String str = parameters.getParameter(PARAM_VERSION_MAJOR); - if (str != null) - { - versionMajor = new Boolean(str); - } - String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT); + + Boolean versionMajor = null; + String str = parameters.getParameter(PARAM_VERSION_MAJOR); + if (str != null) + { + versionMajor = new Boolean(str); + } + String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT); // Create the node - NodeRef nodeRef; + NodeRef nodeRef; + + if (isContent) + { + // create empty file node - note: currently will be set to default encoding only (UTF-8) + nodeRef = createNewFile(parentNodeRef, nodeName, nodeTypeQName, null, props, assocTypeQName, parameters, versionMajor, versionComment); + } + else + { + // create non-content node + nodeRef = createNodeImpl(parentNodeRef, nodeName, nodeTypeQName, props, assocTypeQName); + } - if (isContent) - { - // create empty file node - note: currently will be set to default encoding only (UTF-8) - nodeRef = createNewFile(parentNodeRef, nodeName, nodeTypeQName, null, props, assocTypeQName, parameters, versionMajor, versionComment); - } - else - { - // create non-content node - nodeRef = createNodeImpl(parentNodeRef, nodeName, nodeTypeQName, props, assocTypeQName); - } - List aspectNames = nodeInfo.getAspectNames(); if (aspectNames != null) { @@ -1651,7 +1660,7 @@ public class NodesImpl implements Nodes nodeService.addAspect(nodeRef, aspectQName, null); } } - + // eg. to create mandatory assoc(s) if (nodeInfo.getTargets() != null) @@ -1698,17 +1707,17 @@ public class NodesImpl implements Nodes for (AssocChild assoc : entities) { - String childId = assoc.getChildId(); - if (childId == null) - { - throw new InvalidArgumentException("Missing childId"); - } - + String childId = assoc.getChildId(); + if (childId == null) + { + throw new InvalidArgumentException("Missing childId"); + } + QName assocTypeQName = getAssocType(assoc.getAssocType()); try { - NodeRef childNodeRef = validateNode(childId); + NodeRef childNodeRef = validateNode(childId); String nodeName = (String)nodeService.getProperty(childNodeRef, ContentModel.PROP_NAME); QName assocChildQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(nodeName)); @@ -1738,12 +1747,12 @@ public class NodesImpl implements Nodes for (AssocTarget assoc : entities) { - String targetNodeId = assoc.getTargetId(); - if (targetNodeId == null) - { - throw new InvalidArgumentException("Missing targetId"); - } - + String targetNodeId = assoc.getTargetId(); + if (targetNodeId == null) + { + throw new InvalidArgumentException("Missing targetId"); + } + String assocTypeStr = assoc.getAssocType(); QName assocTypeQName = getAssocType(assocTypeStr); try @@ -1970,35 +1979,51 @@ public class NodesImpl implements Nodes return false; } + + private boolean isLocked(NodeRef nodeRef, Set aspects) + { + boolean locked = false; + if (((aspects != null) && aspects.contains(ContentModel.ASPECT_LOCKABLE)) + || nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + LockStatus status = lockService.getLockStatus(nodeRef); + if (status == LockStatus.LOCKED || status == LockStatus.LOCK_OWNER) + { + locked = true; + } + } + + return locked; + } @Override public Node updateNode(String nodeId, Node nodeInfo, Parameters parameters) { - retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Void execute() throws Throwable - { - NodeRef nodeRef = updateNodeImpl(nodeId, nodeInfo, parameters); - ActivityInfo activityInfo = getActivityInfo(getParentNodeRef(nodeRef), nodeRef); - postActivity(Activity_Type.UPDATED, activityInfo, false); - - return null; - } - }, false, true); - - return retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() - { - @Override - public Node execute() throws Throwable - { - return getFolderOrDocument(nodeId, parameters); - } - }, false, false); - } - - protected NodeRef updateNodeImpl(String nodeId, Node nodeInfo, Parameters parameters) - { + retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef nodeRef = updateNodeImpl(nodeId, nodeInfo, parameters); + ActivityInfo activityInfo = getActivityInfo(getParentNodeRef(nodeRef), nodeRef); + postActivity(Activity_Type.UPDATED, activityInfo, false); + + return null; + } + }, false, true); + + return retryingTransactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Node execute() throws Throwable + { + return getFolderOrDocument(nodeId, parameters); + } + }, false, false); + } + + protected NodeRef updateNodeImpl(String nodeId, Node nodeInfo, Parameters parameters) + { final NodeRef nodeRef = validateNode(nodeId); QName nodeTypeQName = getNodeType(nodeRef); @@ -2139,8 +2164,8 @@ public class NodesImpl implements Nodes throw new ConstraintViolatedException(dcne.getMessage()); } } - - return nodeRef; + + return nodeRef; } @Override @@ -2293,17 +2318,17 @@ public class NodesImpl implements Nodes } String versionComment = parameters.getParameter(PARAM_VERSION_COMMENT); - String fileName = parameters.getParameter(PARAM_NAME); - if (fileName != null) - { - // optionally rename, before updating the content - nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, fileName); - } - else - { - fileName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); - } - + String fileName = parameters.getParameter(PARAM_NAME); + if (fileName != null) + { + // optionally rename, before updating the content + nodeService.setProperty(nodeRef, ContentModel.PROP_NAME, fileName); + } + else + { + fileName = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_NAME); + } + return updateExistingFile(null, nodeRef, fileName, contentInfo, stream, parameters, versionMajor, versionComment); } @@ -2314,28 +2339,28 @@ public class NodesImpl implements Nodes behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); try { - writeContent(nodeRef, fileName, stream, true); + writeContent(nodeRef, fileName, stream, true); if ((isVersioned) || (versionMajor != null) || (versionComment != null) ) { - VersionType versionType = null; - if (versionMajor != null) + VersionType versionType = null; + if (versionMajor != null) { - versionType = (versionMajor ? VersionType.MAJOR : VersionType.MINOR); + versionType = (versionMajor ? VersionType.MAJOR : VersionType.MINOR); } - else - { - // note: it is possible to have versionable aspect but no versions (=> no version label) - if ((! isVersioned) || (nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL) == null)) - { - versionType = VersionType.MAJOR; - } - else - { - versionType = VersionType.MINOR; - } - } - + else + { + // note: it is possible to have versionable aspect but no versions (=> no version label) + if ((! isVersioned) || (nodeService.getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL) == null)) + { + versionType = VersionType.MAJOR; + } + else + { + versionType = VersionType.MINOR; + } + } + createVersion(nodeRef, isVersioned, versionType, versionComment); } @@ -2352,100 +2377,100 @@ public class NodesImpl implements Nodes return getFolderOrDocumentFullInfo(nodeRef, null, null, parameters); } - private void writeContent(NodeRef nodeRef, String fileName, InputStream stream, boolean guessEncoding) + private void writeContent(NodeRef nodeRef, String fileName, InputStream stream, boolean guessEncoding) { ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); - - String mimeType = mimetypeService.guessMimetype(fileName); - if ((mimeType != null) && (! mimeType.equals(MimetypeMap.MIMETYPE_BINARY))) - { - // quick/weak guess based on file extension - writer.setMimetype(mimeType); - } - else - { - // stronger guess based on file stream - writer.guessMimetype(fileName); - } - - InputStream is = null; - - if (guessEncoding) - { - is = new BufferedInputStream(stream); - is.mark(1024); - writer.setEncoding(guessEncoding(is, mimeType, false)); - try - { - is.reset(); - } - catch (IOException ioe) - { - if (logger.isWarnEnabled()) - { - logger.warn("Failed to reset stream after trying to guess encoding: " + ioe.getMessage()); - } - } - } - else - { - is = stream; - } - - writer.putContent(is); + + String mimeType = mimetypeService.guessMimetype(fileName); + if ((mimeType != null) && (! mimeType.equals(MimetypeMap.MIMETYPE_BINARY))) + { + // quick/weak guess based on file extension + writer.setMimetype(mimeType); + } + else + { + // stronger guess based on file stream + writer.guessMimetype(fileName); + } + + InputStream is = null; + + if (guessEncoding) + { + is = new BufferedInputStream(stream); + is.mark(1024); + writer.setEncoding(guessEncoding(is, mimeType, false)); + try + { + is.reset(); + } + catch (IOException ioe) + { + if (logger.isWarnEnabled()) + { + logger.warn("Failed to reset stream after trying to guess encoding: " + ioe.getMessage()); + } + } + } + else + { + is = stream; + } + + writer.putContent(is); + } + + private String guessEncoding(InputStream in, String mimeType, boolean close) + { + String encoding = "UTF-8"; + try + { + if (in != null) + { + Charset charset = mimetypeService.getContentCharsetFinder().getCharset(in, mimeType); + encoding = charset.name(); + } + } + finally + { + try + { + if (close && (in != null)) + { + in.close(); + } + } + catch (IOException ioe) + { + if (logger.isWarnEnabled()) + { + logger.warn("Failed to close stream after trying to guess encoding: " + ioe.getMessage()); + } + } + } + return encoding; } - private String guessEncoding(InputStream in, String mimeType, boolean close) - { - String encoding = "UTF-8"; - try - { - if (in != null) - { - Charset charset = mimetypeService.getContentCharsetFinder().getCharset(in, mimeType); - encoding = charset.name(); - } - } - finally - { - try - { - if (close && (in != null)) - { - in.close(); - } - } - catch (IOException ioe) - { - if (logger.isWarnEnabled()) - { - logger.warn("Failed to close stream after trying to guess encoding: " + ioe.getMessage()); - } - } - } - return encoding; - } - protected void createVersion(NodeRef nodeRef, boolean isVersioned, VersionType versionType, String reason) { if (! isVersioned) { - // Ensure versioning is enabled for the file (autoVersion = true, autoVersionProps = false) - Map props = new HashMap<>(2); - props.put(ContentModel.PROP_AUTO_VERSION, true); - props.put(ContentModel.PROP_AUTO_VERSION_PROPS, false); - - nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); - } - - Map versionProperties = new HashMap<>(2); - versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); - if (reason != null) - { - versionProperties.put(VersionModel.PROP_DESCRIPTION, reason); - } + // Ensure versioning is enabled for the file (autoVersion = true, autoVersionProps = false) + Map props = new HashMap<>(2); + props.put(ContentModel.PROP_AUTO_VERSION, true); + props.put(ContentModel.PROP_AUTO_VERSION_PROPS, false); - versionService.createVersion(nodeRef, versionProperties); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); + } + + Map versionProperties = new HashMap<>(2); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); + if (reason != null) + { + versionProperties.put(VersionModel.PROP_DESCRIPTION, reason); + } + + versionService.createVersion(nodeRef, versionProperties); } @Override @@ -2465,9 +2490,9 @@ public class NodesImpl implements Nodes String fileName = null; Content content = null; boolean autoRename = false; - QName nodeTypeQName = ContentModel.TYPE_CONTENT; + QName nodeTypeQName = ContentModel.TYPE_CONTENT; boolean overwrite = false; // If a fileName clashes for a versionable file - Boolean versionMajor = null; + Boolean versionMajor = null; String versionComment = null; String relativePath = null; String renditionNames = null; @@ -2512,7 +2537,7 @@ public class NodesImpl implements Nodes break; case "majorversion": - versionMajor = Boolean.valueOf(field.getValue()); + versionMajor = Boolean.valueOf(field.getValue()); break; case "comment": @@ -2587,7 +2612,7 @@ public class NodesImpl implements Nodes { // overwrite existing (versionable) file BasicContentInfo contentInfo = new ContentInfoImpl(content.getMimetype(), content.getEncoding(), -1, null); - return updateExistingFile(parentNodeRef, existingFile, fileName, contentInfo, content.getInputStream(), parameters, versionMajor, versionComment); + return updateExistingFile(parentNodeRef, existingFile, fileName, contentInfo, content.getInputStream(), parameters, versionMajor, versionComment); } else { @@ -2595,18 +2620,18 @@ public class NodesImpl implements Nodes throw new ConstraintViolatedException(fileName + " already exists."); } } - - // Note: pending REPO-159, we currently auto-enable versioning on new upload (but not when creating empty file) - if (versionMajor == null) - { - versionMajor = true; - } + + // Note: pending REPO-159, we currently auto-enable versioning on new upload (but not when creating empty file) + if (versionMajor == null) + { + versionMajor = true; + } // Create a new file. - NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, versionMajor, versionComment); - - // Create the response - final Node fileNode = getFolderOrDocumentFullInfo(nodeRef, parentNodeRef, nodeTypeQName, parameters); + NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, versionMajor, versionComment); + + // Create the response + final Node fileNode = getFolderOrDocumentFullInfo(nodeRef, parentNodeRef, nodeTypeQName, parameters); // RA-1052 try @@ -2658,44 +2683,44 @@ public class NodesImpl implements Nodes } } - private NodeRef createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType, Content content, Map props, QName assocTypeQName, Parameters params, - Boolean versionMajor, String versionComment) + private NodeRef createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType, Content content, Map props, QName assocTypeQName, Parameters params, + Boolean versionMajor, String versionComment) { - NodeRef nodeRef = createNodeImpl(parentNodeRef, fileName, nodeType, props, assocTypeQName); - - if (content == null) + NodeRef nodeRef = createNodeImpl(parentNodeRef, fileName, nodeType, props, assocTypeQName); + + if (content == null) { - // Write "empty" content - writeContent(nodeRef, fileName, new ByteArrayInputStream("".getBytes()), false); + // Write "empty" content + writeContent(nodeRef, fileName, new ByteArrayInputStream("".getBytes()), false); } - else - { - // Write content - writeContent(nodeRef, fileName, content.getInputStream(), true); - } - - if ((versionMajor != null) || (versionComment != null)) - { - behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); - try - { - // by default, first version is major, unless specified otherwise - VersionType versionType = VersionType.MAJOR; - if ((versionMajor != null) && (!versionMajor)) - { - versionType = VersionType.MINOR; - } + else + { + // Write content + writeContent(nodeRef, fileName, content.getInputStream(), true); + } + + if ((versionMajor != null) || (versionComment != null)) + { + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + try + { + // by default, first version is major, unless specified otherwise + VersionType versionType = VersionType.MAJOR; + if ((versionMajor != null) && (!versionMajor)) + { + versionType = VersionType.MINOR; + } - createVersion(nodeRef, false, versionType, versionComment); - - extractMetadata(nodeRef); - } finally - { - behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); - } - } - - return nodeRef; + createVersion(nodeRef, false, versionType, versionComment); + + extractMetadata(nodeRef); + } finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + } + } + + return nodeRef; } private String getStringOrNull(String value) @@ -2903,10 +2928,10 @@ public class NodesImpl implements Nodes { result.add(name); } - } - return result; - } - + } + return result; + } + @Override public Node lock(String nodeId, LockInfo lockInfo, Parameters parameters) { @@ -2943,27 +2968,27 @@ public class NodesImpl implements Nodes lockInfo.setTimeToExpire(0); } return lockInfo; - } - - /** - * @author Jamal Kaabi-Mofrad + } + + /** + * @author Jamal Kaabi-Mofrad */ - /* - private static class ContentInfoWrapper implements BasicContentInfo + /* + private static class ContentInfoWrapper implements BasicContentInfo { private String mimeType; private String encoding; - public String getEncoding() - { - return encoding; - } - - public String getMimeType() - { - return mimeType; - } - + public String getEncoding() + { + return encoding; + } + + public String getMimeType() + { + return mimeType; + } + ContentInfoWrapper(BasicContentInfo basicContentInfo) { if (basicContentInfo != null) @@ -3004,6 +3029,6 @@ public class NodesImpl implements Nodes } } } - */ -} + */ +} diff --git a/source/java/org/alfresco/rest/api/model/Node.java b/source/java/org/alfresco/rest/api/model/Node.java index a9c15652fb..519eed92ba 100644 --- a/source/java/org/alfresco/rest/api/model/Node.java +++ b/source/java/org/alfresco/rest/api/model/Node.java @@ -73,6 +73,7 @@ public class Node implements Comparable protected Boolean isFolder; protected Boolean isFile; protected Boolean isLink; + protected Boolean isLocked; protected NodeRef parentNodeRef; protected PathInfo pathInfo; @@ -305,6 +306,16 @@ public class Node implements Comparable this.isLink = isLink; } + public Boolean getIsLocked() + { + return isLocked; + } + + public void setIsLocked(Boolean isLocked) + { + this.isLocked = isLocked; + } + public List getAllowableOperations() { return allowableOperations; diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java index 2112ef5c25..35eac7e231 100644 --- a/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/NodeApiTest.java @@ -3593,19 +3593,33 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest Document d1 = createTextFile(folderId, d1Name, "The quick brown fox jumps over the lazy dog 1."); String d1Id = d1.getId(); + HttpResponse response = getSingle(URL_NODES, d1Id, null, null, 200); + Node node = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(node.getProperties().get("cm:lockType")); + assertNull(node.getProperties().get("cm:lockOwner")); + assertNull(node.getIsLocked()); + + Map params = Collections.singletonMap("include", "isLocked"); + response = getSingle(URL_NODES, d1Id, params, null, 200); + node = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(node.getProperties().get("cm:lockType")); + assertNull(node.getProperties().get("cm:lockOwner")); + assertFalse(node.getIsLocked()); + Map body = new HashMap<>(); body.put("includeChildren", "true"); body.put("timeToExpire", "60"); body.put("type", "FULL"); body.put("lifetime", "PERSISTENT"); - HttpResponse response = post(URL_NODES, d1Id, "lock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); + response = post(URL_NODES, d1Id, "lock", toJsonAsStringNonNull(body).getBytes(), null, null, 200); Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class); assertEquals(d1Name, documentResp.getName()); assertEquals(d1Id, documentResp.getId()); assertEquals("READ_ONLY_LOCK", documentResp.getProperties().get("cm:lockType")); assertNotNull(documentResp.getProperties().get("cm:lockOwner")); + assertNull(documentResp.getIsLocked()); // Empty lock body, the default values are used post("nodes/"+folderId+"/lock", "{}", null, 200); @@ -3627,6 +3641,23 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest Document dA2 = createTextFile(folderId, dA2Name, "A2 content"); String dA2Id = dA2.getId(); + params = Collections.singletonMap("include", "isLocked"); + response = getSingle(URL_NODES, folderAId, params, null, 200); + node = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Node.class); + assertNull(node.getProperties()); + assertFalse(node.getIsLocked()); + + params = Collections.singletonMap("include", "aspectNames,properties,isLocked"); + response = getAll(getNodeChildrenUrl(folderAId), null, params, 200); + List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + // Check children nodes are not locked. + for (Node child : nodes) + { + assertNull(child.getProperties().get("cm:lockType")); + assertNull(child.getProperties().get("cm:lockOwner")); + assertFalse(child.getIsLocked()); + } + body = new HashMap<>(); body.put("includeChildren", "true"); body.put("timeToExpire", "60"); @@ -3641,15 +3672,17 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest assertEquals(folderAId, documentResp.getId()); assertNotNull(documentResp.getProperties().get("cm:lockType")); assertNotNull(documentResp.getProperties().get("cm:lockOwner")); + assertNull(documentResp.getIsLocked()); - Map params = Collections.singletonMap("include", "aspectNames,properties"); + params = Collections.singletonMap("include", "aspectNames,properties,isLocked"); response = getAll(getNodeChildrenUrl(folderAId), null, params, 200); - List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); // Test if children nodes are locked as well. for (Node child : nodes) { assertNotNull(child.getProperties().get("cm:lockType")); assertNotNull(child.getProperties().get("cm:lockOwner")); + assertTrue(child.getIsLocked()); } Folder folderB = createFolder(Nodes.PATH_MY, "folderB"); diff --git a/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java b/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java index d6e77264f8..a82a504d4f 100644 --- a/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java +++ b/source/test-java/org/alfresco/rest/api/tests/client/data/Node.java @@ -23,56 +23,57 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.rest.api.tests.client.data; - +package org.alfresco.rest.api.tests.client.data; + import org.alfresco.rest.api.model.AssocChild; import org.alfresco.rest.api.model.AssocTarget; -import java.util.Date; +import java.util.Date; import java.util.List; import java.util.Map; - + import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -/** +/** * Representation of a node - initially for client tests for Nodes (aka File Folder) API - * + * * TODO push null check down into AssertUtil (making sure that no other existing tests break) * * @author janv - */ + */ public class Node -{ +{ protected String id; protected String name; - + protected Date createdAt; protected Date modifiedAt; protected UserInfo createdByUser; protected UserInfo modifiedByUser; - - // Archived info - specifically for archive (deleted) node - see Trashcan API + + // Archived info - specifically for archive (deleted) node - see Trashcan API protected Date archivedAt; protected UserInfo archivedByUser; - // Version info - specifically for version node - see Version History API - protected String versionComment; - + // Version info - specifically for version node - see Version History API + protected String versionComment; + protected Boolean isFolder; protected Boolean isFile; protected Boolean isLink; - + protected Boolean isLocked; + protected String parentId; protected PathInfo path; protected String nodeType; - + protected List aspectNames; - + protected Map properties; - + protected ContentInfo contentInfo; protected List allowableOperations; @@ -85,12 +86,12 @@ public class Node public Node() { } - + public String getId() { return id; } - + public void setId(String id) { this.id = id; @@ -100,27 +101,27 @@ public class Node { return name; } - + public void setName(String name) { this.name = name; } - + public Date getCreatedAt() { return createdAt; } - + public Date getModifiedAt() { return modifiedAt; } - + public UserInfo getCreatedByUser() { return createdByUser; } - + public void setCreatedByUser(UserInfo createdByUser) { this.createdByUser = createdByUser; @@ -130,7 +131,7 @@ public class Node { return modifiedByUser; } - + public void setModifiedByUser(UserInfo modifiedByUser) { this.modifiedByUser = modifiedByUser; @@ -156,26 +157,26 @@ public class Node this.archivedByUser = archivedByUser; } - public String getVersionComment() - { - return versionComment; - } - - public void setVersionComment(String versionComment) - { - this.versionComment = versionComment; - } - + public String getVersionComment() + { + return versionComment; + } + + public void setVersionComment(String versionComment) + { + this.versionComment = versionComment; + } + public Boolean getIsFolder() { return isFolder; } - + public void setIsFolder(Boolean isFolder) { this.isFolder = isFolder; } - + public Boolean getIsFile() { return isFile; @@ -190,57 +191,67 @@ public class Node { return isLink; } - + public void setIsLink(Boolean link) { isLink = link; } - + + public Boolean getIsLocked() + { + return isLocked; + } + + public void setIsLocked(Boolean locked) + { + isLocked = locked; + } + public String getParentId() { return parentId; } - + public void setParentId(String parentId) { this.parentId = parentId; } - + public PathInfo getPath() { return path; } - + public void setPath(PathInfo path) { this.path = path; } - + public String getNodeType() { return nodeType; } - + public void setNodeType(String nodeType) { this.nodeType = nodeType; } - + public List getAspectNames() { return aspectNames; } - + public void setAspectNames(List aspectNames) { this.aspectNames = aspectNames; } - + public Map getProperties() { return properties; } - + public void setProperties(Map properties) { this.properties = properties; @@ -396,6 +407,7 @@ public class Node AssertUtil.assertEquals("isFolder", isFolder, other.getIsFolder()); AssertUtil.assertEquals("isFile", isFile, other.getIsFile()); AssertUtil.assertEquals("isLink", isLink, other.getIsLink()); + AssertUtil.assertEquals("isLocked", isLocked, other.getIsLocked()); if (path != null) {