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