Location: PHPKode > projects > Webgenerator-X content management system > wgx_rc1.5/WG-X/class.TE.php
<?
/*
* This file is part of Webgenerator-X,
* an object oriented website management engine working an top of
* Apache/PHP4/MySQL.
* http://www.webgenerator-x.com
* @2001 REGNI Giorgio
* hide@address.com
*
* Webgenerator-X 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 2 of the License, or
* (at your option) any later version.
*
* Webgenerator-X 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 Webgenerator-X; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*************************************************************************/

/*	Inspired from FastTemplate( http://www.thewebmasters.net/php/) 
	by hide@address.com
*/

/* REGNI Giorgio
   20/04/2001
   29/01/2002 templates via mysql
   Webgenerator-X
*/

/**
* WGX template system
* 
* Altough a module is not restricted concerning librairy it wants to use, Webgenerator-X provides some
* basic functionalities.
* Any module can use the global var $TE which is an instance of this class where the root
* directory is set to its "templates" folder. ( ie /Modules/Modulename/templates ).
* No other initialisation is needed expected the templates to use themselves.
* This class is for use only in normal pages, admin pages don't use html but the class page_builder.
* You will find documentation for only the needed functions of a WG-X module.
*
* Inspired from FastTemplate( http://www.thewebmasters.net/php/) by cdi.
* @version 	1.0
* @author	REGNI Giogio
* @package	Engine
* @subpackage Module development
* @copyright Public domain
*/

class A_TE {

	var $FILELIST	=	array();	//	Holds the array of filehandles
									//	FILELIST[HANDLE] == "fileName"

	var $DYNAMIC	=	array();	//	Holds the array of dynamic
									//	blocks, and the fileHandles they
									//	live in.

	var $PARSEVARS	=	array();	//	Holds the array of Variable
									//	handles.
									//	PARSEVARS[HANDLE] == "value"

	var	$LOADED		=	array();	//	We only want to load a template
									//	once - when it's used.
									//	LOADED[FILEHANDLE] == 1 if loaded
									//	undefined if not loaded yet.

	var	$HANDLE		=	array();	//	Holds the handle names assigned
									//	by a call to parse()

	
	var	$FROMTEXT	=	array();	//	Holds the handle names for templates not loaded from files
							//      but whose content is directly given in a string
	                                                //      when calling define.
                                                        //      the data is stored in the FILELIST array

	var	$ROOT		=	"";			//	Holds path-to-templates

	var $WIN32		;		

	var $ERROR		=	"";			//	Holds the last error message

	var $LAST		=	"";			//	Holds the HANDLE to the last
									//	template parsed by parse()

	
//	************************************************************

	function A_TE ()
	{
		global $php_errormsg,$win32,$template_dir,$root_dir;
		
		$this->WIN32 = $win32;
		
		$this->set_root(concat_dirs($root_dir,$template_dir));
		
	}	// end (new) TE ()


//	************************************************************
//	All templates will be loaded from this "root" directory
//	Can be changed in mid-process by re-calling with a new
//	value.

	function set_root ($root)
	{
		$trailer = substr($root,-1);

		if(!$this->WIN32)
		{
			if( (ord($trailer)) != 47 )
			{
				$root = "$root". chr(47);
			}

			if(is_dir($root))
			{
				$this->ROOT = $root;
			}
			else
			{
				$this->ROOT = "";
				$this->error("Specified ROOT dir [$root] is not a directory");
			}
		}
		else
		{
			// WIN32 box - no testing
			if( (ord($trailer)) != 92 )
			{
				$root = "$root" . chr(92);
			}
			$this->ROOT = $root;
		}

	}	// End set_root()


	// reset the template directory to the basic template directory
	function reset_template_root()
	{
		global $template_dir,$root_dir;
	
		$this->set_root(concat_dirs($root_dir,$template_dir));
	}
	
