Merged 5.2.N (5.2.1) to HEAD (5.2)

131742 rneamtu:       131742 rneamtu: SHA-1629 : Creating a link to file in a different location
            - Added support for multiple files in doclink.post webscript


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@132292 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Alan Davis
2016-11-03 13:56:56 +00:00
parent 74dc12f5c3
commit 53cbbd39fe
6 changed files with 776 additions and 621 deletions

View File

@@ -41,12 +41,23 @@
<![CDATA[{ "destinationNodeRef" : string }]]> <![CDATA[{ "destinationNodeRef" : string }]]>
</type> </type>
</request> </request>
<request>
<format>json</format>
<type>
<![CDATA[
{
"destinationNodeRef": string,
"multipleFiles": string
}
]]>
</type>
</request>
</requests> </requests>
<responses> <responses>
<response> <response>
<format>json</format> <format>json</format>
<type> <type>
<![CDATA[{ "linkNodeRef" : string }]]> <![CDATA[{ "result" : string }]]>
</type> </type>
</response> </response>
</responses> </responses>

View File

@@ -1,5 +1,16 @@
<#escape x as jsonUtils.encodeJSONString(x)> <#escape x as jsonUtils.encodeJSONString(x)>
{ {
"linkNodeRef": "${linkNodeRef}" "linkNodes" :
[
<#list results as result>
{
"nodeRef" : "${result.nodeRef}"
}
<#if result_has_next>,</#if>
</#list>
],
"successCount": "${successCount}",
"failureCount": "${failureCount}",
"overallSuccess": "${overallSuccess?c}"
} }
</#escape> </#escape>

View File

@@ -1846,6 +1846,7 @@
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="siteService" ref="SiteService" /> <property name="siteService" ref="SiteService" />
<property name="documentLinkService" ref="DocumentLinkService" /> <property name="documentLinkService" ref="DocumentLinkService" />
<property name="activityService" ref="activityService"/>
</bean> </bean>
<bean id="webscript.org.alfresco.repository.doclink.doclinks.delete" <bean id="webscript.org.alfresco.repository.doclink.doclinks.delete"
@@ -1854,6 +1855,7 @@
<property name="nodeService" ref="NodeService" /> <property name="nodeService" ref="NodeService" />
<property name="siteService" ref="SiteService" /> <property name="siteService" ref="SiteService" />
<property name="documentLinkService" ref="DocumentLinkService" /> <property name="documentLinkService" ref="DocumentLinkService" />
<property name="activityService" ref="activityService"/>
</bean> </bean>
<!-- CMM - Custom model import API --> <!-- CMM - Custom model import API -->

View File

