From 4604e92a0102383b8b87152852ba3fed74cf9600 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@iml.unibe.ch>
Date: Fri, 27 Jan 2023 16:46:02 +0100
Subject: [PATCH] multi langage support

---
 classes/cronlog-renderer.class.php |  85 +++++++--
 classes/cronlog.class.php          |   1 -
 config/inc_cronlog.php.dist        |   3 +
 config/lang_de-de.php              | 118 ++++++------
 config/lang_en-en.php              | 100 ++++++++++
 config/page.tpl.php                | 286 +----------------------------
 config/page_replacements.php       |   7 +-
 index.php                          |   2 +-
 js/asimax.class.js                 |   2 +-
 js/functions.js                    |   2 +-
 main.css                           |   5 +-
 11 files changed, 254 insertions(+), 357 deletions(-)
 create mode 100644 config/lang_en-en.php

diff --git a/classes/cronlog-renderer.class.php b/classes/cronlog-renderer.class.php
index 12348bf..e2cf5e2 100644
--- a/classes/cronlog-renderer.class.php
+++ b/classes/cronlog-renderer.class.php
@@ -51,9 +51,10 @@ class cronlogrenderer extends cronlog{
             return '';
         }
         $iAge=round((date('U')-$iLast)/60);
-        return ''
+        return '<div class="accessandage">'
             . sprintf($this->t("request-time"), date("Y-m-d H:i:s")).'<br>'
-            . sprintf($this->t("last-entry"), $iAge).'<br><br>'
+            . sprintf($this->t("last-entry"), $iAge)
+            .'</div>'
             ;
     }
     
@@ -68,6 +69,33 @@ class cronlogrenderer extends cronlog{
         return '$(\'#'.$sDatatable.'\').dataTable().fnFilter(\''.$sFiltertext.'\'); return false;';
     }
 
