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    }