@@ -23,122 +23,171 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.web.scripts.doclink; package org.alfresco.repo.web.scripts.doclink;
import java.util.Map; import java.io.StringWriter;
import java.util.StringTokenizer; import java.util.Map;
import java.util.StringTokenizer;
import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.DocumentLinkService; import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.repo.web.scripts.links.AbstractLinksWebScript;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.links.LinkInfo;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.repository.DocumentLinkService;
import org.alfresco.util.ParameterCheck; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.PropertyCheck; import org.alfresco.service.cmr.repository.NodeService;
import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.alfresco.service.cmr.site.SiteInfo;
import org.springframework.extensions.webscripts.Status; import org.alfresco.service.cmr.site.SiteService;
import org.springframework.extensions.webscripts.WebScriptException; import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
/** import org.apache.commons.logging.Log;
* This class contains common code for doclink webscripts controllers import org.apache.commons.logging.LogFactory;
* import org.json.JSONStringer;
* @author Ana Bozianu import org.json.simple.JSONObject;
* @since 5.1 import org.springframework.extensions.webscripts.DeclarativeWebScript;
*/ import org.springframework.extensions.webscripts.Status;
public abstract class AbstractDocLink extends DeclarativeWebScript import org.springframework.extensions.webscripts.WebScriptException;
{ import org.springframework.extensions.webscripts.WebScriptRequest;
private static String PARAM_STORE_TYPE = "store_type"; import org.springframework.extensions.webscripts.json.JSONWriter;
private static String PARAM_STORE_ID = "store_id";
private static String PARAM_ID = "id"; /**
private static String PARAM_SITE = "site"; * This class contains common code for doclink webscripts controllers
private static String PARAM_CONTAINER = "container"; *
private static String PARAM_PATH = "path"; * @author Ana Bozianu
* @since 5.1
protected NodeService nodeService; */
protected SiteService siteService; public abstract class AbstractDocLink extends DeclarativeWebScript
protected DocumentLinkService documentLinkService; {
private static String PARAM_STORE_TYPE = "store_type";
protected NodeRef parseNodeRefFromTemplateArgs(Map<String, String> templateVars) private static String PARAM_STORE_ID = "store_id";
{ private static String PARAM_ID = "id";
if (templateVars == null) private static String PARAM_SITE = "site";
{ private static String PARAM_CONTAINER = "container";
return null; private static String PARAM_PATH = "path";
}
private static final String ACTIVITY_TOOL = "documentLinkService";
String storeTypeArg = templateVars.get(PARAM_STORE_TYPE);
String storeIdArg = templateVars.get(PARAM_STORE_ID); protected NodeService nodeService;
String idArg = templateVars.get(PARAM_ID); protected SiteService siteService;
protected DocumentLinkService documentLinkService;
if (storeTypeArg != null) protected ActivityService activityService;
{
ParameterCheck.mandatoryString("storeTypeArg", storeTypeArg); private static Log logger = LogFactory.getLog(AbstractDocLink.class);
ParameterCheck.mandatoryString("storeIdArg", storeIdArg);
ParameterCheck.mandatoryString("idArg", idArg); protected NodeRef parseNodeRefFromTemplateArgs(Map<String, String> templateVars)
{
/* if (templateVars == null)
* NodeRef based request {
* <url>URL_BASE/{store_type}/{store_id}/{id}</url> return null;
*/ }
return new NodeRef(storeTypeArg, storeIdArg, idArg);
} String storeTypeArg = templateVars.get(PARAM_STORE_TYPE);
else String storeIdArg = templateVars.get(PARAM_STORE_ID);
{ String idArg = templateVars.get(PARAM_ID);
String siteArg = templateVars.get(PARAM_SITE);
String containerArg = templateVars.get(PARAM_CONTAINER); if (storeTypeArg != null)
String pathArg = templateVars.get(PARAM_PATH); {
ParameterCheck.mandatoryString("storeTypeArg", storeTypeArg);
if (siteArg != null) ParameterCheck.mandatoryString("storeIdArg", storeIdArg);
{ ParameterCheck.mandatoryString("idArg", idArg);
ParameterCheck.mandatoryString("siteArg", siteArg);
ParameterCheck.mandatoryString("containerArg", containerArg); /*
* NodeRef based request
/* * <url>URL_BASE/{store_type}/{store_id}/{id}</url>
* Site based request <url>URL_BASE/{site}/{container}</url> or */
* <url>URL_BASE/{site}/{container}/{path}</url> return new NodeRef(storeTypeArg, storeIdArg, idArg);
*/ }
SiteInfo site = siteService.getSite(siteArg); else
PropertyCheck.mandatory(this, "site", site); {
String siteArg = templateVars.get(PARAM_SITE);
NodeRef node = siteService.getContainer(site.getShortName(), containerArg); String containerArg = templateVars.get(PARAM_CONTAINER);
if (node == null) String pathArg = templateVars.get(PARAM_PATH);
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'container' variable"); if (siteArg != null)
} {
ParameterCheck.mandatoryString("siteArg", siteArg);
if (pathArg != null) ParameterCheck.mandatoryString("containerArg", containerArg);
{
// <url>URL_BASE/{site}/{container}/{path}</url> /*
StringTokenizer st = new StringTokenizer(pathArg, "/"); * Site based request <url>URL_BASE/{site}/{container}</url> or
while (st.hasMoreTokens()) * <url>URL_BASE/{site}/{container}/{path}</url>
{ */
String childName = st.nextToken(); SiteInfo site = siteService.getSite(siteArg);
node = nodeService.getChildByName(node, ContentModel.ASSOC_CONTAINS, childName); PropertyCheck.mandatory(this, "site", site);
if (node == null)
{ NodeRef node = siteService.getContainer(site.getShortName(), containerArg);
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'path' variable"); if (node == null)
} {
} throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'container' variable");
} }
return node; if (pathArg != null)
} {
} // <url>URL_BASE/{site}/{container}/{path}</url>
return null; StringTokenizer st = new StringTokenizer(pathArg, "/");
} while (st.hasMoreTokens())
{
public void setNodeService(NodeService nodeService) String childName = st.nextToken();
{ node = nodeService.getChildByName(node, ContentModel.ASSOC_CONTAINS, childName);
this.nodeService = nodeService; if (node == null)
} {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid 'path' variable");
public void setSiteService(SiteService siteService) }
{ }
this.siteService = siteService; }
}
return node;
public void setDocumentLinkService(DocumentLinkService documentLinkService) }
{ }
this.documentLinkService = documentLinkService; 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;
}
}