+    /**
+     * helper function to be used in methods that render datatable tables
+     * get javascript code to be added in init options and set language specifi texts
+     * @return string
+     */
+    protected function _getDatatableLanguage(){
+        return $this->t("dt-USE")
+            ? ', "oLanguage":{
+                "sProcessing":"'.$this->t("dt-sProcessing").'",
+                "sLengthMenu":"'.$this->t("dt-sLengthMenu").'",
+                "sZeroRecords":"'.$this->t("dt-sZeroRecords").'",
+                "sInfo":"'.$this->t("dt-sInfo").'",
+                "sInfoEmpty":"'.$this->t("dt-sInfoEmpty").'",
+                "sInfoFiltered":"'.$this->t("dt-sInfoFiltered").'",
+                "sInfoPostFix":"'.$this->t("dt-sInfoPostFix").'",
+                "sSearch":"'.$this->t("dt-sSearch").'",
+                "sUrl":"'.$this->t("dt-sUrl").'",
+                "oPaginate":{
+                    "sFirst":"'.$this->t("dt-sFirst").'",
+                    "sPrevious":"'.$this->t("dt-sPrevious").'",
+                    "sNext":"'.$this->t("dt-sNext").'",
+                    "sLast":"'.$this->t("dt-sLast").'"
+                }
+        }'
+            : ''
+        ;
+    }
     /**
      * get html code for a table with events of executed cronjobs
      * 
@@ -222,11 +250,10 @@ class cronlogrenderer extends cronlog{
             . '</p>'
             . '<div>'
                 . $this->_renderAccessAndAge($iLast)
-                . ($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>'
+                . ($iErrors ? '<a href="#" class="btn bg-danger" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-error')).'"><i class="fas fa-exclamation-circle"></i> &nbsp; ' . $iErrors.'</a> ' : '')
+                . ( $iOK ? '<a href="#" class="btn bg-success" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-ok')).'"><i class="fas fa-check"></i> &nbsp; ' . $iOK.'</a>' : '') 
+                . ($iErrors && $iOK ? ' ... '.$this->t('total').': <a href="#" class="btn bg-gray" onclick="'.$this->_filterDatatable($sIdTable, "").'"><i class="fas fa-th-large"></i> &nbsp; ' . count($aData).'</a>' : '' )
+            . '</div><br>'
             . '<table id="'.$sIdTable.'" class="table-striped">'
             . '<thead><tr>'.$sTblHead.'</tr></thead>'
             . '<tbody>'
@@ -236,7 +263,13 @@ class cronlogrenderer extends cronlog{
 
             // init datatable
             . '<script>'
-                . '$(document).ready( function () { $(\'#'.$sIdTable.'\').DataTable({"retrieve": true, "bPaginate":false, "aaSorting":[[0,"desc"]]}); } );'
+                . '$(document).ready( function () { $(\'#'.$sIdTable.'\').DataTable({
+                        "retrieve": true, 
+                        "bPaginate":false, 
+                        "aaSorting":[[0,"desc"]]
+                        '.$this->_getDatatableLanguage().'
+                     }); 
+                   });'
             . '</script>'
 
             . '
@@ -312,6 +345,7 @@ class cronlogrenderer extends cronlog{
                     $this->t("col-duration"), 
                     $this->t("col-ttl"), 
                     $this->t("col-rc"), 
+                    $this->t("col-status"), 
                 ) as $sKey){
                     $sTblHead.='<th>'.$sKey.'</th>';
                 }
@@ -339,7 +373,8 @@ class cronlogrenderer extends cronlog{
                     . '<td>'.$aEntry['ttl'].'</td>'
                     . '<td class="'
                         .($aEntry['rc']>0 ? 'message-error': 'message-ok')
-                    . '">'.$aEntry['rc'].'</td>'
+                        . '">'.$aEntry['rc'].'</td>'
+                    . '<td>'.( $aEntry['rc'] ? $this->t('status-error') : $this->t('status-ok') ).'</td>'
                     . '</tr>'
                     ;
         }
@@ -353,10 +388,12 @@ class cronlogrenderer extends cronlog{
             . '</p>'
             . '<div>'
                 . $this->_renderAccessAndAge($iLast)
-                . $this->t('total').': <strong>' . count($aData).'</strong>'
-                . ($iErrors ? ' ('.$this->t('status-error').': <strong>' . $iErrors.'</strong>... '.$this->t('status-ok').': <strong>' . $iOK.'</strong>)' : '')
-                . '<br><br>'
+                . ($iErrors ? '<a href="#" class="btn bg-danger" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-error')).'"><i class="fas fa-exclamation-circle"></i> &nbsp; ' . $iErrors.'</a> ' : '')
+                . ( $iOK ? '<a href="#" class="btn bg-success" onclick="'.$this->_filterDatatable($sIdTable, $this->t('status-ok')).'"><i class="fas fa-check"></i> &nbsp; ' . $iOK.'</a>' : '') 
+                . ($iErrors && $iOK ? ' ... '.$this->t('total').': <a href="#" class="btn bg-gray" onclick="'.$this->_filterDatatable($sIdTable, "").'"><i class="fas fa-th-large"></i> &nbsp; ' . count($aData).'</a>' : '' )
             . '</div>'
+            . '<br>'
+
             . '<table id="'.$sIdTable.'">'
             . '<thead><tr>'.$sTblHead.'</tr></thead>'
             . '<tbody>'
@@ -366,7 +403,12 @@ class cronlogrenderer extends cronlog{
 
             // init datatable
             . '<script>'
-            . '$(document).ready( function () {$(\'#'.$sIdTable.'\').DataTable({"retrieve": true, "aaSorting":[[0,"desc"]],"aaSorting":[[0,"desc"]], "aLengthMenu":[[25,100,-1],[25,100,"---"]]});} );'
+            . '$(document).ready( function () {$(\'#'.$sIdTable.'\').DataTable({
+                    "retrieve": true, 
+                    "aaSorting":[[0,"desc"]], 
+                    "aLengthMenu":[[25,100,-1],[25,100,"---"]]
+                    '.$this->_getDatatableLanguage().'
+                });} );'
             . '</script>'
 
             . '
@@ -519,6 +561,23 @@ class cronlogrenderer extends cronlog{
         return $this->renderJobGraph($aData);
     }
     
+    /**
+     * used in config/page-replacements.php
+     * generate an array of javascript lang texts
+     */
+    public function renderJSLang(){
+        $aReturn=[];
+
+        foreach($this->_aLang as $sKey=>$sText){
+            if(preg_match('/^JS_/', $sKey)){
+                $aReturn[preg_replace('/^JS_/', '', $sKey)]=$sText;
+            }
+        }
+        return "\n"
+            .'// generated by '.__METHOD__."\n"
+            .'var aLang='.json_encode($aReturn, JSON_PRETTY_PRINT).'; '."\n"
+        ;
+    }
     
    /**
     * show a single log file
diff --git a/classes/cronlog.class.php b/classes/cronlog.class.php
index a5dca3b..48a079e 100644
--- a/classes/cronlog.class.php
+++ b/classes/cronlog.class.php
@@ -336,5 +336,4 @@ class cronlog {
         return $this->_sActiveServer;
     }
     
-    
 }
\ No newline at end of file
diff --git a/config/inc_cronlog.php.dist b/config/inc_cronlog.php.dist
index bcacdd6..cc03cc2 100644
--- a/config/inc_cronlog.php.dist
+++ b/config/inc_cronlog.php.dist
@@ -20,6 +20,9 @@ return array(
     // starting directory with all servers cronwrapper logs
     'sDatadir'=>'__APPDIR__/data',
 
+    // language - see config/lang_*.php files
+    'lang'=>'en-en',
+
     // caching time for serverinfos
     'iTtlCache'=>60,
 
diff --git a/config/lang_de-de.php b/config/lang_de-de.php
index 2383747..4506a22 100644
--- a/config/lang_de-de.php
+++ b/config/lang_de-de.php
@@ -1,51 +1,51 @@
 <?php
-
+/*
+    CronLog viewer :: language file
+*/
 return [
 
-    "id"           => "deutsch",
+    "id"                => "deutsch",
 
     // ---------- left bar
-    "search"       => "Suche",
+    "search"            => "Suche",
 
     // ---------- top bar
-    "instances"    => "Instanzen",
+    "instances"        => "Instanzen",
 
     // ---------- content
+    "ALL"              => "alle Server",
 
-    "ALL"          => "alle Server",
-
+    "request-time"      => "Abruf: %s",
+    "last-entry"        => "Letzer Eintrag: vor <strong>%s</strong> min",
 
-    "request-time" => "Abruf: %s",
-    "last-entry"   => "Letzer Eintrag: vor <strong>%s</strong> min",
+    "total"             => "Gesamt",
 
-    "total" => "Gesamt",
+    "logs"              => "Logdateien",
+    "logs-head"         => "Aktueller Status aller Cronjobs",
+    "logs-hint"         => "Von jedem Cronjob kann man das jeweils letzte Log im Detail ansehen. Mit Klick in der Tabelle wird die Logdatei geöffnet.",
 
-    "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&uuml;hrungszeiten und deren Exitcode f&uuml;r 6 Tage aufgehoben.",
 
-    "history" => "History",
-    "history-head" => "History",
-    "history-hint" => "Von den gestarteten Cronjobs werden die Ausf&uuml;hrungszeiten und deren Exitcode f&uuml;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>"
+    "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&auml;ndern.",
+        ."Innerhalb der Timeline kann man mit dem Mausrad den Zoom ver&auml;ndern. Mit gedr&uuml;ckter Maustaste kann man nach links und rechts schieben, um das Zeitabschnitt zu verschieben.",
 
 
     // ---------- 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",
+    "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",
@@ -53,34 +53,48 @@ return [
     // ---------- 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] &uuml;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&uuml;r den Cronjob erkannt. Bitte Kommandozeile des Jobs pr&uuml;fen.",
-
-    "graph-rendered-jobs" => "Jobs mit mehr als %s s Laufzeit",
-    "graph-no-data" => "(Keine Daten zum Rendern eines Graphen)",
+    "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&uuml;r den Cronjob erkannt. Bitte Kommandozeile des Jobs pr&uuml;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&uuml;ck",
-    "logfile" => "Logdatei",
+    "back"              => "zur&uuml;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-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" => [
-    ],
-
+    "JS_Refresh"        => "Aktualisierung in <span>__TIME__</span> s",
+
+    // ---------- datatable texts
+    // flag: set to true to all languages except English (it is the default of datatables)
+    "dt-USE"            => true, 
+
+    // these will be used if dt-USE is set to true:
+    "dt-sProcessing"    => "Bitte warten...",
+    "dt-sLengthMenu"    => "_MENU_ Eintr&auml;ge anzeigen",
+    "dt-sZeroRecords"   => "Keine Eintr&auml;ge vorhanden.",
+    "dt-sInfo"          => "_START_ bis _END_ von _TOTAL_ Eintr&auml;gen",
+    "dt-sInfoEmpty"     => "0 bis 0 von 0 Eintr&auml;gen",
+    "dt-sInfoFiltered"  => "(gefiltert von _MAX_  Eintr&auml;gen)",
+    "dt-sInfoPostFix"   => "",
+    "dt-sSearch"        => "Suchen: ",
+    "dt-sUrl"           => "",
+    "dt-sFirst"         => "Erster",
+    "dt-sPrevious"      => "Zur&uuml;ck",
+    "dt-sNext"          => "N&auml;chster",
+    "dt-sLast"          => "Letzter",
+    
     "" => "",
 
-
 ];
\ No newline at end of file
diff --git a/config/lang_en-en.php b/config/lang_en-en.php
new file mode 100644
index 0000000..eef52a0
--- /dev/null
+++ b/config/lang_en-en.php
@@ -0,0 +1,100 @@
+<?php
+/*
+    CronLog viewer :: language file
+*/
+return [
+
+    "id"                => "english",
+
+    // ---------- left bar
+    "search"            => "Search",
+
+    // ---------- top bar
+    "instances"         => "Instances",
+
+    // ---------- content
+    "ALL"               => "all servers",
+
+    "request-time"      => "Request time: %s",
+    "last-entry"        => "Last entry: <strong>%s</strong> min ago",
+
+    "total"             => "Total",
+
+    "logs"              => "Logfiles",
+    "logs-head"         => "Current status of each cronjob",
+    "logs-hint"         => "View the last logfile of each cronjob. Click into the table row to open it.",
+
+    "history"           => "History",
+    "history-head"      => "History",
+    "history-hint"      => "The cronwrapper keeps data for 6 days. For each executed cronjob you can see execution times and exitcode.",
+
+    "timeline"          => "Timeline",
+    "timeline-head"     => "Graph with timeline",
+    "timeline-hint"     => "The a graph of the history of the last 6 days - draw cronjobs running longer than %s seconds.<br>"
+        ."It helps to find timing conflicts and errors.<br>"
+        ."Inside the timeline you can zoom with the mouse wheel or drag to the left and right to change the time window.",
+
+
+    // ---------- table
+    "col-starting-time" => "Starting time",
+    "col-label"         => "Label",
+    "col-server"        => "Server",
+    "col-duration"      => "Duration",
+    "col-ttl"           => "TTL",
+    "col-rc"            => "$?",
+    "col-expired"       => "Expired?",
+    "col-status"        => "Status",
+    "status-ok"         => "OK",
+    "status-error"      => "ERROR",
+    
+
+    "row-click-show-logfile" => "Click=show [%s]",
+    
+    // ---------- errors
+    "error-host-in-log-differs-servername-label"       => "Hostname?",
+    "error-host-in-log-differs-servername-description" => "The hostname in the logfile [%s] does not match with server name [%s].",
+    "error-no-fqdn-label"          => "No fqdn",
+    "error-no-fqdn-description"    => "The hostname in the logfile [%s] is no fqdn.",
+    "error-expired-label"          => "Expired",
+    "error-expired-description"    => "The Job was not executed anymore (or no sync to the logserver).",
+    "error-exitcode-label"         => "Exitcode %s (<> 0)",
+    "error-exitcode-description"   => "The job did not finish with exitcode 0.",
+    "error-no-label-label"         => "No label?",
+    "error-no-label-description"   => "No label detected for the cronjob. Check the command line of the job.",
+
+    "graph-rendered-jobs" => "Jobs with an execution time longer %s s",
+    "graph-no-data"       => "(No data to render a graph)",
+
+    // ---------- popup with log file
+    "back"              => "back",
+    "logfile"           => "Logfile",
+
+    "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).",
+
+    // ---------- javascript texts
+    "JS_Refresh"        => "Refresh in <span>__TIME__</span> sec",
+
+    // ---------- datatable texts
+    // flag: set to true to all languages except English (it is the default of datatables)
+    "dt-USE"            => false,
+
+    // these will be used if dt-USE is set to true:
+    "dt-sProcessing"    => "Bitte warten...",
+    "dt-sLengthMenu"    => "_MENU_ Eintr&auml;ge anzeigen",
+    "dt-sZeroRecords"   => "Keine Eintr&auml;ge vorhanden.",
+    "dt-sInfo"          => "_START_ bis _END_ von _TOTAL_ Eintr&auml;gen",
+    "dt-sInfoEmpty"     => "0 bis 0 von 0 Eintr&auml;gen",
+    "dt-sInfoFiltered"  => "(gefiltert von _MAX_  Eintr&auml;gen)",
+    "dt-sInfoPostFix"   => "",
+    "dt-sSearch"        => "Suchen: ",
+    "dt-sUrl"           => "",
+    "dt-sFirst"         => "Erster",
+    "dt-sPrevious"      => "Zur&uuml;ck",
+    "dt-sNext"          => "N&auml;chster",
+    "dt-sLast"          => "Letzter",
+
+    "" => "",
+
+];
\ No newline at end of file
diff --git a/config/page.tpl.php b/config/page.tpl.php
index 9ae00c0..d429d45 100644
--- a/config/page.tpl.php
+++ b/config/page.tpl.php
@@ -22,10 +22,6 @@
     <!-- fontawesome -->
     <link href="vendor/font-awesome/5.15.4/css/all.min.css" rel="stylesheet" type="text/css"/>
 
-
-    <!--
-    <link rel="stylesheet" href="{{DIR_ADMINLTEPLUGINS}}/fontawesome-free/css/all.min.css">
-    -->
     <!-- Adminlte -->
     <link rel="stylesheet" href="{{DIR_ADMINLTE}}/css/adminlte.min.css?v=3.2.0">
 
@@ -102,225 +98,13 @@
     <div class="wrapper">
 
         {{NAVI_TOP}}
-        <!--
-        <nav class="main-header navbar navbar-expand navbar-white navbar-light">
-
-            <ul class="navbar-nav">
-                <li class="nav-item">
-                    <a class="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="index3.html" class="nav-link">Home</a>
-                </li>
-                <li class="nav-item d-none d-sm-inline-block">
-                    <a href="#" class="nav-link">Contact</a>
-                </li>
-            </ul>
-
-            <ul class="navbar-nav ml-auto">
-
-                <li class="nav-item">
-                    <a class="nav-link" data-widget="navbar-search" href="#" role="button">
-                        <i class="fas fa-search"></i>
-                    </a>
-                    <div class="navbar-search-block">
-                        <form class="form-inline">
-                            <div class="input-group input-group-sm">
-                                <input class="form-control form-control-navbar" type="search" placeholder="Search"
-                                    aria-label="Search">
-                                <div class="input-group-append">
-                                    <button class="btn btn-navbar" type="submit">
-                                        <i class="fas fa-search"></i>
-                                    </button>
-                                    <button class="btn btn-navbar" type="button" data-widget="navbar-search">
-                                        <i class="fas fa-times"></i>
-                                    </button>
-                                </div>
-                            </div>
-                        </form>
-                    </div>
-                </li>
-
-                <li class="nav-item dropdown">
-                    <a class="nav-link" data-toggle="dropdown" href="#">
-                        <i class="far fa-comments"></i>
-                        <span class="badge badge-danger navbar-badge">3</span>
-                    </a>
-                    <div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
-                        <a href="#" class="dropdown-item">
-
-                            <div class="media">
-                                <img src="dist/img/user1-128x128.jpg" alt="User Avatar"
-                                    class="img-size-50 mr-3 img-circle">
-                                <div class="media-body">
-                                    <h3 class="dropdown-item-title">
-                                        Brad Diesel
-                                        <span class="float-right text-sm text-danger"><i class="fas fa-star"></i></span>
-                                    </h3>
-                                    <p class="text-sm">Call me whenever you can...</p>
-                                    <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p>
-                                </div>
-                            </div>
-
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item">
-
-                            <div class="media">
-                                <img src="dist/img/user8-128x128.jpg" alt="User Avatar"
-                                    class="img-size-50 img-circle mr-3">
-                                <div class="media-body">
-                                    <h3 class="dropdown-item-title">
-                                        John Pierce
-                                        <span class="float-right text-sm text-muted"><i class="fas fa-star"></i></span>
-                                    </h3>
-                                    <p class="text-sm">I got your message bro</p>
-                                    <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p>
-                                </div>
-                            </div>
-
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item">
-
-                            <div class="media">
-                                <img src="dist/img/user3-128x128.jpg" alt="User Avatar"
-                                    class="img-size-50 img-circle mr-3">
-                                <div class="media-body">
-                                    <h3 class="dropdown-item-title">
-                                        Nora Silvester
-                                        <span class="float-right text-sm text-warning"><i
-                                                class="fas fa-star"></i></span>
-                                    </h3>
-                                    <p class="text-sm">The subject goes here</p>
-                                    <p class="text-sm text-muted"><i class="far fa-clock mr-1"></i> 4 Hours Ago</p>
-                                </div>
-                            </div>
-
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item dropdown-footer">See All Messages</a>
-                    </div>
-                </li>
-
-                <li class="nav-item dropdown">
-                    <a class="nav-link" data-toggle="dropdown" href="#">
-                        <i class="far fa-bell"></i>
-                        <span class="badge badge-warning navbar-badge">15</span>
-                    </a>
-                    <div class="dropdown-menu dropdown-menu-lg dropdown-menu-right">
-                        <span class="dropdown-header">15 Notifications</span>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item">
-                            <i class="fas fa-envelope mr-2"></i> 4 new messages
-                            <span class="float-right text-muted text-sm">3 mins</span>
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item">
-                            <i class="fas fa-users mr-2"></i> 8 friend requests
-                            <span class="float-right text-muted text-sm">12 hours</span>
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item">
-                            <i class="fas fa-file mr-2"></i> 3 new reports
-                            <span class="float-right text-muted text-sm">2 days</span>
-                        </a>
-                        <div class="dropdown-divider"></div>
-                        <a href="#" class="dropdown-item dropdown-footer">See All Notifications</a>
-                    </div>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-widget="fullscreen" href="#" role="button">
-                        <i class="fas fa-expand-arrows-alt"></i>
-                    </a>
-                </li>
-                <li class="nav-item">
-                    <a class="nav-link" data-widget="control-sidebar" data-slide="true" href="#" role="button">
-                        <i class="fas fa-th-large"></i>
-                    </a>
-                </li>
-            </ul>
-        </nav>
-        -->
 
         <aside class="main-sidebar sidebar-dark-primary elevation-4">
 
             {{BRAND}}
-            <!--
-            <a href="index3.html" class="brand-link">
-                <img src="dist/img/AdminLTELogo.png" alt="AdminLTE Logo" class="brand-image img-circle elevation-3"
-                    style="opacity: .8">
-                <span class="brand-text font-weight-light">AdminLTE 3</span>
-            </a>
-            -->
 
             <div class="sidebar">
-
-                <!--
-                <div class="user-panel mt-3 pb-3 mb-3 d-flex">
-                    <div class="image">
-                        <img src="dist/img/user2-160x160.jpg" class="img-circle elevation-2" alt="User Image">
-                    </div>
-                    <div class="info">
-                        <a href="#" class="d-block">Alexander Pierce</a>
-                    </div>
-                </div>
-
-                <div class="form-inline">
-                    <div class="input-group" data-widget="sidebar-search">
-                        <input class="form-control form-control-sidebar" type="search" placeholder="Search"
-                            aria-label="Search">
-                        <div class="input-group-append">
-                            <button class="btn btn-sidebar">
-                                <i class="fas fa-search fa-fw"></i>
-                            </button>
-                        </div>
-                    </div>
-                </div>
-                -->
                 {{NAVI_LEFT}}
-                <br><br>
-
-                <!--
-                <nav class="mt-2">
-                    <ul class="nav nav-pills nav-sidebar flex-column" data-widget="treeview" role="menu" data-accordion="false">
-
-                        <li class="nav-item menu-open">
-                            <a href="#" class="nav-link active">
-                                <i class="nav-icon fas fa-tachometer-alt"></i>
-                                <p>
-                                    Starter Pages
-                                    <i class="right fas fa-angle-left"></i>
-                                </p>
-                            </a>
-                            <ul class="nav nav-treeview">
-                                <li class="nav-item">
-                                    <a href="#" class="nav-link active">
-                                        <i class="far fa-circle nav-icon"></i>
-                                        <p>Active Page</p>
-                                    </a>
-                                </li>
-                                <li class="nav-item">
-                                    <a href="#" class="nav-link">
-                                        <i class="far fa-circle nav-icon"></i>
-                                        <p>Inactive Page</p>
-                                    </a>
-                                </li>
-                            </ul>
-                        </li>
-                        <li class="nav-item">
-                            <a href="#" class="nav-link">
-                                <i class="nav-icon fas fa-th"></i>
-                                <p>
-                                    Simple Link
-                                    <span class="right badge badge-danger">New</span>
-                                </p>
-                            </a>
-                        </li>
-                    </ul>
-                </nav>
-                -->
-
             </div>
 
         </aside>
@@ -338,8 +122,7 @@
                         </div>
                         <div class="col-sm-6">
                             <ol class="breadcrumb float-sm-right">
-                                {{PAGE_HEADER_RIGHT}}
-                                
+                                {{PAGE_HEADER_RIGHT}}                                
                                 <!--
                                 <li class="breadcrumb-item"><a href="#">Home</a></li>
                                 <li class="breadcrumb-item active">Starter Page</li>
@@ -353,68 +136,8 @@
 
 
             <div class="content">
-
-
                 <div class="container-fluid">
-
                     {{PAGE_BODY}}
-
-                    <!--
-                    <div class="row">
-                        <div class="col-lg-6">
-                            <div class="card">
-                                <div class="card-body">
-                                    <h5 class="card-title">Card title</h5>
-                                    <p class="card-text">
-                                        Some quick example text to build on the card title and make up the bulk of the
-                                        card's
-                                        content.
-                                    </p>
-                                    <a href="#" class="card-link">Card link</a>
-                                    <a href="#" class="card-link">Another link</a>
-                                </div>
-                            </div>
-                            <div class="card card-primary card-outline">
-                                <div class="card-body">
-                                    <h5 class="card-title">Card title</h5>
-                                    <p class="card-text">
-                                        Some quick example text to build on the card title and make up the bulk of the
-                                        card's
-                                        content.
-                                    </p>
-                                    <a href="#" class="card-link">Card link</a>
-                                    <a href="#" class="card-link">Another link</a>
-                                </div>
-                            </div>
-                        </div>
-
-                        <div class="col-lg-6">
-                            <div class="card">
-                                <div class="card-header">
-                                    <h5 class="m-0">Featured</h5>
-                                </div>
-                                <div class="card-body">
-                                    <h6 class="card-title">Special title treatment</h6>
-                                    <p class="card-text">With supporting text below as a natural lead-in to additional
-                                        content.</p>
-                                    <a href="#" class="btn btn-primary">Go somewhere</a>
-                                </div>
-                            </div>
-                            <div class="card card-primary card-outline">
-                                <div class="card-header">
-                                    <h5 class="m-0">Featured</h5>
-                                </div>
-                                <div class="card-body">
-                                    <h6 class="card-title">Special title treatment</h6>
-                                    <p class="card-text">With supporting text below as a natural lead-in to additional
-                                        content.</p>
-                                    <a href="#" class="btn btn-primary">Go somewhere</a>
-                                </div>
-                            </div>
-                        </div>
-
-                    </div>
-                    -->
                 </div>
             </div>
 
@@ -434,16 +157,9 @@
 
             <div class="float-right d-none d-sm-inline">
                 {{PAGE_FOOTER_RIGHT}}
-                <!--
-                Anything you want
-                -->
             </div>
 
             {{PAGE_FOOTER_LEFT}}
-            <!--
-            <strong>Copyright &copy; 2014-2021 <a href="https://adminlte.io">AdminLTE.io</a>.</strong> All rights
-            reserved.
-            -->
         </footer>
     </div>
 
diff --git a/config/page_replacements.php b/config/page_replacements.php
index f0e71e9..f9551de 100644
--- a/config/page_replacements.php
+++ b/config/page_replacements.php
@@ -1,5 +1,10 @@
 <?php
+/*
 
+    this file is included by ../index.php
+    $cr=new cronlogrenderer();
+
+*/
 return [
     '{{DIR_ADMINLTE}}' =>'/vendor/admin-lte/3.2.0',
     '{{DIR_ADMINLTEPLUGINS}}' =>'/vendor/admin-lte-plugins',
@@ -92,7 +97,7 @@ return [
                       </div>
           
                       ',
-    '{{INJECT_JS}}' => '<!-- TODO -->',
+    '{{INJECT_JS}}' => $cr->renderJSLang(),
     '{{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 9ba69ba..613a93b 100644
--- a/index.php
+++ b/index.php
@@ -1,6 +1,6 @@
 <?php
 
-define("APP_VERSION", '2.0.4-dev');
+define("APP_VERSION", '2.1.0-beta');
 
 require_once('classes/render-adminlte.class.php');
 require_once('classes/cronlog-renderer.class.php');
diff --git a/js/asimax.class.js b/js/asimax.class.js
index ea49a4b..70a2bed 100644
--- a/js/asimax.class.js
+++ b/js/asimax.class.js
@@ -79,7 +79,7 @@ var asimax = function () {
         } catch(e){
             // nop
         }
-        console.log('ERROR in getVar() - argument "'+sName+'" does not exist');
+        // console.log('ERROR in getVar() - argument "'+sName+'" does not exist');
         return false;
     };
     /**
diff --git a/js/functions.js b/js/functions.js
index b6ff8a9..de8b1e9 100644
--- a/js/functions.js
+++ b/js/functions.js
@@ -83,7 +83,7 @@ function getPageItem(id, sMoreData, bNoAdressbarUpdate) {
  */
 function refreshTimer() {
     if(iRefreshCounter==0){
-        $('#counter').html('Refresh in <span>'+(iRefreshTime)+'</span> sec'+'<div style="width:100%;">&nbsp;</div>');
+        $('#counter').html(aLang['Refresh'].replace('__TIME__', iRefreshTime)+'<div style="width:100%;">&nbsp;</div>');
     } else {
         $('#counter span').html(iRefreshTime-iRefreshCounter);
         $('#counter div').css('width', (100 - (iRefreshCounter/iRefreshTime*100))+'%');
diff --git a/main.css b/main.css
index dff2104..09ee0ec 100644
--- a/main.css
+++ b/main.css
@@ -10,7 +10,7 @@
 /* ----- head area */
 
 #counter{border-bottom: 1px solid #ccc;}
-#counter div{background:#bcd; background:#aaa;  height: 5px; transition: width 0.3s ;}
+#counter div{background:#17a2b8; height: 5px; transition: width 0.3s ;}
 
 
 #subnav .nav-link.active{
@@ -26,10 +26,11 @@
 #errorlog {background:#fcc; color:#800;}
 
 p.hint{margin-bottom: 1em; background:#f8f8f8; padding: 1em; }
-
+div.accessandage{float: left; margin-right: 2em;}
 
 /* ----- override datatable defaults */
 .dataTables_wrapper{clear: none;float: left; margin: auto 1px;}
+.dataTables_wrapper input[type="search"]{ border: 1px solid rgba(0,0,0,0.3); border-radius: 0.2em;;}
 /* 
 table.dataTable tbody tr.even_{background: #f0f4f8;}
 table.dataTable tbody tr.odd{} 
-- 
GitLab