<?php
//
//	file: includes/class_template.php
//	author: ptirhiik
//	begin: 29/08/2004
//	version: 1.6.0 - 10/06/2006
//	license: http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
//

if ( !defined('IN_PHPBB') )
{
	die('Hacking attempt');
}

class template_class
{
	var $_tpldata = array();

	// hash of filenames for each template handle.
	var $files = array();
	var $filename = array();
	var $mains = array();

	// this will hash handle names to the compiled/uncompiled code for that handle.
	var $compiled_code = array();

	// directories
	var $template_path;
	var $cache_path;

	// parms
	var $root_template;
	var $template_name;
	var $root;
	var $cacheprefix;
	var $alt_template_name;
	var $alt_root;
	var $alt_cacheprefix;

	var $no_debug;
	var $_id;

	var $local_images;

	function template_class($root='.', $alt_template_name='', $template_path='')
	{
		global $config;

		// default values
		if ( !is_object($config) || empty($config->data) )
		{
			$config->data['cache_path'] = 'cache/';
			$config->data['cache_disabled_template'] = true;
			$config->data['cache_check_template'] = true;
		}
		else if ( $config->data['cache_disabled_template'] )
		{
			$config->data['cache_check_template'] = true;
		}

		// set the cache path
		$this->cache_path = $config->data['cache_path'];
		$this->template_path = empty($template_path) ? 'templates/' : $template_path;

		$this->set($root, $alt_template_name);

		$this->no_debug = false;
		$this->_id = 0;
	}

	function set($root, $alt_template_name='')
	{
		global $config;

		$this->root_template = $config->root . $this->template_path;

		// get main template settings
		$this->template_name = str_replace('//', '/', str_replace('./', '', substr($root, strlen($this->root_template))) . '/');
		$this->root = $this->root_template . $this->template_name;
		$this->cacheprefix = $config->root . $this->cache_path . 'tpl_' . str_replace('/', '_', $this->template_name);

		// get custom tpls settings
		$this->alt_template_name = $this->alt_root = $this->alt_prefix = '';
		if ( $alt_template_name && ($this->alt_template_name = $this->tpl_realpath($alt_template_name)) )
		{
			$this->alt_root = $this->root_template . $this->alt_template_name;
			$this->alt_cacheprefix = $config->root . $this->cache_path . 'tpl_' . str_replace('/', '_', $this->alt_template_name);
		}

		// raz
		$this->_tpldata = $this->local_images = array();
	}

	function tpl_realpath($tpl_name)
	{
		global $config;

		if ( !empty($tpl_name) )
		{
			$real_path = phpbb_realpath($this->root);
			if ( $real_path != $this->root)
			{
				$tpl_real_path = phpbb_realpath($this->root . $tpl_name);
				if ( empty($tpl_real_path) )
				{
					$tpl_name = '';
				}
				else
				{
					$root_real_path = phpbb_realpath($this->root_template);
					$tpl_name = str_replace('//', '/', str_replace('\\', '/', substr($tpl_real_path, strlen($root_real_path)+1)) . '/');
				}
			}
			// realpath fails to get the real path sometime (when not available), so find another way
			else
			{
				$res = $this->template_name;
				if ( substr($tpl_name, 0, 2) == './' )
				{
					$tpl_name = substr($tpl_name, 2);
				}
				if ( substr($tpl_name, 0, 3) == '../' )
				{
					$res = '';
					$tpl_name = substr($tpl_name, 3);
				}
				if ( preg_match('/\.\.\//', $tpl_name) )
				{
					$tpl_name = '';
				}
				else
				{
					$tpl_name = str_replace('//', '/', str_replace('./', '', $res . $tpl_name) . '/');
				}
			}
		}
		return $tpl_name;
	}

	function set_switch($switch_name, $on=true, $onset=true)
	{
		if ( $onset )
		{
			$this->assign_block_vars($switch_name . ($on ? '' : '_ELSE'), array());
		}
	}

	function save(&$save)
	{
		$save = $this->_tpldata;
	}

	function destroy()
	{
		$this->_tpldata = array();
	}

	function restore(&$save)
	{
		$this->_tpldata = $save;
	}

