| 1 | <?php |
|---|
| 2 | |
|---|
| 3 | /* |
|---|
| 4 | +-----------------------------------------------------------------------+ |
|---|
| 5 | | program/include/rcube_html_page.php | |
|---|
| 6 | | | |
|---|
| 7 | | This file is part of the RoundCube PHP suite | |
|---|
| 8 | | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland | |
|---|
| 9 | | Licensed under the GNU GPL | |
|---|
| 10 | | | |
|---|
| 11 | | CONTENTS: | |
|---|
| 12 | | Class to build XHTML page output | |
|---|
| 13 | | | |
|---|
| 14 | +-----------------------------------------------------------------------+ |
|---|
| 15 | | Author: Thomas Bruederli <roundcube@gmail.com> | |
|---|
| 16 | +-----------------------------------------------------------------------+ |
|---|
| 17 | |
|---|
| 18 | $Id$ |
|---|
| 19 | |
|---|
| 20 | */ |
|---|
| 21 | |
|---|
| 22 | /** |
|---|
| 23 | * Class for HTML page creation |
|---|
| 24 | * |
|---|
| 25 | * @package HTML |
|---|
| 26 | */ |
|---|
| 27 | class rcube_html_page |
|---|
| 28 | { |
|---|
| 29 | protected $scripts_path = ''; |
|---|
| 30 | protected $script_files = array(); |
|---|
| 31 | protected $scripts = array(); |
|---|
| 32 | protected $charset = RCMAIL_CHARSET; |
|---|
| 33 | |
|---|
| 34 | protected $script_tag_file = "<script type=\"text/javascript\" src=\"%s\"></script>\n"; |
|---|
| 35 | protected $script_tag = "<script type=\"text/javascript\">\n/* <![CDATA[ */\n%s\n/* ]]> */\n</script>"; |
|---|
| 36 | protected $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>"; |
|---|
| 37 | |
|---|
| 38 | protected $title = ''; |
|---|
| 39 | protected $header = ''; |
|---|
| 40 | protected $footer = ''; |
|---|
| 41 | protected $body = ''; |
|---|
| 42 | protected $base_path = ''; |
|---|
| 43 | |
|---|
| 44 | |
|---|
| 45 | /** Constructor */ |
|---|
| 46 | public function __construct() {} |
|---|
| 47 | |
|---|
| 48 | /** |
|---|
| 49 | * Link an external script file |
|---|
| 50 | * |
|---|
| 51 | * @param string File URL |
|---|
| 52 | * @param string Target position [head|foot] |
|---|
| 53 | */ |
|---|
| 54 | public function include_script($file, $position='head') |
|---|
| 55 | { |
|---|
| 56 | static $sa_files = array(); |
|---|
| 57 | |
|---|
| 58 | if (!preg_match('|^https?://|i', $file) && $file[0] != '/') |
|---|
| 59 | $file = $this->scripts_path . $file . (($fs = @filemtime($this->scripts_path . $file)) ? '?s='.$fs : ''); |
|---|
| 60 | |
|---|
| 61 | if (in_array($file, $sa_files)) { |
|---|
| 62 | return; |
|---|
| 63 | } |
|---|
| 64 | if (!is_array($this->script_files[$position])) { |
|---|
| 65 | $this->script_files[$position] = array(); |
|---|
| 66 | } |
|---|
| 67 | $this->script_files[$position][] = $file; |
|---|
| 68 | } |
|---|
| 69 | |
|---|
| 70 | /** |
|---|
| 71 | * Add inline javascript code |
|---|
| 72 | * |
|---|
| 73 | * @param string JS code snippet |
|---|
| 74 | * @param string Target position [head|head_top|foot] |
|---|
| 75 | */ |
|---|
| 76 | public function add_script($script, $position='head') |
|---|
| 77 | { |
|---|
| 78 | if (!isset($this->scripts[$position])) { |
|---|
| 79 | $this->scripts[$position] = "\n".rtrim($script); |
|---|
| 80 | } else { |
|---|
| 81 | $this->scripts[$position] .= "\n".rtrim($script); |
|---|
| 82 | } |
|---|
| 83 | } |
|---|
| 84 | |
|---|
| 85 | /** |
|---|
| 86 | * Add HTML code to the page header |
|---|
| 87 | */ |
|---|
| 88 | public function add_header($str) |
|---|
| 89 | { |
|---|
| 90 | $this->header .= "\n".$str; |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | /** |
|---|
| 94 | * Add HTML code to the page footer |
|---|
| 95 | * To be added right befor </body> |
|---|
| 96 | */ |
|---|
| 97 | public function add_footer($str) |
|---|
| 98 | { |
|---|
| 99 | $this->footer .= "\n".$str; |
|---|
| 100 | } |
|---|
| 101 | |
|---|
| 102 | /** |
|---|
| 103 | * Setter for page title |
|---|
| 104 | */ |
|---|
| 105 | public function set_title($t) |
|---|
| 106 | { |
|---|
| 107 | $this->title = $t; |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | /** |
|---|
| 111 | * Setter for output charset. |
|---|
| 112 | * To be specified in a meta tag and sent as http-header |
|---|
| 113 | */ |
|---|
| 114 | public function set_charset($charset) |
|---|
| 115 | { |
|---|
| 116 | $this->charset = $charset; |
|---|
| 117 | } |
|---|
| 118 | |
|---|
| 119 | /** |
|---|
| 120 | * Getter for output charset |
|---|
| 121 | */ |
|---|
| 122 | public function get_charset() |
|---|
| 123 | { |
|---|
| 124 | return $this->charset; |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | /** |
|---|
| 128 | * Reset all saved properties |
|---|
| 129 | */ |
|---|
| 130 | public function reset() |
|---|
| 131 | { |
|---|
| 132 | $this->script_files = array(); |
|---|
| 133 | $this->scripts = array(); |
|---|
| 134 | $this->title = ''; |
|---|
| 135 | $this->header = ''; |
|---|
| 136 | $this->footer = ''; |
|---|
| 137 | $this->body = ''; |
|---|
| 138 | } |
|---|
| 139 | |
|---|
| 140 | /** |
|---|
| 141 | * Process template and write to stdOut |
|---|
| 142 | * |
|---|
| 143 | * @param string HTML template |
|---|
| 144 | * @param string Base for absolute paths |
|---|
| 145 | */ |
|---|
| 146 | public function write($templ='', $base_path='') |
|---|
| 147 | { |
|---|
| 148 | $output = empty($templ) ? $this->default_template : trim($templ); |
|---|
| 149 | |
|---|
| 150 | // set default page title |
|---|
| 151 | if (empty($this->title)) { |
|---|
| 152 | $this->title = 'RoundCube Mail'; |
|---|
| 153 | } |
|---|
| 154 | |
|---|
| 155 | // replace specialchars in content |
|---|
| 156 | $__page_title = Q($this->title, 'show', FALSE); |
|---|
| 157 | $__page_header = $__page_body = $__page_footer = ''; |
|---|
| 158 | |
|---|
| 159 | // include meta tag with charset |
|---|
| 160 | if (!empty($this->charset)) { |
|---|
| 161 | if (!headers_sent()) { |
|---|
| 162 | header('Content-Type: text/html; charset=' . $this->charset); |
|---|
| 163 | } |
|---|
| 164 | $__page_header = '<meta http-equiv="content-type"'; |
|---|
| 165 | $__page_header.= ' content="text/html; charset='; |
|---|
| 166 | $__page_header.= $this->charset . '" />'."\n"; |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | // definition of the code to be placed in the document header and footer |
|---|
| 170 | if (is_array($this->script_files['head'])) { |
|---|
| 171 | foreach ($this->script_files['head'] as $file) { |
|---|
| 172 | $__page_header .= sprintf($this->script_tag_file, $file); |
|---|
| 173 | } |
|---|
| 174 | } |
|---|
| 175 | |
|---|
| 176 | $head_script = $this->scripts['head_top'] . $this->scripts['head']; |
|---|
| 177 | if (!empty($head_script)) { |
|---|
| 178 | $__page_header .= sprintf($this->script_tag, $head_script); |
|---|
| 179 | } |
|---|
| 180 | |
|---|
| 181 | if (!empty($this->header)) { |
|---|
| 182 | $__page_header .= $this->header; |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | if (is_array($this->script_files['foot'])) { |
|---|
| 186 | foreach ($this->script_files['foot'] as $file) { |
|---|
| 187 | $__page_footer .= sprintf($this->script_tag_file, $file); |
|---|
| 188 | } |
|---|
| 189 | } |
|---|
| 190 | |
|---|
| 191 | if (!empty($this->scripts['foot'])) { |
|---|
| 192 | $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']); |
|---|
| 193 | } |
|---|
| 194 | |
|---|
| 195 | if (!empty($this->footer)) { |
|---|
| 196 | $__page_footer .= $this->footer; |
|---|
| 197 | } |
|---|
| 198 | |
|---|
| 199 | // find page header |
|---|
| 200 | if ($hpos = stripos($output, '</head>')) { |
|---|
| 201 | $__page_header .= "\n"; |
|---|
| 202 | } |
|---|
| 203 | else { |
|---|
| 204 | if (!is_numeric($hpos)) { |
|---|
| 205 | $hpos = stripos($output, '<body'); |
|---|
| 206 | } |
|---|
| 207 | if (!is_numeric($hpos) && ($hpos = stripos($output, '<html'))) { |
|---|
| 208 | while ($output[$hpos] != '>') { |
|---|
| 209 | $hpos++; |
|---|
| 210 | } |
|---|
| 211 | $hpos++; |
|---|
| 212 | } |
|---|
| 213 | $__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n"; |
|---|
| 214 | } |
|---|
| 215 | |
|---|
| 216 | // add page hader |
|---|
| 217 | if ($hpos) { |
|---|
| 218 | $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output)); |
|---|
| 219 | } |
|---|
| 220 | else { |
|---|
| 221 | $output = $__page_header . $output; |
|---|
| 222 | } |
|---|
| 223 | |
|---|
| 224 | // find page body |
|---|
| 225 | if ($bpos = stripos($output, '<body')) { |
|---|
| 226 | while ($output[$bpos] != '>') { |
|---|
| 227 | $bpos++; |
|---|
| 228 | } |
|---|
| 229 | $bpos++; |
|---|
| 230 | } |
|---|
| 231 | else { |
|---|
| 232 | $bpos = stripos($output, '</head>')+7; |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | // add page body |
|---|
| 236 | if ($bpos && $__page_body) { |
|---|
| 237 | $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output)); |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | // find and add page footer |
|---|
| 241 | if (($fpos = strripos($output, '</body>')) || ($fpos = strripos($output, '</html>'))) { |
|---|
| 242 | $output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos); |
|---|
| 243 | } |
|---|
| 244 | else { |
|---|
| 245 | $output .= "\n".$__page_footer; |
|---|
| 246 | } |
|---|
| 247 | |
|---|
| 248 | // reset those global vars |
|---|
| 249 | $__page_header = $__page_footer = ''; |
|---|
| 250 | |
|---|
| 251 | $this->base_path = $base_path; |
|---|
| 252 | // correct absolute paths in images and other tags |
|---|
| 253 | // add timestamp to .js and .css filename |
|---|
| 254 | $output = preg_replace_callback('!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', |
|---|
| 255 | array($this, 'file_callback'), $output); |
|---|
| 256 | $output = str_replace('$__skin_path', $base_path, $output); |
|---|
| 257 | |
|---|
| 258 | if ($this->charset != RCMAIL_CHARSET) |
|---|
| 259 | echo rcube_charset_convert($output, RCMAIL_CHARSET, $this->charset); |
|---|
| 260 | else |
|---|
| 261 | echo $output; |
|---|
| 262 | } |
|---|
| 263 | |
|---|
| 264 | /** |
|---|
| 265 | * Callback function for preg_replace_callback in write() |
|---|
| 266 | */ |
|---|
| 267 | private function file_callback($matches) |
|---|
| 268 | { |
|---|
| 269 | $file = $matches[3]; |
|---|
| 270 | |
|---|
| 271 | // correct absolute paths |
|---|
| 272 | if ($file[0] == '/') |
|---|
| 273 | $file = $this->base_path . $file; |
|---|
| 274 | |
|---|
| 275 | // add file modification timestamp |
|---|
| 276 | if (preg_match('/\.(js|css)$/', $file)) |
|---|
| 277 | $file .= '?s=' . @filemtime($file); |
|---|
| 278 | |
|---|
| 279 | return sprintf("%s=%s%s%s", $matches[1], $matches[2], $file, $matches[4]); |
|---|
| 280 | } |
|---|
| 281 | } |
|---|
| 282 | |
|---|