<?PHP
/********************************************************************************
 * @author Tommy Gildseth, Akili AS
 * @contact gildseth@start.no
 * @website http://www.akili.no
 * @copyright (C) Tommy Gildseth, January 2002
 * @description A simple templating engine. See documentation for detailed '
 *                    description on use.
 * @version 1.2.2
 *******************************************************************************/
// Modification log
// (date)       (author)    (activity/purpose)
// 19/03/2002   Tommy       Added version number to script header.
// 21/03/2002   Tommy       Added possibility to pass array as parameter 2 in mergePages()
//                          More consistent with the other functions.
// 22/03/2002   Tommy       Fixed bug where no error msg is printed if parameter 2
//                          is not an array.
// 22/03/2002   Tommy       Changed makePage() to accept arrays as arguments for each
//                          it's parameters.
// 17/04/2002   Tommy       Modified appendToPage, to take a filename parameter.
// 02/06/2002   Tommy       Added function parseTemplate
// 08/06/2002   Tommy       Added ability to set a base directory
// 24/07/2002   Tommy       Modified appendToPage() to accept a page name, to append

class ezeTemplate
    
{
    var 
$limiter = array('{','}');
    var 
$templates;
    var 
$pages;
    var 
$baseDir;

    
/**
     *Constructor.
     * @since 1.0.0
     * @access public
     * @see addTemplate()
     * @prototype void EzeTemplate (mixed name, mixed template, string baseDir)
     */
    
function EzeTemplate ($tmplName=0,$template=0$baseDir='')
        {
        
$this->templates=array();
        
$this->pages=array();
        
$this->baseDir=$baseDir;
        if (
!== $tmplName && !== $template)
            {
            
$this->addTemplate($tmplName,$template);
            }
        }

    
/**
     *Add new template(s).
     * @pre  If an array is passed to the function, both $tmplName and $template must
     *       have an equal number of elements.
     * @post  Specified template(s) has been added to the template object.
     * @params $tmplName string or array containing the name(s) of the template(s)
     * @params $template string or array containing the actual template(s) or a
     *                   filename where they can be read from.
     * @since 1.0.0
     * @access public
     * @prototype void addTemplate (mixed name, mixed template)
     */
    
function addTemplate($tmplName,$template)
        {
        if (
is_array($tmplName))
            {
            for(
$index=0;$index<count($tmplName);$index++)
                {
                
$this->addTemplateHelper($tmplName[$index],$template[$index]);
                }
            }
        else
            {
            
$this->addTemplateHelper($tmplName,$template);
            }
        return 
true;
        }

    
/**
     *Add new template(s). Helper function for addTemplate()
     * @pre  If $template is a file, PHP must have read access to it.
     * @post  Specified template has been added to the template object.
     * @params $tmplName string containing the name of the template
     * @params $template string containing the actual template or a filename where
     *                   it can be read from.
     * @return Boolean value, template successfully imported
     * @since 1.0.0
     * @access public
     * @prototype bool addTemplateHelper (string name, string template)
     */
    
function addTemplateHelper($tmplName,$template)
        {
        if (
strlen($template)<50 && is_file($this->baseDir.$template))
            {
            
$this->templates[$tmplName]=@implode("", @file($this->baseDir.$template));
            if (!
$this->templates[$tmplName])
                {
                
trigger_error (" Failed to import template file \"$template\" "E_USER_WARNING);
                }
            }
        else
            {
            
$this->templates[$tmplName]=$template;
            }
        return 
true;
        }

    
/**
     *Make a page from a template or a file, and assign it a name.
     * @pre  Excisting pages may be overwritten, if same name is used.
     *      Array parameters $tmplName must contain same amount of elements as
     *      $pageName, and $isFile must have same amount of elements as $tmplName
     * @post  $this->pages[$pageName] contains the unmodified template from
     *       $this->$templates[$tmplName]
     * @params $pageName Name that will be used to refference the new page.
     *                   Can be array of names
     * @params $tmplName Name of template to assign to the new page. This can also
     *                   be a filename, in which case $isFile must be set to true
     *                   Can be array. If it is, $pageName must also be an array
     *                   With equal number of elements
     * @params $isFile Specifies wether $tmplName is a filename. Default is false
     *                 Can be array. If it is, it must have same amount of elements
     *                 as $tmplName. Can also just specify on value false/true for
     *                 ALL elements in $tmplName
     * @return boolean value "$tmplName exist"
     * @since 1.0.0
     * @access public
     * @prototype bool makePage(mixed pageName, mixed tmplName[,mixed isFile])
     */
    
function makePage($pageName,$tmplName$isFile=false)
        {
        
//Check preconditions
        
if (is_array($tmplName) && count($tmplName)!=count($pageName))
            {
            
trigger_error (" Wrong element count in parameters pageName and tmplName "E_USER_WARNING);
            return 
false;
            }
        else if (
is_array($isFile) && count($isFile)!=count($tmplName))
            {
            
trigger_error (" Wrong element count in parameters tmplName and isFile "E_USER_WARNING);
            return 
false;
            }

        if (
is_array($pageName) && !is_array($tmplName))
            {
            foreach (
$pageName as $nameOfPage)
                {
                if (!
$this->makePageHelper($nameOfPage,$tmplName,$isFile))
                    return 
false;
                }
            }
        else if (
is_array($pageName) && is_array($tmplName))
            {
            for (
$counter=0;$counter<count($pageName);$counter++)
                {
                if (
is_array($isFile))
                    {
                    if (!
$this->makePageHelper($pageName[$counter],
                                               
$tmplName[$counter],
                                               
$isFile[$counter]))
                        {
                        return 
false;
                        }
                    }
                else
                    {
                    if (!
$this->makePageHelper($pageName[$counter],
                                               
$tmplName[$counter],
                                               
$isFile))
                        {
                        return 
false;
                        }
                    }
                }
            }
        else
            {
            return 
$this->makePageHelper($pageName$tmplName$isFile);
            }
        }



    
/**
     *Basically same as makePage() except it does not accept any array parameters
     * @since 1.0.0
     * @access private
     * @prototype bool makePageHelper (string pageName, string tmplName [, bool isFile])
     */
    
function makePageHelper($pageName$tmplName$isFile)
        {
        if (
$isFile)
            {
            
$file=@implode("", @file($this->baseDir.$tmplName));
            if (!
$file)
                {
                
trigger_error (" Failed to import specified file \"$tmplName\" "E_USER_WARNING);
                }
            
$this->pages[$pageName]=$file;
            return 
true;
            }
        else
            {
            if (isset(
$this->templates[$tmplName]))
                {
                
$this->pages[$pageName]=$this->templates[$tmplName];
                return 
true;
                }
            else
                {
                return 
false;
                }
            }
        }

    
/**
     *Add a template to the end of an excisting page.
     * @post  tmplName has been added to pageName
     * @params pageName string Name of page to append template to.
     * @params tmplName string Name of template or page to append to page, or
     *                          a filename. isFile must be set to true in that case.
     * @params isFile boolean indicates wether value of tmplName is a filename
     *                         or a template name.
     * @return boolean "$pageName and $tmplName exists"
     * @since 1.0.0
     * @access public
     */
    
function appendToPage($pageName$tmplName$isFile=false)
        {
        if (
$isFile and is_file($this->baseDir.$tmplName))
            {
            
$file=@implode("", @file($this->baseDir.$tmplName));
            if (!
$file)
                {
                
trigger_error (" Failed to import specified file \"$tmplName\" "E_USER_WARNING);
                }
            
$this->pages[$pageName] .= $file;
            }
        else if (isset(
$this->pages[$pageName]) && isset($this->templates[$tmplName]))
            {
            
$this->pages[$pageName].=$this->templates[$tmplName];
            }
        else if (isset(
$this->pages[$pageName]) && isset($this->pages[$tmplName]))
            {
            
$this->pages[$pageName].=$this->pages[$tmplName];
            }
        else
            {
            
trigger_error (" Page \"$pageName\" or template \"$tmplName\" does not excist "E_USER_WARNING);
            }
        }

    
/**
     *Insert values into the page.
     * @pre  If $values is an array, it must be an associative array, where the keys
     *      are the names of the variables to replace.
     *      If it is a string, $valueName must be set with the name of the variable
     *      to replace.
     * @post  Variables in page $pageName has been replaced with the values from
     *       $values
     * @params $pageName Name of page to modify.
     * @params $values String or Array, containing the text to replace with the
     *                 variables in the page by.
     * @params $valueName Need only be used if $values is a simple string.
     *                    Default value is empty.
     * @return boolean value "$pageName exists"
     * @since 1.0.0
     * @access public
     * @prototype bool insertVal(string pageName, mixed values[, string valueName])
     */
    
function insertVal($pageName,$values,$valueName='')
        {
        if (isset(
$this->pages[$pageName]))
            {
            if (
is_array($values))
                {
                foreach(
$values as $eachKey=>$eachValue)
                    {
                    
$oldPage=$this->pages[$pageName];
                    
$replace=$this->limiter[0].$eachKey.$this->limiter[1];
                    
$this->pages[$pageName] = str_replace($replace,
                                                          
$eachValue,
                                                          
$oldPage);
                    if (
$this->pages[$pageName] == $oldPage)
                        {
                        
trigger_error (" Template variable $eachKey does not exist "E_USER_WARNING);
                        }
                    }
                }
            else
                {
                
$oldPage=$this->pages[$pageName];
                
$replace=$this->limiter[0].$valueName.$this->limiter[1];
                
$this->pages[$pageName] = str_replace($replace,
                                                      
$values,
                                                      
$oldPage);
                if (
$this->pages[$pageName] == $oldPage)
                    {
                    
trigger_error (" Template variable $valueName does not exist "E_USER_WARNING);
                    }
                }
            }
        else
            {
            
trigger_error (" Page \"$pageName\" does not excist "E_USER_WARNING);
            }
        }

    
/**
     *Insert content of a file into a page.
     * @pre  $pageName has a variable $valueName
     * @post  File $filename has been inserted into $pageName.
     * @params $pageName Name of page to modify.
     * @params $filename Name of file to import.
     * @params $valueName name of variabel in $pageName to insert $filename at.
     * @return boolean value "$filename has been successfully imported"
     * @since 1.0.0
     * @access public
     * @prototype bool insertFile(string pageName, string filename, string valueName)
     */
    
function insertFile($pageName,$filename,$valueName)
        {
        if (
is_file($this->baseDir.$filename))
            {
            
$file=@implode("", @file($this->baseDir.$filename));
            if (!
$file)
                {
                
trigger_error (" Failed to import specified file \"$filename\" "E_USER_WARNING);
                return 
false;
                }
            
$this->insertVal($pageName,$file,$valueName);
            return 
true;
            }
        else
            {
            
trigger_error (" Failed to import specified file \"$filename\" "E_USER_WARNING);
            return 
false;
            }
        }

    
/**
     *Merge two pages.
     * @pre  $pageName1 has a variable $valueName
     * @post  $pageName2 has been inserted into $pageName1. $pageName2 remains unaltered
     * @params $pageName1 Name of page to insert $pageName2 into
     * @params $pageName2 Name of page to insert into $pageName1 or associative
     *                    array where the key is the insertion point, and the value
     *                    is the page to insert.
     * @params $valueName name of variabel in $pageName1 to insert $pageName2 at.
     *                    If $pageName2 is an array, this parameter is not used
     * @return boolean value "$pageName2 has been inserted into $pageName1"
     * @since 1.0.0
     * @access public
     * @prototype bool mergePages(string pageName1, mixed pageName2, string valueName)
     */
    
function mergePages($pageName1,$pageName2,$valueName='')
        {
        if (isset(
$this->pages[$pageName1]) && is_array($pageName2))
            {
            foreach (
$pageName2 as $key=>$value)
                {
                if (isset(
$this->pages[$value]))
                    {
                    
$replace=$this->limiter[0].$key.$this->limiter[1];
                    
$this->pages[$pageName1] = str_replace($replace,
                                                           
$this->pages[$value],
                                                           
$this->pages[$pageName1]);
                    }
                else
                    {
                    
$error=$value;
                    break;
                    }
                }
            }
        else if (isset(
$this->pages[$pageName1]) && isset($this->pages[$pageName2]))
            {
            
$replace=$this->limiter[0].$valueName.$this->limiter[1];
            
$this->pages[$pageName1] = str_replace($replace,
                                                   
$this->pages[$pageName2],
                                                   
$this->pages[$pageName1]);
            }
        else
            {
            
$error=$pageName2;
            }

        if (isset(
$error))
            {
            
trigger_error (" Page \"$pageName1\" or page \"$error\" does not excist "E_USER_WARNING);
            return 
false;
            }
        }

    
/**
     *Read in file specified in parameter, and parse it according to give criterias
     *
     *This function parses the content of a file, and generates a complete page from
     *it's content, based on a set of template tags in the file.
     *This tags are delimited by start |% and end %|. There are five allowed tags you
     *can use. They are:
     * |%template%|,
     * |%page%|,
     * |%append%|,
     * |%var%| and
     * |%recursTmpl%|
     *and they are roughly equivilant to the ezetemplate functions
     * addTemplate(),
     * makePage(),
     * appendToPage()
     * insertVar()
     *respectively, not including the last, which will be explained more later.
     *Usage of each tag is as follows:
     * |%template=nameOfTmpl%|value, can be filename or actual template|%/template%|
     *  OR
     * |%template=nameOfTmpl filename=nameOfFile%|
     *  - tmplName is the name you want to assign to that tempalte.
     *  - This tag requires an open and close tag, if the template is specified as a
     *    value.
     *  - If the filename attribute is specified, the specified file will be loaded
     *    into the template, and any value specified between the start and end tags will
     *    be appended to this template.
     * |%page=nameOfPage tmplname=nameOfTmpl%|
     *  OR
     * |%page=nameOfPage filename=nameOfFile%|
     *  - This simply creates a page, based on the give template or filename.
     *  - It is also possible to specify a page between a start and end |%page%| tag
     * |%append=nameOfTmpl pagename=nameOfPage%|
     *  - Appends the template nameOfTmpl to the page nameOfPage
     * |%var=nameOfVar pageName=nameOfPage%|variable value goes here.|%/var%|
     *  OR
     * |%var=nameOfVar pageName=nameOfPage filename=nameOfFile|
     *  - Inserts the values specified between the open and close tags, into the
     *    page nameOfPage, at insertion point {nameOfVar}
     *  - if filename is specified, the file is inserted.
     *  - If both filename and value is specified, the file is read and the value
     *    appended to the end of the value read from the file.
     * |%recursTmpl=nameOfFile%|
     *  - This allows recursiv processing of templatefiles.
     *  - You have to use this tag, before you refer to any page created in the
     *    "subtemplatefile", but, you CAN create a page in your current templatefile,
     *    and populate it with values in your "subtemplatefile".
     *
     * @params string $file Filename of file to parse
     * @since 1.2.1
     * @access public
     * @prototype void parseTemplate(file)
     */
    
function parseTemplate($file)
        {
        if (!
is_file($this->baseDir.$file))
            {
            
trigger_error ("The specfied file \"$file\" could not be found "E_USER_WARNING);
            return;
            }

        
//Do initial parsing
        
if (is_file($this->baseDir.$file))
            {
            
$tmplFile=implode('',file($this->baseDir.$file));
            
$tmplArr=array_values(array_filter(preg_split('/\|\%|\%\|/'$tmplFile), 'removeEmpty'));

            while (
count($tmplArr))
                {
                
$attributes=preg_split('/\s+|=/'array_shift($tmplArr));
                switch (
$attributes[0])
                    {
                    case 
'template':
                        
$this->_parseTemplateTag($tmplArr,
                                                 
$this->_parseAttribs($attributes));
                        break;
                    case 
'page':
                        
$this->_parsePageTag($tmplArr,
                                             
$this->_parseAttribs($attributes));
                        break;
                    case 
'append':
                        
$this->_parseAppendTag($tmplArr,
                                               
$this->_parseAttribs($attributes));
                        break;
                    case 
'var':
                        
$this->_parseVarTag($tmplArr,
                                            
$this->_parseAttribs($attributes));
                        break;
                    case 
'recurseTmpl':
                        
$this->_parseRecursTmplTag($tmplArr,
                                                   
$this->_parseAttribs($attributes));
                        break;
                    default:
                        
$errorMsg "Undefined tag $attributes[0] in template file: ";
                        
$errorMsg .= $this->baseDir.$file;
                        
trigger_error ($errorMsg,
                                       
E_USER_WARNING);
                    }
                }
            }
        else
            {
            
trigger_error ('File not found in '.$this->baseDir.$file.' 'E_USER_WARNING);
            }
        }

    
/**
     * Short description - one line
     *
     * @param array $attributes Array containing the parsed tag.
     * @since 1.2.1
     * @return Hash array.
     * @access private
     * @prototype _parseAttribs(array attributes)
     */
    
function _parseAttribs($attributes)
        {
        if (!(
count($attributes)%2))
            {
            
$attrib=array();
            for (
$counter=0$counter<count($attributes);)
                {
                
$attrib[strtolower($attributes[$counter])]=$attributes[$counter+1];
                
$counter+=2;
                }
            return 
$attrib;
            }
        else
            {
            
trigger_error ("Missing attributes in $attributes[0] tag"E_USER_WARNING);
            }
        }

    
/**
     * Parses the template tag extracted in parseTemplate()
     *
     * @param array $tmplArr Array containing the entire parsed templatefile
     * @param array $attrib Array containing the parsed tag
     * @since 1.2.1
     * @access private
     * @prototype _parseTemplateTag(array refference tmplArr, array attributes)
     */
    
function _parseTemplateTag(&$tmplArr$attrib)
        {
        
//if template is specified by value
        
$value='';
        if (
count($tmplArr) > && '/template' == $tmplArr[1])
            {
            
$value=array_shift($tmplArr);
            
array_shift($tmplArr); //Discard closing tag
            
}
        else if (
count($tmplArr) >= && '/template' == $tmplArr[0])
            {
            
array_shift($tmplArr); //Discard closing tag
            
}

        if (isset(
$attrib['filename']))
            {
            
$this->addTemplate($attrib['template'], $attrib['filename']);
            
$this->templates[$attrib['template']].=$value;
            }
        else
            {
            
$this->addTemplate($attrib['template'], $value);
            }
        }

    
/**
     * Parses the page tag extracted in parseTemplate()
     *
     * @param array $tmplArr Array containing the entire parsed templatefile
     * @param array $attrib Array containing the parsed tag
     * @since 1.2.1
     * @access private
     * @prototype _parsePageTag(array refference tmplArr, array attrib)
     */
    
function _parsePageTag(&$tmplArr$attrib)
        {
        
//if template is specified by value
        
$value='';
        if (
count($tmplArr) > && '/page' == $tmplArr[1])
            {
            
$value=array_shift($tmplArr);
            
array_shift($tmplArr); //Discard closing tag
            
}
        else if (
count($tmplArr) >= && '/page' == $tmplArr[0])
            {
            
array_shift($tmplArr); //Discard closing tag
            
}

        if (isset(
$attrib['filename']))
            {
            
$this->makePage($attrib['page'], $attrib['filename'],true);
            }
        else if (isset(
$attrib['tmplname']))
            {
            
$this->makePage($attrib['page'], $attrib['tmplname']);
            }
        else
            {
            
$this->pages[$attrib['page']]=$value;
            }
        }

    
/**
     * Parses the append tag extracted in parseTemplate()
     *
     * @param array $tmplArr Array containing the entire parsed templatefile
     * @param array $attrib Array containing the parsed tag
     * @since 1.2.1
     * @access private
     * @prototype _parseVarTag(array refference tmplArr, array attrib)
     */
    
function _parseAppendTag(&$tmplArr$attrib)
        {
        if (isset(
$attrib['isfile']))
            {
            
$this->appendToPage($attrib['pagename'], $attrib['append'], true);
            }
        else
            {
            
$this->appendToPage($attrib['pagename'], $attrib['append']);
            }
        }

    
/**
     * Parses the var tag extracted in parseTemplate()
     *
     * @param array $tmplArr Array containing the entire parsed templatefile
     * @param array $attrib Array containing the parsed tag
     * @since 1.2.1
     * @access private
     * @prototype _parseVarTag(array refference tmplArr, array attrib)
     */
    
function _parseVarTag(&$tmplArr$attrib)
        {
        
//if template is specified by value
        
$value='';
        if (
count($tmplArr) > && '/var' == $tmplArr[1])
            {
            
$value=array_shift($tmplArr);
            
array_shift($tmplArr); //Discard closing tag
            
}
        else if (
count($tmplArr) >= && '/var' == $tmplArr[0])
            {
            
array_shift($tmplArr); //Discard closing tag
            
}

        if (isset(
$attrib['filename']))
            {
            if (
'' == $value)
                {
                
$this->insertFile($attrib['pagename'], $attrib['filename'], $attrib['var']);
                }
            else
                {
                if (
is_file($this->baseDir.$attrib['filename']))
                    {
                    
$file=implode('',file($this->baseDir.$attrib['filename']));
                    
$file.=$value;
                    
$this->insertVal($attrib['pagename'], $file$attrib['var']);
                    }
                else
                    {
                    
trigger_error ("Specified file not found in |%var%| tag"E_USER_WARNING);
                    }
                }
            }
        else
            {
            
$this->insertVal($attrib['pagename'], $value$attrib['var']);
            }
        }

    
/**
     * Parses the recurseTmpl tag extracted in parseTemplate()
     *
     * @param array $tmplArr Array containing the entire parsed templatefile
     * @param array $attrib Array containing the parsed tag
     * @since 1.2.1
     * @access private
     * @prototype _parseTemplateTag(array refference tmplArr, array attrib)
     */
    
function _parseRecursTmplTag(&$tmplArr$attrib)
        {
        if (
'' != $attrib['recursetmpl'])
            {
            
$this->parseTemplate($attrib['recursetmpl']);
            }
        else
            {
            
trigger_error ("No filename specified in recurseTmpl tag."E_USER_WARNING);
            }
        }

    
/**
     *output page specified in parameter.
     * @params string $pageName Name of page to output.
     * @return boolean value "$pageName exists"
     * @since 1.0.0
     * @access public
     * @prototype bool printPage(string pageName)
     */
    
function printPage($pageName)
        {
        if (isset(
$this->pages[$pageName]))
            {
            echo 
$this->pages[$pageName];
            return 
true;
            }
        else
            {
            
trigger_error (" Page \"$pageName\" does not excist "E_USER_WARNING);
            return 
false;
            }
        }

    
/**
     *Return page to caller of function.
     * @params string $pageName Name of page to output.
     * @return page reffenced by $pageName, or false if it does not excist.
     * @since 1.0.0
     * @access public
     * @prototype string getPage(string pageName)
     */
    
function getPage($pageName)
        {
        if (isset(
$this->pages[$pageName]))
            {
            return 
$this->pages[$pageName];
            }
        else
            {
            
trigger_error (" Page \"$pageName\" does not excist "E_USER_WARNING);
            return 
false;
            }
        }

    
/**
     *Specify which characters identify a variable in a template
     * @post  $this->limiter has been set to new values, $left and $right.
     * @params string $left Left limiter
     * @params string $right Right limiter
     * @since 1.0.0
     * @access public
     * @prototype void setLimiter(string left, string right)
     */
    
function setLimiter($left,$right)
        {
        
$this->limiter=array($left,$right);
        }

    
/**
     *Check if a page is defined
     * @params string $name Page name
     * @since 1.0.0
     * @access public
     * @prototype bool exists(string name)
     */
    
function exists($name)
        {
        return isset(
$this->pages[$name]);
        }

    
/**
     *Sets and gets the base directory for the template object
     * @params string $dir Directory name
     * @since 1.2.2
     * @access public
     * @prototype string setBaseDir(string dir)
     */
    
function setBaseDir($dir)
        {
        
$this->baseDir=$dir;

        return 
$this->baseDir;
        }

    
/**
     *Gets the base directory for the template object.
     * @since 1.2.2
     * @access public
     * @prototype string getBaseDir()
     */
    
function getBaseDir()
        {
        return 
$this->baseDir;
        }

    }

//call_back function for array_filter
function removeEmpty($var)
    {
    if (
trim($var)=='')
        return 
false;
    else
        return 
true;
    }