001/*
002 * (C) Copyright 2006-2007 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 *     Nuxeo - initial API and implementation
018 *
019 * $Id: JOOoConvertPluginImpl.java 18651 2007-05-13 20:28:53Z sfermigier $
020 */
021
022package org.nuxeo.ecm.platform.ui.web.rest;
023
024import static org.jboss.seam.ScopeType.EVENT;
025
026import java.io.IOException;
027import java.io.Serializable;
028import java.util.HashMap;
029import java.util.Map;
030import java.util.regex.Pattern;
031
032import javax.faces.component.UIViewRoot;
033import javax.faces.context.FacesContext;
034import javax.servlet.ServletRequest;
035import javax.servlet.ServletResponse;
036import javax.servlet.http.HttpServletRequest;
037import javax.servlet.http.HttpServletResponse;
038
039import org.apache.commons.lang.StringUtils;
040import org.jboss.seam.ScopeType;
041import org.jboss.seam.annotations.Begin;
042import org.jboss.seam.annotations.Factory;
043import org.jboss.seam.annotations.In;
044import org.jboss.seam.annotations.Name;
045import org.jboss.seam.annotations.Scope;
046import org.jboss.seam.contexts.Contexts;
047import org.jboss.seam.core.Manager;
048import org.jboss.seam.international.LocaleSelector;
049import org.nuxeo.ecm.core.api.DocumentLocation;
050import org.nuxeo.ecm.core.api.DocumentModel;
051import org.nuxeo.ecm.core.api.DocumentRef;
052import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl;
053import org.nuxeo.ecm.platform.types.adapter.TypeInfo;
054import org.nuxeo.ecm.platform.ui.web.api.NavigationContext;
055import org.nuxeo.ecm.platform.ui.web.api.WebActions;
056import org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants;
057import org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions;
058import org.nuxeo.ecm.platform.ui.web.util.BaseURL;
059import org.nuxeo.ecm.platform.url.DocumentViewImpl;
060import org.nuxeo.ecm.platform.url.api.DocumentView;
061import org.nuxeo.ecm.platform.util.RepositoryLocation;
062
063/**
064 * Helper for generation of URLs and related update of document context.
065 *
066 * @author tiry
067 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
068 * @author Florent Guillaume
069 */
070@Name("restHelper")
071@Scope(EVENT)
072public class RestHelper implements Serializable {
073
074    private static final long serialVersionUID = 1L;
075
076    @In(create = true)
077    protected transient NavigationContext navigationContext;
078
079    @In(create = true)
080    protected transient WebActions webActions;
081
082    protected DocumentView docView;
083
084    protected String baseURL = "";
085
086    @In(create = true)
087    protected transient LocaleSelector localeSelector;
088
089    /**
090     * Sets current server location (core repository) and core document as provided by the document view.
091     * <p>
092     * Only sets current server location if the document reference is null.
093     */
094    @Begin(id = "#{conversationIdGenerator.currentOrNewMainConversationId}", join = true)
095    public String initContextFromRestRequest(DocumentView docView) {
096        String outcome = null;
097
098        if (docView != null) {
099            DocumentLocation docLoc = docView.getDocumentLocation();
100            String serverName = docLoc.getServerName();
101            if (serverName != null) {
102                DocumentRef docRef = docLoc.getDocRef();
103                RepositoryLocation repoLoc = new RepositoryLocation(serverName);
104                if (docRef != null) {
105                    if (docView.getParameter(WebActions.MAIN_TAB_ID_PARAMETER) == null
106                            && !webActions.hasCurrentTabId(WebActions.MAIN_TABS_CATEGORY)) {
107                        webActions.setCurrentTabId(WebActions.MAIN_TABS_CATEGORY, WebActions.DOCUMENTS_MAIN_TAB_ID);
108                    }
109                    outcome = navigationContext.navigateTo(repoLoc, docRef);
110                } else {
111                    navigationContext.setCurrentServerLocation(repoLoc);
112                }
113            }
114            if (outcome == null) {
115                outcome = docView.getViewId();
116            }
117        }
118
119        return outcome;
120    }
121
122    public void setDocumentView(DocumentView docView) {
123        this.docView = docView;
124    }
125
126    public DocumentView getNewDocumentView(String mainTabId) {
127        DocumentView docView = null;
128        DocumentModel currentDocument = navigationContext.getCurrentDocument();
129        if (currentDocument != null) {
130            DocumentLocation docLoc = new DocumentLocationImpl(currentDocument);
131            TypeInfo typeInfo = currentDocument.getAdapter(TypeInfo.class);
132            Map<String, String> params = new HashMap<String, String>();
133            if (currentDocument.isVersion()) {
134                params.put("version", "true");
135            }
136            if (!StringUtils.isEmpty(mainTabId)) {
137                params.put(WebActions.MAIN_TAB_ID_PARAMETER, WebActions.MAIN_TABS_CATEGORY + ":" + mainTabId);
138            }
139            // additional params will be set according to the url pattern,
140            // calling getters on bindings.
141            docView = new DocumentViewImpl(docLoc, typeInfo.getDefaultView(), params);
142        }
143        return docView;
144    }
145
146    public DocumentView getNewDocumentView() {
147        return getNewDocumentView("documents");
148    }
149
150    public DocumentView getDocumentView() {
151        return docView;
152    }
153
154    /**
155     * @return the Seam conversation manager.
156     */
157    public static Manager getConversationManager() {
158        if (Contexts.isEventContextActive()) {
159            return Manager.instance();
160        }
161        return null;
162    }
163
164    protected static String addConversationRequestParameters(String url, Manager conversationManager,
165            String conversationId) {
166        Map<String, Object> params = new HashMap<String, Object>();
167        params.put(conversationManager.getConversationIdParameter(), conversationId);
168        return conversationManager.encodeParameters(url, params);
169    }
170
171    /**
172     * Adds current conversation request parameters to the given url.
173     *
174     * @param url
175     * @return the url with additional conversation request parameters
176     */
177    public static String addCurrentConversationParameters(String url) {
178        Manager conversationManager = getConversationManager();
179        if (conversationManager == null) {
180            return url;
181        }
182        // XXX : deprecated
183        return conversationManager.encodeConversationId(url);
184    }
185
186    /**
187     * Adds main conversation request parameters to the given url.
188     *
189     * @param url
190     * @return the url with additional conversation request parameters
191     */
192    public static String addMainConversationParameters(String url) {
193        Manager conversationManager = getConversationManager();
194        if (conversationManager == null) {
195            return url;
196        } else {
197            String conversationId;
198            if (conversationManager.isNestedConversation()) {
199                conversationId = conversationManager.getParentConversationId();
200            } else {
201                conversationId = conversationManager.getCurrentConversationId();
202            }
203            return addConversationRequestParameters(url, conversationManager, conversationId);
204        }
205    }
206
207    public String getDocumentUrl(DocumentModel doc) {
208        return DocumentModelFunctions.documentUrl(doc);
209    }
210
211    public String getDocumentUrl(DocumentModel doc, String viewId, boolean newConversation) {
212        return DocumentModelFunctions.documentUrl(null, doc, viewId, null, newConversation, BaseURL.getBaseURL());
213    }
214
215    public String getDocumentUrl(String patternName, DocumentModel doc, String viewId, Map<String, String> parameters,
216            boolean newConversation) {
217        return DocumentModelFunctions.documentUrl(patternName, doc, viewId, parameters, newConversation,
218                BaseURL.getBaseURL());
219    }
220
221    @Factory(value = "baseURL", scope = ScopeType.CONVERSATION)
222    public String getBaseURL() {
223        if (baseURL.equals("")) {
224            baseURL = BaseURL.getBaseURL();
225        }
226        return baseURL;
227    }
228
229    @Factory(value = "contextPath", scope = ScopeType.CONVERSATION)
230    public String getContextPath() {
231        return BaseURL.getContextPath();
232    }
233
234    public String doPrint(String defaultTheme) throws IOException {
235        return doPrint(navigationContext.getCurrentDocument(), defaultTheme);
236    }
237
238    public String doPrint(DocumentModel doc, String defaultTheme) throws IOException {
239        FacesContext facesContext = FacesContext.getCurrentInstance();
240        if (facesContext == null) {
241            return null;
242        }
243        HttpServletResponse response = getHttpServletResponse();
244        if (response != null) {
245            handleRedirect(response, getPrintUrl(doc, defaultTheme));
246        }
247        return null;
248    }
249
250    public String getPrintUrl(String defaultTheme) {
251        return getPrintUrl(navigationContext.getCurrentDocument(), defaultTheme);
252    }
253
254    public String getPrintUrl(DocumentModel doc, String defaultTheme) {
255        Map<String, String> parameters = new HashMap<String, String>();
256        int separatorIndex = defaultTheme.indexOf("/");
257        if (separatorIndex != -1) {
258            // defaultTheme includes the default page
259            defaultTheme = defaultTheme.substring(0, separatorIndex);
260            StringBuilder sb = new StringBuilder();
261            sb.append(defaultTheme);
262            sb.append("/print");
263            parameters.put("page", sb.toString());
264        }
265        return DocumentModelFunctions.documentUrl(null, doc, null, parameters, false, BaseURL.getBaseURL());
266    }
267
268    public static HttpServletResponse getHttpServletResponse() {
269        ServletResponse response = null;
270        final FacesContext facesContext = FacesContext.getCurrentInstance();
271        if (facesContext != null) {
272            response = (ServletResponse) facesContext.getExternalContext().getResponse();
273        }
274
275        if (response != null && response instanceof HttpServletResponse) {
276            return (HttpServletResponse) response;
277        }
278        return null;
279    }
280
281    public static HttpServletRequest getHttpServletRequest() {
282        ServletRequest request = null;
283        final FacesContext facesContext = FacesContext.getCurrentInstance();
284        if (facesContext != null) {
285            request = (ServletRequest) facesContext.getExternalContext().getRequest();
286        }
287
288        if (request != null && request instanceof HttpServletRequest) {
289            return (HttpServletRequest) request;
290        }
291        return null;
292    }
293
294    public static void handleRedirect(HttpServletResponse response, String url) throws IOException {
295        response.resetBuffer();
296        response.sendRedirect(url);
297        response.flushBuffer();
298        getHttpServletRequest().setAttribute(NXAuthConstants.DISABLE_REDIRECT_REQUEST_KEY, Boolean.TRUE);
299        FacesContext.getCurrentInstance().responseComplete();
300    }
301
302    /**
303     * Returns the locale string.
304     * <p>
305     * Useful for url pattern bindings.
306     *
307     * @since 5.4.2
308     */
309    public String getLocaleString() {
310        return localeSelector.getLocaleString();
311    }
312
313    public static final Pattern VALID_LOCALE = Pattern.compile("[A-Za-z0-9-_]*");
314
315    /**
316     * Sets the locale string if given string is not null and not empty, as well as on faces context view root in case
317     * it was already created so that it holds the new locale for future lookups by JSF components.
318     * <p>
319     * Useful for url pattern bindings.
320     */
321    public void setLocaleString(String localeString) {
322        // injected directly in JavaScript in a number of places, so should be sanitized
323        if (!StringUtils.isBlank(localeString) && VALID_LOCALE.matcher(localeString).matches()) {
324            localeSelector.setLocaleString(localeString.trim());
325            FacesContext ctx = FacesContext.getCurrentInstance();
326            if (ctx != null) {
327                UIViewRoot viewRoot = ctx.getViewRoot();
328                if (viewRoot != null) {
329                    viewRoot.setLocale(localeSelector.getLocale());
330                }
331            }
332        }
333    }
334
335}