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