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