source: github/program/include/html.php @ 3f3ec14

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 3f3ec14 was 3f3ec14, checked in by alecpl <alec@…>, 2 years ago
  • Force allowed attributes on table rows
  • Property mode set to 100644
File size: 20.7 KB
Line 
1<?php
2
3/*
4 +-----------------------------------------------------------------------+
5 | program/include/html.php                                              |
6 |                                                                       |
7 | This file is part of the Roundcube Webmail client                     |
8 | Copyright (C) 2005-2010, The Roundcube Dev Team                       |
9 | Licensed under the GNU GPL                                            |
10 |                                                                       |
11 | PURPOSE:                                                              |
12 |   Helper class to create valid XHTML code                             |
13 |                                                                       |
14 +-----------------------------------------------------------------------+
15 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16 +-----------------------------------------------------------------------+
17
18 $Id$
19
20 */
21
22
23/**
24 * Class for HTML code creation
25 *
26 * @package HTML
27 */
28class html
29{
30    protected $tagname;
31    protected $attrib = array();
32    protected $allowed = array();
33    protected $content;
34
35    public static $lc_tags = true;
36    public static $common_attrib = array('id','class','style','title','align');
37    public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script');
38
39    /**
40     * Constructor
41     *
42     * @param array $attrib Hash array with tag attributes
43     */
44    public function __construct($attrib = array())
45    {
46        if (is_array($attrib)) {
47            $this->attrib = $attrib;
48        }
49    }
50
51    /**
52     * Return the tag code
53     *
54     * @return string The finally composed HTML tag
55     */
56    public function show()
57    {
58        return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed));
59    }
60
61    /****** STATIC METHODS *******/
62
63    /**
64     * Generic method to create a HTML tag
65     *
66     * @param string $tagname Tag name
67     * @param array  $attrib  Tag attributes as key/value pairs
68     * @param string $content Optinal Tag content (creates a container tag)
69     * @param array  $allowed_attrib List with allowed attributes, omit to allow all
70     * @return string The XHTML tag
71     */
72    public static function tag($tagname, $attrib = array(), $content = null, $allowed_attrib = null)
73    {
74        if (is_string($attrib))
75            $attrib = array('class' => $attrib);
76
77        $inline_tags = array('a','span','img');
78        $suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : '';
79
80        $tagname = self::$lc_tags ? strtolower($tagname) : $tagname;
81        if (isset($content) || in_array($tagname, self::$containers)) {
82            $templ = $attrib['noclose'] ? "<%s%s>%s" : "<%s%s>%s</%s>%s";
83            unset($attrib['noclose']);
84            return sprintf($templ, $tagname, self::attrib_string($attrib, $allowed_attrib), $content, $tagname, $suffix);
85        }
86        else {
87            return sprintf("<%s%s />%s", $tagname, self::attrib_string($attrib, $allowed_attrib), $suffix);
88        }
89    }
90
91    /**
92     * Derrived method for <div> containers
93     *
94     * @param mixed  $attr Hash array with tag attributes or string with class name
95     * @param string $cont Div content
96     * @return string HTML code
97     * @see html::tag()
98     */
99    public static function div($attr = null, $cont = null)
100    {
101        if (is_string($attr)) {
102            $attr = array('class' => $attr);
103        }
104        return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick')));
105    }
106
107    /**
108     * Derrived method for <p> blocks
109     *
110     * @param mixed  $attr Hash array with tag attributes or string with class name
111     * @param string $cont Paragraph content
112     * @return string HTML code
113     * @see html::tag()
114     */
115    public static function p($attr = null, $cont = null)
116    {
117        if (is_string($attr)) {
118            $attr = array('class' => $attr);
119        }
120        return self::tag('p', $attr, $cont, self::$common_attrib);
121    }
122
123    /**
124     * Derrived method to create <img />
125     *
126     * @param mixed $attr Hash array with tag attributes or string with image source (src)
127     * @return string HTML code
128     * @see html::tag()
129     */
130    public static function img($attr = null)
131    {
132        if (is_string($attr)) {
133            $attr = array('src' => $attr);
134        }
135        return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib,
136            array('src','alt','width','height','border','usemap')));
137    }
138
139    /**
140     * Derrived method for link tags
141     *
142     * @param mixed  $attr Hash array with tag attributes or string with link location (href)
143     * @param string $cont Link content
144     * @return string HTML code
145     * @see html::tag()
146     */
147    public static function a($attr, $cont)
148    {
149        if (is_string($attr)) {
150            $attr = array('href' => $attr);
151        }
152        return self::tag('a', $attr, $cont, array_merge(self::$common_attrib,
153            array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup')));
154    }
155
156    /**
157     * Derrived method for inline span tags
158     *
159     * @param mixed  $attr Hash array with tag attributes or string with class name
160     * @param string $cont Tag content
161     * @return string HTML code
162     * @see html::tag()
163     */
164    public static function span($attr, $cont)
165    {
166        if (is_string($attr)) {
167            $attr = array('class' => $attr);
168        }
169        return self::tag('span', $attr, $cont, self::$common_attrib);
170    }
171
172    /**
173     * Derrived method for form element labels
174     *
175     * @param mixed  $attr Hash array with tag attributes or string with 'for' attrib
176     * @param string $cont Tag content
177     * @return string HTML code
178     * @see html::tag()
179     */
180    public static function label($attr, $cont)
181    {
182        if (is_string($attr)) {
183            $attr = array('for' => $attr);
184        }
185        return self::tag('label', $attr, $cont, array_merge(self::$common_attrib, array('for')));
186    }
187
188    /**
189     * Derrived method to create <iframe></iframe>
190     *
191     * @param mixed $attr Hash array with tag attributes or string with frame source (src)
192     * @return string HTML code
193     * @see html::tag()
194     */
195    public static function iframe($attr = null, $cont = null)
196    {
197        if (is_string($attr)) {
198            $attr = array('src' => $attr);
199        }
200        return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib,
201            array('src','name','width','height','border','frameborder')));
202    }
203
204    /**
205     * Derrived method for line breaks
206     *
207     * @return string HTML code
208     * @see html::tag()
209     */
210    public static function br()
211    {
212        return self::tag('br');
213    }
214
215    /**
216     * Create string with attributes
217     *
218     * @param array $attrib Associative arry with tag attributes
219     * @param array $allowed List of allowed attributes
220     * @return string Valid attribute string
221     */
222    public static function attrib_string($attrib = array(), $allowed = null)
223    {
224        if (empty($attrib)) {
225            return '';
226        }
227
228        $allowed_f = array_flip((array)$allowed);
229        $attrib_arr = array();
230        foreach ($attrib as $key => $value) {
231            // skip size if not numeric
232            if (($key=='size' && !is_numeric($value))) {
233                continue;
234            }
235
236            // ignore "internal" or not allowed attributes
237            if ($key == 'nl' || ($allowed && !isset($allowed_f[$key])) || $value === null) {
238                continue;
239            }
240
241            // skip empty eventhandlers
242            if (preg_match('/^on[a-z]+/', $key) && !$value) {
243                continue;
244            }
245
246            // attributes with no value
247            if (in_array($key, array('checked', 'multiple', 'disabled', 'selected'))) {
248                if ($value) {
249                    $attrib_arr[] = sprintf('%s="%s"', $key, $key);
250                }
251            }
252            else if ($key=='value') {
253                $attrib_arr[] = sprintf('%s="%s"', $key, Q($value, 'strict', false));
254            }
255            else {
256                $attrib_arr[] = sprintf('%s="%s"', $key, Q($value));
257            }
258        }
259        return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
260    }
261}
262
263/**
264 * Class to create an HTML input field
265 *
266 * @package HTML
267 */
268class html_inputfield extends html
269{
270    protected $tagname = 'input';
271    protected $type = 'text';
272    protected $allowed = array('type','name','value','size','tabindex',
273        'autocomplete','checked','onchange','onclick','disabled','readonly',
274        'spellcheck','results','maxlength','src','multiple');
275
276    /**
277     * Object constructor
278     *
279     * @param array $attrib Associative array with tag attributes
280     */
281    public function __construct($attrib = array())
282    {
283        if (is_array($attrib)) {
284            $this->attrib = $attrib;
285        }
286
287        if ($attrib['type']) {
288            $this->type = $attrib['type'];
289        }
290
291        if ($attrib['newline']) {
292            $this->newline = true;
293        }
294    }
295
296    /**
297     * Compose input tag
298     *
299     * @param string $value Field value
300     * @param array  $attrib Additional attributes to override
301     * @return string HTML output
302     */
303    public function show($value = null, $attrib = null)
304    {
305        // overwrite object attributes
306        if (is_array($attrib)) {
307            $this->attrib = array_merge($this->attrib, $attrib);
308        }
309
310        // set value attribute
311        if ($value !== null) {
312            $this->attrib['value'] = $value;
313        }
314        // set type
315        $this->attrib['type'] = $this->type;
316        return parent::show();
317    }
318}
319
320/**
321 * Class to create an HTML password field
322 *
323 * @package HTML
324 */
325class html_passwordfield extends html_inputfield
326{
327    protected $type = 'password';
328}
329
330/**
331 * Class to create an hidden HTML input field
332 *
333 * @package HTML
334 */
335
336class html_hiddenfield extends html_inputfield
337{
338    protected $type = 'hidden';
339    protected $fields_arr = array();
340    protected $newline = true;
341
342    /**
343     * Constructor
344     *
345     * @param array $attrib Named tag attributes
346     */
347    public function __construct($attrib = null)
348    {
349        if (is_array($attrib)) {
350            $this->add($attrib);
351        }
352    }
353
354    /**
355     * Add a hidden field to this instance
356     *
357     * @param array $attrib Named tag attributes
358     */
359    public function add($attrib)
360    {
361        $this->fields_arr[] = $attrib;
362    }
363
364    /**
365     * Create HTML code for the hidden fields
366     *
367     * @return string Final HTML code
368     */
369    public function show()
370    {
371        $out = '';
372        foreach ($this->fields_arr as $attrib) {
373            $out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
374        }
375        return $out;
376    }
377}
378
379/**
380 * Class to create HTML radio buttons
381 *
382 * @package HTML
383 */
384class html_radiobutton extends html_inputfield
385{
386    protected $type = 'radio';
387
388    /**
389     * Get HTML code for this object
390     *
391     * @param string $value  Value of the checked field
392     * @param array  $attrib Additional attributes to override
393     * @return string HTML output
394     */
395    public function show($value = '', $attrib = null)
396    {
397        // overwrite object attributes
398        if (is_array($attrib)) {
399            $this->attrib = array_merge($this->attrib, $attrib);
400        }
401
402        // set value attribute
403        $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
404
405        return parent::show();
406    }
407}
408
409/**
410 * Class to create HTML checkboxes
411 *
412 * @package HTML
413 */
414class html_checkbox extends html_inputfield
415{
416    protected $type = 'checkbox';
417
418    /**
419     * Get HTML code for this object
420     *
421     * @param string $value  Value of the checked field
422     * @param array  $attrib Additional attributes to override
423     * @return string HTML output
424     */
425    public function show($value = '', $attrib = null)
426    {
427        // overwrite object attributes
428        if (is_array($attrib)) {
429            $this->attrib = array_merge($this->attrib, $attrib);
430        }
431
432        // set value attribute
433        $this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
434
435        return parent::show();
436    }
437}
438
439/**
440 * Class to create an HTML textarea
441 *
442 * @package HTML
443 */
444class html_textarea extends html
445{
446    protected $tagname = 'textarea';
447    protected $allowed = array('name','rows','cols','wrap','tabindex',
448        'onchange','disabled','readonly','spellcheck');
449
450    /**
451     * Get HTML code for this object
452     *
453     * @param string $value  Textbox value
454     * @param array  $attrib Additional attributes to override
455     * @return string HTML output
456     */
457    public function show($value = '', $attrib = null)
458    {
459        // overwrite object attributes
460        if (is_array($attrib)) {
461            $this->attrib = array_merge($this->attrib, $attrib);
462        }
463
464        // take value attribute as content
465        if (empty($value) && !empty($this->attrib['value'])) {
466            $value = $this->attrib['value'];
467        }
468
469        // make shure we don't print the value attribute
470        if (isset($this->attrib['value'])) {
471            unset($this->attrib['value']);
472        }
473
474        if (!empty($value) && !preg_match('/mce_editor/', $this->attrib['class'])) {
475            $value = Q($value, 'strict', false);
476        }
477
478        return self::tag($this->tagname, $this->attrib, $value,
479            array_merge(self::$common_attrib, $this->allowed));
480    }
481}
482
483/**
484 * Builder for HTML drop-down menus
485 * Syntax:<pre>
486 * // create instance. arguments are used to set attributes of select-tag
487 * $select = new html_select(array('name' => 'fieldname'));
488 *
489 * // add one option
490 * $select->add('Switzerland', 'CH');
491 *
492 * // add multiple options
493 * $select->add(array('Switzerland','Germany'), array('CH','DE'));
494 *
495 * // generate pulldown with selection 'Switzerland'  and return html-code
496 * // as second argument the same attributes available to instanciate can be used
497 * print $select->show('CH');
498 * </pre>
499 *
500 * @package HTML
501 */
502class html_select extends html
503{
504    protected $tagname = 'select';
505    protected $options = array();
506    protected $allowed = array('name','size','tabindex','autocomplete',
507        'multiple','onchange','disabled','rel');
508   
509    /**
510     * Add a new option to this drop-down
511     *
512     * @param mixed $names  Option name or array with option names
513     * @param mixed $values Option value or array with option values
514     */
515    public function add($names, $values = null)
516    {
517        if (is_array($names)) {
518            foreach ($names as $i => $text) {
519                $this->options[] = array('text' => $text, 'value' => $values[$i]);
520            }
521        }
522        else {
523            $this->options[] = array('text' => $names, 'value' => $values);
524        }
525    }
526
527    /**
528     * Get HTML code for this object
529     *
530     * @param string $select Value of the selection option
531     * @param array  $attrib Additional attributes to override
532     * @return string HTML output
533     */
534    public function show($select = array(), $attrib = null)
535    {
536        // overwrite object attributes
537        if (is_array($attrib)) {
538            $this->attrib = array_merge($this->attrib, $attrib);
539        }
540
541        $this->content = "\n";
542        $select = (array)$select;
543        foreach ($this->options as $option) {
544            $attr = array(
545                'value' => $option['value'],
546                'selected' => (in_array($option['value'], $select, true) ||
547                  in_array($option['text'], $select, true)) ? 1 : null);
548
549            $this->content .= self::tag('option', $attr, Q($option['text']));
550        }
551        return parent::show();
552    }
553}
554
555
556/**
557 * Class to build an HTML table
558 *
559 * @package HTML
560 */
561class html_table extends html
562{
563    protected $tagname = 'table';
564    protected $allowed = array('id','class','style','width','summary',
565            'cellpadding','cellspacing','border');
566
567    private $header = array();
568    private $rows = array();
569    private $rowindex = 0;
570    private $colindex = 0;
571
572    /**
573     * Constructor
574     *
575     * @param array $attrib Named tag attributes
576     */
577    public function __construct($attrib = array())
578    {
579        $this->attrib = array_merge($attrib, array('summary' => '', 'border' => 0));
580    }
581
582    /**
583     * Add a table cell
584     *
585     * @param array  $attr Cell attributes
586     * @param string $cont Cell content
587     */
588    public function add($attr, $cont)
589    {
590        if (is_string($attr)) {
591            $attr = array('class' => $attr);
592        }
593
594        $cell = new stdClass;
595        $cell->attrib = $attr;
596        $cell->content = $cont;
597
598        $this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
599        $this->colindex++;
600
601        if ($this->attrib['cols'] && $this->colindex == $this->attrib['cols']) {
602            $this->add_row();
603        }
604    }
605
606    /**
607     * Add a table header cell
608     *
609     * @param array  $attr Cell attributes
610     * @param string $cont Cell content
611     */
612    public function add_header($attr, $cont)
613    {
614        if (is_string($attr))
615            $attr = array('class' => $attr);
616
617        $cell = new stdClass;
618        $cell->attrib = $attr;
619        $cell->content = $cont;
620        $this->header[] = $cell;
621    }
622
623     /**
624     * Remove a column from a table
625     * Useful for plugins making alterations
626     *
627     * @param string $class
628     */
629    public function remove_column($class)
630    {
631        // Remove the header
632        foreach ($this->header as $index=>$header){
633            if ($header->attrib['class'] == $class){
634                unset($this->header[$index]);
635                break;
636            }
637        }
638
639        // Remove cells from rows
640        foreach ($this->rows as $i=>$row){
641            foreach ($row->cells as $j=>$cell){
642                if ($cell->attrib['class'] == $class){
643                    unset($this->rows[$i]->cells[$j]);
644                    break;
645                }
646            }
647        }
648    }
649
650    /**
651     * Jump to next row
652     *
653     * @param array $attr Row attributes
654     */
655    public function add_row($attr = array())
656    {
657        $this->rowindex++;
658        $this->colindex = 0;
659        $this->rows[$this->rowindex] = new stdClass;
660        $this->rows[$this->rowindex]->attrib = $attr;
661        $this->rows[$this->rowindex]->cells = array();
662    }
663
664    /**
665     * Set row attributes
666     *
667     * @param array $attr  Row attributes
668     * @param int   $index Optional row index (default current row index)
669     */
670    public function set_row_attribs($attr = array(), $index = null)
671    {
672        if (is_string($attr))
673            $attr = array('class' => $attr);
674
675        if ($index === null)
676            $index = $this->rowindex;
677
678        if ($this->rows[$index])
679            $this->rows[$index]->attrib = $attr;
680    }
681
682    /**
683     * Get row attributes
684     *
685     * @param int $index Row index
686     *
687     * @return array Row attributes
688     */
689    public function get_row_attribs($index = null)
690    {
691        if ($index === null)
692            $index = $this->rowindex;
693
694        return $this->rows[$index] ? $this->rows[$index]->attrib : null;
695    }
696
697    /**
698     * Build HTML output of the table data
699     *
700     * @param array $attrib Table attributes
701     * @return string The final table HTML code
702     */
703    public function show($attrib = null)
704    {
705        if (is_array($attrib))
706            $this->attrib = array_merge($this->attrib, $attrib);
707
708        $thead = $tbody = "";
709
710        // include <thead>
711        if (!empty($this->header)) {
712            $rowcontent = '';
713            foreach ($this->header as $c => $col) {
714                $rowcontent .= self::tag('td', $col->attrib, $col->content);
715            }
716            $thead = self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib));
717        }
718
719        foreach ($this->rows as $r => $row) {
720            $rowcontent = '';
721            foreach ($row->cells as $c => $col) {
722                $rowcontent .= self::tag('td', $col->attrib, $col->content);
723            }
724
725            if ($r < $this->rowindex || count($row->cells)) {
726                $tbody .= self::tag('tr', $row->attrib, $rowcontent, parent::$common_attrib);
727            }
728        }
729
730        if ($this->attrib['rowsonly']) {
731            return $tbody;
732        }
733
734        // add <tbody>
735        $this->content = $thead . self::tag('tbody', null, $tbody);
736
737        unset($this->attrib['cols'], $this->attrib['rowsonly']);
738        return parent::show();
739    }
740
741    /**
742     * Count number of rows
743     *
744     * @return The number of rows
745     */
746    public function size()
747    {
748      return count($this->rows);
749    }
750
751    /**
752     * Remove table body (all rows)
753     */
754    public function remove_body()
755    {
756        $this->rows     = array();
757        $this->rowindex = 0;
758    }
759
760}
761
Note: See TracBrowser for help on using the repository browser.