001/*
002 * (C) Copyright 2006-2011 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 *     rux, tdelprat
018 *
019 */
020
021package org.nuxeo.ecm.webapp.liveedit;
022
023import static org.jboss.seam.ScopeType.SESSION;
024import static org.jboss.seam.annotations.Install.FRAMEWORK;
025
026import java.io.Serializable;
027import java.util.ArrayList;
028import java.util.List;
029import java.util.Map;
030
031import javax.faces.context.FacesContext;
032
033import org.apache.commons.logging.Log;
034import org.apache.commons.logging.LogFactory;
035import org.jboss.seam.annotations.Install;
036import org.jboss.seam.annotations.Name;
037import org.jboss.seam.annotations.Scope;
038import org.nuxeo.runtime.api.Framework;
039
040/**
041 * This Seam component is used to represent the client configuration for LiveEdit.
042 * <p>
043 * On the client side, the LiveEdit plugin advertise its feature via the Accept Header of the browser. This information
044 * may be used to decide if LiveEdit links must be displayed or not.
045 * <p>
046 * The behavior can be configured via the property: org.nuxeo.ecm.platform.liveedit.config
047 * <p>
048 * There are 3 possible values :
049 * <ul>
050 * <li>client : let the client choose what is live editable => use the mime-types send by the client to define what must
051 * be live editable
052 * <li>server : let the server decide => use the mime-type registry define what types are liveEditable
053 * <li>both : use client and server intersection => in order to be liveEditable a type must be advertised by the client
054 * and set to liveEditable in the mimetypeRegistry
055 * </ul>
056 * Client advertising is done in the Accept header: Accept : application/x-nuxeo-liveedit:mimetype1;mimetype2 Starting
057 * the 5.2, the addon can send the standardized accept header, as Accept :
058 * application/x-nuxeo-liveedit;ext0="mimetype1";ext1="mimetype2".. Also, the addon can still send the old way, so the
059 * both forms are accepted. See NXP-3257
060 *
061 * @author Thierry Delprat
062 * @author rux
063 */
064@Scope(SESSION)
065@Name("liveEditClientConfig")
066@Install(precedence = FRAMEWORK)
067public class LiveEditClientConfig implements Serializable {
068
069    private static final long serialVersionUID = 1L;
070
071    private static final Log log = LogFactory.getLog(LiveEditClientConfig.class);
072
073    protected Boolean clientHasLiveEditInstalled;
074
075    protected List<String> advertizedLiveEditableMimeTypes;
076
077    protected static String liveEditConfigPolicy;
078
079    public static final String LE_MIME_TYPE = "application/x-nuxeo-liveedit";
080
081    public static final String LE_CONFIG_PROPERTY = "org.nuxeo.ecm.platform.liveedit.config";
082
083    public static final String LE_CONFIG_CLIENTSIDE = "client";
084
085    public static final String LE_CONFIG_SERVERSIDE = "server";
086
087    public static final String LE_CONFIG_BOTHSIDES = "both";
088
089    protected void detectLiveEditClientConfig() {
090        clientHasLiveEditInstalled = false;
091        advertizedLiveEditableMimeTypes = new ArrayList<String>();
092
093        if (getLiveEditConfigurationPolicy().equals(LE_CONFIG_SERVERSIDE)) {
094            // in case if Server side config, consider liveEdit is installed
095            clientHasLiveEditInstalled = true;
096            return;
097        }
098
099        FacesContext fContext = FacesContext.getCurrentInstance();
100        if (fContext == null) {
101            log.error("unable to fetch facesContext, can not detect liveEdit client config");
102        } else {
103            Map<String, String> headers = fContext.getExternalContext().getRequestHeaderMap();
104            String accept = headers.get("Accept");
105            if (accept == null) {
106                return;
107            }
108
109            String[] accepted = accept.split(",");
110            for (String acceptHeader : accepted) {
111                if (acceptHeader != null) {
112                    acceptHeader = acceptHeader.trim();
113                } else {
114                    continue;
115                }
116                if (acceptHeader.startsWith(LE_MIME_TYPE)) {
117                    clientHasLiveEditInstalled = true;
118                    String[] subTypes = acceptHeader.split(";");
119
120                    for (String subType : subTypes) {
121                        // accept both forms:
122                        // application/x-nuxeo-liveedit:mimetype1;mimetype2
123                        // application/x-nuxeo-liveedit;ext0="mimetype1";ext1="mimetype2"
124                        int equalQuoteIndex = subType.indexOf("=\"");
125                        String valueSubType = subType;
126                        if (equalQuoteIndex >= 0 && subType.length() > equalQuoteIndex + 3) {
127                            valueSubType = subType.substring(equalQuoteIndex + 2, subType.length() - 1);
128                        }
129                        advertizedLiveEditableMimeTypes.add(valueSubType.replace("!", "/"));
130                    }
131                }
132            }
133        }
134    }
135
136    public boolean isLiveEditInstalled() {
137        if (clientHasLiveEditInstalled == null) {
138            detectLiveEditClientConfig();
139        }
140
141        return clientHasLiveEditInstalled;
142    }
143
144    public String getLiveEditConfigurationPolicy() {
145        if (liveEditConfigPolicy == null) {
146            liveEditConfigPolicy = Framework.getProperty(LE_CONFIG_PROPERTY, LE_CONFIG_CLIENTSIDE);
147        }
148        return liveEditConfigPolicy;
149    }
150
151    public boolean isMimeTypeLiveEditable(String mimetype) {
152        if (advertizedLiveEditableMimeTypes == null) {
153            detectLiveEditClientConfig();
154        }
155        return advertizedLiveEditableMimeTypes.contains(mimetype);
156    }
157
158}