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