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