	// reset the template directory to one particular module template directory
	function set_module_template_root($modulename)
	{
		global $root_dir;
	
		$trailer = substr($root_dir,-1);

		if(!$this->WIN32)
		{
			if( (ord($trailer)) != 47 )
			{
				$root = $root_dir. chr(47);
			}
			
			$root = $root.'Modules'.chr(47).$modulename.chr(47).'templates'.chr(47);

			$this->ROOT = $root;
		}
		else
		{
			// WIN32 box - no testing
			if( (ord($trailer)) != 92 )
			{
				$root = $root_dir. chr(92);
			}
			
			$root = $root.'Modules'.chr(92).$modulename.chr(92).'templates'.chr(92);

			$this->ROOT = $root;
		}

	}

//	************************************************************
//	A quick check of the template file before reading it.
//	This is -not- a reliable check, mostly due to inconsistencies
//	in the way PHP determines if a file is readable.

	function is_safe ($filename)
	{
		if(!file_exists($filename))
		{
			$this->error("[$filename] does not exist",0);
			return false;
		}
		return true;
	}

//	************************************************************
//	Grabs a template from the root dir and 
//	reads it into a (potentially REALLY) big string
//  Giorgio: added string language translation
	function get_template ($template)
	{
		if(empty($this->ROOT))
		{
			$this->error("Cannot open template. Root not valid.",1);
			return false;
		}

		$filename	=	"$this->ROOT"."$template";
		$contents = implode("",(@file($filename)));
		if( (!$contents) or (empty($contents)) )
		{
			$this->error("get_template() failure: [$filename] $php_errormsg",1);
		}

		// Do String Translation in templates
		// All !!string!! in templates are evaluated as string to translate 
		while (ereg("!!([a-zA-Z0-9_]+)!!",$contents,$strings))
		{
			eval (TRANSLATE ( $strings[1], "\$tmp"));
			
			$contents = str_replace($strings[0],$tmp,$contents);
		}
		
		return $contents;
	} // end get_template

//	************************************************************
//	Prints the warnings for unresolved variable references
//	in template files. Used if STRICT is true

	function show_unknowns ($Line)
	{
		$unknown = array();
		if (ereg("({[A-Z0-9_]+})",$Line,$unknown))
		{
			$UnkVar = $unknown[1];
			if(!(empty($UnkVar)))
			{
				@error_log("[TE] Warning: no value found for variable: $UnkVar ",0);
			}
		}
	}	// end show_unknowns()

//	************************************************************
//	This routine get's called by parse() and does the actual
//	{VAR} to VALUE conversion within the template.

	function parse_template ($template, $tpl_array)
	{
		while ( list ($key,$val) = each ($tpl_array) )
		{
			if(gettype($val) != "string")
			{
				settype($val,"string");
			}

				//$template = ereg_replace("{".$key."}",$val,$template);
			$template = str_replace("{".$key."}",$val,$template);
		}

		// Silently remove anything not already found

		//$template = ereg_replace("{([A-Z0-9_]+)}","",$template);
		$template = ereg_replace("\\{([A-Z0-9_]+)\\}","",$template);
		
		return $template;

	}	// end parse_template();

//	************************************************************
//	The meat of the whole class. The magic happens here.

