]> git.donarmstrong.com Git - roundcube.git/blob - program/include/rcube_html.inc
Merge commit 'upstream/0.1'
[roundcube.git] / program / include / rcube_html.inc
1 <?php
2
3 /*
4  +-----------------------------------------------------------------------+
5  | rcube_html.inc                                                        |
6  |                                                                       |
7  | This file is part of the RoundCube PHP suite                          |
8  | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
9  | Licensed under the GNU GPL                                            |
10  |                                                                       |
11  | CONTENTS:                                                             |
12  |   Common Classes to create HTML output                                |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id:  $
19
20 */
21
22
23 /**
24  * HTML page builder class
25  *
26  * @package HTML
27  */
28 class rcube_html_page
29 {
30   var $scripts_path = '';
31   var $script_files = array();
32   var $scripts = array();
33   var $charset = 'UTF-8';
34   
35   var $script_tag_file = "<script type=\"text/javascript\" src=\"%s%s\"></script>\n";
36   var $script_tag      = "<script type=\"text/javascript\">\n<!--\n%s\n\n//-->\n</script>\n";
37   var $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
38
39   var $title = 'RoundCube Mail';
40   var $header = '';
41   var $footer = '';
42   var $body = '';
43   var $body_attrib = array();
44   var $meta_tags = array();
45   
46   
47   /**
48    * Link an external script file
49    *
50    * @param string File URL
51    * @param string Target position [head|foot]
52    */
53   function include_script($file, $position='head')
54   {
55     static $sa_files = array();
56     
57     if (in_array($file, $sa_files))
58       return;
59       
60     if (!is_array($this->script_files[$position]))
61       $this->script_files[$position] = array();
62       
63     $this->script_files[$position][] = $file;
64   }
65   
66   /**
67    * Add inline javascript code
68    *
69    * @param string JS code snippet
70    * @param string Target position [head|head_top|foot]
71    */
72   function add_script($script, $position='head')
73   {
74     if (!isset($this->scripts[$position]))
75       $this->scripts[$position] = "\n".rtrim($script);
76     else
77       $this->scripts[$position] .= "\n".rtrim($script);
78   }
79
80   /**
81    * Add HTML code to the page header
82    */
83   function add_header($str)
84   {
85     $this->header .= "\n".$str;
86   }
87
88   /**
89    * Add HTML code to the page footer
90    * To be added right befor </body>
91    */
92   function add_footer($str)
93   {
94     $this->footer .= "\n".$str;
95   }
96
97   /**
98    * Setter for page title
99    */
100   function set_title($t)
101   {
102     $this->title = $t;
103   }
104
105
106   /**
107    * Setter for output charset.
108    * To be specified in a meta tag and sent as http-header
109    */
110   function set_charset($charset)
111   {
112     global $MBSTRING;
113     
114     $this->charset = $charset;
115     
116     if ($MBSTRING && function_exists("mb_internal_encoding"))
117       {
118       if(!@mb_internal_encoding($charset))
119         $MBSTRING = FALSE;
120       }
121   }
122
123   /**
124    * Getter for output charset
125    */
126   function get_charset()
127   {
128     return $this->charset;
129   }
130
131
132   /**
133    * Reset all saved properties
134    */
135   function reset()
136   {
137     $this->script_files = array();
138     $this->scripts = array();
139     $this->title = '';
140     $this->header = '';
141     $this->footer = '';
142   }
143
144
145   /**
146    * Process template and write to stdOut
147    *
148    * @param string HTML template
149    * @param string Base for absolute paths
150    */
151   function write($templ='', $base_path='')
152   {
153     $output = empty($templ) ? $this->default_template : trim($templ);
154     
155     // replace specialchars in content
156     $__page_title = Q($this->title, 'show', FALSE);
157     $__page_header = $__page_body = $__page_footer = '';
158     
159     
160     // include meta tag with charset
161     if (!empty($this->charset))
162     {
163       header('Content-Type: text/html; charset='.$this->charset, true);
164       $__page_header = '<meta http-equiv="content-type" content="text/html; charset='.$this->charset.'" />'."\n";
165     }
166   
167   
168     // definition of the code to be placed in the document header and footer
169     if (is_array($this->script_files['head']))
170       foreach ($this->script_files['head'] as $file)
171         $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
172
173     $head_script = $this->scripts['head_top'] . $this->scripts['head'];
174     if (!empty($head_script))
175       $__page_header .= sprintf($this->script_tag, $head_script);
176
177     if (!empty($this->header))
178       $__page_header .= $this->header;
179
180     if (is_array($this->script_files['foot']))
181       foreach ($this->script_files['foot'] as $file)
182         $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
183
184     if (!empty($this->scripts['foot']))
185       $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']);
186       
187     if (!empty($this->footer))
188       $__page_footer .= $this->footer;
189
190     // find page header
191     if ($hpos = strpos(strtolower($output), '</head>'))
192       $__page_header .= "\n";
193     else 
194     {
195       if (!is_numeric($hpos))
196         $hpos = strpos(strtolower($output), '<body');
197       if (!is_numeric($hpos) && ($hpos = strpos(strtolower($output), '<html')))
198       {
199         while($output[$hpos]!='>')
200           $hpos++;
201         $hpos++;
202       }
203   
204       $__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n";
205     }
206   
207     // add page hader
208     if ($hpos)
209       $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output));
210     else
211       $output = $__page_header . $output;
212   
213   
214     // find page body
215     if($bpos = strpos(strtolower($output), '<body'))
216     {
217       while($output[$bpos]!='>') $bpos++;
218       $bpos++;
219     }
220     else
221       $bpos = strpos(strtolower($output), '</head>')+7;
222   
223     // add page body
224     if($bpos && $__page_body)
225       $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output));
226   
227   
228     // find and add page footer
229     $output_lc = strtolower($output);
230     if(($fpos = strrstr($output_lc, '</body>')) ||
231        ($fpos = strrstr($output_lc, '</html>')))
232       $output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos);
233     else
234       $output .= "\n$__page_footer";
235   
236   
237     // reset those global vars
238     $__page_header = $__page_footer = '';
239   
240   
241     // correct absolute paths in images and other tags
242     $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output);
243     $output = str_replace('$__skin_path', $base_path, $output);
244   
245     print rcube_charset_convert($output, 'UTF-8', $this->charset);
246   }
247     
248 }  // end class rcube_html_page
249
250
251
252 /**
253  * Base class to build a HTML for element
254  *
255  * @package HTML
256  */
257 class rcube_form_element
258   {
259   var $uppertags = FALSE;
260   var $upperattribs = FALSE;
261   var $upperprops = FALSE;
262   var $newline = FALSE;
263   
264   var $attrib = array();
265
266
267   /**
268    * Create string with saved attributes
269    *
270    * @return string HTML formatted tag attributes
271    */
272   function create_attrib_string()
273   {
274     if (!sizeof($this->attrib))
275       return '';
276
277     if ($this->name!='')
278       $this->attrib['name'] = $this->name;
279
280     $attrib_arr = array();
281     foreach ($this->attrib as $key => $value)
282     {
283       // don't output some internally used attributes
284       if (in_array($key, array('form', 'quicksearch')))
285         continue;
286
287       // skip if size if not numeric
288       if (($key=='size' && !is_numeric($value)))
289         continue;
290         
291       // skip empty eventhandlers
292       if ((strpos($key,'on')===0 && $value==''))
293         continue;
294
295       // attributes with no value
296       if (in_array($key, array('checked', 'multiple', 'disabled', 'selected', 'nowrap')))
297       {
298         if ($value)
299           $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $key);
300       }
301       // don't convert size of value attribute
302       else if ($key=='value')
303         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), Q($value, 'strict', false));
304         
305       // regular tag attributes
306       else
307         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $this->_conv_case(Q($value), 'value'));
308     }
309
310     return sizeof($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
311   }
312     
313     
314   /**
315    * Convert tags and attributes to upper-/lowercase
316    *
317    * @param string Input string
318    * @param string Value type (can either be "tag" or "attrib")
319    * @return string Converted output string
320    * @access private
321    */
322   function _conv_case($str, $type='attrib')
323     {
324     if ($type == 'tag')
325       return $this->uppertags ? strtoupper($str) : strtolower($str);
326     else if ($type == 'attrib')
327       return $this->upperattribs ? strtoupper($str) : strtolower($str);
328     else if ($type == 'value')
329       return $this->upperprops ? strtoupper($str) : strtolower($str);
330     }    
331   }
332
333
334 /**
335  * Builder for an <input> field
336  *
337  * @package HTML
338  */
339 class input_field extends rcube_form_element
340 {
341   var $type = 'text';
342   
343   /**
344    * Constructor
345    * @param array Named tag attributes
346    */
347   function input_field($attrib=array())
348   {
349     if (is_array($attrib))
350       $this->attrib = $attrib;
351
352     if ($attrib['type'])
353       $this->type = $attrib['type'];    
354
355     if ($attrib['newline'])
356       $this->newline = TRUE;    
357   }  
358
359   /**
360    * Compose input tag
361    *
362    * @param string Field value
363    * @param array  Additional tag attributes
364    * @return string Final HTML code
365    */
366   function show($value=NULL, $attrib=NULL)
367   {
368     // overwrite object attributes
369     if (is_array($attrib))
370       $this->attrib = array_merge($this->attrib, $attrib);
371
372     // set value attribute
373     if ($value!==NULL)
374       $this->attrib['value'] = $value;
375
376     $this->attrib['type'] = $this->type;
377
378     // return final tag
379     return sprintf(
380       '<%s%s />%s',
381       $this->_conv_case('input', 'tag'),
382       $this->create_attrib_string(),
383       ($this->newline ? "\n" : ""));
384   }  
385 }
386
387
388 /**
389  * Builder for a <input type="text"> field
390  *
391  * @package HTML
392  */
393 class textfield extends input_field
394 {
395   var $type = 'text';
396 }
397
398 /**
399  * Builder for a <input type="password"> field
400  *
401  * @package HTML
402  */
403 class passwordfield extends input_field
404 {
405   var $type = 'password';
406 }
407
408 /**
409  * Builder for <input type="radio"> fields
410  *
411  * @package HTML
412  */
413 class radiobutton extends input_field
414 {
415   var $type = 'radio';
416 }
417
418 /**
419  * Builder for <input type="checkbox"> fields
420  *
421  * @package HTML
422  */
423 class checkbox extends input_field
424 {
425   var $type = 'checkbox';
426
427
428   /**
429    * Compose input tag
430    *
431    * @param string Field value
432    * @param array  Additional tag attributes
433    * @return string Final HTML code
434    */
435   function show($value='', $attrib=NULL)
436   {
437     // overwrite object attributes
438     if (is_array($attrib))
439       $this->attrib = array_merge($this->attrib, $attrib);    
440
441     $this->attrib['type'] = $this->type;
442
443     if ($value && (string)$value==(string)$this->attrib['value'])
444       $this->attrib['checked'] = TRUE;
445     else
446       $this->attrib['checked'] = FALSE;
447
448     // return final tag
449     return sprintf(
450       '<%s%s />%s',
451       $this->_conv_case('input', 'tag'),
452       $this->create_attrib_string(),
453       ($this->newline ? "\n" : ""));
454   }
455 }
456
457
458 /**
459  * Builder for a <textarea> field
460  *
461  * @package HTML
462  */
463 class textarea extends rcube_form_element
464   {
465
466   /**
467    * Constructor
468    * @param array Named tag attributes
469    */
470   function textarea($attrib=array())
471   {
472     $this->attrib = $attrib;
473
474     if ($attrib['newline'])
475       $this->newline = TRUE;
476   }
477   
478   /**
479    * Create HTML representation for this field
480    *
481    * @param string Field value
482    * @param array  Additional tag attributes
483    * @return string Final HTML code
484    */
485   function show($value='', $attrib=NULL)
486   {
487     // overwrite object attributes
488     if (is_array($attrib))
489       $this->attrib = array_merge($this->attrib, $attrib);
490     
491     // take value attribute as content
492     if ($value=='')
493       $value = $this->attrib['value'];
494     
495     // make shure we don't print the value attribute
496     if (isset($this->attrib['value']))
497       unset($this->attrib['value']);
498
499     if (!empty($value) && !isset($this->attrib['mce_editable']))
500       $value = Q($value, 'strict', FALSE);
501
502     // return final tag
503     return sprintf(
504       '<%s%s>%s</%s>%s',
505       $this->_conv_case('textarea', 'tag'),
506       $this->create_attrib_string(),
507       $value,
508       $this->_conv_case('textarea', 'tag'),
509       ($this->newline ? "\n" : ""));       
510   }
511 }
512
513
514 /**
515  * Builder for group of hidden form fields
516  *
517  * @package HTML
518  */
519 class hiddenfield extends rcube_form_element
520 {
521   var $fields_arr = array();
522   var $newline = TRUE;
523
524   /**
525    * Constructor
526    *
527    * @param array Named tag attributes
528    */
529   function hiddenfield($attrib=NULL)
530   {
531     if (is_array($attrib))
532       $this->add($attrib);
533   }
534
535   /**
536    * Add a hidden field to this instance
537    * @param array Named tag attributes
538    */
539   function add($attrib)
540   {
541     $this->fields_arr[] = $attrib;
542   }
543
544
545   /**
546    * Create HTML code for the hidden fields
547    *
548    * @return string Final HTML code
549    */
550   function show()
551   {
552     $out = '';
553     foreach ($this->fields_arr as $attrib)
554     {
555       $this->attrib = $attrib;
556       $this->attrib['type'] = 'hidden';
557       
558       $out .= sprintf(
559         '<%s%s />%s',
560         $this->_conv_case('input', 'tag'),
561         $this->create_attrib_string(),
562         ($this->newline ? "\n" : ""));
563     }
564
565     return $out;
566   }
567 }
568
569
570 /**
571  * Builder for HTML drop-down menus
572  * Syntax:<pre>
573  * // create instance. arguments are used to set attributes of select-tag
574  * $select = new select(array('name' => 'fieldname'));
575  *
576  * // add one option
577  * $select->add('Switzerland', 'CH');
578  *
579  * // add multiple options
580  * $select->add(array('Switzerland','Germany'), array('CH','DE'));
581  *
582  * // generate pulldown with selection 'Switzerland'  and return html-code
583  * // as second argument the same attributes available to instanciate can be used
584  * print $select->show('CH');
585  * </pre>
586  *
587  * @package HTML
588  */
589 class select extends rcube_form_element
590 {
591   var $options = array();
592
593   /**
594    * Constructor
595    *
596    * @param array Named tag attributes
597    */
598   function select($attrib=NULL)
599   {
600     if (is_array($attrib))
601       $this->attrib = $attrib;
602
603     if ($attrib['newline'])
604       $this->newline = TRUE;
605   }
606
607
608   /**
609    * Add one ore more menu options
610    *
611    * @param mixed Array with names or single option name
612    * @param mixed Array with values or single option value
613    */
614   function add($names, $values=NULL)
615   {
616     if (is_array($names))
617     {
618       foreach ($names as $i => $text)
619         $this->options[] = array('text' => $text, 'value' => $values[$i]);
620     }
621     else
622       $this->options[] = array('text' => $names, 'value' => $values);
623   }
624
625
626   /**
627    * Generate HTML code for this drop-down menu
628    *
629    * @param string Value of the selected option
630    * @param array Additional tag attributes
631    * @return string Final HTML code
632    */
633   function show($select=array(), $attrib=NULL)
634   {
635     $options_str = "\n";
636     $value_str = $this->_conv_case(' value="%s"', 'attrib');
637     
638     if (!is_array($select))
639       $select = array($select);
640
641     foreach ($this->options as $option)
642     {
643       $selected = ((isset($option['value']) &&
644                     in_array($option['value'], $select, TRUE)) ||
645                    (in_array($option['text'], $select, TRUE))) ?
646         $this->_conv_case(' selected="selected"', 'attrib') : '';
647                    
648       $options_str .= sprintf("<%s%s%s>%s</%s>\n",
649                              $this->_conv_case('option', 'tag'),
650                              isset($option['value']) ? sprintf($value_str, Q($option['value'])) : '',
651                              $selected, 
652                              Q($option['text'], 'strict', FALSE),
653                              $this->_conv_case('option', 'tag'));
654     }
655
656     // return final tag
657     return sprintf('<%s%s>%s</%s>%s',
658                    $this->_conv_case('select', 'tag'),
659                    $this->create_attrib_string(),
660                    $options_str,
661                    $this->_conv_case('select', 'tag'),
662                    ($this->newline ? "\n" : ""));    
663   }
664 }
665
666
667 ?>