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