	/**
	* All define, assign functions only set some arrays in memory. The parse method does the actual searching and replacing.
	* @param $ReturnVar,string, name of the TARGET where to store the generated text. ( It can be anything, you set it for yourself) 
	* The content is then retrieved by calling fetch with this name.
	* @param $FileTags, string: handle you defined to refer to this template in the define method.
	*/
	function parse ( $ReturnVar, $FileTags )
	{
		$append = false;
		$this->LAST = $ReturnVar;
		$this->HANDLE[$ReturnVar] = 1;

		if (gettype($FileTags) == "array")
		{
			unset($this->$ReturnVar);	// Clear any previous data

			while ( list ( $key , $val ) = each ( $FileTags ) )
			{
				if ( (!isset($this->$val)) || (empty($this->$val)) )
				{
					$this->LOADED["$val"] = 1;
					if(isset($this->DYNAMIC["$val"]))
					  {
					    $this->parse_dynamic($val,$ReturnVar);
					  }
					else
					  if(isset($this->FROMTEXT["$val"]))
					    {
					       $this->$val = $this->FILELIST["$val"];
					    }
					  else
					    {
					      $fileName = $this->FILELIST["$val"];
					      $this->$val = $this->get_template($fileName);
					    }
				}

				//	Array context implies overwrite

				$this->$ReturnVar = $this->parse_template($this->$val,$this->PARSEVARS);

				//	For recursive calls.

				$this->assign( array( $ReturnVar => $this->$ReturnVar ) );

			}
		}	// end if FileTags is array()
		else
		{
			// FileTags is not an array

			$val = $FileTags;

			if( (substr($val,0,1)) == '.' )
			{
				// Append this template to a previous ReturnVar

				$append = true;
				$val = substr($val,1);
			}

			if ( (!isset($this->$val)) || (empty($this->$val)) )
			{
					$this->LOADED["$val"] = 1;
					if(isset($this->DYNAMIC["$val"]))
					{
						$this->parse_dynamic($val,$ReturnVar);
					}
					else
					{
					   if(isset($this->FROMTEXT["$val"]))
					    {
					       $this->$val = $this->FILELIST["$val"];
					    }
					   else
					     {
						$fileName = $this->FILELIST["$val"];
						$this->$val = $this->get_template($fileName);
					     }
					}
			}

			if($append)
			{
				$this->$ReturnVar .= $this->parse_template($this->$val,$this->PARSEVARS);
			}
			else
			{
				$this->$ReturnVar = $this->parse_template($this->$val,$this->PARSEVARS);
			}

			//	For recursive calls.

			$this->assign(array( $ReturnVar => $this->$ReturnVar) );

		}
		return;
	}	//	End parse()


//	************************************************************

	function FastPrint ( $template = "" )
	{
		if(empty($template))
		{
			$template = $this->LAST;
		}

		if( (!(isset($this->$template))) || (empty($this->$template)) )
		{
			$this->error("Nothing parsed, nothing printed",0);
			return;
		}
		else
		{
			print $this->$template;
		}
		return;
	}

//	************************************************************

	/**
	* Returns the data from a parsed handle. 
	* @access public
	* @param $template, string parsed code handle. If it's not set, last used template will be assumed.
	* @return string, parsed code.
	*
	* Example: 
	*
	* $TE->parse(CONTENT, "main");
	*
    * $content = $TE->fetch("CONTENT");
	*/
	function fetch ( $template = "" )
	{
		if(empty($template))
		{
			$template = $this->LAST;
		}
		if( (!(isset($this->$template))) || (empty($this->$template)) )
		{
			$this->error("Nothing parsed, nothing printed",0);
			return "";
		}

		return($this->$template);
	}


//	************************************************************
	/**
	* Dynamic block are templates inside templates.
	* You can think of a table with an undetermined number of row. Each row uses the same template code.
	* You can define a template for the table, have a {ROW} inside and another template with the row, like
	* <tr><td>{NUM}</td><td>{TEXT}</td></tr> and manually fetch each row and concatenate them to form the ROW block that
	* will be then parsed with the main template.
	*
	* You can also define blocks inside a template to reduce the number of files, the code and enhance the comprehension
	* of the template.
	* 
	* Example of a template file with dynamic block:
	*
    * <table bgcolor="#DDDDDD">
	*
	* <tr><td><b>Date</b></td><td><b>Event</b></td></tr>
	* 
    *<!-- BEGIN DYNAMIC block: row -->
    *
	* <tr>
	*
    * <td>{DATE}</td>
	*
    * <td>{EVENT}</td>
	*
    *</tr>
	*
    *<!-- END DYNAMIC block: row -->
	*
	* </table>
	*
	* The syntax of your BEGIN and END lines needs to be VERY exact. It is case sensitive. The code block begins on a new line all by itself. There cannot be ANY OTHER TEXT on the line with the BEGIN or END statement. (although you can have any amount of whitespace before or after) It must be in the format shown; 
	* @access public
	* @param $Macro string, name of the block, here it's "row".
	* @param $ParentName string, handle of the template where the block must be found.
	* @return boolean, always true.
	*
	* Example:
	*
	* $TE->define(    array( eventtable  =>  "table_table.tpl"));
	*
	* $TE->define_dynamic( "row" , "eventtable" );
	*
	* foreach( $events as $event)	// assume $events is an array of events
	*
	* $TE->assign( array( DATE=>$event[date], EVENT=>$event[title]));
	*
	* $TE->parse( Rows, ".Row");	// . means append, notice the s in the Rows statement.
	*
	* // TE assumes the block name with an s, means to be written in place of the block.
	*
	* }
	* 
	* $TE->parse( MAIN, "eventtable");	// must be called
	*
	* $content = $TE->fetch( MAIN );
	*/
	function define_dynamic ($Macro, $ParentName)
	{
		//	A dynamic block lives inside another template file.
		//	It will be stripped from the template when parsed
		//	and replaced with the {$Tag}.

		$this->DYNAMIC["$Macro"] = $ParentName;
		return true;
	}

//	************************************************************