View File

@@ -23,95 +23,177 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.web.scripts.doclink; package org.alfresco.repo.web.scripts.doclink;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.ArrayList;
import java.util.Map; import java.util.HashMap;
import java.util.List;
import org.alfresco.repo.content.MimetypeMap; import java.util.Map;
import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.ParameterCheck; import org.alfresco.model.ContentModel;
import org.json.simple.JSONObject; import org.alfresco.repo.activities.ActivityType;
import org.json.simple.JSONValue; import org.alfresco.repo.content.MimetypeMap;
import org.json.simple.parser.ParseException; import org.alfresco.repo.security.permissions.AccessDeniedException;
import org.springframework.extensions.webscripts.Cache; import org.alfresco.service.cmr.repository.NodeRef;
import org.springframework.extensions.webscripts.Status; import org.alfresco.util.ParameterCheck;
import org.springframework.extensions.webscripts.WebScriptException; import org.json.simple.JSONArray;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
/** import org.json.simple.parser.ParseException;
* This class is the controller for the doclink.post webscript doclink.post is a import org.springframework.extensions.webscripts.Cache;
* webscript for creating a link of a document within a target destination import org.springframework.extensions.webscripts.Status;
* import org.springframework.extensions.webscripts.WebScriptException;
* @author Ana Bozianu import org.springframework.extensions.webscripts.WebScriptRequest;
* @since 5.1
*/ /**
public class DocLinkPost extends AbstractDocLink * 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
private static String PARAM_DESTINATION_NODE = "destinationNodeRef"; *
* @author Ana Bozianu
@Override * @since 5.1
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache) */
{ public class DocLinkPost extends AbstractDocLink
NodeRef sourceNodeRef = null; {
NodeRef destinationNodeRef = null; private static final String PARAM_DESTINATION_NODE = "destinationNodeRef";
private static final String PARAM_MULTIPLE_FILES = "multipleFiles";
/* Parse the template vars */
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars(); @Override
sourceNodeRef = parseNodeRefFromTemplateArgs(templateVars); protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
/* Parse the JSON content */ NodeRef sourceNodeRef = null;
JSONObject json = null; NodeRef destinationNodeRef = null;
String contentType = req.getContentType();
if (contentType != null && contentType.indexOf(';') != -1) /* Parse the template vars */
{ Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
contentType = contentType.substring(0, contentType.indexOf(';')); sourceNodeRef = parseNodeRefFromTemplateArgs(templateVars);
}
if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) /* Parse the JSON content */
{ JSONObject json = null;
try String contentType = req.getContentType();
{ if (contentType != null && contentType.indexOf(';') != -1)
json = (JSONObject) JSONValue.parseWithException(req.getContent().getContent()); {
} contentType = contentType.substring(0, contentType.indexOf(';'));
catch (IOException io) }
{ if (MimetypeMap.MIMETYPE_JSON.equals(contentType))
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); {
} try
catch (ParseException pe) {
{ json = (JSONObject) JSONValue.parseWithException(req.getContent().getContent());
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); }
} catch (IOException io)
} {
else throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage());
{ }
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "invalid request content type"); catch (ParseException pe)
} {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage());
/* Parse the destination NodeRef parameter */ }
String destinationNodeParam = (String) json.get(PARAM_DESTINATION_NODE); }
ParameterCheck.mandatoryString("destinationNodeParam", destinationNodeParam); else
destinationNodeRef = new NodeRef(destinationNodeParam); {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "invalid request content type");
/* Create link */ }
NodeRef linkNodeRef = null;
try /* Parse the destination NodeRef parameter */
{ String destinationNodeParam = (String) json.get(PARAM_DESTINATION_NODE);
linkNodeRef = documentLinkService.createDocumentLink(sourceNodeRef, destinationNodeRef); ParameterCheck.mandatoryString("destinationNodeParam", destinationNodeParam);
} destinationNodeRef = new NodeRef(destinationNodeParam);
catch (IllegalArgumentException ex)
{ List<NodeRef> nodeRefs = new ArrayList<NodeRef>();
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid Arguments: " + ex.getMessage()); if (json.containsKey(PARAM_MULTIPLE_FILES))
} {
catch (AccessDeniedException e) JSONArray multipleFiles = (JSONArray) json.get(PARAM_MULTIPLE_FILES);
{ for (int i = 0; i < multipleFiles.size(); i++)
throw new WebScriptException(Status.STATUS_FORBIDDEN, "You don't have permission to perform this operation"); {
} String nodeRefString = (String) multipleFiles.get(i);
if (nodeRefString != null)
/* Build response */ {
Map<String, Object> model = new HashMap<String, Object>(); try
model.put("linkNodeRef", linkNodeRef.toString()); {
return model; 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);
ArrayList<Object> linksResults = new ArrayList<Object>();
Map<String, Object> linkResult = new HashMap<String, Object>();
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);
}
linkResult.put("nodeRef", linkNodeRef.toString());
linksResults.add(linkResult);
successCount++;
}
}
}
failureCount = nodeRefs.size() - successCount;
Map<String, Object> model = new HashMap<String, Object>();
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)
{
if (ex.getMessage().contains("filelink") || ex.getMessage().contains("folderLink"))
{
return null;
}
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;
}
}

