00001 <?php
00002 #
00003 # DaCMS Content Management System
00004 # Copyright (C) 2002 Jan Dankert, jandankert@jandankert.de
00005 #
00006 # This program is free software; you can redistribute it and/or
00007 # modify it under the terms of the GNU General Public License
00008 # as published by the Free Software Foundation; either version 2
00009 # of the License, or (at your option) any later version.
00010 #
00011 # This program is distributed in the hope that it will be useful,
00012 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014 # GNU General Public License for more details.
00015 #
00016 # You should have received a copy of the GNU General Public License
00017 # along with this program; if not, write to the Free Software
00018 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00019 #
00020
00030 class TemplateEngine
00031 {
00032 var $actualTagName = '';
00033
00039 var $tplName;
00040
00046 function TemplateEngine( $tplName='' )
00047 {
00048 $this->tplName = $tplName;
00049 }
00050
00051
00056 function compile( $tplName = '')
00057 {
00058 if ( empty($tplName) )
00059 $tplName = $this->tplName;
00060
00061 global $conf;
00062 $srcOrmlFilename = 'themes/default/templates/'.$tplName.'.tpl.src.'.PHP_EXT;
00063 $srcXmlFilename = 'themes/default/templates/'.$tplName.'.tpl.src.xml';
00064
00065 if ( is_file($srcOrmlFilename) )
00066 $srcFilename = $srcOrmlFilename;
00067 elseif ( is_file($srcXmlFilename) )
00068 $srcFilename = $srcXmlFilename;
00069 else
00070
00071 die( get_class($this).': Template not found: "'.$tplName.'"' );
00072
00073 $filename = 'themes/default/pages/html/'.$tplName.'.tpl.'.PHP_EXT;
00074
00075
00076 if ( $conf['theme']['compiler']['cache'] && is_file($filename) && filemtime($srcFilename) <= filemtime($filename))
00077 return;
00078
00079 if ( is_file($filename) && !is_writable($filename) )
00080 die( get_class($this).': File is read-only: '.$filename);
00081
00082 Logger::debug("Compile template: ".$srcFilename.' to '.$filename);
00083
00084
00085 $document = $this->loadDocument( $srcFilename );
00086 $outFile = fopen($filename,'w');
00087
00088 if ( !is_resource($outFile) )
00089 die( get_class($this).': Unable to open file for writing: '.$filename);
00090
00091 $openCmd = array();
00092 $depth = 0;
00093
00094 foreach( $document as $line )
00095 {
00096
00097 $type = '';
00098 $attributes = array();
00099 $value = '';
00100 $tag = '';
00101
00102
00103
00104 extract( $line );
00105
00106 $this->actualTagName = $tag;
00107
00108 if ($type == 'complete' || $type == 'open')
00109 $attributes = $this->checkAttributes($tag,$attributes);
00110
00111 if ( $type == 'open' )
00112 $this->copyFileContents( $tag,$outFile,$attributes,++$depth );
00113 elseif ( $type == 'complete' )
00114 {
00115 $this->copyFileContents( $tag ,$outFile,$attributes,$depth+1 );
00116 $this->copyFileContents( $tag.'-end',$outFile,array() ,$depth+1 );
00117 }
00118 elseif ( $type == 'close' )
00119 $this->copyFileContents( $tag.'-end',$outFile,array(),--$depth );
00120 }
00121
00122 fclose($outFile);
00123
00124
00125 if ( !empty($conf['theme']['compiler']['chmod']))
00126 if ( !@chmod($filename,octdec($conf['theme']['compiler']['chmod'])) )
00127 die( "CHMOD failed on file ".$filename );
00128 }
00129
00130
00131
00132 function getElementValue( $elFilename,$attributes )
00133 {
00134 extract($attributes);
00135 require($elFilename);
00136 return $value;
00137 }
00138
00139
00140
00141 function attributeValue( $value )
00142 {
00143 $parts = explode( ':', $value, 2 );
00144
00145 if ( count($parts) < 2 )
00146 $parts = array('',$value);
00147
00148 list( $type,$value ) = $parts;
00149
00150 $invert = '';
00151 if ( substr($type,0,1)=='!' )
00152 {
00153 $type = substr($type,1);
00154 $invert = '! ';
00155 }
00156
00157 switch( $type )
00158 {
00159 case 'var':
00160 return $invert.'$'.$value;
00161 case 'text':
00162 case '':
00163
00164
00165
00166 if ( $value == 'true' || $value == 'false' )
00167 return $value;
00168 else
00169
00170 return "'".preg_replace('/{(\w+)\}/','\'.$\\1.\'',$value)."'";
00171 case 'method':
00172 return $invert.'$this->'.$value.'()';
00173 case 'size':
00174 return '@count($'.$value.')';
00175 case 'property':
00176 return $invert.'$this->'.$value;
00177 case 'message':
00178
00179
00180 return 'lang('."'".preg_replace('/{(\w+)\}/','\'.$\\1.\'',$value)."'".')';
00181 case 'messagevar':
00182 return 'lang($'.$value.')';
00183 case 'mode':
00184 return $invert.'$mode=="'.$value.'"';
00185 case 'arrayvar':
00186 list($arr,$key) = explode(':',$value.':none');
00187 return $invert.'@$'.$arr.'['.$key.']';
00188 case 'config':
00189 $config_parts = explode('/',$value);
00190 return $invert.'@$conf['."'".implode("'".']'.'['."'",$config_parts)."'".']';
00191
00192 default:
00193 die( get_class($this).': Unknown type "'.$type.'" in attribute. Allowed: var|method|property|message|messagevar|config or none');
00194 }
00195 }
00196
00197
00198
00202 function copyFileContents( $infile,$outFileHandler,$attr,$depth )
00203 {
00204 global $conf;
00205 $hash = $depth;
00206
00207 $inFileName = OR_THEMES_DIR.$conf['interface']['theme'].'/include/html/'.$infile.'.inc.'.PHP_EXT;
00208 $elFileName = OR_THEMES_DIR.$conf['interface']['theme'].'/include/html/'.$infile.'.el.' .PHP_EXT;
00209 if ( !is_file($inFileName) )
00210 if ( count($attr)==0 )
00211 return;
00212 else
00213
00214 die( get_class($this).': Compile failed, file not found: '.$inFileName );
00215
00216 $values = array();
00217 foreach( $attr as $attrName=>$attrValue )
00218 {
00219 $values[] = "'".$attrName."'=>".$this->attributeValue($attrValue);
00220 }
00221
00222 fwrite( $outFileHandler,'<?php ');
00223 foreach( $attr as $attrName=>$attrValue )
00224 fwrite( $outFileHandler,' $attr'.$hash.'_'.$attrName."=".$this->attributeValue($attrValue).'; ');
00225 fwrite( $outFileHandler,' ?>');
00226
00227 $file = file( $inFileName );
00228 $ignore = false;
00229 $linebreaks = true;
00230
00231 foreach( $file as $line )
00232 {
00233
00234 if ( strpos($line,'#IF-ATTR')!==FALSE )
00235 {
00236 $found = false;
00237 foreach( $attr as $attrName=>$attrValue )
00238 {
00239 if ( strpos($line,'#IF-ATTR '.$attrName)!==FALSE )
00240 $found = true;
00241 if ( strpos($line,'#IF-ATTR-VALUE '.$attrName.':'.$attrValue)!==FALSE )
00242 $found = true;
00243 }
00244 if ( ! $found )
00245 $ignore = true;
00246 }
00247
00248 if ( strpos($line,'#END-IF')!==FALSE )
00249 {
00250 $ignore = false;
00251 }
00252
00253 if ( strpos($line,'#ELSE')!==FALSE )
00254 {
00255 $ignore = !$ignore;
00256 }
00257
00258
00259 if ( strpos($line,'#SET-LINEBREAK-OFF')!==FALSE )
00260 $linebreaks = false;
00261
00262
00263 if ( strpos($line,'#SET-LINEBREAK-OFF')!==FALSE )
00264 $linebreaks = true;
00265
00266
00267
00268
00269 if ( $ignore )
00270 continue;
00271
00272
00273 if ( strlen(trim($line)) == 0)
00274 continue;
00275
00276 if ( in_array(substr(ltrim($line),0,2),array('//','/*','<!') ) || substr(ltrim($line),0,1) == '#')
00277 continue;
00278
00279 if ( !$linebreaks )
00280 $line = rtrim($line);
00281
00282
00283
00284 $line = str_replace('$attr','$attr'.$hash,$line);
00285 fwrite( $outFileHandler,$line );
00286 }
00287
00288
00289 fwrite( $outFileHandler,'<?php ');
00290 foreach( $attr as $attrName=>$attrValue )
00291 fwrite( $outFileHandler,'unset($attr'.$hash.'_'.$attrName.');');
00292 fwrite( $outFileHandler,' ?>');
00293
00294 if ( is_file($elFileName) )
00295 {
00296 fwrite( $outFileHandler, $this->getElementValue( $elFileName,$attr) );
00297 }
00298 }
00299
00300
00301
00308 function checkAttributes( $cmd,$attr )
00309 {
00310 global $conf;
00311 $elements = parse_ini_file( OR_THEMES_DIR.$conf['interface']['theme'].'/include/elements.ini.'.PHP_EXT);
00312
00313 if ( !isset($elements[$cmd]) )
00314 die( get_class($this).': Parser error, unknown element "'.$cmd.'". Allowed: '.implode(',',array_keys($elements)) );
00315
00316 $checkedAttr = array();
00317
00318
00319 foreach( explode(',',$elements[$cmd]) as $al )
00320 {
00321 $al=trim($al);
00322 if ( $al=='')
00323 continue;
00324
00325
00326 $pair = explode(':',$al,2);
00327 if ( count($pair) == 1 ) $pair[] = null;
00328 list($a,$default) = $pair;
00329
00330 if ( is_string($default))
00331 $default = str_replace('COMMA',',',$default);
00332
00333 if ( isset($attr[$a]))
00334 $checkedAttr[$a]=$attr[$a];
00335 elseif ( $default=='*')
00336 die( get_class($this).': Element "'.$cmd.'" needs the required attribute "'.$a.'"' );
00337 elseif ( !is_null($default) )
00338 $checkedAttr[$a]=$default;
00339 else
00340 ;
00341
00342 unset( $attr[$a] );
00343 }
00344
00345 if ( count($attr) > 0 )
00346 {
00347 foreach($attr as $name=>$value)
00348 die( get_class($this).': Unknown attribute "'.$name.'" in element "'.$cmd.'". Allowed: '.$elements[$cmd]."\n" );
00349 }
00350
00351 return $checkedAttr;
00352
00353 }
00354
00355
00359 function loadDocument( $filename )
00360 {
00361 if ( substr($filename,-4)=='.xml')
00362 return $this->loadXmlDocument( $filename );
00363 else
00364 return $this->loadOrmlDocument( $filename );
00365 }
00366
00367
00371 function loadXmlDocument( $filename )
00372 {
00373 $index = array();
00374 $vals = array();
00375 $p = xml_parser_create();
00376 xml_parser_set_option ( $p, XML_OPTION_CASE_FOLDING,false );
00377 xml_parser_set_option ( $p, XML_OPTION_SKIP_WHITE,false );
00378 xml_parse_into_struct($p, implode('',file($filename)), $vals, $index);
00379 xml_parser_free($p);
00380
00381 return $vals;
00382 }
00383
00384
00389 function loadOrmlDocument( $filename )
00390 {
00391 $vals = array();
00392
00393 $openCmd = array();
00394
00395 foreach( file($filename) as $line )
00396 {
00397 $indent = strlen($line)-strlen(ltrim($line));
00398 $line = trim($line);
00399
00400 if ( empty($line) )
00401 {
00402 continue;
00403 }
00404
00405 if ( substr($line,0,1)=='#' || substr($line,0,2)=='//')
00406 continue;
00407
00408 $openCmdCopy = $openCmd;
00409 krsort($openCmdCopy);
00410 foreach($openCmdCopy as $idx=>$ccmd)
00411 {
00412 if ( $idx >= $indent )
00413 {
00414 $vals[] = array( 'tag' =>$ccmd,
00415 'type' =>'close',
00416 'value' =>'',
00417 'attributes' => array(),
00418 'level' => $indent );
00419 unset($openCmd[$idx]);
00420 }
00421 }
00422
00423
00424 $li = explode(' ',$line);
00425 $attr = array();
00426 foreach( $li as $nr=>$part )
00427 {
00428 if ($nr==0)
00429 $cmd = $part;
00430 else
00431 {
00432 $el = explode(':',$part,2);
00433 if ( count($el) < 2 )
00434 die( 'parser error in line: '.$line );
00435
00436 list($a,$b) = $el;
00437 $attr[$a]=$b;
00438 }
00439
00440 }
00441
00442
00443
00444 $openCmd[$indent]=$cmd;
00445
00446 $vals[] = array( 'tag'=>$cmd,
00447 'type'=>'open',
00448 'value'=>'',
00449 'attributes'=>$attr,
00450 'level'=>$indent );
00451 }
00452
00453
00454 $openCmdCopy = $openCmd;
00455 krsort($openCmdCopy);
00456 foreach($openCmdCopy as $idx=>$ccmd)
00457 {
00458 $vals[] = array( 'tag'=>$ccmd,
00459 'type'=>'close',
00460 'value'=>'',
00461 'attributes'=>array(),
00462 'level'=>$indent );
00463
00464 unset($openCmd[$idx]);
00465 }
00466
00467
00468 return $vals;
00469 }
00470 }
00471
00472 ?>