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