001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software GmbH & Co. KG, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.jsp;
029
030import org.opencms.file.CmsObject;
031import org.opencms.file.CmsResource;
032import org.opencms.flex.CmsFlexController;
033import org.opencms.i18n.CmsEncoder;
034import org.opencms.loader.CmsImageScaler;
035import org.opencms.main.CmsException;
036import org.opencms.main.CmsLog;
037import org.opencms.main.OpenCms;
038import org.opencms.staticexport.CmsLinkManager;
039import org.opencms.util.CmsRequestUtil;
040import org.opencms.util.CmsStringUtil;
041import org.opencms.util.CmsUriSplitter;
042
043import java.util.Arrays;
044import java.util.HashMap;
045import java.util.List;
046import java.util.Map;
047
048import javax.servlet.ServletRequest;
049import javax.servlet.jsp.JspException;
050
051import org.apache.commons.logging.Log;
052
053/**
054 * Creates HTML code for &lt;img src&gt; tags that use the OpenCms image scaling capabilities.<p>
055 *
056 * @since 6.2.0
057 */
058public class CmsJspTagImage extends CmsJspImageScalerTagSupport implements I_CmsJspTagParamParent {
059
060    /** Optional HTML attribute constant. */
061    private static final String ATTR_ALIGN = "align";
062
063    /** Optional HTML attribute constant. */
064    private static final String ATTR_ALT = "alt";
065
066    /** Optional HTML attribute constant. */
067    private static final String ATTR_BORDER = "border";
068
069    /** Optional HTML attribute constant. */
070    private static final String ATTR_CLASS = "class";
071
072    /** Optional HTML attribute constant. */
073    private static final String ATTR_HSPACE = "hspace";
074
075    /** Optional HTML attribute constant. */
076    private static final String ATTR_ID = "id";
077
078    /** Optional HTML attribute constant. */
079    private static final String ATTR_LONGDESC = "longdesc";
080
081    /** Optional HTML attribute constant. */
082    private static final String ATTR_NAME = "name";
083
084    /** Optional HTML attribute constant. */
085    private static final String ATTR_STYLE = "style";
086
087    /** Optional HTML attribute constant. */
088    private static final String ATTR_TITLE = "title";
089
090    /** Optional HTML attribute constant. */
091    private static final String ATTR_USEMAP = "usemap";
092
093    /** Optional HTML attribute constant. */
094    private static final String ATTR_VSPACE = "vspace";
095
096    /** The log object for this class. */
097    private static final Log LOG = CmsLog.getLog(CmsJspTagImage.class);
098
099    /** Required image scaler attributes constant. */
100    private static final String SCALE_ATTR_COLOR = "scalecolor";
101
102    /** Required image scaler attributes constant. */
103    private static final String SCALE_ATTR_FILTER = "scalefilter";
104
105    /** Required image scaler attributes constant. */
106    private static final String SCALE_ATTR_NODIM = "nodim";
107
108    /** Required image scaler attributes constant. */
109    private static final String SCALE_ATTR_PARTIALTAG = "partialtag";
110
111    /** Required image scaler attributes constant. */
112    private static final String SCALE_ATTR_SRC = "src";
113
114    /** Lists for fast lookup. */
115    private static final String[] SCALER_ATTRS = {
116        SCALE_ATTR_COLOR,
117        SCALE_ATTR_FILTER,
118        SCALE_ATTR_HEIGHT,
119        SCALE_ATTR_PARTIALTAG,
120        SCALE_ATTR_POSITION,
121        SCALE_ATTR_QUALITY,
122        SCALE_ATTR_RENDERMODE,
123        SCALE_ATTR_SRC,
124        SCALE_ATTR_TYPE,
125        SCALE_ATTR_WIDTH,
126        SCALE_ATTR_MAXHEIGHT,
127        SCALE_ATTR_MAXWIDTH,
128        SCALE_ATTR_NODIM};
129
130    /** Image scaler attribute list. */
131    private static final List<String> SCALER_ATTRS_LIST = Arrays.asList(SCALER_ATTRS);
132
133    /** Serial version UID required for safe serialization. */
134    private static final long serialVersionUID = 6513320107441256414L;
135
136    /** Map with additionally set image attributes not needed by the image scaler. */
137    private Map<String, String> m_attributes;
138
139    /** Controls if the created HTML image tag contains height and width attributes. */
140    private boolean m_noDim;
141
142    /** Controls if the created HTML image tag is a full or partial tag. */
143    private boolean m_partialTag;
144
145    /**
146     * Creates a new image scaling tag instance.<p>
147     */
148    public CmsJspTagImage() {
149        super();
150    }
151
152    /**
153     * Creates the images scaler used by this image tag.<p>
154     *
155     * @param scaler the scaler created from this tags parameters
156     * @param original a scaler that contains the original image dimensions
157     * @param scaleParam optional scaler parameters for cropping
158     *
159     * @return the images scaler used by this image tag
160     */
161    public static CmsImageScaler getScaler(CmsImageScaler scaler, CmsImageScaler original, String scaleParam) {
162
163        if (scaleParam != null) {
164            CmsImageScaler cropScaler = null;
165            // use cropped image as a base for scaling
166            cropScaler = new CmsImageScaler(scaleParam);
167            if (scaler.getType() == 5) {
168                // must reset height / width parameters in crop scaler for type 5
169                cropScaler.setWidth(cropScaler.getCropWidth());
170                cropScaler.setHeight(cropScaler.getCropHeight());
171            }
172            scaler = cropScaler.getCropScaler(scaler);
173        }
174        // calculate target scale dimensions (if required)
175        if (((scaler.getHeight() <= 0) || (scaler.getWidth() <= 0))
176            || ((scaler.getType() == 5) && scaler.isValid() && !scaler.isCropping())) {
177            // read the image properties for the selected resource
178            if (original.isValid()) {
179                scaler = original.getReScaler(scaler);
180            }
181        }
182        return scaler;
183    }
184
185    /**
186     * Internal action method to create the tag content.<p>
187     *
188     * @param src the image source
189     * @param scaler the image scaleing parameters
190     * @param attributes the additional image HTML attributes
191     * @param partialTag if <code>true</code>, the opening <code>&lt;img</code> and closing <code> /&gt;</code> is omitted
192     * @param noDim if <code>true</code>, the <code>height</code> and <code>width</code> attributes are omitted
193     * @param req the current request
194     *
195     * @return the created &lt;img src&gt; tag content
196     *
197     * @throws CmsException in case something goes wrong
198     */
199    public static String imageTagAction(
200        String src,
201        CmsImageScaler scaler,
202        Map<String, String> attributes,
203        boolean partialTag,
204        boolean noDim,
205        ServletRequest req)
206    throws CmsException {
207
208        CmsFlexController controller = CmsFlexController.getController(req);
209        CmsObject cms = controller.getCmsObject();
210
211        // resolve possible relative URI
212        src = CmsLinkManager.getAbsoluteUri(src, controller.getCurrentRequest().getElementUri());
213        CmsUriSplitter splitSrc = new CmsUriSplitter(src);
214
215        String scaleParam = null;
216        if (splitSrc.getQuery() != null) {
217            // check if the original URI already has parameters, this is true if original has been cropped
218            String[] scaleStr = CmsRequestUtil.createParameterMap(splitSrc.getQuery()).get(CmsImageScaler.PARAM_SCALE);
219            if (scaleStr != null) {
220                scaleParam = scaleStr[0];
221            }
222        }
223
224        CmsResource imageRes = cms.readResource(splitSrc.getPrefix());
225        CmsImageScaler original = new CmsImageScaler(cms, imageRes);
226        scaler = getScaler(scaler, original, scaleParam);
227
228        StringBuffer result = new StringBuffer(128);
229        if (!partialTag) {
230            // open tag if not a partial tag
231            result.append("<img");
232        }
233
234        // append the image source
235        result.append(" src=\"");
236
237        String imageLink = cms.getSitePath(imageRes);
238        if (scaler.isValid()) {
239            // now append the scaler parameters
240            imageLink += scaler.toRequestParam();
241        }
242        result.append(OpenCms.getLinkManager().substituteLink(cms, imageLink));
243        result.append("\"");
244
245        if (!noDim && scaler.isValid()) {
246            // append image width and height
247            result.append(" width=\"");
248            result.append(scaler.getWidth());
249            result.append("\"");
250            result.append(" height=\"");
251            result.append(scaler.getHeight());
252            result.append("\"");
253        }
254
255        if (attributes != null) {
256            // append the HTML attributes
257            for (Map.Entry<String, String> entry : attributes.entrySet()) {
258                String attr = entry.getKey();
259                String value = entry.getValue();
260                result.append(" ");
261                result.append(attr);
262                result.append("=\"");
263                result.append(CmsEncoder.escapeXml(value));
264                result.append("\"");
265            }
266        }
267
268        if (!partialTag) {
269            // close tag if not a partial tag
270            result.append(" />");
271        }
272        return result.toString();
273    }
274
275    /**
276     * Internal action method to create the tag content.<p>
277     *
278     * @param src the image source
279     * @param scaler the image scaleing parameters
280     * @param attributes the additional image HTML attributes
281     * @param partialTag if <code>true</code>, the opening <code>&lt;img</code> and closing <code> /&gt;</code> is omitted
282     * @param req the current request
283     *
284     * @return the created &lt;img src&gt; tag content
285     *
286     * @throws CmsException in case something goes wrong
287     */
288    public static String imageTagAction(
289        String src,
290        CmsImageScaler scaler,
291        Map<String, String> attributes,
292        boolean partialTag,
293        ServletRequest req)
294    throws CmsException {
295
296        return imageTagAction(src, scaler, attributes, partialTag, false, req);
297    }
298
299    /**
300     * @see org.opencms.jsp.I_CmsJspTagParamParent#addParameter(java.lang.String, java.lang.String)
301     */
302    public void addParameter(String name, String value) {
303
304        String key = name.trim().toLowerCase();
305        switch (SCALER_ATTRS_LIST.indexOf(key)) {
306            case 0: // scaleColor
307                setScaleColor(value);
308                break;
309            case 1: // scaleFilter
310                setScaleFilter(value);
311                break;
312            case 2: // height
313                setHeight(value);
314                break;
315            case 3: // partialTag
316                setPartialTag(value);
317                break;
318            case 4: // scalePosition
319                setScalePosition(value);
320                break;
321            case 5: // scaleQuality
322                setScaleQuality(value);
323                break;
324            case 6: // scaleRendermode
325                setScaleRendermode(value);
326                break;
327            case 7: // src
328                setSrc(value);
329                break;
330            case 8: // scaleType
331                setScaleType(value);
332                break;
333            case 9: // width
334                setWidth(value);
335                break;
336            case 10: // maxHeight
337                setMaxHeight(value);
338                break;
339            case 11: // maxWidth
340                setMaxWidth(value);
341                break;
342            case 12: // noDim
343                setNoDim(value);
344                break;
345            default: // not a value used by the image scaler, treat as HTML attribute
346                setAttribute(key, value);
347        }
348    }
349
350    /**
351     * @see javax.servlet.jsp.tagext.Tag#doEndTag()
352     */
353    @Override
354    public int doEndTag() throws JspException {
355
356        ServletRequest req = pageContext.getRequest();
357
358        // this will always be true if the page is called through OpenCms
359        if (CmsFlexController.isCmsRequest(req)) {
360
361            try {
362                // create the HTML image tag
363                String imageTag = null;
364                try {
365                    imageTag = imageTagAction(m_src, m_scaler, m_attributes, m_partialTag, m_noDim, req);
366                } catch (CmsException e) {
367                    // any issue accessing the VFS - just return an empty string
368                    // otherwise template layout will get mixed up with nasty exception messages
369                    if (LOG.isWarnEnabled()) {
370                        LOG.warn(Messages.get().getBundle().key(Messages.ERR_IMAGE_TAG_VFS_ACCESS_1, m_src), e);
371                    }
372                }
373                // make sure that no null String is returned
374                pageContext.getOut().print(imageTag == null ? "" : imageTag);
375
376            } catch (Exception ex) {
377                if (LOG.isErrorEnabled()) {
378                    LOG.error(Messages.get().getBundle().key(Messages.ERR_PROCESS_TAG_1, "image"), ex);
379                }
380                throw new javax.servlet.jsp.JspException(ex);
381            }
382        }
383        release();
384        return EVAL_PAGE;
385    }
386
387    /**
388     * Returns <code>{@link #EVAL_BODY_BUFFERED}</code>.<p>
389     *
390     * @return <code>{@link #EVAL_BODY_BUFFERED}</code>
391     *
392     * @see javax.servlet.jsp.tagext.Tag#doStartTag()
393     */
394    @Override
395    public int doStartTag() {
396
397        return EVAL_BODY_BUFFERED;
398    }
399
400    /**
401     * Returns the value of the HTML "align" attribute.<p>
402     *
403     * @return the value of the HTML "align" attribute
404     */
405    public String getAlign() {
406
407        return getAttribute(ATTR_ALIGN);
408    }
409
410    /**
411     * Returns the value of the HTML "alt" attribute.<p>
412     *
413     * @return the value of the HTML "alt" attribute
414     */
415    public String getAlt() {
416
417        return getAttribute(ATTR_ALT);
418    }
419
420    /**
421     * Returns the value of the HTML "border" attribute.<p>
422     *
423     * @return the value of the HTML "border" attribute
424     */
425    public String getBorder() {
426
427        return getAttribute(ATTR_BORDER);
428    }
429
430    /**
431     * Returns the value of the HTML "class" attribute.<p>
432     *
433     * @return the value of the HTML "class" attribute
434     */
435    public String getCssclass() {
436
437        return getAttribute(ATTR_CLASS);
438    }
439
440    /**
441     * Returns the value of the HTML "hspace" attribute.<p>
442     *
443     * @return the value of the HTML "hspace" attribute
444     */
445    public String getHspace() {
446
447        return getAttribute(ATTR_HSPACE);
448    }
449
450    /**
451     * Returns the value of the HTML "id" attribute.<p>
452     *
453     * @return the value of the HTML "id" attribute
454     */
455    @Override
456    public String getId() {
457
458        return getAttribute(ATTR_ID);
459    }
460
461    /**
462     * Returns the value of the HTML "longdesc" attribute.<p>
463     *
464     * @return the value of the HTML "longdesc" attribute
465     */
466    public String getLongdesc() {
467
468        return getAttribute(ATTR_LONGDESC);
469    }
470
471    /**
472     * Returns the value of the HTML "name" attribute.<p>
473     *
474     * @return the value of the HTML "name" attribute
475     */
476    public String getName() {
477
478        return getAttribute(ATTR_NAME);
479    }
480
481    /**
482     * Returns <code>"true"</code> if the created HTML image tag does not contain height and width attributes.<p>
483     *
484     * @return <code>"true"</code> if the created HTML image tag does not contain height and width attributes
485     */
486
487    public String getNoDim() {
488
489        return String.valueOf(m_noDim);
490    }
491
492    /**
493     * Returns the value of the HTML "style" attribute.<p>
494     *
495     * @return the value of the HTML "style" attribute
496     */
497    public String getStyle() {
498
499        return getAttribute(ATTR_STYLE);
500    }
501
502    /**
503     * Returns the value of the HTML "title" attribute.<p>
504     *
505     * @return the value of the HTML "title" attribute
506     */
507    public String getTitle() {
508
509        return getAttribute(ATTR_TITLE);
510    }
511
512    /**
513     * Returns the value of the HTML "usemap" attribute.<p>
514     *
515     * @return the value of the HTML "usemap" attribute
516     */
517    public String getUsemap() {
518
519        return getAttribute(ATTR_USEMAP);
520    }
521
522    /**
523     * Returns the value of the HTML "vspace" attribute.<p>
524     *
525     * @return the value of the HTML "vspace" attribute
526     */
527    public String getVspace() {
528
529        return getAttribute(ATTR_VSPACE);
530    }
531
532    /**
533     * Returns <code>"true"</code> if the HTML tag should only be created as partial tag.<p>
534     *
535     * @return <code>"true"</code> if the HTML tag should only be created as partial tag
536     */
537    public String isPartialTag() {
538
539        return String.valueOf(m_partialTag);
540    }
541
542    /**
543     * @see javax.servlet.jsp.tagext.Tag#release()
544     */
545    @Override
546    public void release() {
547
548        m_attributes = null;
549        m_partialTag = false;
550        m_noDim = false;
551        super.release();
552    }
553
554    /**
555     * Sets the value of the HTML "align" attribute.<p>
556     *
557     * @param value the value of the HTML "align" attribute to set
558     */
559    public void setAlign(String value) {
560
561        setAttribute(ATTR_ALIGN, value);
562    }
563
564    /**
565     * Sets the value of the HTML "alt" attribute.<p>
566     *
567     * @param value the value of the HTML "alt" attribute to set
568     */
569    public void setAlt(String value) {
570
571        setAttribute(ATTR_ALT, value, true);
572
573    }
574
575    /**
576     * Sets the value of the HTML "border" attribute.<p>
577     *
578     * @param value the value of the HTML "border" attribute to set
579     */
580    public void setBorder(String value) {
581
582        setAttribute(ATTR_BORDER, value);
583
584    }
585
586    /**
587     * Sets the value of the HTML "class" attribute.<p>
588     *
589     * @param value the value of the HTML "class" attribute to set
590     */
591    public void setCssclass(String value) {
592
593        setAttribute(ATTR_CLASS, value);
594
595    }
596
597    /**
598     * Sets the value of the HTML "hspace" attribute.<p>
599     *
600     * @param value the value of the HTML "hspace" attribute to set
601     */
602    public void setHspace(String value) {
603
604        setAttribute(ATTR_HSPACE, value);
605
606    }
607
608    /**
609     * Sets the value of the HTML "id" attribute.<p>
610     *
611     * @param value the value of the HTML "id" attribute to set
612     */
613    @Override
614    public void setId(String value) {
615
616        setAttribute(ATTR_ID, value);
617
618    }
619
620    /**
621     * Sets the value of the HTML "longdesc" attribute.<p>
622     *
623     * @param value the value of the HTML "longdesc" attribute to set
624     */
625    public void setLongdesc(String value) {
626
627        setAttribute(ATTR_LONGDESC, value);
628
629    }
630
631    /**
632     * Sets the value of the HTML "name" attribute.<p>
633     *
634     * @param value the value of the HTML "name" attribute to set
635     */
636    public void setName(String value) {
637
638        setAttribute(ATTR_NAME, value);
639
640    }
641
642    /**
643     * Controls if the created HTML image tag contains height and width attributes.<p>
644     *
645     * @param noDim the value to set
646     */
647    public void setNoDim(String noDim) {
648
649        m_noDim = Boolean.valueOf(noDim).booleanValue();
650    }
651
652    /**
653     * Controls if the created HTML image tag is a full or partial tag.<p>
654     *
655     * @param partialTag the value to set
656     */
657    public void setPartialTag(String partialTag) {
658
659        m_partialTag = Boolean.valueOf(partialTag).booleanValue();
660    }
661
662    /**
663     * Sets the value of the HTML "style" attribute.<p>
664     *
665     * @param value the value of the HTML "style" attribute to set
666     */
667    public void setStyle(String value) {
668
669        setAttribute(ATTR_STYLE, value);
670    }
671
672    /**
673     * Sets the value of the HTML "title" attribute.<p>
674     *
675     * @param value the value of the HTML "title" attribute to set
676     */
677    public void setTitle(String value) {
678
679        setAttribute(ATTR_TITLE, value);
680    }
681
682    /**
683     * Sets the value of the HTML "usemap" attribute.<p>
684     *
685     * @param value the value of the HTML "usemap" attribute to set
686     */
687    public void setUsemap(String value) {
688
689        setAttribute(ATTR_USEMAP, value);
690    }
691
692    /**
693     * Sets the value of the HTML "vspace" attribute.<p>
694     *
695     * @param value the value of the HTML "vspace" attribute to set
696     */
697    public void setVspace(String value) {
698
699        setAttribute(ATTR_VSPACE, value);
700
701    }
702
703    /**
704     * Returns the given keys attribute value from the attribute map.<p>
705     *
706     * @param key the attribute to read from the map
707     * @return the given keys attribute value from the attribute map
708     */
709    private String getAttribute(String key) {
710
711        if (m_attributes != null) {
712            return m_attributes.get(key);
713        }
714        return null;
715    }
716
717    /**
718     * Sets the given key with the given value in the attribute map.<p>
719     *
720     * @param key the key to set
721     * @param value the value to set
722     */
723    private void setAttribute(String key, String value) {
724
725        setAttribute(key, value, false);
726    }
727
728    /**
729     * Sets the given key with the given value in the attribute map.<p>
730     *
731     * @param key the key to set
732     * @param value the value to set
733     * @param allowEmptyValue flag to determine if an empty value (not <code>null</code>!) should be set
734     */
735    private void setAttribute(String key, String value, boolean allowEmptyValue) {
736
737        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(value) || (allowEmptyValue && (value != null))) {
738            if (m_attributes == null) {
739                m_attributes = new HashMap<String, String>();
740            }
741            m_attributes.put(key, value);
742        }
743    }
744}