diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.desc.xml index e95ce9b7a4..1f9f6aea3e 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.desc.xml +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.desc.xml @@ -41,12 +41,23 @@ + + json + + + + json - + diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.json.ftl index 321d5972da..e69a986e8a 100644 --- a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.json.ftl +++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.json.ftl @@ -1,5 +1,16 @@ <#escape x as jsonUtils.encodeJSONString(x)> { - "linkNodeRef": "${linkNodeRef}" + "linkNodes" : + [ + <#list results as result> + { + "nodeRef" : "${result}" + } + <#if result_has_next>, + + ], + "successCount": "${successCount}", + "failureCount": "${failureCount}", + "overallSuccess": "${overallSuccess?c}" } \ No newline at end of file diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml index 5e2e150b76..6ce6f52f33 100644 --- a/config/alfresco/web-scripts-application-context.xml +++ b/config/alfresco/web-scripts-application-context.xml @@ -1846,6 +1846,7 @@ + + diff --git a/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java b/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java index 8e38144138..43a54bf75e 100644 --- a/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java +++ b/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java @@ -23,122 +23,171 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.repo.web.scripts.doclink; - -import java.util.Map; -import java.util.StringTokenizer; - -import org.alfresco.model.ContentModel; -import org.alfresco.service.cmr.repository.DocumentLinkService; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.site.SiteInfo; -import org.alfresco.service.cmr.site.SiteService; -import org.alfresco.util.ParameterCheck; -import org.alfresco.util.PropertyCheck; -import org.springframework.extensions.webscripts.DeclarativeWebScript; -import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; - -/** - * This class contains common code for doclink webscripts controllers - * - * @author Ana Bozianu - * @since 5.1 - */ -public abstract class AbstractDocLink extends DeclarativeWebScript -{ - private static String PARAM_STORE_TYPE = "store_type"; - private static String PARAM_STORE_ID = "store_id"; - private static String PARAM_ID = "id"; - private static String PARAM_SITE = "site"; - private static String PARAM_CONTAINER = "container"; - private static String PARAM_PATH = "path"; - - protected NodeService nodeService; - protected SiteService siteService; - protected DocumentLinkService documentLinkService; - - protected NodeRef parseNodeRefFromTemplateArgs(Map templateVars) - { - if (templateVars == null) - { - return null; - } - - String storeTypeArg = templateVars.get(PARAM_STORE_TYPE); - String storeIdArg = templateVars.get(PARAM_STORE_ID); - String idArg = templateVars.get(PARAM_ID); - - if (storeTypeArg != null) - { - ParameterCheck.mandatoryString("storeTypeArg", storeTypeArg); - ParameterCheck.mandatoryString("storeIdArg", storeIdArg); - ParameterCheck.mandatoryString("idArg", idArg); - - /* - * NodeRef based request - * URL_BASE/{store_type}/{store_id}/{id} - */ - return new NodeRef(storeTypeArg, storeIdArg, idArg); - } - else - { - String siteArg = templateVars.get(PARAM_SITE); - String containerArg = templateVars.get(PARAM_CONTAINER); - String pathArg = templateVars.get(PARAM_PATH); - - if (siteArg != null) - { - ParameterCheck.mandatoryString("siteArg", siteArg); - ParameterCheck.mandatoryString("containerArg", containerArg); - - /* - * Site based request URL_BASE/{site}/{container} or - * URL_BASE/{site}/{container}/{path} - */ - SiteInfo site = siteService.getSite(siteArg); - PropertyCheck.mandatory(this, "site", site); - - NodeRef node = siteService.getContainer(site.getShortName(), containerArg); - if (node == null) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'container' variable"); - } - - if (pathArg != null) - { - // URL_BASE/{site}/{container}/{path} - StringTokenizer st = new StringTokenizer(pathArg, "/"); - while (st.hasMoreTokens()) - { - String childName = st.nextToken(); - node = nodeService.getChildByName(node, ContentModel.ASSOC_CONTAINS, childName); - if (node == null) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'path' variable"); - } - } - } - - return node; - } - } - return null; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public void setSiteService(SiteService siteService) - { - this.siteService = siteService; - } - - public void setDocumentLinkService(DocumentLinkService documentLinkService) - { - this.documentLinkService = documentLinkService; - } -} +package org.alfresco.repo.web.scripts.doclink; + +import java.io.StringWriter; +import java.util.Map; +import java.util.StringTokenizer; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.web.scripts.links.AbstractLinksWebScript; +import org.alfresco.service.cmr.activities.ActivityService; +import org.alfresco.service.cmr.links.LinkInfo; +import org.alfresco.service.cmr.repository.DocumentLinkService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.alfresco.util.ParameterCheck; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.json.JSONStringer; +import org.json.simple.JSONObject; +import org.springframework.extensions.webscripts.DeclarativeWebScript; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; +import org.springframework.extensions.webscripts.json.JSONWriter; + +/** + * This class contains common code for doclink webscripts controllers + * + * @author Ana Bozianu + * @since 5.1 + */ +public abstract class AbstractDocLink extends DeclarativeWebScript +{ + private static String PARAM_STORE_TYPE = "store_type"; + private static String PARAM_STORE_ID = "store_id"; + private static String PARAM_ID = "id"; + private static String PARAM_SITE = "site"; + private static String PARAM_CONTAINER = "container"; + private static String PARAM_PATH = "path"; + + private static final String ACTIVITY_TOOL = "documentLinkService"; + + protected NodeService nodeService; + protected SiteService siteService; + protected DocumentLinkService documentLinkService; + protected ActivityService activityService; + + private static Log logger = LogFactory.getLog(AbstractDocLink.class); + + protected NodeRef parseNodeRefFromTemplateArgs(Map templateVars) + { + if (templateVars == null) + { + return null; + } + + String storeTypeArg = templateVars.get(PARAM_STORE_TYPE); + String storeIdArg = templateVars.get(PARAM_STORE_ID); + String idArg = templateVars.get(PARAM_ID); + + if (storeTypeArg != null) + { + ParameterCheck.mandatoryString("storeTypeArg", storeTypeArg); + ParameterCheck.mandatoryString("storeIdArg", storeIdArg); + ParameterCheck.mandatoryString("idArg", idArg); + + /* + * NodeRef based request + * URL_BASE/{store_type}/{store_id}/{id} + */ + return new NodeRef(storeTypeArg, storeIdArg, idArg); + } + else + { + String siteArg = templateVars.get(PARAM_SITE); + String containerArg = templateVars.get(PARAM_CONTAINER); + String pathArg = templateVars.get(PARAM_PATH); + + if (siteArg != null) + { + ParameterCheck.mandatoryString("siteArg", siteArg); + ParameterCheck.mandatoryString("containerArg", containerArg); + + /* + * Site based request URL_BASE/{site}/{container} or + * URL_BASE/{site}/{container}/{path} + */ + SiteInfo site = siteService.getSite(siteArg); + PropertyCheck.mandatory(this, "site", site); + + NodeRef node = siteService.getContainer(site.getShortName(), containerArg); + if (node == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'container' variable"); + } + + if (pathArg != null) + { + // URL_BASE/{site}/{container}/{path} + StringTokenizer st = new StringTokenizer(pathArg, "/"); + while (st.hasMoreTokens()) + { + String childName = st.nextToken(); + node = nodeService.getChildByName(node, ContentModel.ASSOC_CONTAINS, childName); + if (node == null) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'path' variable"); + } + } + } + + return node; + } + } + return null; + } + + /** + * Generates an activity entry for the link + */ + + protected void addActivityEntry(String activityType, String title, String nodeRef, String site) + { + try + { + StringWriter activityJson = new StringWriter(); + JSONWriter activity = new JSONWriter(activityJson); + activity.startObject(); + activity.writeValue("title", title); + activity.writeValue("nodeRef", nodeRef); + activity.writeValue("page", "document-details?nodeRef=" + nodeRef); + activity.endObject(); + + activityService.postActivity( + activityType, + site, + ACTIVITY_TOOL, + activityJson.toString()); + } + catch (Exception e) + { + // Warn, but carry on + logger.warn("Error adding link event to activities feed", e); + } + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setDocumentLinkService(DocumentLinkService documentLinkService) + { + this.documentLinkService = documentLinkService; + } + + public void setActivityService(ActivityService activityService) + { + this.activityService = activityService; + } +} diff --git a/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java index d0feef5c06..1e9e74d374 100644 --- a/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java +++ b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java @@ -23,95 +23,169 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.repo.web.scripts.doclink; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import org.alfresco.repo.content.MimetypeMap; -import org.alfresco.repo.security.permissions.AccessDeniedException; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.util.ParameterCheck; -import org.json.simple.JSONObject; -import org.json.simple.JSONValue; -import org.json.simple.parser.ParseException; -import org.springframework.extensions.webscripts.Cache; -import org.springframework.extensions.webscripts.Status; -import org.springframework.extensions.webscripts.WebScriptException; -import org.springframework.extensions.webscripts.WebScriptRequest; - -/** - * This class is the controller for the doclink.post webscript doclink.post is a - * webscript for creating a link of a document within a target destination - * - * @author Ana Bozianu - * @since 5.1 - */ -public class DocLinkPost extends AbstractDocLink -{ - private static String PARAM_DESTINATION_NODE = "destinationNodeRef"; - - @Override - protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) - { - NodeRef sourceNodeRef = null; - NodeRef destinationNodeRef = null; - - /* Parse the template vars */ - Map templateVars = req.getServiceMatch().getTemplateVars(); - sourceNodeRef = parseNodeRefFromTemplateArgs(templateVars); - - /* Parse the JSON content */ - JSONObject json = null; - String contentType = req.getContentType(); - if (contentType != null && contentType.indexOf(';') != -1) - { - contentType = contentType.substring(0, contentType.indexOf(';')); - } - if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) - { - try - { - json = (JSONObject) JSONValue.parseWithException(req.getContent().getContent()); - } - catch (IOException io) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); - } - catch (ParseException pe) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); - } - } - else - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "invalid request content type"); - } - - /* Parse the destination NodeRef parameter */ - String destinationNodeParam = (String) json.get(PARAM_DESTINATION_NODE); - ParameterCheck.mandatoryString("destinationNodeParam", destinationNodeParam); - destinationNodeRef = new NodeRef(destinationNodeParam); - - /* Create link */ - NodeRef linkNodeRef = null; - try - { - linkNodeRef = documentLinkService.createDocumentLink(sourceNodeRef, destinationNodeRef); - } - catch (IllegalArgumentException ex) - { - throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid Arguments: " + ex.getMessage()); - } - catch (AccessDeniedException e) - { - throw new WebScriptException(Status.STATUS_FORBIDDEN, "You don't have permission to perform this operation"); - } - - /* Build response */ - Map model = new HashMap(); - model.put("linkNodeRef", linkNodeRef.toString()); - return model; - } -} +package org.alfresco.repo.web.scripts.doclink; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.activities.ActivityType; +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.ParameterCheck; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; +import org.json.simple.parser.ParseException; +import org.springframework.extensions.webscripts.Cache; +import org.springframework.extensions.webscripts.Status; +import org.springframework.extensions.webscripts.WebScriptException; +import org.springframework.extensions.webscripts.WebScriptRequest; + +/** + * This class is the controller for the doclink.post webscript doclink.post is a + * webscript for creating a link of a document within a target destination + * + * @author Ana Bozianu + * @since 5.1 + */ +public class DocLinkPost extends AbstractDocLink +{ + private static final String PARAM_DESTINATION_NODE = "destinationNodeRef"; + private static final String PARAM_MULTIPLE_FILES = "multipleFiles"; + + @Override + protected Map executeImpl(WebScriptRequest req, Status status, Cache cache) + { + NodeRef sourceNodeRef = null; + NodeRef destinationNodeRef = null; + + /* Parse the template vars */ + Map templateVars = req.getServiceMatch().getTemplateVars(); + sourceNodeRef = parseNodeRefFromTemplateArgs(templateVars); + + /* Parse the JSON content */ + JSONObject json = null; + String contentType = req.getContentType(); + if (contentType != null && contentType.indexOf(';') != -1) + { + contentType = contentType.substring(0, contentType.indexOf(';')); + } + if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) + { + try + { + json = (JSONObject) JSONValue.parseWithException(req.getContent().getContent()); + } + catch (IOException io) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); + } + catch (ParseException pe) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); + } + } + else + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "invalid request content type"); + } + + /* Parse the destination NodeRef parameter */ + String destinationNodeParam = (String) json.get(PARAM_DESTINATION_NODE); + ParameterCheck.mandatoryString("destinationNodeParam", destinationNodeParam); + destinationNodeRef = new NodeRef(destinationNodeParam); + + List nodeRefs = new ArrayList(); + if (json.containsKey(PARAM_MULTIPLE_FILES)) + { + JSONArray multipleFiles = (JSONArray) json.get(PARAM_MULTIPLE_FILES); + for (int i = 0; i < multipleFiles.size(); i++) + { + String nodeRefString = (String) multipleFiles.get(i); + if (nodeRefString != null) + { + try + { + NodeRef nodeRefToCreateLink = new NodeRef(nodeRefString); + nodeRefs.add(nodeRefToCreateLink); + } + catch (AlfrescoRuntimeException ex) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid Arguments: " + ex.getMessage()); + } + } + } + } + else + { + nodeRefs.add(sourceNodeRef); + } + + // getSite for destination folder + String siteName = siteService.getSiteShortName(destinationNodeRef); + + List linksResults = new ArrayList(); + NodeRef linkNodeRef = null; + int successCount = 0; + int failureCount = 0; + + if (nodeRefs != null && nodeRefs.size() > 0) + { + for (NodeRef sourceNode : nodeRefs) + { + /* Create link */ + linkNodeRef = createLink(destinationNodeRef, sourceNode); + + if (linkNodeRef != null) + { + String sourceName = (String) nodeService.getProperty(sourceNode, ContentModel.PROP_NAME); + if (siteName != null) + { + addActivityEntry(ActivityType.DOCLINK_CREATED, sourceName, sourceNode.toString(), siteName); + } + linksResults.add(linkNodeRef.toString()); + successCount++; + } + } + } + + failureCount = nodeRefs.size() - successCount; + Map model = new HashMap(); + model.put("results", linksResults); + model.put("successCount", successCount); + model.put("failureCount", failureCount); + model.put("overallSuccess", failureCount == 0); + return model; + } + + /** + * Create link for sourceNodeRef in destinationNodeRef location + * + * @param destinationNodeRef + * @param sourceNodeRef + * @return + */ + private NodeRef createLink(NodeRef destinationNodeRef, NodeRef sourceNodeRef) + { + NodeRef linkNodeRef = null; + try + { + linkNodeRef = documentLinkService.createDocumentLink(sourceNodeRef, destinationNodeRef); + } + catch (IllegalArgumentException ex) + { + throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid Arguments: " + ex.getMessage()); + } + catch (AccessDeniedException e) + { + throw new WebScriptException(Status.STATUS_FORBIDDEN, "You don't have permission to perform this operation"); + } + return linkNodeRef; + } +} diff --git a/source/test-java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java b/source/test-java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java index a0fa15047d..9ae49bc4e5 100644 --- a/source/test-java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java +++ b/source/test-java/org/alfresco/repo/web/scripts/WebScriptTestSuite.java @@ -45,6 +45,7 @@ import org.alfresco.repo.web.scripts.groups.GroupsTest; import org.alfresco.repo.web.scripts.invitation.InvitationWebScriptTest; import org.alfresco.repo.web.scripts.invite.InviteServiceTest; import org.alfresco.repo.web.scripts.links.LinksRestApiTest; +import org.alfresco.repo.web.scripts.node.NodeWebScripTest; import org.alfresco.repo.web.scripts.person.PersonServiceTest; import org.alfresco.repo.web.scripts.preference.PreferenceServiceTest; import org.alfresco.repo.web.scripts.publishing.PublishingRestApiTest; @@ -117,6 +118,7 @@ public class WebScriptTestSuite extends TestSuite suite.addTestSuite( ReadOnlyTransactionInGetRestApiTest.class ); suite.addTestSuite( CustomModelImportTest.class ); suite.addTestSuite( SurfConfigTest.class ); + suite.addTestSuite( NodeWebScripTest.class ); // This uses a slightly different context // As such, we can't run it in the same suite as the others, // due to finalisers closing caches when we're not looking diff --git a/source/test-java/org/alfresco/repo/web/scripts/node/NodeWebScripTest.java b/source/test-java/org/alfresco/repo/web/scripts/node/NodeWebScripTest.java index 533e393105..f01490d4ff 100644 --- a/source/test-java/org/alfresco/repo/web/scripts/node/NodeWebScripTest.java +++ b/source/test-java/org/alfresco/repo/web/scripts/node/NodeWebScripTest.java @@ -26,9 +26,12 @@ package org.alfresco.repo.web.scripts.node; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.node.archive.NodeArchiveService; @@ -37,6 +40,7 @@ import org.alfresco.repo.site.SiteModel; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.web.scripts.BaseWebScriptTest; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.MutableAuthenticationService; @@ -44,10 +48,12 @@ import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.springframework.context.support.AbstractRefreshableApplicationContext; @@ -377,4 +383,209 @@ public class NodeWebScripTest extends BaseWebScriptTest AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO); sendRequest(req, Status.STATUS_FORBIDDEN); } + + @SuppressWarnings("unchecked") + public void testLinkCreation() throws Exception + { + // Create a folder within the DocLib + NodeRef siteDocLib = siteService.getContainer(TEST_SITE.getShortName(), SiteService.DOCUMENT_LIBRARY); + + String testFolder1Name = "testingLinkCreationFolder1"; + Map testFolderProps = new HashMap(); + testFolderProps.put(ContentModel.PROP_NAME, testFolder1Name); + NodeRef testFolder1 = nodeService.createNode(siteDocLib, ContentModel.ASSOC_CONTAINS, + QName.createQName("testingLinkCreationFolder1"), ContentModel.TYPE_FOLDER, testFolderProps).getChildRef(); + + JSONObject jsonReq = null; + JSONObject json = null; + JSONArray jsonArray = new JSONArray(); + JSONArray jsonLinkNodes = null; + JSONObject jsonLinkNode = null; + + //Create files in the folder + NodeRef testFile1 = createNode(testFolder1, "testingLinkCreationFile1", ContentModel.TYPE_CONTENT, + AuthenticationUtil.getAdminUserName()); + NodeRef testFile2 = createNode(testFolder1, "testingLinkCreationFile2", ContentModel.TYPE_CONTENT, + AuthenticationUtil.getAdminUserName()); + NodeRef testFile3 = createNode(testFolder1, "testingLinkCreationFile3", ContentModel.TYPE_CONTENT, + AuthenticationUtil.getAdminUserName()); + + //Create another folder in the folder + String testFolder2Name = "testingLinkCreationFolder2"; + testFolderProps = new HashMap(); + testFolderProps.put(ContentModel.PROP_NAME, testFolder2Name); + NodeRef testFolder2 = nodeService.createNode(siteDocLib, ContentModel.ASSOC_CONTAINS, + QName.createQName("testingLinkCreationFolder2"), ContentModel.TYPE_FOLDER, testFolderProps).getChildRef(); + + // Create link to file1 in same folder + Request req = new Request("POST", "/api/node/doclink/" + testFile1.getStoreRef().getProtocol() + "/" + + testFile1.getStoreRef().getIdentifier() + "/" + testFile1.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", testFolder1.toString()); + + jsonArray.add(testFile1.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_OK)); + jsonLinkNodes = (JSONArray) json.get("linkNodes"); + assertNotNull(jsonLinkNodes); + assertEquals(1, jsonLinkNodes.size()); + assertEquals("true", json.get("overallSuccess")); + assertEquals("1", json.get("successCount")); + assertEquals("0", json.get("failureCount")); + + jsonLinkNode = (JSONObject) jsonLinkNodes.get(0); + String nodeRef = (String) jsonLinkNode.get("nodeRef"); + NodeRef file1Link = new NodeRef(nodeRef); + + //Check that app:linked aspect is added on sourceNode + assertEquals(true, nodeService.hasAspect(testFile1, ApplicationModel.ASPECT_LINKED)); + assertEquals(true, nodeService.exists(file1Link)); + nodeService.deleteNode(file1Link); + assertEquals(false, nodeService.hasAspect(testFile1, ApplicationModel.ASPECT_LINKED)); + + //Create link to testFolder2 in same folder (testFolder1) + req = new Request("POST", "/api/node/doclink/" + testFolder2.getStoreRef().getProtocol() + "/" + + testFolder2.getStoreRef().getIdentifier() + "/" + testFolder2.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", testFolder1.toString()); + jsonArray = new JSONArray(); + jsonArray.add(testFolder2.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_OK)); + jsonLinkNodes = (JSONArray) json.get("linkNodes"); + assertNotNull(jsonLinkNodes); + assertEquals(1, jsonLinkNodes.size()); + assertEquals("true", json.get("overallSuccess")); + assertEquals("1", json.get("successCount")); + assertEquals("0", json.get("failureCount")); + + jsonLinkNode = (JSONObject) jsonLinkNodes.get(0); + nodeRef = (String) jsonLinkNode.get("nodeRef"); + NodeRef folder2Link = new NodeRef(nodeRef); + assertEquals(true, nodeService.hasAspect(testFolder2, ApplicationModel.ASPECT_LINKED)); + assertEquals(true, nodeService.exists(folder2Link)); + + // create another link of testFolder2 in siteDocLib + req = new Request("POST", "/api/node/doclink/" + testFolder2.getStoreRef().getProtocol() + "/" + + testFolder2.getStoreRef().getIdentifier() + "/" + testFolder2.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", siteDocLib.toString()); + jsonArray = new JSONArray(); + jsonArray.add(testFolder2.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_OK)); + jsonLinkNodes = (JSONArray) json.get("linkNodes"); + assertNotNull(jsonLinkNodes); + assertEquals(1, jsonLinkNodes.size()); + assertEquals("true", json.get("overallSuccess")); + assertEquals("1", json.get("successCount")); + assertEquals("0", json.get("failureCount")); + + jsonLinkNode = (JSONObject) jsonLinkNodes.get(0); + nodeRef = (String) jsonLinkNode.get("nodeRef"); + NodeRef folder2Link2 = new NodeRef(nodeRef); + + // delete folder2Link and check that aspect exists since we have another + // link for testFolder2 + nodeService.deleteNode(folder2Link); + assertEquals(true, nodeService.hasAspect(testFolder2, ApplicationModel.ASPECT_LINKED)); + nodeService.deleteNode(folder2Link2); + assertEquals(false, nodeService.hasAspect(testFolder2, ApplicationModel.ASPECT_LINKED)); + + // Create link to testFile1, testFile2 and testFile3 in same testFolder1 + req = new Request("POST", "/api/node/doclink/" + testFolder1.getStoreRef().getProtocol() + "/" + + testFolder1.getStoreRef().getIdentifier() + "/" + testFolder1.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", testFolder1.toString()); + jsonArray = new JSONArray(); + jsonArray.add(testFile1.toString()); + jsonArray.add(testFile2.toString()); + jsonArray.add(testFile3.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_OK)); + jsonLinkNodes = (JSONArray) json.get("linkNodes"); + assertNotNull(jsonLinkNodes); + assertEquals(3, jsonLinkNodes.size()); + assertEquals("true", json.get("overallSuccess")); + assertEquals("3", json.get("successCount")); + assertEquals("0", json.get("failureCount")); + + NodeRef fileLink = null; + List fileLinks = new ArrayList(); + for (int i = 0; i < jsonLinkNodes.size(); i++) + { + jsonLinkNode = (JSONObject) jsonLinkNodes.get(i); + nodeRef = (String) jsonLinkNode.get("nodeRef"); + fileLink = new NodeRef(nodeRef); + fileLinks.add(fileLink); + assertEquals(true, nodeService.exists(fileLink)); + } + + //try to create another link in the same location - an exception should be thrown + req = new Request("POST", "/api/node/doclink/" + testFolder1.getStoreRef().getProtocol() + "/" + + testFolder1.getStoreRef().getIdentifier() + "/" + testFolder1.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", testFolder1.toString()); + jsonArray = new JSONArray(); + jsonArray.add(testFile1.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_BAD_REQUEST)); + + // delete all 3 files and check that the links are deleted too + nodeService.deleteNode(testFile1); + nodeService.deleteNode(testFile2); + nodeService.deleteNode(testFile3); + for (NodeRef linkNodeRef : fileLinks) + { + assertEquals(false, nodeService.exists(linkNodeRef)); + } + + //try create a link to a site + SiteInfo site2 = createSite("Site2TestingNodeCreateLink"); + NodeRef siteNodeRef = site2.getNodeRef(); + + req = new Request("POST", "/api/node/doclink/" + testFolder1.getStoreRef().getProtocol() + "/" + + testFolder1.getStoreRef().getIdentifier() + "/" + testFolder1.getId()); + jsonReq = new JSONObject(); + jsonReq.put("destinationNodeRef", testFolder1.toString()); + jsonArray = new JSONArray(); + jsonArray.add(siteNodeRef.toString()); + jsonReq.put("multipleFiles", jsonArray); + req.setBody(jsonReq.toString().getBytes()); + req.setType(MimetypeMap.MIMETYPE_JSON); + + json = asJSON(sendRequest(req, Status.STATUS_BAD_REQUEST)); + + siteService.deleteSite(site2.getShortName()); + nodeArchiveService.purgeArchivedNode(nodeArchiveService.getArchivedNode(siteNodeRef)); + } + + private NodeRef createNode(NodeRef parentNode, String nodeCmName, QName nodeType, String ownerUserName) + { + QName childName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, nodeCmName); + + Map props = new HashMap(); + props.put(ContentModel.PROP_NAME, nodeCmName); + ChildAssociationRef childAssoc = nodeService.createNode(parentNode, + ContentModel.ASSOC_CONTAINS, + childName, + nodeType, + props); + return childAssoc.getChildRef(); + } } \ No newline at end of file