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
new file mode 100644
index 0000000000..13a2b1ae45
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.desc.xml
@@ -0,0 +1,52 @@
+
+ Create document link
+ Creates a link to a document in a given folder
+ /api/node/doclink/{store_type}/{store_id}/{id}
+ /api/site/doclink/{site}/{container}/{path}
+ /api/site/doclink/{site}/{container}
+ argument
+ user
+ required
+
+
+ store_type
+ Protocol for the source store, e.g. workspace or versionstore.
+
+
+ store_id
+ The source identifier, which may be specific to the protocol, e.g. spacesstore.
+
+
+ id
+ The identifier of the source node.
+
+
+ site
+ The site ID of the source.
+
+
+ container
+ The container folder for the site.
+
+
+ path
+ The path to the source node in the site's container.
+
+
+
+
+ json
+
+
+
+
+
+
+
+ json
+
+
+
+
+
+
\ No newline at end of file
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
new file mode 100644
index 0000000000..321d5972da
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclink.post.json.ftl
@@ -0,0 +1,5 @@
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "linkNodeRef": "${linkNodeRef}"
+}
+#escape>
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.desc.xml
new file mode 100644
index 0000000000..0ac1c912c7
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.desc.xml
@@ -0,0 +1,57 @@
+
+ Delete links of a document
+ Delete all links having as given destination
+ /api/node/doclink/{store_type}/{store_id}/{id}/delete
+ /api/site/doclink/{site}/{container}/{path}/delete
+ /api/site/doclink/{site}/{container}/delete
+ argument
+ user
+ required
+
+
+ store_type
+ Protocol for the destination store, e.g. workspace or versionstore.
+
+
+ store_id
+ The destination identifier, which may be specific to the protocol, e.g. spacesstore.
+
+
+ id
+ The identifier of the destination node.
+
+
+ site
+ The site ID of the destination.
+
+
+ container
+ The container folder for the site.
+
+
+ path
+ The path to the destination node in the site's container.
+
+
+
+
+
+
+ json
+
+
+ {
+ "total": "3",
+ "deleted": "1",
+ "error details": [
+ "workspace://SpacesStore/48afc4b7-d91a-4167-8b9e-db14961b8fed 04290001 Access Denied. You do not have the appropriate permissions to perform this operation.",
+ "workspace://SpacesStore/10b18342-8e70-4dc0-ba73-4b0a6fdf5a9b 04290002 Access Denied. You do not have the appropriate permissions to perform this operation."
+ ]
+ }
+
+ ]]>
+
+
+
+
\ No newline at end of file
diff --git a/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.json.ftl b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.json.ftl
new file mode 100644
index 0000000000..9e6087fbcb
--- /dev/null
+++ b/config/alfresco/templates/webscripts/org/alfresco/repository/doclink/doclinks.delete.json.ftl
@@ -0,0 +1,13 @@
+<#escape x as jsonUtils.encodeJSONString(x)>
+{
+ "found": "${total_count}",
+ "deleted": "${deleted_count}",
+ "error details" :
+ [
+ <#list error_details?keys as key>
+ "${key} - ${error_details[key]}"
+ <#if key_has_next>,#if>
+ #list>
+ ]
+}
+#escape>
\ 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 1dda62127e..f11410bf84 100644
--- a/config/alfresco/web-scripts-application-context.xml
+++ b/config/alfresco/web-scripts-application-context.xml
@@ -1840,5 +1840,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java b/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java
new file mode 100644
index 0000000000..8c894a68c4
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/doclink/AbstractDocLink.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+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;
+ }
+}
diff --git a/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java
new file mode 100644
index 0000000000..e062c30dd3
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinkPost.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+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;
+ }
+}
diff --git a/source/java/org/alfresco/repo/web/scripts/doclink/DocLinksDelete.java b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinksDelete.java
new file mode 100644
index 0000000000..974e642aae
--- /dev/null
+++ b/source/java/org/alfresco/repo/web/scripts/doclink/DocLinksDelete.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2005-2015 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.web.scripts.doclink;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.service.cmr.repository.DeleteLinksStatusReport;
+import org.alfresco.service.cmr.repository.NodeRef;
+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 DocLinksDelete extends AbstractDocLink
+{
+
+ @Override
+ protected Map executeImpl(WebScriptRequest req, Status status, Cache cache)
+ {
+ NodeRef destinationNodeRef = null;
+
+ /* Parse the template vars */
+ Map templateVars = req.getServiceMatch().getTemplateVars();
+ destinationNodeRef = parseNodeRefFromTemplateArgs(templateVars);
+
+ /* Delete links */
+ DeleteLinksStatusReport report;
+ try
+ {
+ report = documentLinkService.deleteLinksToDocument(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("total_count", report.getTotalLinksFoundCount());
+ model.put("deleted_count", report.getDeletedLinksCount());
+
+ Map errorDetails = new HashMap();
+ Iterator> it = report.getErrorDetails().entrySet().iterator();
+ while (it.hasNext())
+ {
+ Map.Entry pair = it.next();
+
+ Throwable th = pair.getValue();
+
+ errorDetails.put(pair.getKey().toString(), th.getMessage());
+ }
+
+ model.put("error_details", errorDetails);
+
+
+ return model;
+ }
+}