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