001package org.apache.turbine.util.template;
002
003
004/*
005 * Licensed to the Apache Software Foundation (ASF) under one
006 * or more contributor license agreements.  See the NOTICE file
007 * distributed with this work for additional information
008 * regarding copyright ownership.  The ASF licenses this file
009 * to you under the Apache License, Version 2.0 (the
010 * "License"); you may not use this file except in compliance
011 * with the License.  You may obtain a copy of the License at
012 *
013 *   http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing,
016 * software distributed under the License is distributed on an
017 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018 * KIND, either express or implied.  See the License for the
019 * specific language governing permissions and limitations
020 * under the License.
021 */
022
023
024import java.util.ArrayList;
025import java.util.LinkedHashMap;
026import java.util.List;
027import java.util.Map;
028
029import org.apache.commons.configuration.Configuration;
030import org.apache.commons.lang.StringUtils;
031import org.apache.turbine.Turbine;
032import org.apache.turbine.TurbineConstants;
033import org.apache.turbine.services.pull.ApplicationTool;
034
035/**
036 * Template context tool that can be used to set various attributes of a
037 * HTML page.  This tool does not automatically make the changes in the HTML
038 * page for you.  You must use this tool in your layout template to retrieve
039 * the attributes.
040 * <p>
041 * The set/add methods are can be used from a screen template, action, screen
042 * class, layour template, or anywhere else.  The get methods should be used in
043 * your layout template(s) to construct the appropriate HTML tags.
044 *<p>
045 * Example usage of this tool to build the HEAD and BODY tags in your layout
046 * templates:
047 * <p>
048 *  <code>
049 *  ## Set defaults for all pages using this layout.  Anything set here can<br>
050 *  ## be overridden in the screen template.<br>
051 *  $page.setTitle("My default page title");<br>
052 *  $page.setHttpEquiv("Content-Style-Type","text/css")<br>
053 *  $page.addStyleSheet($content.getURI("myStyleSheet.css"))<br>
054 *  $page.addScript($content.getURI("globalJavascriptCode.js"))<br>
055 *  <br>
056 *  ## build the HTML, HEAD, and BODY tags dynamically<br>
057 *  &lt;html&gt;<br>
058 *    &lt;head&gt;<br>
059 *      #if( $page.Title != "" )<br>
060 *      &lt;title&gt;$page.Title&lt;/title&gt;<br>
061 *      #end<br>
062 *      #foreach($metaTag in $page.MetaTags.keySet())<br>
063 *      &lt;meta name="$metaTag" content="$page.MetaTags.get($metaTag)"&gt;<br>
064 *      #end<br>
065 *      #foreach($httpEquiv in $page.HttpEquivs.keySet())<br>
066 *      &lt;meta http-equiv="$httpEquiv" content="$page.HttpEquivs.get($httpEquiv)"&gt;<br>
067 *      #end<br>
068 *      #foreach( $styleSheet in $page.StyleSheets )<br>
069 *        &lt;link rel="stylesheet" href="$styleSheet.Url"<br>
070 *          #if($styleSheet.Type != "" ) type="$styleSheet.Type" #end<br>
071 *          #if($styleSheet.Media != "") media="$styleSheet.Media" #end<br>
072 *          #if($styleSheet.Title != "") title="$styleSheet.Title" #end<br>
073 *        &gt;<br>
074 *      #end<br>
075 *      #foreach( $script in $page.Scripts )<br>
076 *        &lt;script type="text/javascript" src="$script" language="JavaScript"&gt;&lt;/script&gt;<br>
077 *      #end<br>
078 *    &lt;/head&gt;<br>
079 *<br>
080 *    ## Construct the body tag.  Iterate through the body attributes to build the opening tag<br>
081 *    &lt;body<br>
082 *      #foreach( $attributeName in $page.BodyAttributes.keySet() )<br>
083 *        $attributeName = "$page.BodyAttributes.get($attributeName)"<br>
084 *      #end<br>
085 *     &gt;
086 * </code>
087 * <p>
088 * Example usages of this tool in your screen templates:<br>
089 *   <code>$page.addScript($content.getURI("myJavascript.js")<br>
090 *   $page.setTitle("My page title")<br>
091 *   $page.setHttpEquiv("refresh","5; URL=http://localhost/nextpage.html")</code>
092 *
093 * @author <a href="mailto:quintonm@bellsouth.net">Quinton McCombs</a>
094 * @author <a href="mailto:seade@backstagetech.com.au">Scott Eade</a>
095 * @version $Id: HtmlPageAttributes.java 1709648 2015-10-20 17:08:10Z tv $
096 */
097public class HtmlPageAttributes
098        implements ApplicationTool
099{
100    /** The title */
101    private String title;
102
103    /** Body Attributes */
104    private final Map<String, String> bodyAttributes = new LinkedHashMap<String, String>();
105
106    /** Script references */
107    private final List<String> scripts = new ArrayList<String>();
108
109    /** External references */
110    private final List<LinkTag> linkTags = new ArrayList<LinkTag>();
111
112    /** Inline styles */
113    private final List<String> styles = new ArrayList<String>();
114
115    /** Meta tags for the HEAD */
116    private final Map<String, String> metaTags = new LinkedHashMap<String, String>();
117
118    /** http-equiv tags */
119    private final Map<String, String> httpEquivs = new LinkedHashMap<String, String>();
120
121    /** Doctype */
122    private String doctype = null;
123
124    /**
125     * Construct a new instance
126     */
127    public HtmlPageAttributes()
128    {
129        init(null);
130    }
131
132    /**
133     * Initialize this instance.
134     * (ApplicationTool method)
135     *
136     * @param data not used
137     */
138    @Override
139    public void init(Object data)
140    {
141        this.title = null;
142        this.bodyAttributes.clear();
143        this.scripts.clear();
144        this.linkTags.clear();
145        this.styles.clear();
146        this.metaTags.clear();
147        this.httpEquivs.clear();
148    }
149
150    /**
151     * Refresh method - does nothing
152     */
153    @Override
154    public void refresh()
155    {
156        // empty
157    }
158
159    /**
160     * Set the title in the page.  This returns an empty String so
161     * that the template doesn't complain about getting a null return
162     * value.  Subsequent calls to this method will replace the current
163     * title.
164     *
165     * @param title A String with the title.
166     * @return a <code>HtmlPageAttributes</code> (self).
167     */
168    public HtmlPageAttributes setTitle(String title)
169    {
170        this.title = title;
171        return this;
172    }
173
174    /**
175     * Get the title in the page.  This returns an empty String if
176     * empty so that the template doesn't complain about getting a null
177     * return value.
178     *
179     * @return A String with the title.
180     */
181    public String getTitle()
182    {
183        if (StringUtils.isEmpty(this.title))
184        {
185            return "";
186        }
187        return title;
188    }
189
190    /**
191     * Adds an attribute to the BODY tag.
192     *
193     * @param name A String.
194     * @param value A String.
195     * @return a <code>HtmlPageAttributes</code> (self).
196     */
197    public HtmlPageAttributes addBodyAttribute(String name, String value)
198    {
199        this.bodyAttributes.put(name, value);
200        return this;
201    }
202
203    /**
204     * Returns the map of body attributes
205     *
206     * @return the map
207     */
208    public Map<String, String> getBodyAttributes()
209    {
210        return this.bodyAttributes;
211    }
212
213    /**
214     * Adds a script reference
215     *
216     * @param scriptURL
217     * @return a <code>HtmlPageAttributes</code> (self).
218     */
219    public HtmlPageAttributes addScript(String scriptURL)
220    {
221        this.scripts.add(scriptURL);
222        return this;
223    }
224
225    /**
226     * Returns a collection of script URLs
227     *
228     * @return list of String objects containing URLs of javascript files
229     * to include
230     */
231    public List<String> getScripts()
232    {
233        return this.scripts;
234    }
235
236    /**
237     * Adds a style sheet reference
238     *
239     * @param styleSheetURL URL of the style sheet
240     * @return a <code>HtmlPageAttributes</code> (self).
241     */
242    public HtmlPageAttributes addStyleSheet(String styleSheetURL)
243    {
244        addStyleSheet(styleSheetURL, "screen", null, "text/css");
245        return this;
246    }
247
248    /**
249     * Adds a style sheet reference
250     *
251     * @param styleSheetURL URL of the style sheet
252     * @param media name of the media
253     * @param title title of the stylesheet
254     * @param type content type
255     * @return a <code>HtmlPageAttributes</code> (self).
256     */
257    public HtmlPageAttributes addStyleSheet(String styleSheetURL,
258                                            String media, String title, String type)
259    {
260        LinkTag ss = new LinkTag("stylesheet", styleSheetURL);
261        ss.setMedia(media);
262        ss.setTitle(title);
263        ss.setType(type);
264        this.linkTags.add(ss);
265        return this;
266    }
267
268    /**
269     * Adds a generic external reference
270     *
271     * @param relation type of the reference (prev, next, first, last, top, etc.)
272     * @param linkURL URL of the reference
273     * @return a <code>HtmlPageAttributes</code> (self).
274     */
275    public HtmlPageAttributes addLink(String relation, String linkURL)
276    {
277        return addLink(relation, linkURL, null, null);
278    }
279
280    /**
281     * Adds a generic external reference
282     *
283     * @param relation type of the reference (prev, next, first, last, top, etc.)
284     * @param linkURL URL of the reference
285     * @param title title of the reference
286     * @return a <code>HtmlPageAttributes</code> (self).
287     */
288    public HtmlPageAttributes addLink(String relation, String linkURL, String title)
289    {
290        return addLink(relation, linkURL, title, null);
291    }
292
293    /**
294     * Adds a generic external reference
295     *
296     * @param relation type of the reference (prev, next, first, last, top, etc.)
297     * @param linkURL URL of the reference
298     * @param title title of the reference
299     * @param type content type
300     * @return a <code>HtmlPageAttributes</code> (self).
301     */
302    public HtmlPageAttributes addLink(String relation, String linkURL, String title,
303                                        String type)
304    {
305        LinkTag ss = new LinkTag(relation, linkURL);
306        ss.setTitle(title);
307        ss.setType(type);
308        this.linkTags.add(ss);
309        return this;
310    }
311
312    /**
313     * Returns a collection of link URLs
314     *
315     * @return list LinkTag objects (inner class)
316     */
317    public List<LinkTag> getLinks()
318    {
319        return this.linkTags;
320    }
321
322    /**
323     * Adds a STYLE element to the HEAD of the page with the provided content.
324     *
325     * @param styleText The contents of the <code>style</code> tag.
326     * @return a <code>HtmlPageAttributes</code> (self).
327     */
328    public HtmlPageAttributes addStyle(String styleText)
329    {
330        this.styles.add(styleText);
331        return this;
332    }
333
334    /**
335     * Returns a collection of styles
336     *
337     * @return list of String objects containing the contents of style tags
338     */
339    public List<String> getStyles()
340    {
341        return this.styles;
342    }
343
344    /**
345     * Set a keywords META tag in the HEAD of the page.
346     *
347     * @param keywords A String.
348     * @return a <code>HtmlPageAttributes</code> (self).
349     */
350    public HtmlPageAttributes setKeywords(String keywords)
351    {
352        this.metaTags.put("keywords", keywords);
353        return this;
354    }
355
356    /**
357     * Sets a HttpEquiv META tag in the HEAD of the page, usage:
358     * <br><code>setHttpEquiv("refresh", "5; URL=http://localhost/nextpage.html")</code>
359     * <br><code>setHttpEquiv("Expires", "Tue, 20 Aug 1996 14:25:27 GMT")</code>
360     *
361     * @param httpEquiv The value to use for the http-equiv attribute.
362     * @param content   The text for the content attribute of the meta tag.
363     * @return a <code>HtmlPageAttributes</code> (self).
364     */
365    public HtmlPageAttributes setHttpEquiv(String httpEquiv, String content)
366    {
367        this.httpEquivs.put(httpEquiv, content);
368        return this;
369    }
370
371    /**
372     * Add a description META tag to the HEAD of the page.
373     *
374     * @param description A String.
375     * @return a <code>HtmlPageAttributes</code> (self).
376     */
377    public HtmlPageAttributes setDescription(String description)
378    {
379        this.metaTags.put("description", description);
380        return this;
381    }
382
383    /**
384     * Set the background image for the BODY tag.
385     *
386     * @param url A String.
387     * @return a <code>HtmlPageAttributes</code> (self).
388     */
389    public HtmlPageAttributes setBackground(String url)
390    {
391        this.bodyAttributes.put("background", url);
392        return this;
393    }
394
395    /**
396     * Set the background color for the BODY tag.  You can use either
397     * color names or color values (e.g. "white" or "#ffffff" or
398     * "ffffff").
399     *
400     * @param color A String.
401     * @return a <code>HtmlPageAttributes</code> (self).
402     */
403    public HtmlPageAttributes setBgColor(String color)
404    {
405        this.bodyAttributes.put("BGCOLOR", color);
406        return this;
407    }
408
409    /**
410     * Set the text color for the BODY tag.  You can use either color
411     * names or color values (e.g. "white" or "#ffffff" or "ffffff").
412     *
413     * @param color A String.
414     * @return a <code>HtmlPageAttributes</code> (self).
415     */
416    public HtmlPageAttributes setTextColor(String color)
417    {
418        this.bodyAttributes.put("TEXT", color);
419        return this;
420    }
421
422    /**
423     * Set the link color for the BODY tag.  You can use either color
424     * names or color values (e.g. "white" or "#ffffff" or "ffffff").
425     *
426     * @param color A String.
427     * @return a <code>HtmlPageAttributes</code> (self).
428     */
429    public HtmlPageAttributes setLinkColor(String color)
430    {
431        this.bodyAttributes.put("LINK", color);
432        return this;
433    }
434
435    /**
436     * Set the visited link color for the BODY tag.
437     *
438     * @param color A String.
439     * @return a <code>HtmlPageAttributes</code> (self).
440     */
441    public HtmlPageAttributes setVlinkColor(String color)
442    {
443        this.bodyAttributes.put("VLINK", color);
444        return this;
445    }
446
447    /**
448     * Set the active link color for the BODY tag.
449     *
450     * @param color A String.
451     * @return a <code>HtmlPageAttributes</code> (self).
452     */
453    public HtmlPageAttributes setAlinkColor(String color)
454    {
455        this.bodyAttributes.put("ALINK", color);
456        return this;
457    }
458
459    /**
460     * Gets the map of http equiv tags
461     *
462     * @return Map of http equiv names to the contents
463     */
464    public Map<String, String> getHttpEquivs()
465    {
466        return this.httpEquivs;
467    }
468
469    /**
470     * Gets the map of meta tags
471     *
472     * @return Map of http equiv names to the contents
473     */
474    public Map<String, String> getMetaTags()
475    {
476        return this.metaTags;
477    }
478
479    /**
480     * A dummy toString method that returns an empty string.
481     *
482     * @return An empty String ("").
483     */
484    @Override
485    public String toString()
486    {
487        return "";
488    }
489
490    /**
491     * Helper class to hold data about a &lt;link ... /&gt; html header tag
492     */
493    public static class LinkTag
494    {
495        private String relation;
496        private String url;
497        private String title;
498        private String media;
499        private String type;
500
501        /**
502         * Constructor requiring the URL and relation to be set
503         *
504         * @param relation Relation type the external link such as prev, next,
505         *        stylesheet, shortcut icon
506         * @param url URL of the external link
507         */
508        public LinkTag(String relation, String url)
509        {
510            setRelation(relation);
511            setUrl(url);
512        }
513
514        /**
515         * Gets the content type of the style sheet
516         *
517         * @return content type
518         */
519        public String getType()
520        {
521            return (StringUtils.isEmpty(type) ? "" : type);
522        }
523
524        /**
525         * Sets the content type of the style sheet
526         *
527         * @param type content type
528         */
529        public void setType(String type)
530        {
531            this.type = type;
532        }
533
534        /**
535         * @return String representation of the URL
536         */
537        public String getUrl()
538        {
539            return url;
540        }
541
542        /**
543         * Sets the URL of the external style sheet
544         *
545         * @param url The URL of the stylesheet
546         */
547        private void setUrl(String url)
548        {
549            this.url = url;
550        }
551
552        /**
553         * Gets the title of the style sheet
554         *
555         * @return title
556         */
557        public String getTitle()
558        {
559            return (StringUtils.isEmpty(title) ? "" : title);
560        }
561
562        /**
563         * Sets the title of the stylesheet
564         *
565         * @param title
566         */
567        public void setTitle(String title)
568        {
569            this.title = title;
570        }
571
572        /**
573         * Gets the media for which the stylesheet should be applied.
574         *
575         * @return name of the media
576         */
577        public String getMedia()
578        {
579            return (StringUtils.isEmpty(media) ? "" : media);
580        }
581
582        /**
583         * Sets the media for which the stylesheet should be applied.
584         *
585         * @param media name of the media
586         */
587        public void setMedia(String media)
588        {
589            this.media = media;
590        }
591
592        /**
593         * Gets the relation type of the tag.
594         *
595         * @return name of the relation
596         */
597        public String getRelation()
598        {
599            return (StringUtils.isEmpty(relation) ? "" : relation);
600        }
601
602        /**
603         * Sets the relation type of the tag.
604         *
605         * @param relation name of the relation
606         */
607        public void setRelation(String relation)
608        {
609            this.relation = relation;
610        }
611    }
612
613    /**
614     * Retrieve the default Doctype as configured by the
615     * TurbineResources.peoperties
616     * default.doctype.root.element, default.doctype.identifier and
617     * default.doctype.url properties (defaults are "HTML",
618     * "-//W3C//DTD HTML 4.01 Transitional//EN" and
619     * "http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd" respectively).
620     *
621     * @return the DOCTYPE tag constructed from the properties in
622     * TurbineResources.properties.
623     */
624    public String getDefaultDoctype()
625    {
626        if (doctype == null)
627        {
628            Configuration conf = Turbine.getConfiguration();
629            String tag = conf.getString(
630                    TurbineConstants.DEFAULT_HTML_DOCTYPE_ROOT_ELEMENT_KEY,
631                    TurbineConstants.DEFAULT_HTML_DOCTYPE_ROOT_ELEMENT_DEFAULT);
632
633            if (StringUtils.isEmpty(tag))
634            {
635                doctype = "";
636            }
637            else
638            {
639                String identifier = conf.getString(
640                        TurbineConstants.DEFAULT_HTML_DOCTYPE_IDENTIFIER_KEY,
641                        TurbineConstants.DEFAULT_HTML_DOCTYPE_IDENTIFIER_DEFAULT);
642
643                String uri = conf.getString(
644                        TurbineConstants.DEFAULT_HTML_DOCTYPE_URI_KEY,
645                        TurbineConstants.DEFAULT_HTML_DOCTYPE_URI_DEFAULT);
646
647                doctype = buildDoctype(tag, identifier, uri);
648            }
649        }
650
651        return doctype;
652    }
653
654    /**
655     * Build the doctype element.
656     *
657     * @param tag the tag whose DTD is being declared.
658     * @param identifier the identifier for the doctype declaration.
659     * @param uri the uri for the doctype declaration.
660     * @return the doctype.
661     */
662    private String buildDoctype(String tag, String identifier, String uri)
663    {
664        StringBuilder doctypeBuf = new StringBuilder("<!DOCTYPE ");
665        doctypeBuf.append(tag);
666
667        if (StringUtils.isNotEmpty(identifier))
668        {
669            doctypeBuf.append(" PUBLIC \"");
670            doctypeBuf.append(identifier);
671            doctypeBuf.append("\" \"");
672        }
673        else
674        {
675            doctypeBuf.append(" SYSTEM \"");
676        }
677
678        doctypeBuf.append(uri);
679        doctypeBuf.append("\">");
680
681        return doctypeBuf.toString();
682    }
683}