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