diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/content.get.desc.xml b/config/alfresco/templates/webscripts/org/alfresco/cmis/content.get.desc.xml
index be1ea72b51..7d29ae356e 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/cmis/content.get.desc.xml
+++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/content.get.desc.xml
@@ -7,18 +7,18 @@
- /cmis/i/{id}/content{property}?a={attach?}
- /cmis/s/{store}/i/{id}/content{property}?a={attach?}
+ /cmis/i/{id}/content{property}?a={attach?}&streamId={streamId?}
+ /cmis/s/{store}/i/{id}/content{property}?a={attach?}&streamId={streamId?}
- /cmis/p{path}/content{property}?a={attach?}
- /cmis/s/{store}/p{path}/content{property}?a={attach?}
+ /cmis/p{path}/content{property}?a={attach?}&streamId={streamId?}
+ /cmis/s/{store}/p{path}/content{property}?a={attach?}&streamId={streamId?}
- /api/node/content{property}/{store_type}/{store_id}/{id}?a={attach?}
- /api/path/content{property}/{store_type}/{store_id}/{path}?a={attach?}
- /api/avmpath/content{property}/{store_id}/{avmpath}?a={attach?}
- /api/node/{store_type}/{store_id}/{id}/content{property}?a={attach?}
- /api/path/{store_type}/{store_id}/{path}/content{property}?a={attach?}
+ /api/node/content{property}/{store_type}/{store_id}/{id}?a={attach?}&streamId={streamId?}
+ /api/path/content{property}/{store_type}/{store_id}/{path}?a={attach?}&streamId={streamId?}
+ /api/avmpath/content{property}/{store_id}/{avmpath}?a={attach?}&streamId={streamId?}
+ /api/node/{store_type}/{store_id}/{id}/content{property}?a={attach?}&streamId={streamId?}
+ /api/path/{store_type}/{store_id}/{path}/content{property}?a={attach?}&streamId={streamId?}
@@ -43,6 +43,10 @@
if true, force download of content as attachment
false
+
+ streamId
+ if provided, download the rendition of the content as identified by the stream id
+
argument
diff --git a/config/alfresco/templates/webscripts/org/alfresco/cmis/lib/atomentry.lib.atom.ftl b/config/alfresco/templates/webscripts/org/alfresco/cmis/lib/atomentry.lib.atom.ftl
index b73bc48019..a7d26770a1 100644
--- a/config/alfresco/templates/webscripts/org/alfresco/cmis/lib/atomentry.lib.atom.ftl
+++ b/config/alfresco/templates/webscripts/org/alfresco/cmis/lib/atomentry.lib.atom.ftl
@@ -53,7 +53,9 @@
[@linksLib.linkstream node "enclosure"/]
[@linksLib.linknodeedit node/]
[@linksLib.linkstream node "edit-media"/]
-[@documentCMISLinks node=node renditionfilter=renditionfilter/]
+[@documentCMISLinks node=node/]
+[#local renditionsMap=cmisrenditions(node, renditionfilter)/]
+[@renditionLinks node renditionsMap/]
${xmldate(node.properties.created)}
[@contentsummary node/]
${node.name?xml}
@@ -65,12 +67,13 @@
[#if includeallowableactions][@allowableactions node/][/#if]
[@relationships node includerelationships includeallowableactions propfilter/]
[#if includeacl][@aclreport node/][/#if]
+[@renditions renditionsMap/]
${node.name?xml}
[/@entry]
[/#macro]
-[#macro documentCMISLinks node renditionfilter="cmis:none"]
+[#macro documentCMISLinks node]
[@linksLib.linkallowableactions node/]
[@linksLib.linkrelationships node/]
[@linksLib.linkpolicies node/]
@@ -86,10 +89,6 @@
[/#if]
[@linksLib.linktype node/]
[@linksLib.linkservice/]
-[#local nodeMap=cmisrenditions(node, renditionfilter)/]
-[#list nodeMap.renditions as rendition]
-[@linksLib.linkrendition node=node rendition=rendition renditionNode=nodeMap.renditionNodes[rendition_index]/]
-[/#list]
[/#macro]
[#-- --]
@@ -103,7 +102,7 @@
[#macro folder node renditionfilter="cmis:none" propfilter="*" typesfilter="any" includeallowableactions=false includerelationships="none" includeacl=false ns="" depth=1 maxdepth=1 relativePathSegment="" nestedkind=""]
[@entry ns]
${node.properties.creator!""}
-${node.id} [#-- TODO --]
+[@contentstream node/]
urn:uuid:${node.id}
[@linksLib.linknodeself node/]
[@linksLib.linknodeedit node/]
@@ -205,19 +204,16 @@
[#if row.nodes?? && row.nodes?size == 1][#assign node = row.nodes?first/][/#if]
[#if node??]
${node.properties.creator!""}
-[#-- TODO: review if consistent with ATOM --]
-[#if node.isDocument]
- [@contentstream node/]
-[#else]
- ${node.id} [#-- TODO --]
-[/#if]
+[@contentstream node/]
urn:uuid:${node.id}
[@linksLib.linknodeself node/]
[@linksLib.linknodeedit node/]
[#if node.isDocument]
[@linksLib.linkstream node "enclosure"/]
[@linksLib.linkstream node "edit-media"/]
- [@documentCMISLinks node=node renditionfilter=renditionfilter/]
+ [@documentCMISLinks node=node/]
+ [#assign renditionsMap=cmisrenditions(node, renditionfilter)/]
+ [@renditionLinks node renditionsMap/]
[#else]
[@folderCMISLinks node=node/]
[/#if]
@@ -254,6 +250,9 @@
[#if node??]
[#if includeallowableactions][@allowableactions node/][/#if]
[@relationships node includerelationships includeallowableactions/]
+[#if node.isDocument]
+[@renditions renditionsMap/]
+[/#if]
[/#if]
[/@entry]
@@ -284,7 +283,6 @@
[/@entry]
[/#macro]
-
[#-- --]
[#-- CMIS Properties --]
[#-- --]
@@ -463,6 +461,30 @@
[/#macro]
+[#-- --]
+[#-- Renditions --]
+[#-- --]
+
+[#macro renditionLinks node renditionsMap]
+[#list renditionsMap.renditions as rendition]
+[@linksLib.linkrendition node=node rendition=rendition renditionNode=renditionsMap.renditionNodes[rendition_index]/]
+[/#list]
+[/#macro]
+
+[#macro renditions renditionsMap]
+[#list renditionsMap.renditions as rendition]
+
+ ${rendition.streamId}
+ ${rendition.mimeType}
+ [#if rendition.length??]${rendition.length?c}[#else]-1[/#if]
+ ${rendition.kind.label}
+ [#if rendition.title??]${rendition.title}[/#if]
+ [#if rendition.height??]${rendition.height?c}[/#if]
+ [#if rendition.width??]${rendition.width?c}[/#if]
+
+[/#list]
+[/#macro]
+
[#-- --]
[#-- ATOM Entry for Type Definition --]
[#-- --]
@@ -769,7 +791,7 @@
[#macro foldersummary node][#if node.properties.description??]${node.properties.description?xml}[#elseif node.properties.title??]${node.properties.title?xml}[#else][/#if][/#macro]
[#-- Helper to render Alfresco content type to Atom content type --]
-[#macro contenttype type][#if type == "text/html"]text[#elseif type == "text/xhtml"]xhtml[#elseif type == "text/plain"]text<#else>${type}[/#if][/#macro]
+[#macro contenttype type][#if type == "text/html"]text[#elseif type == "text/xhtml"]xhtml[#elseif type == "text/plain"]text[#else]${type}[/#if][/#macro]
[#-- Helper to render atom content element --]
[#macro contentstream node][/#macro]
diff --git a/config/alfresco/web-scripts-application-context.xml b/config/alfresco/web-scripts-application-context.xml
index 9e20351882..0f18bbd28e 100644
--- a/config/alfresco/web-scripts-application-context.xml
+++ b/config/alfresco/web-scripts-application-context.xml
@@ -310,7 +310,9 @@
+
+
diff --git a/source/java/org/alfresco/repo/web/scripts/content/ContentGet.java b/source/java/org/alfresco/repo/web/scripts/content/ContentGet.java
index 8b42e7408f..6f9589d2fc 100644
--- a/source/java/org/alfresco/repo/web/scripts/content/ContentGet.java
+++ b/source/java/org/alfresco/repo/web/scripts/content/ContentGet.java
@@ -20,21 +20,34 @@ package org.alfresco.repo.web.scripts.content;
import java.io.IOException;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
+import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
+import org.alfresco.cmis.CMISFilterNotValidException;
import org.alfresco.cmis.CMISObjectReference;
+import org.alfresco.cmis.CMISRendition;
+import org.alfresco.cmis.CMISRenditionService;
+import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.cmis.reference.ReferenceFactory;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.repo.web.scripts.FileTypeImageUtils;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.repository.FileTypeImageSize;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import org.springframework.util.FileCopyUtils;
+import org.springframework.web.context.ServletContextAware;
+import org.springframework.web.context.support.ServletContextResource;
/**
@@ -44,16 +57,27 @@ import org.apache.commons.logging.LogFactory;
*
* @author davidc
*/
-public class ContentGet extends StreamContent
+public class ContentGet extends StreamContent implements ServletContextAware
{
// Logger
@SuppressWarnings("unused")
private static final Log logger = LogFactory.getLog(ContentGet.class);
// Component dependencies
+ private ServletContext servletContext;
private ReferenceFactory referenceFactory;
+ private DictionaryService dictionaryService;
private NamespaceService namespaceService;
-
+ private CMISRenditionService renditionService;
+
+ /**
+ * @param
+ */
+ public void setServletContext(ServletContext servletContext)
+ {
+ this.servletContext = servletContext;
+ }
+
/**
* @param reference factory
*/
@@ -62,6 +86,14 @@ public class ContentGet extends StreamContent
this.referenceFactory = referenceFactory;
}
+ /**
+ * @param dictionaryService
+ */
+ public void setDictionaryService(DictionaryService dictionaryService)
+ {
+ this.dictionaryService = dictionaryService;
+ }
+
/**
* @param namespaceService
*/
@@ -69,6 +101,14 @@ public class ContentGet extends StreamContent
{
this.namespaceService = namespaceService;
}
+
+ /**
+ * @param renditionService
+ */
+ public void setCMISRenditionService(CMISRenditionService renditionService)
+ {
+ this.renditionService = renditionService;
+ }
/**
* @see org.alfresco.web.scripts.WebScript#execute(org.alfresco.web.scripts.WebScriptRequest, org.alfresco.web.scripts.WebScriptResponse)
@@ -95,26 +135,139 @@ public class ContentGet extends StreamContent
throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find " + reference.toString());
}
- // determine content property
- QName propertyQName = ContentModel.PROP_CONTENT;
- String contentPart = templateVars.get("property");
- 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);
+ // stream content on node, or rendition of node
+ String streamId = req.getParameter("streamId");
+ if (streamId != null && streamId.length() > 0)
+ {
+ // render content rendition
+ streamRendition(req, res, reference, streamId, attach);
+ }
+ else
+ {
+ // render content
+ QName propertyQName = ContentModel.PROP_CONTENT;
+ String contentPart = templateVars.get("property");
+ 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);
+ }
+ }
+
+ // Stream the content
+ streamContent(req, res, nodeRef, propertyQName, attach);
+ }
}
+
+ /**
+ * Stream content rendition
+ *
+ * @param req
+ * @param res
+ * @param reference
+ * @param streamId
+ * @param attach
+ * @throws IOException
+ */
+ private void streamRendition(WebScriptRequest req, WebScriptResponse res, CMISObjectReference reference, String streamId, boolean attach)
+ throws IOException
+ {
+ try
+ {
+ // find rendition
+ CMISRendition rendition = null;
+ List renditions = renditionService.getRenditions(reference.getNodeRef(), "*");
+ for (CMISRendition candidateRendition : renditions)
+ {
+ if (candidateRendition.getStreamId().equals(streamId))
+ {
+ rendition = candidateRendition;
+ break;
+ }
+ }
+ if (rendition == null)
+ {
+ throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find rendition " + streamId + " for " + reference.toString());
+ }
+
+ // determine if special case for icons
+ if (streamId.startsWith("alf:icon"))
+ {
+ streamIcon(res, reference, streamId, attach);
+ }
+ else
+ {
+ streamContent(req, res, rendition.getNodeRef(), ContentModel.PROP_CONTENT, attach);
+ }
+ }
+ catch(CMISFilterNotValidException e)
+ {
+ throw new WebScriptException(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Invalid Rendition Filter");
+ }
+ }
+
+ /**
+ * Stream Icon
+ *
+ * @param res
+ * @param reference
+ * @param streamId
+ * @param attach
+ * @throws IOException
+ */
+ private void streamIcon(WebScriptResponse res, CMISObjectReference reference, String streamId, boolean attach)
+ throws IOException
+ {
+ // convert stream id to icon size
+ FileTypeImageSize imageSize = streamId.equals("alf:icon16") ? FileTypeImageSize.Small : FileTypeImageSize.Medium;
+ String iconSize = streamId.equals("alf:icon16") ? "-16" : "";
+
+ // calculate icon file name and path
+ String iconPath = null;
+ if (dictionaryService.isSubClass(nodeService.getType(reference.getNodeRef()), ContentModel.TYPE_CONTENT))
+ {
+ String name = (String)nodeService.getProperty(reference.getNodeRef(), ContentModel.PROP_NAME);
+ iconPath = FileTypeImageUtils.getFileTypeImage(servletContext, name, imageSize);
+ }
+ else
+ {
+ String icon = (String)nodeService.getProperty(reference.getNodeRef(), ApplicationModel.PROP_ICON);
+ if (icon != null)
+ {
+ iconPath = "/images/icons/" + icon + iconSize + ".gif";
+ }
+ else
+ {
+ iconPath = "/images/icons/space-icon-default" + iconSize + ".gif";
+ }
+ }
+
+ // set mimetype
+ String mimetype = MimetypeMap.MIMETYPE_BINARY;
+ int extIndex = iconPath.lastIndexOf('.');
+ if (extIndex != -1)
+ {
+ String ext = iconPath.substring(extIndex + 1);
+ mimetype = mimetypeService.getMimetype(ext);
+ }
+ res.setContentType(mimetype);
+
+ // stream icon
+ ServletContextResource resource = new ServletContextResource(servletContext, iconPath);
+ if (!resource.exists())
+ {
+ throw new WebScriptException(HttpServletResponse.SC_NOT_FOUND, "Unable to find rendition " + streamId + " for " + reference.toString());
+ }
+ FileCopyUtils.copy(resource.getInputStream(), res.getOutputStream());
+ }
+
}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java b/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java
index 111a910c43..46134c991e 100644
--- a/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java
+++ b/source/java/org/alfresco/repo/web/scripts/content/StreamContent.java
@@ -489,22 +489,7 @@ public class StreamContent extends AbstractWebScript
protected void streamContentImpl(WebScriptRequest req, WebScriptResponse res, ContentReader reader, boolean attach,
Date modified, String eTag, String attachFileName) throws IOException
{
- // handle attachment
- if (attach == true)
- {
- String headerValue = "attachment";
- if (attachFileName != null && attachFileName.length() > 0)
- {
- if (logger.isDebugEnabled())
- logger.debug("Attaching content using filename: " + attachFileName);
-
- headerValue += "; filename=" + attachFileName;
- }
-
- // 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
- res.setHeader("Content-Disposition", headerValue);
- }
+ setAttachment(res, attach, attachFileName);
// establish mimetype
String mimetype = reader.getMimetype();
@@ -553,4 +538,31 @@ public class StreamContent extends AbstractWebScript
logger.info("Client aborted stream read:\n\tcontent: " + reader);
}
}
+
+ /**
+ * Set attachment header
+ *
+ * @param res
+ * @param attach
+ * @param attachFileName
+ */
+ protected void setAttachment(WebScriptResponse res, boolean attach, String attachFileName)
+ {
+ if (attach == true)
+ {
+ String headerValue = "attachment";
+ if (attachFileName != null && attachFileName.length() > 0)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Attaching content using filename: " + attachFileName);
+
+ headerValue += "; filename=" + attachFileName;
+ }
+
+ // 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
+ res.setHeader("Content-Disposition", headerValue);
+ }
+ }
+
}