001/* 002 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * 018 * Nelson Silva <nsilva@nuxeo.com> 019 */ 020package org.nuxeo.ecm.restapi.server.jaxrs.adapters; 021 022import static javax.ws.rs.core.Response.Status.NOT_FOUND; 023 024import java.io.IOException; 025import java.net.URI; 026import java.util.Collections; 027import java.util.List; 028import java.util.Optional; 029 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032import javax.ws.rs.GET; 033import javax.ws.rs.Path; 034import javax.ws.rs.PathParam; 035import javax.ws.rs.Produces; 036import javax.ws.rs.QueryParam; 037import javax.ws.rs.core.Context; 038import javax.ws.rs.core.MediaType; 039import javax.ws.rs.core.Response; 040 041import org.nuxeo.ecm.core.api.Blob; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.NuxeoException; 044import org.nuxeo.ecm.core.api.blobholder.BlobHolder; 045import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder; 046import org.nuxeo.ecm.core.blob.BlobManager; 047import org.nuxeo.ecm.core.io.download.DownloadService; 048import org.nuxeo.ecm.core.io.download.DownloadService.DownloadContext; 049import org.nuxeo.ecm.platform.preview.api.HtmlPreviewAdapter; 050import org.nuxeo.ecm.platform.preview.api.PreviewException; 051import org.nuxeo.ecm.platform.preview.helper.PreviewHelper; 052import org.nuxeo.ecm.restapi.server.jaxrs.blob.BlobObject; 053import org.nuxeo.ecm.webengine.model.Resource; 054import org.nuxeo.ecm.webengine.model.WebAdapter; 055import org.nuxeo.ecm.webengine.model.impl.DefaultAdapter; 056import org.nuxeo.runtime.api.Framework; 057 058/** 059 * @since 8.2 060 */ 061@WebAdapter(name = PreviewAdapter.NAME, type = "previewAdapter") 062@Produces({ MediaType.APPLICATION_JSON }) 063public class PreviewAdapter extends DefaultAdapter { 064 065 public static final String NAME = "preview"; 066 067 @GET 068 public Object preview(@QueryParam("blobPostProcessing") boolean postProcessing, @Context HttpServletRequest request, 069 @Context HttpServletResponse response) { 070 071 DocumentBlobHolder bh = getBlobHolderToPreview(); 072 if (bh == null) { 073 return Response.status(NOT_FOUND).build(); 074 } 075 076 // if it's a managed blob try to use the embed uri for preview 077 BlobManager blobManager = Framework.getService(BlobManager.class); 078 try { 079 URI uri = blobManager.getURI(bh.getBlob(), BlobManager.UsageHint.EMBED, null); 080 if (uri != null) { 081 return Response.seeOther(uri).build(); 082 } 083 } catch (IOException e) { 084 throw new NuxeoException(e); 085 } 086 087 List<Blob> previewBlobs = getPreviewBlobs(bh, postProcessing); 088 if (previewBlobs == null || previewBlobs.isEmpty()) { 089 return buildPreviewNotAvailable(); 090 } 091 092 try { 093 Blob blob = previewBlobs.get(0); 094 DownloadContext context = DownloadContext.builder(request, response) 095 .doc(bh.getDocument()) 096 .xpath(bh.getXpath()) 097 .blob(blob) 098 .reason("preview") 099 .inline(true) 100 .build(); 101 Framework.getService(DownloadService.class).downloadBlob(context); 102 } catch (IOException e) { 103 throw new NuxeoException(e); 104 } 105 106 return Response.ok().build(); 107 } 108 109 protected Object buildPreviewNotAvailable() { 110 return Response.status(NOT_FOUND) 111 .entity(getTemplate("preview/preview_not_available.ftl")) 112 .type("text/html") 113 .build(); 114 } 115 116 @GET 117 @Path("{subPath}") 118 public Object subPath(@PathParam("subPath") String subPath, 119 @QueryParam("blobPostProcessing") boolean postProcessing, @Context HttpServletRequest request, 120 @Context HttpServletResponse response) { 121 122 DocumentBlobHolder bh = getBlobHolderToPreview(); 123 if (bh == null) { 124 return Response.status(NOT_FOUND).build(); 125 } 126 127 List<Blob> previewBlobs = getPreviewBlobs(bh, postProcessing); 128 if (previewBlobs == null || previewBlobs.isEmpty()) { 129 return buildPreviewNotAvailable(); 130 } 131 132 // find blob 133 Optional<Blob> subBlob = previewBlobs.stream().filter(b -> subPath.equals(b.getFilename())).findFirst(); 134 135 if (!subBlob.isPresent()) { 136 return buildPreviewNotAvailable(); 137 } 138 139 try { 140 Blob blob = subBlob.get(); 141 DownloadContext context = DownloadContext.builder(request, response) 142 .doc(bh.getDocument()) 143 .xpath(bh.getXpath()) 144 .blob(blob) 145 .reason("preview") 146 .extendedInfos(Collections.singletonMap("subPath", subPath)) 147 .inline(true) 148 .build(); 149 Framework.getService(DownloadService.class).downloadBlob(context); 150 } catch (IOException e) { 151 throw new NuxeoException(e); 152 } 153 154 return Response.ok().build(); 155 } 156 157 private List<Blob> getPreviewBlobs(DocumentBlobHolder bh, boolean blobPostProcessing) { 158 DocumentModel doc = bh.getDocument(); 159 String xpath = bh.getXpath(); 160 HtmlPreviewAdapter preview; 161 162 try { 163 if (isBlobTarget() && !isBlobHolder(doc, xpath)) { 164 preview = PreviewHelper.getBlobPreviewAdapter(doc); 165 return preview.getFilePreviewBlobs(xpath, blobPostProcessing); 166 } 167 168 preview = doc.getAdapter(HtmlPreviewAdapter.class); 169 if (preview == null) { 170 return List.of(); 171 } 172 173 return preview.getFilePreviewBlobs(blobPostProcessing); 174 } catch (PreviewException e) { 175 return List.of(); 176 } 177 } 178 179 private DocumentBlobHolder getBlobHolderToPreview() { 180 Resource target = getTarget(); 181 if (isBlobTarget()) { 182 return ((BlobObject) target).getBlobHolder(); 183 } else { 184 DocumentModel doc = target.getAdapter(DocumentModel.class); 185 return (DocumentBlobHolder) doc.getAdapter(BlobHolder.class); 186 } 187 } 188 189 private boolean isBlobTarget() { 190 return getTarget().isInstanceOf("blob"); 191 } 192 193 private boolean isBlobHolder(DocumentModel doc, String xpath) { 194 DocumentBlobHolder bh = (DocumentBlobHolder) doc.getAdapter(BlobHolder.class); 195 return bh != null && bh.getXpath().equals(xpath); 196 } 197}