	function get_pparse($handle)
	{
		ob_start();
		$this->pparse($handle);
		$res = ob_get_contents();
		ob_end_clean();
		return $res;
	}

	// Sets the template filenames for handles. $filename_array
	// should be a hash of handle => filename pairs.
	function set_filenames($filename_array)
	{
		if ( !is_array($filename_array) )
		{
			return false;
		}

		$template_names = '';
		foreach ($filename_array as $handle => $filename)
		{
			if ( empty($filename) )
			{
				message_die(GENERAL_ERROR, 'template error - Empty filename specified for ' . $handle, '', __LINE__, __FILE__);
			}

			$this->filename[$handle] = $filename;
			if ( !empty($this->alt_root) )
			{
				$this->files[$handle] = $this->alt_root . $filename;
			}

			// doesn't exists : try the main
			if ( !$this->mains[$handle] = (!empty($this->alt_root) && file_exists($this->files[$handle])) )
			{
				$this->files[$handle] = $this->root . $filename;
				$this->mains[$handle] = false;
			}
		}

		return true;
	}

	function make_filename($filename)
	{
		return !empty($this->alt_root) && file_exists($this->alt_root . $filename) ?  $this->alt_root . $filename : (file_exists($this->root . $filename) ? $this->root . $filename : '');
	}

	// Methods for loading and evaluating the templates
	function pparse($handle)
	{
		global $user, $images;

		$d = &$this->_tpldata;
		if ( is_object($user) && !$user->style_set )
		{
			$user->set_style();
		}
		$this->no_debug = $this->no_debug || !is_object($user) || ($user->data['user_level'] != ADMIN);

		if ( defined('DEBUG_TEMPLATE') && !$this->no_debug )
		{
			echo '<!-- Start of : ' . $this->files[$handle] . ' :: ' . $handle . ' -->' . "\n";
		}
		if ($filename = $this->_tpl_load($handle))
		{
			include($filename);
		}
		else
		{
			eval(' ?>' . $this->compiled_code[$handle] . '<?php ');
		}
		if ( defined('DEBUG_TEMPLATE') && !$this->no_debug )
		{
			echo '<!-- End of : ' . $this->files[$handle] . ' :: ' . $handle . ' -->' . "\n";
		}

		return true;
	}

	function assign_var_from_handle($varname, $handle)
	{
		return $this->assign_vars(array($varname => $this->include_file($this->filename[$handle])));
	}

	// Load a compiled template if possible, if not, recompile it
	function _tpl_load($handle)
	{
		global $config, $user, $db;

		// If we don't have a file assigned to this handle, die.
		if ( !isset($this->files[$handle]) )
		{
			message_die(GENERAL_ERROR, 'template->_tpl_load(): No file specified for handle ' . $handle, '', __LINE__, __FILE__);
		}

		// get the file name
		$w_filename = str_replace('/', '_', $this->filename[$handle]);
		$filename = ($this->mains[$handle] ? $this->alt_cacheprefix : $this->cacheprefix) . $w_filename . '.' . $config->ext;

		// Recompile page if the original template is newer, otherwise load the compiled version
		if ( !empty($this->compiled_code[$handle]) )
		{
			return false;
		}
		else if ( !$config->data['cache_disabled_template'] && @file_exists($filename) && (!$config->data['cache_check_template'] || (@filemtime($filename) > @filemtime($this->files[$handle]))) )
		{
			return $filename;
		}
		else
		{
			$this->_tpl_load_file($handle);
		}
		return false;
	}

	// Load template source from file
	function _tpl_load_file($handle)
	{
		global $config;

		// Try and open template for read
		if (!($fp = @fopen($this->files[$handle], 'r')))
		{
			message_die(GENERAL_ERROR, 'template->_tpl_load(): File ' . $this->files[$handle] . ' does not exist or is empty', '', __LINE__, __FILE__);
		}

		// compile required
		include_once($config->url('includes/class_template_compiler'));
		$tpl_compiler = new tpl_compiler();
		$this->compiled_code[$handle] = $tpl_compiler->compile(trim(@fread($fp, filesize($this->files[$handle]))));
		@fclose($fp);

		// output the template to the cache
		if ( !$config->data['cache_disabled_template'] )
		{
			$filename = ($this->mains[$handle] ? $this->alt_cacheprefix : $this->cacheprefix) . str_replace('/', '_', $this->filename[$handle]) . '.' . $config->ext;
			$tpl_compiler->compile_write($handle, $this->compiled_code[$handle], $filename);
		}
		unset($tpl_compiler);
	}

