source: subversion/trunk/roundcubemail/program/include/rcube_html_page.php @ 5787

Last change on this file since 5787 was 5787, checked in by thomasb, 16 months ago

Changed license to GNU GPLv3+ with exceptions for skins and plugins

  • Property svn:keywords set to Id
File size: 9.4 KB
Line 
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-2011 The Roundcube Dev Team                       |
9 |                                                                       |
10 | Licensed under the GNU General Public License version 3 or            |
11 | any later version with exceptions for skins & plugins.                |
12 | See the README file for a full license statement.                     |
13 |                                                                       |
14 | CONTENTS:                                                             |
15 |   Class to build XHTML page output                                    |
16 |                                                                       |
17 +-----------------------------------------------------------------------+
18 | Author: Thomas Bruederli <roundcube@gmail.com>                        |
19 +-----------------------------------------------------------------------+
20
21 $Id$
22
23*/
24
25/**
26 * Class for HTML page creation
27 *
28 * @package HTML
29 */
30class rcube_html_page
31{
32    protected $scripts_path = '';
33    protected $script_files = array();
34    protected $css_files = array();
35    protected $scripts = array();
36    protected $charset = RCMAIL_CHARSET;
37    protected $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
38
39    protected $title = '';
40    protected $header = '';
41    protected $footer = '';
42    protected $body = '';
43    protected $base_path = '';
44
45
46    /** Constructor */
47    public function __construct() {}
48
49    /**
50     * Link an external script file
51     *
52     * @param string File URL
53     * @param string Target position [head|foot]
54     */
55    public function include_script($file, $position='head')
56    {
57        static $sa_files = array();
58
59        if (!preg_match('|^https?://|i', $file) && $file[0] != '/') {
60            $file = $this->scripts_path . $file;
61            if ($fs = @filemtime($file)) {
62                $file .= '?s=' . $fs;
63            }
64        }
65
66        if (in_array($file, $sa_files)) {
67            return;
68        }
69
70        $sa_files[] = $file;
71
72        if (!is_array($this->script_files[$position])) {
73            $this->script_files[$position] = array();
74        }
75
76        $this->script_files[$position][] = $file;
77    }
78
79    /**
80     * Add inline javascript code
81     *
82     * @param string JS code snippet
83     * @param string Target position [head|head_top|foot]
84     */
85    public function add_script($script, $position='head')
86    {
87        if (!isset($this->scripts[$position])) {
88            $this->scripts[$position] = "\n" . rtrim($script);
89        }
90        else {
91            $this->scripts[$position] .= "\n" . rtrim($script);
92        }
93    }
94
95    /**
96     * Link an external css file
97     *
98     * @param string File URL
99     */
100    public function include_css($file)
101    {
102        $this->css_files[] = $file;
103    }
104
105    /**
106     * Add HTML code to the page header
107     *
108     * @param string $str HTML code
109     */
110    public function add_header($str)
111    {
112        $this->header .= "\n" . $str;
113    }
114
115    /**
116     * Add HTML code to the page footer
117     * To be added right befor </body>
118     *
119     * @param string $str HTML code
120     */
121    public function add_footer($str)
122    {
123        $this->footer .= "\n" . $str;
124    }
125
126    /**
127     * Setter for page title
128     *
129     * @param string $t Page title
130     */
131    public function set_title($t)
132    {
133        $this->title = $t;
134    }
135
136    /**
137     * Setter for output charset.
138     * To be specified in a meta tag and sent as http-header
139     *
140     * @param string $charset Charset
141     */
142    public function set_charset($charset)
143    {
144        $this->charset = $charset;
145    }
146
147    /**
148     * Getter for output charset
149     *
150     * @return string Output charset
151     */
152    public function get_charset()
153    {
154        return $this->charset;
155    }
156
157    /**
158     * Reset all saved properties
159     */
160    public function reset()
161    {
162        $this->script_files = array();
163        $this->scripts      = array();
164        $this->title        = '';
165        $this->header       = '';
166        $this->footer       = '';
167        $this->body         = '';
168    }
169
170    /**
171     * Process template and write to stdOut
172     *
173     * @param string HTML template
174     * @param string Base for absolute paths
175     */
176    public function write($templ='', $base_path='')
177    {
178        $output = empty($templ) ? $this->default_template : trim($templ);
179
180        // set default page title
181        if (empty($this->title)) {
182            $this->title = 'Roundcube Mail';
183        }
184
185        // replace specialchars in content
186        $page_title  = Q($this->title, 'show', FALSE);
187        $page_header = '';
188        $page_footer = '';
189
190        // include meta tag with charset
191        if (!empty($this->charset)) {
192            if (!headers_sent()) {
193                header('Content-Type: text/html; charset=' . $this->charset);
194            }
195            $page_header = '<meta http-equiv="content-type"';
196            $page_header.= ' content="text/html; charset=';
197            $page_header.= $this->charset . '" />'."\n";
198        }
199
200        // definition of the code to be placed in the document header and footer
201        if (is_array($this->script_files['head'])) {
202            foreach ($this->script_files['head'] as $file) {
203                $page_header .= html::script($file);
204            }
205        }
206
207        $head_script = $this->scripts['head_top'] . $this->scripts['head'];
208        if (!empty($head_script)) {
209            $page_header .= html::script(array(), $head_script);
210        }
211
212        if (!empty($this->header)) {
213            $page_header .= $this->header;
214        }
215
216        // put docready commands into page footer
217        if (!empty($this->scripts['docready'])) {
218            $this->add_script('$(document).ready(function(){ ' . $this->scripts['docready'] . "\n});", 'foot');
219        }
220
221        if (is_array($this->script_files['foot'])) {
222            foreach ($this->script_files['foot'] as $file) {
223                $page_footer .= html::script($file);
224            }
225        }
226
227        if (!empty($this->footer)) {
228            $page_footer .= $this->footer . "\n";
229        }
230
231        if (!empty($this->scripts['foot'])) {
232            $page_footer .= html::script(array(), $this->scripts['foot']);
233        }
234
235        // find page header
236        if ($hpos = stripos($output, '</head>')) {
237            $page_header .= "\n";
238        }
239        else {
240            if (!is_numeric($hpos)) {
241                $hpos = stripos($output, '<body');
242            }
243            if (!is_numeric($hpos) && ($hpos = stripos($output, '<html'))) {
244                while ($output[$hpos] != '>') {
245                    $hpos++;
246                }
247                $hpos++;
248            }
249            $page_header = "<head>\n<title>$page_title</title>\n$page_header\n</head>\n";
250        }
251
252        // add page hader
253        if ($hpos) {
254            $output = substr_replace($output, $page_header, $hpos, 0);
255        }
256        else {
257            $output = $page_header . $output;
258        }
259
260        // add page footer
261        if (($fpos = strripos($output, '</body>')) || ($fpos = strripos($output, '</html>'))) {
262            $output = substr_replace($output, $page_footer."\n", $fpos, 0);
263        }
264        else {
265            $output .= "\n".$page_footer;
266        }
267
268        // add css files in head, before scripts, for speed up with parallel downloads
269        if (!empty($this->css_files) && 
270            (($pos = stripos($output, '<script ')) || ($pos = stripos($output, '</head>')))
271        ) {
272            $css = '';
273            foreach ($this->css_files as $file) {
274                $css .= html::tag('link', array('rel' => 'stylesheet',
275                    'type' => 'text/css', 'href' => $file, 'nl' => true));
276            }
277            $output = substr_replace($output, $css, $pos, 0);
278        }
279
280        $this->base_path = $base_path;
281
282        // correct absolute paths in images and other tags
283        // add timestamp to .js and .css filename
284        $output = preg_replace_callback(
285            '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i',
286            array($this, 'file_callback'), $output);
287        $output = str_replace('$__skin_path', $base_path, $output);
288
289        // trigger hook with final HTML content to be sent
290        $hook = rcmail::get_instance()->plugins->exec_hook("send_page", array('content' => $output));
291        if (!$hook['abort']) {
292            if ($this->charset != RCMAIL_CHARSET) {
293                echo rcube_charset_convert($hook['content'], RCMAIL_CHARSET, $this->charset);
294            }
295            else {
296                echo $hook['content'];
297            }
298        }
299    }
300
301    /**
302     * Callback function for preg_replace_callback in write()
303     *
304     * @return string Parsed string
305     */
306    private function file_callback($matches)
307    {
308            $file = $matches[3];
309
310        // correct absolute paths
311            if ($file[0] == '/') {
312                $file = $this->base_path . $file;
313        }
314
315        // add file modification timestamp
316            if (preg_match('/\.(js|css)$/', $file)) {
317            if ($fs = @filemtime($file)) {
318                $file .= '?s=' . $fs;
319            }
320        }
321
322            return $matches[1] . '=' . $matches[2] . $file . $matches[4];
323    }
324}
Note: See TracBrowser for help on using the repository browser.