001/*
002 * Copyright 2004 The Apache Software Foundation.
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 */
016package org.nuxeo.ecm.platform.ui.web.component.file;
017
018import java.io.IOException;
019
020import javax.el.ELException;
021import javax.el.ExpressionFactory;
022import javax.el.MethodExpression;
023import javax.el.ValueExpression;
024import javax.faces.FacesException;
025import javax.faces.application.Application;
026import javax.faces.component.NamingContainer;
027import javax.faces.component.UICommand;
028import javax.faces.component.UIComponent;
029import javax.faces.component.UIOutput;
030import javax.faces.component.html.HtmlCommandLink;
031import javax.faces.context.FacesContext;
032import javax.faces.context.ResponseWriter;
033
034import org.nuxeo.ecm.core.api.Blob;
035import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeEntry;
036import org.nuxeo.ecm.platform.mimetype.interfaces.MimetypeRegistry;
037import org.nuxeo.ecm.platform.ui.web.util.ComponentUtils;
038import org.nuxeo.runtime.api.Framework;
039
040/**
041 * UIOutput file.
042 * <p>
043 * Attribute named value is the file to be displayed. Its submitted value as well as filename are handled by sub
044 * components in facets. Rendering is handled here.
045 * <p>
046 * If convertAction and editOnlineAction method bindings are set, corresponding links are rendered. The
047 * editOnlineActionRendered is used to filter action visibility.
048 *
049 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a>
050 */
051public class UIOutputFile extends UIOutput implements NamingContainer {
052
053    public static final String COMPONENT_TYPE = UIOutputFile.class.getName();
054
055    public static final String COMPONENT_FAMILY = "javax.faces.Output";
056
057    private static final String DOWNLOAD_FACET_NAME = "download";
058
059    private static final String CONVERT_PDF_FACET_NAME = "convertToPdf";
060
061    private static final String EDITONLINE_FACET_NAME = "editOnline";
062
063    private String filename;
064
065    private MethodExpression convertAction;
066
067    private MethodExpression editOnlineAction;
068
069    // to perform test on rights or service availability
070    private Boolean editOnlineActionRendered;
071
072    // to get values from parent component, useful when embedded within an
073    // UIInputFile component
074    private Boolean queryParent;
075
076    private String separator = " | ";
077
078    private String downloadLabel;
079
080    private Boolean iconRendered;
081
082    public UIOutputFile() {
083        FacesContext faces = FacesContext.getCurrentInstance();
084        Application app = faces.getApplication();
085        ComponentUtils.initiateSubComponent(this, DOWNLOAD_FACET_NAME,
086                app.createComponent(UIOutputFileCommandLink.COMPONENT_TYPE));
087        ComponentUtils.initiateSubComponent(this, CONVERT_PDF_FACET_NAME,
088                app.createComponent(HtmlCommandLink.COMPONENT_TYPE));
089        ComponentUtils.initiateSubComponent(this, EDITONLINE_FACET_NAME,
090                app.createComponent(HtmlCommandLink.COMPONENT_TYPE));
091    }
092
093    // component will render itself
094    @Override
095    public String getRendererType() {
096        return null;
097    }
098
099    // getters and setters
100
101    @Override
102    public Object getValue() {
103        if (getQueryParent()) {
104            UIComponent parent = getParent();
105            if (parent instanceof UIInputFile) {
106                UIInputFile inputFile = (UIInputFile) parent;
107                return inputFile.getCurrentBlob();
108            }
109        }
110        return super.getValue();
111    }
112
113    public String getFilename() {
114        if (getQueryParent()) {
115            UIComponent parent = getParent();
116            if (parent instanceof UIInputFile) {
117                UIInputFile inputFile = (UIInputFile) parent;
118                return inputFile.getCurrentFilename();
119            }
120        }
121        // default way to get it
122        if (filename != null) {
123            return filename;
124        }
125        ValueExpression ve = getValueExpression("filename");
126        if (ve != null) {
127            try {
128                return (String) ve.getValue(getFacesContext().getELContext());
129            } catch (ELException e) {
130                throw new FacesException(e);
131            }
132        } else {
133            return null;
134        }
135    }
136
137    public void setFilename(String filename) {
138        this.filename = filename;
139    }
140
141    public MethodExpression getConvertAction() {
142        return convertAction;
143    }
144
145    public void setConvertAction(MethodExpression convertToPdfAction) {
146        convertAction = convertToPdfAction;
147    }
148
149    public MethodExpression getEditOnlineAction() {
150        return editOnlineAction;
151    }
152
153    public void setEditOnlineAction(MethodExpression editOnlineAction) {
154        this.editOnlineAction = editOnlineAction;
155    }
156
157    public Boolean getEditOnlineActionRendered() {
158        if (editOnlineActionRendered != null) {
159            return editOnlineActionRendered;
160        }
161        ValueExpression ve = getValueExpression("editOnlineActionRendered");
162        if (ve != null) {
163            try {
164                return !Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()));
165            } catch (ELException e) {
166                throw new FacesException(e);
167            }
168        } else {
169            // default value
170            return false;
171        }
172    }
173
174    public void setEditOnlineActionRendered(Boolean editOnlineActionRendered) {
175        this.editOnlineActionRendered = editOnlineActionRendered;
176    }
177
178    public String getSeparator() {
179        if (separator != null) {
180            return separator;
181        }
182        ValueExpression ve = getValueExpression("separator");
183        if (ve != null) {
184            try {
185                return (String) ve.getValue(getFacesContext().getELContext());
186            } catch (ELException e) {
187                throw new FacesException(e);
188            }
189        } else {
190            return null;
191        }
192    }
193
194    public Boolean getQueryParent() {
195        if (queryParent != null) {
196            return queryParent;
197        }
198        ValueExpression ve = getValueExpression("queryParent");
199        if (ve != null) {
200            try {
201                return !Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()));
202            } catch (ELException e) {
203                throw new FacesException(e);
204            }
205        } else {
206            // default value
207            return false;
208        }
209    }
210
211    public void setQueryParent(Boolean queryParent) {
212        this.queryParent = queryParent;
213    }
214
215    public void setSeparator(String actionsSeparator) {
216        separator = actionsSeparator;
217    }
218
219    public String getDownloadLabel() {
220        if (downloadLabel != null) {
221            return downloadLabel;
222        }
223        ValueExpression ve = getValueExpression("downloadLabel");
224        if (ve != null) {
225            try {
226                return (String) ve.getValue(getFacesContext().getELContext());
227            } catch (ELException e) {
228                throw new FacesException(e);
229            }
230        } else {
231            return null;
232        }
233    }
234
235    public void setDownloadLabel(String downloadLabel) {
236        this.downloadLabel = downloadLabel;
237    }
238
239    public Boolean getIconRendered() {
240        if (iconRendered != null) {
241            return iconRendered;
242        }
243        ValueExpression ve = getValueExpression("iconRendered");
244        if (ve != null) {
245            try {
246                return !Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()));
247            } catch (ELException e) {
248                throw new FacesException(e);
249            }
250        } else {
251            // default value
252            return true;
253        }
254    }
255
256    public void setIconRendered(Boolean iconRendered) {
257        this.iconRendered = iconRendered;
258    }
259
260    // rendering methods
261    protected ValueExpression getBlobExpression(FacesContext context) {
262        if (getQueryParent()) {
263            UIComponent parent = getParent();
264            if (parent instanceof UIInputFile) {
265                UIInputFile inputFile = (UIInputFile) parent;
266                ExpressionFactory ef = context.getApplication().getExpressionFactory();
267                return ef.createValueExpression(inputFile.getCurrentBlob(), Blob.class);
268            }
269        }
270        // default get
271        Object local = getLocalValue();
272        if (local != null) {
273            ExpressionFactory ef = context.getApplication().getExpressionFactory();
274            return ef.createValueExpression(local, Blob.class);
275        } else {
276            return getValueExpression("value");
277        }
278    }
279
280    protected ValueExpression getFileNameExpression(FacesContext context) {
281        if (getQueryParent()) {
282            UIComponent parent = getParent();
283            if (parent instanceof UIInputFile) {
284                UIInputFile inputFile = (UIInputFile) parent;
285                ExpressionFactory ef = context.getApplication().getExpressionFactory();
286                return ef.createValueExpression(inputFile.getCurrentFilename(), String.class);
287            }
288        }
289        if (filename != null) {
290            ExpressionFactory ef = context.getApplication().getExpressionFactory();
291            return ef.createValueExpression(filename, String.class);
292        } else {
293            return getValueExpression("filename");
294        }
295    }
296
297    protected String getDownloadLinkValue(FacesContext context, Blob blob, String filename) {
298        String linkValue = getDownloadLabel();
299        if (linkValue == null) {
300            if (filename == null || filename.length() == 0) {
301                linkValue = ComponentUtils.translate(context, "label.outputFile.download");
302            } else {
303                linkValue = filename;
304            }
305        } else {
306            // try to translate it
307            linkValue = ComponentUtils.translate(context, linkValue);
308        }
309        // XXX AT: LazyBlob always returns 0
310        // if (blob != null) {
311        // Long size = blob.getLength();
312        // if (size != null) {
313        // DecimalFormatSymbols dfs = new DecimalFormatSymbols();
314        // dfs.setDecimalSeparator('.');
315        // DecimalFormat df = new DecimalFormat("########.0", dfs);
316        // String stringSize = df.format(size / 1024.);
317        // linkValue += " (" + stringSize + "Ko)";
318        // }
319        // }
320        return linkValue;
321    }
322
323    // encode component with its sub components
324    @Override
325    public void encodeBegin(FacesContext context) throws IOException {
326        Object value = getValue();
327        if (value != null && value instanceof Blob) {
328            UIComponent downloadFacet = getFacet(DOWNLOAD_FACET_NAME);
329            if (downloadFacet != null) {
330                Blob blob = (Blob) value;
331                String filenameSet = getFilename();
332                UICommand downloadComp = (UICommand) downloadFacet;
333                // action expression will be set thanks to parent values
334                downloadComp.setValue(getDownloadLinkValue(context, blob, filenameSet));
335                downloadComp.setImmediate(true);
336                ComponentUtils.copyLinkValues(this, downloadComp);
337                if (getIconRendered()) {
338                    // encode icon within link
339                    encodeFileIcon(context, blob);
340                }
341                // encode component
342                ComponentUtils.encodeComponent(context, downloadComp);
343            }
344        }
345    }
346
347    public void encodeFileIcon(FacesContext context, Blob blob) throws IOException {
348        String iconPath = "";
349        MimetypeRegistry mimeService = Framework.getService(MimetypeRegistry.class);
350        MimetypeEntry mimeEntry = mimeService.getMimetypeEntryByMimeType(blob.getMimeType());
351        if (mimeEntry != null) {
352            if (mimeEntry.getIconPath() != null) {
353                // FIXME: above Context should find it
354                iconPath = "/icons/" + mimeEntry.getIconPath();
355            }
356        }
357        if (iconPath.length() > 0) {
358            ResponseWriter writer = context.getResponseWriter();
359            writer.startElement("img", this);
360            String src = context.getApplication().getViewHandler().getResourceURL(context, iconPath);
361            writer.writeURIAttribute("src", context.getExternalContext().encodeResourceURL(src), null);
362            writer.writeAttribute("alt", blob.getMimeType(), null);
363            writer.endElement("img");
364            writer.write(ComponentUtils.WHITE_SPACE_CHARACTER);
365            writer.flush();
366        }
367    }
368
369    // state holder
370
371    @Override
372    public Object saveState(FacesContext context) {
373        Object[] values = new Object[6];
374        values[0] = super.saveState(context);
375        values[1] = filename;
376        values[2] = convertAction;
377        values[3] = editOnlineAction;
378        values[4] = editOnlineActionRendered;
379        values[5] = queryParent;
380        return values;
381    }
382
383    @Override
384    public void restoreState(FacesContext context, Object state) {
385        Object[] values = (Object[]) state;
386        super.restoreState(context, values[0]);
387        filename = (String) values[1];
388        convertAction = (MethodExpression) values[2];
389        editOnlineAction = (MethodExpression) values[3];
390        editOnlineActionRendered = (Boolean) values[4];
391        queryParent = (Boolean) values[5];
392    }
393
394}