thomascube
2006-12-13 7a5c48e7f70b8bc938fcae3ffd2be0fdbeaab145
commit | author | age
4e17e6 1 <?php
T 2
3 /*
4  +-----------------------------------------------------------------------+
5  | rcube_shared.inc                                                      |
6  |                                                                       |
7  | This file is part of the RoundCube PHP suite                          |
8  | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
30233b 9  | Licensed under the GNU GPL                                            |
4e17e6 10  |                                                                       |
T 11  | CONTENTS:                                                             |
12  |   Shared functions and classes used in PHP projects                   |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id$
19
20 */
21
22
23 // ********* round cube schared classes *********
24
25 class rcube_html_page
26   {
27   var $css;
28   
29   var $scripts_path = '';
30   var $script_files = array();
a0109c 31   var $external_scripts = array();
4e17e6 32   var $scripts = array();
7cc38e 33   var $charset = 'ISO-8859-1';
4e17e6 34   
T 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";
8d4bcd 37   var $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
a0109c 38   var $tag_format_external_script = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
S 39
4e17e6 40   var $title = '';
T 41   var $header = '';
42   var $footer = '';
43   var $body = '';
44   var $body_attrib = array();
45   var $meta_tags = array();
46
47
48   // PHP 5 constructor
49   function __construct()
50     {
51     $this->css = new rcube_css();
52     }
53
54   // PHP 4 compatibility
55   function rcube_html_page()
56     {
57     $this->__construct();
58     }
59
60
61   function include_script($file, $position='head')
62     {
63     static $sa_files = array();
64     
65     if (in_array($file, $sa_files))
66       return;
67       
68     if (!is_array($this->script_files[$position]))
69       $this->script_files[$position] = array();
70       
71     $this->script_files[$position][] = $file;
72     }
73     
a0109c 74   function include_external_script($script_location, $position='head')
S 75   {
76      if (!is_array($this->external_scripts[$position]))
77      {
78         $this->external_scripts[$position] = array();
79      }
80      
81      $this->external_scripts[$position][] = $script_location;
82   }
83
4e17e6 84   function add_script($script, $position='head')
T 85     {
86     if (!isset($this->scripts[$position]))
a0109c 87       $this->scripts[$position] = "\n$script";
S 88     else
89       $this->scripts[$position] .= "\n$script";
4e17e6 90     }
T 91
92
41fa0b 93   function set_title($t)
4e17e6 94     {
41fa0b 95     $this->title = $t;
4e17e6 96     }
41fa0b 97
4e17e6 98
7cc38e 99   function set_charset($charset)
T 100     {
f88d41 101     global $MBSTRING;
13c1af 102     
7cc38e 103     $this->charset = $charset;
13c1af 104     
T 105     if ($MBSTRING && function_exists("mb_internal_encoding"))
f88d41 106       {
13c1af 107       if(!@mb_internal_encoding($charset))
f88d41 108         $MBSTRING = FALSE;
T 109       }
7cc38e 110     }
41fa0b 111
3f9edb 112   function get_charset()
0af7e8 113     {
3f9edb 114     return $this->charset;
0af7e8 115     }
7cc38e 116
4e17e6 117
41fa0b 118   function reset()
T 119     {
120     $this->css = new rcube_css();
121     $this->script_files = array();
122     $this->scripts = array();
123     $this->title = '';
124     }
125
126
4e17e6 127   function write($templ='', $base_path='')
T 128     {
41fa0b 129     $output = empty($templ) ? $this->default_template : trim($templ);
8d4bcd 130     
4e17e6 131     // set default page title
T 132     if (!strlen($this->title))
ea7c46 133       $this->title = 'RoundCube Mail';
4e17e6 134   
T 135     // replace specialchars in content
136     $__page_title = rep_specialchars_output($this->title, 'html', 'show', FALSE);
137     $__page_header = $__page_body = $__page_footer = '';
7cc38e 138     
T 139     
140     // include meta tag with charset
141     if (!empty($this->charset))
ea7c46 142       {
T 143       header('Content-Type: text/html; charset='.$this->charset);
144       $__page_header = '<meta http-equiv="content-type" content="text/html; charset='.$this->charset.'" />'."\n";
145       }
4e17e6 146   
T 147   
148     // definition of the code to be placed in the document header and footer
149     if (is_array($this->script_files['head']))
150       foreach ($this->script_files['head'] as $file)
151         $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
152
a0109c 153    if (is_array($this->external_scripts['head']))
S 154    {
155       foreach ($this->external_scripts['head'] as $xscript)
156       {
157          $__page_header .= sprintf($this->tag_format_external_script, $xscript);
158       }
159    }
160
4e17e6 161     if (strlen($this->scripts['head']))
T 162       $__page_header .= sprintf($this->script_tag, $this->scripts['head']);
163           
164     if (is_array($this->script_files['foot']))
a0109c 165       {
4e17e6 166       foreach ($this->script_files['foot'] as $file)
T 167         $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
a0109c 168       }
4e17e6 169
T 170     if (strlen($this->scripts['foot']))
171       $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']);
b19097 172       
T 173     if ($this->footer)
174       $__page_footer .= "\n" . $this->footer;
4e17e6 175
T 176     $__page_header .= $this->css->show();
177   
178     // find page header
179     if($hpos = strpos(strtolower($output), '</head>'))
180       $__page_header .= "\n";
181     else 
182       {
183       if (!is_numeric($hpos))
184         $hpos = strpos(strtolower($output), '<body');
185       if (!is_numeric($hpos) && ($hpos = strpos(strtolower($output), '<html')))
186         {
187         while($output[$hpos]!='>')
188         $hpos++;
189         $hpos++;
190         }
191   
192       $__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n";
193       }
194   
195     // add page hader
196     if($hpos)
197       $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output));
198     else
199       $output = $__page_header . $output;
200   
201   
202     // find page body
203     if($bpos = strpos(strtolower($output), '<body'))
204       {
205       while($output[$bpos]!='>') $bpos++;
206       $bpos++;
207       }
208     else
209       $bpos = strpos(strtolower($output), '</head>')+7;
210   
211     // add page body
212     if($bpos && $__page_body)
213       $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output));
214   
215   
216     // find and add page footer
a0109c 217     $output_lc = strtolower($output);
4ec0e7 218     if(($fpos = strrstr($output_lc, '</body>')) ||
T 219        ($fpos = strrstr($output_lc, '</html>')))
674a0f 220       $output = substr($output,0,$fpos) . "$__page_footer\n" . substr($output,$fpos);
4e17e6 221     else
T 222       $output .= "\n$__page_footer";
223   
224   
225     // reset those global vars
226     $__page_header = $__page_footer = '';
227   
228   
a0109c 229     // correct absolute paths in images and other tags
4e17e6 230     $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output);
dd53e2 231     $output = str_replace('$__skin_path', $base_path, $output);
4e17e6 232   
3f9edb 233     print rcube_charset_convert($output, 'UTF-8', $this->charset);
4e17e6 234     }
T 235     
236     
237   function _parse($templ)
238     {
239     
240     }
241   }
242
243
244
245
246 class rcube_css
247   {
248   var $css_data = array();
249
250   var $css_groups = array();
251
252   var $include_files = array();
253
254   var $grouped_output = TRUE;
255
256   var $content_type = 'text/css';
257
258   var $base_path = '';
259
260   var $indent_chars = "\t";
261
262
263   // add or overwrite a css definition
264   // either pass porperty and value as separate arguments
265   // or provide an associative array as second argument
266   function set_style($selector, $property, $value='')
267     {
268     $a_elements = $this->_parse_selectors($selector);
269     foreach ($a_elements as $element)
270       {
271       if (!is_array($property))
272         $property = array($property => $value);
273
274       foreach ($property as $name => $value)
275         $this->css_data[$element][strtolower($name)] = $value;
276       }
277
278     // clear goups array
279     $this->css_groups = array();
280     }
281
282
283   // unset a style property
284   function remove_style($selector, $property)
285     {
286     if (!is_array($property))
287       $property = array($property);
288
289     foreach ($property as $key)
290       unset($this->css_data[$selector][strtolower($key)]);
291
292     // clear goups array
293     $this->css_groups = array();
294     }
295
296
297   // define base path for external css files
298   function set_basepath($path)
299     {
300     $this->base_path = preg_replace('/\/$/', '', $path);
301     }
302
303
304   // enable/disable grouped output
305   function set_grouped_output($grouped)
306     {
307     $this->grouped_output = $grouped;
308     }
309
310
311   // add a css file as external source
312   function include_file($filename, $media='')
313     {
314     // include multiple files
315     if (is_array($filename))
316       {
317       foreach ($filename as $file)
318         $this->include_file($file, $media);
319       }
320     // add single file
321     else if (!in_array($filename, $this->include_files))
322       $this->include_files[] = array('file' => $filename,
323                                      'media' => $media);
324     }
325
326
327   // parse css code
328   function import_string($str)
329     {
330     $ret = FALSE;
331     if (strlen($str))
332       $ret = $this->_parse($str);
333
334     return $ret;
335     }
336
337
338   // open and parse a css file
339   function import_file($file)
340     {
341     $ret = FALSE;
342
343     if (!is_file($file))
344       return $ret;
345
346     // for php version >= 4.3.0
347     if (function_exists('file_get_contents'))
348       $ret = $this->_parse(file_get_contents($file));
349
350     // for order php versions
351     else if ($fp = fopen($file, 'r'))
352       {
353       $ret = $this->_parse(fread($fp, filesize($file)));
354       fclose($fp);
355       }
356
357     return $ret;
358     }
359
360
361   // copy all properties inherited from superior styles to a specific selector
362   function copy_inherited_styles($selector)
363     {
364     // get inherited props from body and tag/class selectors
365     $css_props = $this->_get_inherited_styles($selector);
366
367     // write modified props back and clear goups array
368     if (sizeof($css_props))
369       {
370       $this->css_data[$selector] = $css_props;
371       $this->css_groups = array();
372       }
373     }
374
375
376   // return css definition for embedding in HTML
377   function show()
378     {
379     $out = '';
380
381     // include external css files
382     if (sizeof($this->include_files))
383       foreach ($this->include_files as $file_arr)
384       $out .= sprintf('<link rel="stylesheet" type="%s" href="%s"%s>'."\n",
385                         $this->content_type,
386                         $this->_get_file_path($file_arr['file']),
387                         $file_arr['media'] ? ' media="'.$file_arr['media'].'"' : '');
388
389
390     // compose css string
391     if (sizeof($this->css_data))
392       $out .= sprintf("<style type=\"%s\">\n<!--\n\n%s-->\n</style>",
393                       $this->content_type,
394                       $this->to_string());
395
396
397     return $out;
398     }
399
400
401   // return valid css code of the current styles grid
402   function to_string($selector=NULL)
403     {
404     // return code for a single selector
405     if ($selector)
406       {
407       $indent_str = $this->indent_chars;
408       $this->indent_chars = '';
409
410       $prop_arr = $this->to_array($selector);
411       $out = $this->_style2string($prop_arr, TRUE);
412
413       $this->indent_chars = $indent_str;
414       }
415
416     // compose css code for complete data grid
417     else
418       {
419       $out = '';
420       $css_data = $this->to_array();
421
422       foreach ($css_data as $key => $prop_arr)
423         $out .= sprintf("%s {\n%s}\n\n",
424                         $key,
425                         $this->_style2string($prop_arr, TRUE));
426       }
427
428     return $out;
429     }
430
431
432   // return a single-line string of a css definition
433   function to_inline($selector)
434     {
435     if ($this->css_data[$selector])
436       return str_replace('"', '\\"', $this->_style2string($this->css_data[$selector], FALSE));
437     }
438
439
440   // return an associative array with selector(s) as key and styles array as value
441   function to_array($selector=NULL)
442     {
443     if (!$selector && $this->grouped_output)
444       {
445       // build groups if desired
446       if (!sizeof($this->css_groups))
447         $this->_build_groups();
448
449       // modify group array to get an array(selector => properties)
450       $out_arr = array();
451       foreach ($this->css_groups as $group_arr)
452         {
453         $key = join(', ', $group_arr['selectors']);
454         $out_arr[$key] = $group_arr['properties'];
455         }
456       }
457     else
458       $out_arr = $this->css_data;
459
460     return $selector ? $out_arr[$selector] : $out_arr;
461     }
462
463
464   // create a css file
465   function to_file($filepath)
466     {
467     if ($fp = fopen($filepath, 'w'))
468       {
469       fwrite($fp, $this->to_string());
470       fclose($fp);
471       return TRUE;
472       }
473
474     return FALSE;
475     }
476
477
478   // alias method for import_string() [DEPRECATED]
479   function add($str)
480     {
481     $this->import_string($str);
482     }
483
484   // alias method for to_string() [DEPRECATED]
485   function get()
486     {
487     return $this->to_string();
488     }
489
490
491
492   // ******** private methods ********
493
494
495   // parse a string and add styles to internal data grid
496   function _parse($str)
497     {
498     // remove comments
499     $str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
500
501     // parse style definitions
502     if (!preg_match_all ('/([a-z0-9\.#*:_][a-z0-9\.\-_#:*,\[\]\(\)\s\"\'\+\|>~=]+)\s*\{([^\}]*)\}/ims', $str, $matches, PREG_SET_ORDER))
503       return FALSE;
504
505
506     foreach ($matches as $match_arr)
507       {
508       // split selectors into array
509       $a_keys = $this->_parse_selectors(trim($match_arr[1]));
510
511       // parse each property of an element
512       $codes = explode(";", trim($match_arr[2]));
513       foreach ($codes as $code)
514         {
515         if (strlen(trim($code))>0)
516           {
517           // find the property and the value
518           if (!($sep = strpos($code, ':')))
519             continue;
520
521           $property = strtolower(trim(substr($code, 0, $sep)));
522           $value    = trim(substr($code, $sep+1));
523
524           // add the property to the object array
525           foreach ($a_keys as $key)
526             $this->css_data[$key][$property] = $value;
527           }
528         }
529       }
530
531     // clear goups array
532     if (sizeof($matches))
533       {
534       $this->css_groups = array();
535       return TRUE;
536       }
537
538     return FALSE;
539     }
540
541
542   // split selector group
543   function _parse_selectors($selector)
544     {
545     // trim selector and remove multiple spaces
546     $selector = preg_replace('/\s+/', ' ', trim($selector));
547
548     if (strpos($selector, ','))
549       return preg_split('/[\t\s\n\r]*,[\t\s\n\r]*/mi', $selector);
550     else
551       return array($selector);
552     }
553
554
555   // compare identical styles and make groups
556   function _build_groups()
557     {
558     // clear group array
559     $this->css_groups = array();
560     $string_group_map = array();
561
562     // bulild css string for each selector and check if the same is already defines
563     foreach ($this->css_data as $selector => $prop_arr)
564       {
565       // make shure to compare props in the same order
566       ksort($prop_arr);
567       $compare_str = preg_replace('/[\s\t]+/', '', $this->_style2string($prop_arr, FALSE));
568
569       // add selector to extisting group
570       if (isset($string_group_map[$compare_str]))
571         {
572         $group_index = $string_group_map[$compare_str];
573         $this->css_groups[$group_index]['selectors'][] = $selector;
574         }
575
576       // create new group
577       else
578         {
579         $i = sizeof($this->css_groups);
580         $string_group_map[$compare_str] = $i;
581         $this->css_groups[$i] = array('selectors' => array($selector),
582                                       'properties' => $this->css_data[$selector]);
583         }
584       }
585     }
586
587
588   // convert the prop array into a valid css definition
589   function _style2string($prop_arr, $multiline=TRUE)
590     {
591     $out = '';
592     $delm   = $multiline ? "\n" : '';
593     $spacer = $multiline ? ' ' : '';
594     $indent = $multiline ? $this->indent_chars : '';
595
596     if (is_array($prop_arr))
597       foreach ($prop_arr as $prop => $value)
598         if (strlen($value))
599           $out .= sprintf('%s%s:%s%s;%s',
600                           $indent,
601                           $prop,
602                           $spacer,
603                           $value,
604                           $delm);
605
606     return $out;
607     }
608
609
610   // copy all properties inherited from superior styles to a specific selector
611   function _get_inherited_styles($selector, $loop=FALSE)
612     {
613     $css_props = $this->css_data[$selector] ? $this->css_data[$selector] : array();
614
615     // get styles from tag selector
616     if (preg_match('/(([a-z0-9]*)(\.[^\s]+)?)$/i', $selector, $regs))
617       {
618       $sel = $regs[1];
619       $tagname = $regs[2];
620       $class = $regs[3];
621
622       if ($sel && is_array($this->css_data[$sel]))
623         $css_props = $this->_merge_styles($this->css_data[$sel], $css_props);
624
625       if ($class && is_array($this->css_data[$class]))
626         $css_props = $this->_merge_styles($this->css_data[$class], $css_props);
627
628       if ($tagname && is_array($this->css_data[$tagname]))
629         $css_props = $this->_merge_styles($this->css_data[$tagname], $css_props);
630       }
631
632     // analyse inheritance
633     if (strpos($selector, ' '))
634       {
635       $a_hier = split(' ', $selector);
636       if (sizeof($a_hier)>1)
637         {
638         array_pop($a_hier);
639         $base_selector = join(' ', $a_hier);
640
641         // call this method recursively
642         $new_props = $this->_get_inherited_styles($base_selector, TRUE);
643         $css_props = $this->_merge_styles($new_props, $css_props);
644         }
645       }
646
647     // get body style
648     if (!$loop && is_array($this->css_data['body']))
649       $css_props = $this->_merge_styles($this->css_data['body'], $css_props);
650
651     return $css_props;
652     }
653
654
655   // merge two arrays with style properties together like a browser would do
656   function _merge_styles($one, $two)
657     {
658     // these properties are additive
659     foreach (array('text-decoration') as $prop)
660       if ($one[$prop] && $two[$prop])
661         {
662         // if value contains 'none' it's ignored
663         if (strstr($one[$prop], 'none'))
664           continue;
665         else if (strstr($two[$prop], 'none'))
666           unset($two[$prop]);
667
668         $a_values_one = split(' ', $one[$prop]);
669         $a_values_two = split(' ', $two[$prop]);
670         $two[$prop] = join(' ', array_unique(array_merge($a_values_one, $a_values_two)));
671         }
672
673     return array_merge($one, $two);
674     }
675
676
677   // resolve file path
678   function _get_file_path($file)
679     {
680     if (!$this->base_path && $GLOBALS['CSS_PATH'])
681       $this->set_basepath($GLOBALS['CSS_PATH']);
682
683     $base = ($file{0}=='/' || $file{0}=='.' || substr($file, 0, 7)=='http://') ? '' :
684             ($this->base_path ? $this->base_path.'/' : '');
685     return $base.$file;
686     }
687
688   }
689
690
691
692 class base_form_element
693   {
694   var $uppertags = FALSE;
695   var $upperattribs = FALSE;
696   var $upperprops = FALSE;
697   var $newline = FALSE;
698   
699   var $attrib = array();
700
701
702   // create string with attributes
703   function create_attrib_string($tagname='')
704     {
705     if (!sizeof($this->attrib))
706       return '';
707
708     if ($this->name!='')
709       $this->attrib['name'] = $this->name;
710
711     $attrib_arr = array();
712     foreach ($this->attrib as $key => $value)
713       {
714       // don't output some internally used attributes
715       if (in_array($key, array('form', 'quicksearch')))
716         continue;
717
718       // skip if size if not numeric
719       if (($key=='size' && !is_numeric($value)))
720         continue;
721         
722       // skip empty eventhandlers
723       if ((strpos($key,'on')===0 && $value==''))
724         continue;
725
726       // encode textarea content
727       if ($key=='value')
728         $value = rep_specialchars_output($value, 'html', 'replace', FALSE);
729
730       // attributes with no value
731       if (in_array($key, array('checked', 'multiple', 'disabled', 'selected')))
732         {
733         if ($value)
734           $attrib_arr[] = $key;
735         }
736       // don't convert size of value attribute
737       else if ($key=='value')
738         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $value, 'value');
739         
740       // regular tag attributes
741       else
742         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $this->_conv_case($value, 'value'));
743       }
744
745     return sizeof($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
746     }
747     
748     
749   // convert tags and attributes to upper-/lowercase
750   // $type can either be "tag" or "attrib"
751   function _conv_case($str, $type='attrib')
752     {
753     if ($type == 'tag')
754       return $this->uppertags ? strtoupper($str) : strtolower($str);
755     else if ($type == 'attrib')
756       return $this->upperattribs ? strtoupper($str) : strtolower($str);
757     else if ($type == 'value')
758       return $this->upperprops ? strtoupper($str) : strtolower($str);
759     }    
760   }
761
762
763 class input_field extends base_form_element
764   {
765   var $type = 'text';
766   
767   // PHP 5 constructor
768   function __construct($attrib=NULL)
769     {
770     if (is_array($attrib))
771       $this->attrib = $attrib;
772
773     if ($attrib['type'])
774       $this->type = $attrib['type'];    
775
776     if ($attrib['newline'])
777       $this->newline = TRUE;    
778     }
779
780   // PHP 4 compatibility
781   function input_field($attrib=array())
782     {
783     $this->__construct($attrib);
784     }  
785
786   // compose input tag
787   function show($value=NULL, $attrib=NULL)
788     {
789     // overwrite object attributes
790     if (is_array($attrib))
791       $this->attrib = array_merge($this->attrib, $attrib);
792
793     // set value attribute
794     if ($value!==NULL)
795       $this->attrib['value'] = $value;
796
797     $this->attrib['type'] = $this->type;
798
799     // return final tag
800     return sprintf('<%s%s />%s',
801                    $this->_conv_case('input', 'tag'),
802                    $this->create_attrib_string(),
803                    ($this->newline ? "\n" : ""));    
804     }  
805   }
806
807
808 class textfield extends input_field
809   {
810   var $type = 'text';
811   }
812
813 class passwordfield extends input_field
814   {
815   var $type = 'password';
816   }
817
818 class radiobutton extends input_field
819   {
820   var $type = 'radio';
821   }
822
823 class checkbox extends input_field
824   {
825   var $type = 'checkbox';
826
827
828   function show($value='', $attrib=NULL)
829     {
830     // overwrite object attributes
831     if (is_array($attrib))
832       $this->attrib = array_merge($this->attrib, $attrib);    
833
834     $this->attrib['type'] = $this->type;
835
836     if ($value && (string)$value==(string)$this->attrib['value'])
837       $this->attrib['checked'] = TRUE;
838     else
839       $this->attrib['checked'] = FALSE;
840
841     // return final tag
842     return sprintf('<%s%s />%s',
843                    $this->_conv_case('input', 'tag'),
844                    $this->create_attrib_string(),
845                    ($this->newline ? "\n" : ""));    
846     }
847   }
848
849
850 class textarea extends base_form_element
851   {
852   // PHP 5 constructor
853   function __construct($attrib=array())
854     {
855     $this->attrib = $attrib;
856
857     if ($attrib['newline'])
858       $this->newline = TRUE;    
859     }
860
861   // PHP 4 compatibility
862   function textarea($attrib=array())
863     {
864     $this->__construct($attrib);
865     }
866     
867   function show($value='', $attrib=NULL)
868     {
869     // overwrite object attributes
870     if (is_array($attrib))
871       $this->attrib = array_merge($this->attrib, $attrib);
872     
873     // take value attribute as content
874     if ($value=='')
875       $value = $this->attrib['value'];
876     
877     // make shure we don't print the value attribute
878     if (isset($this->attrib['value']))
879       unset($this->attrib['value']);
880
a0109c 881     if (strlen($value) && !isset($this->attrib['mce_editable']))
4e17e6 882       $value = rep_specialchars_output($value, 'html', 'replace', FALSE);
a0109c 883
4e17e6 884     // return final tag
T 885     return sprintf('<%s%s>%s</%s>%s',
886                    $this->_conv_case('textarea', 'tag'),
887                    $this->create_attrib_string(),
888                    $value,
889                    $this->_conv_case('textarea', 'tag'),
890                    ($this->newline ? "\n" : ""));       
891     }
892   }
893
894
895 class hiddenfield extends base_form_element
896   {
897   var $fields_arr = array();
898   var $newline = TRUE;
899
900   // PHP 5 constructor
901   function __construct($attrib=NULL)
902     {
903     if (is_array($attrib))
904       $this->add($attrib);
905     }
906
907   // PHP 4 compatibility
908   function hiddenfield($attrib=NULL)
909     {
910     $this->__construct($attrib);
911     }
912
913   // add a hidden field to this instance
914   function add($attrib)
915     {
916     $this->fields_arr[] = $attrib;
917     }
918
919
920   function show()
921     {
922     $out = '';
923     foreach ($this->fields_arr as $attrib)
924       {
925       $this->attrib = $attrib;
926       $this->attrib['type'] = 'hidden';
927       
928       $out .= sprintf('<%s%s />%s',
929                    $this->_conv_case('input', 'tag'),
930                    $this->create_attrib_string(),
931                    ($this->newline ? "\n" : ""));   
932       }
933
934     return $out;
935     }
936   }
937
938
939 class select extends base_form_element
940   {
941   var $options = array();
942
943   /*
944   syntax:
945   -------
946   // create instance. arguments are used to set attributes of select-tag
947   $select = new select(array('name' => 'fieldname'));
948
949   // add one option
950   $select->add('Switzerland', 'CH');
951
952   // add multiple options
953   $select->add(array('Switzerland', 'Germany'),
954                array('CH', 'DE'));
955
956   // add 10 blank options with 50 chars
957   // used to fill with javascript (necessary for 4.x browsers)
958   $select->add_blank(10, 50);
959
960   // generate pulldown with selection 'Switzerland'  and return html-code
961   // as second argument the same attributes available to instanciate can be used
962   print $select->show('CH');
963   */
964
965   // PHP 5 constructor
966   function __construct($attrib=NULL)
967     {
968     if (is_array($attrib))
969       $this->attrib = $attrib;
970
971     if ($attrib['newline'])
972       $this->newline = TRUE;
973     }
974
975   // PHP 4 compatibility
976   function select($attrib=NULL)
977     {
978     $this->__construct($attrib);
979     }
980
981
982   function add($names, $values=NULL)
983     {
984     if (is_array($names))
985       {
986       foreach ($names as $i => $text)
987         $this->options[] = array('text' => $text, 'value' => (string)$values[$i]);
988       }
989     else
990       {
991       $this->options[] = array('text' => $names, 'value' => (string)$values);
992       }
993     }
994
995     
996   function add_blank($nr, $width=0)
997     {
998     $text = $width ? str_repeat('&nbsp;', $width) : '';
999     
1000     for ($i=0; $i < $nr; $i++)
1001       $this->options[] = array('text' => $text);
1002     }
1003
1004   
1005   function show($select=array(), $attrib=NULL)
1006     {
1007     $options_str = "\n";
1008     $value_str = $this->_conv_case(' value="%s"', 'attrib');
1009     
1010     if (!is_array($select))
1011       $select = array((string)$select);
1012     
1013     foreach ($this->options as $option)
1014       {
1015       $selected = ((strlen($option['value']) && in_array($option['value'], $select, TRUE)) ||
1016                    (in_array($option['text'], $select, TRUE))) ? $this->_conv_case(' selected', 'attrib') : '';
1017                   
1018       $options_str .= sprintf("<%s%s%s>%s</%s>\n",
1019                              $this->_conv_case('option', 'tag'),
1020                              strlen($option['value']) ? sprintf($value_str, $option['value']) : '',
1021                              $selected, 
1022                              rep_specialchars_output($option['text'], 'html', 'replace', FALSE),
1023                              $this->_conv_case('option', 'tag'));
1024       }
1025                              
1026     // return final tag
1027     return sprintf('<%s%s>%s</%s>%s',
1028                    $this->_conv_case('select', 'tag'),
1029                    $this->create_attrib_string(),
1030                    $options_str,
1031                    $this->_conv_case('select', 'tag'),
1032                    ($this->newline ? "\n" : ""));    
1033     }
1034   }
1035
1036
1037
1038
1039 // ********* rcube schared functions *********
1040
1041
1042 // provide details about the client's browser
1043 function rcube_browser()
1044   {
de2e1e 1045   $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
4e17e6 1046
T 1047   $bw['ver'] = 0;
1048   $bw['win'] = stristr($HTTP_USER_AGENT, 'win');
1049   $bw['mac'] = stristr($HTTP_USER_AGENT, 'mac');
1050   $bw['linux'] = stristr($HTTP_USER_AGENT, 'linux');
1051   $bw['unix']  = stristr($HTTP_USER_AGENT, 'unix');
1052
1053   $bw['ns4'] = stristr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
1054   $bw['ns']  = ($bw['ns4'] || stristr($HTTP_USER_AGENT, 'netscape'));
1055   $bw['ie']  = stristr($HTTP_USER_AGENT, 'msie');
1056   $bw['mz']  = stristr($HTTP_USER_AGENT, 'mozilla/5');
1057   $bw['opera'] = stristr($HTTP_USER_AGENT, 'opera');
1058   $bw['safari'] = stristr($HTTP_USER_AGENT, 'safari');
1059
1060   if($bw['ns'])
1061     {
1062     $test = eregi("mozilla\/([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1063     $bw['ver'] = $test ? (float)$regs[1] : 0;
1064     }
1065   if($bw['mz'])
1066     {
1067     $test = ereg("rv:([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1068     $bw['ver'] = $test ? (float)$regs[1] : 0;
1069     }
1070   if($bw['ie'])
1071     {
1072     $test = eregi("msie ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1073     $bw['ver'] = $test ? (float)$regs[1] : 0;
1074     }
1075   if($bw['opera'])
1076     {
1077     $test = eregi("opera ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1078     $bw['ver'] = $test ? (float)$regs[1] : 0;
1079     }
1080
1081   if(eregi(" ([a-z]{2})-([a-z]{2})", $HTTP_USER_AGENT, $regs))
1082     $bw['lang'] =  $regs[1];
1083   else
1084     $bw['lang'] =  'en';
1085
1086   $bw['dom'] = ($bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5) || ($bw['opera'] && $bw['ver']>=7));
1087   $bw['pngalpha'] = $bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5.5) ||
1088                     ($bw['ie'] && $bw['ver']>=5 && $bw['mac']) || ($bw['opera'] && $bw['ver']>=7) ? TRUE : FALSE;
1089
1090   return $bw;
1091   }
1092
1093
1094 // get text in the desired language from the language file
1095 function rcube_label($attrib)
1096   {
7cc38e 1097   global $sess_user_lang, $INSTALL_PATH, $OUTPUT;
a95e0e 1098   static $sa_text_data, $s_language, $utf8_decode;
4e17e6 1099
T 1100   // extract attributes
1101   if (is_string($attrib))
1102     $attrib = array('name' => $attrib);
1103     
1104   $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
1105   $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
1106
1107   $command_name = strlen($attrib['command']) ? $attrib['command'] : NULL;
1108   $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
1109
1110
1111   // load localized texts
1112   if (!$sa_text_data || $s_language != $sess_user_lang)
1113     {
1114     $sa_text_data = array();
1115     
1116     // get english labels (these should be complete)
0af7e8 1117     @include($INSTALL_PATH.'program/localization/en_US/labels.inc');
T 1118     @include($INSTALL_PATH.'program/localization/en_US/messages.inc');
4e17e6 1119
T 1120     if (is_array($labels))
1121       $sa_text_data = $labels;
1122     if (is_array($messages))
1123       $sa_text_data = array_merge($sa_text_data, $messages);
1124     
1125     // include user language files
1126     if ($sess_user_lang!='en' && is_dir($INSTALL_PATH.'program/localization/'.$sess_user_lang))
1127       {
1128       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/labels.inc');
1129       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/messages.inc');
0af7e8 1130
4e17e6 1131       if (is_array($labels))
T 1132         $sa_text_data = array_merge($sa_text_data, $labels);
1133       if (is_array($messages))
1134         $sa_text_data = array_merge($sa_text_data, $messages);
1135       }
1136       
1137     $s_language = $sess_user_lang;
1138     }
1139
1140   // text does not exist
1141   if (!($text_item = $sa_text_data[$alias]))
1142     {
1143     /*
1144     raise_error(array('code' => 500,
1145                       'type' => 'php',
1146                       'line' => __LINE__,
1147                       'file' => __FILE__,
1148                       'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
1149     */
1150     return "[$alias]";
1151     }
1152
1153   // make text item array 
1154   $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
1155
1156   // decide which text to use
1157   if ($nr==1)
1158     $text = $a_text_item['single'];
1159   else if ($nr>0)
1160     $text = $a_text_item['multiple'];
1161   else if ($nr==0)
1162     {
1163     if ($a_text_item['none'])
1164       $text = $a_text_item['none'];
1165     else if ($a_text_item['single'])
1166       $text = $a_text_item['single'];
1167     else if ($a_text_item['multiple'])
1168       $text = $a_text_item['multiple'];
1169     }
1170
1171   // default text is single
1172   if ($text=='')
1173     $text = $a_text_item['single'];
1174
1175   // replace vars in text
1176   if (is_array($attrib['vars']))
1177     {
1178     foreach ($attrib['vars'] as $var_key=>$var_value)
1179       $a_replace_vars[substr($var_key, 0, 1)=='$' ? substr($var_key, 1) : $var_key] = $var_value;
1180     }
1181
1182   if ($a_replace_vars)
1183     $text = preg_replace('/\${?([_a-z]{1}[_a-z0-9]*)}?/ei', '$a_replace_vars["\1"]', $text);
1184
1185   // remove variables in text which were not available in arg $vars and $nr
1186   eval("\$text = <<<EOF
1187 $text
1188 EOF;
1189 ");
a95e0e 1190
4e17e6 1191   // format output
T 1192   if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
1193     return ucfirst($text);
1194   else if ($attrib['uppercase'])
1195     return strtoupper($text);
1196   else if ($attrib['lowercase'])
1197     return strtolower($text);
1198   else
1199     return $text;
1200
1201   return $text;
1202   }
1203
1204
1205 // send HTTP header for no-cacheing steps
1206 function send_nocacheing_headers()
1207   {
1208   if (headers_sent())
1209     return;
1210
1211   header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
1212   header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
1213   header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
1214   header("Pragma: no-cache");
1215   }
1216
1217
1218 // send header with expire date 30 days in future
1219 function send_future_expire_header()
1220   {
1a98a6 1221   if (headers_sent())
T 1222     return;
1223
1224   header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+2600000)." GMT");
1225   header("Cache-Control: ");
1226   header("Pragma: ");
4e17e6 1227   }
T 1228
1229
1230 // function to convert an array to a javascript array
1231 function array2js($arr, $type='')
1232   {
1233   if (!$type)
1234     $type = 'mixed';
1235
1236   if (is_array($arr))
1237     {
1238     // no items in array
1239     if (!sizeof($arr))
1240       return 'new Array()';
1241     else
1242       {
1243       $a_pairs = array();
1244       $keys_arr = array_keys($arr);
1245       $is_assoc = $have_numeric = 0;
1246
1247       for ($i=0; $i<sizeof($keys_arr); ++$i)
1248         {
1249         if(is_numeric($keys_arr[$i]))
1250           $have_numeric = 1;
1251         if (!is_numeric($keys_arr[$i]) || $keys_arr[$i]!=$i)
1252           $is_assoc = 1;
1253         if($is_assoc && $have_numeric)
1254           break;
1255         }
1256
1257       $previous_was_array = false;
1258       while (list($key, $value) = each($arr))
1259         {
1260         // enclose key with quotes if it is not variable-name conform
1261         if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */)
1262           $key = "'$key'";
1263
a0109c 1264         if (!is_array($value) && is_string($value))
4e17e6 1265           {
T 1266           $value = str_replace("\r\n", '\n', $value);
1267           $value = str_replace("\n", '\n', $value);
1268           }
1269
1270         $is_string = false;
1271         if (!is_array($value))
1272           {
1273           if ($type=='string')
1274             $is_string = true;
a0109c 1275           else if (($type == 'mixed' && is_bool($value)) || $type == 'bool')
S 1276             {
1277             $is_string = false;
1278             $value = $value ? "true" : "false";
1279             }
4e17e6 1280           else if ((($type=='mixed' && is_numeric($value)) || $type=='int') && strlen($value)<16)   // js interprets numbers with digits >15 as ...e+... 
T 1281             $is_string = FALSE;
1282           else
1283             $is_string = TRUE;
1284           }
1285
1286         if ($is_string)
1287           $value = "'".preg_replace("/(?<!\\\)'/", "\'", $value)."'";
1288
1289         $a_pairs[] = sprintf("%s%s",
1290                              $is_assoc ? "$key:" : '',
1291                              is_array($value) ? array2js($value, $type) : $value);
1292         }
1293
1294       if ($a_pairs)
1295         {
1296         if ($is_assoc)
1297           $return = '{'.implode(',', $a_pairs).'}';
1298         else
1299           $return = '['.implode(',', $a_pairs).']';
1300         }
1301
1302       return $return;
1303       }
1304     }
1305   else
a0109c 1306     {
4e17e6 1307     return $arr;
a0109c 1308     }
4e17e6 1309   }
T 1310
1311
1312 // similar function as in_array() ut case-insensitive
1313 function in_array_nocase($needle, $haystack)
1314   {
1315   foreach ($haystack as $value)
1316     {
1317     if (strtolower($needle)===strtolower($value))
1318       return TRUE;
1319     }
1320     
1321   return FALSE;
1322   }
1323
1324
1325
1326 // find out if the string content means TRUE or FALSE
1327 function get_boolean($str)
1328   {
1329   $str = strtolower($str);
1330   if(in_array($str, array('false', '0', 'no', 'nein', ''), TRUE))
1331     return FALSE;
1332   else
1333     return TRUE;
1334   }
1335
1336
3ea0e3 1337 // create a human readable string for a number of bytes
T 1338 function show_bytes($bytes)
4e17e6 1339   {
3ea0e3 1340   if ($bytes > 1073741824)
T 1341     {
1342     $gb = $bytes/1073741824;
1343     $str = sprintf($gb>=10 ? "%d GB" : "%.1f GB", $gb);
1344     }
1345   else if ($bytes > 1048576)
1346     {
1347     $mb = $bytes/1048576;
1348     $str = sprintf($mb>=10 ? "%d MB" : "%.1f MB", $mb);
1349     }
1350   else if ($bytes > 1024)
1351     $str = sprintf("%d KB",  round($bytes/1024));
4e17e6 1352   else
3ea0e3 1353     $str = sprintf('%d B', $bytes);
T 1354
1355   return $str;
4e17e6 1356   }
T 1357
1358
1359 // convert paths like ../xxx to an absolute path using a base url
1360 function make_absolute_url($path, $base_url)
1361     {
1362     $host_url = $base_url;
1363     $abs_path = $path;
1364
1365     // cut base_url to the last directory
1366     if (strpos($base_url, '/')>7)
1367       {
1368       $host_url = substr($base_url, 0, strpos($base_url, '/'));
1369       $base_url = substr($base_url, 0, strrpos($base_url, '/'));
1370       }
1371
1372     // $path is absolute
1373     if ($path{0}=='/')
1374       $abs_path = $host_url.$path;
1375     else
1376       {
1377       // strip './' because its the same as ''
1378       $path = preg_replace('/^\.\//', '', $path);
1379
1380       if(preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER))
1381         foreach($matches as $a_match)
1382           {
1383           if (strrpos($base_url, '/'))
1384             $base_url = substr($base_url, 0, strrpos($base_url, '/'));
1385           
1386           $path = substr($path, 3);
1387           }
1388
1389       $abs_path = $base_url.'/'.$path;
1390       }
1391       
1392     return $abs_path;
1393     }
1394
1395
bac7d1 1396 // replace the middle part of a string with ...
T 1397 // if it is longer than the allowed length
9fee0e 1398 function abbrevate_string($str, $maxlength, $place_holder='...')
T 1399   {
1400   $length = strlen($str);
1401   $first_part_length = floor($maxlength/2) - strlen($place_holder);
1402   
1403   if ($length > $maxlength)
1404     {
1405     $second_starting_location = $length - $maxlength + $first_part_length + 1;
1406     $str = substr($str, 0, $first_part_length) . $place_holder . substr($str, $second_starting_location, $length);
1407     }
1408
1409   return $str;
1410   }
1cded8 1411
T 1412
bac7d1 1413 // make sure the string ends with a slash
T 1414 function slashify($str)
1415   {
1416   return unslashify($str).'/';
1417   }
1418
1419
1420 // remove slash at the end of the string
1421 function unslashify($str)
1422   {
1423   return preg_replace('/\/$/', '', $str);
1424   }
1425   
1426
1cded8 1427 // delete all files within a folder
T 1428 function clear_directory($dir_path)
1429   {
1430   $dir = @opendir($dir_path);
1431   if(!$dir) return FALSE;
1432
1433   while ($file = readdir($dir))
1434     if (strlen($file)>2)
1435       unlink("$dir_path/$file");
1436
1437   closedir($dir);
1438   return TRUE;
1439   }
9fee0e 1440
T 1441
cc9570 1442 // create a unix timestamp with a specified offset from now
T 1443 function get_offset_time($offset_str, $factor=1)
1444   {
1445   if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
1446     {
1447     $amount = (int)$regs[1];
1448     $unit = strtolower($regs[2]);
1449     }
1450   else
1451     {
1452     $amount = (int)$offset_str;
1453     $unit = 's';
1454     }
1455     
1456   $ts = mktime();
1457   switch ($unit)
1458     {
1459     case 'w':
1460       $amount *= 7;
1461     case 'd':
1462       $amount *= 24;
1463     case 'h':
1464       $amount *= 60;
1465     case 'h':
1466       $amount *= 60;
1467     case 's':
1468       $ts += $amount * $factor;
1469     }
1470
1471   return $ts;
1472   }
1473
1474
a0109c 1475 /**
S 1476  * strrstr
1477  *
1478  * return the last occurence of a string in another string
1479  * @param haystack string string in which to search
1480  * @param needle string string for which to search
1481  * @return index of needle within haystack, or false if not found
1482  */
1483 function strrstr($haystack, $needle)
1484   {
1485     $pver = phpversion();
1486     if ($pver[0] >= 5)
1487       {
1488         return strrpos($haystack, $needle);
1489       }
1490     else
1491       {
1492         $index = strpos(strrev($haystack), strrev($needle));
1493         if($index === false) {
1494             return false;
1495         }
1496         $index = strlen($haystack) - strlen($needle) - $index;
1497         return $index;
1498       }
1499   }
1500
1501
f88d41 1502 ?>