| | |
| | | protected $js_env = array(); |
| | | protected $js_labels = array(); |
| | | protected $js_commands = array(); |
| | | protected $plugin_skin_path; |
| | | protected $skin_paths = array(); |
| | | protected $template_name; |
| | | protected $scripts_path = ''; |
| | | protected $script_files = array(); |
| | |
| | | |
| | | $this->config->set('skin_path', $skin_path); |
| | | |
| | | // register skin path(s) |
| | | $this->skin_paths = array(); |
| | | $this->load_skin($skin_path); |
| | | |
| | | return $valid; |
| | | } |
| | | |
| | | /** |
| | | * Helper method to recursively read skin meta files and register search paths |
| | | */ |
| | | private function load_skin($skin_path) |
| | | { |
| | | $this->skin_paths[] = $skin_path; |
| | | |
| | | // read meta file and check for dependecies |
| | | $meta = @json_decode(@file_get_contents($skin_path.'/meta.json'), true); |
| | | if ($meta['extends'] && is_dir('skins/' . $meta['extends'])) { |
| | | $this->load_skin('skins/' . $meta['extends']); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | public function template_exists($name) |
| | | { |
| | | $filename = $this->config->get('skin_path') . '/templates/' . $name . '.html'; |
| | | return (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name])); |
| | | $found = false; |
| | | foreach ($this->skin_paths as $skin_path) { |
| | | $filename = $skin_path . '/templates/' . $name . '.html'; |
| | | $found = (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name])); |
| | | if ($found) |
| | | break; |
| | | } |
| | | return $found; |
| | | } |
| | | |
| | | |
| | |
| | | */ |
| | | function parse($name = 'main', $exit = true, $write = true) |
| | | { |
| | | $skin_path = $this->config->get('skin_path'); |
| | | $plugin = false; |
| | | $realname = $name; |
| | | $temp = explode('.', $name, 2); |
| | | $this->template_name = $realname; |
| | | |
| | | $this->plugin_skin_path = null; |
| | | $this->template_name = $realname; |
| | | |
| | | $temp = explode('.', $name, 2); |
| | | if (count($temp) > 1) { |
| | | $plugin = $temp[0]; |
| | | $name = $temp[1]; |
| | | $skin_dir = $plugin . '/skins/' . $this->config->get('skin'); |
| | | $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir; |
| | | |
| | | // fallback to default skin |
| | | if (!is_dir($skin_path)) { |
| | | // apply skin search escalation list to plugin directory |
| | | $plugin_skin_paths = array(); |
| | | foreach ($this->skin_paths as $skin_path) { |
| | | $plugin_skin_paths[] = $this->app->plugins->dir . $plugin . '/' . $skin_path; |
| | | } |
| | | |
| | | // add fallback to default skin |
| | | if (is_dir($this->app->plugins->dir . $plugin . '/skins/default')) { |
| | | $skin_dir = $plugin . '/skins/default'; |
| | | $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir; |
| | | $plugin_skin_paths[] = $this->app->plugins->dir . $skin_dir; |
| | | } |
| | | |
| | | // add plugin skin paths to search list |
| | | $this->skin_paths = array_merge($plugin_skin_paths, $this->skin_paths); |
| | | } |
| | | |
| | | // find skin template |
| | | $path = false; |
| | | foreach ($this->skin_paths as $skin_path) { |
| | | $path = "$skin_path/templates/$name.html"; |
| | | |
| | | // fallback to deprecated template names |
| | | if (!is_readable($path) && $this->deprecated_templates[$realname]) { |
| | | $path = "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html"; |
| | | rcube::raise_error(array( |
| | | 'code' => 502, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Using deprecated template '" . $this->deprecated_templates[$realname] |
| | | . "' in $skin_path/templates. Please rename to '$realname'"), |
| | | true, false); |
| | | } |
| | | |
| | | if (is_readable($path)) { |
| | | $this->config->set('skin_path', $skin_path); |
| | | $this->base_path = $skin_path; |
| | | break; |
| | | } |
| | | else { |
| | | $path = false; |
| | | } |
| | | } |
| | | |
| | | $path = "$skin_path/templates/$name.html"; |
| | | |
| | | if (!is_readable($path) && $this->deprecated_templates[$realname]) { |
| | | $path = "$skin_path/templates/".$this->deprecated_templates[$realname].".html"; |
| | | if (is_readable($path)) |
| | | rcube::raise_error(array('code' => 502, 'type' => 'php', |
| | | 'file' => __FILE__, 'line' => __LINE__, |
| | | 'message' => "Using deprecated template '".$this->deprecated_templates[$realname] |
| | | ."' in $skin_path/templates. Please rename to '".$realname."'"), |
| | | true, false); |
| | | } |
| | | |
| | | // read template file |
| | | if (($templ = @file_get_contents($path)) === false) { |
| | | if (!$path || ($templ = @file_get_contents($path)) === false) { |
| | | rcube::raise_error(array( |
| | | 'code' => 501, |
| | | 'type' => 'php', |
| | |
| | | $output = $hook['content']; |
| | | unset($hook['content']); |
| | | |
| | | $output = $this->parse_with_globals($output); |
| | | $output = $this->parse_with_globals($this->fix_paths($output)); |
| | | |
| | | // make sure all <form> tags have a valid request token |
| | | $output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output); |
| | |
| | | public function abs_url($str) |
| | | { |
| | | if ($str[0] == '/') |
| | | return $this->config->get('skin_path') . $str; |
| | | return $this->base_path . $str; |
| | | else |
| | | return $str; |
| | | } |
| | |
| | | { |
| | | $GLOBALS['__version'] = html::quote(RCMAIL_VERSION); |
| | | $GLOBALS['__comm_path'] = html::quote($this->app->comm_path); |
| | | $GLOBALS['__skin_path'] = html::quote($this->config->get('skin_path')); |
| | | $GLOBALS['__skin_path'] = html::quote($this->base_path); |
| | | |
| | | return preg_replace_callback('/\$(__[a-z0-9_\-]+)/', |
| | | array($this, 'globals_callback'), $input); |
| | |
| | | protected function globals_callback($matches) |
| | | { |
| | | return $GLOBALS[$matches[1]]; |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Correct absolute paths in images and other tags |
| | | * add timestamp to .js and .css filename |
| | | */ |
| | | protected function fix_paths($output) |
| | | { |
| | | return preg_replace_callback( |
| | | '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', |
| | | array($this, 'file_callback'), $output); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback function for preg_replace_callback in write() |
| | | * |
| | | * @return string Parsed string |
| | | */ |
| | | protected function file_callback($matches) |
| | | { |
| | | $file = $matches[3]; |
| | | |
| | | // correct absolute paths |
| | | if ($file[0] == '/') { |
| | | $file = $this->base_path . $file; |
| | | } |
| | | |
| | | // add file modification timestamp |
| | | if (preg_match('/\.(js|css)$/', $file)) { |
| | | if ($fs = @filemtime($file)) { |
| | | $file .= '?s=' . $fs; |
| | | } |
| | | } |
| | | |
| | | return $matches[1] . '=' . $matches[2] . $file . $matches[4]; |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | // show a label |
| | | case 'label': |
| | | if ($attrib['expression']) |
| | | $attrib['name'] = eval("return " . $this->parse_expression($attrib['expression']) .";"); |
| | | |
| | | if ($attrib['name'] || $attrib['command']) { |
| | | // @FIXME: 'noshow' is useless, remove? |
| | | if ($attrib['noshow']) { |
| | |
| | | |
| | | // include a file |
| | | case 'include': |
| | | if (!$this->plugin_skin_path || !is_file($path = realpath($this->plugin_skin_path . $attrib['file']))) |
| | | $path = realpath(($attrib['skin_path'] ? $attrib['skin_path'] : $this->config->get('skin_path')).$attrib['file']); |
| | | $old_base_path = $this->base_path; |
| | | $skin_paths = $this->skin_paths; |
| | | if ($attrib['skin_path']) |
| | | array_unshift($skin_paths, $attrib['skin_path']); |
| | | foreach ($skin_paths as $skin_path) { |
| | | $path = realpath($skin_path . $attrib['file']); |
| | | if (is_file($path)) { |
| | | $this->base_path = $skin_path; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (is_readable($path)) { |
| | | if ($this->config->get('skin_include_php')) { |
| | |
| | | $incl = file_get_contents($path); |
| | | } |
| | | $incl = $this->parse_conditions($incl); |
| | | return $this->parse_xml($incl); |
| | | $incl = $this->parse_xml($incl); |
| | | $incl = $this->fix_paths($incl); |
| | | $this->base_path = $old_base_path; |
| | | return $incl; |
| | | } |
| | | break; |
| | | |
| | | case 'plugin.include': |
| | | $hook = $this->app->plugins->exec_hook("template_plugin_include", $attrib); |
| | | return $hook['content']; |
| | | break; |
| | | |
| | | // define a container block |
| | | case 'container': |
| | |
| | | $output = substr_replace($output, $css, $pos, 0); |
| | | } |
| | | |
| | | $this->base_path = $base_path; |
| | | |
| | | // correct absolute paths in images and other tags |
| | | // add timestamp to .js and .css filename |
| | | $output = preg_replace_callback( |
| | | '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', |
| | | array($this, 'file_callback'), $output); |
| | | |
| | | // trigger hook with final HTML content to be sent |
| | | $hook = $this->app->plugins->exec_hook("send_page", array('content' => $output)); |
| | | if (!$hook['abort']) { |
| | |
| | | echo $hook['content']; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * Callback function for preg_replace_callback in write() |
| | | * |
| | | * @return string Parsed string |
| | | */ |
| | | protected function file_callback($matches) |
| | | { |
| | | $file = $matches[3]; |
| | | |
| | | // correct absolute paths |
| | | if ($file[0] == '/') { |
| | | $file = $this->base_path . $file; |
| | | } |
| | | |
| | | // add file modification timestamp |
| | | if (preg_match('/\.(js|css)$/', $file)) { |
| | | if ($fs = @filemtime($file)) { |
| | | $file .= '?s=' . $fs; |
| | | } |
| | | } |
| | | |
| | | return $matches[1] . '=' . $matches[2] . $file . $matches[4]; |
| | | } |
| | | |
| | | |