	// Assign key variable pairs from an array
	function assign_vars($vararray)
	{
		$this->_tpldata['.'][0] = array_merge(empty($this->_tpldata['.'][0]) ? array() : $this->_tpldata['.'][0], $vararray);
		return true;
	}

	// Assign key variable pairs from an array to a specified block
	function assign_block_vars($blockname, $vararray)
	{
		if (strstr($blockname, '.'))
		{
			// Nested block.
			$blocks = explode('.', $blockname);
			$blockcount = sizeof($blocks) - 1;

			$str = &$this->_tpldata; 
			for ($i = 0; $i < $blockcount; $i++) 
			{
				$str = &$str[$blocks[$i]]; 
				$str = &$str[sizeof($str) - 1]; 
			}
			$str[$blocks[$blockcount]][] = $vararray;
		}
		else
		{
			$this->_tpldata[$blockname][] = $vararray;
		}

		return true;
	}

	function assign_lastblock_vars($blockname, $vararray)
	{
		$ok = false;
		if ( ($addr = $this->retrieve_lastblock_addr($blockname)) )
		{
			$this->assign_addrblock_vars($addr, $vararray);
			$ok = true;
		}
		return $ok;
	}

	function assign_addrblock_vars(&$addr, $vararray)
	{
		if ( $addr )
		{
			$str = &$this->_tpldata;
			$count_addr = count($addr);
			for ( $i = 0; $i < $count_addr; $i++ )
			{
				$str = &$str[ $addr[$i] ];
			}
			$str = array_merge($str, $vararray);
		}
	}

	function retrieve_lastblock_addr($blockname)
	{
		$ok = false;
		$addr = array();
		if ( strstr($blockname, '.') )
		{
			$blocks = explode('.', $blockname);
			$count_blocks = count($blocks);
			if ( $count_blocks && isset($this->_tpldata[ $blocks[0] ]) )
			{
				$ok = true;
				$addr[] = $blocks[0];
				$str = &$this->_tpldata[ $blocks[0] ];
				for ( $i = 1; $i < $count_blocks; $i++ )
				{
					$ok = false;
					for ( $j = count($str)-1; $j >= 0; $j-- )
					{
						if ( isset($str[$j][ $blocks[$i] ]) )
						{
							$ok = true;
							$str = &$str[$j][ $blocks[$i] ];
							$addr[] = $j;
							$addr[] = $blocks[$i];
							break;
						}
					}
					if ( !$ok )
					{
						break;
					}
				}
			}
		}
		else if ( isset($this->_tpldata[$blockname]) )
		{
			$ok = true;
			$str = &$this->_tpldata[$blockname];
			$addr[] = $blockname;
		}
		if ( $ok && ($ok = ($count_str = count($str))) )
		{
			$addr[] = $count_str - 1;
			return $addr;
		}
		return false;
	}

	function unset_block_vars($blockname)
	{
		// find the block (last iteration)
		if ( strstr($blockname, '.') )
		{
			$blocks = explode('.', $blockname);
			$blockcount = sizeof($blocks) - 1;

			$str = &$this->_tpldata; 
			for ($i = 0; $i < $blockcount; $i++) 
			{
				$str = &$str[ $blocks[$i] ];
				$str = &$str[ sizeof($str) - 1 ];
			}
			if ( isset($str[ $blocks[$blockcount] ]) )
			{
				unset($str[ $blocks[$blockcount] ]);
				return true;
			}
		}
		else
		{
			if ( isset($this->_tpldata[$blockname]) )
			{
				unset($this->_tpldata[$blockname]);
				return true;
			}
		}
		return false;
	}

	// this one will only works with root level blocks switch
	function move_vars($from, $to)
	{
		$this->_tpldata[$to] = $this->_tpldata[$from];
		$this->unset_block_vars($from);
	}

