diff --git a/classes/cronlog-renderer.class.php b/classes/cronlog-renderer.class.php index 0a730ca0260da8bf15d093f1ac265e446ce3faca..12348bfd08e3ed0039110bb2a4356a27cfed24d0 100644 --- a/classes/cronlog-renderer.class.php +++ b/classes/cronlog-renderer.class.php @@ -52,8 +52,8 @@ class cronlogrenderer extends cronlog{ } $iAge=round((date('U')-$iLast)/60); return '' - . 'Abruf: '.date("Y-m-d H:i:s").' min<br>' - . 'letzter Eintrag: vor '.$iAge.' min<br><br>' + . sprintf($this->t("request-time"), date("Y-m-d H:i:s")).'<br>' + . sprintf($this->t("last-entry"), $iAge).'<br><br>' ; } @@ -65,11 +65,6 @@ class cronlogrenderer extends cronlog{ * */ protected function _filterDatatable($sDatatable, $sFiltertext){ - /* - <a href="#" title="Filtere nach VHost «crawler.ascii.iml.unibe.ch:443»" - onclick="$('#tableRenderedallrequestsphp1_filter>INPUT').val('crawler.ascii.iml.unibe.ch:443'); - $('#tableRenderedallrequestsphp1').dataTable().fnFilter('crawler.ascii.iml.unibe.ch:443'); return false;">crawler.ascii.iml.unibe.ch:443</a> - */ return '$(\'#'.$sDatatable.'\').dataTable().fnFilter(\''.$sFiltertext.'\'); return false;'; } @@ -97,7 +92,16 @@ class cronlogrenderer extends cronlog{ // Array ( [SCRIPTNAME] => apt-get update [SCRIPTTTL] => 1440 [SCRIPTSTARTTIME] => 2016-06-21 06:00:02, 1466481602 [SCRIPTLABEL] => apt-get [SCRIPTENDTIME] => 2016-06-21 06:00:49, 1466481649 [SCRIPTEXECTIME] => 47 s [SCRIPTRC] => 0 ) foreach($aData as $sDtakey=>$aEntry){ if(!$sTblHead){ - foreach(array('Startzeit', 'Label', 'Server', 'Dauer', 'TTL', '$?', 'Expired', 'Status' /*, 'Aktionen'*/) as $sKey){ + foreach(array( + $this->t("col-starting-time"), + $this->t("col-label"), + $this->t("col-server"), + $this->t("col-duration"), + $this->t("col-ttl"), + $this->t("col-rc"), + $this->t("col-expired"), + $this->t("col-status"), + ) as $sKey){ $sTblHead.='<th>'.$sKey.'</th>'; } } @@ -115,25 +119,29 @@ class cronlogrenderer extends cronlog{ $sServerFromLogfile=preg_replace('/_.*/', '', basename($aEntry['logfile'])); if($sServerFromLogfile!=$aEntry['server']){ $aErrors[]=[ - 'Hostname?', - 'Der Hostname im Log ['.$sServerFromLogfile.'] stimmt nicht mit Servernamen ['.$aEntry['server'].'] überein.', + $this->t('error-host-in-log-differs-servername-label'), + sprintf($this->t('error-host-in-log-differs-servername-description'),$sServerFromLogfile, $aEntry['server']), ]; } if(!strstr($sServerFromLogfile, ".")){ $aErrors[]=[ - 'No FQDN', - 'Der Hostname im Log ['.$sServerFromLogfile.'] ist kein KFQDN.', + $this->t('error-no-fqdn-label'), + sprintf($this->t('error-no-fqdn-description'),$sServerFromLogfile), ]; } if($iNextRunErr < date("U")){ $aErrors[]=[ - 'Abgelaufen', - 'Job wurde nicht mehr gestartet oder kein Sync zum Logserver', + $this->t('error-expired-label'), + $this->t('error-expired-description'), ]; } if($aEntry['SCRIPTRC']>0){ + $aErrors[]=[ + sprintf($this->t('error-exitcode-label'), (int)$aEntry['SCRIPTRC']), + $this->t('error-exitcode-description'), + ]; $aErrors[]=[ 'Exitcode '.(int)$aEntry['SCRIPTRC'].' (<>0)', 'The command finished with a non error status' @@ -141,8 +149,8 @@ class cronlogrenderer extends cronlog{ } if(!$aEntry['SCRIPTLABEL']){ $aErrors[]=[ - 'Kein Label?', - 'No label was detected for this job. Check the scheduled cronjob on server.' + $this->t('error-no-label-label'), + $this->t('error-no-label-description'), ]; } @@ -165,14 +173,14 @@ class cronlogrenderer extends cronlog{ $sColStatus=''; if (count($aErrors)){ foreach($aErrors as $aErr){ - $sColStatus.='<li><abbr title="'.$aErr[1].'">FEHLER: '.$aErr[0].'</abbr></li>'; + $sColStatus.='<li><abbr title="'.$aErr[1].'">'.$this->t('status-error').': '.$aErr[0].'</abbr></li>'; } $sColStatus='<ul>'.$sColStatus.'</ul>'; } else { - $sColStatus.='OK'; + $sColStatus.=$this->t('status-ok'); } // render table of last logfile per cron job - $sHtml.='<tr onclick="showFile(\''.$aEntry['logfile'].'\');" title="Klick=['.$aEntry['logfile'].'] anzeigen ">' + $sHtml.='<tr onclick="showFile(\''.$aEntry['logfile'].'\');" title="'.sprintf($this->t('row-click-show-logfile'), $aEntry['logfile']).'">' . '<td>'.date("Y-m-d H:i:s", $aEntry['SCRIPTSTARTTIME']).'</td>' // . '<td>'.$aEntry['SCRIPTNAME'].'</td>' . '<td>'.$aEntry['SCRIPTLABEL'].'</td>' @@ -208,17 +216,17 @@ class cronlogrenderer extends cronlog{ <!-- START '.__METHOD__.' --> ' - . '<h3>Letztes Logfile pro Job</h3>' - . '<p class="hint">' - . 'Von jedem Cronjob kann man das jeweils letzte Log im Detail ansehen. Mit Klick in der Tabelle wird die Logdatei geöffnet.' - . '</p>' - . '<div>' + . '<h3>'.$this->t('logs-head').'</h3>' + . '<p class="hint">' + . $this->t('logs-hint') + . '</p>' + . '<div>' . $this->_renderAccessAndAge($iLast) - . ($iErrors ? '<a href="#" class="btn bg-danger" onclick="'.$this->_filterDatatable($sIdTable, "FEHLER").'">' . $iErrors.'</a> ' : '') - . ( $iOK ? '<a href="#" class="btn bg-success" onclick="'.$this->_filterDatatable($sIdTable, "OK").'">' . $iOK.'</a>' : '') - . ($iErrors && $iOK ? ' ... gesamt: <a href="#" class="btn bg-gray" onclick="'.$this->_filterDatatable($sIdTable, "").'">' . count($aData).'</a>' : '' ) + . ($iErrors ? '<a href="#" class="btn bg-danger" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-error')).'">' . $iErrors.'</a> ' : '') + . ( $iOK ? '<a href="#" class="btn bg-success" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-ok')).'">' . $iOK.'</a>' : '') + . ($iErrors && $iOK ? ' ... '.$this->t('total').': <a href="#" class="btn bg-gray" onclick="'.$this->_filterDatatable($sIdTable, "").'">' . count($aData).'</a>' : '' ) . '<br>' - . '</div>' + . '</div>' . '<table id="'.$sIdTable.'" class="table-striped">' . '<thead><tr>'.$sTblHead.'</tr></thead>' . '<tbody>' @@ -262,7 +270,7 @@ class cronlogrenderer extends cronlog{ $sReturn=''; $sServer=isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : false; $sReturn.='<li class="nav-item"><a class="nav-link nav-link" data-widget="pushmenu" href="#" role="button"><i class="fas fa-bars"></i></a></li>' - . '<li class="nav-item d-none d-sm-inline-block"><a href="#" class="nav-link">Instances:</a></li>'; + . '<li class="nav-item d-none d-sm-inline-block"><a href="#" class="nav-link">'.$this->t('instances').':</a></li>'; foreach($this->_aInstances as $sInstance => $sUrl){ $sHost=parse_url($sUrl, PHP_URL_HOST); @@ -297,7 +305,14 @@ class cronlogrenderer extends cronlog{ // job=dok-kvm-instances:host=kalium:start=1538358001:end=1538358001:exectime=0:ttl=60:rc=0 foreach($aData as $aEntry){ if(!$sTblHead){ - foreach(array('Startzeit', /*'Ende',*/ 'Label', 'Server', 'Dauer', 'TTL', '$?') as $sKey){ + foreach(array( + $this->t("col-starting-time"), + $this->t("col-label"), + $this->t("col-server"), + $this->t("col-duration"), + $this->t("col-ttl"), + $this->t("col-rc"), + ) as $sKey){ $sTblHead.='<th>'.$sKey.'</th>'; } } @@ -332,17 +347,16 @@ class cronlogrenderer extends cronlog{ $sHtml=' <!-- START '.__METHOD__.' --> ' - - . '<h3>History</h3>' - . '<p class="hint">' - . 'Von den gestarteten Cronjobs werden die Ausführungszeiten und deren Exitcode für 6 Tage aufgehoben.' - . '</p>' - . '<div>' + . '<h3>'.$this->t('history-head').'</h3>' + . '<p class="hint">' + . $this->t('history-hint') + . '</p>' + . '<div>' . $this->_renderAccessAndAge($iLast) - . 'gesamt: <strong>' . count($aData).'</strong>' - . ($iErrors ? ' (Fehler: <strong>' . $iErrors.'</strong>... OK: <strong>' . $iOK.'</strong>)' : '') + . $this->t('total').': <strong>' . count($aData).'</strong>' + . ($iErrors ? ' ('.$this->t('status-error').': <strong>' . $iErrors.'</strong>... '.$this->t('status-ok').': <strong>' . $iOK.'</strong>)' : '') . '<br><br>' - . '</div>' + . '</div>' . '<table id="'.$sIdTable.'">' . '<thead><tr>'.$sTblHead.'</tr></thead>' . '<tbody>' @@ -447,18 +461,16 @@ class cronlogrenderer extends cronlog{ <!-- START '.__METHOD__.' --> - <h3>Graph mit Timeline</h3> - <p class="hint"> - Aus der History der letzten 6 Tage werden die Cronjobs mit mehr als '.$this->_iMinTime4Timeline.' Sekunden Laufzeit dargestellt.<br> - So kann man ggf. Konflikte und Ungereimtheiten finden.<br> - Innerhalb der Timeline kann man mit dem Mausrad den Zoom verändern. - </p> - - <p> - Jobs mit mehr als '.$this->_iMinTime4Timeline.'s Laufzeit: <strong>'.count($aDataset).'</strong> (ges.: '.count($aData).')<br><br> - '. (count($aDataset) ? $this->_renderAccessAndAge($iLast) : '' ).' - </p> ' + . '<h3>'.$this->t('timeline-head').'</h3>' + . '<p class="hint">' + . sprintf($this->t('timeline-hint'), $this->_iMinTime4Timeline) + . '</p> + <p>' + .sprintf($this->t('graph-rendered-jobs'), $this->_iMinTime4Timeline).': <strong>'.count($aDataset).'</strong> ' + .'('.$this->t('total').': '.count($aData).')<br><br>' + .(count($aDataset) ? $this->_renderAccessAndAge($iLast) : '' ) + .'</p>' .(count($aDataset) ? '<div id="'.$sDivId.'"></div> @@ -481,7 +493,9 @@ class cronlogrenderer extends cronlog{ // fix: some timelines do not properly work ... but I make them visible $(\'#'.$sDivId.' .vis-timeline\').css(\'visibility\', \'visible\'); </script> - ' : '(kein Graph)<br>') + ' + : $this->t('graph-no-data').'<br>' + ) .' <!-- ENDE '.__METHOD__.'--> @@ -514,19 +528,19 @@ class cronlogrenderer extends cronlog{ */ public function renderLogfile($sLogfile){ $sHtml='' - . '<button style="position: fixed;" onclick="closeOverlay();" class="btn btn-default"><i class="fas fa-chevron-left"></i> back</button><br><br>' - . '<h3>Logfile '.basename($sLogfile).'</h3>' + . '<button style="position: fixed;" onclick="closeOverlay();" class="btn btn-default"><i class="fas fa-chevron-left"></i> '.$this->t('back').'</button><br><br>' + . '<h3>'. $this->t('logfile').' '.basename($sLogfile).'</h3>' ; if(!$sLogfile){ - return $sHtml . 'ERROR: empty filename for log file was given.'; + return $sHtml . $this->t('error-nologfile'); } if(strstr($sLogfile, '..')){ - return $sHtml . 'ERROR: wrong log file chars [..] are not allowed.'; + return $sHtml . $this->t('error-dots-not-allowed'); } // $sMyFile=$this->_getServerlogDir().'/'.$sLogfile; $sMyFile=$this->_sDataDir.'/'.$sLogfile; if(!file_exists($sMyFile)){ - return $sHtml . 'ERROR: The requested logfile<br>['.$sMyFile.']<br>does not exist (anymore).'; + return $sHtml . sprintf($this->t('error-logfile-not-found'), $sMyFile); } @@ -574,7 +588,7 @@ class cronlogrenderer extends cronlog{ $iMaxItemsToShow=30; $sHtml.='<option value="ALL"' .($sSelectedItem===false || $sSelectedItem==='ALL' ? ' selected="selected"' : '') - . '>[ALLE ('.count($this->getServers()).')]</option>'; + . '>['.$this->t('ALL').' ('.count($this->getServers()).')]</option>'; foreach($this->getServers() as $sServer=>$aData){ $sHtml.='<option value="'.$sServer.'"' .($sSelectedItem===$sServer ? ' selected="selected"' : '') diff --git a/classes/cronlog.class.php b/classes/cronlog.class.php index d0a0afee369572da935bf05b512af27b9697c586..a5dca3b8391a72241574edcb4a6982912100dd77 100644 --- a/classes/cronlog.class.php +++ b/classes/cronlog.class.php @@ -55,6 +55,9 @@ class cronlog { protected $_sFileFilter_serverjoblog = '*joblog*.done'; protected $_sFileFilter_serverlog = '*.log'; + protected $_sLang = ''; // language ... read from config file + protected $_aLang = []; // language data + // ---------------------------------------------------------------------- // MAIN // ---------------------------------------------------------------------- @@ -75,6 +78,8 @@ class cronlog { $this->_aSkipJoblogs = isset($aCfgTemp['aHidelogs']) && is_array($aCfgTemp['aHidelogs']) ? $aCfgTemp['aHidelogs'] : $this->_aSkipJoblogs; $this->_aInstances = isset($aCfgTemp['instances']) ? $aCfgTemp['instances'] : []; + $this->_sLang=$aCfgTemp['lang'] ? $aCfgTemp['lang'] : 'en-en'; + $this->_aLang=$aCfgTemp=include(__DIR__.'/../config/lang_'.$this->_sLang.'.php'); } $this->_sDataDir = str_replace("__APPDIR__", dirname(dirname(__FILE__)), $this->_sDataDir); $this->_sDataDir = str_replace('\\', '/', $this->_sDataDir); @@ -299,6 +304,14 @@ class cronlog { return $aReturn; } + /** + * translate ... get a language specific text of a given key + */ + public function t($id){ + return '' + .(isset($this->_aLang[$id]) ? $this->_aLang[$id] : '['.$id.'] ???') + ; + } // ---------------------------------------------------------------------- // public setter // ---------------------------------------------------------------------- diff --git a/config/lang_de-de.php b/config/lang_de-de.php new file mode 100644 index 0000000000000000000000000000000000000000..2383747ccfc34964e205aa47cbd59c1c7952383e --- /dev/null +++ b/config/lang_de-de.php @@ -0,0 +1,86 @@ +<?php + +return [ + + "id" => "deutsch", + + // ---------- left bar + "search" => "Suche", + + // ---------- top bar + "instances" => "Instanzen", + + // ---------- content + + "ALL" => "alle Server", + + + "request-time" => "Abruf: %s", + "last-entry" => "Letzer Eintrag: vor <strong>%s</strong> min", + + "total" => "Gesamt", + + "logs" => "Logdateien", + "logs-head" => "Letztes Logfile pro Job", + "logs-hint" => "Von jedem Cronjob kann man das jeweils letzte Log im Detail ansehen. Mit Klick in der Tabelle wird die Logdatei geöffnet.", + + "history" => "History", + "history-head" => "History", + "history-hint" => "Von den gestarteten Cronjobs werden die Ausführungszeiten und deren Exitcode für 6 Tage aufgehoben.", + + "timeline" => "Timeline", + "timeline-head" => "Graph mit Timeline", + "timeline-hint" => "Aus der History der letzten 6 Tage werden die Cronjobs mit mehr als %s Sekunden Laufzeit dargestellt.<br>" + ."So kann man ggf. Konflikte und Ungereimtheiten finden.<br>" + ."Innerhalb der Timeline kann man mit dem Mausrad den Zoom verändern.", + + + // ---------- table + "col-starting-time" => "Startzeit", + "col-label" => "Label", + "col-server" => "Server", + "col-duration" => "Dauer", + "col-ttl" => "TTL", + "col-rc" => "$?", + "col-expired" => "veraltet?", + "col-status" => "Status", + "status-ok" => "OK", + "status-error" => "FEHLER", + + + "row-click-show-logfile" => "Klick=[%s] anzeigen", + + // ---------- errors + "error-host-in-log-differs-servername-label" => "Hostname?", + "error-host-in-log-differs-servername-description" => "Der Hostname im Log [%s] stimmt nicht mit Servernamen [%s] überein.", + "error-no-fqdn-label" => "Kein FQDN", + "error-no-fqdn-description" => "Der Hostname im Log [%s] ist kein FQDN.", + "error-expired-label" => "Abgelaufen", + "error-expired-description" => "Job wurde nicht mehr gestartet oder kein Sync zum Logserver.", + "error-exitcode-label" => "Exitcode %s (<> 0)", + "error-exitcode-description" => "Job wurde nicht mit exitcode 0 beendet.", + "error-no-label-label" => "Kein Label?", + "error-no-label-description" => "Es wurde kein Label für den Cronjob erkannt. Bitte Kommandozeile des Jobs prüfen.", + + "graph-rendered-jobs" => "Jobs mit mehr als %s s Laufzeit", + "graph-no-data" => "(Keine Daten zum Rendern eines Graphen)", + + // ---------- popup with log file + "back" => "zurück", + "logfile" => "Logdatei", + + "error-no-logfile" => "ERROR: empty filename for log file was given.", + "error-dots-not-allowed" => "ERROR: wrong log file chars [..] are not allowed.", + "error-logfile-not-found" => "ERROR: The requested logfile<br>[%s]<br>does not exist (anymore).", + + // ---------- navigation + // "all" => "ALLE", + + // ---------- javascript texts + "JS" => [ + ], + + "" => "", + + +]; \ No newline at end of file diff --git a/config/page.tpl.php b/config/page.tpl.php index 3659b4545b783f4ff1f1201e17d01e6230c1ca9b..9ae00c0e054fd7724d2b601907bc40edc858a3bd 100644 --- a/config/page.tpl.php +++ b/config/page.tpl.php @@ -447,6 +447,7 @@ </footer> </div> + <script>{{INJECT_JS}}</script> <script src="{{DIR_ADMINLTEPLUGINS}}/bootstrap/js/bootstrap.bundle.min.js"></script> <script src="{{DIR_ADMINLTE}}/js/adminlte.min.js?v=3.2.0"></script> <script type="text/javascript" src="js/functions.js"></script> diff --git a/config/page_replacements.php b/config/page_replacements.php index c063d2fb8091c7d3e88f3f63cdd0f16a40fd7573..f0e71e92fe7b6b063cb3f8b08147e284f080f275 100644 --- a/config/page_replacements.php +++ b/config/page_replacements.php @@ -60,13 +60,13 @@ return [ <ul class="navbar-nav"> <li class="nav-item"> <a href="#cronlogs" onclick="setTab(this); return false;" class="nav-link active"> - <i class="far fa-file-alt"></i> Logs</a></li> + <i class="far fa-file-alt"></i> '.$cr->t('logs').'</a></li> <li class="nav-item"> <a href="#cronhistory" onclick="setTab(this); return false;" class="nav-link"> - <i class="fas fa-history"></i> History</a></li> + <i class="fas fa-history"></i> '.$cr->t('history').'</a></li> <li class="nav-item"> <a href="#graph" onclick="setTab(this); return false;" class="nav-link"> - <i class="far fa-chart-bar"></i> Timeline</a></li> + <i class="far fa-chart-bar"></i> '.$cr->t('timeline').'</a></li> </ul> </nav>', '{{PAGE_HEADER_RIGHT}}'=>'<span id="counter" style="float: right;"></span>', @@ -92,6 +92,7 @@ return [ </div> ', - '{{PAGE_FOOTER_LEFT}}'=>'2018 -2022 // University of Bern * Institute for Medical education', + '{{INJECT_JS}}' => '<!-- TODO -->', + '{{PAGE_FOOTER_LEFT}}'=>'2018 - '.date('Y').' // University of Bern * Institute for Medical education', '{{PAGE_FOOTER_RIGHT}}'=>'Source: <a href="https://git-repo.iml.unibe.ch/iml-open-source/cronlog-viewer/" target="_blank">git-repo.iml.unibe.ch</a>', ]; \ No newline at end of file diff --git a/index.php b/index.php index f27f56482ce2b42db4cdac40fcc819bcda69c2a0..9ba69ba3f9b65b29781bf48bf6be16934aa82ed5 100644 --- a/index.php +++ b/index.php @@ -1,9 +1,11 @@ <?php -define("APP_VERSION", '2.0.3'); +define("APP_VERSION", '2.0.4-dev'); require_once('classes/render-adminlte.class.php'); +require_once('classes/cronlog-renderer.class.php'); $renderAdminLTE=new renderadminlte(); +$cr=new cronlogrenderer(); $aReplace=include("./config/page_replacements.php"); $sTemplate=file_get_contents('config/page.tpl.php');