mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Added stream content 'kind of' web script, modified ContentGet webscript to use common code, create GET thumbnail method based on stream content kind of web script
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@9395 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
<webscript>
|
||||
<shortname>Thumbnails</shortname>
|
||||
<description>Delete a thumbnail for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<format default="json"/>
|
||||
<authentication>guest</authentication>
|
||||
<transaction>required</transaction>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<webscript>
|
||||
<webscript kind="org.alfresco.repository.content.stream">
|
||||
<shortname>Thumbnails</shortname>
|
||||
<description>Create a new thumbnail for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property?}/thumbnails</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property?}/thumbnails</url>
|
||||
<format default="json"/>
|
||||
<description>Get a named thumbnail for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<format default="">argument</format>
|
||||
<authentication>guest</authentication>
|
||||
<transaction>required</transaction>
|
||||
</webscript>
|
@@ -13,17 +13,25 @@ function main()
|
||||
}
|
||||
|
||||
// Get the thumbnail name from the JSON content
|
||||
var thumbnailName = pathSegments[8];
|
||||
var thumbnailName = url.templateArgs.thumbnailname; //pathSegments[pathSegments.length - 1];
|
||||
|
||||
// 404 if no thumbnail name found
|
||||
if (thumbnailName == null)
|
||||
{
|
||||
status.setCode(status.STATUS_NOT_FOUND, "Thumbnail name was not provided");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the thumbnail ...
|
||||
|
||||
// Get the thumbnail
|
||||
var thumbnail = node.getThumbnail(thumbnailName);
|
||||
if (thumbnail == null)
|
||||
{
|
||||
// 404 since no thumbnail was found
|
||||
status.setCode(status.STATUS_NOT_FOUND, "Thumbnail was not found");
|
||||
}
|
||||
|
||||
// Place the details of the thumbnail into the model, this will be used to stream the content to the client
|
||||
model.contentNode = thumbnail;
|
||||
}
|
||||
|
||||
main();
|
@@ -1,8 +1,8 @@
|
||||
<webscript>
|
||||
<shortname>Thumbnails</shortname>
|
||||
<description>Update a thumbnail for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property}/thumbnails/{thumbnailname}</url>
|
||||
<format default="json"/>
|
||||
<authentication>guest</authentication>
|
||||
<transaction>required</transaction>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<webscript>
|
||||
<shortname>Thumbnails</shortname>
|
||||
<description>Get a named thumbnail for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property?}/thumbnails/{thumbnailname}</url>
|
||||
<format default=""/>
|
||||
<description>Get the thumbnails for a content resource</description>
|
||||
<url>/api/node/{store_type}/{store_id}/{id}/content{property}/thumbnails</url>
|
||||
<url>/api/path/{store_type}/{store_id}/{id}/content{property}/thumbnails</url>
|
||||
<format default="json"/>
|
||||
<authentication>guest</authentication>
|
||||
<transaction>required</transaction>
|
||||
</webscript>
|
@@ -34,7 +34,6 @@ function main()
|
||||
model.node = node;
|
||||
model.thumbnailName = thumbnailName;
|
||||
model.thumbnail = thumbnail;
|
||||
|
||||
}
|
||||
|
||||
main();
|
@@ -187,7 +187,7 @@
|
||||
</bean>
|
||||
|
||||
<!-- Content Retrieval -->
|
||||
<bean id="webscript.org.alfresco.repository.store.content.get" class="org.alfresco.repo.web.scripts.bean.ContentGet" parent="webscript">
|
||||
<bean id="webscript.org.alfresco.repository.store.content.get" class="org.alfresco.repo.web.scripts.content.ContentGet" parent="webscript">
|
||||
<property name="repository" ref="repositoryHelper" />
|
||||
<property name="namespaceService" ref="NamespaceService" />
|
||||
<property name="permissionService" ref="PermissionService" />
|
||||
@@ -196,6 +196,13 @@
|
||||
<property name="mimetypeService" ref="MimetypeService" />
|
||||
</bean>
|
||||
|
||||
<bean id="webscript.org.alfresco.repository.content.stream" class="org.alfresco.repo.web.scripts.content.StreamContent" parent="webscript">
|
||||
<property name="permissionService" ref="PermissionService" />
|
||||
<property name="nodeService" ref="NodeService" />
|
||||
<property name="contentService" ref="ContentService" />
|
||||
<property name="mimetypeService" ref="MimetypeService" />
|
||||
</bean>
|
||||
|
||||
<!-- Remote Store service - AVM -->
|
||||
<bean id="webscript.org.alfresco.repository.store.remoteavm.get" class="org.alfresco.repo.web.scripts.bean.AVMRemoteStore" parent="webscript">
|
||||
<property name="mimetypeService" ref="MimetypeService" />
|
||||
|
@@ -131,11 +131,12 @@ public abstract class BaseWebScriptTest extends TestCase
|
||||
MockHttpServletResponse response = BaseWebScriptTest.getServer().submitRequest(method, url, new HashMap<String, String>(), body, contentType);
|
||||
if (expectedStatus > 0 && expectedStatus != response.getStatus())
|
||||
{
|
||||
if (response.getStatus() == 500)
|
||||
{
|
||||
//if (response.getStatus() == 500)
|
||||
//{
|
||||
System.out.println(response.getContentAsString());
|
||||
}
|
||||
fail("Expected status code " + expectedStatus + " , " + response.getStatus() + " was returned.");
|
||||
//}
|
||||
|
||||
fail("Expected status code " + expectedStatus + " , " + response.getStatus() + " was returned");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
@@ -1,283 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.web.scripts.bean;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.URLEncoder;
|
||||
import org.alfresco.web.scripts.AbstractWebScript;
|
||||
import org.alfresco.web.scripts.Cache;
|
||||
import org.alfresco.web.scripts.WebScriptException;
|
||||
import org.alfresco.web.scripts.WebScriptRequest;
|
||||
import org.alfresco.web.scripts.WebScriptResponse;
|
||||
import org.alfresco.web.scripts.servlet.WebScriptServletRequest;
|
||||
import org.alfresco.web.scripts.servlet.WebScriptServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Content Retrieval Service
|
||||
*
|
||||
* Stream content from the Repository.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class ContentGet extends AbstractWebScript
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(ContentGet.class);
|
||||
|
||||
private static final String NODE_URL = "/api/node/content/{0}/{1}/{2}/{3}";
|
||||
|
||||
// Component dependencies
|
||||
private Repository repository;
|
||||
private NamespaceService namespaceService;
|
||||
private PermissionService permissionService;
|
||||
private NodeService nodeService;
|
||||
private ContentService contentService;
|
||||
private MimetypeService mimetypeService;
|
||||
|
||||
/**
|
||||
* @param repository
|
||||
*/
|
||||
public void setRepository(Repository repository)
|
||||
{
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param namespaceService
|
||||
*/
|
||||
public void setNamespaceService(NamespaceService namespaceService)
|
||||
{
|
||||
this.namespaceService = namespaceService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param permissionService
|
||||
*/
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeService
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentService
|
||||
*/
|
||||
public void setContentService(ContentService contentService)
|
||||
{
|
||||
this.contentService = contentService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mimetypeService
|
||||
*/
|
||||
public void setMimetypeService(MimetypeService mimetypeService)
|
||||
{
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
|
||||
*/
|
||||
public void execute(WebScriptRequest req, WebScriptResponse res)
|
||||
throws IOException
|
||||
{
|
||||
// NOTE: This web script must be executed in a HTTP Servlet environment
|
||||
if (!(req instanceof WebScriptServletRequest))
|
||||
{
|
||||
throw new WebScriptException("Content retrieval must be executed in HTTP Servlet environment");
|
||||
}
|
||||
HttpServletRequest httpReq = ((WebScriptServletRequest)req).getHttpServletRequest();
|
||||
HttpServletResponse httpRes = ((WebScriptServletResponse)res).getHttpServletResponse();
|
||||
|
||||
// convert web script URL to node reference in Repository
|
||||
String match = req.getServiceMatch().getPath();
|
||||
String[] matchParts = match.split("/");
|
||||
String extensionPath = req.getExtensionPath();
|
||||
String[] extParts = extensionPath == null ? new String[1] : extensionPath.split("/");
|
||||
String[] path = new String[extParts.length -1];
|
||||
System.arraycopy(extParts, 1, path, 0, extParts.length -1);
|
||||
NodeRef nodeRef = repository.findNodeRef(matchParts[2], path);
|
||||
if (nodeRef == null)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find " + matchParts[2] + " reference " + Arrays.toString(path));
|
||||
}
|
||||
|
||||
// determine content property
|
||||
QName propertyQName = ContentModel.PROP_CONTENT;
|
||||
String contentPart = extParts[0];
|
||||
if (contentPart.length() > 0 && contentPart.charAt(0) == ';')
|
||||
{
|
||||
if (contentPart.length() < 2)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Content property malformed");
|
||||
}
|
||||
String propertyName = contentPart.substring(1);
|
||||
if (propertyName.length() > 0)
|
||||
{
|
||||
propertyQName = QName.createQName(propertyName, namespaceService);
|
||||
}
|
||||
}
|
||||
|
||||
// determine attachment
|
||||
boolean attach = Boolean.valueOf(req.getParameter("a"));
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieving content from node ref " + nodeRef.toString() + " (property: " + propertyQName.toString() + ") (attach: " + attach + ")");
|
||||
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
}
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
long modifiedSince = httpReq.getDateHeader("If-Modified-Since");
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
httpRes.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle attachment
|
||||
if (attach == true)
|
||||
{
|
||||
// set header based on filename - will force a Save As from the browse if it doesn't recognize it
|
||||
// this is better than the default response of the browser trying to display the contents
|
||||
httpRes.setHeader("Content-Disposition", "attachment");
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
if (reader == null || !reader.exists())
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to locate content for node ref " + nodeRef + " (property: " + propertyQName.toString() + ")");
|
||||
}
|
||||
|
||||
// establish mimetype
|
||||
String mimetype = reader.getMimetype();
|
||||
if (mimetype == null || mimetype.length() == 0)
|
||||
{
|
||||
mimetype = MimetypeMap.MIMETYPE_BINARY;
|
||||
int extIndex = extensionPath.lastIndexOf('.');
|
||||
if (extIndex != -1)
|
||||
{
|
||||
String ext = extensionPath.substring(extIndex + 1);
|
||||
String mt = mimetypeService.getMimetypesByExtension().get(ext);
|
||||
if (mt != null)
|
||||
{
|
||||
mimetype = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set mimetype for the content and the character encoding + length for the stream
|
||||
httpRes.setContentType(mimetype);
|
||||
httpRes.setCharacterEncoding(reader.getEncoding());
|
||||
httpRes.setHeader("Content-Length", Long.toString(reader.getSize()));
|
||||
|
||||
// set caching
|
||||
Cache cache = new Cache();
|
||||
cache.setNeverCache(false);
|
||||
cache.setMustRevalidate(true);
|
||||
cache.setLastModified(modified);
|
||||
res.setCache(cache);
|
||||
|
||||
// get the content and stream directly to the response output stream
|
||||
// assuming the repository is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
reader.getContent(res.getOutputStream());
|
||||
}
|
||||
catch (SocketException e1)
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Client aborted stream read:\n\tnode: " + nodeRef + "\n\tcontent: " + reader);
|
||||
}
|
||||
catch (ContentIOException e2)
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Client aborted stream read:\n\tnode: " + nodeRef + "\n\tcontent: " + reader);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied directly in the reponse. This generally means a browser will
|
||||
* attempt to open the content directly if possible, else it will prompt to save the file.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name end element to return on the url (used by the browser on Save)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateNodeURL(NodeRef ref, String name)
|
||||
{
|
||||
return MessageFormat.format(NODE_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
URLEncoder.encode(name) } );
|
||||
}
|
||||
}
|
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.web.scripts.content;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.model.Repository;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.URLEncoder;
|
||||
import org.alfresco.web.scripts.WebScriptException;
|
||||
import org.alfresco.web.scripts.WebScriptRequest;
|
||||
import org.alfresco.web.scripts.WebScriptResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Content Retrieval Service
|
||||
*
|
||||
* Stream content from the Repository.
|
||||
*
|
||||
* @author davidc
|
||||
*/
|
||||
public class ContentGet extends StreamContent
|
||||
{
|
||||
// Logger
|
||||
@SuppressWarnings("unused")
|
||||
private static final Log logger = LogFactory.getLog(ContentGet.class);
|
||||
|
||||
private static final String NODE_URL = "/api/node/content/{0}/{1}/{2}/{3}";
|
||||
|
||||
// Component dependencies
|
||||
private Repository repository;
|
||||
private NamespaceService namespaceService;
|
||||
|
||||
/**
|
||||
* @param repository
|
||||
*/
|
||||
public void setRepository(Repository repository)
|
||||
{
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param namespaceService
|
||||
*/
|
||||
public void setNamespaceService(NamespaceService namespaceService)
|
||||
{
|
||||
this.namespaceService = namespaceService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
|
||||
*/
|
||||
public void execute(WebScriptRequest req, WebScriptResponse res)
|
||||
throws IOException
|
||||
{
|
||||
// convert web script URL to node reference in Repository
|
||||
String match = req.getServiceMatch().getPath();
|
||||
String[] matchParts = match.split("/");
|
||||
String extensionPath = req.getExtensionPath();
|
||||
String[] extParts = extensionPath == null ? new String[1] : extensionPath.split("/");
|
||||
String[] path = new String[extParts.length -1];
|
||||
System.arraycopy(extParts, 1, path, 0, extParts.length -1);
|
||||
NodeRef nodeRef = repository.findNodeRef(matchParts[2], path);
|
||||
if (nodeRef == null)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find " + matchParts[2] + " reference " + Arrays.toString(path));
|
||||
}
|
||||
|
||||
// determine content property
|
||||
QName propertyQName = ContentModel.PROP_CONTENT;
|
||||
String contentPart = extParts[0];
|
||||
if (contentPart.length() > 0 && contentPart.charAt(0) == ';')
|
||||
{
|
||||
if (contentPart.length() < 2)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_BAD_REQUEST, "Content property malformed");
|
||||
}
|
||||
String propertyName = contentPart.substring(1);
|
||||
if (propertyName.length() > 0)
|
||||
{
|
||||
propertyQName = QName.createQName(propertyName, namespaceService);
|
||||
}
|
||||
}
|
||||
|
||||
// determine attachment
|
||||
boolean attach = Boolean.valueOf(req.getParameter("a"));
|
||||
|
||||
// Stream the content
|
||||
streamContent(req, res, nodeRef, propertyQName, attach);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to generate a URL to a content node for downloading content from the server.
|
||||
* The content is supplied directly in the reponse. This generally means a browser will
|
||||
* attempt to open the content directly if possible, else it will prompt to save the file.
|
||||
*
|
||||
* @param ref NodeRef of the content node to generate URL for (cannot be null)
|
||||
* @param name File name end element to return on the url (used by the browser on Save)
|
||||
*
|
||||
* @return URL to download the content from the specified node
|
||||
*/
|
||||
public final static String generateNodeURL(NodeRef ref, String name)
|
||||
{
|
||||
return MessageFormat.format(NODE_URL, new Object[] {
|
||||
ref.getStoreRef().getProtocol(),
|
||||
ref.getStoreRef().getIdentifier(),
|
||||
ref.getId(),
|
||||
URLEncoder.encode(name) } );
|
||||
}
|
||||
}
|
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Copyright (C) 2005-2008 Alfresco Software Limited.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
|
||||
* This program 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 General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
* As a special exception to the terms and conditions of version 2.0 of
|
||||
* the GPL, you may redistribute this Program in connection with Free/Libre
|
||||
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
||||
* FLOSS exception. You should have recieved a copy of the text describing
|
||||
* the FLOSS exception, and it is also available here:
|
||||
* http://www.alfresco.com/legal/licensing"
|
||||
*/
|
||||
package org.alfresco.repo.web.scripts.content;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.net.SocketException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.repo.content.MimetypeMap;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentService;
|
||||
import org.alfresco.service.cmr.repository.MimetypeService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.scripts.AbstractWebScript;
|
||||
import org.alfresco.web.scripts.Cache;
|
||||
import org.alfresco.web.scripts.Container;
|
||||
import org.alfresco.web.scripts.Description;
|
||||
import org.alfresco.web.scripts.ScriptContent;
|
||||
import org.alfresco.web.scripts.Status;
|
||||
import org.alfresco.web.scripts.WebScriptException;
|
||||
import org.alfresco.web.scripts.WebScriptRequest;
|
||||
import org.alfresco.web.scripts.WebScriptResponse;
|
||||
import org.alfresco.web.scripts.WebScriptStatus;
|
||||
import org.alfresco.web.scripts.servlet.WebScriptServletRequest;
|
||||
import org.alfresco.web.scripts.servlet.WebScriptServletResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Web script 'type' that can be used when the binary data of a content property needs to be streamed back to the client
|
||||
* as the result of executing the web script.
|
||||
*
|
||||
* @author Roy Wetherall
|
||||
*/
|
||||
public class StreamContent extends AbstractWebScript
|
||||
{
|
||||
// Logger
|
||||
private static final Log logger = LogFactory.getLog(StreamContent.class);
|
||||
|
||||
protected PermissionService permissionService;
|
||||
protected NodeService nodeService;
|
||||
protected ContentService contentService;
|
||||
protected MimetypeService mimetypeService;
|
||||
|
||||
// Script Context
|
||||
private String basePath;
|
||||
private ScriptContent executeScript;
|
||||
|
||||
/**
|
||||
* @param mimetypeService
|
||||
*/
|
||||
public void setMimetypeService(MimetypeService mimetypeService)
|
||||
{
|
||||
this.mimetypeService = mimetypeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param permissionService
|
||||
*/
|
||||
public void setPermissionService(PermissionService permissionService)
|
||||
{
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nodeService
|
||||
*/
|
||||
public void setNodeService(NodeService nodeService)
|
||||
{
|
||||
this.nodeService = nodeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentService
|
||||
*/
|
||||
public void setContentService(ContentService contentService)
|
||||
{
|
||||
this.contentService = contentService;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.alfresco.web.scripts.AbstractWebScript#init(org.alfresco.web.scripts.WebScriptRegistry)
|
||||
*/
|
||||
@Override
|
||||
public void init(Container container, Description description)
|
||||
{
|
||||
super.init(container, description);
|
||||
|
||||
// Test for "execute" script
|
||||
basePath = getDescription().getId();
|
||||
String scriptPath = basePath + ".js";
|
||||
executeScript = container.getScriptProcessor().findScript(scriptPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
|
||||
*/
|
||||
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
|
||||
{
|
||||
// retrieve requested format
|
||||
String format = req.getFormat();
|
||||
|
||||
try
|
||||
{
|
||||
// construct model for script / template
|
||||
Status status = new Status();
|
||||
Cache cache = new Cache(getDescription().getRequiredCache());
|
||||
Map<String, Object> model = executeImpl(req, status, cache);
|
||||
if (model == null)
|
||||
{
|
||||
model = new HashMap<String, Object>(8, 1.0f);
|
||||
}
|
||||
model.put("status", status);
|
||||
model.put("cache", cache);
|
||||
|
||||
// execute script if it exists
|
||||
if (executeScript != null)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Executing script " + executeScript.getPathDescription());
|
||||
|
||||
Map<String, Object> scriptModel = createScriptParameters(req, res, model);
|
||||
// add return model allowing script to add items to template model
|
||||
Map<String, Object> returnModel = new HashMap<String, Object>(8, 1.0f);
|
||||
scriptModel.put("model", returnModel);
|
||||
executeScript(executeScript, scriptModel);
|
||||
mergeScriptModelIntoTemplateModel(returnModel, model);
|
||||
}
|
||||
|
||||
// Get the content parameters from the model
|
||||
NodeRef nodeRef = (NodeRef)model.get("contentNode");
|
||||
if (nodeRef == null)
|
||||
{
|
||||
throw new WebScriptException("The content node was not specified so the content cannot be streamed to the client");
|
||||
}
|
||||
QName propertyQName = null;
|
||||
String contentProperty = (String)model.get("contentProperty");
|
||||
if (contentProperty == null)
|
||||
{
|
||||
// default to the standard content property
|
||||
propertyQName = ContentModel.PROP_CONTENT;
|
||||
}
|
||||
else
|
||||
{
|
||||
propertyQName = QName.createQName(contentProperty);
|
||||
}
|
||||
Boolean attachBoolean = (Boolean)model.get("attach");
|
||||
boolean attach = false;
|
||||
if (attachBoolean != null)
|
||||
{
|
||||
attach = attachBoolean.booleanValue();
|
||||
}
|
||||
|
||||
// is a redirect to a status specific template required?
|
||||
if (status.getRedirect())
|
||||
{
|
||||
// create model for template rendering
|
||||
Map<String, Object> templateModel = createTemplateParameters(req, res, model);
|
||||
sendStatus(req, res, status, cache, format, templateModel);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Stream the content
|
||||
streamContent(req, res, nodeRef, propertyQName, attach);
|
||||
}
|
||||
}
|
||||
catch(Throwable e)
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Caught exception & redirecting to status template: " + e.getMessage());
|
||||
|
||||
// extract status code, if specified
|
||||
int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||
if (e instanceof WebScriptException)
|
||||
{
|
||||
statusCode = ((WebScriptException)e).getStatus();
|
||||
}
|
||||
|
||||
// send status
|
||||
Status status = new Status();
|
||||
status.setCode(statusCode);
|
||||
status.setMessage(e.getMessage());
|
||||
status.setException(e);
|
||||
Cache cache = new Cache();
|
||||
cache.setNeverCache(true);
|
||||
Map<String, Object> customModel = new HashMap<String, Object>(8, 1.0f);
|
||||
customModel.put("status", status);
|
||||
Map<String, Object> templateModel = createTemplateParameters(req, res, customModel);
|
||||
sendStatus(req, res, status, cache, format, templateModel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge script generated model into template-ready model
|
||||
*
|
||||
* @param scriptModel script model
|
||||
* @param templateModel template model
|
||||
*/
|
||||
final private void mergeScriptModelIntoTemplateModel(Map<String, Object> scriptModel, Map<String, Object> templateModel)
|
||||
{
|
||||
for (Map.Entry<String, Object> entry : scriptModel.entrySet())
|
||||
{
|
||||
// retrieve script model value
|
||||
Object value = entry.getValue();
|
||||
Object templateValue = getContainer().getScriptProcessor().unwrapValue(value);
|
||||
templateModel.put(entry.getKey(), templateValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute custom Java logic
|
||||
*
|
||||
* @param req Web Script request
|
||||
* @param status Web Script status
|
||||
* @return custom service model
|
||||
* @deprecated
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Map<String, Object> executeImpl(WebScriptRequest req, WebScriptStatus status)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute custom Java logic
|
||||
*
|
||||
* @param req Web Script request
|
||||
* @param status Web Script status
|
||||
* @return custom service model
|
||||
* @deprecated
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status)
|
||||
{
|
||||
return executeImpl(req, new WebScriptStatus(status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute custom Java logic
|
||||
*
|
||||
* @param req Web Script request
|
||||
* @param status Web Script status
|
||||
* @param cache Web Script cache
|
||||
* @return custom service model
|
||||
*/
|
||||
protected Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
|
||||
{
|
||||
// NOTE: Redirect to those web scripts implemented before cache support and v2.9
|
||||
return executeImpl(req, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a template (of given format) to the Web Script Response
|
||||
*
|
||||
* @param format template format (null, default format)
|
||||
* @param model data model to render
|
||||
* @param writer where to output
|
||||
*/
|
||||
final protected void renderFormatTemplate(String format, Map<String, Object> model, Writer writer)
|
||||
{
|
||||
format = (format == null) ? "" : format;
|
||||
String templatePath = basePath + "." + format + ".ftl";
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Rendering template '" + templatePath + "'");
|
||||
|
||||
renderTemplate(templatePath, model, writer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the content on a given node's content property to the response of the web script.
|
||||
*
|
||||
* @param req request
|
||||
* @param res response
|
||||
* @param nodeRef the node reference
|
||||
* @param propertyQName the content property name
|
||||
* @param attach indicates whether the content should be streamed as an attachment or not
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void streamContent(WebScriptRequest req, WebScriptResponse res, NodeRef nodeRef, QName propertyQName, boolean attach)
|
||||
throws IOException
|
||||
{
|
||||
// NOTE: This web script must be executed in a HTTP Servlet environment
|
||||
if (!(req instanceof WebScriptServletRequest))
|
||||
{
|
||||
throw new WebScriptException("Content retrieval must be executed in HTTP Servlet environment");
|
||||
}
|
||||
HttpServletRequest httpReq = ((WebScriptServletRequest)req).getHttpServletRequest();
|
||||
HttpServletResponse httpRes = ((WebScriptServletResponse)res).getHttpServletResponse();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Retrieving content from node ref " + nodeRef.toString() + " (property: " + propertyQName.toString() + ") (attach: " + attach + ")");
|
||||
|
||||
// check that the user has at least READ_CONTENT access - else redirect to the login page
|
||||
if (permissionService.hasPermission(nodeRef, PermissionService.READ_CONTENT) == AccessStatus.DENIED)
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_FORBIDDEN, "Permission denied");
|
||||
}
|
||||
|
||||
// check If-Modified-Since header and set Last-Modified header as appropriate
|
||||
Date modified = (Date)nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIED);
|
||||
long modifiedSince = httpReq.getDateHeader("If-Modified-Since");
|
||||
if (modifiedSince > 0L)
|
||||
{
|
||||
// round the date to the ignore millisecond value which is not supplied by header
|
||||
long modDate = (modified.getTime() / 1000L) * 1000L;
|
||||
if (modDate <= modifiedSince)
|
||||
{
|
||||
httpRes.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle attachment
|
||||
if (attach == true)
|
||||
{
|
||||
// set header based on filename - will force a Save As from the browse if it doesn't recognize it
|
||||
// this is better than the default response of the browser trying to display the contents
|
||||
httpRes.setHeader("Content-Disposition", "attachment");
|
||||
}
|
||||
|
||||
// get the content reader
|
||||
ContentReader reader = contentService.getReader(nodeRef, propertyQName);
|
||||
if (reader == null || !reader.exists())
|
||||
{
|
||||
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to locate content for node ref " + nodeRef + " (property: " + propertyQName.toString() + ")");
|
||||
}
|
||||
|
||||
// establish mimetype
|
||||
String mimetype = reader.getMimetype();
|
||||
String extensionPath = req.getExtensionPath();
|
||||
if (mimetype == null || mimetype.length() == 0)
|
||||
{
|
||||
mimetype = MimetypeMap.MIMETYPE_BINARY;
|
||||
int extIndex = extensionPath.lastIndexOf('.');
|
||||
if (extIndex != -1)
|
||||
{
|
||||
String ext = extensionPath.substring(extIndex + 1);
|
||||
String mt = mimetypeService.getMimetypesByExtension().get(ext);
|
||||
if (mt != null)
|
||||
{
|
||||
mimetype = mt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set mimetype for the content and the character encoding + length for the stream
|
||||
httpRes.setContentType(mimetype);
|
||||
httpRes.setCharacterEncoding(reader.getEncoding());
|
||||
httpRes.setHeader("Content-Length", Long.toString(reader.getSize()));
|
||||
|
||||
// set caching
|
||||
Cache cache = new Cache();
|
||||
cache.setNeverCache(false);
|
||||
cache.setMustRevalidate(true);
|
||||
cache.setLastModified(modified);
|
||||
res.setCache(cache);
|
||||
|
||||
// get the content and stream directly to the response output stream
|
||||
// assuming the repository is capable of streaming in chunks, this should allow large files
|
||||
// to be streamed directly to the browser response stream.
|
||||
try
|
||||
{
|
||||
reader.getContent(res.getOutputStream());
|
||||
}
|
||||
catch (SocketException e1)
|
||||
{
|
||||
// the client cut the connection - our mission was accomplished apart from a little error message
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Client aborted stream read:\n\tnode: " + nodeRef + "\n\tcontent: " + reader);
|
||||
}
|
||||
catch (ContentIOException e2)
|
||||
{
|
||||
if (logger.isInfoEnabled())
|
||||
logger.info("Client aborted stream read:\n\tnode: " + nodeRef + "\n\tcontent: " + reader);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -105,17 +105,27 @@ public class ThumbnailServiceTest extends BaseWebScriptTest
|
||||
if (this.contentService.getTransformer(MimetypeMap.MIMETYPE_PDF, MimetypeMap.MIMETYPE_FLASH) != null)
|
||||
{
|
||||
String url = "/api/node/" + pdfNode.getStoreRef().getProtocol() + "/" + pdfNode.getStoreRef().getIdentifier() + "/" + pdfNode.getId() + "/content/thumbnails";
|
||||
System.out.println(url);
|
||||
|
||||
JSONObject tn = new JSONObject();
|
||||
tn.put("thumbnailName", "webpreview");
|
||||
System.out.println(tn.toString());
|
||||
|
||||
MockHttpServletResponse response = this.postRequest(url, 200, tn.toString(), "application/json");
|
||||
//JSONObject result = new JSONObject(response.getContentAsString());
|
||||
|
||||
System.out.println(response.getContentAsString());
|
||||
}
|
||||
|
||||
// Do a image transformation (medium)
|
||||
String url = "/api/node/" + jpgNode.getStoreRef().getProtocol() + "/" + jpgNode.getStoreRef().getIdentifier() + "/" + jpgNode.getId() + "/content/thumbnails";
|
||||
JSONObject tn = new JSONObject();
|
||||
tn.put("thumbnailName", "medium");
|
||||
MockHttpServletResponse response = this.postRequest(url, 200, tn.toString(), "application/json");
|
||||
System.out.println(response.getContentAsString());
|
||||
JSONObject result = new JSONObject(response.getContentAsString());
|
||||
String thumbnailUrl = result.getString("url").substring(17);
|
||||
|
||||
System.out.println(thumbnailUrl);
|
||||
response = getRequest(thumbnailUrl, 200);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user