001/*
002 * (C) Copyright 2006-2013 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 *
017 * $Id$
018 */
019
020package org.nuxeo.ecm.mobile.webengine.document;
021
022import java.util.HashMap;
023import java.util.Map;
024
025import javax.servlet.http.HttpServletRequest;
026import javax.ws.rs.GET;
027import javax.ws.rs.Path;
028import javax.ws.rs.core.Response;
029
030import org.apache.commons.lang.StringUtils;
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.nuxeo.ecm.automation.AutomationService;
034import org.nuxeo.ecm.automation.OperationContext;
035import org.nuxeo.ecm.automation.OperationException;
036import org.nuxeo.ecm.core.api.Blob;
037import org.nuxeo.ecm.core.api.DocumentModel;
038import org.nuxeo.ecm.core.api.DocumentRef;
039import org.nuxeo.ecm.core.api.NuxeoException;
040import org.nuxeo.ecm.core.api.NuxeoPrincipal;
041import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
042import org.nuxeo.ecm.core.io.download.DownloadService;
043import org.nuxeo.ecm.core.rest.DocumentObject;
044import org.nuxeo.ecm.mobile.webengine.RedirectHelper;
045import org.nuxeo.ecm.mobile.webengine.adapter.JSonExportAdapter;
046import org.nuxeo.ecm.platform.preview.helper.PreviewHelper;
047import org.nuxeo.ecm.platform.url.api.DocumentViewCodecManager;
048import org.nuxeo.ecm.platform.usermanager.UserManager;
049import org.nuxeo.ecm.platform.web.common.vh.VirtualHostHelper;
050import org.nuxeo.ecm.webengine.WebException;
051import org.nuxeo.ecm.webengine.model.ResourceType;
052import org.nuxeo.ecm.webengine.model.WebContext;
053import org.nuxeo.runtime.api.Framework;
054
055import static org.nuxeo.ecm.mobile.filter.ApplicationRedirectionFilter.INITIAL_TARGET_URL_PARAM_NAME;
056
057/**
058 * This Class resolve a DocumentModel and expose differents restitutions according Adapter defined (PreviewAdapter,
059 * CommentAdapter) in uri and "mode" parameter given as parameter into the url. Default mode if given doesn't exist or
060 * not set is view mode.
061 *
062 * @author <a href="mailto:bjalon@nuxeo.com">Benjamin JALON</a>
063 * @since 5.5
064 */
065public class MobileDocument extends DocumentObject {
066
067    private static final Log log = LogFactory.getLog(MobileDocument.class);
068
069    public MobileDocument(WebContext ctx, DocumentRef docRef) {
070        try {
071            ResourceType resType = ctx.getModule().getType("Document");
072            DocumentModel docModel = ctx.getCoreSession().getDocument(docRef);
073            initialize(ctx, resType, docModel);
074            ctx.push(this);
075        } catch (NuxeoException e) {
076            throw WebException.wrap(e);
077        }
078    }
079
080    /**
081     * Needed to not break the navigation after a false PUT see NXP-8778 as I'm redirected to this /@put URL
082     */
083    @GET
084    @Path("@put")
085    public Object redirectToRealURL() {
086        String uri = ctx.getRequest().getRequestURI();
087        // Remove the @put
088        uri = uri.substring(0, uri.length() - "@put".length() - 1);
089        String queryString = ctx.getRequest().getQueryString();
090        return redirect(uri + (queryString != null ? "?" + queryString : ""));
091    }
092
093    @GET
094    @Path("mailIt")
095    public Object emailToCurrentPrincipal() {
096        try {
097            OperationContext subctx = new OperationContext(ctx.getCoreSession(), null);
098            subctx.setInput(getDocument());
099            getAutomationService().run(subctx, "sendEmailToMe");
100        } catch (NuxeoException | OperationException e) {
101            log.error(e, e);
102            return Response.status(400).build();
103        }
104        return Response.ok().build();
105    }
106
107    @Path("like")
108    public Object doLike() {
109        try {
110            return ctx.newObject("Like", getDocument());
111        } catch (NuxeoException e) {
112            log.debug(e, e);
113            return ctx.newObject("Empty");
114        }
115    }
116
117    @Path("hasLiked")
118    public Object doHasLiked() {
119        try {
120            return ctx.newObject("hasLiked", getDocument());
121        } catch (NuxeoException e) {
122            log.debug(e, e);
123            return ctx.newObject("Empty");
124        }
125    }
126
127    @Override
128    public Object doGet() {
129        Map<String, Object> args = new HashMap<String, Object>();
130
131        // must override the original doGet to override the resolution
132        // so get parameter into the request instead injection
133        HttpServletRequest request = ctx.getRequest();
134        String mode = request.getParameter("mode");
135        if (mode == null) {
136            mode = "view";
137        }
138
139        // if url from JSF => ask to push mobile URL into browser history
140        if (request.getParameter(INITIAL_TARGET_URL_PARAM_NAME) != null) {
141            String mobileURL = String.format("%s/doc/%s?mode=%s", ctx.getRoot().getPath(), doc.getId(), mode);
142            args.put("mobileURL", mobileURL);
143        }
144        args.put("hasBlob", getHasBlob());
145
146        // Add the JSON DForm export
147        JSonExportAdapter json = (JSonExportAdapter) ctx.newObject("JSONExport");
148        args.put("doc", json.doGet(request.getRequestURI(), "post"));
149
150        return getView(mode).args(args);
151    }
152
153    protected boolean getHasBlob() {
154        DocumentModel doc = getDocument();
155        BlobHolder bh = doc.getAdapter(BlobHolder.class);
156        return bh != null && bh.getBlob() != null;
157    }
158
159    public boolean hasPreview() {
160        if (doc.hasSchema("file") && doc.getPropertyValue("file:content") != null) {
161            return PreviewHelper.typeSupportsPreview(doc);
162        }
163        return false;
164    }
165
166    public NuxeoPrincipal getPrincipal() {
167        if (ctx.getPrincipal() instanceof NuxeoPrincipal) {
168            return (NuxeoPrincipal) ctx.getPrincipal();
169        }
170
171        throw new WebException("Principal found is not a NuxeoPrincipal can't generate it!");
172    }
173
174    public String getJSFURLPath(DocumentModel docModel) {
175        return RedirectHelper.getJSFDocumentPath(docModel, VirtualHostHelper.getBaseURL(ctx.getRequest()));
176    }
177
178    public String getDownloadURL() {
179        DocumentModel docModel = getDocument();
180
181        return getDownloadURL(docModel);
182    }
183
184    public String getDownloadURL(DocumentModel docModel) {
185        Blob blob = doc.getAdapter(BlobHolder.class).getBlob();
186        DownloadService downloadService = Framework.getService(DownloadService.class);
187        String xpath = DownloadService.BLOBHOLDER_0;
188        String filename = blob.getFilename();
189        return getNuxeoContextPath() + "/" + downloadService.getDownloadUrl(doc, xpath, filename) + "?mimetype="
190                + blob.getMimeType();
191    }
192
193    public String getJSFURLPath() {
194        return getJSFURLPath(getDocument());
195    }
196
197    // **** TODO REMOVE WHEN PREVIEW WITH NAVIGATION RESOLVED ****
198    private String nuxeoContextPath;
199
200    private DocumentViewCodecManager codecManager;
201
202    public String getPreviewURL() {
203        Object targetObject = ctx.getTargetObject();
204        if (!(targetObject instanceof MobileDocument)) {
205            throw new WebException("Target Object must be MobileDocument");
206        }
207        return getNuxeoContextPath() + "/" + PreviewHelper.getPreviewURL(((MobileDocument) targetObject).getDocument());
208    }
209
210    private String getNuxeoContextPath() {
211        if (nuxeoContextPath == null) {
212            nuxeoContextPath = Framework.getProperty("org.nuxeo.ecm.contextPath");
213        }
214        return nuxeoContextPath;
215    }
216
217    private AutomationService getAutomationService() {
218        return Framework.getService(AutomationService.class);
219    }
220
221    public String getDisplayPrincipalName(String name) {
222        NuxeoPrincipal principal = Framework.getService(UserManager.class).getPrincipal(name);
223        if (principal != null) {
224            return getDisplayPrincipalName(principal);
225        }
226        return name;
227    }
228
229    /**
230     * Return the display name of an expected principal. It passes as an Object to prevent Freemarker to thrown an
231     * exception when creator is null.
232     *
233     * @param object must be of type org.nuxeo.ecm.core.api.NuxeoPrincipal
234     * @return the display name will be empty if not a NuxeoPrincipal
235     */
236    public String getDisplayPrincipalName(Object object) {
237        if (object instanceof NuxeoPrincipal) {
238            NuxeoPrincipal principal = (NuxeoPrincipal) object;
239            String display = (principal.getFirstName() + " " + principal.getLastName()).trim();
240            return StringUtils.isBlank(display) ? principal.getName() : display;
241        }
242        return "";
243    }
244
245    public String getDisplayPrincipalName() {
246        return getDisplayPrincipalName(getPrincipal());
247    }
248
249}