openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit a388d0b8b2ed0dd88d978e4cae0f578f3a1757b7
parent d06b2b213a1412ee0359452bdd6e89a10d22a3d8
Author: Jan Dankert <devnull@localhost>
Date:   Fri,  3 Nov 2017 01:29:45 +0100

Fehlerhandling überarbeitet. Eine Exception wird im Dispatcher gefangen und ordentlich geloggt und als HTTP-Serverfehler gemeldet.

Diffstat:
action/Action.class.php | 47++++++++++++++++-------------------------------
dispatcher.php | 418++++++++++++++++++++++++++++++++++++++-----------------------------------------
themes/default/js/openrat.js | 129++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
util/TemplateEngine.class.php | 187+++++++++++++++++++++++++++++++++++++++++++------------------------------------
4 files changed, 435 insertions(+), 346 deletions(-)

diff --git a/action/Action.class.php b/action/Action.class.php @@ -348,22 +348,6 @@ class Action $db = db_connection(); -// if ( isset($this->actionConfig[$this->subActionName]['direct']) ) -// { -// if ( is_object( $db ) ) -// $db->commit(); -// exit; // Die Ausgabe ist bereits erfolgt (z.B. Bin�rdateien o. WebDAV) -// } - - // Pruefen, ob HTTP-Header gesendet wurden. Dies deutet stark darauf hin, dass eine - // PHP-Fehlermeldung ausgegeben wurde. In diesem Fall wird hier abgebrochen. - // Weitere Ausgabe wuerde keinen Sinn machen, da wir nicht wissen, was - // passiert ist. -// if ( headers_sent() ) -// { -// Http::serverError("Some server error messages occured - see above - CMS canceled."); -// } - if ( is_object( $db ) ) $db->commit(); @@ -476,25 +460,26 @@ class Action if ( $conf['theme']['compiler']['enable'] ) { - $te = new TemplateEngine(); - $te->compile( $tplName ); - unset($te); + try + { + $te = new TemplateEngine(); + $te->compile( $tplName ); + unset($te); + } + catch (Exception $e) + { + throw new DomainException("Template compilation failed",0,$e ); + } } $iFile = FileUtils::getTempDir().'/'.'or.cache.tpl.'.str_replace('/', '.',$tplName).'.tpl.'.PHP_EXT;; + header("X-CMS-Template-File: ".$iFile); - //try - //{ - if ( is_file($iFile)) - // Einbinden des Templates - require_once( $iFile ); - else - echo Http::serverError("File not found: $iFile","Template not found: $iFile"); - //} - //catch( Exception $e ) - //{ - // echo "Error occured:".$e; - //} + if ( is_file($iFile)) + // Einbinden des Templates + require_once( $iFile ); + else + throw new LogicException("File not found: $iFile"); } diff --git a/dispatcher.php b/dispatcher.php @@ -23,233 +23,219 @@ // Statische Resourcen (CSS,JS,Bilder,...) gehen nicht über diesen Dispatcher, sondern werden // direkt geladen. -require_once( 'init.php' ); - - -// Werkzeugklassen einbinden. -require_once( OR_OBJECTCLASSES_DIR ."include.inc.".PHP_EXT ); -require_once( OR_TEXTCLASSES_DIR ."include.inc.".PHP_EXT ); - -// Datenbank-Funktionen einbinden. -require_once( OR_DBCLASSES_DIR."include.inc.".PHP_EXT ); - -// Jetzt erst die Sitzung starten (nachdem alle Klassen zur Verfügung stehen). -session_start(); -require_once( OR_SERVICECLASSES_DIR."Session.class.".PHP_EXT ); - -// Vorhandene Konfiguration aus der Sitzung lesen. -$conf = Session::getConfig(); - -// Konfiguration lesen. -// Wenn Konfiguration noch nicht in Session vorhanden oder die Konfiguration geändert wurde (erkennbar anhand des Datei-Datums) -// dann die Konfiguration neu einlesen. -if ( !is_array( $conf ) || $conf['config']['auto_reload'] && Preferences::lastModificationTime()>$conf['config']['last_modification'] ) -{ - // Da die Konfiguration neu eingelesen wird, sollten wir auch die Sitzung komplett leeren. - if ( is_array($conf) && $conf['config']['session_destroy_on_config_reload'] ) - session_unset(); - - $conf = Preferences::load(); - - $conf['build'] = parse_ini_file('build.ini'); - // Sprache lesen - - if ( $conf['i18n']['use_http'] ) - // Die vom Browser angeforderten Sprachen ermitteln - $languages = Http::getLanguages(); - else - // Nur Default-Sprache erlauben - $languages = array(); - - if ( isset($_COOKIE['or_language']) ) - $languages = array($_COOKIE['or_language']) + $languages; - - // Default-Sprache hinzufuegen. - // Wird dann verwendet, wenn die vom Browser angeforderten Sprachen - // nicht vorhanden sind - $languages[] = $conf['i18n']['default']; - $available = explode(',',$conf['i18n']['available']); - - foreach( $languages as $l ) - { - if ( !in_array($l,$available) ) - continue; - - // Pruefen, ob Sprache vorhanden ist. - $langFile = OR_LANGUAGE_DIR.$l.'.ini.'.PHP_EXT; - - if ( !file_exists( $langFile ) ) - Http::serverError("File does not exist: ".$langFile); - - $conf['language'] = parse_ini_file( $langFile ); - $conf['language']['language_code'] = $l; - break; - } - - - if ( !isset($conf['language']) ) - Http::serverError('no language found! (languages='.implode(',',$languages).')' ); - - // Schreibt die Konfiguration in die Sitzung. Diese wird anschliessend nicht - // mehr veraendert. - Session::setConfig( $conf ); -} - -// Nachdem die Konfiguration gelesen wurde, kann nun der Logger benutzt werden. -require_once( OR_SERVICECLASSES_DIR."Logger.class.".PHP_EXT ); - -if ( !empty($conf['security']['umask']) ) - umask( octdec($conf['security']['umask']) ); - -if ( !empty($conf['interface']['timeout']) ) - set_time_limit( intval($conf['interface']['timeout']) ); - -if ( config('security','use_post_token') && $_SERVER['REQUEST_METHOD'] == 'POST' && @$REQ[REQ_PARAM_TOKEN]!=token() ) -{ - Logger::error('Token mismatch: Needed '.token().' but got '.@$REQ[REQ_PARAM_TOKEN].'. Maybe an attacker?'); - Http::notAuthorized("Token mismatch","Token mismatch"); -} - - -define('FILE_SEP',$conf['interface']['file_separator']); - -define('TEMPLATE_DIR',OR_THEMES_DIR.$conf['interface']['theme'].'/templates'); -define('CSS_DIR' ,OR_THEMES_DIR.$conf['interface']['theme'].'/css' ); -define('IMAGE_DIR' ,OR_THEMES_DIR.$conf['interface']['theme'].'/images' ); -define('DEVELOPMENT' ,!$conf['production']); - -require_once( "functions/config.inc.php" ); -require_once( "functions/language.inc.".PHP_EXT ); -require_once( "functions/db.inc.".PHP_EXT ); - -// Verbindung zur Datenbank -// -$db = Session::getDatabase(); -if ( is_object( $db ) ) -{ - $ok = $db->connect(); - if ( !$ok ) - Http::sendStatus('503','Service Unavailable','Database is not available: '.$db->error); - - Session::setDatabase( $db ); - $db->start(); -} - -if ( !empty($REQ[REQ_PARAM_ACTION]) ) - $action = $REQ[REQ_PARAM_ACTION]; -else - Http::serverError("no action supplied"); - //$action = 'login'; - -if ( !empty( $REQ[REQ_PARAM_SUBACTION] ) ) - $subaction = $REQ[REQ_PARAM_SUBACTION]; -else -{ - Http::serverError("no method (subaction) supplied"); -} - -require( OR_ACTIONCLASSES_DIR.'/Action.class.php' ); -require( OR_ACTIONCLASSES_DIR.'/ObjectAction.class.php' ); - - -$actionClassName = ucfirst($action).'Action'; - -require_once( OR_ACTIONCLASSES_DIR.'/'.$actionClassName.'.class.php' ); - -// Erzeugen der Action-Klasse try { - $do = new $actionClassName; + require_once( 'init.php' ); + + + // Werkzeugklassen einbinden. + require_once( OR_OBJECTCLASSES_DIR ."include.inc.".PHP_EXT ); + require_once( OR_TEXTCLASSES_DIR ."include.inc.".PHP_EXT ); + + // Datenbank-Funktionen einbinden. + require_once( OR_DBCLASSES_DIR."include.inc.".PHP_EXT ); + + // Jetzt erst die Sitzung starten (nachdem alle Klassen zur Verfügung stehen). + session_start(); + require_once( OR_SERVICECLASSES_DIR."Session.class.".PHP_EXT ); + + // Vorhandene Konfiguration aus der Sitzung lesen. + $conf = Session::getConfig(); + + // Konfiguration lesen. + // Wenn Konfiguration noch nicht in Session vorhanden oder die Konfiguration geändert wurde (erkennbar anhand des Datei-Datums) + // dann die Konfiguration neu einlesen. + if ( !is_array( $conf ) || $conf['config']['auto_reload'] && Preferences::lastModificationTime()>$conf['config']['last_modification'] ) + { + // Da die Konfiguration neu eingelesen wird, sollten wir auch die Sitzung komplett leeren. + if ( is_array($conf) && $conf['config']['session_destroy_on_config_reload'] ) + session_unset(); + + $conf = Preferences::load(); + + $conf['build'] = parse_ini_file('build.ini'); + // Sprache lesen + + if ( $conf['i18n']['use_http'] ) + // Die vom Browser angeforderten Sprachen ermitteln + $languages = Http::getLanguages(); + else + // Nur Default-Sprache erlauben + $languages = array(); + + if ( isset($_COOKIE['or_language']) ) + $languages = array($_COOKIE['or_language']) + $languages; + + // Default-Sprache hinzufuegen. + // Wird dann verwendet, wenn die vom Browser angeforderten Sprachen + // nicht vorhanden sind + $languages[] = $conf['i18n']['default']; + $available = explode(',',$conf['i18n']['available']); + + foreach( $languages as $l ) + { + if ( !in_array($l,$available) ) + continue; + + // Pruefen, ob Sprache vorhanden ist. + $langFile = OR_LANGUAGE_DIR.$l.'.ini.'.PHP_EXT; + + if ( !file_exists( $langFile ) ) + Http::serverError("File does not exist: ".$langFile); + + $conf['language'] = parse_ini_file( $langFile ); + $conf['language']['language_code'] = $l; + break; + } + + + if ( !isset($conf['language']) ) + Http::serverError('no language found! (languages='.implode(',',$languages).')' ); + + // Schreibt die Konfiguration in die Sitzung. Diese wird anschliessend nicht + // mehr veraendert. + Session::setConfig( $conf ); + } + + // Nachdem die Konfiguration gelesen wurde, kann nun der Logger benutzt werden. + require_once( OR_SERVICECLASSES_DIR."Logger.class.".PHP_EXT ); + + if ( !empty($conf['security']['umask']) ) + umask( octdec($conf['security']['umask']) ); + + if ( !empty($conf['interface']['timeout']) ) + set_time_limit( intval($conf['interface']['timeout']) ); + + if ( config('security','use_post_token') && $_SERVER['REQUEST_METHOD'] == 'POST' && @$REQ[REQ_PARAM_TOKEN]!=token() ) + { + Logger::error('Token mismatch: Needed '.token().' but got '.@$REQ[REQ_PARAM_TOKEN].'. Maybe an attacker?'); + Http::notAuthorized("Token mismatch","Token mismatch"); + } + + + define('FILE_SEP',$conf['interface']['file_separator']); + + define('TEMPLATE_DIR',OR_THEMES_DIR.$conf['interface']['theme'].'/templates'); + define('CSS_DIR' ,OR_THEMES_DIR.$conf['interface']['theme'].'/css' ); + define('IMAGE_DIR' ,OR_THEMES_DIR.$conf['interface']['theme'].'/images' ); + define('DEVELOPMENT' ,!$conf['production']); + + require_once( "functions/config.inc.php" ); + require_once( "functions/language.inc.".PHP_EXT ); + require_once( "functions/db.inc.".PHP_EXT ); + + // Verbindung zur Datenbank + // + $db = Session::getDatabase(); + if ( is_object( $db ) ) + { + $ok = $db->connect(); + if ( !$ok ) + Http::sendStatus('503','Service Unavailable','Database is not available: '.$db->error); + + Session::setDatabase( $db ); + $db->start(); + } + + if ( !empty($REQ[REQ_PARAM_ACTION]) ) + $action = $REQ[REQ_PARAM_ACTION]; + else + Http::serverError("no action supplied"); + + if ( !empty( $REQ[REQ_PARAM_SUBACTION] ) ) + $subaction = $REQ[REQ_PARAM_SUBACTION]; + else + { + Http::serverError("no method (subaction) supplied"); + } + + require( OR_ACTIONCLASSES_DIR.'/Action.class.php' ); + require( OR_ACTIONCLASSES_DIR.'/ObjectAction.class.php' ); + + + $actionClassName = ucfirst($action).'Action'; + + require_once( OR_ACTIONCLASSES_DIR.'/'.$actionClassName.'.class.php' ); + + // Erzeugen der Action-Klasse + try + { + $do = new $actionClassName; + } + catch( ObjectNotFoundException $e ) + { + Logger::debug( "Object not found: ".$e->__toString() ); + Http::noContent(); + } + + $do->actionClassName = $actionClassName; + $do->actionName = $action; + $do->subActionName = $subaction; + + $do->init(); + + + switch( @$do->security ) + { + case SECURITY_GUEST: + // Ok. + break; + case SECURITY_USER: + if ( !is_object($do->currentUser) ) + throw new SecurityException('No user logged in, but this action requires a valid user'); + break; + case SECURITY_ADMIN: + if ( !is_object($do->currentUser) || !$do->currentUser->isAdmin ) + throw new SecurityException('This action requires administration privileges, but user '.$do->currentUser->name.' is not an admin'); + break; + default: + } + + + + $isAction = $_SERVER['REQUEST_METHOD'] == 'POST'; + + if ( $isAction ) + { + // POST-Request => ...Post() wird aufgerufen. + $subactionMethodName = $subaction.'Post'; + } + else + { + // GET-Request => ...View() wird aufgerufen. + $subactionMethodName = $subaction.'View'; + // Daten werden nur angezeigt, die Sitzung kann also schon geschlossen werden. + if ( $action != 'index' ) // In Index wird die Perspektive manipuliert. + Session::close(); + } + + Logger::debug("Executing $action/$subaction/".@$REQ[REQ_PARAM_ID]); + + if ( ! method_exists($do,$subactionMethodName) ) + Http::noContent(); + + // Jetzt wird die Aktion aus der Actionklasse aufgerufen. + $do->$subactionMethodName(); + + $do->forward(); } catch( ObjectNotFoundException $e ) { - Logger::debug( "Object not found: ".$e->__toString() ); + Logger::warn( "Object not found: ".$e->__toString() ); // Nur Debug, da dies bei gelöschten Objekten vorkommen kann. Http::noContent(); -} -catch( Exception $e ) -{ - Http::serverError($e->getMessage(),$e->getTraceAsString() ); -} - -$do->actionClassName = $actionClassName; -$do->actionName = $action; -$do->subActionName = $subaction; - -$do->init(); - - -switch( @$do->security ) -{ - case SECURITY_GUEST: - // Ok. - break; - case SECURITY_USER: - if ( !is_object($do->currentUser) ) - { - Logger::debug('No user logged in, but this action requires a valid user'); - Http::notAuthorized( lang('SESSION_EXPIRED'),'login required' ); - $do->templateVars['error'] = 'not logged in'; - exit; - } - break; - case SECURITY_ADMIN: - if ( !is_object($do->currentUser) || !$do->currentUser->isAdmin ) - { - Logger::debug('This action requires administration privileges, but user '.$do->currentUser->name.' is not an admin'); - Http::notAuthorized( lang('SESSION_EXPIRED'),'intrusion detection' ); - $do->templateVars['error'] = 'no admin'; - exit; - } - break; - default: - Http::notAuthorized( lang('SESSION_EXPIRED'),'no security information for this action' ); -} - - - -$isAction = $_SERVER['REQUEST_METHOD'] == 'POST'; - -if ( $isAction ) -{ - // POST-Request => ...Post() wird aufgerufen. - $subactionMethodName = $subaction.'Post'; -} -else -{ - // GET-Request => ...View() wird aufgerufen. - $subactionMethodName = $subaction.'View'; - // Daten werden nur angezeigt, die Sitzung kann also schon geschlossen werden. - if ( $action != 'index' ) // In Index wird die Perspektive manipuliert. - Session::close(); -} - -Logger::debug("Executing $action/$subaction/".@$REQ[REQ_PARAM_ID]); - -if ( ! method_exists($do,$subactionMethodName) ) - Http::noContent(); - -// Jetzt wird die Aktion aus der Actionklasse aufgerufen. -try -{ - $do->$subactionMethodName(); -} -catch( ObjectNotFoundException $e ) -{ - Logger::debug( $e->__toString() ); // Nur Debug, da dies bei gelöschten Objekten vorkommen kann. - Http::noContent(); -} -catch( RuntimeException $e ) -{ - Logger::warn( $e->__toString() ); - Http::serverError($e->__toString() ); } catch( OpenRatException $e ) { - Logger::warn( $e->__toString() ); Http::serverError( lang($e->key),$e->__toString()); +} +catch( SecurityException $e ) +{ + Http::notAuthorized("You are not allowed to execute this action."); } - -$do->forward(); +catch( Exception $e ) +{ + Http::serverError( "Internal CMS error",$e->__toString() ); +} // fertig :) ?> \ No newline at end of file diff --git a/themes/default/js/openrat.js b/themes/default/js/openrat.js @@ -432,8 +432,10 @@ function loadView(contentEl,action,method,id,params ) if ( status == "error" ) { // Seite nicht gefunden. - $(this).html(""); - $(this).removeClass("loader"); + $(targetEl).html(""); + $(targetEl).removeClass("loader"); + + notify('error',response); // OK-button Ausblenden. //$(targetEl).closest('div.panel').find('div.bottom > div.command > input').addClass('invisible'); // var msg = "Sorry but there was an error: "; @@ -441,7 +443,7 @@ function loadView(contentEl,action,method,id,params ) return; } - $(this).removeClass("loader"); + $(targetEl).removeClass("loader"); registerViewEvents( targetEl ); }); } @@ -629,7 +631,7 @@ function registerViewEvents( viewEl ) $(viewEl).find('textarea').orAutoheight(); // Autosave in Formularen. Bei Veränderungen wird das Formular sofort abgeschickt. - $(viewEl).find('form.[data-autosave="true"] input[type="checkbox"]').click( function() { + $(viewEl).find('form[data-autosave="true"] input[type="checkbox"]').click( function() { formSubmit( $(this).closest('form') ); }); @@ -637,11 +639,103 @@ function registerViewEvents( viewEl ) $(viewEl).find('input').change( function() { $(this).closest('div.panel').find('ul.views li.action.active').addClass('dirty'); }); + + + + + var form = $(viewEl).find('form'); + + // Dateiupload über Drag and Drop + var dropzone = $(viewEl).find('div.filedropzone > div.input'); + dropzone.on('dragenter', function (e) + { + e.stopPropagation(); + e.preventDefault(); + $(this).css('border', '1px dotted gray'); + }); + dropzone.on('dragover', function (e) + { + e.stopPropagation(); + e.preventDefault(); + }); + dropzone.on('drop', function (e) + { + $(this).css('border','1px dotted red'); + e.preventDefault(); + var files = e.originalEvent.dataTransfer.files; + + //We need to send dropped files to Server + handleFileUpload(form,files); + }); + + + // Dateiupload über File-Input-Button + $(viewEl).find('input[type=file]').change( function() { + + var files = $(this).prop('files'); + + handleFileUpload(form,files); + }); + + // QR-Code anzeigen. + $(viewEl).find('[data-qrcode]').each( function() { + + var qrcodetext = $(this).attr('data-qrcode'); + $(this).removeAttr('data-qrcode'); + + $(this).qrcode( { render : 'div', + text : qrcodetext, + fill : 'currentColor' } ); + } ); } +function handleFileUpload(form,files) +{ + for (var i = 0, f; f = files[i]; i++) + { + var form_data = new FormData(); + form_data.append('file' , f); + form_data.append('action' ,'folder'); + form_data.append('subaction','createfile'); + form_data.append('output' ,'json'); + form_data.append('token' ,$(form).find('input[name=token]').val() ); + form_data.append('id' ,$(form).find('input[name=id]' ).val() ); + + var status = $('<div class="notice info"><div class="text loader"></div></div'); + $('#noticebar').prepend(status); // Notice anhängen. + $(status).show(); + + $.ajax( { 'type':'POST',url:'dispatcher.php', cache:false,contentType: false, processData: false, data:form_data, success:function(data, textStatus, jqXHR) + { + $(status).remove(); + doResponse(data,textStatus,form); + }, + error:function(jqXHR, textStatus, errorThrown) { + $(form).closest('div.content').removeClass('loader'); + $(status).remove(); + + var msg; + try + { + var error = jQuery.parseJSON( jqXHR.responseText ); + msg = error.error + '/' + error.description + ': ' + error.reason; + } + catch( e ) + { + msg = jqXHR.responseText; + } + + notify('error',msg); + } + + } ); + } +} + + function registerHeaderEvents() { // Links aktivieren... @@ -1116,16 +1210,7 @@ function formSubmit(form) msg = jqXHR.responseText; } - // Notice-Bar mit dieser Meldung erweitern. - var notice = $('<div class="notice error"><div class="text">'+msg+'</div></div'); - $('#noticebar').prepend(notice); // Notice anhängen. - notifyBrowser(msg); - - // Per Klick wird die Notice entfernt. - $(notice).fadeIn().click( function() - { - $(this).fadeOut('fast',function() { $(this).remove(); } ); - } ); + notify('error',msg); } @@ -1611,4 +1696,20 @@ function help(el,url,suffix) var method = $(el).closest('div.panel').find('li.action.active').attr('data-method'); window.open(url + action + '/'+ method + suffix, 'OpenRat_Help', 'location=no,menubar=no,scrollbars=yes,toolbar=no,resizable=yes'); +} + + +function notify( type,msg ) +{ + // Notice-Bar mit dieser Meldung erweitern. + var notice = $('<div class="notice '+type+'"><div class="text">'+msg+'</div></div'); + $('#noticebar').prepend(notice); // Notice anhängen. + notifyBrowser(msg); + + // Per Klick wird die Notice entfernt. + $(notice).fadeIn().click( function() + { + $(this).fadeOut('fast',function() { $(this).remove(); } ); + } ); + } \ No newline at end of file diff --git a/util/TemplateEngine.class.php b/util/TemplateEngine.class.php @@ -26,8 +26,6 @@ */ class TemplateEngine { - private $actualTagName = ''; - /** * Name Template. * @@ -52,77 +50,87 @@ class TemplateEngine */ public function compile( $tplName = '') { - if ( empty($tplName) ) - $tplName = $this->tplName; - - global $conf; - $confCompiler = $conf['theme']['compiler']; - - $srcXmlFilename = 'themes/default/templates/'.$tplName.'.tpl.src.xml'; - - - if ( is_file($srcXmlFilename) ) - $srcFilename = $srcXmlFilename; - else - // Wenn Vorlage (noch) nicht existiert - die( get_class($this).': Template not found: "'.$tplName.'"' ); - - $filename = FileUtils::getTempDir().'/'.'or.cache.tpl.'.str_replace('/', '.',$tplName).'.tpl.'.PHP_EXT; - - // Wenn Vorlage gaendert wurde, dann Umwandlung erneut ausf�hren. - if ( $confCompiler['cache'] && is_file($filename) && filemtime($srcFilename) <= filemtime($filename)) - return; - - if ( is_file($filename) && !is_writable($filename) ) - die( get_class($this).': File is read-only: '.$filename); - - Logger::debug("Compile template: ".$srcFilename.' to '.$filename); - - // Vorlage und Zieldatei oeffnen - $document = $this->loadDocument( $srcFilename ); - - $outFile = @fopen($filename,'w'); - - if ( !is_resource($outFile) ) - Http::serverError( get_class($this).': Unable to open file for writing: '.$filename); - - $openCmd = array(); - $depth = 0; - - foreach( $document as $line ) + try { + if ( empty($tplName) ) + $tplName = $this->tplName; + + global $conf; + $confCompiler = $conf['theme']['compiler']; + + $srcXmlFilename = 'themes/default/templates/'.$tplName.'.tpl.src.xml'; + + + if ( is_file($srcXmlFilename) ) + $srcFilename = $srcXmlFilename; + else + // Wenn Vorlage (noch) nicht existiert + throw new LogicException( "Template not found: $tplName" ); + + $filename = FileUtils::getTempDir().'/'.'or.cache.tpl.'.str_replace('/', '.',$tplName).'.tpl.'.PHP_EXT; + + // Wenn Vorlage gaendert wurde, dann Umwandlung erneut ausf�hren. + if ( $confCompiler['cache'] && is_file($filename) && filemtime($srcFilename) <= filemtime($filename)) + return; + + if ( is_file($filename) && !is_writable($filename) ) + throw new LogicException("File is read-only: $filename"); + + Logger::debug("Compile template: ".$srcFilename.' to '.$filename); + + // Vorlage und Zieldatei oeffnen + $document = $this->loadDocument( $srcFilename ); + + // Wir legen erstmal eine temporaere Datei an. + // Falls ein Fehler auftritt, ist nur die temporaere Datei defekt. + $tmpFilename = $filename.'.tmp'; + $outFile = @fopen($tmpFilename,'w'); + + if ( !is_resource($outFile) ) + throw new LogicException( "Template $tplName: Unable to open file for writing: $filename" ); + + $openCmd = array(); + $depth = 0; + + foreach( $document as $line ) + { + // Initialisieren der m�glichen Element-Inhalte + $type = ''; + $attributes = array(); + $value = ''; + $tag = ''; + + + // Setzt: $tag, $attributes, $value, $type + extract( $line ); + + if ($type == 'complete' || $type == 'open') + $attributes = $this->checkAttributes($tag,$attributes); + + if ( $type == 'open' ) + $this->copyFileContents( $tag,$outFile,$attributes,++$depth ); + elseif ( $type == 'complete' ) + { + $this->copyFileContents( $tag ,$outFile,$attributes,++$depth ); + $this->copyFileContents( $tag.'-end',$outFile,array() , $depth-- ); + } + elseif ( $type == 'close' ) + $this->copyFileContents( $tag.'-end',$outFile,array(),$depth-- ); + } + + fclose($outFile); + + rename($tmpFilename,$filename); + + // CHMOD ausfuehren. + if ( !empty($confCompiler['chmod'])) + if ( !@chmod($filename,octdec($confCompiler['chmod'])) ) + throw new InvalidArgumentException( "Template {$this->tplName} failed to compile: CHMOD '{$confCompiler['chmod']}' failed on file {$filename}." ); + + } + catch( Exception $e) { - // Initialisieren der m�glichen Element-Inhalte - $type = ''; - $attributes = array(); - $value = ''; - $tag = ''; - - - // Setzt: $tag, $attributes, $value, $type - extract( $line ); - - $this->actualTagName = $tag; - - if ($type == 'complete' || $type == 'open') - $attributes = $this->checkAttributes($tag,$attributes); - - if ( $type == 'open' ) - $this->copyFileContents( $tag,$outFile,$attributes,++$depth ); - elseif ( $type == 'complete' ) - { - $this->copyFileContents( $tag ,$outFile,$attributes,++$depth ); - $this->copyFileContents( $tag.'-end',$outFile,array() , $depth-- ); - } - elseif ( $type == 'close' ) - $this->copyFileContents( $tag.'-end',$outFile,array(),$depth-- ); + throw new LogicException("Template $tplName failed to compile", 0, $e); } - - fclose($outFile); - - // CHMOD ausfuehren. - if ( !empty($confCompiler['chmod'])) - if ( !@chmod($filename,octdec($confCompiler['chmod'])) ) - die( "CHMOD failed on file ".$filename ); } @@ -194,8 +202,8 @@ class TemplateEngine return $invert.'@$conf['."'".implode("'".']'.'['."'",$config_parts)."'".']'; default: - //Http::serverError( get_class($this).': Unknown type "'.$type.'" in attribute. Allowed: var|method|property|message|messagevar|config or none'); - } + throw new LogicException("Unknown type '$type' in attribute. Allowed: var|function|method|text|size|property|message|messagevar|arrayvar|config or none"); + } } @@ -214,7 +222,8 @@ class TemplateEngine return; else // Baustein nicht vorhanden, Abbbruch. - die( get_class($this).': Compile failed, file not found: '.$inFileName ); + + throw new LogicException("Compile failed, file not found: $inFileName" ); if ( DEVELOPMENT ) fwrite( $outFileHandler,"<!-- Compiling $infile @ ".date('r')." -->" ); @@ -326,7 +335,7 @@ class TemplateEngine $elements = parse_ini_file( OR_THEMES_DIR.$conf['interface']['theme'].'/include/elements.ini.'.PHP_EXT); if ( !isset($elements[$cmd]) ) - Http::serverError( get_class($this).': Parser error, unknown element "'.$cmd.'". Allowed: '.implode(',',array_keys($elements)) ); + throw new InvalidArgumentException("Parser error, unknown element $cmd. Allowed: "+implode(',',array_keys($elements)) ); $checkedAttr = array(); @@ -337,31 +346,39 @@ class TemplateEngine if ( $al=='') continue; // Leeres Attribut... nicht zu gebrauchen. - - $pair = explode(':',$al,2); - if ( count($pair) == 1 ) $pair[] = null; - list($a,$default) = $pair; - + $rpos = strrpos($al,':'); + if ($rpos===FALSE) + { + $a = $al; + $default = null; + } + else + { + $a = substr($al,0,$rpos); + $default = substr($al,$rpos+1); + } if ( is_string($default)) $default = str_replace('COMMA',',',$default); // Komma in Default-Werten ersetzen. if ( isset($attr[$a])) $checkedAttr[$a]=$attr[$a]; // Attribut ist bereits vorhanden, alles ok. elseif ( $default=='*') // Pflichtfeld! - die( get_class($this).': Element "'.$cmd.'" needs the required attribute "'.$a.'"' ); + throw new InvalidArgumentException( "Template {$this->tplName} failed to compile: Element {$cmd} needs the required attribute {$a}" ); elseif ( !is_null($default) ) $checkedAttr[$a]=$default; else ; - unset( $attr[$a] ); // Damit am Ende das Urprungsarray leer wird. + unset( $attr[$a] ); // Damit am Ende das Ursprungsarray leer wird. } + + unset( $attr['xmlns' ]); + unset( $attr['xmlns:xsi' ]); + unset( $attr['xsi:schemaLocation']); if ( count($attr) > 0 ) { - - //foreach($attr as $name=>$value) - //Http::serverError( get_class($this).': Unknown attribute "'.$name.'" in element "'.$cmd.'". Allowed: '.$elements[$cmd]."\n" ); + throw new InvalidArgumentException( "Template {$this->tplName} failed to compile: Unknown attributes '[".implode(',',array_keys($attr))."]' in element $cmd. Allowed attributes are: {$elements[$cmd]}" ); } return $checkedAttr;