	function parse_dynamic ($Macro,$MacroName)
	{
		// The file must already be in memory.

		$ParentTag = $this->DYNAMIC["$Macro"];
		if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
		{
		  if(isset($this->FROMTEXT["$ParentTag"]))
		    {
		      $this->$ParentTag = $this->FILELIST["$ParentTag"];
		    }
		  else
		    {
			$fileName = $this->FILELIST[$ParentTag];
			 
			$this->$ParentTag = $this->get_template($fileName);
			$this->LOADED[$ParentTag] = 1;
		    }
		}
		if($this->$ParentTag)
		{
			$template = $this->$ParentTag;
			$DataArray = split("\n",$template);
			$newMacro = "";
			$newParent = "";
			$outside = true;
			$start = false;
			$end = false;
			while ( list ($lineNum,$lineData) = each ($DataArray) )
			{
				$lineTest = trim($lineData);
				if("<!-- BEGIN DYNAMIC BLOCK: $Macro -->" == $lineTest )
				{
					$start = true;
					$end = false;
					$outside = false;
				}
				else
				if("<!-- END DYNAMIC BLOCK: $Macro -->" == $lineTest )
				{
					$start = false;
					$end = true;
					$outside = true;
				}
				
				if( (!$outside) and (!$start) and (!$end) )
				{
					$newMacro .= "$lineData\n"; // Restore linebreaks
				}
				if( ($outside) and (!$start) and (!$end) )
				{
					$newParent .= "$lineData\n"; // Restore linebreaks
				}
				if($end)
				{
					$newParent .= "{".$MacroName."}\n";
				}
				// Next line please
				if($end) { $end = false; }
				if($start) { $start = false; }
			}	// end While

			$this->$Macro = $newMacro;
			$this->$ParentTag = $newParent;
			return true;

		}	// $ParentTag NOT loaded - MAJOR oopsie
		else
		{
			@error_log("ParentTag: [$ParentTag] not loaded!",0);
			$this->error("ParentTag: [$ParentTag] not loaded!",0);
		}
		return false;
	}

//	************************************************************
//	Strips a DYNAMIC block from a template.
	/**
	* Strips a DYNAMIC block from a template.
	*
	* Useful when for example the database output is null and you don't want to let an empty row in the final page.
	*
	* ( To clear a dynamic block parsed content, you must use the clear method. )
	* @access public
	* @param $Macro string, name of the block with an s.
	* @return boolean, always true.
	*/
	function clear_dynamic ($Macro="")
	{
		if(empty($Macro)) { return false; }

		// The file must already be in memory.

		$ParentTag = $this->DYNAMIC["$Macro"];

		if( (!$this->$ParentTag) or (empty($this->$ParentTag)) )
		{
		  
		if(isset($this->FROMTEXT["$ParentTag"]))
		    {
		      $this->$ParentTag = $this->FILELIST["$ParentTag"];
		    }
		  else
		    {
			$fileName = $this->FILELIST[$ParentTag];
			 
			$this->$ParentTag = $this->get_template($fileName);
			$this->LOADED[$ParentTag] = 1;
		    }	
		}

		if($this->$ParentTag)
		{
			$template = $this->$ParentTag;
			$DataArray = split("\n",$template);
			$newParent = "";
			$outside = true;
			$start = false;
			$end = false;
			while ( list ($lineNum,$lineData) = each ($DataArray) )
			{
				$lineTest = trim($lineData);
				if("<!-- BEGIN DYNAMIC BLOCK: $Macro -->" == $lineTest )
				{
					$start = true;
					$end = false;
					$outside = false;
				}
				else
				if("<!-- END DYNAMIC BLOCK: $Macro -->" == $lineTest )
				{
					$start = false;
					$end = true;
					$outside = true;
				}
				
				if( ($outside) and (!$start) and (!$end) )
				{
					$newParent .= "$lineData\n"; // Restore linebreaks
				}
				// Next line please
				if($end) { $end = false; }
				if($start) { $start = false; }
			}	// end While

			$this->$ParentTag = $newParent;
			return true;

		}	// $ParentTag NOT loaded - MAJOR oopsie
		else
		{
			@error_log("ParentTag: [$ParentTag] not loaded!",0);
			$this->error("ParentTag: [$ParentTag] not loaded!",0);
		}
		return false;
	}


//	************************************************************

