diff --git a/config/alfresco/public-rest-context.xml b/config/alfresco/public-rest-context.xml index 9312b52125..49847bd0b1 100644 --- a/config/alfresco/public-rest-context.xml +++ b/config/alfresco/public-rest-context.xml @@ -142,10 +142,12 @@ + + diff --git a/source/java/org/alfresco/rest/api/impl/NodesImpl.java b/source/java/org/alfresco/rest/api/impl/NodesImpl.java index 8165040675..820a016164 100644 --- a/source/java/org/alfresco/rest/api/impl/NodesImpl.java +++ b/source/java/org/alfresco/rest/api/impl/NodesImpl.java @@ -2269,11 +2269,16 @@ public class NodesImpl implements Nodes if ((isVersioned) || (versionMajor != null) || (versionComment != null) ) { - VersionType versionType = VersionType.MINOR; - if ((versionMajor != null) && (versionMajor == true)) + VersionType versionType = null; + if (versionMajor != null) { - versionType = VersionType.MAJOR; + versionType = (versionMajor ? VersionType.MAJOR : VersionType.MINOR); } + else + { + versionType = (isVersioned ? versionType = VersionType.MINOR : VersionType.MAJOR); + } + createVersion(nodeRef, isVersioned, versionType, versionComment); } @@ -2368,20 +2373,22 @@ public class NodesImpl implements Nodes { if (! isVersioned) { - // Ensure the file is versionable (autoVersion = true, autoVersionProps = false) - ensureVersioningEnabled(nodeRef, true, false); + // 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); } - else + + Map versionProperties = new HashMap<>(2); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); + if (reason != null) { - Map versionProperties = new HashMap<>(2); - versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); - if (reason != null) - { - versionProperties.put(VersionModel.PROP_DESCRIPTION, reason); - } + versionProperties.put(VersionModel.PROP_DESCRIPTION, reason); + } - versionService.createVersion(nodeRef, versionProperties); - } + versionService.createVersion(nodeRef, versionProperties); } @Override @@ -2533,7 +2540,7 @@ public class NodesImpl implements Nodes } // Create a new file. - final Node fileNode = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters); + final Node fileNode = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, majorVersion, versionComment); // RA-1052 try @@ -2585,7 +2592,8 @@ public class NodesImpl implements Nodes } } - private Node createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType, Content content, Map props, QName assocTypeQName, Parameters params) + private Node createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType, Content content, Map props, QName assocTypeQName, Parameters params, + Boolean versionMajor, String versionComment) { if (nodeType == null) { @@ -2597,12 +2605,25 @@ public class NodesImpl implements Nodes // Write content writeContent(nodeRef, fileName, content.getInputStream(), true); - // Ensure the file is versionable (autoVersion = true, autoVersionProps = false) - ensureVersioningEnabled(nodeRef, true, false); + 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; + } - // Extract the metadata - extractMetadata(nodeRef); + createVersion(nodeRef, false, versionType, versionComment); + extractMetadata(nodeRef); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + } + // Create the response return getFolderOrDocumentFullInfo(nodeRef, parentNodeRef, nodeType, params); } @@ -2701,25 +2722,6 @@ public class NodesImpl implements Nodes } } - /** - * Ensures the given node has the {@code cm:versionable} aspect applied to it, and - * that it has the initial version in the version store. - * - * @param nodeRef the reference to the node to be checked - * @param autoVersion If the {@code cm:versionable} aspect is applied, should auto - * versioning be requested? - * @param autoVersionProps If the {@code cm:versionable} aspect is applied, should - * auto versioning of properties be requested? - */ - protected void ensureVersioningEnabled(NodeRef nodeRef, boolean autoVersion, boolean autoVersionProps) - { - Map props = new HashMap<>(2); - props.put(ContentModel.PROP_AUTO_VERSION, autoVersion); - props.put(ContentModel.PROP_AUTO_VERSION_PROPS, autoVersionProps); - - versionService.ensureVersioningEnabled(nodeRef, props); - } - /** * Extracts the given node metadata asynchronously. * diff --git a/source/java/org/alfresco/rest/api/model/VersionOptions.java b/source/java/org/alfresco/rest/api/model/VersionOptions.java new file mode 100644 index 0000000000..c81846d2d1 --- /dev/null +++ b/source/java/org/alfresco/rest/api/model/VersionOptions.java @@ -0,0 +1,63 @@ +/* + * #%L + * Alfresco Remote API + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * 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 . + * #L% + */ +package org.alfresco.rest.api.model; + +/** + * Version Options + * + * Initially, used by version revert. Should also be consistent with version options used with Node Create & Update. + * + * @author janv + */ +public class VersionOptions +{ + private Boolean majorVersion; + private String comment; + + public VersionOptions() + { + } + + public String getComment() + { + return comment; + } + + public void setComment(String comment) + { + this.comment = comment; + } + + public Boolean getMajorVersion() + { + return majorVersion; + } + + public void setMajorVersion(Boolean majorVersion) + { + this.majorVersion = majorVersion; + } +} diff --git a/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java b/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java index e544e1cfab..c375efa098 100644 --- a/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java +++ b/source/java/org/alfresco/rest/api/nodes/NodeVersionsRelation.java @@ -25,11 +25,16 @@ */ package org.alfresco.rest.api.nodes; +import org.alfresco.model.ContentModel; import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionModel; import org.alfresco.rest.api.Nodes; import org.alfresco.rest.api.model.Node; +import org.alfresco.rest.api.model.NodeTarget; import org.alfresco.rest.api.model.UserInfo; +import org.alfresco.rest.api.model.VersionOptions; import org.alfresco.rest.framework.BinaryProperties; +import org.alfresco.rest.framework.Operation; import org.alfresco.rest.framework.WebApiDescription; import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException; import org.alfresco.rest.framework.resource.RelationshipResource; @@ -39,15 +44,20 @@ import org.alfresco.rest.framework.resource.content.BinaryResource; import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo; import org.alfresco.rest.framework.resource.parameters.Paging; import org.alfresco.rest.framework.resource.parameters.Parameters; +import org.alfresco.rest.framework.webscripts.WithResponse; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionService; +import org.alfresco.service.cmr.version.VersionType; import org.alfresco.util.ParameterCheck; import org.alfresco.util.PropertyCheck; import org.springframework.beans.factory.InitializingBean; +import javax.servlet.http.HttpServletResponse; +import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -171,21 +181,63 @@ public class NodeVersionsRelation implements throw new EntityNotFoundException(nodeId+"-"+versionId); } - private Version findVersion(String nodeId, String versionLabelId) - { - // note: sub-optimal - NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null); - VersionHistory vh = versionService.getVersionHistory(nodeRef); - if (vh != null) + @Operation("revert") + @WebApiDescription(title = "Revert Version", + description="Reverts (ie. promotes) specified version to become a new, most recent, version", + successStatus = HttpServletResponse.SC_OK) + public Node revertById(String nodeId, String versionId, VersionOptions versionOptions, Parameters parameters, WithResponse withResponse) + { + Version v = findVersion(nodeId, versionId); + + if (v != null) { - for (Version v : vh.getAllVersions()) + CheckOutCheckInService cociService = sr.getCheckOutCheckInService(); + + NodeRef nodeRef = v.getVersionedNodeRef(); + + String versionComment = versionOptions.getComment(); + + VersionType versionType = VersionType.MINOR; + Boolean versionMajor = versionOptions.getMajorVersion(); + if ((versionMajor != null) && (versionMajor)) { - if (v.getVersionLabel().equals(versionLabelId)) - { - return v; - } + versionType = VersionType.MAJOR; } + + Map versionProperties = new HashMap<>(2); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); + if (versionComment != null) + { + versionProperties.put(VersionModel.PROP_DESCRIPTION, versionComment); + } + + //cancel editing if we want to revert + if (sr.getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY)) + { + nodeRef = cociService.cancelCheckout(nodeRef); + } + + // TODO review default for deep and/or whether we should make it an option + versionService.revert(nodeRef, v, false); + + // Checkout/Checkin the node - to store the new version in version history + NodeRef wcNodeRef = cociService.checkout(nodeRef); + cociService.checkin(wcNodeRef, versionProperties); + + // get latest version + v = versionService.getVersionHistory(nodeRef).getHeadVersion(); + + Node node = nodes.getFolderOrDocumentFullInfo(v.getFrozenStateNodeRef(), null, null, parameters, null); + mapVersionInfo(v, node); + return node; } - return null; + + throw new EntityNotFoundException(nodeId+"-"+versionId); + } + + private Version findVersion(String nodeId, String versionLabelId) + { + NodeRef nodeRef = nodes.validateOrLookupNode(nodeId, null); + return versionService.getVersionHistory(nodeRef).getVersion(versionLabelId); } } diff --git a/source/test-java/org/alfresco/rest/api/tests/NodeVersionsApiTest.java b/source/test-java/org/alfresco/rest/api/tests/NodeVersionsApiTest.java index 05846bf51e..989ed362b6 100644 --- a/source/test-java/org/alfresco/rest/api/tests/NodeVersionsApiTest.java +++ b/source/test-java/org/alfresco/rest/api/tests/NodeVersionsApiTest.java @@ -27,6 +27,8 @@ package org.alfresco.rest.api.tests; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.rest.api.Nodes; +import org.alfresco.rest.api.model.VersionOptions; import org.alfresco.rest.api.nodes.NodesEntityResource; import org.alfresco.rest.api.tests.client.HttpResponse; import org.alfresco.rest.api.tests.client.PublicApiClient.Paging; @@ -116,6 +118,12 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest AuthenticationUtil.clearCurrentSecurityContext(); } + + protected String getNodeVersionRevertUrl(String nodeId, String versionId) + { + return getNodeVersionsUrl(nodeId) + "/" + versionId + "/revert"; + } + protected String getNodeVersionsUrl(String nodeId) { return URL_NODES + "/" + nodeId + "/" + URL_VERSIONS; @@ -305,8 +313,9 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest int verCnt = 1; + String textContentSuffix = "The quick brown fox jumps over the lazy dog "; String contentName = "content " + System.currentTimeMillis(); - String content = "The quick brown fox jumps over the lazy dog "+verCnt; + String content = textContentSuffix+verCnt; Document documentResp = createTextFile(user1, myFolderNodeId, contentName, content, "UTF-8", null); String d1Id = documentResp.getId(); @@ -343,7 +352,7 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest minorVersion++; // Update - content = "The quick brown fox jumps over the lazy dog "+verCnt; + content = textContentSuffix+verCnt; ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes()); File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt"); PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile); @@ -373,19 +382,20 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest int totalVerCnt = verCnt; + // check total version count - also get properties so that we can check version label etc params = new HashMap<>(); params.put("include", "properties"); response = getAll(getNodeVersionsUrl(d1Id), user1, paging, params, 200); nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); assertEquals(totalVerCnt, nodes.size()); - checkVersionHistoryAndContent(d1Id, nodes, verCnt, majorVersion, minorVersion); + checkVersionHistoryAndContent(d1Id, nodes, verCnt, textContentSuffix, null, majorVersion, minorVersion, false); // delete to trashcan/archive ... delete(URL_NODES, user1, d1Id, null, 204); { - // -ver tests + // -ve tests getSingle(NodesEntityResource.class, user1, d1Id, null, 404); getAll(getNodeVersionsUrl(d1Id), user1, null, null, 404); } @@ -401,7 +411,7 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest // -ve test - unauthenticated - belts-and-braces ;-) getAll(getNodeVersionsUrl(d1Id), null, paging, null, 401); - // -ve test - unauthenticated - belts-and-braces ;-) + // -ve test - unknown nodeId getAll(getNodeVersionsUrl("dummy"), user1, paging, null, 404); } } @@ -413,7 +423,137 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest } } - private void checkVersionHistoryAndContent(String docId, List nodesWithProps, int verCnt, int majorVersion, int minorVersion) throws Exception + /** + * Tests revert (ie. promote older version to become the latest/most recent version). + * + *

POST:

+ * {@literal :/alfresco/api/-default-/public/alfresco/versions/1/nodes//versions//revert} + */ + @Test + public void testRevert() throws Exception + { + // As user 1 ... + String sharedFolderNodeId = getSharedNodeId(user1); + + // create folder + String f1Id = null; + + try + { + f1Id = createFolder(user1, sharedFolderNodeId, "testRevert-f1-"+System.currentTimeMillis()).getId(); + + int majorVersion = 1; + int minorVersion = 0; + int verCnt = 1; + + String textContentSuffix = "The quick brown fox jumps over the lazy dog "; + String contentName = "content " + System.currentTimeMillis(); + String content = textContentSuffix+verCnt; + + String updateVerCommentSuffix = "Update comment "; + Map params = new HashMap<>(); + params.put(Nodes.PARAM_VERSION_COMMENT, updateVerCommentSuffix+verCnt); + + // Upload text file - versioning is currently auto enabled on upload (create file via multi-part/form-data) + Document documentResp = createTextFile(user1, f1Id, contentName, content, "UTF-8", params); + String d1Id = documentResp.getId(); + + // Update the content + int updateCnt = 3; + for (int i = 1; i <= updateCnt; i++) + { + verCnt++; + minorVersion++; + + // Update + content = textContentSuffix+verCnt; + ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes()); + File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt"); + PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile); + + params = new HashMap<>(); + params.put(Nodes.PARAM_VERSION_COMMENT, updateVerCommentSuffix+verCnt); + + putBinary(getNodeContentUrl(d1Id), user1, payload, null, params, 200); + } + + // check version history count - also get properties so that we can check version label etc + params = new HashMap<>(); + params.put("include", "properties"); + HttpResponse response = getAll(getNodeVersionsUrl(d1Id), user1, null, params, 200); + List nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(verCnt, nodes.size()); + + // check version labels and content + checkVersionHistoryAndContent(d1Id, nodes, verCnt, textContentSuffix, updateVerCommentSuffix, majorVersion, minorVersion, false); + + int revertMajorVersion = 1; + int revertMinorVersion = 0; + + String revertVerCommentSuffix = "Revert comment "; + + int revertCnt = 3; + for (int i = 1; i <= revertCnt; i++) + { + String revertVersionId = revertMajorVersion+"."+revertMinorVersion; + + VersionOptions versionOptions = new VersionOptions(); + versionOptions.setMajorVersion(true); + versionOptions.setComment(revertVerCommentSuffix+i); + + post(getNodeVersionRevertUrl(d1Id, revertVersionId), user1, toJsonAsStringNonNull(versionOptions), null, 200); + + verCnt++; + revertMinorVersion++; + + majorVersion++; + } + + // check version history count - also get properties so that we can check version label etc + params = new HashMap<>(); + params.put("include", "properties"); + response = getAll(getNodeVersionsUrl(d1Id), user1, null, params, 200); + nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); + assertEquals(verCnt, nodes.size()); + + // check version labels and content - most recently reverted, eg. version labels 4.0, 3.0, 2.0 + List revertedNodes = nodes.subList(0, revertCnt); + checkVersionHistoryAndContent(d1Id, revertedNodes, updateCnt, textContentSuffix, revertVerCommentSuffix, majorVersion, 0, true); + + // check version labels and content - the rest of the version history (prior to reverted), eg. version labels 1.3, 1.2, 1.1, 1.0 + minorVersion = 3; + List originalUpdatedNodes = nodes.subList(revertCnt, nodes.size()); + checkVersionHistoryAndContent(d1Id, originalUpdatedNodes, updateCnt+1, textContentSuffix, updateVerCommentSuffix, 1, minorVersion, false); + + // Currently, we also allow the most recent version to be reverted (ie. not disallowed by underlying VersionService) + post(getNodeVersionRevertUrl(d1Id, majorVersion+".0"), user1, "{}", null, 200); + + { + // -ve test - unauthenticated - belts-and-braces ;-) + post(getNodeVersionRevertUrl(d1Id, "1.0"), null, "{}", null, 401); + + // -ve test - unknown nodeId + post(getNodeVersionRevertUrl("dummy", "1.0"), user1, "{}", null, 404); + + // -ve test - unknown versionId + post(getNodeVersionRevertUrl(d1Id, "15.0"), user1, "{}", null, 404); + + // -ve test - permission denied + post(getNodeVersionRevertUrl(d1Id, "1.0"), user2, "{}", null, 403); + } + } + finally + { + if (f1Id != null) + { + // some cleanup + Map params = Collections.singletonMap("permanent", "true"); + delete(URL_NODES, user1, f1Id, params, 204); + } + } + } + + private void checkVersionHistoryAndContent(String docId, List nodesWithProps, int verCnt, String textContentSuffix, String verCommentSuffix, int majorVersion, int minorVersion, boolean majorVersions) throws Exception { String versionId = null; @@ -425,7 +565,8 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest assertEquals(versionId, versionNode.getId()); assertEquals(versionId, versionNode.getProperties().get("cm:versionLabel")); - if (versionId.equals("1.0")) + + if (versionId.endsWith(".0")) { assertEquals("MAJOR", versionNode.getProperties().get("cm:versionType")); } @@ -437,12 +578,22 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest assertNull(versionNode.getCreatedByUser()); assertNull(versionNode.getCreatedAt()); + assertEquals((verCommentSuffix != null ? verCommentSuffix+verCnt : null), versionNode.getVersionComment()); + // Download version content - by default with Content-Disposition header HttpResponse response = getSingle(getNodeVersionsUrl(docId), user1, versionId+"/content", null, 200); String textContent = response.getResponse(); - assertEquals("The quick brown fox jumps over the lazy dog "+verCnt, textContent); + assertEquals(textContentSuffix+verCnt, textContent); + + if (majorVersions) + { + majorVersion--; + } + else + { + minorVersion--; + } - minorVersion--; verCnt--; } @@ -501,9 +652,11 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest int cntBefore = 2; int verCnt = 1; + String textContentSuffix = "The quick brown fox jumps over the lazy dog "; + for (int i = 1; i <= cntBefore; i++) { - String content = "The quick brown fox jumps over the lazy dog " + verCnt; + String content = textContentSuffix + verCnt; ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes()); File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt"); PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile); @@ -548,7 +701,7 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest for (int i = 1; i <= cntAfter; i++) { // Update again - String content = "The quick brown fox jumps over the lazy dog " + verCnt; + String content = textContentSuffix + verCnt; ByteArrayInputStream inputStream = new ByteArrayInputStream(content.getBytes()); File txtFile = TempFileProvider.createTempFile(inputStream, getClass().getSimpleName(), ".txt"); PublicApiHttpClient.BinaryPayload payload = new PublicApiHttpClient.BinaryPayload(txtFile); @@ -579,7 +732,7 @@ public class NodeVersionsApiTest extends AbstractBaseApiTest nodes = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class); assertEquals(totalVerCnt, nodes.size()); - checkVersionHistoryAndContent(d1Id, nodes, verCnt, 1, minorVersion); + checkVersionHistoryAndContent(d1Id, nodes, verCnt, textContentSuffix, null, 1, minorVersion, false); // delete to trashcan/archive ... delete(URL_NODES, user1, d1Id, null, 204); 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 6b5d6c272b..d6e77264f8 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 @@ -54,9 +54,13 @@ public class Node protected UserInfo createdByUser; protected UserInfo modifiedByUser; + // 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; + protected Boolean isFolder; protected Boolean isFile; protected Boolean isLink; @@ -152,6 +156,16 @@ public class Node this.archivedByUser = archivedByUser; } + public String getVersionComment() + { + return versionComment; + } + + public void setVersionComment(String versionComment) + { + this.versionComment = versionComment; + } + public Boolean getIsFolder() { return isFolder;