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.io.Serializable;
026import java.net.URI;
027import java.util.Collections;
028import java.util.List;
029import java.util.Map;
030import java.util.Optional;
031
032import javax.servlet.http.HttpServletRequest;
033import javax.servlet.http.HttpServletResponse;
034import javax.ws.rs.GET;
035import javax.ws.rs.Path;
036import javax.ws.rs.PathParam;
037import javax.ws.rs.Produces;
038import javax.ws.rs.QueryParam;
039import javax.ws.rs.core.Context;
040import javax.ws.rs.core.MediaType;
041import javax.ws.rs.core.Response;
042
043import org.nuxeo.ecm.core.api.Blob;
044import org.nuxeo.ecm.core.api.DocumentModel;
045import org.nuxeo.ecm.core.api.NuxeoException;
046import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
047import org.nuxeo.ecm.core.api.blobholder.DocumentBlobHolder;
048import org.nuxeo.ecm.core.blob.BlobManager;
049import org.nuxeo.ecm.core.io.download.DownloadService;
050import org.nuxeo.ecm.platform.preview.api.HtmlPreviewAdapter;
051import org.nuxeo.ecm.platform.preview.api.PreviewException;
052import org.nuxeo.ecm.platform.preview.helper.PreviewHelper;
053import org.nuxeo.ecm.restapi.server.jaxrs.blob.BlobObject;
054import org.nuxeo.ecm.webengine.model.Resource;
055import org.nuxeo.ecm.webengine.model.WebAdapter;
056import org.nuxeo.ecm.webengine.model.exceptions.WebResourceNotFoundException;
057import org.nuxeo.ecm.webengine.model.impl.DefaultAdapter;
058import org.nuxeo.runtime.api.Framework;
059
060/**
061 * @since 8.2
062 */
063@WebAdapter(name = PreviewAdapter.NAME, type = "previewAdapter")
064@Produces({ MediaType.APPLICATION_JSON })
065public class PreviewAdapter extends DefaultAdapter {
066
067    public static final String NAME = "preview";
068
069    @GET
070    public Object preview(@QueryParam("blobPostProcessing") boolean postProcessing, @Context HttpServletRequest request,
071            @Context HttpServletResponse response) {
072
073        DocumentBlobHolder bh = getBlobHolderToPreview();
074        if (bh == null) {
075            return Response.status(NOT_FOUND).build();
076        }
077
078        // if it's a managed blob try to use the embed uri for preview
079        BlobManager blobManager = Framework.getService(BlobManager.class);
080        try {
081            URI uri = blobManager.getURI(bh.getBlob(), BlobManager.UsageHint.EMBED, null);
082            if (uri != null) {
083                return Response.seeOther(uri).build();
084            }
085        } catch (IOException e) {
086            throw new NuxeoException(e);
087        }
088
089        List<Blob> previewBlobs = getPreviewBlobs(bh, postProcessing);
090        if (previewBlobs == null || previewBlobs.isEmpty()) {
091            throw new WebResourceNotFoundException("Preview not available");
092        }
093
094        try {
095            Blob blob = previewBlobs.get(0);
096            DownloadService downloadService = Framework.getService(DownloadService.class);
097            downloadService.downloadBlob(request, response, bh.getDocument(), bh.getXpath(), blob, blob.getFilename(),
098                    "preview", null, true);
099        } catch (IOException e) {
100            throw new NuxeoException(e);
101        }
102
103        return Response.ok().build();
104    }
105
106    @GET
107    @Path("{subPath}")
108    public Object subPath(@PathParam("subPath") String subPath,
109            @QueryParam("blobPostProcessing") boolean postProcessing, @Context HttpServletRequest request,
110            @Context HttpServletResponse response) {
111
112        DocumentBlobHolder bh = getBlobHolderToPreview();
113        if (bh == null) {
114            return Response.status(NOT_FOUND).build();
115        }
116
117        List<Blob> previewBlobs = getPreviewBlobs(bh, postProcessing);
118        if (previewBlobs == null || previewBlobs.isEmpty()) {
119            throw new WebResourceNotFoundException("Preview not available");
120        }
121
122        // find blob
123        Optional<Blob> subBlob = previewBlobs.stream().filter(b -> subPath.equals(b.getFilename())).findFirst();
124
125        if (!subBlob.isPresent()) {
126            throw new WebResourceNotFoundException(String.format("Preview blob %s not found", subPath));
127        }
128
129        try {
130            Blob blob = subBlob.get();
131            DownloadService downloadService = Framework.getService(DownloadService.class);
132            Map<String, Serializable> extendedInfos = Collections.singletonMap("subPath", subPath);
133            downloadService.downloadBlob(request, response, bh.getDocument(), bh.getXpath(), blob, blob.getFilename(),
134                    "preview", extendedInfos, true);
135        } catch (IOException e) {
136            throw new NuxeoException(e);
137        }
138
139        return Response.ok().build();
140    }
141
142    private List<Blob> getPreviewBlobs(DocumentBlobHolder bh, boolean blobPostProcessing) {
143        DocumentModel doc = bh.getDocument();
144        String xpath = bh.getXpath();
145        HtmlPreviewAdapter preview;
146
147        try {
148            if (isBlobTarget() && !isBlobHolder(doc, xpath)) {
149                preview = PreviewHelper.getBlobPreviewAdapter(doc);
150                return preview.getFilePreviewBlobs(xpath, blobPostProcessing);
151            }
152
153            preview = doc.getAdapter(HtmlPreviewAdapter.class);
154            if (preview == null) {
155                return null;
156            }
157
158            return preview.getFilePreviewBlobs(blobPostProcessing);
159        } catch (PreviewException e) {
160            throw new WebResourceNotFoundException("Preview not available", e);
161        }
162    }
163
164    private DocumentBlobHolder getBlobHolderToPreview() {
165        Resource target = getTarget();
166        if (isBlobTarget()) {
167            return ((BlobObject) target).getBlobHolder();
168        } else {
169            DocumentModel doc = target.getAdapter(DocumentModel.class);
170            return (DocumentBlobHolder) doc.getAdapter(BlobHolder.class);
171        }
172    }
173
174    private boolean isBlobTarget() {
175        return getTarget().isInstanceOf("blob");
176    }
177
178    private boolean isBlobHolder(DocumentModel doc, String xpath) {
179        DocumentBlobHolder bh = (DocumentBlobHolder) doc.getAdapter(BlobHolder.class);
180        return bh != null && bh.getXpath().equals(xpath);
181    }
182}