	/**
	* Maps a template filename to a (usually shorter) name referenced as handle. 
	* This new name is the name that you will use to refer to the templates. Filenames should not appear in any place other than a define(). 
	* You actually call define at the begining of your code with an array of all templates you plan to use.
	* Define does not actually load the templates.
	*
	* @access public
	* @param $fileList, array( handle_name=>filename,)
	* @return boolean, true
	*
	* Example
	*
	* $tpl->define( array(    article    => "article.tpl",
	*
    *                         comments  => "comments.tpl" ));
	*/

	/* By giorgio:
           If the template name starts with text:// when it's considered as a template from the text given
           as the filename.
           In this case, define can be used to update the template if it changes.
	* Example
	*
	* $tpl->define( array(    "text://article"    => $template_data,
	*
        *                         comments  => "comments.tpl" ));
        * THE TEMPLATE IS THEN REFERED TO WITHOUT TEXT:// !
	*/
	function define ($fileList)
	{
		while ( list ($FileTag,$FileName) = each ($fileList) )
		{
		  if (substr ($FileTag, 0, 7)== 'text://')
		    {
		      $FileTag = substr ($FileTag, 7);
		      $this->FILELIST["$FileTag"] = $FileName;// filename holds the data here
		      $this->FROMTEXT["$FileTag"] = 1;
		      $this->LOADED["$FileTag"] = 1;
		    }
		  else
			$this->FILELIST["$FileTag"] = $FileName;
		}
		return true;
	}

//	************************************************************

	function clear_parse ( $ReturnVar = "")
	{
		$this->clear($ReturnVar);
	}

//	************************************************************

	/**
	* Clears a template in case you want to use it many times.
	* Useful for recurrent dynamic blocks.
	* @access public
	* @param $Macro string, hanfle of the generated text to clear, with an s if it's a dynamic block.
	*/
	function clear ( $ReturnVar = "" )
	{
		// Clears out hash created by call to parse()

		if(!empty($ReturnVar))
		{
			if( (gettype($ReturnVar)) != "array")
			{
				unset($this->$ReturnVar);
				return;
			}
			else
			{
				while ( list ($key,$val) = each ($ReturnVar) )
				{
					unset($this->$val);
				}
				return;
			}
		}

		// Empty - clear all of them

		while ( list ( $key,$val) = each ($this->HANDLE) )
		{
			$KEY = $key;
			unset($this->$KEY);
		}
		return;

	}	//	end clear()

//	************************************************************

	function clear_all ()
	{
		$this->clear();
		$this->clear_assign();
		$this->clear_define();
		$this->clear_tpl();

		return;

	}	//	end clear_all

//	************************************************************

