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