/interfase/template.php
PHP | 1479 lines | 1053 code | 160 blank | 266 comment | 189 complexity | a2db2cedd7212c254333ba8a4f8e3429 MD5 | raw file
Possible License(s): MIT
- <?php
- /*
- <Orion, a web development framework for RK.>
- Copyright (C) <2011> <Orion>
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- // Cache filenames prefix
- define('XS_TPL_PREFIX', 'tpl_');
- define('XS_TPL_PREFIX2', 'tpl2_');
- // Templates directory
- define('XS_TPL_START', 'template/');
- define('XS_TPL_ANY', '/template/');
- // Internal xs mod definitions. do not edit.
- define('XS_TAG_NONE', 0);
- define('XS_TAG_PHP', 1);
- define('XS_TAG_BEGIN', 2);
- define('XS_TAG_END', 3);
- define('XS_TAG_INCLUDE', 4);
- define('XS_TAG_IF', 5);
- define('XS_TAG_ELSE', 6);
- define('XS_TAG_ELSEIF', 7);
- define('XS_TAG_ENDIF', 8);
- define('XS_TAG_DEFINE', 9);
- define('XS_TAG_UNDEFINE', 10);
- define('XS_TAG_BEGINELSE', 11);
- class Template {
- // variable that holds all the data we'll be substituting into
- // the compiled templates.
- // ...
- // This will end up being a multi-dimensional array like this:
- // $this->_tpldata[block.][iteration#][child.][iteration#][child2.][iteration#][variablename] == value
- // if it's a root-level variable, it'll be like this:
- // $this->vars[varname] == value or $this->_tpldata['.'][0][varname] == value
- // array "vars" is added for easier access to data
- public $_tpldata = array('.' => array(0 => array()));
- public $vars;
- // Hash of filenames for each template handle.
- public $files = array();
- public $files_cache = array(); // array of cache files that exists
- public $files_cache2 = array(); // array of cache files (exists or not exists)
- // Root template directory.
- public $root = '';
- // Cache directory (compatible with default cache mod)
- public $cachedir = '';
- // Search/replace for unknown files
- public $cache_search = array();
- public $cache_replace = array();
- // Template root directory (generated by set_rootdir)
- public $tpldir = '';
- public $tpldir_len = 0;
- // Default template directory.
- // If file for default template isn't found file from this template is used.
- public $tpldef = '';
- // this will hash handle names to the compiled code for that handle.
- public $compiled_code = array();
- // This will hold the uncompiled code for that handle.
- public $uncompiled_code = array();
- // Cache settings
- public $use_cache = 1;
- public $cache_writable = 1;
- // Auto-compile setting
- public $auto_compile = 1;
- public $auto_recompile = 1;
- public $warn_includes = 1;
- // Current template name
- public $tpl = '';
- // List of replacements. tpl files in this list will be replaced with other tpl files
- // according to configuration in xs.cfg
- public $replace = array();
- // counter for include
- public $include_count = 0;
- // True if check switches
- public $xs_check_switches = 1;
- // eXtreme Styles variables
- public $xs_started = 0;
-
- // These handles will be parsed if pparse() is executed.
- // Can be used to automatically include header/footer if there is any content.
- public $preparse = '';
- public $postparse = '';
- // subtemplates mod detection
- public $subtemplates = false;
- public $replace_vars = true;
- // style configuration
- public $style_config = array();
- // list of switches that are known typos in some mods.
- // when error checking is enabled these errors will be auto-fixed.
- // format:
- // array(start_tag, end_tag)
- public $bugs = array(
- // ezportal typo:
- array('fetchpost_row', 'fetch_post_row'),
- // mycalendar 2.2.7 typos:
- array('date_cell', 'date_cells'),
- array('date_row', 'date_rows'),
- // history mod typo:
- array('site_today', 'site_week'),
- );
- /**
- * Constructor.
- */
- public function __contructor() {
- return;
- }
-
- /**
- * Sets the root dir.
- */
- public function set_template($root = '.') {
- global $config;
-
- $this->vars = &$this->_tpldata['.'][0];
-
- $this->use_cache = $config->xs_use_cache;
- $this->cache_search = array('.', '\\', '/', '_tpl');
- $this->cache_replace = array('_', '.', '.', '.php');
-
- $old_root = $this->root;
-
- $root = str_replace('\\', '/', $root);
- // $this->cachedir = ROOT . 'cache/';
- $this->cachedir = $config->cache_path;
- $this->tpldir = ROOT . 'template/';
- $this->tpldir_len = strlen($this->tpldir);
- $this->root = $root;
- $this->tpl = $this->template_name($root);
-
- // Check configuration
- $this->get_config();
-
- if ($old_root !== $this->root) {
- $this->files = array();
- $this->files_cache = array();
- $this->files_cache2 = array();
- $this->compiled_code = array();
- $this->uncompiled_code = array();
- }
-
- return;
- }
- /**
- * Destroys this template object. Should be called when you're done with it, in order
- * to clear out the template data so you can load/parse a new template set.
- */
- public function destroy() {
- $this->_tpldata = array('.' => array(0 => array()));
- $this->vars = &$this->_tpldata['.'][0];
- $this->xs_started = 0;
- }
- /**
- * Extracts template name from path
- */
- public function template_name($dir) {
- $tpl = XS_TPL_ANY; // can start at any position
- $tpl_null = XS_TPL_START; // can start only at zero position
-
- // searching for 'templates/' and removing everything before it
- $pos = strpos($dir, $tpl);
- if ($pos === false) {
- if(substr($dir, 0, strlen($tpl_null)) !== $tpl_null) {
- return '';
- }
- $str = substr($dir, strlen($tpl_null), strlen($dir));
- } else {
- $str = substr($dir, $pos + strlen($tpl), strlen($dir));
- }
- // searching for one more 'templates/'
- $dir = $this->template_name($str);
- if (!$dir) {
- $dir = $str;
- }
-
- if(strpos($str, $tpl) !== false) {
- $dir = $this->template_name($str);
- }
-
- // Check for another subdirectory
- $pos = strpos($dir, '/');
- if ($pos) {
- $dir = substr($dir, 0, $pos);
- }
- return $dir;
- }
- /**
- * Generates a full path+filename for the given filename, which can either
- * be an absolute name, or a name relative to the rootdir for this Template
- * object.
- */
- public function make_filename($filename, $xs_include = false) {
- // Check replacements list
- if (!$xs_include && isset($this->replace[$filename])) {
- $filename = $this->replace[$filename];
- }
-
- // Check if it's an absolute or relative path.
- if ((substr($filename, 0, 1) !== '/') && (substr($filename, 1, 1) !== ':')) {
- return $this->root . '/' . $filename;
- } else {
- return str_replace('\\', '/', $filename);
- }
- }
- /**
- * Converts template filename to cache filename.
- * Returns empty string if non-cachable (for tpl files outside of root dir).
- * $file should be absolute filename
- */
- public function make_filename_cache($file) {
- $str = str_replace($this->cache_search, $this->cache_replace, $file);
- if(substr($file, 0, $this->tpldir_len) !== $this->tpldir || empty($this->tpl)) {
- return $this->cachedir . XS_TPL_PREFIX2 . $str;
- }
-
- // removing not needed part
- $file = substr($file, $this->tpldir_len, strlen($file));
-
- // creating filename
- return $this->cachedir . XS_TPL_PREFIX . str_replace($this->cache_search, $this->cache_replace, $file);
- }
- /**
- * Sets the template filenames for handles. $filename_array
- * should be a hash of handle => filename pairs.
- */
- public function set_filenames($filename_array) {
- if (!is_array($filename_array)) {
- return false;
- }
- foreach($filename_array as $handle => $filename) {
- $this->set_filename($handle, $filename);
- }
- return true;
- }
- /**
- * Assigns template filename for handle.
- */
- public function set_filename($handle, $filename, $xs_include = false, $quiet = false) {
- global $config;
-
- $can_cache = $this->use_cache;
- if(strpos($filename, '..') !== false) {
- $can_cache = false;
- }
-
- $this->files[$handle] = $this->make_filename($filename, $xs_include);
- $this->files_cache[$handle] = '';
- $this->files_cache2[$handle] = '';
-
- // check if we are in admin control panel and override extreme styles mod controls if needed
- if(defined('XS_ADMIN_OVERRIDE') && XS_ADMIN_OVERRIDE === true && @function_exists('xs_admin_override')) {
- xs_admin_override();
- }
- // checking if we have valid filename
- if (!$this->files[$handle]) {
- if($xs_include || $quiet) {
- return false;
- } else {
- _pre("Template->make_filename(): Error - invalid template $filename", true);
- }
- }
- // creating cache filename
- if ($can_cache) {
- $this->files_cache2[$handle] = $this->make_filename_cache($this->files[$handle]);
- if(@file_exists($this->files_cache2[$handle])) {
- $this->files_cache[$handle] = $this->files_cache2[$handle];
- }
- }
-
- // checking if tpl and/or php file exists
- if (empty($this->files_cache[$handle]) && !@file_exists($this->files[$handle])) {
- // trying to load alternative filename (usually subSilver)
- if (!empty($this->tpldef) && !empty($this->tpl) && ($this->tpldef !== $this->tpl)) {
- $this->files[$handle] = '';
- // save old configuration
- $root = $this->root;
- $tpl_name = $this->tpl;
- // set temporary configuration
- $this->root = $this->tpldir . $this->tpldef;
- $this->tpl = $this->tpldef;
- // recursively run set_filename
- $res = $this->set_filename($handle, $filename, $xs_include, $quiet);
- // restore old configuration
- $this->root = $root;
- $this->tpl = $tpl_name;
- return $res;
- }
-
- if ($quiet) {
- return false;
- }
-
- if ($xs_include) {
- if ($this->warn_includes) {
- _pre('Template->make_filename(): Error - included template file not found: ' . $filename, true);
- }
- return false;
- } else {
- _pre('Template->make_filename(): Error - template file not found: ' . $this->files[$handle], true);
- }
- }
- // checking if we should recompile cache
- if (!empty($this->files_cache[$handle]) && $this->auto_recompile) {
- $cache_time = @filemtime($this->files_cache[$handle]);
- if(@filemtime($this->files[$handle]) > $cache_time || $config->xs_template_time > $cache_time) {
- // file was changed. don't use cache file (will be recompled if configuration allowes it)
- $this->files_cache[$handle] = '';
- }
- }
- return true;
- }
- /**
- * includes file or executes code
- */
- public function execute($filename, $code, $handle = false) {
- global $theme, $config;
-
- $template = $theme['template_name'];
- global $$template;
- $theme_info = &$$template;
-
- if ($filename) {
- require_once($filename);
- } else {
- eval($code);
- }
- return true;
- }
- /**
- * Load the file for the handle, compile the file,
- * and run the compiled code. This will print out
- * the results of executing the template.
- */
- public function pparse($handle) {
- global $config;
-
- // Parsing header if there is one
- if ($this->preparse || $this->postparse) {
- $preparse = $this->preparse;
- $postparse = $this->postparse;
- $this->preparse = '';
- $this->postparse = '';
-
- if ($preparse) {
- $this->pparse($preparse);
- }
-
- if ($postparse) {
- $str = $handle;
- $handle = $postparse;
- $this->pparse($str);
- }
- }
-
- // checking if handle exists
- if (empty($this->files[$handle]) && empty($this->files_cache[$handle])) {
- _pre("Template->loadfile(): No files found for handle $handle", true);
- }
-
- $this->xs_startup();
-
- $force_recompile = empty($this->uncompiled_code[$handle]) ? false : true;
-
- // Checking if php file exists.
- if (!empty($this->files_cache[$handle]) && !$force_recompile) {
- $this->execute($this->files_cache[$handle], '');
- return true;
- }
-
- if (!$this->loadfile($handle)) {
- _pre("Template->pparse(): Couldn't load template file for handle $handle", true);
- }
-
- // Actually compile the template now.
- if (empty($this->compiled_code[$handle])) {
- // Actually compile the code now.
- if (!empty($this->files_cache2[$handle]) && empty($this->files_cache[$handle]) && !$force_recompile) {
- $this->compiled_code[$handle] = $this->compile2($this->uncompiled_code[$handle], $handle, $this->files_cache2[$handle]);
- } else {
- $this->compiled_code[$handle] = $this->compile2($this->uncompiled_code[$handle], '', '');
- }
- }
-
- // Run the compiled code.
- if (empty($this->files_cache[$handle]) || $force_recompile) {
- $this->execute('', $this->compiled_code[$handle]);
- } else {
- $this->execute($this->files_cache[$handle], '');
- }
- return true;
- }
- /**
- * Precompile file
- */
- public function precompile($template, $filename) {
- global $precompile_num, $config;
-
- if (empty($precompile_num)) {
- $precompile_num = 0;
- }
-
- $precompile_num ++;
- $handle = 'precompile_' . $precompile_num;
- // save old configuration
- $root = $this->root;
- $tpl_name = $this->tpl;
- $old_config = $this->use_cache;
- $old_autosave = $this->auto_compile;
- // set temporary configuration
- $this->root = $this->tpldir . $template;
- $this->tpl = $template;
- $this->use_cache = 1;
- $this->auto_compile = 1;
- // set filename
- $res = $this->set_filename($handle, $filename, true, true);
-
- if (!$res || !$this->files_cache2[$handle]) {
- $this->root = $root;
- $this->tpl = $tpl_name;
- $this->use_cache = $old_config;
- $this->auto_compile = $old_autosave;
- return false;
- }
-
- $this->files_cache[$handle] = '';
- // load template
- $res = $this->loadfile($handle);
- if(!$res || empty($this->uncompiled_code[$handle])) {
- $this->root = $root;
- $this->tpl = $tpl_name;
- $this->use_cache = $old_config;
- $this->auto_compile = $old_autosave;
- return false;
- }
-
- // Compile the code
- $this->compile2($this->uncompiled_code[$handle], $handle, $this->files_cache2[$handle]);
- // Restore confirugation
- $this->root = $root;
- $this->tpl = $tpl_name;
- $this->use_cache = $old_config;
- $this->auto_compile = $old_autosave;
- return true;
- }
- /**
- * Inserts the uncompiled code for $handle as the
- * value of $varname in the root-level. This can be used
- * to effectively include a template in the middle of another
- * template.
- * Note that all desired assignments to the variables in $handle should be done
- * BEFORE calling this function.
- */
- public function assign_var_from_handle($varname, $handle) {
- ob_start();
- $res = $this->pparse($handle);
- $this->vars[$varname] = ob_get_contents();
- ob_end_clean();
- return $res;
- }
- /**
- * Block-level variable assignment. Adds a new block iteration with the given
- * variable assignments. Note that this should only be called once per block
- * iteration.
- */
- public function assign_block_vars($blockname, $vararray) {
- if (strpos($blockname, '.')) {
- // Nested block.
- $blocks = explode('.', $blockname);
- $blockcount = count($blocks) - 1;
- $str = &$this->_tpldata;
- for ($i = 0; $i < $blockcount; $i++)
- {
- $str = &$str[$blocks[$i].'.'];
- $str = &$str[count($str)-1];
- }
- // Now we add the block that we're actually assigning to.
- // We're adding a new iteration to this block with the given
- // variable assignments.
- $str[$blocks[$blockcount].'.'][] = $vararray;
- } else {
- // Top-level block.
- // Add a new iteration to this block with the variable assignments
- // we were given.
- $this->_tpldata[$blockname.'.'][] = $vararray;
- }
- return true;
- }
- /**
- * Root-level variable assignment. Adds to current assignments, overriding
- * any existing variable assignment with the same name.
- */
- public function assign_vars($vararray) {
- foreach ($vararray as $key => $val) {
- $this->vars[$key] = $val;
- }
- return true;
- }
- /**
- * If not already done, load the file for the given handle and populate
- * the uncompiled_code[] hash with its code. Do not compile.
- */
- public function loadfile($handle) {
- global $config;
-
- if (!empty($this->files_cache[$handle]) || !empty($this->uncompiled_code[$handle])) {
- return true;
- }
- // If we don't have a file assigned to this handle, die.
- if (empty($this->files[$handle])) {
- _pre("Template->loadfile(): No file specified for handle $handle", true);
- }
- $str = implode('', @file($this->files[$handle]));
- if (empty($str)) {
- _pre("Template->loadfile(): File " . $this->files[$handle] . " for handle $handle is empty", true);
- }
- $this->uncompiled_code[$handle] = $str;
- return true;
- }
- /**
- * Generates a reference to the given variable inside the given (possibly nested)
- * block namespace. This is a string of the form:
- * ' . $this->_tpldata['parent.'][$_parent_i]['$child1.'][$_child1_i]['$child2.'][$_child2_i]...['varname'] . '
- * It's ready to be inserted into an "echo" line in one of the templates.
- * NOTE: expects a trailing "." on the namespace.
- */
- public function generate_block_varref($namespace, $varname) {
- // Strip the trailing period.
- $namespace = substr($namespace, 0, strlen($namespace) - 1);
- // Get a reference to the data block for this namespace.
- $varref = $this->generate_block_data_ref($namespace, true);
- // Prepend the necessary code to stick this in an echo line.
- // Append the variable reference.
- $varref .= '[\'' . $varname . '\']';
- $varref = '<'.'?php echo isset(' . $varref . ') ? ' . $varref . ' : \'\'; ?'.'>';
- return $varref;
- }
- /**
- * Generates a reference to the array of data values for the given
- * (possibly nested) block namespace. This is a string of the form:
- * $this->_tpldata['parent.'][$_parent_i]['$child1.'][$_child1_i]['$child2.'][$_child2_i]...['$childN.']
- *
- * If $include_last_iterator is true, then [$_childN_i] will be appended to the form shown above.
- * NOTE: does not expect a trailing "." on the blockname.
- */
- public function generate_block_data_ref($blockname, $include_last_iterator, $defop = false) {
- // Get an array of the blocks involved.
- $blocks = explode('.', $blockname);
- $blockcount = count($blocks) - 1;
-
- if ($defop) {
- $varref = '$this->_tpldata[\'DEFINE\']';
- // Build up the string with everything but the last child.
- for ($i = 0; $i < $blockcount; $i++) {
- $varref .= "['" . $blocks[$i] . ".'][\$" . $blocks[$i] . '_i]';
- }
- // Add the block reference for the last child.
- $varref .= "['" . $blocks[$blockcount] . ".']";
- // Add the iterator for the last child if requried.
- if ($include_last_iterator) {
- $varref .= '[$' . $blocks[$blockcount] . '_i]';
- }
- return $varref;
- }
-
- if ($include_last_iterator) {
- return '$'. $blocks[$blockcount]. '_item';
- } else {
- return '$'. $blocks[$blockcount-1]. '_item[\''. $blocks[$blockcount]. '.\']';
- }
- }
- public function compile_code($filename, $code) {
- // $filename - file to load code from. used if $code is empty
- // $code - tpl code
- // $use_isset - if false then compiled code looks more beautiful and easier
- // to understand and it adds error_reporting() to supress php warnings.
- // if true then isset() is used to check variables instead of supressing
- // php warnings. note: for extreme styles mod 2.x it works only for
- // block variables and for usual variables its always true.
- // Load code from file
- if (!$code && !empty($filename)) {
- $code = @implode('', @file($filename));
- }
- // Break it up into lines and put " -->" back.
- $code_lines = explode(' -->', $code);
- $count = count($code_lines);
- for ($i = 0; $i < ($count - 1); $i++) {
- $code_lines[$i] .= ' -->';
- }
- $block_nesting_level = 0;
- $block_names = array();
- $block_names[0] = '.';
- $block_items = array();
- $count_if = 0;
- // prepare array for compiled code
- $compiled = array();
- $count_bugs = count($this->bugs);
- // array of switches
- $sw = array();
-
- // main loop
- $line_count = count($code_lines);
-
- for ($i = 0; $i < $line_count; $i++) {
- $line = $code_lines[$i];
-
- // Reset keyword type
- $keyword_type = XS_TAG_NONE;
-
- // Check if we have valid keyword in current line
- $pos1 = strpos($line, '<!-- ');
- if ($pos1 === false) {
- // no keywords in this line
- $compiled[] = $this->_compile_text($line);
- continue;
- }
-
- // Find end of html comment
- $pos2 = strpos($line, ' -->', $pos1);
- if ($pos2 !== false) {
- // find end of keyword in comment
- $pos3 = strpos($line, ' ', $pos1 + 5);
- if ($pos3 !== false && $pos3 <= $pos2) {
- $keyword = substr($line, $pos1 + 5, $pos3 - $pos1 - 5);
-
- // Check keyword against list of supported keywords. case-sensitive
- if($keyword === 'BEGIN') {
- $keyword_type = XS_TAG_BEGIN;
- } elseif($keyword === 'END') {
- $keyword_type = XS_TAG_END;
- } elseif($keyword === 'INCLUDE') {
- $keyword_type = XS_TAG_INCLUDE;
- } elseif($keyword === 'IF') {
- $keyword_type = XS_TAG_IF;
- } elseif($keyword === 'ELSE') {
- $keyword_type = XS_TAG_ELSE;
- } elseif($keyword === 'ELSEIF') {
- $keyword_type = XS_TAG_ELSEIF;
- } elseif($keyword === 'ENDIF') {
- $keyword_type = XS_TAG_ENDIF;
- } elseif($keyword === 'DEFINE') {
- $keyword_type = XS_TAG_DEFINE;
- } elseif($keyword === 'UNDEFINE') {
- $keyword_type = XS_TAG_UNDEFINE;
- } elseif($keyword === 'BEGINELSE') {
- $keyword_type = XS_TAG_BEGINELSE;
- }
- }
- }
-
- if (!$keyword_type) {
- // Not valid keyword. process the rest of line
- $compiled[] = $this->_compile_text(substr($line, 0, $pos1 + 4));
- $code_lines[$i] = substr($line, $pos1 + 4);
- $i --;
- continue;
- }
-
- // Remove code before keyword
- if ($pos1 > 0) {
- $compiled[] = $this->_compile_text(substr($line, 0, $pos1));
- }
-
- // Remove keyword
- $keyword_str = substr($line, $pos1, $pos2 - $pos1 + 4);
- $params_str = $pos2 == $pos3 ? '' : substr($line, $pos3 + 1, $pos2 - $pos3 - 1);
- $code_lines[$i] = substr($line, $pos2 + 4);
- $i--;
-
- //
- // Check keywords
- //
- /*
- * <!-- BEGIN -->
- */
- if ($keyword_type == XS_TAG_BEGIN) {
- $params = explode(' ', $params_str);
- $num_params = count($params);
-
- // get variable name
- if ($num_params == 1) {
- $var = $params[0];
- } elseif($num_params == 2) {
- if ($params[0] === '') {
- $var = $params[1];
- } elseif($params[1] === '') {
- $var = $params[0];
- } else {
- // invalid tag
- $compiled[] = $keyword_str;
- continue;
- }
- } else {
- // invalid tag
- $compiled[] = $keyword_str;
- continue;
- }
- // check variable for matching end
- if ($this->xs_check_switches) {
- $found = 0;
- $str = '<!-- END ' . $var . ' -->';
- for ($j = $i + 1; ($j < $line_count) && !$found; $j++) {
- $pos = strpos($code_lines[$j], $str);
- if($pos !== false) {
- $found = 1;
- $found_var = $var;
- }
- }
- if (!$found) {
- $compiled[] = $keyword_str;
- continue;
- }
- // adding to list of switches
- if (isset($sw[$found_var])) {
- $sw[$found_var]++;
- } else {
- $sw[$found_var] = 1;
- }
- }
-
- // adding code
- $block_nesting_level++;
- $block_names[$block_nesting_level] = $var;
- if (isset($block_items[$var])) {
- $block_items[$var]++;
- } else {
- $block_items[$var] = 1;
- }
-
- if ($block_nesting_level < 2) {
- // Block is not nested.
- $line = '<'."?php ";
- $line .= '$'. $var. '_count = ( isset($this->_tpldata[\''. $var. '.\']) ) ? count($this->_tpldata[\''. $var. '.\']) : 0;';
- $line .= ' for ($'. $var. '_i = 0; $'. $var. '_i < $'. $var. '_count; $'. $var. '_i++)';
- $line .= '{';
- $line .= ' $'. $var. '_item = &$this->_tpldata[\''. $var. '.\'][$'. $var. '_i];';
- $line .= " \${$var}_item['S_ROW_COUNT'] = \${$var}_i;";
- $line .= " \${$var}_item['S_NUM_ROWS'] = \${$var}_count;";
- $line .= " ?".">";
- } else {
- // This block is nested.
- // Generate a namespace string for this block.
- $namespace = implode('.', $block_names);
- // strip leading period from root level..
- $namespace = substr($namespace, 2);
- // Get a reference to the data array for this block that depends on the
- // current indices of all parent blocks.
- $varref = $this->generate_block_data_ref($namespace, false);
- // Create the for loop code to iterate over this block.
- $line = '<'."?php ";
- $line .= '$'. $var. '_count = ( isset('. $varref. ') ) ? count('. $varref. ') : 0;';
- $line .= ' for ($'. $var. '_i = 0; $'. $var. '_i < $'. $var. '_count; $'. $var. '_i++)';
- $line .= '{';
- $line .= ' $'. $var. '_item = &'. $varref. '[$'. $var. '_i];';
- $line .= " \${$var}_item['S_ROW_COUNT'] = \${$var}_i;";
- $line .= " \${$var}_item['S_NUM_ROWS'] = \${$var}_count;";
- $line .= " ?".">";
- }
- $compiled[] = $line;
- continue;
- }
- /*
- * <!-- END -->
- */
- if ($keyword_type == XS_TAG_END) {
- $params = explode(' ', $params_str);
- $num_params = count($params);
- if ($num_params == 1) {
- $var = $params[0];
- } elseif($num_params == 2 && $params[0] === '') {
- $var = $params[1];
- } elseif($num_params == 2 && $params[1] === '') {
- $var = $params[0];
- } else {
- $compiled[] = $keyword_str;
- continue;
- }
-
- if ($this->xs_check_switches) {
- // checking if this switch was opened
- if (!isset($sw[$var]) || ($sw[$var] < 1)) {
- // there is no opening switch
- $compiled[] = $keyword_str;
- continue;
- }
- $sw[$var] --;
- }
-
- // We have the end of a block.
- $line = '<'."?php".' } if(isset($' . $var . '_item)) { unset($' . $var . '_item); } '."?".">";
- if (isset($block_items[$var])) {
- $block_items[$var] --;
- } else {
- $block_items[$var] = -1;
- }
- unset($block_names[$block_nesting_level]);
- $block_nesting_level--;
- $compiled[] = $line;
- continue;
- }
- /*
- * <!-- BEGINELSE -->
- */
- if ($keyword_type == XS_TAG_BEGINELSE) {
- if ($block_nesting_level) {
- $var = $block_names[$block_nesting_level];
- $compiled[] = '<'.'?php } if(!$' . $var . '_count) { ?'.'>';
- } else {
- $compiled[] = $keyword_str;
- continue;
- }
- }
- /*
- * <!-- INCLUDE -->
- */
- if ($keyword_type == XS_TAG_INCLUDE) {
- $params = explode(' ', $params_str);
- if (count($params) != 1) {
- $compiled[] = $keyword_str;
- continue;
- }
- $filehash = md5($params_str . $this->include_count . time());
- $this->include_count++;
- $line = '<'.'?php $this->set_filename(\'xs_include_' . $filehash . '\', \'' . $params_str .'\', true); $this->pparse(\'xs_include_' . $filehash . '\'); ?'.'>';
- $compiled[] = $line;
- continue;
- }
- /*
- * <!-- IF -->
- */
- if ($keyword_type == XS_TAG_IF || $keyword_type == XS_TAG_ELSEIF) {
- if (!$count_if) {
- $keyword_type = XS_TAG_IF;
- }
- $str = $this->compile_tag_if($params_str, $keyword_type == XS_TAG_IF ? false : true);
- if ($str) {
- $compiled[] = '<?php ' . $str . ' ?>';
- if ($keyword_type == XS_TAG_IF) {
- $count_if++;
- }
- } else {
- $compiled[] = $keyword_str;
- }
- continue;
- }
-
- /*
- * <!-- ELSE -->
- */
- if ($keyword_type == XS_TAG_ELSE && $count_if > 0) {
- $compiled[] = '<?php } else { ?>';
- continue;
- }
- /*
- * <!-- ENDIF -->
- */
- if ($keyword_type == XS_TAG_ENDIF && $count_if > 0) {
- $compiled[] = '<?php } ?>';
- $count_if --;
- continue;
- }
- /*
- * <!-- DEFINE -->
- */
- if ($keyword_type == XS_TAG_DEFINE) {
- $str = $this->compile_tag_define($params_str);
- if ($str) {
- $compiled[] = '<?php ' . $str . ' ?>';
- } else {
- $compiled[] = $keyword_str;
- }
- }
- /*
- * <!-- UNDEFINE -->
- */
- if ($keyword_type == XS_TAG_UNDEFINE) {
- $str = $this->compile_tag_undefine($params_str);
- if ($str) {
- $compiled[] = '<?php ' . $str . ' ?>';
- } else {
- $compiled[] = $keyword_str;
- }
- }
- }
-
- // bring it back into a single string.
- $code_header = '';
- $code_footer = '';
- return $code_header . implode('', $compiled) . $code_footer;
- }
- /*
- * Compile code between tags
- */
- public function _compile_text($code) {
- if (strlen($code) < 3) {
- return $code;
- }
- // change template varrefs into PHP varrefs
- // This one will handle varrefs WITH namespaces
- $varrefs = array();
- preg_match_all('#\{(([a-z0-9\-_]+?\.)+?)([a-z0-9\-_]+?)\}#is', $code, $varrefs);
-
- $varcount = count($varrefs[1]);
- $search = array();
- $replace = array();
- for ($i = 0; $i < $varcount; $i++) {
- $namespace = $varrefs[1][$i];
- $varname = $varrefs[3][$i];
- $new = $this->generate_block_varref($namespace, $varname);
- $search[] = $varrefs[0][$i];
- $replace[] = $new;
- }
-
- if (count($search) > 0) {
- $code = str_replace($search, $replace, $code);
- }
-
- if (isset($this->files['body']) && strpos($this->files['body'], '.js') !== false) {
- $this->replace_vars = false;
- }
-
- // This will handle the remaining root-level varrefs
- if ($this->replace_vars) {
- $code = preg_replace('#\{([a-z0-9\-_]*?)\}#is', '<'.'?php echo isset($this->vars[\'\1\']) ? $this->vars[\'\1\'] : $this->lang(\'\1\'); ?'.'>', $code);
- $code = preg_replace('#\{\$([a-z0-9\-_]*?)\}#is', '<'.'?php echo isset($this->_tpldata[\'DEFINE\'][\'.\'][\'\\1\']) ? $this->_tpldata[\'DEFINE\'][\'.\'][\'\\1\'] : \'\'; ?'.'>', $code);
- }
- return $code;
- }
- //
- // Compile IF tags - much of this is from Smarty with
- // some adaptions for our block level methods
- //
- public function compile_tag_if($tag_args, $elseif) {
- /* Tokenize args for 'if' tag. */
- preg_match_all('/(?:
- "[^"\\\\]*(?:\\\\.[^"\\\\]*)*" |
- \'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\' |
- [(),] |
- [^\s(),]+)/x', $tag_args, $match);
-
- $tokens = $match[0];
- $is_arg_stack = array();
-
- for ($i = 0; $i < count($tokens); $i++) {
- $token = &$tokens[$i];
- switch ($token) {
- case '!':
- case '%':
- case '!==':
- case '==':
- case '===':
- case '>':
- case '<':
- case '!=':
- case '<>':
- case '<<':
- case '>>':
- case '<=':
- case '>=':
- case '&&':
- case '||':
- case '|':
- case '^':
- case '&':
- case '~':
- case ')':
- case ',':
- case '+':
- case '-':
- case '*':
- case '/':
- case '@':
- break;
- case '(':
- array_push($is_arg_stack, $i);
- break;
- case 'is':
- $is_arg_start = ($tokens[$i-1] == ')') ? array_pop($is_arg_stack) : $i-1;
- $is_arg = implode(' ', array_slice($tokens, $is_arg_start, $i - $is_arg_start));
- $new_tokens = $this->_parse_is_expr($is_arg, array_slice($tokens, $i+1));
- array_splice($tokens, $is_arg_start, count($tokens), $new_tokens);
- $i = $is_arg_start;
- default:
- if (preg_match('#^(([a-z0-9\-_]+?\.)+?)?(\$)?([A-Z]+[A-Z0-9\-_]+)$#s', $token, $varrefs)) {
- $token = (!empty($varrefs[1])) ? $this->generate_block_data_ref(substr($varrefs[1], 0, -1), true, $varrefs[3]) . '[\'' . $varrefs[4] . '\']' : (($varrefs[3]) ? '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $varrefs[4] . '\']' : '$this->vars[\'' . $varrefs[4] . '\']');
- }
- break;
- }
- }
-
- $code = (($elseif) ? '} elseif (' : 'if (') . (implode(' ', $tokens) . ') { ');
-
- return $code;
- }
- // This is from Smarty
- public function _parse_is_expr($is_arg, $tokens) {
- $expr_end = 0;
- $negate_expr = false;
- if (($first_token = array_shift($tokens)) == 'not') {
- $negate_expr = true;
- $expr_type = array_shift($tokens);
- } else {
- $expr_type = $first_token;
- }
- switch ($expr_type) {
- case 'even':
- if (@$tokens[$expr_end] == 'by') {
- $expr_end++;
- $expr_arg = $tokens[$expr_end++];
- $expr = "!(($is_arg / $expr_arg) % $expr_arg)";
- } else {
- $expr = "!($is_arg % 2)";
- }
- break;
- case 'odd':
- if (@$tokens[$expr_end] == 'by') {
- $expr_end++;
- $expr_arg = $tokens[$expr_end++];
- $expr = "(($is_arg / $expr_arg) % $expr_arg)";
- } else {
- $expr = "($is_arg % 2)";
- }
- break;
- case 'div':
- if (@$tokens[$expr_end] == 'by') {
- $expr_end++;
- $expr_arg = $tokens[$expr_end++];
- $expr = "!($is_arg % $expr_arg)";
- }
- break;
- default:
- break;
- }
- if ($negate_expr) {
- $expr = "!($expr)";
- }
- array_splice($tokens, 0, $expr_end, $expr);
- return $tokens;
- }
- public function compile_tag_define($tag_args) {
- preg_match('#^(([a-z0-9\-_]+?\.)+?)?\$([A-Z][A-Z0-9_\-]*?) = (\'?)(.*?)(\'?)$#', $tag_args, $match);
- if (empty($match[3]) || empty($match[5])) {
- return '';
- }
- // Are we a string?
- if ($match[4] && $match[6]) {
- $match[5] = "'" . addslashes(str_replace(array('\\\'', '\\\\'), array('\'', '\\'), $match[5])) . "'";
- } else {
- preg_match('#(true|false|\.)#i', $match[5], $type);
- switch (strtolower($type[1]))
- {
- case 'true':
- case 'false':
- $match[5] = strtoupper($match[5]);
- break;
- case '.';
- $match[5] = doubleval($match[5]);
- break;
- default:
- $match[5] = intval($match[5]);
- break;
- }
- }
- return (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[3] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[3] . '\']') . ' = ' . $match[5] . ';';
- }
- public function compile_tag_undefine($tag_args) {
- preg_match('#^(([a-z0-9\-_]+?\.)+?)?\$([A-Z][A-Z0-9_\-]*?)$#', $tag_args, $match);
- if (empty($match[3])) {
- return '';
- }
- return 'unset(' . (($match[1]) ? $this->generate_block_data_ref(substr($match[1], 0, -1), true, true) . '[\'' . $match[3] . '\']' : '$this->_tpldata[\'DEFINE\'][\'.\'][\'' . $match[3] . '\']') . ');';
- }
- /**
- * Compiles code and writes to cache if needed
- */
- public function compile2($code, $handle, $cache_file) {
- $code = $this->compile_code('', $code);
- if ($cache_file && !empty($this->use_cache) && !empty($this->auto_compile)) {
- $res = $this->write_cache($cache_file, $code);
- if ($handle && $res) {
- $this->files_cache[$handle] = $cache_file;
- }
- }
- $code = '?'.'>'.$code.'<'."?php ";
- return $code;
- }
- /**
- * Compiles the given string of code, and returns
- * the result in a string.
- * If "do_not_echo" is true, the returned code will not be directly
- * executable, but can be used as part of a variable assignment
- * for use in assign_code_from_handle().
- * This function isn't used and kept only for compatibility with original template.php
- */
- public function compile($code, $do_not_echo = false, $retvar = '') {
- $code = ' ?'.'>' . $this->compile_code('', $code) . '<'."?php \n";
- if ($do_not_echo) {
- $code = "ob_start();\n". $code. "\n\${$retvar} = ob_get_contents();\nob_end_clean();\n";
- }
- return $code;
- }
- /**
- * Write cache to disk
- */
- public function write_cache($filename, $code) {
- global $config;
-
- // check if cache is writable
- if (!$this->cache_writable) {
- return false;
- }
-
- // check if filename is valid
- if(substr($filename, 0, strlen($this->cachedir)) !== $this->cachedir) {
- return false;
- }
- // try to open file
- $file = @fopen($filename, 'w');
- if(!$file) {
- // try to create directories
- $dir = substr($filename, strlen($this->cachedir), strlen($filename));
- $dirs = explode('/', $dir);
- $path = $this->cachedir;
- @umask(0);
- if (!@is_dir($path)) {
- if (!@mkdir($path)) {
- $this->cache_writable = 0;
- return false;
- }
- _chmod($path, $config->mask);
- }
-
- $count = count($dirs);
- if ($count > 0) {
- for ($i = 0; $i < $count-1; $i++) {
- if ($i>0) {
- $path .= '/';
- }
- $path .= $dirs[$i];
- if(!@is_dir($path)) {
- if(!@mkdir($path)) {
- $this->cache_writable = 0;
- return false;
- }
- _chmod($path, $config->mask);
- }
- }
- }
-
- // try to open file again after directories were created
- $file = @fopen($filename, 'w');
- }
- if (!$file) {
- $this->cache_writable = 0;
- return false;
- }
- fwrite($file, "<?php\n\n// Generated on " . date('r') . " (time=" . time() . ")\n\n?>");
- fwrite($file, $code);
- fclose($file);
- _chmod($filename, $config->mask);
- return true;
- }
- public function xs_startup() {
- if (!empty($this->xs_started)) {
- return;
- }
- $this->xs_started = 1;
-
- // Adding current template
- $tpl = $this->root . '/';
- if(substr($tpl, 0, 2) === './') {
- $tpl = substr($tpl, 2, strlen($tpl));
- }
-
- $extra_template = array(
- 'TEMPLATE' => $tpl,
- 'TEMPLATE_NAME' => $this->tpl
- );
-
- $this->vars = array_merge($this->vars, $extra_template);
- }
- /**
- * Checks for empty variable and shows language variable if possible.
- */
- public function lang($var) {
- global $user;
- if (substr($var, 0, 2) === 'L_') {
- $lang = substr($var, 2);
- return lang($lang, $lang);
- }
- if (substr($var, 0, 2) === 'U_') {
- $lang = strtolower(substr($var, 2));
- return s_link($lang);
- }
- return '';
- }
- //
- //
- // Functions added for USERGROUP MOD (optimized)
- //
- //
- public function append_var_from_handle_to_block($blockname, $varname, $handle) {
- $this->assign_var_from_handle('_tmp', $handle);
- // assign the value of the generated variable to the given varname.
- $this->append_block_vars($blockname, array($varname => $this->vars['_tmp']));
- return true;
- }
- public function append_block_vars($blockname, $vararray) {
- if (strstr($blockname, '.')) {
- // Nested block.
- $blocks = explode('.', $blockname);
- $blockcount = count($blocks) - 1;
- $str = &$this->_tpldata;
- for($i = 0; $i < $blockcount; $i++) {
- $str = &$str[$blocks[$i].'.'];
- $str = &$str[count($str)-1];
- }
- // Now we add the block that we're actually assigning to.
- // We're adding a new iteration to this block with the given
- // variable assignments.
- $str = &$str[$blocks[$blockcount].'.'];
- $count = count($str) - 1;
- if ($count >= 0) {
- // adding only if there is at least one item
- $str[$count] = array_merge($str[$count], $vararray);
- }
- } else {
- // Top-level block.
- // Add a new iteration to this block with the variable assignments
- // we were given.
- $str = &$this->_tpldata[$blockname.'.'];
- $count = count($str) - 1;
- if ($count >= 0) {
- // adding only if there is at least one item
- $str[$count] = array_merge($str[$count], $vararray);
- }
- }
- return true;
- }
- /*
- * Flush a root level block, so it becomes empty.
- */
- public function flush_block_vars($blockname) {
- // Top-level block.
- // flush a existing block we were given.
- $current_iteration = count($this->_tpldata[$blockname . '.']) - 1;
- unset($this->_tpldata[$blockname . '.']);
- return true;
- }
- /*
- * Add style configuration
- */
- public function _add_config($tpl, $add_vars = true) {
- return false;
- }
- public function add_config($tpl) {
- $config_name = 'xs_style_' . $tpl;
- global $config;
- $result = false;
- if(empty($config[$config_name])) {
- $old = $this->style_config;
- $result = $this->_add_config($tpl, false);
- $this->style_config = $old;
- }
- return $result;
- }
- /*
- * Refresh config data
- */
- public function _refresh_config($tpl, $add_vars = false) {
- return false;
- }
- public function refresh_config($tpl = '') {
- if ($tpl === '') {
- $tpl = $this->tpl;
- }
- if ($tpl == $this->tpl) {
- $result = $this->_refresh_config($tpl, true);
- } else {
- $old = $this->style_config;
- $result = $this->_refresh_config($tpl, false);
- $this->style_config = $old;
- }
- return $result;
- }
- /*
- * Get style configuration
- */
- public function _get_config($tpl, $add_config) {
- $this->style_config = array();
- if (empty($tpl)) {
- $tpl = $this->tpl;
- }
-
- $config_name = 'xs_style_' . $tpl;
- global $config;
-
- if (empty($config[$config_name])) {
- if($add_config) {
- $this->_add_config($tpl, $tpl === $this->tpl ? true : false);
- }
- return $this->style_config;
- }
-
- $this->style_config = $this->_unserialize($config[$config_name]);
- if ($tpl === $this->tpl) {
- foreach ($this->style_config as $var => $value) {
- $this->vars['TPL_CFG_' . strtoupper($var)] = $value;
- }
- }
- return $this->style_config;
- }
- public function get_config($tpl = '', $add_config = true) {
- if (empty($tpl)) {
- if (empty($this->tpl)) {
- return array();
- }
- $this->_get_config($this->tpl, $add_config);
- return $this->style_config;
- } else {
- $old_config = $this->style_config;
- $result = $this->_get_config($tpl, $add_config);
- $this->style_config = $old_config;
- return $result;
- }
- }
- /*
- * Split/merge config data.
- * Using this function instead of (un)serialize because it generates smaller string
- */
- public function _serialize($array) {
- if (!is_array($array)) {
- return '';
- }
-
- $str = '';
- foreach ($array as $var => $value) {
- if ($str) {
- $str .= '|';
- }
- $str .= $var . '=' . str_replace('|', '', $value);
- }
- return $str;
- }
-
- public function _unserialize($str) {
- $array = array();
- $list = explode('|', $str);
-
- for ($i = 0; $i < count($list); $i++) {
- $row = explode('=', $list[$i], 2);
- if (count($row) == 2) {
- $array[$row[0]] = $row[1];
- }
- }
- return $array;
- }
- }
- if (!function_exists('xs_switch')) {
- function xs_switch($tpl, $name) {
- return (isset($tpl->_tpldata[$name.'.']) && count($tpl->_tpldata[$name.'.']) > 0);
- }
- }