View File

@@ -23,411 +23,411 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L% * #L%
*/ */
package org.alfresco.repo.web.scripts.links; package org.alfresco.repo.web.scripts.links;
import java.io.IOException; import java.io.IOException;
import java.io.StringWriter; import java.io.StringWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingRequest;
import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.service.cmr.activities.ActivityService; import org.alfresco.service.cmr.activities.ActivityService;
import org.alfresco.service.cmr.links.LinkInfo; import org.alfresco.service.cmr.links.LinkInfo;
import org.alfresco.service.cmr.links.LinksService; import org.alfresco.service.cmr.links.LinksService;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.security.NoSuchPersonException; import org.alfresco.service.cmr.security.NoSuchPersonException;
import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.util.ScriptPagingDetails; import org.alfresco.util.ScriptPagingDetails;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONArray; import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser; import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException; import org.json.simple.parser.ParseException;
import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.Cache;
import org.springframework.extensions.webscripts.DeclarativeWebScript; import org.springframework.extensions.webscripts.DeclarativeWebScript;
import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException; import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest; import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.json.JSONWriter; import org.springframework.extensions.webscripts.json.JSONWriter;
/** /**
* @author Nick Burch * @author Nick Burch
* @since 4.0 * @since 4.0
*/ */
public abstract class AbstractLinksWebScript extends DeclarativeWebScript public abstract class AbstractLinksWebScript extends DeclarativeWebScript
{ {
public static final String LINKS_SERVICE_ACTIVITY_APP_NAME = "links"; public static final String LINKS_SERVICE_ACTIVITY_APP_NAME = "links";
protected static final String PARAM_MESSAGE = "message"; protected static final String PARAM_MESSAGE = "message";
protected static final String PARAM_ITEM = "item"; protected static final String PARAM_ITEM = "item";
private static Log logger = LogFactory.getLog(AbstractLinksWebScript.class); private static Log logger = LogFactory.getLog(AbstractLinksWebScript.class);
// Injected services // Injected services
protected NodeService nodeService; protected NodeService nodeService;
protected SiteService siteService; protected SiteService siteService;
protected LinksService linksService; protected LinksService linksService;
protected PersonService personService; protected PersonService personService;
protected ActivityService activityService; protected ActivityService activityService;
private String protocolsWhiteList = "http,https,ftp,mailto"; private String protocolsWhiteList = "http,https,ftp,mailto";
private ArrayList<String> allowedProtocols; private ArrayList<String> allowedProtocols;
private ArrayList<Pattern> xssPatterns; private ArrayList<Pattern> xssPatterns;
public void setNodeService(NodeService nodeService) public void setNodeService(NodeService nodeService)
{ {
this.nodeService = nodeService; this.nodeService = nodeService;
} }
public void setSiteService(SiteService siteService) public void setSiteService(SiteService siteService)
{ {
this.siteService = siteService; this.siteService = siteService;
} }
public void setLinksService(LinksService linksService) public void setLinksService(LinksService linksService)
{ {
this.linksService = linksService; this.linksService = linksService;
} }
public void setPersonService(PersonService personService) public void setPersonService(PersonService personService)
{ {
this.personService = personService; this.personService = personService;
} }
public void setActivityService(ActivityService activityService) public void setActivityService(ActivityService activityService)
{ {
this.activityService = activityService; this.activityService = activityService;
} }
public void setProtocolsWhiteList(String protocolsWhiteList) public void setProtocolsWhiteList(String protocolsWhiteList)
{ {
this.protocolsWhiteList = protocolsWhiteList; this.protocolsWhiteList = protocolsWhiteList;
} }
public void setXssRegexp(ArrayList<String> xssRegexp) public void setXssRegexp(ArrayList<String> xssRegexp)
{ {
xssPatterns = new ArrayList<>(xssRegexp.size()); xssPatterns = new ArrayList<>(xssRegexp.size());
for (String xssRegexpStr : xssRegexp) for (String xssRegexpStr : xssRegexp)
{ {
xssPatterns.add(Pattern.compile(xssRegexpStr)); xssPatterns.add(Pattern.compile(xssRegexpStr));
} }
} }
private boolean isProtocolAllowed(String protocol) private boolean isProtocolAllowed(String protocol)
{ {
// will be used default protocol prefix // will be used default protocol prefix
if (protocol.length() == 0) if (protocol.length() == 0)
{ {
return true; return true;
} }
if (allowedProtocols == null) if (allowedProtocols == null)
{ {
allowedProtocols = new ArrayList<String>(); allowedProtocols = new ArrayList<String>();
for (String delimProtocol : protocolsWhiteList.split(",")) for (String delimProtocol : protocolsWhiteList.split(","))
{ {
if (delimProtocol.trim().length() == 0) if (delimProtocol.trim().length() == 0)
{ {
continue; continue;
} }
allowedProtocols.add(delimProtocol.trim()); allowedProtocols.add(delimProtocol.trim());
} }
} }
return allowedProtocols.contains(protocol); return allowedProtocols.contains(protocol);
} }
private boolean isPossibleXSS(String url) private boolean isPossibleXSS(String url)
{ {
// check for null // check for null
if (xssPatterns == null) if (xssPatterns == null)
{ {
return false; return false;
} }
boolean result = false; boolean result = false;
for (Pattern pattern : xssPatterns) for (Pattern pattern : xssPatterns)
{ {
if (pattern.matcher(url).matches()) if (pattern.matcher(url).matches())
{ {
result = true; result = true;
} }
} }
return result; return result;
} }
private boolean isUrlCorrect(String url) private boolean isUrlCorrect(String url)
{ {
//default behavior if url absent //default behavior if url absent
if (url == null) if (url == null)
{ {
return true; return true;
} }
if (url.trim().length() == 0 || isPossibleXSS(url)) if (url.trim().length() == 0 || isPossibleXSS(url))
{ {
return false; return false;
} }
int colonPos = url.indexOf(":"); int colonPos = url.indexOf(":");
colonPos = colonPos > 0 ? colonPos : 0; colonPos = colonPos > 0 ? colonPos : 0;
String protocol = url.substring(0, colonPos); String protocol = url.substring(0, colonPos);
boolean result = isProtocolAllowed(protocol); boolean result = isProtocolAllowed(protocol);
//check for record host:port e.g.: localhost:8080 //check for record host:port e.g.: localhost:8080
if (!result) if (!result)
{ {
String secondUrlPart = url.substring(colonPos+1); String secondUrlPart = url.substring(colonPos+1);
int slashPos = secondUrlPart.indexOf("/"); int slashPos = secondUrlPart.indexOf("/");
slashPos = slashPos > 0 ? slashPos : secondUrlPart.length(); slashPos = slashPos > 0 ? slashPos : secondUrlPart.length();
String port = secondUrlPart.substring(0, slashPos); String port = secondUrlPart.substring(0, slashPos);
Pattern p = Pattern.compile("^[0-9]*$"); Pattern p = Pattern.compile("^[0-9]*$");
if (p.matcher(port).matches()) if (p.matcher(port).matches())
{ {
result = true; result = true;
} }
} }
return result; return result;
} }
protected String getOrNull(JSONObject json, String key) protected String getOrNull(JSONObject json, String key)
{ {
if (json.containsKey(key)) if (json.containsKey(key))
{ {
return (String)json.get(key); return (String)json.get(key);
} }
return null; return null;
} }
protected List<String> getTags(JSONObject json) protected List<String> getTags(JSONObject json)
{ {
List<String> tags = null; List<String> tags = null;
if (json.containsKey("tags")) if (json.containsKey("tags"))
{ {
// Is it "tags":"" or "tags":[...] ? // Is it "tags":"" or "tags":[...] ?
if (json.get("tags") instanceof String) if (json.get("tags") instanceof String)
{ {
// This is normally an empty string, skip // This is normally an empty string, skip
String tagsS = (String)json.get("tags"); String tagsS = (String)json.get("tags");
if ("".equals(tagsS)) if ("".equals(tagsS))
{ {
// No tags were given // No tags were given
return null; return null;
} }
else else
{ {
// Log, and treat as empty // Log, and treat as empty
// (We don't support "tags":"a,b,c" in these webscripts) // (We don't support "tags":"a,b,c" in these webscripts)
logger.warn("Unexpected tag data: " + tagsS); logger.warn("Unexpected tag data: " + tagsS);
return null; return null;
} }
} }
else else
{ {
tags = new ArrayList<String>(); tags = new ArrayList<String>();
JSONArray jsTags = (JSONArray)json.get("tags"); JSONArray jsTags = (JSONArray)json.get("tags");
for (int i=0; i<jsTags.size(); i++) for (int i=0; i<jsTags.size(); i++)
{ {
tags.add( (String)jsTags.get(i) ); tags.add( (String)jsTags.get(i) );
} }
} }
} }
return tags; return tags;
} }
/** /**
* Builds up a listing Paging request, based on the arguments * Builds up a listing Paging request, based on the arguments
* specified in the URL * specified in the URL
*/ */
protected PagingRequest buildPagingRequest(WebScriptRequest req) protected PagingRequest buildPagingRequest(WebScriptRequest req)
{ {
if (req.getParameter("page") == null || req.getParameter("pageSize") == null) if (req.getParameter("page") == null || req.getParameter("pageSize") == null)
{ {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Paging size parameters missing"); throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Paging size parameters missing");
} }
return new ScriptPagingDetails(req, 100); return new ScriptPagingDetails(req, 100);
} }
/** /**
* Generates an activity entry for the link * Generates an activity entry for the link
*/ */
protected void addActivityEntry(String event, LinkInfo link, SiteInfo site, protected void addActivityEntry(String event, LinkInfo link, SiteInfo site,
WebScriptRequest req, JSONObject json) WebScriptRequest req, JSONObject json)
{ {
// What page is this for? // What page is this for?
String page = req.getParameter("page"); String page = req.getParameter("page");
if (page == null && json != null) if (page == null && json != null)
{ {
if (json.containsKey("page")) if (json.containsKey("page"))
{ {
page = (String)json.get("page"); page = (String)json.get("page");
} }
} }
if (page == null) if (page == null)
{ {
// Default // Default
page = "links"; page = "links";
} }
try try
{ {
StringWriter activityJson = new StringWriter(); StringWriter activityJson = new StringWriter();
JSONWriter activity = new JSONWriter(activityJson); JSONWriter activity = new JSONWriter(activityJson);
activity.startObject(); activity.startObject();
activity.writeValue("title", link.getTitle()); activity.writeValue("title", link.getTitle());
activity.writeValue("page", page + "?linkId=" + link.getSystemName()); activity.writeValue("page", page + "?linkId=" + link.getSystemName());
activity.endObject(); activity.endObject();
activityService.postActivity( activityService.postActivity(
"org.alfresco.links.link-" + event, "org.alfresco.links.link-" + event,
site.getShortName(), site.getShortName(),
LINKS_SERVICE_ACTIVITY_APP_NAME, LINKS_SERVICE_ACTIVITY_APP_NAME,
activityJson.toString()); activityJson.toString());
} }
catch (Exception e) catch (Exception e)
{ {
// Warn, but carry on // Warn, but carry on
logger.warn("Error adding link " + event + " to activities feed", e); logger.warn("Error adding link " + event + " to activities feed", e);
} }
} }
protected Map<String, Object> renderLink(LinkInfo link) protected Map<String, Object> renderLink(LinkInfo link)
{ {
Map<String, Object> res = new HashMap<String, Object>(); Map<String, Object> res = new HashMap<String, Object>();
res.put("node", link.getNodeRef()); res.put("node", link.getNodeRef());
res.put("name", link.getSystemName()); res.put("name", link.getSystemName());
res.put("title", link.getTitle()); res.put("title", link.getTitle());
res.put("description", link.getDescription()); res.put("description", link.getDescription());
res.put("url", link.getURL()); res.put("url", link.getURL());
res.put("createdOn", link.getCreatedAt()); res.put("createdOn", link.getCreatedAt());
res.put("modifiedOn", link.getModifiedAt()); res.put("modifiedOn", link.getModifiedAt());
res.put("tags", link.getTags()); res.put("tags", link.getTags());
res.put("internal", link.isInternal()); res.put("internal", link.isInternal());
// FTL needs a script node of the person, if available // FTL needs a script node of the person, if available
String creator = link.getCreator(); String creator = link.getCreator();
Object creatorO; Object creatorO;
if ((null == creator) || !personService.personExists(creator)) if ((null == creator) || !personService.personExists(creator))
{ {
creatorO = ""; creatorO = "";
} }
else else
{ {
NodeRef person = personService.getPerson(creator); NodeRef person = personService.getPerson(creator);
creatorO = person; creatorO = person;
} }
res.put("creator", creatorO); res.put("creator", creatorO);
// We want blank instead of null // We want blank instead of null
for (String key : res.keySet()) for (String key : res.keySet())
{ {
if (res.get(key) == null) if (res.get(key) == null)
{ {
res.put(key, ""); res.put(key, "");
} }
} }
return res; return res;
} }
@Override @Override
protected Map<String, Object> executeImpl(WebScriptRequest req, protected Map<String, Object> executeImpl(WebScriptRequest req,
Status status, Cache cache) Status status, Cache cache)
{ {
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars(); Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
if (templateVars == null) if (templateVars == null)
{ {
String error = "No parameters supplied"; String error = "No parameters supplied";
throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); throw new WebScriptException(Status.STATUS_BAD_REQUEST, error);
} }
// Parse the JSON, if supplied // Parse the JSON, if supplied
JSONObject json = null; JSONObject json = null;
String contentType = req.getContentType(); String contentType = req.getContentType();
if (contentType != null && contentType.indexOf(';') != -1) if (contentType != null && contentType.indexOf(';') != -1)
{ {
contentType = contentType.substring(0, contentType.indexOf(';')); contentType = contentType.substring(0, contentType.indexOf(';'));
} }
if (MimetypeMap.MIMETYPE_JSON.equals(contentType)) if (MimetypeMap.MIMETYPE_JSON.equals(contentType))
{ {
JSONParser parser = new JSONParser(); JSONParser parser = new JSONParser();
try try
{ {
json = (JSONObject)parser.parse(req.getContent().getContent()); json = (JSONObject)parser.parse(req.getContent().getContent());
} }
catch (IOException io) catch (IOException io)
{ {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage()); throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + io.getMessage());
} }
catch(ParseException pe) catch(ParseException pe)
{ {
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage()); throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Invalid JSON: " + pe.getMessage());
} }
} }
// Get the site short name. Try quite hard to do so... // Get the site short name. Try quite hard to do so...
String siteName = templateVars.get("site"); String siteName = templateVars.get("site");
if (siteName == null) if (siteName == null)
{ {
siteName = req.getParameter("site"); siteName = req.getParameter("site");
} }
if (siteName == null && json != null) if (siteName == null && json != null)
{ {
if (json.containsKey("siteid")) if (json.containsKey("siteid"))
{ {
siteName = (String)json.get("siteid"); siteName = (String)json.get("siteid");
} }
else if (json.containsKey("site")) else if (json.containsKey("site"))
{ {
siteName = (String)json.get("site"); siteName = (String)json.get("site");
} }
} }
if (siteName == null) if (siteName == null)
{ {
String error = "No site given"; String error = "No site given";
throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); throw new WebScriptException(Status.STATUS_BAD_REQUEST, error);
} }
// Grab the requested site // Grab the requested site
SiteInfo site = siteService.getSite(siteName); SiteInfo site = siteService.getSite(siteName);
if (site == null) if (site == null)
{ {
String error = "Could not find site: " + siteName; String error = "Could not find site: " + siteName;
throw new WebScriptException(Status.STATUS_NOT_FOUND, error); throw new WebScriptException(Status.STATUS_NOT_FOUND, error);
} }
// Link name is optional // Link name is optional
String linkName = templateVars.get("path"); String linkName = templateVars.get("path");
//sanitise url //sanitise url
if (json != null) if (json != null)
{ {
String url = getOrNull(json, "url"); String url = getOrNull(json, "url");
if (!isUrlCorrect(url)) if (!isUrlCorrect(url))
{ {
String error = "Url not allowed"; String error = "Url not allowed";
throw new WebScriptException(Status.STATUS_BAD_REQUEST, error); throw new WebScriptException(Status.STATUS_BAD_REQUEST, error);
} }
} }
// Have the real work done // Have the real work done
return executeImpl(site, linkName, req, json, status, cache); return executeImpl(site, linkName, req, json, status, cache);
} }
protected abstract Map<String, Object> executeImpl(SiteInfo site, protected abstract Map<String, Object> executeImpl(SiteInfo site,
String linkName, WebScriptRequest req, JSONObject json, String linkName, WebScriptRequest req, JSONObject json,
Status status, Cache cache); Status status, Cache cache);
} }