	function include_file($filename, $tpl_reassign='')
	{
		$parms = '';
		if ( $tpl_reassign )
		{
			$tpl_reassign = !is_array($tpl_reassign) ? array($tpl_reassign) : $tpl_reassign;
			$this->_id++;
			foreach ( $tpl_reassign as $blockname )
			{
				$parms .= ', _' . $this->_id . '_' . $blockname . ' => ' . $blockname;
				$this->move_vars($blockname, '_' . $this->_id . '_' . $blockname);
			}
		}
		return array('function' => '_include', 'parms' => $filename . $parms);
	}

	function include_escaped_file($filename, $tpl_reassign='')
	{
		$res = $this->include_file($filename, $tpl_reassign);
		return array('function' => '_include_escaped', 'parms' => $res['parms']);
	}

	function template_escape_string($str)
	{
		return str_replace(array('<br />', '&nbsp;', '<', '>', '"'), array("\n", ' ', '&lt;', '&gt;', '&quot;'), preg_replace("/[\n\r]{1,2}/", '', $str));
	}

	// used at display-time, but only within the display-time functions, not by the compiler itself
	function _get_include_parms($filename)
	{
		if ( !empty($filename) )
		{
			$tpl_reassign = explode(',', $filename);
			$parms = array();
			foreach ( $tpl_reassign as $item )
			{
				if ( empty($parms) )
				{
					$parms[] = trim($item);
					$first = false;
				}
				else if ( ($item = explode('=>', $item)) && ($from = trim($item[0])) && ($to = trim($item[1])) )
				{
					$this->move_vars($from, $to);
					$parms[] = $to;
				}
			}
		}
		return empty($parms) ? false : $parms;
	}

	function _delete_include_parms($filename)
	{
		if ( empty($filename) )
		{
			return;
		}
		$first = true;
		foreach ( $filename as $to )
		{
			if ( !$first )
			{
				$this->unset_block_vars($to);
			}
			$first = false;
		}
	}

	//
	// display-time functions
	//

	function _echo($varref)
	{
		$d = &$this->_tpldata;
		if ( !is_array($varref) )
		{
			echo $varref;
		}
		else
		{
			$this->$varref['function']($varref['parms']);
		}
	}

	// Include a seperate template
	function _include($filename)
	{
		if ( $filename = $this->_get_include_parms($filename) )
		{
			$this->set_filenames(array($filename[0] => $filename[0]));
			$this->pparse($filename[0]);
			$this->_delete_include_parms($filename);
		}
	}

	function _include_escaped($filename)
	{
		if ( $filename = $this->_get_include_parms($filename) )
		{
			$no_debug = $this->no_debug;
			$this->no_debug = true;
			$this->set_filenames(array($filename[0] => $filename[0]));
			echo $this->template_escape_string($this->get_pparse($filename[0]));
			$this->_delete_include_parms($filename);
			$this->no_debug = $no_debug;
		}
	}

	// user api support
	function lang($key='')
	{
		global $user, $lang, $requester;

		if ( !empty($key) )
		{
			if ( is_object($user) && !$user->lang_set )
			{
				$user->set_lang($requester);
			}
			echo isset($lang[$key]) ? $lang[$key] : $key;
		}
	}

	function img($key='')
	{
		global $images, $config;
		echo !empty($key) && isset($images[$key]) ? $config->root . $images[$key] : (($sub = substr($key, 0, 5)) && (($sub == 'http:') || ($sub == 'ftp:/')) ? $key : $config->root . $key);
	}

	function img_styled($key='')
	{
		echo $this->_img_styled_get($key);
	}

	function _img_styled_get($key='')
	{
		global $config;
		if ( empty($key) )
		{
			return '';
		}
		$path = $key;
		if ( isset($this->local_images[$key]) )
		{
			$path = $this->local_images[$key];
		}
		else if ( @file_exists($this->root . $key) )
		{
			$path = $this->root . $key;
		}
		else if ( @file_exists($this->alt_root . $key) )
		{
			$path = $this->alt_root . $key;
		}
		else if ( $config->root != './' )
		{
			$path = $config->root . $key;
		}
		$this->local_images[$key] = $path;
		return $path;
	}
}

?>