	function clear_tpl ($fileHandle = "")
	{
		if(empty($this->LOADED))
		{
			// Nothing loaded, nothing to clear

			return true;
		}
		if(empty($fileHandle))
		{
			// Clear ALL fileHandles

			while ( list ($key, $val) = each ($this->LOADED) )
			{
				unset($this->$key);
			}
			unset($this->LOADED);

			return true;
		}
		else
		{
			if( (gettype($fileHandle)) != "array")
			{
				if( (isset($this->$fileHandle)) || (!empty($this->$fileHandle)) )
				{
					unset($this->LOADED[$fileHandle]);
					unset($this->$fileHandle);
					return true;
				}
			}
			else
			{
				while ( list ($Key, $Val) = each ($fileHandle) )
				{
					unset($this->LOADED[$Key]);
					unset($this->$Key);
				}
				return true;
			}
		}

		return false;

	}	// end clear_tpl

//	************************************************************

	function clear_define ( $FileTag = "" )
	{
		if(empty($FileTag))
		{
			unset($this->FILELIST);
			return;
		}

		if( (gettype($Files)) != "array")
		{
			unset($this->FILELIST[$FileTag]);
			return;
		}
		else
		{
			while ( list ( $Tag, $Val) = each ($FileTag) )
			{
				unset($this->FILELIST[$Tag]);
			}
			return;
		}
	}

//	************************************************************
//	Aliased function - used for compatibility with CGI::TE
	function clear_parse ()
	{
		$this->clear_assign();
	}

//	************************************************************
//	Clears all variables set by assign()

	function clear_assign ()
	{
		if(!(empty($this->PARSEVARS)))
		{
			while(list($Ref,$Val) = each ($this->PARSEVARS) )
			{
				unset($this->PARSEVARS["$Ref"]);
			}
		}
	}

//	************************************************************

	function clear_href ($href)
	{
		if(!empty($href))
		{
			if( (gettype($href)) != "array")
			{
				unset($this->PARSEVARS[$href]);
				return;
			}
			else
			{
				while (list ($Ref,$val) = each ($href) )
				{
					unset($this->PARSEVARS[$Ref]);
				}
				return;
			}
		}
		else
		{
			// Empty - clear them all

			$this->clear_assign();
		}
		return;
	}

//	************************************************************

	/**
	* Assign a string value to a template variable.
	* Template vars are defined as text between {} in the template file.
	* They are usually upper case letters like {DATE},{TEXT}....
	*
	* The actual conversion is done by calling the parse method.
	* Assigning a value for the same key will overwrite that key.
	*
	* @access public
	* @param $tpl_array, array( var => value,.....)
	* @param $trailer, string, in case $tpl_array is not an array, $trailer can be use as the value and $tpl_array as the variable name. Depreciated, prefer the array form.
	*/
	function assign ($tpl_array, $trailer="")
	{
		if(gettype($tpl_array) == "array")
		{
			while ( list ($key,$val) = each ($tpl_array) )
			{
				if (!(empty($key)))
				{
					//	Empty values are allowed
					//	Empty Keys are NOT

					$this->PARSEVARS["$key"] = $val;
				}
			}
		}
		else
		{
			// Empty values are allowed in non-array context now.
			if (!empty($tpl_array))
			{
				$this->PARSEVARS["$tpl_array"] = $trailer;
			}
		}
	}

//	************************************************************
//	Return the value of an assigned variable.
//	Christian Brandel hide@address.com

	function get_assigned($tpl_name = "")
	{
		if(empty($tpl_name)) { return false; }
		if(isset($this->PARSEVARS["$tpl_name"]))
		{
			return ($this->PARSEVARS["$tpl_name"]);
		}
		else
		{
			return false;
        }
	}

//	************************************************************

	function error ($errorMsg, $die = 0)
	{
		$this->ERROR = $errorMsg;
		echo "ERROR: $this->ERROR <BR> \n";
		if ($die == 1)
		{
			exit;
		}

		return;

	} // end error()


//	************************************************************



//	************************************************************

} // End class.TE.php

$TE = new A_TE();
?>
Return current item: Webgenerator-X content management system